changeset 2841:90115cf4e731

plugin android: improved state handling: state handling is not using mmap anymore but an unix socket instead. State change can now be managed. Bridge signals are disabled when frontend is paused.
author Goffi <goffi@goffi.org>
date Sat, 09 Mar 2019 16:23:01 +0100 (2019-03-09)
parents 0f277708e2ae
children dbfedde9bc61
files sat/plugins/plugin_misc_android.py
diffstat 1 files changed, 85 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/sat/plugins/plugin_misc_android.py	Sat Mar 09 16:23:00 2019 +0100
+++ b/sat/plugins/plugin_misc_android.py	Sat Mar 09 16:23:01 2019 +0100
@@ -17,15 +17,18 @@
 # 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
+import os
+import os.path
 from sat.core.i18n import _, D_
 from sat.core.constants import Const as C
 from sat.core.log import getLogger
+from sat.core import exceptions
+from twisted.internet import reactor
+from twisted.internet import protocol
+from twisted.internet import error as int_error
 
 log = getLogger(__name__)
-from sat.core import exceptions
-import sys
-import mmap
-
 
 PLUGIN_INFO = {
     C.PI_NAME: "Android ",
@@ -46,6 +49,34 @@
 PARAM_VIBRATE_CATEGORY = "Notifications"
 PARAM_VIBRATE_NAME = "vibrate"
 PARAM_VIBRATE_LABEL = D_(u"Vibrate on notifications")
+SOCKET_DIR = "/data/data/org.salutatoi.cagou/"
+SOCKET_FILE = ".socket"
+STATE_RUNNING = "running"
+STATE_PAUSED = "paused"
+STATE_STOPPED = "stopped"
+STATES = (STATE_RUNNING, STATE_PAUSED, STATE_STOPPED)
+
+
+class FrontendStateProtocol(protocol.Protocol):
+
+    def __init__(self, android_plugin):
+        self.android_plugin = android_plugin
+
+    def dataReceived(self, data):
+        if data in STATES:
+            self.android_plugin.state = data
+        else:
+            log.warning(u"Unexpected data: {data}".format(data=data))
+
+
+class FrontendStateFactory(protocol.Factory):
+
+    def __init__(self, android_plugin):
+        self.android_plugin = android_plugin
+
+    def buildProtocol(self, addr):
+        return FrontendStateProtocol(self.android_plugin)
+
 
 
 class AndroidPlugin(object):
@@ -66,20 +97,62 @@
     )
 
     def __init__(self, host):
-        log.info(_("plugin Android initialization"))
+        log.info(_(u"plugin Android initialization"))
         self.host = host
         host.memory.updateParams(self.params)
-        self.cagou_status_fd = open("app/.cagou_status", "rb")
-        self.cagou_status = mmap.mmap(
-            self.cagou_status_fd.fileno(), 1, prot=mmap.PROT_READ
-        )
-        # we set a low priority because we want the notification to be sent after all plugins have done their job
+        try:
+            os.mkdir(SOCKET_DIR, 0700)
+        except OSError as e:
+            if e.errno == 17:
+                # dir already exists
+                pass
+            else:
+                raise e
+        self._state = None
+        factory = FrontendStateFactory(self)
+        socket_path = os.path.join(SOCKET_DIR, SOCKET_FILE)
+        try:
+            reactor.listenUNIX(socket_path, factory)
+        except int_error.CannotListenError as e:
+            if e.socketError.errno == 98:
+                # the address is already in use, we need to remove it
+                os.unlink(socket_path)
+                reactor.listenUNIX(socket_path, factory)
+            else:
+                raise e
+        # we set a low priority because we want the notification to be sent after all
+        # plugins have done their job
         host.trigger.add("MessageReceived", self.messageReceivedTrigger, priority=-1000)
 
     @property
+    def state(self):
+        return self._state
+
+    @state.setter
+    def state(self, new_state):
+        log.debug(u"frontend state has changed: {state}".format(state=new_state))
+        previous_state = self._state
+        self._state = new_state
+        if new_state == STATE_RUNNING:
+            self._onRunning(previous_state)
+        elif new_state == STATE_PAUSED:
+            self._onPaused(previous_state)
+        elif new_state == STATE_STOPPED:
+            self._onStopped(previous_state)
+
+    @property
     def cagou_active(self):
-        #  'R' status means Cagou is running in front
-        return self.cagou_status[0] == "R"
+        return self._state == STATE_RUNNING
+
+    def _onRunning(self, previous_state):
+        if previous_state is not None:
+            self.host.bridge.bridgeReactivateSignals()
+
+    def _onPaused(self, previous_state):
+        self.host.bridge.bridgeDeactivateSignals()
+
+    def _onStopped(self, previous_state):
+        pass
 
     def _notifyMessage(self, mess_data, client):
         # send notification if there is a message and it is not a groupchat