Mercurial > libervia-desktop-kivy
comparison cagou/core/platform_/android.py @ 491:203755bbe0fe
massive refactoring from camelCase -> snake_case. See backend commit log for more details
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 08 Apr 2023 13:44:32 +0200 |
parents | 83c67b093350 |
children |
comparison
equal
deleted
inserted
replaced
490:962d17c4078c | 491:203755bbe0fe |
---|---|
91 | 91 |
92 activity.bind(on_new_intent=self.on_new_intent) | 92 activity.bind(on_new_intent=self.on_new_intent) |
93 self.cache.append((self.on_new_intent, mActivity.getIntent())) | 93 self.cache.append((self.on_new_intent, mActivity.getIntent())) |
94 self.last_selected_wid = None | 94 self.last_selected_wid = None |
95 self.restore_selected_wid = True | 95 self.restore_selected_wid = True |
96 host.addListener('profilePlugged', self.onProfilePlugged) | 96 host.addListener('profile_plugged', self.on_profile_plugged) |
97 host.addListener('selected', self.onSelectedWidget) | 97 host.addListener('selected', self.on_selected_widget) |
98 local_dir = Path(host.getConfig('', 'local_dir')).resolve() | 98 local_dir = Path(host.config_get('', 'local_dir')).resolve() |
99 self.tmp_dir = local_dir / 'tmp' | 99 self.tmp_dir = local_dir / 'tmp' |
100 # we assert to avoid disaster if `/ 'tmp'` is removed by mistake on the line | 100 # we assert to avoid disaster if `/ 'tmp'` is removed by mistake on the line |
101 # above | 101 # above |
102 assert self.tmp_dir.resolve() != local_dir | 102 assert self.tmp_dir.resolve() != local_dir |
103 # we reset tmp dir on each run, to be sure that there is no residual file | 103 # we reset tmp dir on each run, to be sure that there is no residual file |
104 if self.tmp_dir.exists(): | 104 if self.tmp_dir.exists(): |
105 shutil.rmtree(self.tmp_dir) | 105 shutil.rmtree(self.tmp_dir) |
106 self.tmp_dir.mkdir(0o700, parents=True) | 106 self.tmp_dir.mkdir(0o700, parents=True) |
107 | 107 |
108 def on_initFrontendState(self): | 108 def on_init_frontend_state(self): |
109 # XXX: we use a separated socket instead of bridge because if we | 109 # XXX: we use a separated socket instead of bridge because if we |
110 # try to call a bridge method in on_pause method, the call data | 110 # try to call a bridge method in on_pause method, the call data |
111 # is not written before the actual pause | 111 # is not written before the actual pause |
112 s = self._frontend_status_socket = socket.socket( | 112 s = self._frontend_status_socket = socket.socket( |
113 socket.AF_UNIX, socket.SOCK_STREAM) | 113 socket.AF_UNIX, socket.SOCK_STREAM) |
114 s.connect(os.path.join(SOCKET_DIR, SOCKET_FILE)) | 114 s.connect(os.path.join(SOCKET_DIR, SOCKET_FILE)) |
115 s.sendall(STATE_RUNNING) | 115 s.sendall(STATE_RUNNING) |
116 | 116 |
117 def profileAutoconnectGetCb(self, profile=None): | 117 def profile_autoconnect_get_cb(self, profile=None): |
118 if profile is not None: | 118 if profile is not None: |
119 G.host.options.profile = profile | 119 G.host.options.profile = profile |
120 G.host.postInit() | 120 G.host.post_init() |
121 | 121 |
122 def profileAutoconnectGetEb(self, failure_): | 122 def profile_autoconnect_get_eb(self, failure_): |
123 log.error(f"Error while getting profile to autoconnect: {failure_}") | 123 log.error(f"Error while getting profile to autoconnect: {failure_}") |
124 G.host.postInit() | 124 G.host.post_init() |
125 | 125 |
126 def _show_perm_warning(self, permissions): | 126 def _show_perm_warning(self, permissions): |
127 root_wid = G.host.app.root | 127 root_wid = G.host.app.root |
128 perm_warning = Label( | 128 perm_warning = Label( |
129 size_hint=(1, 1), | 129 size_hint=(1, 1), |
164 f"not all mandatory permissions are granted, requesting again: " | 164 f"not all mandatory permissions are granted, requesting again: " |
165 f"{perm_dict}") | 165 f"{perm_dict}") |
166 request_permissions(PERMISSION_MANDATORY, callback=self.permission_cb) | 166 request_permissions(PERMISSION_MANDATORY, callback=self.permission_cb) |
167 return | 167 return |
168 | 168 |
169 Clock.schedule_once(lambda *args: G.host.bridge.profileAutoconnectGet( | 169 Clock.schedule_once(lambda *args: G.host.bridge.profile_autoconnect_get( |
170 callback=self.profileAutoconnectGetCb, | 170 callback=self.profile_autoconnect_get_cb, |
171 errback=self.profileAutoconnectGetEb), | 171 errback=self.profile_autoconnect_get_eb), |
172 0) | 172 0) |
173 | 173 |
174 def do_postInit(self): | 174 def do_post_init(self): |
175 request_permissions(PERMISSION_MANDATORY, callback=self.permission_cb) | 175 request_permissions(PERMISSION_MANDATORY, callback=self.permission_cb) |
176 return False | 176 return False |
177 | 177 |
178 def privateDataGetCb(self, data_s, profile): | 178 def private_data_get_cb(self, data_s, profile): |
179 data = data_format.deserialise(data_s, type_check=None) | 179 data = data_format.deserialise(data_s, type_check=None) |
180 if data is not None and self.restore_selected_wid: | 180 if data is not None and self.restore_selected_wid: |
181 log.debug(f"restoring previous widget {data}") | 181 log.debug(f"restoring previous widget {data}") |
182 try: | 182 try: |
183 name = data['name'] | 183 name = data['name'] |
185 except KeyError as e: | 185 except KeyError as e: |
186 log.error(f"Bad data format for selected widget: {e}\ndata={data}") | 186 log.error(f"Bad data format for selected widget: {e}\ndata={data}") |
187 return | 187 return |
188 if target: | 188 if target: |
189 target = jid.JID(data['target']) | 189 target = jid.JID(data['target']) |
190 plugin_info = G.host.getPluginInfo(name=name) | 190 plugin_info = G.host.get_plugin_info(name=name) |
191 if plugin_info is None: | 191 if plugin_info is None: |
192 log.warning("Can't restore unknown plugin: {name}") | 192 log.warning("Can't restore unknown plugin: {name}") |
193 return | 193 return |
194 factory = plugin_info['factory'] | 194 factory = plugin_info['factory'] |
195 G.host.switchWidget( | 195 G.host.switch_widget( |
196 None, | 196 None, |
197 factory(plugin_info, target=target, profiles=[profile]) | 197 factory(plugin_info, target=target, profiles=[profile]) |
198 ) | 198 ) |
199 | 199 |
200 def onProfilePlugged(self, profile): | 200 def on_profile_plugged(self, profile): |
201 log.debug("ANDROID profilePlugged") | 201 log.debug("ANDROID profile_plugged") |
202 G.host.bridge.setParam( | 202 G.host.bridge.param_set( |
203 "autoconnect_backend", C.BOOL_TRUE, "Connection", -1, profile, | 203 "autoconnect_backend", C.BOOL_TRUE, "Connection", -1, profile, |
204 callback=lambda: log.info(f"profile {profile} autoconnection set"), | 204 callback=lambda: log.info(f"profile {profile} autoconnection set"), |
205 errback=lambda: log.error(f"can't set {profile} autoconnection")) | 205 errback=lambda: log.error(f"can't set {profile} autoconnection")) |
206 for method, *args in self.cache: | 206 for method, *args in self.cache: |
207 method(*args) | 207 method(*args) |
208 del self.cache | 208 del self.cache |
209 G.host.removeListener("profilePlugged", self.onProfilePlugged) | 209 G.host.removeListener("profile_plugged", self.on_profile_plugged) |
210 # we restore the stored widget if any | 210 # we restore the stored widget if any |
211 # user will then go back to where they was when the frontend was closed | 211 # user will then go back to where they was when the frontend was closed |
212 G.host.bridge.privateDataGet( | 212 G.host.bridge.private_data_get( |
213 "cagou", "selected_widget", profile, | 213 "cagou", "selected_widget", profile, |
214 callback=partial(self.privateDataGetCb, profile=profile), | 214 callback=partial(self.private_data_get_cb, profile=profile), |
215 errback=partial( | 215 errback=partial( |
216 G.host.errback, | 216 G.host.errback, |
217 title=_("can't get selected widget"), | 217 title=_("can't get selected widget"), |
218 message=_("error while retrieving selected widget: {msg}")) | 218 message=_("error while retrieving selected widget: {msg}")) |
219 ) | 219 ) |
220 | 220 |
221 def onSelectedWidget(self, wid): | 221 def on_selected_widget(self, wid): |
222 """Store selected widget in backend, to restore it on next startup""" | 222 """Store selected widget in backend, to restore it on next startup""" |
223 if self.last_selected_wid == None: | 223 if self.last_selected_wid == None: |
224 self.last_selected_wid = wid | 224 self.last_selected_wid = wid |
225 # we skip the first selected widget, as we'll restore stored one if possible | 225 # we skip the first selected widget, as we'll restore stored one if possible |
226 return | 226 return |
252 data = { | 252 data = { |
253 "name": plugin_info["name"], | 253 "name": plugin_info["name"], |
254 "target": target, | 254 "target": target, |
255 } | 255 } |
256 | 256 |
257 G.host.bridge.privateDataSet( | 257 G.host.bridge.private_data_set( |
258 "cagou", "selected_widget", data_format.serialise(data), profile, | 258 "cagou", "selected_widget", data_format.serialise(data), profile, |
259 errback=partial( | 259 errback=partial( |
260 G.host.errback, | 260 G.host.errback, |
261 title=_("can set selected widget"), | 261 title=_("can set selected widget"), |
262 message=_("error while setting selected widget: {msg}")) | 262 message=_("error while setting selected widget: {msg}")) |
283 share_widget.close() | 283 share_widget.close() |
284 PythonActivity.moveTaskToBack(True) | 284 PythonActivity.moveTaskToBack(True) |
285 return True | 285 return True |
286 | 286 |
287 def _disconnect(self, profile): | 287 def _disconnect(self, profile): |
288 G.host.bridge.setParam( | 288 G.host.bridge.param_set( |
289 "autoconnect_backend", C.BOOL_FALSE, "Connection", -1, profile, | 289 "autoconnect_backend", C.BOOL_FALSE, "Connection", -1, profile, |
290 callback=lambda: log.info(f"profile {profile} autoconnection unset"), | 290 callback=lambda: log.info(f"profile {profile} autoconnection unset"), |
291 errback=lambda: log.error(f"can't unset {profile} autoconnection")) | 291 errback=lambda: log.error(f"can't unset {profile} autoconnection")) |
292 G.host.profiles.unplug(profile) | 292 G.host.profiles.unplug(profile) |
293 G.host.bridge.disconnect(profile) | 293 G.host.bridge.disconnect(profile) |
294 G.host.app.showProfileManager() | 294 G.host.app.show_profile_manager() |
295 G.host.closeUI() | 295 G.host.close_ui() |
296 | 296 |
297 def _on_disconnect(self): | 297 def _on_disconnect(self): |
298 current_profile = next(iter(G.host.profiles)) | 298 current_profile = next(iter(G.host.profiles)) |
299 wid = dialog.ConfirmDialog( | 299 wid = dialog.ConfirmDialog( |
300 title=_("Are you sure to disconnect?"), | 300 title=_("Are you sure to disconnect?"), |
301 message=_( | 301 message=_( |
302 "If you disconnect the current user ({profile}), you won't receive " | 302 "If you disconnect the current user ({profile}), you won't receive " |
303 "any notification until you connect it again, is this really what you " | 303 "any notification until you connect it again, is this really what you " |
304 "want?").format(profile=current_profile), | 304 "want?").format(profile=current_profile), |
305 yes_cb=partial(self._disconnect, profile=current_profile), | 305 yes_cb=partial(self._disconnect, profile=current_profile), |
306 no_cb=G.host.closeUI, | 306 no_cb=G.host.close_ui, |
307 ) | 307 ) |
308 G.host.showExtraUI(wid) | 308 G.host.show_extra_ui(wid) |
309 | 309 |
310 def on_extra_menu_init(self, extra_menu): | 310 def on_extra_menu_init(self, extra_menu): |
311 extra_menu.addItem(_('disconnect'), self._on_disconnect) | 311 extra_menu.add_item(_('disconnect'), self._on_disconnect) |
312 | 312 |
313 def updateParamsExtra(self, extra): | 313 def update_params_extra(self, extra): |
314 # on Android, we handle autoconnection automatically, | 314 # on Android, we handle autoconnection automatically, |
315 # user must not modify those parameters | 315 # user must not modify those parameters |
316 extra.update( | 316 extra.update( |
317 { | 317 { |
318 "ignore": [ | 318 "ignore": [ |
321 ["Connection", "autodisconnect"], | 321 ["Connection", "autodisconnect"], |
322 ], | 322 ], |
323 } | 323 } |
324 ) | 324 ) |
325 | 325 |
326 def getColDataFromUri(self, uri, col_name): | 326 def get_col_data_from_uri(self, uri, col_name): |
327 cursor = mActivity.getContentResolver().query(uri, None, None, None, None) | 327 cursor = mActivity.getContentResolver().query(uri, None, None, None, None) |
328 if cursor is None: | 328 if cursor is None: |
329 return None | 329 return None |
330 try: | 330 try: |
331 cursor.moveToFirst() | 331 cursor.moveToFirst() |
334 return None | 334 return None |
335 return cursor.getString(col_idx) | 335 return cursor.getString(col_idx) |
336 finally: | 336 finally: |
337 cursor.close() | 337 cursor.close() |
338 | 338 |
339 def getFilenameFromUri(self, uri, media_type): | 339 def get_filename_from_uri(self, uri, media_type): |
340 filename = self.getColDataFromUri(uri, DISPLAY_NAME) | 340 filename = self.get_col_data_from_uri(uri, DISPLAY_NAME) |
341 if filename is None: | 341 if filename is None: |
342 uri_p = Path(uri.toString()) | 342 uri_p = Path(uri.toString()) |
343 filename = uri_p.name or "unnamed" | 343 filename = uri_p.name or "unnamed" |
344 if not uri_p.suffix and media_type: | 344 if not uri_p.suffix and media_type: |
345 suffix = mimetypes.guess_extension(media_type, strict=False) | 345 suffix = mimetypes.guess_extension(media_type, strict=False) |
346 if suffix: | 346 if suffix: |
347 filename = filename + suffix | 347 filename = filename + suffix |
348 return filename | 348 return filename |
349 | 349 |
350 def getPathFromUri(self, uri): | 350 def get_path_from_uri(self, uri): |
351 # FIXME: using DATA is not recommended (and DATA is deprecated) | 351 # FIXME: using DATA is not recommended (and DATA is deprecated) |
352 # we should read directly the file with | 352 # we should read directly the file with |
353 # ContentResolver#openFileDescriptor(Uri, String) | 353 # ContentResolver#openFileDescriptor(Uri, String) |
354 path = self.getColDataFromUri(uri, DATA) | 354 path = self.get_col_data_from_uri(uri, DATA) |
355 return uri.getPath() if path is None else path | 355 return uri.getPath() if path is None else path |
356 | 356 |
357 def on_new_intent(self, intent): | 357 def on_new_intent(self, intent): |
358 log.debug("on_new_intent") | 358 log.debug("on_new_intent") |
359 action = intent.getAction(); | 359 action = intent.getAction(); |
376 log.debug("cancelling restoration of previous widget") | 376 log.debug("cancelling restoration of previous widget") |
377 self.restore_selected_wid = False | 377 self.restore_selected_wid = False |
378 # and now we open the widget linked to the intent | 378 # and now we open the widget linked to the intent |
379 current_profile = next(iter(G.host.profiles)) | 379 current_profile = next(iter(G.host.profiles)) |
380 Clock.schedule_once( | 380 Clock.schedule_once( |
381 lambda *args: G.host.doAction( | 381 lambda *args: G.host.do_action( |
382 widget, jid.JID(target), [current_profile]), | 382 widget, jid.JID(target), [current_profile]), |
383 0) | 383 0) |
384 else: | 384 else: |
385 log.warning(f"unexpected action: {action}") | 385 log.warning(f"unexpected action: {action}") |
386 | 386 |
398 item = intent.getParcelableExtra(Intent.EXTRA_STREAM) | 398 item = intent.getParcelableExtra(Intent.EXTRA_STREAM) |
399 if item is not None: | 399 if item is not None: |
400 uri = cast('android.net.Uri', item) | 400 uri = cast('android.net.Uri', item) |
401 if uri.getScheme() == 'content': | 401 if uri.getScheme() == 'content': |
402 # Android content, we'll dump it to a temporary file | 402 # Android content, we'll dump it to a temporary file |
403 filename = self.getFilenameFromUri(uri, intent_type) | 403 filename = self.get_filename_from_uri(uri, intent_type) |
404 filepath = self.tmp_dir / filename | 404 filepath = self.tmp_dir / filename |
405 input_stream = mActivity.getContentResolver().openInputStream(uri) | 405 input_stream = mActivity.getContentResolver().openInputStream(uri) |
406 buff = bytearray(4096) | 406 buff = bytearray(4096) |
407 with open(filepath, 'wb') as f: | 407 with open(filepath, 'wb') as f: |
408 while True: | 408 while True: |
413 break | 413 break |
414 input_stream.close() | 414 input_stream.close() |
415 data['path'] = path = str(filepath) | 415 data['path'] = path = str(filepath) |
416 else: | 416 else: |
417 data['uri'] = uri.toString() | 417 data['uri'] = uri.toString() |
418 path = self.getPathFromUri(uri) | 418 path = self.get_path_from_uri(uri) |
419 if path is not None and path not in data: | 419 if path is not None and path not in data: |
420 data['path'] = path | 420 data['path'] = path |
421 else: | 421 else: |
422 uri = None | 422 uri = None |
423 path = None | 423 path = None |