toga
toga copied to clipboard
Android file browser
This PR implements Window.open_file_dialog and Window.select_folder_dialog for Android. It uses the Android Storage Access Framework (SAF) which works with content URIs. So, open_file_dialog and select_folder_dialog return URIs, not paths like on other platforms. Also, the user will need to use the SAF for reading/writing files. The downside of this is that the user's code will be platform specific, but the advantage is that the user will actually be allowed to read from the chosen location and he also has access to all supported locations (e.g. also Cloud storage if supported by the device). If the user's code should remain cross platform, it is pretty easy to create a real file path from the returned URI and then use the standard file I/O methods.
The PR includes a generic Intent invocation method (app.invoke_intent_for_result) which can be used for invoking arbitrary Android Intents. There is a new example (example/filebrowser) which I used for testing it all. The example also shows how to use the exposed Intents of OpenIntent Filemanager. (EDIT: The filebrowser example was removed in commit b4bc066)
Important: For this example to work under Android, you need a briefcase android template which supports onActivityResult in MainActivity.java see this PR: https://github.com/beeware/briefcase-android-gradle-template/pull/25
PR Checklist:
- [x] All new features have been tested
- [ ] All new features have been documented
- [x] I have read the CONTRIBUTING.md file
- [x] I will abide by the code of conduct
I haven't manually tested this example, and I've only looked at the Android parts of it, but those Android parts are stellar! I'll let @freakboy3742 comment on the rest.
I'm hopeful to find some time to manually test this sometime, but I don't know exactly when.
The invoke_intent_for_result
method (renamed to intent_result
), was merged in #1217, but still isn't documented (#1798). It can be used to launch a native file open dialog like this:
from android.content import Intent
from java import jarray, jbyte
fileChose = Intent(Intent.ACTION_GET_CONTENT)
fileChose.addCategory(Intent.CATEGORY_OPENABLE)
fileChose.setType("*/*")
# Assuming `app` is your toga.App object
results = await app._impl.intent_result(Intent.createChooser(fileChose, "Choose a file"))
data = results['resultData'].getData()
context = app._impl.native
stream = context.getContentResolver().openInputStream(data)
def read_stream(stream):
block = jarray(jbyte)(1024 * 1024)
blocks = []
while True:
bytes_read = stream.read(block)
if bytes_read == -1:
return b"".join(blocks)
else:
blocks.append(bytes(block)[:bytes_read])
content = read_stream(stream)
thanks for your code. it helpd me!
It looks like the recommended intent for saving files is ACTION_CREATE_DOCUMENT. I don't have an example in Python, but here's one in Java: https://gist.github.com/neonankiti/05922cf0a44108a2e2732671ed9ef386.