Daniel Brötzmann pushed to branch mainwindow at gajim / gajim
Commits: 4dce9b32 by wurstsalat at 2021-08-27T21:10:08+02:00 ConversationView: Add Call row
- - - - -
9 changed files:
- gajim/common/const.py - gajim/data/style/default-dark.css - gajim/data/style/default.css - gajim/data/style/gajim.css - gajim/gtk/controls/base.py - gajim/gtk/controls/chat.py - + gajim/gtk/conversation/rows/call.py - gajim/gtk/conversation/view.py - gajim/gui_interface.py
Changes:
===================================== gajim/common/const.py ===================================== @@ -32,6 +32,7 @@ class AvatarSize(IntEnum): WORKSPACE_EDIT = 100 CHAT = 48 NOTIFICATION = 48 + CALL = 100 GROUP_INFO = 100 TOOLTIP = 100 ACCOUNT_PAGE = 150
===================================== gajim/data/style/default-dark.css ===================================== @@ -15,6 +15,10 @@ .gajim-subject-message { color: rgb(205, 225, 255); background: rgb(40, 60, 80); } +.gajim-call-message { + color: rgb(205, 225, 255); + background: rgb(40, 60, 80); +} .gajim-url { color: rgb(60, 135, 220); }
===================================== gajim/data/style/default.css ===================================== @@ -15,6 +15,10 @@ .gajim-subject-message { color: rgb(40, 70, 100); background: rgb(225, 235, 250); } +.gajim-call-message { + color: rgb(40, 70, 100); + background: rgb(225, 235, 250); +} .gajim-url { color: rgb(30, 75, 135); }
===================================== gajim/data/style/gajim.css ===================================== @@ -82,7 +82,8 @@ .conversation-system-row { .conversation-date-row { padding: 12px; } -.conversation-subject-box { +.conversation-subject-box, +.conversation-call-box { border-radius: 6px; padding: 12px; }
===================================== gajim/gtk/controls/base.py ===================================== @@ -1098,6 +1098,9 @@ def add_info_message(self, text): def add_file_transfer(self, transfer): self.conversation_view.add_file_transfer(transfer)
+ def add_call_message(self, timestamp, text, event=None): + self.conversation_view.add_call_message(timestamp, text, event) + def add_message(self, text, kind,
===================================== gajim/gtk/controls/chat.py ===================================== @@ -136,6 +136,7 @@ def __init__(self, account, jid): } self._video_widget_other = None self._video_widget_self = None + self.conversation_view.connect('call-answered', self._on_call_answered)
self.update_toolbar() self.update_all_pep_types() @@ -831,6 +832,44 @@ def on_sound_hscale_value_changed(self, _widget, value): self._get_audio_content().set_out_volume(value / 100) app.settings.set('audio_output_volume', int(value))
+ def _on_call_answered(self, _view, action, event): + if action == 'accept': + session = self._client.get_module('Jingle').get_jingle_session( + event.fjid, event.sid) + if not session: + return + + audio = session.get_content('audio') + video = session.get_content('video') + + if audio and not audio.negotiated: + self.set_jingle_state( + 'audio', JingleState.CONNECTING, event.sid) + if video and not video.negotiated: + self.set_jingle_state( + 'video', JingleState.CONNECTING, event.sid) + + if not session.accepted: + session.approve_session() + + for item in event.contents: + session.approve_content(item.media) + + if action == 'reject': + session = self._client.get_module('Jingle').get_jingle_session( + event.fjid, event.sid) + if not session: + return + + if not session.accepted: + session.decline_session() + else: + for item in event.contents: + session.reject_content(item.media) + + def add_incoming_call(self, event): + self.add_call_message(time.time(), '', event=event) + def on_location_eventbox_button_release_event(self, _widget, _event): return # TODO @@ -1200,61 +1239,6 @@ def _got_file_error(self, file_props, type_, pri_txt, sec_txt): file_props, Gtk.MessageType.ERROR)
- def _on_reject_call(self, _button, event): - app.events.remove_events( - self.account, event.jid, types='jingle-incoming') - - session = self._client.get_module('Jingle').get_jingle_session( - event.peerjid, event.sid) - if not session: - return - - if not session.accepted: - session.decline_session() - else: - for content in event.content_types: - session.reject_content(content) - - def _on_accept_call(self, _button, event): - app.events.remove_events( - self.account, event.jid, types='jingle-incoming') - - session = self._client.get_module('Jingle').get_jingle_session( - event.peerjid, event.sid) - if not session: - return - - audio = session.get_content('audio') - video = session.get_content('video') - - if audio and not audio.negotiated: - self.set_jingle_state('audio', JingleState.CONNECTING, event.sid) - if video and not video.negotiated: - self.set_jingle_state('video', JingleState.CONNECTING, event.sid) - - if not session.accepted: - session.approve_session() - - for content in event.content_types: - session.approve_content(content) - - def add_call_received_message(self, event): - markup = '<b>%s</b>' % (_('Incoming Call')) - if 'video' in event.content_types: - markup += _('\nVideo Call') - else: - markup += _('\nVoice Call') - - button_reject = Gtk.Button.new_with_mnemonic(_('_Reject')) - button_reject.connect('clicked', self._on_reject_call, event) - button_accept = Gtk.Button.new_with_mnemonic(_('_Accept')) - button_accept.connect('clicked', self._on_accept_call, event) - self._add_info_bar_message( - markup, - [button_reject, button_accept], - event, - Gtk.MessageType.QUESTION) - def on_event_added(self, event): if event.account != self.account: return
===================================== gajim/gtk/conversation/rows/call.py ===================================== @@ -0,0 +1,112 @@ +# This file is part of Gajim. +# +# Gajim is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published +# by the Free Software Foundation; version 3 only. +# +# Gajim is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Gajim. If not, see http://www.gnu.org/licenses/. + +import time +from datetime import datetime + +from gi.repository import Gtk + +from gajim.common.const import AvatarSize +from gajim.common.i18n import _ + +from .widgets import SimpleLabel +from .base import BaseRow + + +class CallRow(BaseRow): + def __init__(self, account, contact, timestamp, text, event=None): + BaseRow.__init__(self, account) + + self.type = 'call' + timestamp = time.time() + self.timestamp = datetime.fromtimestamp(timestamp) + self.db_timestamp = timestamp + + self._contact = contact + self._event = event + + avatar_placeholder = Gtk.Box() + avatar_placeholder.set_size_request(AvatarSize.ROSTER, -1) + self.grid.attach(avatar_placeholder, 0, 0, 1, 1) + + if event is None: + icon = Gtk.Image.new_from_icon_name('call-start-symbolic', + Gtk.IconSize.MENU) + self.grid.attach(icon, 1, 0, 1, 1) + + self._label = SimpleLabel() + self._label.set_text(text) + self.grid.attach(self._label, 2, 0, 1, 1) + else: + self._prepare_incoming_call() + + timestamp_widget = self.create_timestamp_widget(self.timestamp) + timestamp_widget.set_hexpand(True) + timestamp_widget.set_halign(Gtk.Align.END) + timestamp_widget.set_valign(Gtk.Align.START) + self.grid.attach(timestamp_widget, 3, 0, 1, 1) + + self.show_all() + + def _on_accept(self, button): + button.set_sensitive(False) + self._reject_button.set_sensitive(False) + self.get_parent().on_call_answered('accept', self._event) + + def _on_reject(self, button): + button.set_sensitive(False) + self._accept_button.set_sensitive(False) + self.get_parent().on_call_answered('reject', self._event) + + def _prepare_incoming_call(self): + call_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) + call_box.set_size_request(350, -1) + call_box.get_style_context().add_class('conversation-call-box') + call_box.get_style_context().add_class('gajim-call-message') + + scale = self.get_scale_factor() + avatar = self._contact.get_avatar( + AvatarSize.CALL, + scale, + add_show=False) + avatar_image = Gtk.Image.new_from_surface(avatar) + call_box.add(avatar_image) + + content_types = [] + for item in self._event.contents: + content_types.append(item.media) + text = _('%s is calling') % self._contact.name + if 'video' in content_types: + text += _(' (Video Call)') + else: + text += _(' (Voice Call)') + label = Gtk.Label(label=text) + label.get_style_context().add_class('bold') + label.set_max_width_chars(40) + label.set_line_wrap(True) + call_box.add(label) + + self._accept_button = Gtk.Button.new_with_label(_('Accept Call')) + self._accept_button.connect('clicked', self._on_accept) + + self._reject_button = Gtk.Button.new_with_label(_('Reject')) + self._reject_button.connect('clicked', self._on_reject) + + button_box = Gtk.Box(spacing=12) + button_box.set_halign(Gtk.Align.CENTER) + button_box.add(self._accept_button) + button_box.add(self._reject_button) + call_box.add(button_box) + + self.grid.attach(call_box, 1, 0, 1, 1)
===================================== gajim/gtk/conversation/view.py ===================================== @@ -29,6 +29,7 @@ from .rows.scroll_hint import ScrollHintRow from .rows.message import MessageRow from .rows.info import InfoMessage +from .rows.call import CallRow from .rows.date import DateRow from .rows.file_transfer import FileTransferRow from .rows.muc_subject import MUCSubject @@ -47,6 +48,11 @@ class ConversationView(Gtk.ListBox): None, (str, ) ), + 'call-answered': ( + GObject.SignalFlags.RUN_LAST | GObject.SignalFlags.ACTION, + None, + (str, object) + ), }
def __init__(self, account, contact): @@ -168,6 +174,14 @@ def add_file_transfer(self, transfer): transfer_row = FileTransferRow(self._account, transfer) self._insert_message(transfer_row)
+ def add_call_message(self, timestamp, text, event): + call_row = CallRow(self._account, + self._contact, + timestamp, + text, + event) + self._insert_message(call_row) + def add_message(self, text, kind, @@ -409,5 +423,8 @@ def show_error(self, id_, error): def on_quote(self, text): self.emit('quote', text)
+ def on_call_answered(self, action, event): + self.emit('call-answered', action, event) + def _on_contact_setting_changed(self, *args): self.invalidate_filter()
===================================== gajim/gui_interface.py ===================================== @@ -1022,32 +1022,22 @@ def _popup_ft_result(self, account, jid, file_props): text=txt)
# Jingle AV handling - def handle_event_jingle_incoming(self, event): - # ('JINGLE_INCOMING', account, peer jid, sid, tuple-of-contents==(type, - # data...)) + @staticmethod + def handle_event_jingle_incoming(event): + # ('JINGLE_INCOMING', conn, fjid, sid, resource, jingle session, + # tuple-of-contents==(type, data...)) # TODO: conditional blocking if peer is not in roster
account = event.conn.name + content_types = [] for item in event.contents: content_types.append(item.media) - # check type of jingle session - if 'audio' in content_types or 'video' in content_types: - # a voip session... - # we now handle only voip, so the only thing we will do here is - # not to return from function - pass - else: - # unknown session type... it should be declined in common/jingle.py - return
- notification_event = events.JingleIncomingEvent( - event.fjid, - event.sid, - content_types) + if 'audio' not in content_types and 'video' not in content_types: + return
- ctrl = (app.window.get_control(account, event.fjid) or - app.window.get_control(account, event.jid)) + ctrl = app.window.get_control(account, event.jid) if ctrl: if 'audio' in content_types: ctrl.set_jingle_state( @@ -1059,25 +1049,24 @@ def handle_event_jingle_incoming(self, event): 'video', JingleState.CONNECTION_RECEIVED, event.sid) - ctrl.add_call_received_message(notification_event) + ctrl.add_incoming_call(event)
if helpers.allow_popup_window(account): def _prepare_control(): ctrl = app.window.get_control(account, event.jid) - ctrl.add_call_received_message(notification_event) + ctrl.add_incoming_call(event)
if not ctrl: app.window.add_chat( account, event.jid, 'contact', select=True) - GLib.idle_add(_prepare_control) - return - - self.add_event(account, event.fjid, notification_event) + GLib.idle_add(_prepare_control) + return
if helpers.allow_showing_notification(account): heading = _('Incoming Call') - contact = app.get_name_from_jid(account, event.jid) - text = _('%s is calling') % contact + client = app.get_client(account) + contact = client.get_module('Contacts').get_contact(event.jid) + text = _('%s is calling') % contact.name app.notification.popup( heading, event.jid, @@ -1092,11 +1081,10 @@ def handle_event_jingle_connected(event): # ('JINGLE_CONNECTED', account, (peerjid, sid, media)) if event.media in ('audio', 'video'): account = event.conn.name - ctrl = (app.window.get_control(account, event.fjid) or - app.window.get_control(account, event.jid)) + ctrl = app.window.get_control(account, event.jid) if ctrl: - con = app.connections[account] - session = con.get_module('Jingle').get_jingle_session( + client = app.get_client(account) + session = client.get_module('Jingle').get_jingle_session( event.fjid, event.sid)
if event.media == 'audio': @@ -1123,8 +1111,7 @@ def handle_event_jingle_connected(event): def handle_event_jingle_disconnected(event): # ('JINGLE_DISCONNECTED', account, (peerjid, sid, reason)) account = event.conn.name - ctrl = (app.window.get_control(account, event.fjid) or - app.window.get_control(account, event.jid)) + ctrl = app.window.get_control(account, event.jid) if ctrl: if event.media is None: ctrl.stop_jingle(sid=event.sid, reason=event.reason) @@ -1145,8 +1132,7 @@ def handle_event_jingle_disconnected(event): def handle_event_jingle_error(event): # ('JINGLE_ERROR', account, (peerjid, sid, reason)) account = event.conn.name - ctrl = (app.window.get_control(account, event.fjid) or - app.window.get_control(account, event.jid)) + ctrl = app.window.get_control(account, event.jid) if ctrl and event.sid == ctrl.jingle['audio'].sid: ctrl.set_jingle_state( 'audio',
View it on GitLab: https://dev.gajim.org/gajim/gajim/-/commit/4dce9b3244d5fe56c0bd448aebf0d32db...