Daniel Brötzmann pushed to branch master at gajim / gajim

Commits:

9 changed files:

Changes:

  • gajim/common/app.py
    ... ... @@ -223,12 +223,11 @@ def detect_dependencies() -> None:
    223 223
             gi.require_version('Farstream', '0.2')
    
    224 224
             from gi.repository import Farstream
    
    225 225
             _dependencies['FARSTREAM'] = True
    
    226
    -    except Exception:
    
    227
    -        pass
    
    226
    +    except Exception as error:
    
    227
    +        log('gajim').warning('AV dependency test failed: %s', error)
    
    228 228
     
    
    229 229
         try:
    
    230 230
             if _dependencies['GST'] and _dependencies['FARSTREAM']:
    
    231
    -            Gst.init(None)
    
    232 231
                 conference = Gst.ElementFactory.make('fsrtpconference', None)
    
    233 232
                 conference.new_session(Farstream.MediaType.AUDIO)
    
    234 233
                 from gajim.gui.gstreamer import create_gtk_widget
    
    ... ... @@ -412,7 +411,7 @@ def get_enabled_accounts_with_labels(
    412 411
         Returns a list with [account, account_label] entries.
    
    413 412
         Order by account_label
    
    414 413
         """
    
    415
    -    accounts = []
    
    414
    +    accounts: list[list[str]] = []
    
    416 415
         for acc in connections:
    
    417 416
             if exclude_local and account_is_zeroconf(acc):
    
    418 417
                 continue
    

  • gajim/common/jingle_rtp.py
    ... ... @@ -155,7 +155,7 @@ def _next_dtmf(self, events):
    155 155
             else:
    
    156 156
                 self._dtmf_running = False
    
    157 157
     
    
    158
    -    def start_dtmf(self, key):
    
    158
    +    def start_dtmf(self, key: str) -> None:
    
    159 159
             if key == 'star':
    
    160 160
                 event = Farstream.DTMFEvent.STAR
    
    161 161
             elif key == 'pound':
    
    ... ... @@ -344,13 +344,13 @@ def __init__(self, session, transport=None):
    344 344
             JingleRTPContent.__init__(self, session, 'audio', transport)
    
    345 345
             self.setup_stream()
    
    346 346
     
    
    347
    -    def set_mic_volume(self, vol):
    
    347
    +    def set_mic_volume(self, vol: float) -> None:
    
    348 348
             """
    
    349 349
             vol must be between 0 and 1
    
    350 350
             """
    
    351 351
             self.mic_volume.set_property('volume', vol)
    
    352 352
     
    
    353
    -    def set_out_volume(self, vol):
    
    353
    +    def set_out_volume(self, vol: float) -> None:
    
    354 354
             """
    
    355 355
             vol must be between 0 and 1
    
    356 356
             """
    

  • gajim/common/jingle_session.py
    ... ... @@ -27,6 +27,10 @@
    27 27
     #   - Tie-breaking
    
    28 28
     # * timeout
    
    29 29
     
    
    30
    +from __future__ import annotations
    
    31
    +
    
    32
    +from typing import Optional
    
    33
    +
    
    30 34
     import time
    
    31 35
     import logging
    
    32 36
     from enum import Enum, unique
    
    ... ... @@ -38,11 +42,13 @@
    38 42
     
    
    39 43
     from gajim.common import app
    
    40 44
     from gajim.common import events
    
    45
    +from gajim.common.client import Client
    
    41 46
     from gajim.common.const import KindConstant
    
    42 47
     from gajim.common.helpers import AdditionalDataDict
    
    43 48
     from gajim.common.jingle_transport import get_jingle_transport
    
    44 49
     from gajim.common.jingle_transport import JingleTransportIBB
    
    45 50
     from gajim.common.jingle_content import get_jingle_content
    
    51
    +from gajim.common.jingle_content import JingleContent
    
    46 52
     from gajim.common.jingle_content import JingleContentSetupException
    
    47 53
     from gajim.common.jingle_ft import State
    
    48 54
     from gajim.common.file_props import FilesProp
    
    ... ... @@ -93,14 +99,21 @@ class JingleSession:
    93 99
         negotiated between an initiator and a responder.
    
    94 100
         """
    
    95 101
     
    
    96
    -    def __init__(self, con, weinitiate, jid, iq_id=None, sid=None,
    
    97
    -                 werequest=False):
    
    102
    +    def __init__(self,
    
    103
    +                 con: Client,
    
    104
    +                 weinitiate: bool,
    
    105
    +                 jid: str,
    
    106
    +                 iq_id: Optional[str] = None,
    
    107
    +                 sid: Optional[str] = None,
    
    108
    +                 werequest: bool = False
    
    109
    +                 ) -> None:
    
    98 110
             """
    
    99 111
             con -- connection object,
    
    100 112
             weinitiate -- boolean, are we the initiator?
    
    101 113
             jid - jid of the other entity
    
    102 114
             """
    
    103
    -        self.contents = {} # negotiated contents
    
    115
    +        # negotiated contents
    
    116
    +        self.contents: dict[tuple[str, str], JingleContent] = {}
    
    104 117
             self.connection = con # connection to use
    
    105 118
             # our full jid
    
    106 119
             self.ourjid = str(self.connection.get_own_jid())
    
    ... ... @@ -188,13 +201,13 @@ def cancel_session(self):
    188 201
             reason.addChild('cancel')
    
    189 202
             self._session_terminate(reason)
    
    190 203
     
    
    191
    -    def approve_content(self, media, name=None):
    
    204
    +    def approve_content(self, media: str, name: Optional[str] = None) -> None:
    
    192 205
             content = self.get_content(media, name)
    
    193 206
             if content:
    
    194 207
                 content.accepted = True
    
    195 208
                 self.on_session_state_changed(content)
    
    196 209
     
    
    197
    -    def reject_content(self, media, name=None):
    
    210
    +    def reject_content(self, media: str, name: Optional[str] = None) -> None:
    
    198 211
             content = self.get_content(media, name)
    
    199 212
             if content:
    
    200 213
                 if self.state == JingleStates.ACTIVE:
    
    ... ... @@ -213,15 +226,23 @@ def end_session(self):
    213 226
                 reason.addChild('cancel')
    
    214 227
             self._session_terminate(reason)
    
    215 228
     
    
    216
    -    def get_content(self, media=None, name=None):
    
    229
    +    def get_content(self,
    
    230
    +                    media: Optional[str] = None,
    
    231
    +                    name: Optional[str] = None
    
    232
    +                    ) -> Optional[JingleContent]:
    
    217 233
             if media is None:
    
    218
    -            return
    
    234
    +            return None
    
    219 235
             for content in self.contents.values():
    
    220 236
                 if content.media == media:
    
    221 237
                     if name is None or content.name == name:
    
    222 238
                         return content
    
    239
    +        return None
    
    223 240
     
    
    224
    -    def add_content(self, name, content, creator='we'):
    
    241
    +    def add_content(self,
    
    242
    +                    name: str,
    
    243
    +                    content: JingleContent,
    
    244
    +                    creator: str = 'we'
    
    245
    +                    ) -> None:
    
    225 246
             """
    
    226 247
             Add new content to session. If the session is active, this will send
    
    227 248
             proper stanza to update session
    
    ... ... @@ -268,7 +289,9 @@ def modify_content(self, creator, name, transport=None):
    268 289
             content.sent = False
    
    269 290
             content.accepted = True
    
    270 291
     
    
    271
    -    def on_session_state_changed(self, content=None):
    
    292
    +    def on_session_state_changed(self,
    
    293
    +                                 content: Optional[JingleContent] = None
    
    294
    +                                 ) -> None:
    
    272 295
             if self.state == JingleStates.ENDED:
    
    273 296
                 # Session not yet started, only one action possible: session-initiate
    
    274 297
                 if self.is_ready() and self.weinitiate:
    
    ... ... @@ -825,7 +848,7 @@ def _session_terminate(self, reason=None):
    825 848
                               media=None,
    
    826 849
                               reason=text)
    
    827 850
     
    
    828
    -    def __content_add(self, content):
    
    851
    +    def __content_add(self, content: JingleContent) -> None:
    
    829 852
             # TODO: test
    
    830 853
             assert self.state != JingleStates.ENDED
    
    831 854
             stanza, jingle = self.__make_jingle('content-add')
    
    ... ... @@ -834,7 +857,7 @@ def __content_add(self, content):
    834 857
             id_ = self.connection.connection.send(stanza)
    
    835 858
             self.collect_iq_id(id_)
    
    836 859
     
    
    837
    -    def __content_accept(self, content):
    
    860
    +    def __content_accept(self, content: JingleContent) -> None:
    
    838 861
             # TODO: test
    
    839 862
             assert self.state != JingleStates.ENDED
    
    840 863
             stanza, jingle = self.__make_jingle('content-accept')
    
    ... ... @@ -843,7 +866,7 @@ def __content_accept(self, content):
    843 866
             id_ = self.connection.connection.send(stanza)
    
    844 867
             self.collect_iq_id(id_)
    
    845 868
     
    
    846
    -    def __content_reject(self, content):
    
    869
    +    def __content_reject(self, content: JingleContent) -> None:
    
    847 870
             assert self.state != JingleStates.ENDED
    
    848 871
             stanza, jingle = self.__make_jingle('content-reject')
    
    849 872
             self.__append_content(jingle, content)
    

  • gajim/gtk/call_widget.py
    ... ... @@ -14,8 +14,7 @@
    14 14
     
    
    15 15
     from __future__ import annotations
    
    16 16
     
    
    17
    -from typing import Any
    
    18
    -from typing import List
    
    17
    +from typing import Callable
    
    19 18
     from typing import Optional
    
    20 19
     
    
    21 20
     import logging
    
    ... ... @@ -29,14 +28,20 @@
    29 28
     
    
    30 29
     from gajim.common import app
    
    31 30
     from gajim.common import types
    
    31
    +from gajim.common import sound
    
    32 32
     from gajim.common.const import JingleState
    
    33 33
     from gajim.common.const import KindConstant
    
    34
    +from gajim.common.events import JingleEvent
    
    35
    +from gajim.common.events import JingleConnectedReceived
    
    36
    +from gajim.common.events import JingleDisconnectedReceived
    
    37
    +from gajim.common.events import JingleErrorReceived
    
    38
    +from gajim.common.events import JingleRequestReceived
    
    39
    +from gajim.common.events import Notification
    
    34 40
     from gajim.common.helpers import AdditionalDataDict
    
    41
    +from gajim.common.helpers import play_sound
    
    35 42
     from gajim.common.i18n import _
    
    36 43
     from gajim.common.jingle_rtp import JingleAudio
    
    37
    -from gajim.common.events import Notification
    
    38
    -from gajim.common.helpers import play_sound
    
    39
    -from gajim.common import sound
    
    44
    +from gajim.common.jingle_session import JingleSession
    
    40 45
     
    
    41 46
     from .gstreamer import create_gtk_widget
    
    42 47
     from .builder import get_builder
    
    ... ... @@ -69,9 +74,9 @@ def __init__(self, account: str, contact: types.BareContact) -> None:
    69 74
     
    
    70 75
             # Helper for upgrading voice call to voice + video without
    
    71 76
             # having to show a new call row
    
    72
    -        self._incoming_video_event = None
    
    77
    +        self._incoming_video_event: Optional[JingleRequestReceived] = None
    
    73 78
     
    
    74
    -        self._jingle: dict(str, JingleObject) = {
    
    79
    +        self._jingle: dict[str, JingleObject] = {
    
    75 80
                 'audio': JingleObject(
    
    76 81
                     JingleState.NULL,
    
    77 82
                     self._update_audio),
    
    ... ... @@ -88,7 +93,7 @@ def __init__(self, account: str, contact: types.BareContact) -> None:
    88 93
     
    
    89 94
             self._ui.connect_signals(self)
    
    90 95
     
    
    91
    -    def _on_destroy(self, *args: Any) -> None:
    
    96
    +    def _on_destroy(self, _widget: Gtk.Widget) -> None:
    
    92 97
             for jingle_type in ('audio', 'video'):
    
    93 98
                 self._close_jingle_content(jingle_type, shutdown=True)
    
    94 99
             self._jingle.clear()
    
    ... ... @@ -121,7 +126,8 @@ def _on_call_with_mic_and_cam(self, _button: Gtk.Button) -> None:
    121 126
             self._ui.av_start_box.hide()
    
    122 127
     
    
    123 128
         def _on_answer_video_clicked(self, button: Gtk.Button) -> None:
    
    124
    -        self.accept_call(self._incoming_video_event)
    
    129
    +        assert self._incoming_video_event is not None
    
    130
    +        self.accept_call(self._incoming_video_event.jingle_session)
    
    125 131
             self._incoming_video_event = None
    
    126 132
             button.hide()
    
    127 133
     
    
    ... ... @@ -137,7 +143,7 @@ def _on_end_call_clicked(self, _button: Gtk.Button) -> None:
    137 143
         def _on_video(self, _button: Gtk.Button) -> None:
    
    138 144
             self._on_jingle_button_toggled(['video'])
    
    139 145
     
    
    140
    -    def _on_jingle_button_toggled(self, jingle_types: List[str]) -> None:
    
    146
    +    def _on_jingle_button_toggled(self, jingle_types: list[str]) -> None:
    
    141 147
             call_resources = self._get_call_resources()
    
    142 148
             if not call_resources:
    
    143 149
                 return
    
    ... ... @@ -185,40 +191,48 @@ def _on_num_button_press(self,
    185 191
                                  ) -> None:
    
    186 192
             button_id = Gtk.Buildable.get_name(button)
    
    187 193
             key = button_id.split('_')[1]
    
    188
    -        self._get_audio_content().start_dtmf(key)
    
    194
    +        content = self._get_audio_content()
    
    195
    +        if content is not None:
    
    196
    +            content.start_dtmf(key)
    
    189 197
     
    
    190 198
         def _on_num_button_release(self,
    
    191 199
                                    _button: Gtk.Button,
    
    192 200
                                    _event: Gdk.EventButton
    
    193 201
                                    ) -> None:
    
    194
    -        self._get_audio_content().stop_dtmf()
    
    202
    +        content = self._get_audio_content()
    
    203
    +        if content is not None:
    
    204
    +            content.stop_dtmf()
    
    195 205
     
    
    196 206
         def _on_mic_volume_changed(self,
    
    197 207
                                    _button: Gtk.VolumeButton,
    
    198 208
                                    value: float
    
    199 209
                                    ) -> None:
    
    200
    -        self._get_audio_content().set_mic_volume(value / 100)
    
    201
    -        app.settings.set('audio_input_volume', int(value))
    
    210
    +        content = self._get_audio_content()
    
    211
    +        if content is not None:
    
    212
    +            content.set_mic_volume(value / 100)
    
    213
    +            app.settings.set('audio_input_volume', int(value))
    
    202 214
     
    
    203 215
         def _on_output_volume_changed(self,
    
    204 216
                                       _button: Gtk.VolumeButton,
    
    205 217
                                       value: float
    
    206 218
                                       ) -> None:
    
    207
    -        self._get_audio_content().set_out_volume(value / 100)
    
    208
    -        app.settings.set('audio_output_volume', int(value))
    
    219
    +        content = self._get_audio_content()
    
    220
    +        if content is not None:
    
    221
    +            content.set_out_volume(value / 100)
    
    222
    +            app.settings.set('audio_output_volume', int(value))
    
    209 223
     
    
    210
    -    def process_event(self, event):
    
    211
    -        if event.name == 'jingle-request-received':
    
    224
    +    def process_event(self, event: JingleEvent) -> None:
    
    225
    +        if isinstance(event, JingleRequestReceived):
    
    212 226
                 self._on_jingle_request(event)
    
    213
    -        if event.name == 'jingle-connected-received':
    
    227
    +        if isinstance(event, JingleConnectedReceived):
    
    214 228
                 self._on_jingle_connected(event)
    
    215
    -        if event.name == 'jingle-disconnected-received':
    
    229
    +        if isinstance(event, JingleDisconnectedReceived):
    
    216 230
                 self._on_jingle_disconnected(event)
    
    217
    -        if event.name == 'jingle-error':
    
    231
    +        if isinstance(event, JingleErrorReceived):
    
    218 232
                 self._on_jingle_error(event)
    
    219 233
     
    
    220
    -    def _on_jingle_request(self, event):
    
    221
    -        content_types: list(str) = []
    
    234
    +    def _on_jingle_request(self, event: JingleRequestReceived) -> None:
    
    235
    +        content_types: list[str] = []
    
    222 236
             for item in event.contents:
    
    223 237
                 content_types.append(item.media)
    
    224 238
             if not any(item in ('audio', 'video') for item in content_types):
    
    ... ... @@ -251,7 +265,7 @@ def _on_jingle_request(self, event):
    251 265
                                  title=_('Incoming Call'),
    
    252 266
                                  text=_('%s is calling') % self._contact.name))
    
    253 267
     
    
    254
    -    def _on_jingle_connected(self, event):
    
    268
    +    def _on_jingle_connected(self, event: JingleConnectedReceived) -> None:
    
    255 269
             session = self._client.get_module('Jingle').get_jingle_session(
    
    256 270
                 event.fjid, event.sid)
    
    257 271
     
    
    ... ... @@ -272,9 +286,11 @@ def _on_jingle_connected(self, event):
    272 286
                 session.approve_session()
    
    273 287
             for content in event.media:
    
    274 288
                 session.approve_content(content)
    
    275
    -        sound.stop() # dialing/ringing
    
    289
    +        sound.stop()
    
    276 290
     
    
    277
    -    def _on_jingle_disconnected(self, event):
    
    291
    +    def _on_jingle_disconnected(self,
    
    292
    +                                event: JingleDisconnectedReceived
    
    293
    +                                ) -> None:
    
    278 294
             if event.media is None:
    
    279 295
                 self._stop_jingle(sid=event.sid, reason=event.reason)
    
    280 296
             if event.media == 'audio':
    
    ... ... @@ -289,15 +305,15 @@ def _on_jingle_disconnected(self, event):
    289 305
                     JingleState.NULL,
    
    290 306
                     sid=event.sid,
    
    291 307
                     reason=event.reason)
    
    292
    -        sound.stop() # dialing/ringing
    
    308
    +        sound.stop()
    
    293 309
     
    
    294
    -    def _on_jingle_error(self, event):
    
    310
    +    def _on_jingle_error(self, event: JingleErrorReceived) -> None:
    
    295 311
             if event.sid == self._jingle['audio'].sid:
    
    296 312
                 self._set_jingle_state(
    
    297 313
                     'audio',
    
    298 314
                     JingleState.ERROR,
    
    299 315
                     reason=event.reason)
    
    300
    -        sound.stop() # dialing/ringing
    
    316
    +        sound.stop()
    
    301 317
     
    
    302 318
         def start_call(self) -> None:
    
    303 319
             audio_state = self._jingle['audio'].state
    
    ... ... @@ -311,8 +327,8 @@ def start_call(self) -> None:
    311 327
                     self._jingle['video'].available)
    
    312 328
                 self._ui.av_cam_button.set_sensitive(False)
    
    313 329
     
    
    314
    -    def accept_call(self, session):
    
    315
    -        sound.stop() # dialing/ringing
    
    330
    +    def accept_call(self, session: JingleSession) -> None:
    
    331
    +        sound.stop()
    
    316 332
     
    
    317 333
             if not session:
    
    318 334
                 return
    
    ... ... @@ -336,8 +352,8 @@ def accept_call(self, session):
    336 352
                 session.approve_content('video')
    
    337 353
     
    
    338 354
         @staticmethod
    
    339
    -    def decline_call(session):
    
    340
    -        sound.stop() # dialing/ringing
    
    355
    +    def decline_call(session: JingleSession) -> None:
    
    356
    +        sound.stop()
    
    341 357
     
    
    342 358
             if not session:
    
    343 359
                 return
    
    ... ... @@ -473,6 +489,10 @@ def _update_video(self) -> None:
    473 489
     
    
    474 490
                 other_gtk_widget = create_gtk_widget()
    
    475 491
                 self_gtk_widget = create_gtk_widget()
    
    492
    +            if other_gtk_widget is None or self_gtk_widget is None:
    
    493
    +                log.warning('Could not create GStreamer widgets')
    
    494
    +                return
    
    495
    +
    
    476 496
                 sink_other, self._video_widget_other, _name = other_gtk_widget
    
    477 497
                 sink_self, self._video_widget_self, _name = self_gtk_widget
    
    478 498
                 self._ui.incoming_viewport.add(self._video_widget_other)
    
    ... ... @@ -498,8 +518,12 @@ def _update_video(self) -> None:
    498 518
                 self._ui.jingle_connection_spinner.stop()
    
    499 519
                 self._ui.jingle_connection_spinner.hide()
    
    500 520
     
    
    501
    -    def _set_jingle_state(self, jingle_type: str, state: str, sid: str = None,
    
    502
    -                          reason: str = None) -> None:
    
    521
    +    def _set_jingle_state(self,
    
    522
    +                          jingle_type: str,
    
    523
    +                          state: JingleState,
    
    524
    +                          sid: Optional[str] = None,
    
    525
    +                          reason: Optional[str] = None
    
    526
    +                          ) -> None:
    
    503 527
             jingle = self._jingle[jingle_type]
    
    504 528
             if state in (
    
    505 529
                     JingleState.CONNECTING,
    
    ... ... @@ -529,7 +553,7 @@ def _set_jingle_state(self, jingle_type: str, state: str, sid: str = None,
    529 553
     
    
    530 554
         def _stop_jingle(self,
    
    531 555
                          sid: Optional[str] = None,
    
    532
    -                     reason: Optional[str] = None
    
    556
    +                     _reason: Optional[str] = None
    
    533 557
                          ) -> None:
    
    534 558
             audio_sid = self._jingle['audio'].sid
    
    535 559
             video_sid = self._jingle['video'].sid
    
    ... ... @@ -563,18 +587,22 @@ def _get_audio_content(self) -> Optional[JingleAudio]:
    563 587
                 self._contact.jid, self._jingle['audio'].sid)
    
    564 588
             return session.get_content('audio')
    
    565 589
     
    
    566
    -    def _get_call_resources(self) -> List[types.ResourceContact]:
    
    567
    -        resource_list = []
    
    590
    +    def _get_call_resources(self) -> list[types.ResourceContact]:
    
    591
    +        resource_list: list[types.ResourceContact] = []
    
    568 592
             for resource_contact in self._contact.iter_resources():
    
    569 593
                 if resource_contact.supports(Namespace.JINGLE_RTP):
    
    570 594
                     resource_list.append(resource_contact)
    
    571 595
             return resource_list
    
    572 596
     
    
    597
    +
    
    573 598
     class JingleObject:
    
    574 599
         __slots__ = ('sid', 'state', 'available', 'update')
    
    575 600
     
    
    576
    -    def __init__(self, state, update):
    
    577
    -        self.sid = None
    
    601
    +    def __init__(self,
    
    602
    +                 state: JingleState,
    
    603
    +                 update: Callable[..., None]
    
    604
    +                 ) -> None:
    
    605
    +        self.sid: Optional[str] = None
    
    578 606
             self.state = state
    
    579
    -        self.available = False
    
    607
    +        self.available: bool = False
    
    580 608
             self.update = update

  • gajim/gtk/contacts_flowbox.py
    ... ... @@ -11,7 +11,9 @@
    11 11
     # You should have received a copy of the GNU General Public License
    
    12 12
     # along with Gajim. If not, see <http://www.gnu.org/licenses/>.
    
    13 13
     
    
    14
    -from typing import List
    
    14
    +from __future__ import annotations
    
    15
    +
    
    16
    +from typing import cast
    
    15 17
     
    
    16 18
     from gi.repository import GObject
    
    17 19
     from gi.repository import Gtk
    
    ... ... @@ -69,7 +71,8 @@ def __init__(self, account: str, jid: str, is_new: bool = False) -> None:
    69 71
             self.show_all()
    
    70 72
     
    
    71 73
         def _on_remove(self, _button: Gtk.Button) -> None:
    
    72
    -        self.get_parent().on_contact_removed(self)
    
    74
    +        flow_box = cast(ContactsFlowBox, self.get_parent())
    
    75
    +        flow_box.on_contact_removed(self)
    
    73 76
     
    
    74 77
     
    
    75 78
     class ContactsFlowBox(Gtk.FlowBox):
    
    ... ... @@ -91,7 +94,7 @@ def __init__(self) -> None:
    91 94
             self.show_all()
    
    92 95
     
    
    93 96
         def clear(self) -> None:
    
    94
    -        def _remove(item):
    
    97
    +        def _remove(item: Gtk.FlowBoxChild):
    
    95 98
                 self.remove(item)
    
    96 99
                 item.destroy()
    
    97 100
             self.foreach(_remove)
    
    ... ... @@ -107,9 +110,10 @@ def add_contact(self,
    107 110
         def has_contacts(self) -> bool:
    
    108 111
             return bool(self.get_child_at_index(0) is not None)
    
    109 112
     
    
    110
    -    def get_contact_jids(self) -> List[str]:
    
    111
    -        contacts = []
    
    112
    -        for contact in self.get_children():
    
    113
    +    def get_contact_jids(self) -> list[str]:
    
    114
    +        contacts: list[str] = []
    
    115
    +        contact_items = cast(list[ContactItem], self.get_children())
    
    116
    +        for contact in contact_items:
    
    113 117
                 contacts.append(contact.jid)
    
    114 118
             return contacts
    
    115 119
     
    

  • gajim/gtk/controls/base.py
    ... ... @@ -54,6 +54,7 @@
    54 54
     from gajim.common.helpers import get_file_path_from_dnd_dropped_uri
    
    55 55
     from gajim.common.i18n import _
    
    56 56
     from gajim.common.ged import EventHelper
    
    57
    +from gajim.common.events import JingleRequestReceived
    
    57 58
     from gajim.common.events import Notification
    
    58 59
     from gajim.common.helpers import AdditionalDataDict
    
    59 60
     from gajim.common.helpers import get_retraction_text
    
    ... ... @@ -61,6 +62,7 @@
    61 62
     from gajim.common.modules.httpupload import HTTPFileTransfer
    
    62 63
     from gajim.common.preview_helpers import filename_from_uri
    
    63 64
     from gajim.common.preview_helpers import guess_simple_file_type
    
    65
    +from gajim.common.storage.archive import ConversationRow
    
    64 66
     from gajim.common.structs import OutgoingMessage
    
    65 67
     
    
    66 68
     from gajim.gui.conversation.view import ConversationView
    
    ... ... @@ -1175,7 +1177,7 @@ def add_jingle_file_transfer(self, event):
    1175 1177
             if self._allow_add_message():
    
    1176 1178
                 self.conversation_view.add_jingle_file_transfer(event)
    
    1177 1179
     
    
    1178
    -    def add_call_message(self, event):
    
    1180
    +    def add_call_message(self, event: JingleRequestReceived) -> None:
    
    1179 1181
             if self._allow_add_message():
    
    1180 1182
                 self.conversation_view.add_call_message(event=event)
    
    1181 1183
     
    
    ... ... @@ -1451,7 +1453,7 @@ def fetch_n_lines_history(self,
    1451 1453
     
    
    1452 1454
             self.conversation_view.unlock()
    
    1453 1455
     
    
    1454
    -    def add_messages(self, messages):
    
    1456
    +    def add_messages(self, messages: list[ConversationRow]):
    
    1455 1457
             for msg in messages:
    
    1456 1458
                 if msg.kind in (KindConstant.FILE_TRANSFER_INCOMING,
    
    1457 1459
                                 KindConstant.FILE_TRANSFER_OUTGOING):
    

  • gajim/gtk/controls/chat.py
    ... ... @@ -23,6 +23,8 @@
    23 23
     # You should have received a copy of the GNU General Public License
    
    24 24
     # along with Gajim. If not, see <http://www.gnu.org/licenses/>.
    
    25 25
     
    
    26
    +from __future__ import annotations
    
    27
    +
    
    26 28
     from typing import ClassVar  # pylint: disable=unused-import
    
    27 29
     from typing import Type  # pylint: disable=unused-import
    
    28 30
     from typing import Optional
    
    ... ... @@ -42,13 +44,17 @@
    42 44
     
    
    43 45
     from gajim.common import app
    
    44 46
     from gajim.common import helpers
    
    47
    +from gajim.common.events import JingleRequestReceived
    
    45 48
     from gajim.common.i18n import _
    
    46 49
     from gajim.common.helpers import AdditionalDataDict
    
    47 50
     from gajim.common.const import AvatarSize
    
    48 51
     from gajim.common.const import KindConstant
    
    49 52
     from gajim.common.const import PEPEventType
    
    53
    +from gajim.common.jingle_session import JingleSession
    
    50 54
     
    
    51 55
     from gajim.gui.call_widget import CallWidget
    
    56
    +from gajim.gui.controls.base import BaseControl
    
    57
    +from gajim.gui.conversation.view import ConversationView
    
    52 58
     from gajim.gui.const import TARGET_TYPE_URI_LIST
    
    53 59
     from gajim.gui.const import ControlType
    
    54 60
     from gajim.gui.dialogs import DialogButton
    
    ... ... @@ -58,7 +64,6 @@
    58 64
     
    
    59 65
     from gajim.command_system.implementation.hosts import ChatCommands
    
    60 66
     from gajim.command_system.framework import CommandHost  # pylint: disable=unused-import
    
    61
    -from gajim.gui.controls.base import BaseControl
    
    62 67
     
    
    63 68
     from ..menus import get_encryption_menu
    
    64 69
     from ..menus import get_singlechat_menu
    
    ... ... @@ -475,16 +480,25 @@ def _on_start_call(self,
    475 480
         def _process_jingle_av_event(self, event):
    
    476 481
             self._call_widget.process_event(event)
    
    477 482
     
    
    478
    -    def _on_accept_call(self, _view, session):
    
    483
    +    def _on_accept_call(self,
    
    484
    +                        _view: ConversationView,
    
    485
    +                        session: JingleSession
    
    486
    +                        ) -> None:
    
    479 487
             self._call_widget.accept_call(session)
    
    480 488
     
    
    481
    -    def _on_decline_call(self, _view, session):
    
    489
    +    def _on_decline_call(self,
    
    490
    +                         _view: ConversationView,
    
    491
    +                         session: JingleSession
    
    492
    +                         ) -> None:
    
    482 493
             self._call_widget.decline_call(session)
    
    483 494
     
    
    484
    -    def _on_call_ended(self, _call_widget):
    
    495
    +    def _on_call_ended(self, _call_widget: CallWidget) -> None:
    
    485 496
             self.conversation_view.update_call_rows()
    
    486 497
     
    
    487
    -    def _add_incoming_call(self, _call_widget, event):
    
    498
    +    def _add_incoming_call(self,
    
    499
    +                           _call_widget: CallWidget,
    
    500
    +                           event: JingleRequestReceived
    
    501
    +                           ) -> None:
    
    488 502
             self.add_call_message(event)
    
    489 503
     
    
    490 504
         def on_location_eventbox_button_release_event(self, _widget, _event):
    

  • gajim/gtk/conversation/rows/call.py
    ... ... @@ -12,34 +12,48 @@
    12 12
     # You should have received a copy of the GNU General Public License
    
    13 13
     # along with Gajim. If not, see <http://www.gnu.org/licenses/>.
    
    14 14
     
    
    15
    +from __future__ import annotations
    
    16
    +
    
    15 17
     from typing import Optional
    
    18
    +from typing import cast
    
    19
    +from typing import TYPE_CHECKING
    
    16 20
     
    
    17 21
     import time
    
    18 22
     from datetime import datetime
    
    19 23
     
    
    24
    +from gi.repository import GdkPixbuf
    
    20 25
     from gi.repository import Gtk
    
    21 26
     
    
    22 27
     from gajim.common import app
    
    28
    +from gajim.common import types
    
    23 29
     from gajim.common.const import AvatarSize
    
    24 30
     from gajim.common.const import KindConstant
    
    31
    +from gajim.common.events import JingleRequestReceived
    
    25 32
     from gajim.common.i18n import _
    
    26 33
     from gajim.common.jingle_session import JingleSession
    
    34
    +from gajim.common.storage.archive import ConversationRow
    
    27 35
     
    
    28 36
     from .widgets import SimpleLabel
    
    29 37
     from .base import BaseRow
    
    30 38
     
    
    39
    +if TYPE_CHECKING:
    
    40
    +    from gajim.gui.conversation.view import ConversationView
    
    41
    +
    
    31 42
     
    
    32 43
     class CallRow(BaseRow):
    
    33
    -    def __init__(self, account, contact, event=None, db_message=None):
    
    44
    +    def __init__(self,
    
    45
    +                 account: str,
    
    46
    +                 contact: types.BareContact,
    
    47
    +                 event: Optional[JingleRequestReceived] = None,
    
    48
    +                 db_message: Optional[ConversationRow] = None
    
    49
    +                 ) -> None:
    
    34 50
             BaseRow.__init__(self, account)
    
    35 51
     
    
    36 52
             self.type = 'call'
    
    37 53
     
    
    38 54
             self._client = app.get_client(account)
    
    39 55
     
    
    40
    -        is_from_db = bool(db_message is not None)
    
    41
    -
    
    42
    -        if is_from_db:
    
    56
    +        if db_message is not None:
    
    43 57
                 timestamp = db_message.time
    
    44 58
             else:
    
    45 59
                 timestamp = time.time()
    
    ... ... @@ -52,7 +66,7 @@ def __init__(self, account, contact, event=None, db_message=None):
    52 66
     
    
    53 67
             self._session: Optional[JingleSession] = None
    
    54 68
     
    
    55
    -        if is_from_db:
    
    69
    +        if db_message is not None:
    
    56 70
                 sid = db_message.additional_data.get_value('gajim', 'sid')
    
    57 71
                 module = self._client.get_module('Jingle')
    
    58 72
                 self._session = module.get_jingle_session(self._contact.jid, sid)
    
    ... ... @@ -87,36 +101,39 @@ def update(self) -> None:
    87 101
         def _on_accept(self, button: Gtk.Button) -> None:
    
    88 102
             button.set_sensitive(False)
    
    89 103
             self._decline_button.set_sensitive(False)
    
    104
    +        view = cast('ConversationView', self.get_parent())
    
    90 105
             if self._event is not None:
    
    91 106
                 session = self._client.get_module('Jingle').get_jingle_session(
    
    92 107
                     self._event.fjid, self._event.sid)
    
    93
    -            self.get_parent().accept_call(session)
    
    108
    +            view.accept_call(session)
    
    94 109
             else:
    
    95
    -            self.get_parent().accept_call(self._session)
    
    110
    +            assert self._session is not None
    
    111
    +            view.accept_call(self._session)
    
    96 112
     
    
    97 113
         def _on_decline(self, _button: Gtk.Button) -> None:
    
    114
    +        view = cast('ConversationView', self.get_parent())
    
    98 115
             if self._event is not None:
    
    99 116
                 session = self._client.get_module('Jingle').get_jingle_session(
    
    100 117
                     self._event.fjid, self._event.sid)
    
    101
    -            self.get_parent().decline_call(session)
    
    118
    +            view.decline_call(session)
    
    102 119
             else:
    
    103
    -            self.get_parent().decline_call(self._session)
    
    120
    +            assert self._session is not None
    
    121
    +            view.decline_call(self._session)
    
    104 122
             self._session = None
    
    105 123
     
    
    106 124
         def _add_history_call_widget(self) -> None:
    
    125
    +        contact = self._client.get_module('Contacts').get_contact(
    
    126
    +            str(self._client.get_own_jid().bare))
    
    127
    +        is_self = True
    
    107 128
             if self._db_message is not None:
    
    108 129
                 if self._db_message.kind == KindConstant.CALL_INCOMING:
    
    109 130
                     contact = self._contact
    
    110 131
                     is_self = True
    
    111 132
                 else:
    
    112
    -                contact = self._client.get_module('Contacts').get_contact(
    
    113
    -                    str(self._client.get_own_jid().bare))
    
    114 133
                     is_self = False
    
    115 134
     
    
    116 135
             if self._event is not None:
    
    117 136
                 if self._event == 'incoming-call':
    
    118
    -                contact = self._client.get_module('Contacts').get_contact(
    
    119
    -                    str(self._client.get_own_jid().bare))
    
    120 137
                     is_self = False
    
    121 138
                 else:
    
    122 139
                     contact = self._contact
    
    ... ... @@ -124,6 +141,7 @@ def _add_history_call_widget(self) -> None:
    124 141
     
    
    125 142
             scale = self.get_scale_factor()
    
    126 143
             avatar = contact.get_avatar(AvatarSize.ROSTER, scale, add_show=False)
    
    144
    +        assert not isinstance(avatar, GdkPixbuf.Pixbuf)
    
    127 145
             avatar_image = Gtk.Image.new_from_surface(avatar)
    
    128 146
             self._avatar_placeholder.add(avatar_image)
    
    129 147
     
    
    ... ... @@ -157,10 +175,11 @@ def _add_incoming_call_widget(self) -> None:
    157 175
                 AvatarSize.CALL,
    
    158 176
                 scale,
    
    159 177
                 add_show=False)
    
    178
    +        assert not isinstance(avatar, GdkPixbuf.Pixbuf)
    
    160 179
             avatar_image = Gtk.Image.new_from_surface(avatar)
    
    161 180
             self._call_box.add(avatar_image)
    
    162 181
     
    
    163
    -        content_types = []
    
    182
    +        content_types: list[str] = []
    
    164 183
             if self._event is not None:
    
    165 184
                 for item in self._event.contents:
    
    166 185
                     content_types.append(item.media)
    

  • gajim/gtk/conversation/view.py
    ... ... @@ -35,13 +35,16 @@
    35 35
     
    
    36 36
     from gajim.common import app
    
    37 37
     from gajim.common.client import Client
    
    38
    +from gajim.common.events import JingleRequestReceived
    
    38 39
     from gajim.common.helpers import AdditionalDataDict
    
    39 40
     from gajim.common.helpers import to_user_string
    
    40 41
     from gajim.common.helpers import get_start_of_day
    
    42
    +from gajim.common.jingle_session import JingleSession
    
    41 43
     from gajim.common.modules.contacts import GroupchatParticipant
    
    42 44
     from gajim.common.modules.contacts import GroupchatContact
    
    43 45
     from gajim.common.modules.contacts import BareContact
    
    44 46
     from gajim.common.modules.httpupload import HTTPFileTransfer
    
    47
    +from gajim.common.storage.archive import ConversationRow
    
    45 48
     
    
    46 49
     from .rows.base import BaseRow
    
    47 50
     from .rows.read_marker import ReadMarkerRow
    
    ... ... @@ -233,7 +236,11 @@ def add_jingle_file_transfer(self, event=None, db_message=None):
    233 236
                 self._account, self._contact, event=event, db_message=db_message)
    
    234 237
             self._insert_message(jingle_transfer_row)
    
    235 238
     
    
    236
    -    def add_call_message(self, event=None, db_message=None):
    
    239
    +    def add_call_message(self,
    
    240
    +                         event: Optional[JingleRequestReceived] = None,
    
    241
    +                         db_message: Optional[ConversationRow] = None
    
    242
    +                         ) -> None:
    
    243
    +        assert isinstance(self._contact, BareContact)
    
    237 244
             call_row = CallRow(
    
    238 245
                 self._account, self._contact, event=event, db_message=db_message)
    
    239 246
             self._insert_message(call_row)
    
    ... ... @@ -455,8 +462,9 @@ def iter_rows(self) -> Generator[BaseRow, None, None]:
    455 462
                 yield row
    
    456 463
     
    
    457 464
         def update_call_rows(self) -> None:
    
    458
    -        for row in self.get_children():
    
    459
    -            if row.type == 'call':
    
    465
    +        rows = cast(list[BaseRow], self.get_children())
    
    466
    +        for row in rows:
    
    467
    +            if isinstance(row, CallRow):
    
    460 468
                     row.update()
    
    461 469
     
    
    462 470
         def set_read_marker(self, id_: str) -> None:
    
    ... ... @@ -516,11 +524,11 @@ def on_quote(self, text: str) -> None:
    516 524
         def on_mention(self, name: str) -> None:
    
    517 525
             self.emit('mention', name)
    
    518 526
     
    
    519
    -    def accept_call(self, event):
    
    520
    -        self.emit('accept-call', event)
    
    527
    +    def accept_call(self, session: JingleSession) -> None:
    
    528
    +        self.emit('accept-call', session)
    
    521 529
     
    
    522
    -    def decline_call(self, event):
    
    523
    -        self.emit('decline-call', event)
    
    530
    +    def decline_call(self, session: JingleSession) -> None:
    
    531
    +        self.emit('decline-call', session)
    
    524 532
     
    
    525 533
         def _on_contact_setting_changed(self, *args: Any) -> None:
    
    526 534
             self.invalidate_filter()