diff cagou/core/platform_/android.py @ 322:e2b51663d8b8

core, android: new share widget + added Cagou to "share" menu: - new intent filter to add Cagou to share menu for all media types - minimum Kivy version is now 1.11.0 - new "Share" widget to display data to share via SàT and select the target - new core.platform_ module (the suffix "_" avoid trouble with standard "platform" module), for platform specific code. - Android intent are now checked on startup and "on_new_intent" events - if a android.intent.action.SEND action is received (i.e. some data is shared), the "Share" widget is shown - new Cagou.share method to share data using "Share" widget - new Cagou.getAncestorWidget method to easily retrieve an instance of a specific class in a widget's ancestors - ContactList's Avatar and ContactItem widgets have been moved to core.common
author Goffi <goffi@goffi.org>
date Fri, 06 Dec 2019 13:23:03 +0100
parents
children 89799148f894
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cagou/core/platform_/android.py	Fri Dec 06 13:23:03 2019 +0100
@@ -0,0 +1,124 @@
+#!/usr/bin/env python3
+
+# Cagou: desktop/mobile frontend for Salut à Toi XMPP client
+# Copyright (C) 2016-2019 Jérôme Poisson (goffi@goffi.org)
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import sys
+from jnius import autoclass, cast
+from android import activity
+from sat.core import log as logging
+from cagou.core.constants import Const as C
+from cagou import G
+from kivy.clock import Clock
+
+
+log = logging.getLogger(__name__)
+
+service = autoclass('org.salutatoi.cagou.ServiceBackend')
+mActivity = autoclass('org.kivy.android.PythonActivity').mActivity
+ImagesMedia = autoclass('android.provider.MediaStore$Images$Media')
+AudioMedia = autoclass('android.provider.MediaStore$Audio$Media')
+VideoMedia = autoclass('android.provider.MediaStore$Video$Media')
+DATA = '_data'
+
+
+STATE_RUNNING = b"running"
+STATE_PAUSED = b"paused"
+STATE_STOPPED = b"stopped"
+SOCKET_DIR = "/data/data/org.salutatoi.cagou/"
+SOCKET_FILE = ".socket"
+
+# cache for callbacks to run when profile is plugged
+cache = []
+
+
+def on_new_intent(intent):
+    log.debug("on_new_intent")
+    Intent = autoclass('android.content.Intent')
+    action = intent.getAction();
+    intent_type = intent.getType();
+    if action == "android.intent.action.SEND":
+        # we have receiving data to share, we parse the intent data
+        # and show the share widget
+        data = {}
+        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 = getPathFromUri(uri)
+            if path is not None:
+                data['path'] = path
+        else:
+            uri = None
+            path = None
+
+        Clock.schedule_once(lambda *args: G.host.share(intent_type, data), 0)
+    else:
+        text = None
+        uri = None
+        path = None
+
+    msg = (f"NEW INTENT RECEIVED\n"
+           f"type: {intent_type}\n"
+           f"action: {action}\n"
+           f"text: {text}\n"
+           f"uri: {uri}\n"
+           f"path: {path}")
+
+    log.debug(msg)
+
+
+def onProfilePlugged(profile):
+    log.debug("ANDROID profilePlugged")
+    global cache
+    for method, *args in cache:
+        method(*args)
+    del cache
+    G.host.removeListener("profilePlugged", onProfilePlugged)
+
+
+def init_platform():
+    # sys.platform is "linux" on android by default
+    # so we change it to allow backend to detect android
+    sys.platform = "android"
+    C.PLUGIN_EXT = 'pyc'
+
+
+def host_init(host):
+    argument = ''
+    service.start(mActivity, argument)
+
+    activity.bind(on_new_intent=on_new_intent)
+    cache.append((on_new_intent, mActivity.getIntent()))
+    host.addListener('profilePlugged', onProfilePlugged)
+
+
+def getPathFromUri(uri):
+    cursor = mActivity.getContentResolver().query(uri, None, None, None, None)
+    if cursor is None:
+        return uri.getPath()
+    else:
+        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);
+        if col_idx == -1:
+            return uri.getPath()
+        return cursor.getString(col_idx)