From f11df0519e3ca778e14bdad5e63cd75929f2381e Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 7 Dec 2020 15:47:39 +0400 Subject: [PATCH] Add global shortcut permissions check on macOS. --- Telegram/Resources/langs/lang.strings | 4 + .../SourceFiles/calls/calls_group_call.cpp | 22 ++-- Telegram/SourceFiles/calls/calls_group_call.h | 1 + .../calls/calls_group_settings.cpp | 109 ++++++++++++++++-- Telegram/lib_base | 2 +- 5 files changed, 120 insertions(+), 18 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 290aa3ed47..2fbfdf870a 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1862,6 +1862,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_call_duration_minutes#other" = "{count} minutes"; "lng_group_call_duration_seconds#one" = "{count} second"; "lng_group_call_duration_seconds#other" = "{count} seconds"; +"lng_group_call_mac_access" = "Telegram Desktop does not have access to system wide keyboard input required for Push to Talk."; +"lng_group_call_mac_input" = "Please allow **Input Monitoring** for Telegram in Privacy Settings."; +"lng_group_call_mac_accessibility" = "Please allow **Accessibility** for Telegram in Privacy Settings.\n\nApp restart may be required."; +"lng_group_call_mac_settings" = "Open Settings"; "lng_no_mic_permission" = "Telegram needs access to your microphone so that you can make calls and record voice messages."; diff --git a/Telegram/SourceFiles/calls/calls_group_call.cpp b/Telegram/SourceFiles/calls/calls_group_call.cpp index 6082a01507..fd057e1e62 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/calls_group_call.cpp @@ -65,6 +65,8 @@ GroupCall::GroupCall( } }, _lifetime); + checkGlobalShortcutAvailability(); + const auto id = inputCall.c_inputGroupCall().vid().v; if (id) { if (const auto call = _channel->call(); call && call->id() == id) { @@ -83,6 +85,16 @@ GroupCall::~GroupCall() { destroyController(); } +void GroupCall::checkGlobalShortcutAvailability() { + auto &settings = Core::App().settings(); + if (!settings.groupCallPushToTalk()) { + return; + } else if (!base::GlobalShortcutsAllowed()) { + settings.setGroupCallPushToTalk(false); + Core::App().saveSettingsDelayed(); + } +} + void GroupCall::setState(State state) { if (_state.current() == State::Failed) { return; @@ -747,18 +759,14 @@ auto GroupCall::ensureGlobalShortcutManager() void GroupCall::applyGlobalShortcutChanges() { auto &settings = Core::App().settings(); if (!settings.groupCallPushToTalk() - || settings.groupCallPushToTalkShortcut().isEmpty()) { + || settings.groupCallPushToTalkShortcut().isEmpty() + || !base::GlobalShortcutsAvailable() + || !base::GlobalShortcutsAllowed()) { _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) { diff --git a/Telegram/SourceFiles/calls/calls_group_call.h b/Telegram/SourceFiles/calls/calls_group_call.h index 0c6cc9fbde..6f54d0100d 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.h +++ b/Telegram/SourceFiles/calls/calls_group_call.h @@ -154,6 +154,7 @@ private: void checkLastSpoke(); void pushToTalkCancel(); + void checkGlobalShortcutAvailability(); void checkJoined(); [[nodiscard]] MTPInputGroupCall inputCall() const; diff --git a/Telegram/SourceFiles/calls/calls_group_settings.cpp b/Telegram/SourceFiles/calls/calls_group_settings.cpp index 15e3fb3cb1..04747441ba 100644 --- a/Telegram/SourceFiles/calls/calls_group_settings.cpp +++ b/Telegram/SourceFiles/calls/calls_group_settings.cpp @@ -14,10 +14,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/continuous_sliders.h" #include "ui/widgets/buttons.h" #include "ui/wrap/slide_wrap.h" +#include "ui/text/text_utilities.h" #include "ui/toast/toast.h" #include "lang/lang_keys.h" +#include "base/timer_rpl.h" #include "base/event_filter.h" #include "base/global_shortcuts.h" +#include "base/platform/base_platform_info.h" #include "data/data_channel.h" #include "data/data_group_call.h" #include "core/application.h" @@ -38,6 +41,7 @@ namespace Calls { namespace { constexpr auto kDelaysCount = 201; +constexpr auto kCheckAccessibilityInterval = crl::time(500); void SaveCallJoinMuted( not_null channel, @@ -182,24 +186,39 @@ void GroupCallSettingsBox( struct PushToTalkState { rpl::variable recordText = tr::lng_group_call_ptt_shortcut(); rpl::variable shortcutText; + rpl::event_stream pushToTalkToggles; + std::shared_ptr manager; GlobalShortcut shortcut; crl::time delay = 0; bool recording = false; }; - const auto manager = call->ensureGlobalShortcutManager(); - if (manager) { + if (base::GlobalShortcutsAvailable()) { const auto state = box->lifetime().make_state(); - state->shortcut = manager->shortcutFromSerialized( - settings.groupCallPushToTalkShortcut()); + if (!base::GlobalShortcutsAllowed()) { + Core::App().settings().setGroupCallPushToTalk(false); + } + const auto tryFillFromManager = [=] { + state->shortcut = state->manager + ? state->manager->shortcutFromSerialized( + Core::App().settings().groupCallPushToTalkShortcut()) + : nullptr; + state->shortcutText = state->shortcut + ? state->shortcut->toDisplayString() + : QString(); + }; + state->manager = settings.groupCallPushToTalk() + ? call->ensureGlobalShortcutManager() + : nullptr; + tryFillFromManager(); + state->delay = settings.groupCallPushToTalkDelay(); - 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())); + )->toggleOn(rpl::single( + settings.groupCallPushToTalk() + ) | rpl::then(state->pushToTalkToggles.events())); const auto pushToTalkWrap = layout->add( object_ptr>( layout, @@ -220,6 +239,65 @@ void GroupCallSettingsBox( call->applyGlobalShortcutChanges(); Core::App().saveSettingsDelayed(); }; + const auto showPrivacyRequest = [=] { +#ifdef Q_OS_MAC + if (!Platform::IsMac10_14OrGreater()) { + return; + } + const auto requestInputMonitoring = Platform::IsMac10_15OrGreater(); + box->getDelegate()->show(Box([=](not_null box) { + box->addRow( + object_ptr( + box.get(), + rpl::combine( + tr::lng_group_call_mac_access(), + (requestInputMonitoring + ? tr::lng_group_call_mac_input() + : tr::lng_group_call_mac_accessibility()) + ) | rpl::map([](QString a, QString b) { + auto result = Ui::Text::RichLangValue(a); + result.append("\n\n").append(Ui::Text::RichLangValue(b)); + return result; + }), + st::groupCallBoxLabel), + style::margins( + st::boxRowPadding.left(), + st::boxPadding.top(), + st::boxRowPadding.right(), + st::boxPadding.bottom())); + box->addButton(tr::lng_group_call_mac_settings(), [=] { + if (requestInputMonitoring) { + Platform::OpenInputMonitoringPrivacySettings(); + } else { + Platform::OpenAccessibilityPrivacySettings(); + } + }); + box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); + + if (!requestInputMonitoring) { + // Accessibility is enabled without app restart, so short-poll it. + base::timer_each( + kCheckAccessibilityInterval + ) | rpl::filter([] { + return base::GlobalShortcutsAllowed(); + }) | rpl::start_with_next([=] { + box->closeBox(); + }, box->lifetime()); + } + })); +#endif // Q_OS_MAC + }; + const auto ensureManager = [=] { + if (state->manager) { + return true; + } else if (base::GlobalShortcutsAllowed()) { + state->manager = call->ensureGlobalShortcutManager(); + tryFillFromManager(); + return true; + } + showPrivacyRequest(); + return false; + }; const auto stopRecording = [=] { state->recording = false; state->recordText = tr::lng_group_call_ptt_shortcut(); @@ -227,9 +305,16 @@ void GroupCallSettingsBox( ? state->shortcut->toDisplayString() : QString(); recording->setColorOverride(std::nullopt); - manager->stopRecording(); + if (state->manager) { + state->manager->stopRecording(); + } }; const auto startRecording = [=] { + if (!ensureManager()) { + state->pushToTalkToggles.fire(false); + pushToTalkWrap->hide(anim::type::instant); + return; + } state->recording = true; state->recordText = tr::lng_group_call_ptt_recording(); recording->setColorOverride( @@ -245,7 +330,7 @@ void GroupCallSettingsBox( applyAndSave(); stopRecording(); }); - manager->startRecording(std::move(progress), std::move(done)); + state->manager->startRecording(std::move(progress), std::move(done)); }; recording->addClickHandler([=] { if (state->recording) { @@ -293,6 +378,10 @@ void GroupCallSettingsBox( ) | rpl::start_with_next([=](bool toggled) { if (!toggled) { stopRecording(); + } else if (!ensureManager()) { + state->pushToTalkToggles.fire(false); + pushToTalkWrap->hide(anim::type::instant); + return; } Core::App().settings().setGroupCallPushToTalk(toggled); applyAndSave(); diff --git a/Telegram/lib_base b/Telegram/lib_base index aadebb110e..01a7497eb8 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit aadebb110e2fe3438cf99b64427b604564ea4ade +Subproject commit 01a7497eb84abc2353011d7f5dd4a5e6441e6957