From aa0c56876c9a11e77ea15a638c982d7b508bab64 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 21 Jan 2025 14:24:43 +0400 Subject: [PATCH] Use sound in native notifications on macOS. --- .../media/audio/media_audio_local_cache.cpp | 30 +++++--------- .../media/audio/media_audio_local_cache.h | 17 ++++++-- .../linux/notifications_manager_linux.cpp | 2 +- .../platform/mac/notifications_manager_mac.mm | 40 +++++++++++++++++-- .../window/notifications_manager.cpp | 13 +++--- .../window/notifications_manager.h | 3 +- 6 files changed, 70 insertions(+), 35 deletions(-) diff --git a/Telegram/SourceFiles/media/audio/media_audio_local_cache.cpp b/Telegram/SourceFiles/media/audio/media_audio_local_cache.cpp index ada57959e..6fa45d2e3 100644 --- a/Telegram/SourceFiles/media/audio/media_audio_local_cache.cpp +++ b/Telegram/SourceFiles/media/audio/media_audio_local_cache.cpp @@ -314,30 +314,20 @@ LocalCache::~LocalCache() { } } -QString LocalCache::path( +LocalSound LocalCache::sound( DocumentId id, - Fn resolveBytes, - Fn fallbackBytes) { + Fn resolveOriginalBytes, + Fn fallbackOriginalBytes) { auto &result = _cache[id]; if (!result.isEmpty()) { - return result; + return { id, result }; } - const auto bytes = resolveBytes(); - if (bytes.isEmpty()) { - return fallbackBytes ? path(0, fallbackBytes, nullptr) : QString(); - } - const auto prefix = cWorkingDir() + u"tdata/audio_cache"_q; - QDir().mkpath(prefix); - - const auto name = QString::number(id, 16).toUpper(); - result = u"%1/%2.wav"_q.arg(prefix, name); - auto file = QFile(result); - if (!file.open(QIODevice::WriteOnly)) { - return fallbackBytes ? path(0, fallbackBytes, nullptr) : QString(); - } - file.write(ConvertAndCut(bytes)); - file.close(); - return result; + result = resolveOriginalBytes(); + return !result.isEmpty() + ? LocalSound{ id, result } + : fallbackOriginalBytes + ? sound(0, fallbackOriginalBytes, nullptr) + : LocalSound(); } } // namespace Media::Audio diff --git a/Telegram/SourceFiles/media/audio/media_audio_local_cache.h b/Telegram/SourceFiles/media/audio/media_audio_local_cache.h index fefc06738..ee21b3f1b 100644 --- a/Telegram/SourceFiles/media/audio/media_audio_local_cache.h +++ b/Telegram/SourceFiles/media/audio/media_audio_local_cache.h @@ -9,18 +9,27 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Media::Audio { +struct LocalSound { + DocumentId id = 0; + QByteArray wav; + + explicit operator bool() const { + return !wav.isEmpty(); + } +}; + class LocalCache final { public: LocalCache() = default; ~LocalCache(); - [[nodiscard]] QString path( + [[nodiscard]] LocalSound sound( DocumentId id, - Fn resolveBytes, - Fn fallbackBytes); + Fn resolveOriginalBytes, + Fn fallbackOriginalBytes); private: - base::flat_map _cache; + base::flat_map _cache; }; diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index 0115d688a..76487bfad 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -232,7 +232,7 @@ NotificationData::NotificationData( bool NotificationData::init(const Info &info) { const auto &title = info.title; const auto &subtitle = info.subtitle; - //const auto sound = info.soundPath ? info.soundPath() : QString(); + //const auto sound = info.sound ? info.sound() : QString(); if (_application) { _notification = Gio::Notification::new_( subtitle.isEmpty() diff --git a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm index 3e89b7a36..d88ab4a1b 100644 --- a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm +++ b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm @@ -229,6 +229,8 @@ private: void clearingThreadLoop(); void checkFocusState(); + [[nodiscard]] QString cacheSound(const Media::Audio::LocalSound &sound); + const uint64 _managerId = 0; QString _managerIdString; @@ -266,6 +268,7 @@ private: QProcess _dnd; QProcess _focus; std::vector> _focusedCallbacks; + base::flat_map _cachedSounds; bool _waitingDnd = false; bool _waitingFocus = false; bool _focused = false; @@ -289,6 +292,37 @@ Manager::Private::Private(Manager *manager) }, _lifetime); } +QString Manager::Private::cacheSound(const Media::Audio::LocalSound &sound) { + const auto i = _cachedSounds.find(sound.id); + if (i != end(_cachedSounds)) { + return i->second; + } + + auto result = u"TDesktop-%1"_q.arg(sound.id + ? QString::number(sound.id, 16).toUpper() + : u"Default"_q); + + NSArray *paths = NSSearchPathForDirectoriesInDomains( + NSLibraryDirectory, + NSUserDomainMask, + YES); + NSString *library = [paths firstObject]; + NSString *sounds = [library stringByAppendingPathComponent:@"Sounds"]; + const auto folder = NS2QString(sounds); + + const auto path = folder + u"/%1.wav"_q.arg(result); + QDir().mkpath(folder); + + auto f = QFile(path); + if (f.open(QIODevice::WriteOnly)) { + f.write(sound.wav); + f.close(); + } + + _cachedSounds.emplace(sound.id, result); + return result; +} + void Manager::Private::showNotification( NotificationInfo &&info, Ui::PeerUserpicView &userpicView) { @@ -334,9 +368,9 @@ void Manager::Private::showNotification( [notification setHasReplyButton:YES]; } - const auto sound = info.soundPath ? info.soundPath() : QString(); - if (!sound.isEmpty()) { - [notification setSoundName:Q2NSString(sound)]; + const auto sound = info.sound ? info.sound() : Media::Audio::LocalSound(); + if (sound) { + [notification setSoundName:Q2NSString(cacheSound(sound))]; } else { [notification setSoundName:nil]; } diff --git a/Telegram/SourceFiles/window/notifications_manager.cpp b/Telegram/SourceFiles/window/notifications_manager.cpp index 2c0ed98b9..15aa20444 100644 --- a/Telegram/SourceFiles/window/notifications_manager.cpp +++ b/Telegram/SourceFiles/window/notifications_manager.cpp @@ -118,8 +118,9 @@ constexpr auto kSystemAlertDuration = crl::time(0); || notifySettings->muteUnknown(from)); const auto fromAlert = !fromUnknown && !notifySettings->isMuted(from); - return (threadAlert || fromAlert) - ? notifySettings->sound(thread).id + const auto &sound = notifySettings->sound(thread); + return ((threadAlert || fromAlert) && !sound.none) + ? sound.id : std::optional(); } @@ -1252,13 +1253,13 @@ void NativeManager::doShowNotification(NotificationFields &&fields) { // #TODO optimize auto userpicView = item->history()->peer->createUserpicView(); const auto owner = &item->history()->owner(); - const auto soundPath = fields.soundId ? [=, id = *fields.soundId] { - return _localSoundCache.path(id, [=] { + const auto sound = fields.soundId ? [=, id = *fields.soundId] { + return _localSoundCache.sound(id, [=] { return Core::App().notifications().lookupSoundBytes(owner, id); }, [=] { return Core::App().notifications().lookupSoundBytes(owner, 0); }); - } : Fn(); + } : Fn(); doShowNativeNotification({ .peer = item->history()->peer, .topicRootId = item->topicRootId(), @@ -1266,7 +1267,7 @@ void NativeManager::doShowNotification(NotificationFields &&fields) { .title = scheduled ? WrapFromScheduled(fullTitle) : fullTitle, .subtitle = subtitle, .message = text, - .soundPath = soundPath, + .sound = sound, .options = options, }, userpicView); } diff --git a/Telegram/SourceFiles/window/notifications_manager.h b/Telegram/SourceFiles/window/notifications_manager.h index 58b08f94a..e04314219 100644 --- a/Telegram/SourceFiles/window/notifications_manager.h +++ b/Telegram/SourceFiles/window/notifications_manager.h @@ -399,6 +399,7 @@ public: return ManagerType::Native; } + using NotificationSound = Media::Audio::LocalSound; struct NotificationInfo { not_null peer; MsgId topicRootId = 0; @@ -406,7 +407,7 @@ public: QString title; QString subtitle; QString message; - Fn soundPath; + Fn sound; DisplayOptions options; };