Daniel Brötzmann pushed to branch master at gajim / gajim
Commits:
-
ba20dc5e
by wurstsalat at 2022-01-12T23:25:42+01:00
-
347a89b4
by wurstsalat at 2022-01-12T23:25:42+01:00
-
098274e8
by wurstsalat at 2022-01-12T23:25:42+01:00
9 changed files:
- gajim/common/app.py
- gajim/common/jingle_rtp.py
- gajim/common/jingle_session.py
- gajim/gtk/call_widget.py
- gajim/gtk/contacts_flowbox.py
- gajim/gtk/controls/base.py
- gajim/gtk/controls/chat.py
- gajim/gtk/conversation/rows/call.py
- gajim/gtk/conversation/view.py
Changes:
| ... | ... | @@ -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
|
| ... | ... | @@ -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 |
"""
|
| ... | ... | @@ -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)
|
| ... | ... | @@ -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
|
| ... | ... | @@ -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 |
|
| ... | ... | @@ -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):
|
| ... | ... | @@ -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):
|
| ... | ... | @@ -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)
|
| ... | ... | @@ -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()
|