# HG changeset patch # User Goffi # Date 1580845637 -3600 # Node ID c7f1176cd2a979af0b293d086433664a11b86715 # Parent eb3f622d87912b4751b0a61ab7b240572affd3ba android: handle Android specific content (wich `content:` scheme) in intent manager: when an Android content is received, it is dumped to a temporary file, and a friendly filename is used when possible. diff -r eb3f622d8791 -r c7f1176cd2a9 cagou/core/platform_/android.py --- a/cagou/core/platform_/android.py Tue Feb 04 20:47:17 2020 +0100 +++ b/cagou/core/platform_/android.py Tue Feb 04 20:47:17 2020 +0100 @@ -24,6 +24,7 @@ from urllib.parse import urlparse from pathlib import Path import shutil +import mimetypes from jnius import autoclass, cast from android import activity from sat.core.i18n import _ @@ -48,6 +49,7 @@ AudioMedia = autoclass('android.provider.MediaStore$Audio$Media') VideoMedia = autoclass('android.provider.MediaStore$Video$Media') +DISPLAY_NAME = '_display_name' DATA = '_data' @@ -186,19 +188,36 @@ } ) - def getPathFromUri(self, uri): + def getColDataFromUri(self, uri, col_name): cursor = mActivity.getContentResolver().query(uri, None, None, None, None) if cursor is None: - return uri.getPath() - else: + return None + try: cursor.moveToFirst() - # FIXME: using DATA is not recommended (and DATA is deprecated) - # we should read directly the file with - # ContentResolver#openFileDescriptor(Uri, String) - col_idx = cursor.getColumnIndex(DATA); + col_idx = cursor.getColumnIndex(col_name); if col_idx == -1: - return uri.getPath() + return None return cursor.getString(col_idx) + finally: + cursor.close() + + def getFilenameFromUri(self, uri, media_type): + filename = self.getColDataFromUri(uri, DISPLAY_NAME) + if filename is None: + uri_p = Path(uri.toString()) + filename = uri_p.name or "unnamed" + if not uri_p.suffix and media_type: + suffix = mimetypes.guess_extension(media_type, strict=False) + if suffix: + filename = filename + suffix + return filename + + def getPathFromUri(self, uri): + # FIXME: using DATA is not recommended (and DATA is deprecated) + # we should read directly the file with + # ContentResolver#openFileDescriptor(Uri, String) + path = self.getColDataFromUri(uri, DATA) + return uri.getPath() if path is None else path def on_new_intent(self, intent): log.debug("on_new_intent") @@ -235,17 +254,35 @@ text = intent.getStringExtra(Intent.EXTRA_TEXT) if text is not None: data['text'] = text + item = intent.getParcelableExtra(Intent.EXTRA_STREAM) if item is not None: uri = cast('android.net.Uri', item) - data['uri'] = uri.toString() - path = self.getPathFromUri(uri) - if path is not None: - data['path'] = path + if uri.getScheme() == 'content': + # Android content, we'll dump it to a temporary file + filename = self.getFilenameFromUri(uri, intent_type) + filepath = self.tmp_dir / filename + input_stream = mActivity.getContentResolver().openInputStream(uri) + buff = bytearray(4096) + with open(filepath, 'wb') as f: + while True: + ret = input_stream.read(buff, 0, 4096) + if ret != -1: + f.write(buff[:ret]) + else: + break + input_stream.close() + data['path'] = path = str(filepath) + else: + data['uri'] = uri.toString() + path = self.getPathFromUri(uri) + if path is not None and path not in data: + data['path'] = path else: uri = None path = None + Clock.schedule_once(lambda *args: G.host.share(intent_type, data), 0) else: text = None