Play ringtones on click in select box.

This commit is contained in:
John Preston 2022-04-07 11:34:29 +04:00
parent f5164fe3e4
commit d35b8f82a3
7 changed files with 107 additions and 23 deletions

View file

@ -11,7 +11,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/random.h"
#include "base/unixtime.h"
#include "data/data_document.h"
#include "data/data_document_media.h"
#include "data/data_session.h"
#include "data/notify/data_notify_settings.h"
#include "main/main_session.h"
#include "storage/file_upload.h"
#include "storage/localimageloader.h"
@ -83,7 +85,7 @@ void Ringtones::upload(
filemime,
content);
const auto uploadedData = UploadedData{ filename, filemime };
const auto uploadedData = UploadedData{ filename, filemime, content };
const auto fakeId = FullMsgId(
_session->userPeerId(),
_session->data().nextLocalMessageId());
@ -111,9 +113,12 @@ void Ringtones::ready(const FullMsgId &msgId, const MTPInputFile &file) {
file,
MTP_string(uploadedData.filename),
MTP_string(uploadedData.filemime)
)).done([=](const MTPDocument &result) {
)).done([=, content = uploadedData.content](const MTPDocument &result) {
const auto document = _session->data().processDocument(result);
_list.documents.insert(_list.documents.begin(), document->id);
const auto media = document->createMediaView();
media->setBytes(content);
document->owner().notifySettings().cacheSound(document);
_uploadDones.fire_copy(document->id);
}).fail([=](const MTP::Error &error) {
_uploadFails.fire_copy(error.type());
@ -134,6 +139,7 @@ void Ringtones::requestList() {
_list.documents.reserve(data.vringtones().v.size());
for (const auto &d : data.vringtones().v) {
const auto document = _session->data().processDocument(d);
document->forceToCache(true);
_list.documents.emplace_back(document->id);
}
requestList();

View file

@ -42,6 +42,7 @@ private:
struct UploadedData {
QString filename;
QString filemime;
QByteArray content;
};
void ready(const FullMsgId &msgId, const MTPInputFile &file);

View file

@ -13,15 +13,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/call_delayed.h"
#include "base/event_filter.h"
#include "base/unixtime.h"
#include "base/timer_rpl.h"
#include "core/file_utilities.h"
#include "core/mime_type.h"
#include "core/application.h"
#include "data/data_document.h"
#include "data/data_document_media.h"
#include "data/data_document_resolver.h"
#include "data/data_peer.h"
#include "data/data_session.h"
#include "data/notify/data_notify_settings.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "media/audio/media_audio.h"
#include "settings/settings_common.h"
#include "ui/boxes/confirm_box.h"
#include "ui/widgets/buttons.h"
@ -40,6 +44,50 @@ namespace {
constexpr auto kDefaultValue = -1;
constexpr auto kNoSoundValue = -2;
constexpr auto kNoDetachTimeout = crl::time(250);
class AudioCreator final {
public:
AudioCreator();
AudioCreator(AudioCreator &&other);
AudioCreator &operator=(AudioCreator &&other);
~AudioCreator();
private:
rpl::lifetime _lifetime;
bool _attached = false;
};
AudioCreator::AudioCreator()
: _attached(true) {
crl::async([] {
QMutexLocker lock(Media::Player::internal::audioPlayerMutex());
Media::Audio::AttachToDevice();
});
base::timer_each(
kNoDetachTimeout
) | rpl::start_with_next([=] {
Media::Audio::StopDetachIfNotUsedSafe();
}, _lifetime);
}
AudioCreator::AudioCreator(AudioCreator &&other)
: _lifetime(base::take(other._lifetime))
, _attached(base::take(other._attached)) {
}
AudioCreator &AudioCreator::operator=(AudioCreator &&other) {
_lifetime = base::take(other._lifetime);
_attached = base::take(other._attached);
return *this;
}
AudioCreator::~AudioCreator() {
if (_attached) {
Media::Audio::ScheduleDetachIfNotUsedSafe();
}
}
} // namespace
@ -79,8 +127,9 @@ void RingtonesBox(
padding.setTop(padding.bottom());
struct State {
AudioCreator creator;
std::shared_ptr<Ui::RadiobuttonGroup> group;
std::vector<DocumentId> documentIds;
std::vector<std::shared_ptr<Data::DocumentMedia>> medias;
Data::NotifySound chosen;
base::unique_qptr<Ui::PopupMenu> menu;
QPointer<Ui::Radiobutton> defaultButton;
@ -113,6 +162,9 @@ void RingtonesBox(
}
if (value == kDefaultValue) {
state->defaultButton = button;
button->setClickedCallback([=] {
Core::App().notifications().playSound(session, 0);
});
}
if (value < 0) {
return;
@ -120,6 +172,14 @@ void RingtonesBox(
while (state->buttons.size() <= value) {
state->buttons.push_back(nullptr);
}
button->setClickedCallback([=] {
const auto media = state->medias[value].get();
if (media->loaded()) {
Core::App().notifications().playSound(
session,
media->owner()->id);
}
});
base::install_event_filter(button, [=](not_null<QEvent*> e) {
if (e->type() != QEvent::ContextMenu || state->menu) {
return base::EventFilterResult::Continue;
@ -128,7 +188,7 @@ void RingtonesBox(
button,
st::popupMenuWithIcons);
auto callback = [=] {
const auto id = state->documentIds[value];
const auto id = state->medias[value]->owner()->id;
session->api().ringtones().remove(id);
};
state->menu->addAction(
@ -172,7 +232,7 @@ void RingtonesBox(
object_ptr<Ui::VerticalLayout>(container));
const auto rebuild = [=] {
state->documentIds.clear();
const auto old = base::take(state->medias);
auto value = 0;
while (custom->count()) {
delete custom->widgetAt(0);
@ -183,7 +243,8 @@ void RingtonesBox(
const auto document = session->data().document(id);
const auto text = ExtractRingtoneName(document);
addToGroup(custom, value++, text, chosen);
state->documentIds.push_back(id);
state->medias.push_back(document->createMediaView());
document->owner().notifySettings().cacheSound(document);
}
custom->resizeToWidth(container->width());
@ -260,7 +321,7 @@ void RingtonesBox(
? Data::NotifySound()
: (value == kNoSoundValue)
? Data::NotifySound{ .none = true }
: Data::NotifySound{ .id = state->documentIds[value] };
: Data::NotifySound{ .id = state->medias[value]->owner()->id };
save(sound);
box->closeBox();
});

View file

@ -163,24 +163,16 @@ void NotifySettings::updateLocal(not_null<PeerData*> peer) {
}
if (const auto sound = peer->notifySound(); sound && sound->id) {
const auto cache = [=](DocumentId id) {
if (const auto document = _owner->document(id)) {
const auto view = document->createMediaView();
_ringtones.views.emplace(id, view);
document->forceToCache(true);
document->save(Data::FileOriginRingtones(), QString());
}
};
if (const auto doc = _owner->document(sound->id); !doc->isNull()) {
cache(sound->id);
cacheSound(doc);
} else {
_ringtones.pendingIds.push_back(sound->id);
if (!_ringtones.pendingLifetime) {
// Not requested yet.
_owner->session().api().ringtones().listUpdates(
) | rpl::start_with_next([=] {
for (const auto &id : base::take(_ringtones.pendingIds)) {
cache(id);
for (const auto id : base::take(_ringtones.pendingIds)) {
cacheSound(id);
}
_ringtones.pendingLifetime.destroy();
}, _ringtones.pendingLifetime);
@ -190,6 +182,20 @@ void NotifySettings::updateLocal(not_null<PeerData*> peer) {
}
}
void NotifySettings::cacheSound(DocumentId id) {
cacheSound(_owner->document(id));
}
void NotifySettings::cacheSound(not_null<DocumentData*> document) {
if (document->isNull()) {
return;
}
const auto view = document->createMediaView();
_ringtones.views.emplace(document->id, view);
document->forceToCache(true);
document->save(Data::FileOriginRingtones(), QString());
}
void NotifySettings::updateLocal(DefaultNotify type) {
defaultValue(type).updates.fire({});

View file

@ -39,7 +39,10 @@ public:
std::optional<NotifySound> sound = std::nullopt);
void resetToDefault(not_null<PeerData*> peer);
std::shared_ptr<DocumentMedia> lookupRingtone(DocumentId id) const;
void cacheSound(DocumentId id);
void cacheSound(not_null<DocumentData*> document);
[[nodiscard]] std::shared_ptr<DocumentMedia> lookupRingtone(
DocumentId id) const;
[[nodiscard]] rpl::producer<> defaultUpdates(DefaultNotify type) const;
[[nodiscard]] rpl::producer<> defaultUpdates(

View file

@ -510,7 +510,7 @@ void System::showNext() {
}
if (settings.soundNotify() && !_manager->skipAudio()) {
const auto track = lookupSound(
alertPeer,
&alertPeer->owner(),
alertPeer->owner().notifySettings().sound(alertPeer).id);
track->playOnce();
Media::Player::mixer()->suppressAll(track->getLengthMs());
@ -699,7 +699,7 @@ void System::showNext() {
}
not_null<Media::Audio::Track*> System::lookupSound(
not_null<PeerData*> peer,
not_null<Data::Session*> owner,
DocumentId id) {
if (!id) {
ensureSoundCreated();
@ -709,7 +709,7 @@ not_null<Media::Audio::Track*> System::lookupSound(
if (i != end(_customSoundTracks)) {
return i->second.get();
}
const auto &notifySettings = peer->owner().notifySettings();
const auto &notifySettings = owner->notifySettings();
const auto custom = notifySettings.lookupRingtone(id);
if (custom && !custom->bytes().isEmpty()) {
const auto j = _customSoundTracks.emplace(
@ -747,6 +747,10 @@ void System::notifySettingsChanged(ChangeType type) {
return _settingsChanged.fire(std::move(type));
}
void System::playSound(not_null<Main::Session*> session, DocumentId id) {
lookupSound(&session->data(), id)->playOnce();
}
Manager::DisplayOptions Manager::getNotificationOptions(
HistoryItem *item,
ItemNotificationType type) const {

View file

@ -15,6 +15,7 @@ struct ItemNotification;
enum class ItemNotificationType;
namespace Data {
class Session;
class CloudImageView;
} // namespace Data
@ -99,6 +100,8 @@ public:
[[nodiscard]] rpl::producer<ChangeType> settingsChanged() const;
void notifySettingsChanged(ChangeType type);
void playSound(not_null<Main::Session*> session, DocumentId id);
[[nodiscard]] rpl::lifetime &lifetime() {
return _lifetime;
}
@ -159,7 +162,7 @@ private:
void showGrouped();
void ensureSoundCreated();
[[nodiscard]] not_null<Media::Audio::Track*> lookupSound(
not_null<PeerData*> peer,
not_null<Data::Session*> owner,
DocumentId id);
base::flat_map<