From d41e93fb1c7f224766b67cde0f3d71d0fe09f4a0 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 3 Dec 2020 21:17:15 +0300 Subject: [PATCH] Add push-to-talk with global shortcut on Windows. --- Telegram/Resources/langs/lang.strings | 3 + Telegram/SourceFiles/calls/calls.style | 16 +-- .../SourceFiles/calls/calls_group_call.cpp | 57 ++++++++++ Telegram/SourceFiles/calls/calls_group_call.h | 18 +++ .../calls/calls_group_settings.cpp | 107 ++++++++++++++++-- Telegram/SourceFiles/core/core_settings.cpp | 13 ++- Telegram/SourceFiles/core/core_settings.h | 14 +++ Telegram/lib_base | 2 +- Telegram/lib_spellcheck | 2 +- Telegram/lib_storage | 2 +- Telegram/lib_ui | 2 +- Telegram/lib_webrtc | 2 +- 12 files changed, 217 insertions(+), 21 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index ee967a1efb..69f04bae18 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1836,6 +1836,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_call_new_muted" = "Mute new members"; "lng_group_call_speakers" = "Speakers"; "lng_group_call_microphone" = "Microphone"; +"lng_group_call_push_to_talk" = "Push to Talk"; +"lng_group_call_ptt_shortcut" = "Edit Shortcut"; +"lng_group_call_ptt_recording" = "Stop Recording"; "lng_group_call_share" = "Share Invite Link"; "lng_group_call_end" = "End Voice Chat"; "lng_group_call_join" = "Join"; diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style index 78000ea93e..5849c02540 100644 --- a/Telegram/SourceFiles/calls/calls.style +++ b/Telegram/SourceFiles/calls/calls.style @@ -641,20 +641,22 @@ groupCallCheckbox: Checkbox(defaultBoxCheckbox) { rippleBgActive: groupCallMembersBgRipple; } -groupCallSettingsButton: SettingsButton { +groupCallSettingsToggle: Toggle(defaultToggle) { + toggledBg: groupCallMembersBg; + toggledFg: groupCallActiveFg; + untoggledBg: groupCallMembersBg; + untoggledFg: groupCallMemberNotJoinedStatus; +} +groupCallSettingsButton: SettingsButton(defaultSettingsButton) { textFg: groupCallMembersFg; textFgOver: groupCallMembersFg; textBg: groupCallMembersBg; textBgOver: groupCallMembersBgOver; - - font: boxTextFont; rightLabel: FlatLabel(defaultSettingsRightLabel) { textFg: groupCallActiveFg; } - - height: 20px; - padding: margins(22px, 10px, 22px, 8px); - + toggle: groupCallSettingsToggle; + toggleOver: groupCallSettingsToggle; ripple: groupCallRipple; } groupCallSettingsAttentionButton: SettingsButton(groupCallSettingsButton) { diff --git a/Telegram/SourceFiles/calls/calls_group_call.cpp b/Telegram/SourceFiles/calls/calls_group_call.cpp index ebdb395c29..b97287e45a 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/calls_group_call.cpp @@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_channel.h" #include "data/data_group_call.h" #include "data/data_session.h" +#include "base/platform/base_platform_global_shortcuts.h" #include @@ -92,6 +93,11 @@ void GroupCall::setState(State state) { } _state = state; + if (_state.current() == State::Joined && !_pushToTalkStarted) { + _pushToTalkStarted = true; + applyGlobalShortcutChanges(); + } + if (false || state == State::Ended || state == State::Failed) { @@ -706,6 +712,57 @@ std::variant> GroupCall::inviteUsers( return result; } +auto GroupCall::ensureGlobalShortcutManager() +-> std::shared_ptr { + if (!_shortcutManager) { + _shortcutManager = base::Platform::CreateGlobalShortcutManager(); + } + return _shortcutManager; +} + +void GroupCall::applyGlobalShortcutChanges() { + auto &settings = Core::App().settings(); + if (!settings.groupCallPushToTalk()) { + _shortcutManager = nullptr; + _pushToTalk = nullptr; + return; + } else if (settings.groupCallPushToTalkShortcut().isEmpty()) { + settings.setGroupCallPushToTalk(false); + Core::App().saveSettingsDelayed(); + _shortcutManager = nullptr; + _pushToTalk = nullptr; + return; + } + ensureGlobalShortcutManager(); + if (!_shortcutManager) { + settings.setGroupCallPushToTalk(false); + Core::App().saveSettingsDelayed(); + _pushToTalk = nullptr; + return; + } + const auto shortcut = _shortcutManager->shortcutFromSerialized( + settings.groupCallPushToTalkShortcut()); + if (!shortcut) { + settings.setGroupCallPushToTalkShortcut(QByteArray()); + settings.setGroupCallPushToTalk(false); + Core::App().saveSettingsDelayed(); + _shortcutManager = nullptr; + _pushToTalk = nullptr; + return; + } + if (_pushToTalk) { + if (shortcut->serialize() == _pushToTalk->serialize()) { + return; + } + _shortcutManager->stopWatching(_pushToTalk); + } + _pushToTalk = shortcut; + _shortcutManager->startWatching(_pushToTalk, [=](bool pressed) { + if (_muted.current() != MuteState::ForceMuted) { + setMuted(pressed ? MuteState::Active : MuteState::Muted); + } + }); +} //void GroupCall::setAudioVolume(bool input, float level) { // if (_instance) { // if (input) { diff --git a/Telegram/SourceFiles/calls/calls_group_call.h b/Telegram/SourceFiles/calls/calls_group_call.h index 7ea4294eb5..1a016a6ebc 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.h +++ b/Telegram/SourceFiles/calls/calls_group_call.h @@ -19,6 +19,13 @@ namespace tgcalls { class GroupInstanceImpl; } // namespace tgcalls +namespace base { +namespace Platform { +class GlobalShortcutManager; +class GlobalShortcutValue; +} // namespace Platform +} // namespace base + namespace Calls { enum class MuteState { @@ -44,6 +51,8 @@ public: }; + using GlobalShortcutManager = base::Platform::GlobalShortcutManager; + GroupCall( not_null delegate, not_null channel, @@ -102,11 +111,16 @@ public: std::variant> inviteUsers( const std::vector> &users); + std::shared_ptr ensureGlobalShortcutManager(); + void applyGlobalShortcutChanges(); + [[nodiscard]] rpl::lifetime &lifetime() { return _lifetime; } private: + using GlobalShortcutValue = base::Platform::GlobalShortcutValue; + enum class FinishType { None, Ended, @@ -160,6 +174,10 @@ private: crl::time _lastSendProgressUpdate = 0; + std::shared_ptr _shortcutManager; + std::shared_ptr _pushToTalk; + bool _pushToTalkStarted = false; + rpl::lifetime _lifetime; }; diff --git a/Telegram/SourceFiles/calls/calls_group_settings.cpp b/Telegram/SourceFiles/calls/calls_group_settings.cpp index 5d1383303b..c26d4dc763 100644 --- a/Telegram/SourceFiles/calls/calls_group_settings.cpp +++ b/Telegram/SourceFiles/calls/calls_group_settings.cpp @@ -10,10 +10,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "calls/calls_group_call.h" #include "calls/calls_group_panel.h" // LeaveGroupCallBox. #include "calls/calls_instance.h" -#include "ui/widgets/checkbox.h" #include "ui/widgets/level_meter.h" +#include "ui/widgets/buttons.h" +#include "ui/wrap/slide_wrap.h" #include "ui/toast/toast.h" #include "lang/lang_keys.h" +#include "base/platform/base_platform_global_shortcuts.h" #include "data/data_channel.h" #include "data/data_group_call.h" #include "core/application.h" @@ -90,12 +92,10 @@ void GroupCallSettingsBox( AddSkip(layout); } const auto muteJoined = addCheck - ? box->addRow(object_ptr( - box.get(), + ? AddButton( + layout, tr::lng_group_call_new_muted(), - joinMuted, - st::groupCallCheckbox, - st::groupCallCheck)) + st::groupCallSettingsButton)->toggleOn(rpl::single(joinMuted)) : nullptr; if (addCheck) { AddSkip(layout); @@ -155,6 +155,97 @@ void GroupCallSettingsBox( }); AddSkip(layout); + //AddDivider(layout); + //AddSkip(layout); + + using namespace base::Platform; + struct PushToTalkState { + rpl::variable recordText = tr::lng_group_call_ptt_shortcut(); + rpl::variable shortcutText; + GlobalShortcut shortcut; + bool recording = false; + }; + const auto manager = call->ensureGlobalShortcutManager(); + if (manager) { + const auto state = box->lifetime().make_state(); + state->shortcut = manager->shortcutFromSerialized( + settings.groupCallPushToTalkShortcut()); + state->shortcutText = state->shortcut + ? state->shortcut->toDisplayString() + : QString(); + const auto pushToTalk = AddButton( + layout, + tr::lng_group_call_push_to_talk(), + st::groupCallSettingsButton + )->toggleOn(rpl::single(settings.groupCallPushToTalk())); + const auto recordingWrap = layout->add( + object_ptr>( + layout, + object_ptr