Mercurial > libervia-desktop-kivy
comparison cagou/core/platform_/android.py @ 342:89799148f894
core: use classes and factory to handle platform specific behaviours in a generic way
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 04 Jan 2020 16:24:57 +0100 |
parents | e2b51663d8b8 |
children | a3cefa7158dc |
comparison
equal
deleted
inserted
replaced
341:89b17a841c2f | 342:89799148f894 |
---|---|
15 | 15 |
16 # You should have received a copy of the GNU Affero General Public License | 16 # You should have received a copy of the GNU Affero General Public License |
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. | 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. |
18 | 18 |
19 import sys | 19 import sys |
20 import os | |
21 import socket | |
20 from jnius import autoclass, cast | 22 from jnius import autoclass, cast |
21 from android import activity | 23 from android import activity |
22 from sat.core import log as logging | 24 from sat.core import log as logging |
23 from cagou.core.constants import Const as C | 25 from cagou.core.constants import Const as C |
24 from cagou import G | 26 from cagou import G |
25 from kivy.clock import Clock | 27 from kivy.clock import Clock |
28 from .base import Platform as BasePlatform | |
26 | 29 |
27 | 30 |
28 log = logging.getLogger(__name__) | 31 log = logging.getLogger(__name__) |
29 | 32 |
30 service = autoclass('org.salutatoi.cagou.ServiceBackend') | 33 service = autoclass('org.salutatoi.cagou.ServiceBackend') |
39 STATE_PAUSED = b"paused" | 42 STATE_PAUSED = b"paused" |
40 STATE_STOPPED = b"stopped" | 43 STATE_STOPPED = b"stopped" |
41 SOCKET_DIR = "/data/data/org.salutatoi.cagou/" | 44 SOCKET_DIR = "/data/data/org.salutatoi.cagou/" |
42 SOCKET_FILE = ".socket" | 45 SOCKET_FILE = ".socket" |
43 | 46 |
44 # cache for callbacks to run when profile is plugged | |
45 cache = [] | |
46 | 47 |
48 class Platform(BasePlatform): | |
47 | 49 |
48 def on_new_intent(intent): | 50 def __init__(self): |
49 log.debug("on_new_intent") | 51 super().__init__() |
50 Intent = autoclass('android.content.Intent') | 52 # cache for callbacks to run when profile is plugged |
51 action = intent.getAction(); | 53 self.cache = [] |
52 intent_type = intent.getType(); | 54 |
53 if action == "android.intent.action.SEND": | 55 def init_platform(self): |
54 # we have receiving data to share, we parse the intent data | 56 # sys.platform is "linux" on android by default |
55 # and show the share widget | 57 # so we change it to allow backend to detect android |
56 data = {} | 58 sys.platform = "android" |
57 text = intent.getStringExtra(Intent.EXTRA_TEXT) | 59 C.PLUGIN_EXT = 'pyc' |
58 if text is not None: | 60 |
59 data['text'] = text | 61 def on_app_build(self, wid): |
60 item = intent.getParcelableExtra(Intent.EXTRA_STREAM) | 62 # we don't want menu on Android |
61 if item is not None: | 63 wid.root_menus.height = 0 |
62 uri = cast('android.net.Uri', item) | 64 |
63 data['uri'] = uri.toString() | 65 def on_host_init(self, host): |
64 path = getPathFromUri(uri) | 66 argument = '' |
65 if path is not None: | 67 service.start(mActivity, argument) |
66 data['path'] = path | 68 |
69 activity.bind(on_new_intent=self.on_new_intent) | |
70 self.cache.append((self.on_new_intent, mActivity.getIntent())) | |
71 host.addListener('profilePlugged', self.onProfilePlugged) | |
72 | |
73 def on_initFrontendState(self): | |
74 # XXX: we use a separated socket instead of bridge because if we | |
75 # try to call a bridge method in on_pause method, the call data | |
76 # is not written before the actual pause | |
77 s = self._frontend_status_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | |
78 s.connect(os.path.join(SOCKET_DIR, SOCKET_FILE)) | |
79 s.sendall(STATE_RUNNING) | |
80 | |
81 def onProfilePlugged(self, profile): | |
82 log.debug("ANDROID profilePlugged") | |
83 for method, *args in self.cache: | |
84 method(*args) | |
85 del self.cache | |
86 G.host.removeListener("profilePlugged", self.onProfilePlugged) | |
87 | |
88 def on_pause(self): | |
89 G.host.sync = False | |
90 self._frontend_status_socket.sendall(STATE_PAUSED) | |
91 return True | |
92 | |
93 def on_resume(self): | |
94 self._frontend_status_socket.sendall(STATE_RUNNING) | |
95 G.host.sync = True | |
96 | |
97 def on_stop(self): | |
98 self._frontend_status_socket.sendall(STATE_STOPPED) | |
99 self._frontend_status_socket.close() | |
100 | |
101 def getPathFromUri(self, uri): | |
102 cursor = mActivity.getContentResolver().query(uri, None, None, None, None) | |
103 if cursor is None: | |
104 return uri.getPath() | |
67 else: | 105 else: |
106 cursor.moveToFirst() | |
107 # FIXME: using DATA is not recommended (and DATA is deprecated) | |
108 # we should read directly the file with | |
109 # ContentResolver#openFileDescriptor(Uri, String) | |
110 col_idx = cursor.getColumnIndex(DATA); | |
111 if col_idx == -1: | |
112 return uri.getPath() | |
113 return cursor.getString(col_idx) | |
114 | |
115 def on_new_intent(self, intent): | |
116 log.debug("on_new_intent") | |
117 Intent = autoclass('android.content.Intent') | |
118 action = intent.getAction(); | |
119 intent_type = intent.getType(); | |
120 if action == "android.intent.action.SEND": | |
121 # we have receiving data to share, we parse the intent data | |
122 # and show the share widget | |
123 data = {} | |
124 text = intent.getStringExtra(Intent.EXTRA_TEXT) | |
125 if text is not None: | |
126 data['text'] = text | |
127 item = intent.getParcelableExtra(Intent.EXTRA_STREAM) | |
128 if item is not None: | |
129 uri = cast('android.net.Uri', item) | |
130 data['uri'] = uri.toString() | |
131 path = self.getPathFromUri(uri) | |
132 if path is not None: | |
133 data['path'] = path | |
134 else: | |
135 uri = None | |
136 path = None | |
137 | |
138 Clock.schedule_once(lambda *args: G.host.share(intent_type, data), 0) | |
139 else: | |
140 text = None | |
68 uri = None | 141 uri = None |
69 path = None | 142 path = None |
70 | 143 |
71 Clock.schedule_once(lambda *args: G.host.share(intent_type, data), 0) | 144 msg = (f"NEW INTENT RECEIVED\n" |
72 else: | 145 f"type: {intent_type}\n" |
73 text = None | 146 f"action: {action}\n" |
74 uri = None | 147 f"text: {text}\n" |
75 path = None | 148 f"uri: {uri}\n" |
149 f"path: {path}") | |
76 | 150 |
77 msg = (f"NEW INTENT RECEIVED\n" | 151 log.debug(msg) |
78 f"type: {intent_type}\n" | |
79 f"action: {action}\n" | |
80 f"text: {text}\n" | |
81 f"uri: {uri}\n" | |
82 f"path: {path}") | |
83 | |
84 log.debug(msg) | |
85 | |
86 | |
87 def onProfilePlugged(profile): | |
88 log.debug("ANDROID profilePlugged") | |
89 global cache | |
90 for method, *args in cache: | |
91 method(*args) | |
92 del cache | |
93 G.host.removeListener("profilePlugged", onProfilePlugged) | |
94 | |
95 | |
96 def init_platform(): | |
97 # sys.platform is "linux" on android by default | |
98 # so we change it to allow backend to detect android | |
99 sys.platform = "android" | |
100 C.PLUGIN_EXT = 'pyc' | |
101 | |
102 | |
103 def host_init(host): | |
104 argument = '' | |
105 service.start(mActivity, argument) | |
106 | |
107 activity.bind(on_new_intent=on_new_intent) | |
108 cache.append((on_new_intent, mActivity.getIntent())) | |
109 host.addListener('profilePlugged', onProfilePlugged) | |
110 | |
111 | |
112 def getPathFromUri(uri): | |
113 cursor = mActivity.getContentResolver().query(uri, None, None, None, None) | |
114 if cursor is None: | |
115 return uri.getPath() | |
116 else: | |
117 cursor.moveToFirst() | |
118 # FIXME: using DATA is not recommended (and DATA is deprecated) | |
119 # we should read directly the file with | |
120 # ContentResolver#openFileDescriptor(Uri, String) | |
121 col_idx = cursor.getColumnIndex(DATA); | |
122 if col_idx == -1: | |
123 return uri.getPath() | |
124 return cursor.getString(col_idx) |