comparison sat/plugins/plugin_misc_android.py @ 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
parents 003b8b4b56a7
children 26d6ac4e4a66
comparison
equal deleted inserted replaced
2840:0f277708e2ae 2841:90115cf4e731
15 # GNU Affero General Public License for more details. 15 # GNU Affero General Public License for more details.
16 16
17 # You should have received a copy of the GNU Affero General Public License 17 # You should have received a copy of the GNU Affero General Public License
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
21 import os
22 import os.path
20 from sat.core.i18n import _, D_ 23 from sat.core.i18n import _, D_
21 from sat.core.constants import Const as C 24 from sat.core.constants import Const as C
22 from sat.core.log import getLogger 25 from sat.core.log import getLogger
26 from sat.core import exceptions
27 from twisted.internet import reactor
28 from twisted.internet import protocol
29 from twisted.internet import error as int_error
23 30
24 log = getLogger(__name__) 31 log = getLogger(__name__)
25 from sat.core import exceptions
26 import sys
27 import mmap
28
29 32
30 PLUGIN_INFO = { 33 PLUGIN_INFO = {
31 C.PI_NAME: "Android ", 34 C.PI_NAME: "Android ",
32 C.PI_IMPORT_NAME: "android", 35 C.PI_IMPORT_NAME: "android",
33 C.PI_TYPE: C.PLUG_TYPE_MISC, 36 C.PI_TYPE: C.PLUG_TYPE_MISC,
44 from plyer import notification, vibrator 47 from plyer import notification, vibrator
45 48
46 PARAM_VIBRATE_CATEGORY = "Notifications" 49 PARAM_VIBRATE_CATEGORY = "Notifications"
47 PARAM_VIBRATE_NAME = "vibrate" 50 PARAM_VIBRATE_NAME = "vibrate"
48 PARAM_VIBRATE_LABEL = D_(u"Vibrate on notifications") 51 PARAM_VIBRATE_LABEL = D_(u"Vibrate on notifications")
52 SOCKET_DIR = "/data/data/org.salutatoi.cagou/"
53 SOCKET_FILE = ".socket"
54 STATE_RUNNING = "running"
55 STATE_PAUSED = "paused"
56 STATE_STOPPED = "stopped"
57 STATES = (STATE_RUNNING, STATE_PAUSED, STATE_STOPPED)
58
59
60 class FrontendStateProtocol(protocol.Protocol):
61
62 def __init__(self, android_plugin):
63 self.android_plugin = android_plugin
64
65 def dataReceived(self, data):
66 if data in STATES:
67 self.android_plugin.state = data
68 else:
69 log.warning(u"Unexpected data: {data}".format(data=data))
70
71
72 class FrontendStateFactory(protocol.Factory):
73
74 def __init__(self, android_plugin):
75 self.android_plugin = android_plugin
76
77 def buildProtocol(self, addr):
78 return FrontendStateProtocol(self.android_plugin)
79
49 80
50 81
51 class AndroidPlugin(object): 82 class AndroidPlugin(object):
52 83
53 params = """ 84 params = """
64 param_name=PARAM_VIBRATE_NAME, 95 param_name=PARAM_VIBRATE_NAME,
65 param_label=PARAM_VIBRATE_LABEL, 96 param_label=PARAM_VIBRATE_LABEL,
66 ) 97 )
67 98
68 def __init__(self, host): 99 def __init__(self, host):
69 log.info(_("plugin Android initialization")) 100 log.info(_(u"plugin Android initialization"))
70 self.host = host 101 self.host = host
71 host.memory.updateParams(self.params) 102 host.memory.updateParams(self.params)
72 self.cagou_status_fd = open("app/.cagou_status", "rb") 103 try:
73 self.cagou_status = mmap.mmap( 104 os.mkdir(SOCKET_DIR, 0700)
74 self.cagou_status_fd.fileno(), 1, prot=mmap.PROT_READ 105 except OSError as e:
75 ) 106 if e.errno == 17:
76 # we set a low priority because we want the notification to be sent after all plugins have done their job 107 # dir already exists
108 pass
109 else:
110 raise e
111 self._state = None
112 factory = FrontendStateFactory(self)
113 socket_path = os.path.join(SOCKET_DIR, SOCKET_FILE)
114 try:
115 reactor.listenUNIX(socket_path, factory)
116 except int_error.CannotListenError as e:
117 if e.socketError.errno == 98:
118 # the address is already in use, we need to remove it
119 os.unlink(socket_path)
120 reactor.listenUNIX(socket_path, factory)
121 else:
122 raise e
123 # we set a low priority because we want the notification to be sent after all
124 # plugins have done their job
77 host.trigger.add("MessageReceived", self.messageReceivedTrigger, priority=-1000) 125 host.trigger.add("MessageReceived", self.messageReceivedTrigger, priority=-1000)
78 126
79 @property 127 @property
128 def state(self):
129 return self._state
130
131 @state.setter
132 def state(self, new_state):
133 log.debug(u"frontend state has changed: {state}".format(state=new_state))
134 previous_state = self._state
135 self._state = new_state
136 if new_state == STATE_RUNNING:
137 self._onRunning(previous_state)
138 elif new_state == STATE_PAUSED:
139 self._onPaused(previous_state)
140 elif new_state == STATE_STOPPED:
141 self._onStopped(previous_state)
142
143 @property
80 def cagou_active(self): 144 def cagou_active(self):
81 #  'R' status means Cagou is running in front 145 return self._state == STATE_RUNNING
82 return self.cagou_status[0] == "R" 146
147 def _onRunning(self, previous_state):
148 if previous_state is not None:
149 self.host.bridge.bridgeReactivateSignals()
150
151 def _onPaused(self, previous_state):
152 self.host.bridge.bridgeDeactivateSignals()
153
154 def _onStopped(self, previous_state):
155 pass
83 156
84 def _notifyMessage(self, mess_data, client): 157 def _notifyMessage(self, mess_data, client):
85 # send notification if there is a message and it is not a groupchat 158 # send notification if there is a message and it is not a groupchat
86 if mess_data["message"] and mess_data["type"] != C.MESS_TYPE_GROUPCHAT: 159 if mess_data["message"] and mess_data["type"] != C.MESS_TYPE_GROUPCHAT:
87 message = mess_data["message"].itervalues().next() 160 message = mess_data["message"].itervalues().next()