Mercurial > libervia-backend
comparison sat/plugins/plugin_misc_android.py @ 3106:7f7cdc6ecfd8
plugin android: sound notification + change settings:
- new notification sound
- vibration and notification sound are now activated depending on ringer mode (normal,
vibrate or silent)
- notification sound/vibration can be configured in settings.
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 03 Jan 2020 13:21:27 +0100 |
parents | fb49587f55c2 |
children | b86060901278 |
comparison
equal
deleted
inserted
replaced
3105:eec0c25c796b | 3106:7f7cdc6ecfd8 |
---|---|
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. | 18 # along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 | 19 |
20 import sys | 20 import sys |
21 import os | 21 import os |
22 import os.path | 22 import os.path |
23 import tempfile | 23 from pathlib import Path |
24 from sat.core.i18n import _, D_ | 24 from sat.core.i18n import _, D_ |
25 from sat.core.constants import Const as C | 25 from sat.core.constants import Const as C |
26 from sat.core.log import getLogger | 26 from sat.core.log import getLogger |
27 from sat.core import exceptions | 27 from sat.core import exceptions |
28 from sat.memory import params | |
28 from twisted.internet import reactor | 29 from twisted.internet import reactor |
29 from twisted.internet import protocol | 30 from twisted.internet import protocol |
30 from twisted.internet import error as int_error | 31 from twisted.internet import error as int_error |
31 from twisted.web import client as web_client | |
32 | 32 |
33 log = getLogger(__name__) | 33 log = getLogger(__name__) |
34 | 34 |
35 PLUGIN_INFO = { | 35 PLUGIN_INFO = { |
36 C.PI_NAME: "Android ", | 36 C.PI_NAME: "Android ", |
56 #: delay between a pause event and sending the inactive indication to server, in seconds | 56 #: delay between a pause event and sending the inactive indication to server, in seconds |
57 #: we don't send the indication immediately because user can be just checking something | 57 #: we don't send the indication immediately because user can be just checking something |
58 #: quickly on an other app. | 58 #: quickly on an other app. |
59 CSI_DELAY = 30 | 59 CSI_DELAY = 30 |
60 | 60 |
61 PARAM_RING_CATEGORY = "Notifications" | |
62 PARAM_RING_NAME = "sound" | |
63 PARAM_RING_LABEL = D_("sound on notifications") | |
64 RING_OPTS = { | |
65 "normal": D_("Normal"), | |
66 "never": D_("Never"), | |
67 } | |
61 PARAM_VIBRATE_CATEGORY = "Notifications" | 68 PARAM_VIBRATE_CATEGORY = "Notifications" |
62 PARAM_VIBRATE_NAME = "vibrate" | 69 PARAM_VIBRATE_NAME = "vibrate" |
63 PARAM_VIBRATE_LABEL = D_("Vibrate on notifications") | 70 PARAM_VIBRATE_LABEL = D_("Vibrate on notifications") |
71 VIBRATION_OPTS = { | |
72 "always": D_("Always"), | |
73 "vibrate": D_("In vibrate mode"), | |
74 "never": D_("Never"), | |
75 } | |
64 SOCKET_DIR = "/data/data/org.salutatoi.cagou/" | 76 SOCKET_DIR = "/data/data/org.salutatoi.cagou/" |
65 SOCKET_FILE = ".socket" | 77 SOCKET_FILE = ".socket" |
66 STATE_RUNNING = b"running" | 78 STATE_RUNNING = b"running" |
67 STATE_PAUSED = b"paused" | 79 STATE_PAUSED = b"paused" |
68 STATE_STOPPED = b"stopped" | 80 STATE_STOPPED = b"stopped" |
73 NET_TYPE_OTHER = "other" | 85 NET_TYPE_OTHER = "other" |
74 | 86 |
75 | 87 |
76 Context = autoclass('android.content.Context') | 88 Context = autoclass('android.content.Context') |
77 ConnectivityManager = autoclass('android.net.ConnectivityManager') | 89 ConnectivityManager = autoclass('android.net.ConnectivityManager') |
78 | 90 MediaPlayer = autoclass('android.media.MediaPlayer') |
79 | 91 AudioManager = autoclass('android.media.AudioManager') |
80 def determineLength_workaround(self, fObj): | |
81 """Method working around seek() bug on Android""" | |
82 try: | |
83 seek = fObj.seek | |
84 tell = fObj.tell | |
85 except AttributeError: | |
86 return web_client.UNKNOWN_LENGTH | |
87 originalPosition = tell() | |
88 seek(os.SEEK_END) | |
89 end = tell() | |
90 seek(os.SEEK_SET, originalPosition) | |
91 return end - originalPosition | |
92 | |
93 | |
94 def patch_seek_bug(): | |
95 """Check seek bug and apply a workaround if still here | |
96 | |
97 cf. https://github.com/kivy/python-for-android/issues/1768 | |
98 """ | |
99 with tempfile.TemporaryFile() as f: | |
100 f.write(b'1234567890') | |
101 f.seek(0, os.SEEK_END) | |
102 size = f.tell() | |
103 if size == 10: | |
104 log.info("seek() bug not present anymore, workaround code can be removed") | |
105 else: | |
106 log.warning("seek() bug detected, applying a workaround") | |
107 web_client.FileBodyProducer._determineLength = determineLength_workaround | |
108 | |
109 patch_seek_bug() | |
110 | 92 |
111 | 93 |
112 class FrontendStateProtocol(protocol.Protocol): | 94 class FrontendStateProtocol(protocol.Protocol): |
113 | 95 |
114 def __init__(self, android_plugin): | 96 def __init__(self, android_plugin): |
135 | 117 |
136 params = """ | 118 params = """ |
137 <params> | 119 <params> |
138 <individual> | 120 <individual> |
139 <category name="{category_name}" label="{category_label}"> | 121 <category name="{category_name}" label="{category_label}"> |
140 <param name="{param_name}" label="{param_label}" value="true" type="bool" security="0" /> | 122 <param name="{ring_param_name}" label="{ring_param_label}" type="list" security="0"> |
123 {ring_options} | |
124 </param> | |
125 <param name="{vibrate_param_name}" label="{vibrate_param_label}" type="list" security="0"> | |
126 {vibrate_options} | |
127 </param> | |
141 </category> | 128 </category> |
142 </individual> | 129 </individual> |
143 </params> | 130 </params> |
144 """.format( | 131 """.format( |
145 category_name=PARAM_VIBRATE_CATEGORY, | 132 category_name=PARAM_VIBRATE_CATEGORY, |
146 category_label=D_(PARAM_VIBRATE_CATEGORY), | 133 category_label=D_(PARAM_VIBRATE_CATEGORY), |
147 param_name=PARAM_VIBRATE_NAME, | 134 vibrate_param_name=PARAM_VIBRATE_NAME, |
148 param_label=PARAM_VIBRATE_LABEL, | 135 vibrate_param_label=PARAM_VIBRATE_LABEL, |
136 vibrate_options=params.makeOptions(VIBRATION_OPTS, "always"), | |
137 ring_param_name=PARAM_RING_NAME, | |
138 ring_param_label=PARAM_RING_LABEL, | |
139 ring_options=params.makeOptions(RING_OPTS, "normal"), | |
149 ) | 140 ) |
150 | 141 |
151 def __init__(self, host): | 142 def __init__(self, host): |
152 log.info(_("plugin Android initialization")) | 143 log.info(_("plugin Android initialization")) |
153 self.host = host | 144 self.host = host |
176 raise e | 167 raise e |
177 # we set a low priority because we want the notification to be sent after all | 168 # we set a low priority because we want the notification to be sent after all |
178 # plugins have done their job | 169 # plugins have done their job |
179 host.trigger.add("MessageReceived", self.messageReceivedTrigger, priority=-1000) | 170 host.trigger.add("MessageReceived", self.messageReceivedTrigger, priority=-1000) |
180 | 171 |
172 # audio manager, to get ring status | |
173 self.am = activity.getSystemService(Context.AUDIO_SERVICE) | |
174 | |
175 # sound notification | |
176 media_dir = Path(host.memory.getConfig("", "media_dir")) | |
177 assert media_dir is not None | |
178 notif_path = media_dir / "sounds" / "notifications" / "music-box.mp3" | |
179 self.notif_player = MediaPlayer() | |
180 self.notif_player.setDataSource(str(notif_path)) | |
181 self.notif_player.setAudioStreamType(AudioManager.STREAM_NOTIFICATION) | |
182 self.notif_player.prepare() | |
183 | |
181 # Connectivity handling | 184 # Connectivity handling |
182 self.cm = activity.getSystemService(Context.CONNECTIVITY_SERVICE) | 185 self.cm = activity.getSystemService(Context.CONNECTIVITY_SERVICE) |
183 self._net_type = None | 186 self._net_type = None |
184 self._checkConnectivity() | 187 self._checkConnectivity() |
185 # XXX: we need to keep a reference to BroadcastReceiver to avoid | 188 # XXX: we need to keep a reference to BroadcastReceiver to avoid |
238 subject = next(iter(mess_data["subject"].values())) | 241 subject = next(iter(mess_data["subject"].values())) |
239 except StopIteration: | 242 except StopIteration: |
240 subject = "Cagou new message" | 243 subject = "Cagou new message" |
241 | 244 |
242 notification.notify(title=subject, message=message) | 245 notification.notify(title=subject, message=message) |
243 if self.host.memory.getParamA( | 246 |
244 PARAM_VIBRATE_NAME, PARAM_VIBRATE_CATEGORY, profile_key=client.profile | 247 ringer_mode = self.am.getRingerMode() |
245 ): | 248 vibrate_mode = ringer_mode == AudioManager.RINGER_MODE_VIBRATE |
246 try: | 249 |
247 vibrator.vibrate() | 250 ring_setting = self.host.memory.getParamA( |
248 except Exception as e: | 251 PARAM_RING_NAME, |
249 log.warning("Can't use vibrator: {e}".format(e=e)) | 252 PARAM_RING_CATEGORY, |
253 profile_key=client.profile | |
254 ) | |
255 | |
256 if ring_setting != 'never' and ringer_mode == AudioManager.RINGER_MODE_NORMAL: | |
257 self.notif_player.start() | |
258 | |
259 vibration_setting = self.host.memory.getParamA( | |
260 PARAM_VIBRATE_NAME, | |
261 PARAM_VIBRATE_CATEGORY, | |
262 profile_key=client.profile | |
263 ) | |
264 if (vibration_setting == 'always' | |
265 or vibration_setting == 'vibrate' and vibrate_mode): | |
266 try: | |
267 vibrator.vibrate() | |
268 except Exception as e: | |
269 log.warning("Can't use vibrator: {e}".format(e=e)) | |
250 return mess_data | 270 return mess_data |
251 | 271 |
252 def messageReceivedTrigger(self, client, message_elt, post_treat): | 272 def messageReceivedTrigger(self, client, message_elt, post_treat): |
253 if not self.cagou_active: | 273 if not self.cagou_active: |
254 # we only send notification is the frontend is not displayed | 274 # we only send notification is the frontend is not displayed |