diff cagou/core/platform_/android.py @ 400:71f51198478c

android: handle runtime permissions: - some mandatory permissions are requested on Cagou start, Cagou won't start at all and display a warning message if they are not granted (we request 5 times before showing the warning) - transfer plugin can now use "android_permissions" in plugin_info, to indicate what is necessary. The permissions will then be requested, and the plugin widget won't be shown if they are not granted (and a warning not will then be displayed)
author Goffi <goffi@goffi.org>
date Sun, 09 Feb 2020 23:47:29 +0100
parents a5457241c17f
children 788e05d1e2bf
line wrap: on
line diff
--- a/cagou/core/platform_/android.py	Sun Feb 09 23:47:29 2020 +0100
+++ b/cagou/core/platform_/android.py	Sun Feb 09 23:47:29 2020 +0100
@@ -27,18 +27,26 @@
 import mimetypes
 from jnius import autoclass, cast
 from android import activity
+from android.permissions import request_permissions, Permission
+from kivy.clock import Clock
+from kivy.uix.label import Label
 from sat.core.i18n import _
 from sat.core import log as logging
 from sat_frontends.tools import jid
 from cagou.core.constants import Const as C
 from cagou.core import dialog
 from cagou import G
-from kivy.clock import Clock
 from .base import Platform as BasePlatform
 
 
 log = logging.getLogger(__name__)
 
+# permission that are necessary to have Cagou running properly
+PERMISSION_MANDATORY = [
+    Permission.READ_EXTERNAL_STORAGE,
+    Permission.WRITE_EXTERNAL_STORAGE,
+]
+
 service = autoclass('org.salutatoi.cagou.ServiceBackend')
 PythonActivity = autoclass('org.kivy.android.PythonActivity')
 mActivity = PythonActivity.mActivity
@@ -109,11 +117,56 @@
         log.error(f"Error while getting profile to autoconnect: {failure_}")
         G.host.postInit()
 
-    def do_postInit(self):
-        G.host.bridge.profileAutoconnectGet(
+    def _show_perm_warning(self, permissions):
+        root_wid = G.host.app.root
+        perm_warning = Label(
+            size_hint=(1, 1),
+            text_size=(root_wid.width, root_wid.height),
+            font_size='22sp',
+            bold=True,
+            color=(0.67, 0, 0, 1),
+            halign='center',
+            valign='center',
+            text=_(
+            "Requested permissions are mandatory to run Cagou, if you don't "
+            "accept them, Cagou can't run properly. Please accept following "
+            "permissions, or set them in Android settings for Cagou:\n"
+            "{permissions}\n\nCagou will be closed in 20 s").format(
+                permissions='\n'.join(p.split('.')[-1] for p in permissions)))
+        root_wid.clear_widgets()
+        root_wid.add_widget(perm_warning)
+        Clock.schedule_once(lambda *args: G.host.app.stop(), 20)
+
+    def permission_cb(self, permissions, grant_results):
+        if not all(grant_results):
+            # we keep asking until they are accepted, as we can't run properly
+            # without them
+            # TODO: a message explaining why permission is needed should be printed
+            # TODO: the storage permission is mainly used to set download_dir, we should
+            #   be able to run Cagou without it.
+            if not hasattr(self, 'perms_counter'):
+                self.perms_counter = 0
+            self.perms_counter += 1
+            if self.perms_counter > 5:
+                Clock.schedule_once(
+                    lambda *args: self._show_perm_warning(permissions),
+                    0)
+                return
+
+            perm_dict = dict(zip(permissions, grant_results))
+            log.warning(
+                f"not all mandatory permissions are granted, requesting again: "
+                f"{perm_dict}")
+            request_permissions(PERMISSION_MANDATORY, callback=self.permission_cb)
+            return
+
+        Clock.schedule_once(lambda *args: G.host.bridge.profileAutoconnectGet(
             callback=self.profileAutoconnectGetCb,
-            errback=self.profileAutoconnectGetEb
-        )
+            errback=self.profileAutoconnectGetEb),
+            0)
+
+    def do_postInit(self):
+        request_permissions(PERMISSION_MANDATORY, callback=self.permission_cb)
         return False
 
     def onProfilePlugged(self, profile):
@@ -298,6 +351,20 @@
 
         log.debug(msg)
 
+    def check_plugin_permissions(self, plug_info, callback, errback):
+        perms = plug_info.get("android_permissons")
+        if not perms:
+            callback()
+        perms = [f"android.permission.{p}" if '.' not in p else p for p in perms]
+
+        def request_permissions_cb(permissions, granted):
+            if all(granted):
+                Clock.schedule_once(lambda *args: callback())
+            else:
+                Clock.schedule_once(lambda *args: errback())
+
+        request_permissions(perms, callback=request_permissions_cb)
+
     def open_url(self, url, wid=None):
         parsed_url = urlparse(url)
         if parsed_url.scheme == "geo":