/* This file is part of Telegram Desktop, the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "calls/calls_group_settings.h" #include "calls/calls_group_call.h" #include "calls/calls_group_panel.h" // LeaveGroupCallBox. #include "calls/calls_instance.h" #include "ui/widgets/level_meter.h" #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_chat.h" #include "data/data_group_call.h" #include "core/application.h" #include "boxes/single_choice_box.h" #include "webrtc/webrtc_audio_input_tester.h" #include "webrtc/webrtc_media_devices.h" #include "settings/settings_common.h" #include "settings/settings_calls.h" #include "main/main_session.h" #include "apiwrap.h" #include "styles/style_layers.h" #include "styles/style_calls.h" #include "styles/style_settings.h" #include namespace Calls { namespace { constexpr auto kDelaysCount = 201; constexpr auto kCheckAccessibilityInterval = crl::time(500); void SaveCallJoinMuted( not_null peer, uint64 callId, bool joinMuted) { const auto call = peer->groupCall(); if (!call || call->id() != callId || !peer->canManageGroupCall() || !call->canChangeJoinMuted() || call->joinMuted() == joinMuted) { return; } call->setJoinMutedLocally(joinMuted); peer->session().api().request(MTPphone_ToggleGroupCallSettings( MTP_flags(MTPphone_ToggleGroupCallSettings::Flag::f_join_muted), call->input(), MTP_bool(joinMuted) )).send(); } [[nodiscard]] crl::time DelayByIndex(int index) { return index * crl::time(10); } [[nodiscard]] QString FormatDelay(crl::time delay) { return (delay < crl::time(1000)) ? tr::lng_group_call_ptt_delay_ms( tr::now, lt_amount, QString::number(delay)) : tr::lng_group_call_ptt_delay_s( tr::now, lt_amount, QString::number(delay / 1000., 'f', 2)); } } // namespace void GroupCallSettingsBox( not_null box, not_null call) { using namespace Settings; const auto weakCall = base::make_weak(call.get()); const auto weakBox = Ui::MakeWeak(box); struct State { rpl::event_stream outputNameStream; rpl::event_stream inputNameStream; std::unique_ptr micTester; Ui::LevelMeter *micTestLevel = nullptr; float micLevel = 0.; Ui::Animations::Simple micLevelAnimation; base::Timer levelUpdateTimer; bool generatingLink = false; }; const auto state = box->lifetime().make_state(); const auto peer = call->peer(); const auto real = peer->groupCall(); const auto id = call->id(); const auto goodReal = (real && real->id() == id); const auto layout = box->verticalLayout(); const auto &settings = Core::App().settings(); const auto joinMuted = goodReal ? real->joinMuted() : false; const auto canChangeJoinMuted = (goodReal && real->canChangeJoinMuted()); const auto addCheck = (peer->canManageGroupCall() && canChangeJoinMuted); if (addCheck) { AddSkip(layout); } const auto muteJoined = addCheck ? AddButton( layout, tr::lng_group_call_new_muted(), st::groupCallSettingsButton)->toggleOn(rpl::single(joinMuted)) : nullptr; if (addCheck) { AddSkip(layout); } AddButtonWithLabel( layout, tr::lng_group_call_speakers(), rpl::single( CurrentAudioOutputName() ) | rpl::then( state->outputNameStream.events() ), st::groupCallSettingsButton )->addClickHandler([=] { box->getDelegate()->show(ChooseAudioOutputBox(crl::guard(box, [=]( const QString &id, const QString &name) { state->outputNameStream.fire_copy(name); }), &st::groupCallCheckbox, &st::groupCallRadio)); }); AddButtonWithLabel( layout, tr::lng_group_call_microphone(), rpl::single( CurrentAudioInputName() ) | rpl::then( state->inputNameStream.events() ), st::groupCallSettingsButton )->addClickHandler([=] { box->getDelegate()->show(ChooseAudioInputBox(crl::guard(box, [=]( const QString &id, const QString &name) { state->inputNameStream.fire_copy(name); if (state->micTester) { state->micTester->setDeviceId(id); } }), &st::groupCallCheckbox, &st::groupCallRadio)); }); state->micTestLevel = box->addRow( object_ptr( box.get(), st::groupCallLevelMeter), st::settingsLevelMeterPadding); state->micTestLevel->resize(QSize(0, st::defaultLevelMeter.height)); state->levelUpdateTimer.setCallback([=] { const auto was = state->micLevel; state->micLevel = state->micTester->getAndResetLevel(); state->micLevelAnimation.start([=] { state->micTestLevel->setValue( state->micLevelAnimation.value(state->micLevel)); }, was, state->micLevel, kMicTestAnimationDuration); }); AddSkip(layout); //AddDivider(layout); //AddSkip(layout); using GlobalShortcut = base::GlobalShortcut; 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; }; if (base::GlobalShortcutsAvailable()) { const auto state = box->lifetime().make_state(); 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(); const auto pushToTalk = AddButton( layout, tr::lng_group_call_push_to_talk(), st::groupCallSettingsButton )->toggleOn(rpl::single( settings.groupCallPushToTalk() ) | rpl::then(state->pushToTalkToggles.events())); const auto pushToTalkWrap = layout->add( object_ptr>( layout, object_ptr(layout))); const auto pushToTalkInner = pushToTalkWrap->entity(); const auto recording = pushToTalkInner->add( object_ptr