mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 22:54:01 +02:00
Use sound in native notifications on macOS.
This commit is contained in:
parent
37c7b0c6d1
commit
aa0c56876c
6 changed files with 70 additions and 35 deletions
|
@ -314,30 +314,20 @@ LocalCache::~LocalCache() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString LocalCache::path(
|
LocalSound LocalCache::sound(
|
||||||
DocumentId id,
|
DocumentId id,
|
||||||
Fn<QByteArray()> resolveBytes,
|
Fn<QByteArray()> resolveOriginalBytes,
|
||||||
Fn<QByteArray()> fallbackBytes) {
|
Fn<QByteArray()> fallbackOriginalBytes) {
|
||||||
auto &result = _cache[id];
|
auto &result = _cache[id];
|
||||||
if (!result.isEmpty()) {
|
if (!result.isEmpty()) {
|
||||||
return result;
|
return { id, result };
|
||||||
}
|
}
|
||||||
const auto bytes = resolveBytes();
|
result = resolveOriginalBytes();
|
||||||
if (bytes.isEmpty()) {
|
return !result.isEmpty()
|
||||||
return fallbackBytes ? path(0, fallbackBytes, nullptr) : QString();
|
? LocalSound{ id, result }
|
||||||
}
|
: fallbackOriginalBytes
|
||||||
const auto prefix = cWorkingDir() + u"tdata/audio_cache"_q;
|
? sound(0, fallbackOriginalBytes, nullptr)
|
||||||
QDir().mkpath(prefix);
|
: LocalSound();
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Media::Audio
|
} // namespace Media::Audio
|
||||||
|
|
|
@ -9,18 +9,27 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
namespace Media::Audio {
|
namespace Media::Audio {
|
||||||
|
|
||||||
|
struct LocalSound {
|
||||||
|
DocumentId id = 0;
|
||||||
|
QByteArray wav;
|
||||||
|
|
||||||
|
explicit operator bool() const {
|
||||||
|
return !wav.isEmpty();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class LocalCache final {
|
class LocalCache final {
|
||||||
public:
|
public:
|
||||||
LocalCache() = default;
|
LocalCache() = default;
|
||||||
~LocalCache();
|
~LocalCache();
|
||||||
|
|
||||||
[[nodiscard]] QString path(
|
[[nodiscard]] LocalSound sound(
|
||||||
DocumentId id,
|
DocumentId id,
|
||||||
Fn<QByteArray()> resolveBytes,
|
Fn<QByteArray()> resolveOriginalBytes,
|
||||||
Fn<QByteArray()> fallbackBytes);
|
Fn<QByteArray()> fallbackOriginalBytes);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
base::flat_map<DocumentId, QString> _cache;
|
base::flat_map<DocumentId, QByteArray> _cache;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -232,7 +232,7 @@ NotificationData::NotificationData(
|
||||||
bool NotificationData::init(const Info &info) {
|
bool NotificationData::init(const Info &info) {
|
||||||
const auto &title = info.title;
|
const auto &title = info.title;
|
||||||
const auto &subtitle = info.subtitle;
|
const auto &subtitle = info.subtitle;
|
||||||
//const auto sound = info.soundPath ? info.soundPath() : QString();
|
//const auto sound = info.sound ? info.sound() : QString();
|
||||||
if (_application) {
|
if (_application) {
|
||||||
_notification = Gio::Notification::new_(
|
_notification = Gio::Notification::new_(
|
||||||
subtitle.isEmpty()
|
subtitle.isEmpty()
|
||||||
|
|
|
@ -229,6 +229,8 @@ private:
|
||||||
void clearingThreadLoop();
|
void clearingThreadLoop();
|
||||||
void checkFocusState();
|
void checkFocusState();
|
||||||
|
|
||||||
|
[[nodiscard]] QString cacheSound(const Media::Audio::LocalSound &sound);
|
||||||
|
|
||||||
const uint64 _managerId = 0;
|
const uint64 _managerId = 0;
|
||||||
QString _managerIdString;
|
QString _managerIdString;
|
||||||
|
|
||||||
|
@ -266,6 +268,7 @@ private:
|
||||||
QProcess _dnd;
|
QProcess _dnd;
|
||||||
QProcess _focus;
|
QProcess _focus;
|
||||||
std::vector<Fn<void()>> _focusedCallbacks;
|
std::vector<Fn<void()>> _focusedCallbacks;
|
||||||
|
base::flat_map<DocumentId, QString> _cachedSounds;
|
||||||
bool _waitingDnd = false;
|
bool _waitingDnd = false;
|
||||||
bool _waitingFocus = false;
|
bool _waitingFocus = false;
|
||||||
bool _focused = false;
|
bool _focused = false;
|
||||||
|
@ -289,6 +292,37 @@ Manager::Private::Private(Manager *manager)
|
||||||
}, _lifetime);
|
}, _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(
|
void Manager::Private::showNotification(
|
||||||
NotificationInfo &&info,
|
NotificationInfo &&info,
|
||||||
Ui::PeerUserpicView &userpicView) {
|
Ui::PeerUserpicView &userpicView) {
|
||||||
|
@ -334,9 +368,9 @@ void Manager::Private::showNotification(
|
||||||
[notification setHasReplyButton:YES];
|
[notification setHasReplyButton:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto sound = info.soundPath ? info.soundPath() : QString();
|
const auto sound = info.sound ? info.sound() : Media::Audio::LocalSound();
|
||||||
if (!sound.isEmpty()) {
|
if (sound) {
|
||||||
[notification setSoundName:Q2NSString(sound)];
|
[notification setSoundName:Q2NSString(cacheSound(sound))];
|
||||||
} else {
|
} else {
|
||||||
[notification setSoundName:nil];
|
[notification setSoundName:nil];
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,8 +118,9 @@ constexpr auto kSystemAlertDuration = crl::time(0);
|
||||||
|| notifySettings->muteUnknown(from));
|
|| notifySettings->muteUnknown(from));
|
||||||
const auto fromAlert = !fromUnknown
|
const auto fromAlert = !fromUnknown
|
||||||
&& !notifySettings->isMuted(from);
|
&& !notifySettings->isMuted(from);
|
||||||
return (threadAlert || fromAlert)
|
const auto &sound = notifySettings->sound(thread);
|
||||||
? notifySettings->sound(thread).id
|
return ((threadAlert || fromAlert) && !sound.none)
|
||||||
|
? sound.id
|
||||||
: std::optional<DocumentId>();
|
: std::optional<DocumentId>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1252,13 +1253,13 @@ void NativeManager::doShowNotification(NotificationFields &&fields) {
|
||||||
// #TODO optimize
|
// #TODO optimize
|
||||||
auto userpicView = item->history()->peer->createUserpicView();
|
auto userpicView = item->history()->peer->createUserpicView();
|
||||||
const auto owner = &item->history()->owner();
|
const auto owner = &item->history()->owner();
|
||||||
const auto soundPath = fields.soundId ? [=, id = *fields.soundId] {
|
const auto sound = fields.soundId ? [=, id = *fields.soundId] {
|
||||||
return _localSoundCache.path(id, [=] {
|
return _localSoundCache.sound(id, [=] {
|
||||||
return Core::App().notifications().lookupSoundBytes(owner, id);
|
return Core::App().notifications().lookupSoundBytes(owner, id);
|
||||||
}, [=] {
|
}, [=] {
|
||||||
return Core::App().notifications().lookupSoundBytes(owner, 0);
|
return Core::App().notifications().lookupSoundBytes(owner, 0);
|
||||||
});
|
});
|
||||||
} : Fn<QString()>();
|
} : Fn<NotificationSound()>();
|
||||||
doShowNativeNotification({
|
doShowNativeNotification({
|
||||||
.peer = item->history()->peer,
|
.peer = item->history()->peer,
|
||||||
.topicRootId = item->topicRootId(),
|
.topicRootId = item->topicRootId(),
|
||||||
|
@ -1266,7 +1267,7 @@ void NativeManager::doShowNotification(NotificationFields &&fields) {
|
||||||
.title = scheduled ? WrapFromScheduled(fullTitle) : fullTitle,
|
.title = scheduled ? WrapFromScheduled(fullTitle) : fullTitle,
|
||||||
.subtitle = subtitle,
|
.subtitle = subtitle,
|
||||||
.message = text,
|
.message = text,
|
||||||
.soundPath = soundPath,
|
.sound = sound,
|
||||||
.options = options,
|
.options = options,
|
||||||
}, userpicView);
|
}, userpicView);
|
||||||
}
|
}
|
||||||
|
|
|
@ -399,6 +399,7 @@ public:
|
||||||
return ManagerType::Native;
|
return ManagerType::Native;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using NotificationSound = Media::Audio::LocalSound;
|
||||||
struct NotificationInfo {
|
struct NotificationInfo {
|
||||||
not_null<PeerData*> peer;
|
not_null<PeerData*> peer;
|
||||||
MsgId topicRootId = 0;
|
MsgId topicRootId = 0;
|
||||||
|
@ -406,7 +407,7 @@ public:
|
||||||
QString title;
|
QString title;
|
||||||
QString subtitle;
|
QString subtitle;
|
||||||
QString message;
|
QString message;
|
||||||
Fn<QString()> soundPath;
|
Fn<NotificationSound()> sound;
|
||||||
DisplayOptions options;
|
DisplayOptions options;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue