Mercurial > libervia-desktop-kivy
comparison cagou/core/platform_/android.py @ 382:c7f1176cd2a9
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.
author | Goffi <goffi@goffi.org> |
---|---|
date | Tue, 04 Feb 2020 20:47:17 +0100 |
parents | eb3f622d8791 |
children | a5457241c17f |
comparison
equal
deleted
inserted
replaced
381:eb3f622d8791 | 382:c7f1176cd2a9 |
---|---|
22 import json | 22 import json |
23 from functools import partial | 23 from functools import partial |
24 from urllib.parse import urlparse | 24 from urllib.parse import urlparse |
25 from pathlib import Path | 25 from pathlib import Path |
26 import shutil | 26 import shutil |
27 import mimetypes | |
27 from jnius import autoclass, cast | 28 from jnius import autoclass, cast |
28 from android import activity | 29 from android import activity |
29 from sat.core.i18n import _ | 30 from sat.core.i18n import _ |
30 from sat.core import log as logging | 31 from sat.core import log as logging |
31 from sat_frontends.tools import jid | 32 from sat_frontends.tools import jid |
46 Uri = autoclass('android.net.Uri') | 47 Uri = autoclass('android.net.Uri') |
47 ImagesMedia = autoclass('android.provider.MediaStore$Images$Media') | 48 ImagesMedia = autoclass('android.provider.MediaStore$Images$Media') |
48 AudioMedia = autoclass('android.provider.MediaStore$Audio$Media') | 49 AudioMedia = autoclass('android.provider.MediaStore$Audio$Media') |
49 VideoMedia = autoclass('android.provider.MediaStore$Video$Media') | 50 VideoMedia = autoclass('android.provider.MediaStore$Video$Media') |
50 | 51 |
52 DISPLAY_NAME = '_display_name' | |
51 DATA = '_data' | 53 DATA = '_data' |
52 | 54 |
53 | 55 |
54 STATE_RUNNING = b"running" | 56 STATE_RUNNING = b"running" |
55 STATE_PAUSED = b"paused" | 57 STATE_PAUSED = b"paused" |
184 ["Connection", "autodisconnect"], | 186 ["Connection", "autodisconnect"], |
185 ], | 187 ], |
186 } | 188 } |
187 ) | 189 ) |
188 | 190 |
189 def getPathFromUri(self, uri): | 191 def getColDataFromUri(self, uri, col_name): |
190 cursor = mActivity.getContentResolver().query(uri, None, None, None, None) | 192 cursor = mActivity.getContentResolver().query(uri, None, None, None, None) |
191 if cursor is None: | 193 if cursor is None: |
192 return uri.getPath() | 194 return None |
193 else: | 195 try: |
194 cursor.moveToFirst() | 196 cursor.moveToFirst() |
195 # FIXME: using DATA is not recommended (and DATA is deprecated) | 197 col_idx = cursor.getColumnIndex(col_name); |
196 # we should read directly the file with | |
197 # ContentResolver#openFileDescriptor(Uri, String) | |
198 col_idx = cursor.getColumnIndex(DATA); | |
199 if col_idx == -1: | 198 if col_idx == -1: |
200 return uri.getPath() | 199 return None |
201 return cursor.getString(col_idx) | 200 return cursor.getString(col_idx) |
201 finally: | |
202 cursor.close() | |
203 | |
204 def getFilenameFromUri(self, uri, media_type): | |
205 filename = self.getColDataFromUri(uri, DISPLAY_NAME) | |
206 if filename is None: | |
207 uri_p = Path(uri.toString()) | |
208 filename = uri_p.name or "unnamed" | |
209 if not uri_p.suffix and media_type: | |
210 suffix = mimetypes.guess_extension(media_type, strict=False) | |
211 if suffix: | |
212 filename = filename + suffix | |
213 return filename | |
214 | |
215 def getPathFromUri(self, uri): | |
216 # FIXME: using DATA is not recommended (and DATA is deprecated) | |
217 # we should read directly the file with | |
218 # ContentResolver#openFileDescriptor(Uri, String) | |
219 path = self.getColDataFromUri(uri, DATA) | |
220 return uri.getPath() if path is None else path | |
202 | 221 |
203 def on_new_intent(self, intent): | 222 def on_new_intent(self, intent): |
204 log.debug("on_new_intent") | 223 log.debug("on_new_intent") |
205 action = intent.getAction(); | 224 action = intent.getAction(); |
206 intent_type = intent.getType(); | 225 intent_type = intent.getType(); |
233 # and show the share widget | 252 # and show the share widget |
234 data = {} | 253 data = {} |
235 text = intent.getStringExtra(Intent.EXTRA_TEXT) | 254 text = intent.getStringExtra(Intent.EXTRA_TEXT) |
236 if text is not None: | 255 if text is not None: |
237 data['text'] = text | 256 data['text'] = text |
257 | |
238 item = intent.getParcelableExtra(Intent.EXTRA_STREAM) | 258 item = intent.getParcelableExtra(Intent.EXTRA_STREAM) |
239 if item is not None: | 259 if item is not None: |
240 uri = cast('android.net.Uri', item) | 260 uri = cast('android.net.Uri', item) |
241 data['uri'] = uri.toString() | 261 if uri.getScheme() == 'content': |
242 path = self.getPathFromUri(uri) | 262 # Android content, we'll dump it to a temporary file |
243 if path is not None: | 263 filename = self.getFilenameFromUri(uri, intent_type) |
244 data['path'] = path | 264 filepath = self.tmp_dir / filename |
265 input_stream = mActivity.getContentResolver().openInputStream(uri) | |
266 buff = bytearray(4096) | |
267 with open(filepath, 'wb') as f: | |
268 while True: | |
269 ret = input_stream.read(buff, 0, 4096) | |
270 if ret != -1: | |
271 f.write(buff[:ret]) | |
272 else: | |
273 break | |
274 input_stream.close() | |
275 data['path'] = path = str(filepath) | |
276 else: | |
277 data['uri'] = uri.toString() | |
278 path = self.getPathFromUri(uri) | |
279 if path is not None and path not in data: | |
280 data['path'] = path | |
245 else: | 281 else: |
246 uri = None | 282 uri = None |
247 path = None | 283 path = None |
284 | |
248 | 285 |
249 Clock.schedule_once(lambda *args: G.host.share(intent_type, data), 0) | 286 Clock.schedule_once(lambda *args: G.host.share(intent_type, data), 0) |
250 else: | 287 else: |
251 text = None | 288 text = None |
252 uri = None | 289 uri = None |