-
-
Notifications
You must be signed in to change notification settings - Fork 671
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Android file browser #1158
base: main
Are you sure you want to change the base?
Android file browser #1158
Changes from all commits
558d9f1
4343df7
df001f5
7ecb67d
5d8658f
f9a1ffd
2f33e22
b66cdcc
62eeca6
26c46dc
85d4f4c
ed40eaa
bc61b05
842e2a1
37b9ca4
2ffc67f
2fc5940
7e177f3
868d06f
b4bc066
2009c6e
a7e016e
32cb78e
f2ae7c1
aad9eab
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,7 +14,9 @@ author_email = "[email protected]" | |
formal_name = "Dialog Demo" | ||
description = "A testing app" | ||
sources = ['dialogs'] | ||
requires = [] | ||
requires = [ | ||
'c:/Projects/Python/Toga/src/core' | ||
] | ||
|
||
|
||
[tool.briefcase.app.dialogs.macOS] | ||
|
@@ -40,5 +42,6 @@ requires = [ | |
|
||
[tool.briefcase.app.dialogs.android] | ||
requires = [ | ||
'toga-android', | ||
#'toga-android', | ||
'c:/Projects/Python/Toga/src/android' | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from rubicon.java import JavaClass | ||
|
||
Uri = JavaClass("android/net/Uri") |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,8 @@ | ||
from . import dialogs | ||
from .libs.activity import Activity | ||
from .libs.android import R__attr | ||
from .libs.android.content import Intent | ||
from .libs.android.net import Uri | ||
from .libs.android.util import TypedValue | ||
|
||
|
||
|
@@ -104,3 +107,89 @@ def stack_trace_dialog(self, title, message, content, retry=False): | |
|
||
def save_file_dialog(self, title, suggested_filename, file_types): | ||
self.interface.factory.not_implemented('Window.save_file_dialog()') | ||
|
||
async def open_file_dialog(self, title, initial_uri, file_mime_types, multiselect): | ||
""" | ||
Opens a file chooser dialog and returns the chosen file as content URI. | ||
Raises a ValueError when nothing has been selected | ||
|
||
:param str title: The title is ignored on Android | ||
:param initial_uri: The initial location shown in the file chooser. Must be a content URI, e.g. | ||
'content://com.android.externalstorage.documents/document/primary%3ADownload%2FTest-dir' | ||
:type initial_uri: str or None | ||
:param file_mime_types: The file types allowed to select. Must be MIME types, e.g. | ||
['application/pdf','application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']. | ||
Currently ignored to avoid error in rubicon | ||
:type file_mime_types: list[str] or None | ||
:param bool multiselect: If True, then several files can be selected | ||
:returns: The content URI of the chosen file or a list of content URIs when multiselect=True. | ||
:rtype: str or list[str] | ||
""" | ||
print('Invoking Intent ACTION_OPEN_DOCUMENT') | ||
intent = Intent(Intent.ACTION_OPEN_DOCUMENT) | ||
intent.addCategory(Intent.CATEGORY_OPENABLE) | ||
intent.setType("*/*") | ||
if initial_uri is not None and initial_uri != '': | ||
intent.putExtra("android.provider.extra.INITIAL_URI", Uri.parse(initial_uri)) | ||
if file_mime_types is not None and file_mime_types != ['']: | ||
# Commented out because rubicon currently does not support arrays and nothing else works with this Intent | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @t-arn! This is a great change overall! With regard to this line, it might be nice to file a rubicon-java bug requesting string arrays be passable-in somehow. Based on beeware/rubicon-java#53 it should be possible to do that. It doesn't have to block this PR! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, I filed a rubicon enhancement request: |
||
# see https://github.com/beeware/rubicon-java/pull/53 | ||
# intent.putExtra(Intent.EXTRA_MIME_TYPES, file_mime_types) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be worth putting in a NotImplemented call here so that the end-user sees a manifestation of this limitation in the logs. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, will do |
||
self.interface.factory.not_implemented( | ||
'Window.open_file_dialog() on Android currently does not support the file_type parameter') | ||
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, multiselect) | ||
selected_uri = None | ||
result = await self.app.intent_result(intent) | ||
if result["resultCode"] == Activity.RESULT_OK: | ||
if result["resultData"] is not None: | ||
selected_uri = result["resultData"].getData() | ||
if multiselect: | ||
if selected_uri is None: | ||
# when the user selects more than 1 file, getData() will be None. Instead, getClipData() will | ||
# contain the list of chosen files | ||
selected_uri = [] | ||
clip_data = result["resultData"].getClipData() | ||
if clip_data is not None: # just to be sure there will never be a null reference exception... | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes - but why will there be a null reference exception? Docstrings should be about the why, not the what. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, I'm not sure if there ever will be a null pointer exception. |
||
for i in range(0, clip_data.getItemCount()): | ||
selected_uri.append(str(clip_data.getItemAt(i).getUri())) | ||
else: | ||
selected_uri = [str(selected_uri)] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems like it's not at the right indent level... is this the else for the multiselect? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, the problem is that even when multiselect is True, the user might only select 1 file which will be returned with getData() (and clip_data will be empty). Only when multiselect is True AND the user selected several files, clip_data will contain the list with the files (and getData will be None) |
||
if selected_uri is None: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code smoothly handles the cancellation possibility! |
||
raise ValueError("No filename provided in the open file dialog") | ||
return selected_uri | ||
|
||
async def select_folder_dialog(self, title, initial_uri=None, multiselect=False): | ||
""" | ||
Opens a folder chooser dialog and returns the chosen folder as content URI. | ||
Raises a ValueError when nothing has been selected | ||
|
||
:param str title: The title is ignored on Android | ||
:param initial_uri: The initial location shown in the file chooser. Must be a content URI, e.g. | ||
'content://com.android.externalstorage.documents/document/primary%3ADownload%2FTest-dir' | ||
:type initial_uri: str or None | ||
:param bool multiselect: If True, then several files can be selected | ||
:returns: The content tree URI of the chosen folder or a list of content URIs when multiselect=True. | ||
:rtype: str or list[str] | ||
""" | ||
print('Invoking Intent ACTION_OPEN_DOCUMENT_TREE') | ||
intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) | ||
if initial_uri is not None and initial_uri != '': | ||
intent.putExtra("android.provider.extra.INITIAL_URI", Uri.parse(initial_uri)) | ||
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, multiselect) | ||
selected_uri = None | ||
result = await self.app.intent_result(intent) | ||
if result["resultCode"] == Activity.RESULT_OK: | ||
if result["resultData"] is not None: | ||
selected_uri = result["resultData"].getData() | ||
if multiselect is True: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's an analogous need for docstrings in this implementation; we can't assume someone will read both methods. |
||
if selected_uri is None: | ||
selected_uri = [] | ||
clip_data = result["resultData"].getClipData() | ||
if clip_data is not None: | ||
for i in range(0, clip_data.getItemCount()): | ||
selected_uri.append(str(clip_data.getItemAt(i).getUri())) | ||
else: | ||
selected_uri = [str(selected_uri)] | ||
if selected_uri is None: | ||
raise ValueError("No folder provided in the open folder dialog") | ||
return selected_uri |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like to do
return await result_future
as one line, rather than two lines like you do here on 129-130. Your way is fine, too, so not a blocker, but just something that occurred to me.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I do not return result_future, I need to return result_future.result()
And return await result_future.result() did not work.