Minor code cleanup
[theonering] / src / autogv.py
1 import logging
2
3 import telepathy
4
5 try:
6         import conic as _conic
7         conic = _conic
8 except (ImportError, OSError):
9         conic = None
10
11 try:
12         import osso as _osso
13         osso = _osso
14 except (ImportError, OSError):
15         osso = None
16
17 import constants
18 import util.coroutines as coroutines
19 import util.go_utils as gobject_utils
20 import util.tp_utils as telepathy_utils
21 import util.misc as misc_utils
22 import gvoice
23
24
25 _moduleLogger = logging.getLogger(__name__)
26
27
28 class NewGVConversations(object):
29
30         def __init__(self, connRef):
31                 self._connRef = connRef
32                 self.__callback = None
33
34         def start(self):
35                 self.__callback = coroutines.func_sink(
36                         coroutines.expand_positional(
37                                 self._on_conversations_updated
38                         )
39                 )
40                 self._connRef().session.voicemails.updateSignalHandler.register_sink(
41                         self.__callback
42                 )
43                 self._connRef().session.texts.updateSignalHandler.register_sink(
44                         self.__callback
45                 )
46
47         def stop(self):
48                 if self.__callback is None:
49                         _moduleLogger.info("New conversation monitor stopped without starting")
50                         return
51                 self._connRef().session.voicemails.updateSignalHandler.unregister_sink(
52                         self.__callback
53                 )
54                 self._connRef().session.texts.updateSignalHandler.unregister_sink(
55                         self.__callback
56                 )
57                 self.__callback = None
58
59         @misc_utils.log_exception(_moduleLogger)
60         def _on_conversations_updated(self, conv, conversationIds):
61                 _moduleLogger.debug("Incoming messages from: %r" % (conversationIds, ))
62                 for phoneNumber in conversationIds:
63                         h = self._connRef().get_handle_by_name(telepathy.HANDLE_TYPE_CONTACT, phoneNumber)
64                         # Just let the TextChannel decide whether it should be reported to the user or not
65                         props = self._connRef().generate_props(telepathy.CHANNEL_TYPE_TEXT, h, False)
66                         if self._connRef()._channel_manager.channel_exists(props):
67                                 continue
68
69                         # Maemo 4.1's RTComm opens a window for a chat regardless if a
70                         # message is received or not, so we need to do some filtering here
71                         mergedConv = conv.get_conversation(phoneNumber)
72                         newConversations = mergedConv.conversations
73                         newConversations = gvoice.conversations.filter_out_read(newConversations)
74                         newConversations = gvoice.conversations.filter_out_self(newConversations)
75                         newConversations = list(newConversations)
76                         if not newConversations:
77                                 continue
78
79                         chan = self._connRef()._channel_manager.channel_for_props(props, signal=True)
80
81
82 class RefreshVoicemail(object):
83
84         def __init__(self, connRef):
85                 self._connRef = connRef
86                 self._newChannelSignaller = telepathy_utils.NewChannelSignaller(self._on_new_channel)
87                 self._outstandingRequests = []
88                 self._isStarted = False
89
90         def start(self):
91                 self._newChannelSignaller.start()
92                 self._isStarted = True
93
94         def stop(self):
95                 if not self._isStarted:
96                         _moduleLogger.info("voicemail monitor stopped without starting")
97                         return
98                 _moduleLogger.info("Stopping voicemail refresh")
99                 self._newChannelSignaller.stop()
100
101                 # I don't want to trust whether the cancel happens within the current
102                 # callback or not which could be the deciding factor between invalid
103                 # iterators or infinite loops
104                 localRequests = [r for r in self._outstandingRequests]
105                 for request in localRequests:
106                         localRequests.cancel()
107
108                 self._isStarted = False
109
110         @misc_utils.log_exception(_moduleLogger)
111         def _on_new_channel(self, bus, serviceName, connObjectPath, channelObjectPath, channelType):
112                 if channelType != telepathy.interfaces.CHANNEL_TYPE_STREAMED_MEDIA:
113                         return
114
115                 cmName = telepathy_utils.cm_from_path(connObjectPath)
116                 if cmName == constants._telepathy_implementation_name_:
117                         _moduleLogger.debug("Ignoring channels from self to prevent deadlock")
118                         return
119
120                 conn = telepathy.client.Connection(serviceName, connObjectPath)
121                 chan = telepathy.client.Channel(serviceName, channelObjectPath)
122                 missDetection = telepathy_utils.WasMissedCall(
123                         bus, conn, chan, self._on_missed_call, self._on_error_for_missed
124                 )
125                 self._outstandingRequests.append(missDetection)
126
127         @misc_utils.log_exception(_moduleLogger)
128         def _on_missed_call(self, missDetection):
129                 _moduleLogger.info("Missed a call")
130                 self._connRef().session.voicemailsStateMachine.reset_timers()
131                 self._outstandingRequests.remove(missDetection)
132
133         @misc_utils.log_exception(_moduleLogger)
134         def _on_error_for_missed(self, missDetection, reason):
135                 _moduleLogger.debug("Error: %r claims %r" % (missDetection, reason))
136                 self._outstandingRequests.remove(missDetection)
137
138
139 class AutoDisconnect(object):
140
141         def __init__(self, connRef):
142                 self._connRef = connRef
143                 if conic is not None:
144                         self.__connection = conic.Connection()
145                 else:
146                         self.__connection = None
147
148                 self.__connectionEventId = None
149                 self.__delayedDisconnect = gobject_utils.Timeout(self._on_delayed_disconnect)
150
151         def start(self):
152                 if self.__connection is not None:
153                         self.__connectionEventId = self.__connection.connect("connection-event", self._on_connection_change)
154
155         def stop(self):
156                 self._cancel_delayed_disconnect()
157
158         @misc_utils.log_exception(_moduleLogger)
159         def _on_connection_change(self, connection, event):
160                 """
161                 @note Maemo specific
162                 """
163                 status = event.get_status()
164                 error = event.get_error()
165                 iap_id = event.get_iap_id()
166                 bearer = event.get_bearer_type()
167
168                 if status == conic.STATUS_DISCONNECTED:
169                         _moduleLogger.info("Disconnected from network, starting countdown to logoff")
170                         self.__delayedDisconnect.start(seconds=5)
171                 elif status == conic.STATUS_CONNECTED:
172                         _moduleLogger.info("Connected to network")
173                         self._cancel_delayed_disconnect()
174                 else:
175                         _moduleLogger.info("Other status: %r" % (status, ))
176
177         def _cancel_delayed_disconnect(self):
178                 _moduleLogger.info("Cancelling auto-log off")
179                 self.__delayedDisconnect.cancel()
180
181         @misc_utils.log_exception(_moduleLogger)
182         def _on_delayed_disconnect(self):
183                 if not self._connRef().session.is_logged_in():
184                         _moduleLogger.info("Received connection change event when not logged in")
185                         return
186                 try:
187                         self._connRef().disconnect(telepathy.CONNECTION_STATUS_REASON_NETWORK_ERROR)
188                 except Exception:
189                         _moduleLogger.exception("Error durring disconnect")
190
191
192 class DisconnectOnShutdown(object):
193         """
194         I'm unsure when I get notified of shutdown or if I have enough time to do
195         anything about it, but thought this might help
196         """
197
198         def __init__(self, connRef):
199                 self._connRef = connRef
200
201                 self._osso = None
202                 self._deviceState = None
203
204         def start(self):
205                 if osso is not None:
206                         self._osso = osso.Context(constants.__app_name__, constants.__version__, False)
207                         self._deviceState = osso.DeviceState(self._osso)
208                         self._deviceState.set_device_state_callback(self._on_device_state_change, 0)
209                 else:
210                         _moduleLogger.warning("No device state support")
211
212         def stop(self):
213                 try:
214                         self._deviceState.close()
215                 except AttributeError:
216                         pass # Either None or close was removed (in Fremantle)
217                 self._deviceState = None
218                 try:
219                         self._osso.close()
220                 except AttributeError:
221                         pass # Either None or close was removed (in Fremantle)
222                 self._osso = None
223
224         @misc_utils.log_exception(_moduleLogger)
225         def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData):
226                 """
227                 @note Hildon specific
228                 """
229                 try:
230                         self._connRef().disconnect(telepathy.CONNECTION_STATUS_REASON_REQUESTED)
231                 except Exception:
232                         _moduleLogger.exception("Error durring disconnect")