# HG changeset patch # User Goffi # Date 1580734185 -3600 # Node ID f2d3ab4390a3f30be277cdb4627e1b32b4e1ed6c # Parent 2c7b42f53e9a6078375f647c1b75ac3644d9c4b3 plugin android: attach an action dict to notification: instead of calling plyer's generic notify, AndroidNotification is now inherited to add a SàT specific behaviour: a "sat_action" dictionary is now attached, to indicate to the frontend what to do when the notification is opened. diff -r 2c7b42f53e9a -r f2d3ab4390a3 sat/plugins/plugin_misc_android.py --- a/sat/plugins/plugin_misc_android.py Mon Feb 03 13:46:24 2020 +0100 +++ b/sat/plugins/plugin_misc_android.py Mon Feb 03 13:49:45 2020 +0100 @@ -1,6 +1,5 @@ #!/usr/bin/env python3 - # SAT plugin for file tansfer # Copyright (C) 2009-2020 Jérôme Poisson (goffi@goffi.org) @@ -20,6 +19,7 @@ import sys import os import os.path +import json from pathlib import Path from sat.core.i18n import _, D_ from sat.core.constants import Const as C @@ -49,10 +49,23 @@ raise exceptions.CancelError("this module is not needed on this platform") -from plyer import notification, vibrator +from plyer import vibrator from plyer.platforms.android import activity +from plyer.platforms.android.notification import AndroidNotification from jnius import autoclass from android.broadcast import BroadcastReceiver +from android import python_act + + +Context = autoclass('android.content.Context') +ConnectivityManager = autoclass('android.net.ConnectivityManager') +MediaPlayer = autoclass('android.media.MediaPlayer') +AudioManager = autoclass('android.media.AudioManager') + +# notifications +AndroidString = autoclass('java.lang.String') +PendingIntent = autoclass('android.app.PendingIntent') +Intent = autoclass('android.content.Intent') #: delay between a pause event and sending the inactive indication to server, in seconds #: we don't send the indication immediately because user can be just checking something @@ -84,12 +97,70 @@ NET_TYPE_WIFI = "wifi" NET_TYPE_MOBILE = "mobile" NET_TYPE_OTHER = "other" +INTENT_EXTRA_ACTION = AndroidString("org.salut-a-toi.IntentAction") -Context = autoclass('android.content.Context') -ConnectivityManager = autoclass('android.net.ConnectivityManager') -MediaPlayer = autoclass('android.media.MediaPlayer') -AudioManager = autoclass('android.media.AudioManager') +class Notification(AndroidNotification): + # We extend plyer's AndroidNotification instead of creating directly with jnius + # because it already handles issues like backward compatibility, and we just want to + # slightly modify the behaviour. + + @staticmethod + def _set_open_behavior(notification, sat_action): + # we reproduce plyer's AndroidNotification._set_open_behavior + # bu we add SàT specific extra action data + + app_context = activity.getApplication().getApplicationContext() + notification_intent = Intent(app_context, python_act) + + notification_intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) + notification_intent.setAction(Intent.ACTION_MAIN) + notification_intent.addCategory(Intent.CATEGORY_LAUNCHER) + if sat_action is not None: + action_data = AndroidString(json.dumps(sat_action).encode()) + log.debug(f"adding extra {INTENT_EXTRA_ACTION} ==> {action_data}") + notification_intent = notification_intent.putExtra( + INTENT_EXTRA_ACTION, action_data) + + # we use PendingIntent.FLAG_UPDATE_CURRENT here, otherwise extra won't be set + # in the new intent (the old ACTION_MAIN intent will be reused). This differs + # from plyers original behaviour which set no flag here + pending_intent = PendingIntent.getActivity( + app_context, 0, notification_intent, PendingIntent.FLAG_UPDATE_CURRENT + ) + + notification.setContentIntent(pending_intent) + notification.setAutoCancel(True) + + def _notify(self, **kwargs): + # we reproduce plyer's AndroidNotification._notify behaviour here + # and we add handling of "sat_action" attribute (SàT specific). + # we also set, where suitable, default values to empty string instead of + # original None, as a string is expected (in plyer the empty string is used + # in the generic "notify" method). + sat_action = kwargs.pop("sat_action", None) + noti = None + message = kwargs.get('message', '').encode('utf-8') + ticker = kwargs.get('ticker', '').encode('utf-8') + title = AndroidString( + kwargs.get('title', '').encode('utf-8') + ) + icon = kwargs.get('app_icon', '') + + if kwargs.get('toast', False): + self._toast(message) + return + else: + noti = self._build_notification(title) + + noti.setContentTitle(title) + noti.setContentText(AndroidString(message)) + noti.setTicker(AndroidString(ticker)) + + self._set_icons(noti, icon=icon) + self._set_open_behavior(noti, sat_action) + + self._open_notification(noti) class FrontendStateProtocol(protocol.Protocol): @@ -252,9 +323,19 @@ try: subject = next(iter(mess_data["subject"].values())) except StopIteration: - subject = "Cagou new message" + subject = D_("new message from {contact}").format( + contact = mess_data['from']) - notification.notify(title=subject, message=message) + notification = Notification() + notification._notify( + title=subject, + message=message, + sat_action={ + "type": "open", + "widget": "chat", + "target": mess_data["from"].userhost(), + }, + ) ringer_mode = self.am.getRingerMode() vibrate_mode = ringer_mode == AudioManager.RINGER_MODE_VIBRATE