From 32e650548f2786e10917503a76aeb637f9826362 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 15 Mar 2023 15:00:20 +0400 Subject: [PATCH] One more attempt to fix DND on macOS. --- .../linux/notifications_manager_linux.cpp | 35 ++--- .../linux/notifications_manager_linux.h | 4 +- .../notifications_manager_linux_dummy.cpp | 12 +- .../platform/mac/notifications_manager_mac.h | 4 +- .../platform/mac/notifications_manager_mac.mm | 131 +++++++++++++++--- .../platform/platform_notifications_manager.h | 4 +- .../win/notifications_manager_win.cpp | 66 ++++++--- .../platform/win/notifications_manager_win.h | 4 +- .../window/notifications_manager.cpp | 28 ++-- .../window/notifications_manager.h | 26 ++-- .../window/notifications_manager_default.cpp | 12 +- .../window/notifications_manager_default.h | 4 +- 12 files changed, 228 insertions(+), 102 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index 2298299534..456d51ff82 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -794,16 +794,16 @@ void NotificationData::notificationReplied( } // namespace -bool SkipAudioForCustom() { - return false; -} - bool SkipToastForCustom() { return false; } -bool SkipFlashBounceForCustom() { - return false; +void MaybePlaySoundForCustom(Fn playSound) { + playSound(); +} + +void MaybeFlashBounceForCustom(Fn flashBounce) { + flashBounce(); } bool WaitForInputForCustom() { @@ -917,10 +917,7 @@ public: void clearFromHistory(not_null history); void clearFromSession(not_null session); void clearNotification(NotificationId id); - - [[nodiscard]] bool inhibited() const { - return _inhibited; - } + void invokeIfNotInhibited(Fn callback); ~Private(); @@ -1154,6 +1151,12 @@ void Manager::Private::clearNotification(NotificationId id) { } } +void Manager::Private::invokeIfNotInhibited(Fn callback) { + if (!_inhibited) { + callback(); + } +} + Manager::Private::~Private() { clearAll(); @@ -1215,16 +1218,16 @@ void Manager::doClearFromSession(not_null session) { _private->clearFromSession(session); } -bool Manager::doSkipAudio() const { - return _private->inhibited(); -} - bool Manager::doSkipToast() const { return false; } -bool Manager::doSkipFlashBounce() const { - return _private->inhibited(); +void Manager::doMaybePlaySound(Fn playSound) { + _private->invokeIfNotInhibited(std::move(playSound)); +} + +void Manager::doMaybeFlashBounce(Fn flashBounce) { + _private->invokeIfNotInhibited(std::move(flashBounce)); } } // namespace Notifications diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h index de0c8cad89..adefaff6ce 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h @@ -33,9 +33,9 @@ protected: void doClearFromTopic(not_null topic) override; void doClearFromHistory(not_null history) override; void doClearFromSession(not_null session) override; - bool doSkipAudio() const override; bool doSkipToast() const override; - bool doSkipFlashBounce() const override; + void doMaybePlaySound(Fn playSound) override; + void doMaybeFlashBounce(Fn flashBounce) override; private: class Private; diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux_dummy.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux_dummy.cpp index 1209cdb6ee..e5ac40868c 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux_dummy.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux_dummy.cpp @@ -13,16 +13,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Platform { namespace Notifications { -bool SkipAudioForCustom() { - return false; -} - bool SkipToastForCustom() { return false; } -bool SkipFlashBounceForCustom() { - return false; +void MaybePlaySoundForCustom(Fn playSound) { + playSound(); +} + +void MaybeFlashBounceForCustom(Fn flashBounce) { + flashBounce(); } bool WaitForInputForCustom() { diff --git a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.h b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.h index 7a5ed2f15a..161c1d3088 100644 --- a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.h +++ b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.h @@ -34,9 +34,9 @@ protected: void doClearFromHistory(not_null history) override; void doClearFromSession(not_null session) override; QString accountNameSeparator() override; - bool doSkipAudio() const override; bool doSkipToast() const override; - bool doSkipFlashBounce() const override; + void doMaybePlaySound(Fn playSound) override; + void doMaybeFlashBounce(Fn flashBounce) override; private: class Private; diff --git a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm index 761ba6fe38..cc747016cc 100644 --- a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm +++ b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm @@ -27,17 +27,34 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace { -static constexpr auto kQuerySettingsEachMs = 1000; -auto DoNotDisturbEnabled = false; -auto LastSettingsQueryMs = 0; +constexpr auto kQuerySettingsEachMs = crl::time(1000); + +crl::time LastSettingsQueryMs/* = 0*/; +bool DoNotDisturbEnabled/* = false*/; + +[[nodiscard]] bool ShouldQuerySettings() { + const auto now = crl::now(); + if (LastSettingsQueryMs > 0 && now <= LastSettingsQueryMs + kQuerySettingsEachMs) { + return false; + } + LastSettingsQueryMs = now; + return true; +} + +[[nodiscard]] QString LibraryPath() { + static const auto result = [] { + NSURL *url = [[NSFileManager defaultManager] URLForDirectory:NSLibraryDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil]; + return url + ? QString::fromUtf8([[url path] fileSystemRepresentation]) + : QString(); + }(); + return result; +} void queryDoNotDisturbState() { - auto ms = crl::now(); - if (LastSettingsQueryMs > 0 && ms <= LastSettingsQueryMs + kQuerySettingsEachMs) { + if (!ShouldQuerySettings()) { return; } - LastSettingsQueryMs = ms; - Boolean isKeyValid; const auto doNotDisturb = CFPreferencesGetAppBooleanValue( CFSTR("doNotDisturb"), @@ -151,16 +168,16 @@ using Manager = Platform::Notifications::Manager; namespace Platform { namespace Notifications { -bool SkipAudioForCustom() { - return false; -} - bool SkipToastForCustom() { return false; } -bool SkipFlashBounceForCustom() { - return false; +void MaybePlaySoundForCustom(Fn playSound) { + playSound(); +} + +void MaybeFlashBounceForCustom(Fn flashBounce) { + flashBounce(); } bool WaitForInputForCustom() { @@ -207,6 +224,8 @@ public: void clearFromSession(not_null session); void updateDelegate(); + void invokeIfNotFocused(Fn callback); + ~Private(); private: @@ -214,6 +233,7 @@ private: void putClearTask(Task task); void clearingThreadLoop(); + void checkFocusState(); const uint64 _managerId = 0; QString _managerIdString; @@ -249,6 +269,14 @@ private: ClearFinish>; std::vector _clearingTasks; + QProcess _dnd; + QProcess _focus; + std::vector> _focusedCallbacks; + bool _waitingDnd = false; + bool _waitingFocus = false; + bool _focused = false; + bool _processesInited = false; + rpl::lifetime _lifetime; }; @@ -457,6 +485,70 @@ void Manager::Private::updateDelegate() { [center setDelegate:_delegate]; } +void Manager::Private::invokeIfNotFocused(Fn callback) { + if (!Platform::IsMac11_0OrGreater()) { + queryDoNotDisturbState(); + if (!DoNotDisturbEnabled) { + callback(); + } + } else if (Platform::IsMacStoreBuild() || LibraryPath().isEmpty()) { + callback(); + } else if (!_focusedCallbacks.empty()) { + _focusedCallbacks.push_back(std::move(callback)); + } else if (!ShouldQuerySettings()) { + if (!_focused) { + callback(); + } + } else { + if (!_processesInited) { + _processesInited = true; + QObject::connect(&_dnd, &QProcess::finished, [=] { + _waitingDnd = false; + checkFocusState(); + }); + QObject::connect(&_focus, &QProcess::finished, [=] { + _waitingFocus = false; + checkFocusState(); + }); + } + const auto start = [](QProcess &process, QString keys) { + auto arguments = QStringList() + << "-extract" + << keys + << "raw" + << "-o" + << "-" + << "--" + << (LibraryPath() + "/Preferences/com.apple.controlcenter.plist"); + DEBUG_LOG(("Focus Check: Started %1.").arg(u"plutil"_q + arguments.join(' '))); + process.start(u"plutil"_q, arguments); + }; + _focusedCallbacks.push_back(std::move(callback)); + _waitingFocus = _waitingDnd = true; + start(_focus, u"NSStatusItem Visible FocusModes"_q); + start(_dnd, u"NSStatusItem Visible DoNotDisturb"_q); + } +} + +void Manager::Private::checkFocusState() { + if (_waitingFocus || _waitingDnd) { + return; + } + const auto istrue = [](QProcess &process) { + const auto output = process.readAllStandardOutput(); + DEBUG_LOG(("Focus Check: %1").arg(output)); + const auto result = (output.trimmed() == u"true"_q); + return result; + }; + _focused = istrue(_focus) || istrue(_dnd); + auto callbacks = base::take(_focusedCallbacks); + if (!_focused) { + for (const auto &callback : callbacks) { + callback(); + } + } +} + Manager::Private::~Private() { if (_clearingThread.joinable()) { putClearTask(ClearFinish()); @@ -517,17 +609,16 @@ QString Manager::accountNameSeparator() { return QString::fromUtf8(" \xE2\x86\x92 "); } -bool Manager::doSkipAudio() const { - queryDoNotDisturbState(); - return DoNotDisturbEnabled; -} - bool Manager::doSkipToast() const { return false; } -bool Manager::doSkipFlashBounce() const { - return doSkipAudio(); +void Manager::doMaybePlaySound(Fn playSound) { + _private->invokeIfNotFocused(std::move(playSound)); +} + +void Manager::doMaybeFlashBounce(Fn flashBounce) { + _private->invokeIfNotFocused(std::move(flashBounce)); } } // namespace Notifications diff --git a/Telegram/SourceFiles/platform/platform_notifications_manager.h b/Telegram/SourceFiles/platform/platform_notifications_manager.h index 0dd577d976..62db3378a7 100644 --- a/Telegram/SourceFiles/platform/platform_notifications_manager.h +++ b/Telegram/SourceFiles/platform/platform_notifications_manager.h @@ -12,9 +12,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Platform { namespace Notifications { -[[nodiscard]] bool SkipAudioForCustom(); [[nodiscard]] bool SkipToastForCustom(); -[[nodiscard]] bool SkipFlashBounceForCustom(); +void MaybePlaySoundForCustom(Fn playSound); +void MaybeFlashBounceForCustom(Fn flashBounce); [[nodiscard]] bool WaitForInputForCustom(); [[nodiscard]] bool Supported(); diff --git a/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp b/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp index beb262be24..156bef5318 100644 --- a/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp +++ b/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp @@ -53,6 +53,19 @@ namespace Notifications { #ifndef __MINGW32__ namespace { +constexpr auto kQuerySettingsEachMs = 1000; + +crl::time LastSettingsQueryMs/* = 0*/; + +[[nodiscard]] bool ShouldQuerySettings() { + const auto now = crl::now(); + if (LastSettingsQueryMs > 0 && now <= LastSettingsQueryMs + kQuerySettingsEachMs) { + return false; + } + LastSettingsQueryMs = now; + return true; +} + [[nodiscard]] std::wstring NotificationTemplate( QString id, Window::Notifications::Manager::DisplayOptions options) { @@ -333,24 +346,16 @@ void QueryUserNotificationState() { } } -static constexpr auto kQuerySettingsEachMs = 1000; -crl::time LastSettingsQueryMs = 0; - void QuerySystemNotificationSettings() { - auto ms = crl::now(); - if (LastSettingsQueryMs > 0 && ms <= LastSettingsQueryMs + kQuerySettingsEachMs) { + if (!ShouldQuerySettings()) { return; } - LastSettingsQueryMs = ms; QueryQuietHours(); QueryFocusAssist(); QueryUserNotificationState(); } -} // namespace -#endif // !__MINGW32__ - -bool SkipAudioForCustom() { +bool SkipSoundForCustom() { QuerySystemNotificationSettings(); return (UserNotificationState == QUNS_NOT_PRESENT) @@ -358,6 +363,19 @@ bool SkipAudioForCustom() { || Core::App().screenIsLocked(); } +bool SkipFlashBounceForCustom() { + return SkipToastForCustom(); +} + +} // namespace +#endif // !__MINGW32__ + +void MaybePlaySoundForCustom(Fn playSound) { + if (!SkipSoundForCustom()) { + playSound(); + } +} + bool SkipToastForCustom() { QuerySystemNotificationSettings(); @@ -365,8 +383,10 @@ bool SkipToastForCustom() { || (UserNotificationState == QUNS_RUNNING_D3D_FULL_SCREEN); } -bool SkipFlashBounceForCustom() { - return SkipToastForCustom(); +void MaybeFlashBounceForCustom(Fn flashBounce) { + if (!SkipFlashBounceForCustom()) { + flashBounce(); + } } bool WaitForInputForCustom() { @@ -942,20 +962,26 @@ void Manager::onAfterNotificationActivated( _private->afterNotificationActivated(id, window); } -bool Manager::doSkipAudio() const { - return SkipAudioForCustom() - || QuietHoursEnabled - || FocusAssistBlocks; -} - bool Manager::doSkipToast() const { return false; } -bool Manager::doSkipFlashBounce() const { - return SkipFlashBounceForCustom() +void Manager::doMaybePlaySound(Fn playSound) { + const auto skip = SkipSoundForCustom() || QuietHoursEnabled || FocusAssistBlocks; + if (!skip) { + playSound(); + } +} + +void Manager::doMaybeFlashBounce(Fn flashBounce) { + const auto skip = SkipFlashBounceForCustom() + || QuietHoursEnabled + || FocusAssistBlocks; + if (!skip) { + flashBounce(); + } } #endif // !__MINGW32__ diff --git a/Telegram/SourceFiles/platform/win/notifications_manager_win.h b/Telegram/SourceFiles/platform/win/notifications_manager_win.h index 675597bc1c..eba5571770 100644 --- a/Telegram/SourceFiles/platform/win/notifications_manager_win.h +++ b/Telegram/SourceFiles/platform/win/notifications_manager_win.h @@ -45,9 +45,9 @@ protected: void onAfterNotificationActivated( NotificationId id, not_null window) override; - bool doSkipAudio() const override; bool doSkipToast() const override; - bool doSkipFlashBounce() const override; + void doMaybePlaySound(Fn playSound) override; + void doMaybeFlashBounce(Fn flashBounce) override; private: class Private; diff --git a/Telegram/SourceFiles/window/notifications_manager.cpp b/Telegram/SourceFiles/window/notifications_manager.cpp index 8fb9b680f1..8344723dff 100644 --- a/Telegram/SourceFiles/window/notifications_manager.cpp +++ b/Telegram/SourceFiles/window/notifications_manager.cpp @@ -574,22 +574,28 @@ void System::showNext() { } const auto &settings = Core::App().settings(); if (alertThread) { - if (settings.flashBounceNotify() && !_manager->skipFlashBounce()) { + if (settings.flashBounceNotify()) { const auto peer = alertThread->peer(); if (const auto window = Core::App().windowFor(peer)) { - if (const auto handle = window->widget()->windowHandle()) { - handle->alert(kSystemAlertDuration); - // (handle, SLOT(_q_clearAlert())); in the future. + if (const auto controller = window->sessionController()) { + _manager->maybeFlashBounce(crl::guard(controller, [=] { + if (const auto handle = window->widget()->windowHandle()) { + handle->alert(kSystemAlertDuration); + // (handle, SLOT(_q_clearAlert())); in the future. + } + })); } } } - if (settings.soundNotify() && !_manager->skipAudio()) { - const auto track = lookupSound( - &alertThread->owner(), - alertThread->owner().notifySettings().sound(alertThread).id); - track->playOnce(); - Media::Player::mixer()->suppressAll(track->getLengthMs()); - Media::Player::mixer()->scheduleFaderCallback(); + if (settings.soundNotify()) { + const auto owner = &alertThread->owner(); + const auto id = owner->notifySettings().sound(alertThread).id; + _manager->maybePlaySound(crl::guard(&owner->session(), [=] { + const auto track = lookupSound(owner, id); + track->playOnce(); + Media::Player::mixer()->suppressAll(track->getLengthMs()); + Media::Player::mixer()->scheduleFaderCallback(); + })); } } diff --git a/Telegram/SourceFiles/window/notifications_manager.h b/Telegram/SourceFiles/window/notifications_manager.h index 4ea6308f94..fb0a010d4a 100644 --- a/Telegram/SourceFiles/window/notifications_manager.h +++ b/Telegram/SourceFiles/window/notifications_manager.h @@ -337,14 +337,14 @@ public: [[nodiscard]] virtual ManagerType type() const = 0; - [[nodiscard]] bool skipAudio() const { - return doSkipAudio(); - } [[nodiscard]] bool skipToast() const { return doSkipToast(); } - [[nodiscard]] bool skipFlashBounce() const { - return doSkipFlashBounce(); + void maybePlaySound(Fn playSound) { + doMaybePlaySound(std::move(playSound)); + } + void maybeFlashBounce(Fn flashBounce) { + doMaybeFlashBounce(std::move(flashBounce)); } virtual ~Manager() = default; @@ -362,9 +362,9 @@ protected: virtual void doClearFromTopic(not_null topic) = 0; virtual void doClearFromHistory(not_null history) = 0; virtual void doClearFromSession(not_null session) = 0; - virtual bool doSkipAudio() const = 0; - virtual bool doSkipToast() const = 0; - virtual bool doSkipFlashBounce() const = 0; + [[nodiscard]] virtual bool doSkipToast() const = 0; + virtual void doMaybePlaySound(Fn playSound) = 0; + virtual void doMaybeFlashBounce(Fn flashBounce) = 0; [[nodiscard]] virtual bool forceHideDetails() const { return false; } @@ -445,14 +445,14 @@ protected: } void doClearFromSession(not_null session) override { } - bool doSkipAudio() const override { - return false; - } bool doSkipToast() const override { return false; } - bool doSkipFlashBounce() const override { - return false; + void doMaybePlaySound(Fn playSound) override { + playSound(); + } + void doMaybeFlashBounce(Fn flashBounce) override { + flashBounce(); } }; diff --git a/Telegram/SourceFiles/window/notifications_manager_default.cpp b/Telegram/SourceFiles/window/notifications_manager_default.cpp index 6c78ff755c..80cca195e3 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.cpp +++ b/Telegram/SourceFiles/window/notifications_manager_default.cpp @@ -445,16 +445,16 @@ void Manager::doClearFromItem(not_null item) { } } -bool Manager::doSkipAudio() const { - return Platform::Notifications::SkipAudioForCustom(); -} - bool Manager::doSkipToast() const { return Platform::Notifications::SkipToastForCustom(); } -bool Manager::doSkipFlashBounce() const { - return Platform::Notifications::SkipFlashBounceForCustom(); +void Manager::doMaybePlaySound(Fn playSound) { + Platform::Notifications::MaybePlaySoundForCustom(std::move(playSound)); +} + +void Manager::doMaybeFlashBounce(Fn flashBounce) { + Platform::Notifications::MaybeFlashBounceForCustom(std::move(flashBounce)); } void Manager::doUpdateAll() { diff --git a/Telegram/SourceFiles/window/notifications_manager_default.h b/Telegram/SourceFiles/window/notifications_manager_default.h index ac8b19bda3..b8e5822468 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.h +++ b/Telegram/SourceFiles/window/notifications_manager_default.h @@ -73,9 +73,9 @@ private: void doClearFromHistory(not_null history) override; void doClearFromSession(not_null session) override; void doClearFromItem(not_null item) override; - bool doSkipAudio() const override; bool doSkipToast() const override; - bool doSkipFlashBounce() const override; + void doMaybePlaySound(Fn playSound) override; + void doMaybeFlashBounce(Fn flashBounce) override; void showNextFromQueue(); void unlinkFromShown(Notification *remove);