comparison sat/plugins/plugin_misc_android.py @ 4037:524856bd7b19

massive refactoring to switch from camelCase to snake_case: historically, Libervia (SàT before) was using camelCase as allowed by PEP8 when using a pre-PEP8 code, to use the same coding style as in Twisted. However, snake_case is more readable and it's better to follow PEP8 best practices, so it has been decided to move on full snake_case. Because Libervia has a huge codebase, this ended with a ugly mix of camelCase and snake_case. To fix that, this patch does a big refactoring by renaming every function and method (including bridge) that are not coming from Twisted or Wokkel, to use fully snake_case. This is a massive change, and may result in some bugs.
author Goffi <goffi@goffi.org>
date Sat, 08 Apr 2023 13:54:42 +0200
parents 3910ad643e9d
children c23cad65ae99
comparison
equal deleted inserted replaced
4036:c4464d7ae97b 4037:524856bd7b19
141 app_context = activity.getApplication().getApplicationContext() 141 app_context = activity.getApplication().getApplicationContext()
142 notification_intent = Intent(app_context, python_act) 142 notification_intent = Intent(app_context, python_act)
143 143
144 notification_intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) 144 notification_intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
145 notification_intent.setAction(Intent.ACTION_MAIN) 145 notification_intent.setAction(Intent.ACTION_MAIN)
146 notification_intent.addCategory(Intent.CATEGORY_LAUNCHER) 146 notification_intent.add_category(Intent.CATEGORY_LAUNCHER)
147 if sat_action is not None: 147 if sat_action is not None:
148 action_data = AndroidString(json.dumps(sat_action).encode()) 148 action_data = AndroidString(json.dumps(sat_action).encode())
149 log.debug(f"adding extra {INTENT_EXTRA_ACTION} ==> {action_data}") 149 log.debug(f"adding extra {INTENT_EXTRA_ACTION} ==> {action_data}")
150 notification_intent = notification_intent.putExtra( 150 notification_intent = notification_intent.putExtra(
151 INTENT_EXTRA_ACTION, action_data) 151 INTENT_EXTRA_ACTION, action_data)
231 """.format( 231 """.format(
232 category_name=PARAM_VIBRATE_CATEGORY, 232 category_name=PARAM_VIBRATE_CATEGORY,
233 category_label=D_(PARAM_VIBRATE_CATEGORY), 233 category_label=D_(PARAM_VIBRATE_CATEGORY),
234 vibrate_param_name=PARAM_VIBRATE_NAME, 234 vibrate_param_name=PARAM_VIBRATE_NAME,
235 vibrate_param_label=PARAM_VIBRATE_LABEL, 235 vibrate_param_label=PARAM_VIBRATE_LABEL,
236 vibrate_options=params.makeOptions(VIBRATION_OPTS, "always"), 236 vibrate_options=params.make_options(VIBRATION_OPTS, "always"),
237 ring_param_name=PARAM_RING_NAME, 237 ring_param_name=PARAM_RING_NAME,
238 ring_param_label=PARAM_RING_LABEL, 238 ring_param_label=PARAM_RING_LABEL,
239 ring_options=params.makeOptions(RING_OPTS, "normal"), 239 ring_options=params.make_options(RING_OPTS, "normal"),
240 ) 240 )
241 241
242 def __init__(self, host): 242 def __init__(self, host):
243 log.info(_("plugin Android initialization")) 243 log.info(_("plugin Android initialization"))
244 log.info(f"using Android API {api_version}") 244 log.info(f"using Android API {api_version}")
245 self.host = host 245 self.host = host
246 self._csi = host.plugins.get('XEP-0352') 246 self._csi = host.plugins.get('XEP-0352')
247 self._csi_timer = None 247 self._csi_timer = None
248 host.memory.updateParams(self.params) 248 host.memory.update_params(self.params)
249 try: 249 try:
250 os.mkdir(SOCKET_DIR, 0o700) 250 os.mkdir(SOCKET_DIR, 0o700)
251 except OSError as e: 251 except OSError as e:
252 if e.errno == 17: 252 if e.errno == 17:
253 # dir already exists 253 # dir already exists
266 reactor.listenUNIX(socket_path, factory) 266 reactor.listenUNIX(socket_path, factory)
267 else: 267 else:
268 raise e 268 raise e
269 # we set a low priority because we want the notification to be sent after all 269 # we set a low priority because we want the notification to be sent after all
270 # plugins have done their job 270 # plugins have done their job
271 host.trigger.add("messageReceived", self.messageReceivedTrigger, priority=-1000) 271 host.trigger.add("messageReceived", self.message_received_trigger, priority=-1000)
272 272
273 # profiles autoconnection 273 # profiles autoconnection
274 host.bridge.addMethod( 274 host.bridge.add_method(
275 "profileAutoconnectGet", 275 "profile_autoconnect_get",
276 ".plugin", 276 ".plugin",
277 in_sign="", 277 in_sign="",
278 out_sign="s", 278 out_sign="s",
279 method=self._profileAutoconnectGet, 279 method=self._profile_autoconnect_get,
280 async_=True, 280 async_=True,
281 ) 281 )
282 282
283 # audio manager, to get ring status 283 # audio manager, to get ring status
284 self.am = activity.getSystemService(Context.AUDIO_SERVICE) 284 self.am = activity.getSystemService(Context.AUDIO_SERVICE)
285 285
286 # sound notification 286 # sound notification
287 media_dir = Path(host.memory.getConfig("", "media_dir")) 287 media_dir = Path(host.memory.config_get("", "media_dir"))
288 assert media_dir is not None 288 assert media_dir is not None
289 notif_path = media_dir / "sounds" / "notifications" / "music-box.mp3" 289 notif_path = media_dir / "sounds" / "notifications" / "music-box.mp3"
290 self.notif_player = MediaPlayer() 290 self.notif_player = MediaPlayer()
291 self.notif_player.setDataSource(str(notif_path)) 291 self.notif_player.setDataSource(str(notif_path))
292 self.notif_player.setAudioStreamType(AudioManager.STREAM_NOTIFICATION) 292 self.notif_player.setAudioStreamType(AudioManager.STREAM_NOTIFICATION)
295 # SSL fix 295 # SSL fix
296 _sslverify.platformTrust = platformTrust 296 _sslverify.platformTrust = platformTrust
297 log.info("SSL Android patch applied") 297 log.info("SSL Android patch applied")
298 298
299 # DNS fix 299 # DNS fix
300 defer.ensureDeferred(self.updateResolver()) 300 defer.ensureDeferred(self.update_resolver())
301 301
302 # Connectivity handling 302 # Connectivity handling
303 self.cm = activity.getSystemService(Context.CONNECTIVITY_SERVICE) 303 self.cm = activity.getSystemService(Context.CONNECTIVITY_SERVICE)
304 self._net_type = None 304 self._net_type = None
305 d = defer.ensureDeferred(self._checkConnectivity()) 305 d = defer.ensureDeferred(self._check_connectivity())
306 d.addErrback(host.logErrback) 306 d.addErrback(host.log_errback)
307 307
308 # XXX: we need to keep a reference to BroadcastReceiver to avoid 308 # XXX: we need to keep a reference to BroadcastReceiver to avoid
309 # "XXX has no attribute 'invoke'" error (looks like the same issue as 309 # "XXX has no attribute 'invoke'" error (looks like the same issue as
310 # https://github.com/kivy/pyjnius/issues/59) 310 # https://github.com/kivy/pyjnius/issues/59)
311 self.br = BroadcastReceiver( 311 self.br = BroadcastReceiver(
312 callback=lambda *args, **kwargs: reactor.callFromThread( 312 callback=lambda *args, **kwargs: reactor.callFromThread(
313 self.onConnectivityChange 313 self.on_connectivity_change
314 ), 314 ),
315 actions=["android.net.conn.CONNECTIVITY_CHANGE"] 315 actions=["android.net.conn.CONNECTIVITY_CHANGE"]
316 ) 316 )
317 self.br.start() 317 self.br.start()
318 318
324 def state(self, new_state): 324 def state(self, new_state):
325 log.debug(f"frontend state has changed: {new_state.decode()}") 325 log.debug(f"frontend state has changed: {new_state.decode()}")
326 previous_state = self._state 326 previous_state = self._state
327 self._state = new_state 327 self._state = new_state
328 if new_state == STATE_RUNNING: 328 if new_state == STATE_RUNNING:
329 self._onRunning(previous_state) 329 self._on_running(previous_state)
330 elif new_state == STATE_PAUSED: 330 elif new_state == STATE_PAUSED:
331 self._onPaused(previous_state) 331 self._on_paused(previous_state)
332 elif new_state == STATE_STOPPED: 332 elif new_state == STATE_STOPPED:
333 self._onStopped(previous_state) 333 self._on_stopped(previous_state)
334 334
335 @property 335 @property
336 def cagou_active(self): 336 def cagou_active(self):
337 return self._state == STATE_RUNNING 337 return self._state == STATE_RUNNING
338 338
339 def _onRunning(self, previous_state): 339 def _on_running(self, previous_state):
340 if previous_state is not None: 340 if previous_state is not None:
341 self.host.bridge.bridgeReactivateSignals() 341 self.host.bridge.bridge_reactivate_signals()
342 self.setActive() 342 self.set_active()
343 343
344 def _onPaused(self, previous_state): 344 def _on_paused(self, previous_state):
345 self.host.bridge.bridgeDeactivateSignals() 345 self.host.bridge.bridge_deactivate_signals()
346 self.setInactive() 346 self.set_inactive()
347 347
348 def _onStopped(self, previous_state): 348 def _on_stopped(self, previous_state):
349 self.setInactive() 349 self.set_inactive()
350 350
351 def _notifyMessage(self, mess_data, client): 351 def _notify_message(self, mess_data, client):
352 """Send notification when suitable 352 """Send notification when suitable
353 353
354 notification is sent if: 354 notification is sent if:
355 - there is a message and it is not a groupchat 355 - there is a message and it is not a groupchat
356 - message is not coming from ourself 356 - message is not coming from ourself
376 ) 376 )
377 377
378 ringer_mode = self.am.getRingerMode() 378 ringer_mode = self.am.getRingerMode()
379 vibrate_mode = ringer_mode == AudioManager.RINGER_MODE_VIBRATE 379 vibrate_mode = ringer_mode == AudioManager.RINGER_MODE_VIBRATE
380 380
381 ring_setting = self.host.memory.getParamA( 381 ring_setting = self.host.memory.param_get_a(
382 PARAM_RING_NAME, 382 PARAM_RING_NAME,
383 PARAM_RING_CATEGORY, 383 PARAM_RING_CATEGORY,
384 profile_key=client.profile 384 profile_key=client.profile
385 ) 385 )
386 386
387 if ring_setting != 'never' and ringer_mode == AudioManager.RINGER_MODE_NORMAL: 387 if ring_setting != 'never' and ringer_mode == AudioManager.RINGER_MODE_NORMAL:
388 self.notif_player.start() 388 self.notif_player.start()
389 389
390 vibration_setting = self.host.memory.getParamA( 390 vibration_setting = self.host.memory.param_get_a(
391 PARAM_VIBRATE_NAME, 391 PARAM_VIBRATE_NAME,
392 PARAM_VIBRATE_CATEGORY, 392 PARAM_VIBRATE_CATEGORY,
393 profile_key=client.profile 393 profile_key=client.profile
394 ) 394 )
395 if (vibration_setting == 'always' 395 if (vibration_setting == 'always'
398 vibrator.vibrate() 398 vibrator.vibrate()
399 except Exception as e: 399 except Exception as e:
400 log.warning("Can't use vibrator: {e}".format(e=e)) 400 log.warning("Can't use vibrator: {e}".format(e=e))
401 return mess_data 401 return mess_data
402 402
403 def messageReceivedTrigger(self, client, message_elt, post_treat): 403 def message_received_trigger(self, client, message_elt, post_treat):
404 if not self.cagou_active: 404 if not self.cagou_active:
405 # we only send notification is the frontend is not displayed 405 # we only send notification is the frontend is not displayed
406 post_treat.addCallback(self._notifyMessage, client) 406 post_treat.addCallback(self._notify_message, client)
407 407
408 return True 408 return True
409 409
410 # Profile autoconnection 410 # Profile autoconnection
411 411
412 def _profileAutoconnectGet(self): 412 def _profile_autoconnect_get(self):
413 return defer.ensureDeferred(self.profileAutoconnectGet()) 413 return defer.ensureDeferred(self.profile_autoconnect_get())
414 414
415 async def _getProfilesAutoconnect(self): 415 async def _get_profiles_autoconnect(self):
416 autoconnect_dict = await self.host.memory.storage.getIndParamValues( 416 autoconnect_dict = await self.host.memory.storage.get_ind_param_values(
417 category='Connection', name='autoconnect_backend', 417 category='Connection', name='autoconnect_backend',
418 ) 418 )
419 return [p for p, v in autoconnect_dict.items() if C.bool(v)] 419 return [p for p, v in autoconnect_dict.items() if C.bool(v)]
420 420
421 async def profileAutoconnectGet(self): 421 async def profile_autoconnect_get(self):
422 """Return profile to connect automatically by frontend, if any""" 422 """Return profile to connect automatically by frontend, if any"""
423 profiles_autoconnect = await self._getProfilesAutoconnect() 423 profiles_autoconnect = await self._get_profiles_autoconnect()
424 if not profiles_autoconnect: 424 if not profiles_autoconnect:
425 return None 425 return None
426 if len(profiles_autoconnect) > 1: 426 if len(profiles_autoconnect) > 1:
427 log.warning( 427 log.warning(
428 f"More that one profiles with backend autoconnection set found, picking " 428 f"More that one profiles with backend autoconnection set found, picking "
429 f"up first one (full list: {profiles_autoconnect!r})") 429 f"up first one (full list: {profiles_autoconnect!r})")
430 return profiles_autoconnect[0] 430 return profiles_autoconnect[0]
431 431
432 # CSI 432 # CSI
433 433
434 def _setInactive(self): 434 def _set_inactive(self):
435 self._csi_timer = None 435 self._csi_timer = None
436 for client in self.host.getClients(C.PROF_KEY_ALL): 436 for client in self.host.get_clients(C.PROF_KEY_ALL):
437 self._csi.setInactive(client) 437 self._csi.set_inactive(client)
438 438
439 def setInactive(self): 439 def set_inactive(self):
440 if self._csi is None or self._csi_timer is not None: 440 if self._csi is None or self._csi_timer is not None:
441 return 441 return
442 self._csi_timer = reactor.callLater(CSI_DELAY, self._setInactive) 442 self._csi_timer = reactor.callLater(CSI_DELAY, self._set_inactive)
443 443
444 def setActive(self): 444 def set_active(self):
445 if self._csi is None: 445 if self._csi is None:
446 return 446 return
447 if self._csi_timer is not None: 447 if self._csi_timer is not None:
448 self._csi_timer.cancel() 448 self._csi_timer.cancel()
449 self._csi_timer = None 449 self._csi_timer = None
450 for client in self.host.getClients(C.PROF_KEY_ALL): 450 for client in self.host.get_clients(C.PROF_KEY_ALL):
451 self._csi.setActive(client) 451 self._csi.set_active(client)
452 452
453 # Connectivity 453 # Connectivity
454 454
455 async def _handleNetworkChange(self, net_type): 455 async def _handle_network_change(self, net_type):
456 """Notify the clients about network changes. 456 """Notify the clients about network changes.
457 457
458 This way the client can disconnect/reconnect transport, or change delays 458 This way the client can disconnect/reconnect transport, or change delays
459 """ 459 """
460 log.debug(f"handling network change ({net_type})") 460 log.debug(f"handling network change ({net_type})")
461 if net_type == NET_TYPE_NONE: 461 if net_type == NET_TYPE_NONE:
462 for client in self.host.getClients(C.PROF_KEY_ALL): 462 for client in self.host.get_clients(C.PROF_KEY_ALL):
463 client.networkDisabled() 463 client.network_disabled()
464 else: 464 else:
465 # DNS servers may have changed 465 # DNS servers may have changed
466 await self.updateResolver() 466 await self.update_resolver()
467 # client may be there but disabled (e.g. with stream management) 467 # client may be there but disabled (e.g. with stream management)
468 for client in self.host.getClients(C.PROF_KEY_ALL): 468 for client in self.host.get_clients(C.PROF_KEY_ALL):
469 log.debug(f"enabling network for {client.profile}") 469 log.debug(f"enabling network for {client.profile}")
470 client.networkEnabled() 470 client.network_enabled()
471 471
472 # profiles may have been disconnected and then purged, we try 472 # profiles may have been disconnected and then purged, we try
473 # to reconnect them in case 473 # to reconnect them in case
474 profiles_autoconnect = await self._getProfilesAutoconnect() 474 profiles_autoconnect = await self._get_profiles_autoconnect()
475 for profile in profiles_autoconnect: 475 for profile in profiles_autoconnect:
476 if not self.host.isConnected(profile): 476 if not self.host.is_connected(profile):
477 log.info(f"{profile} is not connected, reconnecting it") 477 log.info(f"{profile} is not connected, reconnecting it")
478 try: 478 try:
479 await self.host.connect(profile) 479 await self.host.connect(profile)
480 except Exception as e: 480 except Exception as e:
481 log.error(f"Can't connect profile {profile}: {e}") 481 log.error(f"Can't connect profile {profile}: {e}")
482 482
483 async def _checkConnectivity(self): 483 async def _check_connectivity(self):
484 active_network = self.cm.getActiveNetworkInfo() 484 active_network = self.cm.getActiveNetworkInfo()
485 if active_network is None: 485 if active_network is None:
486 net_type = NET_TYPE_NONE 486 net_type = NET_TYPE_NONE
487 else: 487 else:
488 net_type_android = active_network.getType() 488 net_type_android = active_network.getType()
504 log.info("mobile data activated") 504 log.info("mobile data activated")
505 else: 505 else:
506 log.info("network activated (type={net_type_android})" 506 log.info("network activated (type={net_type_android})"
507 .format(net_type_android=net_type_android)) 507 .format(net_type_android=net_type_android))
508 else: 508 else:
509 log.debug("_checkConnectivity called without network change ({net_type})" 509 log.debug("_check_connectivity called without network change ({net_type})"
510 .format(net_type = net_type)) 510 .format(net_type = net_type))
511 511
512 # we always call _handleNetworkChange even if there is not connectivity change 512 # we always call _handle_network_change even if there is not connectivity change
513 # to be sure to reconnect when necessary 513 # to be sure to reconnect when necessary
514 await self._handleNetworkChange(net_type) 514 await self._handle_network_change(net_type)
515 515
516 516
517 def onConnectivityChange(self): 517 def on_connectivity_change(self):
518 log.debug("onConnectivityChange called") 518 log.debug("on_connectivity_change called")
519 d = defer.ensureDeferred(self._checkConnectivity()) 519 d = defer.ensureDeferred(self._check_connectivity())
520 d.addErrback(self.host.logErrback) 520 d.addErrback(self.host.log_errback)
521 521
522 async def updateResolver(self): 522 async def update_resolver(self):
523 # There is no "/etc/resolv.conf" on Android, which confuse Twisted and makes 523 # There is no "/etc/resolv.conf" on Android, which confuse Twisted and makes
524 # SRV record checking unusable. We fixe that by checking DNS server used, and 524 # SRV record checking unusable. We fixe that by checking DNS server used, and
525 # updating Twisted's resolver accordingly 525 # updating Twisted's resolver accordingly
526 dns_servers = await self.getDNSServers() 526 dns_servers = await self.get_dns_servers()
527 527
528 log.info( 528 log.info(
529 "Patching Twisted to use Android DNS resolver ({dns_servers})".format( 529 "Patching Twisted to use Android DNS resolver ({dns_servers})".format(
530 dns_servers=', '.join([s[0] for s in dns_servers])) 530 dns_servers=', '.join([s[0] for s in dns_servers]))
531 ) 531 )
532 dns_client.theResolver = dns_client.createResolver(servers=dns_servers) 532 dns_client.theResolver = dns_client.createResolver(servers=dns_servers)
533 533
534 async def getDNSServers(self): 534 async def get_dns_servers(self):
535 servers = [] 535 servers = []
536 536
537 if api_version < 26: 537 if api_version < 26:
538 # thanks to A-IV at https://stackoverflow.com/a/11362271 for the way to go 538 # thanks to A-IV at https://stackoverflow.com/a/11362271 for the way to go
539 log.debug("Old API, using SystemProperties to find DNS") 539 log.debug("Old API, using SystemProperties to find DNS")