tdesktop/Telegram/SourceFiles/settings/settings_calls.cpp

373 lines
10 KiB
C++
Raw Normal View History

2019-01-05 11:08:02 +00:00
/*
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 "settings/settings_calls.h"
#include "settings/settings_common.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/level_meter.h"
#include "ui/widgets/buttons.h"
2019-01-05 11:08:02 +00:00
#include "boxes/single_choice_box.h"
#include "boxes/confirm_box.h"
#include "platform/platform_specific.h"
#include "main/main_session.h"
2019-01-05 11:08:02 +00:00
#include "lang/lang_keys.h"
#include "layout.h"
#include "styles/style_settings.h"
#include "ui/widgets/continuous_sliders.h"
2019-08-06 16:40:08 +00:00
#include "window/window_session_controller.h"
#include "core/application.h"
#include "core/core_settings.h"
2019-01-05 11:08:02 +00:00
#include "calls/calls_instance.h"
#include "webrtc/webrtc_media_devices.h"
#include "facades.h"
2019-01-05 11:08:02 +00:00
#ifdef slots
#undef slots
#define NEED_TO_RESTORE_SLOTS
#endif // slots
#include <VoIPController.h>
#ifdef NEED_TO_RESTORE_SLOTS
#define slots Q_SLOTS
#undef NEED_TO_RESTORE_SLOTS
#endif // NEED_TO_RESTORE_SLOTS
namespace Settings {
2019-07-24 14:00:30 +00:00
Calls::Calls(
QWidget *parent,
not_null<Window::SessionController*> controller)
: Section(parent)
, _controller(controller) {
setupContent();
2019-01-05 11:08:02 +00:00
}
2019-01-11 10:07:56 +00:00
Calls::~Calls() {
2019-01-05 11:08:02 +00:00
if (_needWriteSettings) {
Core::App().saveSettingsDelayed();
2019-01-05 11:08:02 +00:00
}
}
2019-01-11 10:07:56 +00:00
void Calls::sectionSaveChanges(FnMut<void()> done) {
2019-01-05 11:08:02 +00:00
if (_micTester) {
_micTester.reset();
}
done();
}
void Calls::setupContent() {
using VoIP = tgvoip::VoIPController;
2019-01-11 10:07:56 +00:00
2019-01-05 11:08:02 +00:00
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
2019-01-11 10:07:56 +00:00
const auto getId = [](const auto &device) {
return device.id;
2019-01-11 10:07:56 +00:00
};
const auto getName = [](const auto &device) {
return device.name;
2019-01-11 10:07:56 +00:00
};
2019-01-05 11:08:02 +00:00
const auto &settings = Core::App().settings();
2019-01-11 10:07:56 +00:00
const auto currentOutputName = [&] {
if (settings.callOutputDeviceID() == qsl("default")) {
2019-06-19 15:09:03 +00:00
return tr::lng_settings_call_device_default(tr::now);
2019-01-05 11:08:02 +00:00
}
const auto list = Webrtc::GetAudioOutputList();
2019-01-11 10:07:56 +00:00
const auto i = ranges::find(
list,
settings.callOutputDeviceID(),
2019-01-11 10:07:56 +00:00
getId);
return (i != end(list))
? getName(*i)
: settings.callOutputDeviceID();
2019-01-11 10:07:56 +00:00
}();
2019-01-05 11:08:02 +00:00
2019-01-11 10:07:56 +00:00
const auto currentInputName = [&] {
if (settings.callInputDeviceID() == qsl("default")) {
2019-06-19 15:09:03 +00:00
return tr::lng_settings_call_device_default(tr::now);
2019-01-05 11:08:02 +00:00
}
const auto list = Webrtc::GetAudioInputList();
2019-01-11 10:07:56 +00:00
const auto i = ranges::find(
list,
settings.callInputDeviceID(),
2019-01-11 10:07:56 +00:00
getId);
return (i != end(list))
? getName(*i)
: settings.callInputDeviceID();
2019-01-11 10:07:56 +00:00
}();
2019-01-05 11:08:02 +00:00
AddSkip(content);
2019-06-18 12:16:43 +00:00
AddSubsectionTitle(content, tr::lng_settings_call_section_output());
2019-01-11 10:07:56 +00:00
AddButtonWithLabel(
2019-01-05 11:08:02 +00:00
content,
2019-06-18 12:16:43 +00:00
tr::lng_settings_call_output_device(),
2019-01-11 10:07:56 +00:00
rpl::single(
currentOutputName
) | rpl::then(
_outputNameStream.events()
),
st::settingsButton
)->addClickHandler([=] {
const auto &devices = Webrtc::GetAudioOutputList();
2019-01-11 10:07:56 +00:00
const auto options = ranges::view::concat(
2019-06-19 15:09:03 +00:00
ranges::view::single(tr::lng_settings_call_device_default(tr::now)),
2019-01-11 10:07:56 +00:00
devices | ranges::view::transform(getName)
) | ranges::to_vector;
const auto i = ranges::find(
devices,
Core::App().settings().callOutputDeviceID(),
2019-01-11 10:07:56 +00:00
getId);
const auto currentOption = (i != end(devices))
? int(i - begin(devices) + 1)
: 0;
const auto save = crl::guard(this, [=](int option) {
_outputNameStream.fire_copy(options[option]);
const auto deviceId = option
? devices[option - 1].id
: "default";
Core::App().settings().setCallOutputDeviceID(deviceId);
Core::App().saveSettingsDelayed();
2020-06-25 17:57:36 +00:00
if (const auto call = Core::App().calls().currentCall()) {
call->setCurrentAudioDevice(false, deviceId.toStdString());
2019-01-05 11:08:02 +00:00
}
});
2019-01-11 10:07:56 +00:00
Ui::show(Box<SingleChoiceBox>(
tr::lng_settings_call_output_device(),
2019-01-11 10:07:56 +00:00
options,
currentOption,
save));
2019-01-05 11:08:02 +00:00
});
2019-01-11 10:07:56 +00:00
const auto outputLabel = content->add(
object_ptr<Ui::LabelSimple>(
content,
st::settingsAudioVolumeLabel),
st::settingsAudioVolumeLabelPadding);
const auto outputSlider = content->add(
object_ptr<Ui::MediaSlider>(
content,
st::settingsAudioVolumeSlider),
st::settingsAudioVolumeSliderPadding);
const auto updateOutputLabel = [=](int value) {
const auto percent = QString::number(value);
outputLabel->setText(
tr::lng_settings_call_output_volume(tr::now, lt_percent, percent));
2019-01-11 10:07:56 +00:00
};
const auto updateOutputVolume = [=](int value) {
_needWriteSettings = true;
updateOutputLabel(value);
Core::App().settings().setCallOutputVolume(value);
2020-06-25 17:57:36 +00:00
if (const auto call = Core::App().calls().currentCall()) {
2019-01-11 10:07:56 +00:00
call->setAudioVolume(false, value / 100.0f);
}
2019-01-05 11:08:02 +00:00
};
outputSlider->resize(st::settingsAudioVolumeSlider.seekSize);
outputSlider->setPseudoDiscrete(
101,
2019-01-11 10:07:56 +00:00
[](int val) { return val; },
settings.callOutputVolume(),
2019-01-11 10:07:56 +00:00
updateOutputVolume);
updateOutputLabel(Core::App().settings().callOutputVolume());
2019-01-05 11:08:02 +00:00
AddSkip(content);
AddDivider(content);
AddSkip(content);
2019-06-18 12:16:43 +00:00
AddSubsectionTitle(content, tr::lng_settings_call_section_input());
2019-01-11 10:07:56 +00:00
AddButtonWithLabel(
2019-01-05 11:08:02 +00:00
content,
2019-06-18 12:16:43 +00:00
tr::lng_settings_call_input_device(),
2019-01-11 10:07:56 +00:00
rpl::single(
currentInputName
) | rpl::then(
_inputNameStream.events()
),
st::settingsButton
)->addClickHandler([=] {
const auto devices = Webrtc::GetAudioInputList();
2019-01-11 10:07:56 +00:00
const auto options = ranges::view::concat(
2019-06-19 15:09:03 +00:00
ranges::view::single(tr::lng_settings_call_device_default(tr::now)),
2019-01-11 10:07:56 +00:00
devices | ranges::view::transform(getName)
) | ranges::to_vector;
const auto i = ranges::find(
devices,
Core::App().settings().callInputDeviceID(),
2019-01-11 10:07:56 +00:00
getId);
const auto currentOption = (i != end(devices))
? int(i - begin(devices) + 1)
: 0;
const auto save = crl::guard(this, [=](int option) {
_inputNameStream.fire_copy(options[option]);
const auto deviceId = option
? devices[option - 1].id
: "default";
Core::App().settings().setCallInputDeviceID(deviceId);
Core::App().saveSettingsDelayed();
2019-01-05 11:08:02 +00:00
if (_micTester) {
stopTestingMicrophone();
}
2020-06-25 17:57:36 +00:00
if (const auto call = Core::App().calls().currentCall()) {
call->setCurrentAudioDevice(true, deviceId.toStdString());
2019-01-05 11:08:02 +00:00
}
});
2019-01-11 10:07:56 +00:00
Ui::show(Box<SingleChoiceBox>(
tr::lng_settings_call_input_device(),
2019-01-11 10:07:56 +00:00
options,
currentOption,
save));
2019-01-05 11:08:02 +00:00
});
2019-01-11 10:07:56 +00:00
const auto inputLabel = content->add(
object_ptr<Ui::LabelSimple>(
content,
st::settingsAudioVolumeLabel),
st::settingsAudioVolumeLabelPadding);
const auto inputSlider = content->add(
object_ptr<Ui::MediaSlider>(
content,
st::settingsAudioVolumeSlider),
st::settingsAudioVolumeSliderPadding);
const auto updateInputLabel = [=](int value) {
const auto percent = QString::number(value);
inputLabel->setText(
tr::lng_settings_call_input_volume(tr::now, lt_percent, percent));
2019-01-11 10:07:56 +00:00
};
const auto updateInputVolume = [=](int value) {
_needWriteSettings = true;
updateInputLabel(value);
Core::App().settings().setCallInputVolume(value);
2020-06-25 17:57:36 +00:00
if (const auto call = Core::App().calls().currentCall()) {
2019-08-06 16:40:08 +00:00
call->setAudioVolume(true, value / 100.0f);
2019-01-11 10:07:56 +00:00
}
2019-01-05 11:08:02 +00:00
};
inputSlider->resize(st::settingsAudioVolumeSlider.seekSize);
inputSlider->setPseudoDiscrete(101,
2019-01-11 10:07:56 +00:00
[](int val) { return val; },
settings.callInputVolume(),
2019-01-11 10:07:56 +00:00
updateInputVolume);
updateInputLabel(settings.callInputVolume());
2019-01-05 11:08:02 +00:00
2019-01-11 10:07:56 +00:00
AddButton(
content,
rpl::single(
2019-06-19 15:09:03 +00:00
tr::lng_settings_call_test_mic(tr::now)
2019-01-11 10:07:56 +00:00
) | rpl::then(
_micTestTextStream.events()
),
st::settingsButton
)->addClickHandler([=] {
2019-01-05 11:08:02 +00:00
if (!_micTester) {
requestPermissionAndStartTestingMicrophone();
} else {
stopTestingMicrophone();
}
});
2019-01-11 10:07:56 +00:00
_micTestLevel = content->add(
object_ptr<Ui::LevelMeter>(
content,
st::defaultLevelMeter),
st::settingsLevelMeterPadding);
_micTestLevel->resize(QSize(0, st::defaultLevelMeter.height));
_levelUpdateTimer.setCallback([=] {
2019-01-05 11:08:02 +00:00
_micTestLevel->setValue(_micTester->GetAndResetLevel());
});
AddSkip(content);
AddDivider(content);
AddSkip(content);
2019-06-18 12:16:43 +00:00
AddSubsectionTitle(content, tr::lng_settings_call_section_other());
2019-01-05 11:08:02 +00:00
#if defined Q_OS_MAC && !defined OS_MAC_STORE
2019-01-05 11:08:02 +00:00
AddButton(
content,
2019-06-18 12:16:43 +00:00
tr::lng_settings_call_audio_ducking(),
2019-01-05 11:08:02 +00:00
st::settingsButton
)->toggleOn(
2020-06-19 17:13:43 +00:00
rpl::single(settings.callAudioDuckingEnabled())
2019-01-05 11:08:02 +00:00
)->toggledValue() | rpl::filter([](bool enabled) {
2020-06-19 17:13:43 +00:00
return (enabled != Core::App().settings().callAudioDuckingEnabled());
2019-08-06 16:40:08 +00:00
}) | rpl::start_with_next([=](bool enabled) {
2020-06-19 17:13:43 +00:00
Core::App().settings().setCallAudioDuckingEnabled(enabled);
Core::App().saveSettingsDelayed();
2020-06-25 17:57:36 +00:00
if (const auto call = Core::App().calls().currentCall()) {
2019-01-11 10:07:56 +00:00
call->setAudioDuckingEnabled(enabled);
2019-01-05 11:08:02 +00:00
}
}, content->lifetime());
#endif // Q_OS_MAC && !OS_MAC_STORE
2019-01-05 11:08:02 +00:00
2019-01-11 10:07:56 +00:00
AddButton(
content,
2019-06-18 12:16:43 +00:00
tr::lng_settings_call_open_system_prefs(),
2019-01-11 10:07:56 +00:00
st::settingsButton
)->addClickHandler([] {
const auto opened = Platform::OpenSystemSettings(
Platform::SystemSettingsType::Audio);
if (!opened) {
2019-06-19 15:09:03 +00:00
Ui::show(Box<InformBox>(tr::lng_linux_no_audio_prefs(tr::now)));
2019-01-05 11:08:02 +00:00
}
});
AddSkip(content);
Ui::ResizeFitChild(this, content);
}
2019-01-11 10:07:56 +00:00
void Calls::requestPermissionAndStartTestingMicrophone() {
const auto status = Platform::GetPermissionStatus(
Platform::PermissionType::Microphone);
2019-01-05 11:08:02 +00:00
if (status == Platform::PermissionStatus::Granted) {
startTestingMicrophone();
} else if (status == Platform::PermissionStatus::CanRequest) {
2019-01-11 10:07:56 +00:00
const auto startTestingChecked = crl::guard(this, [=](
Platform::PermissionStatus status) {
2019-01-05 11:08:02 +00:00
if (status == Platform::PermissionStatus::Granted) {
2019-01-11 10:07:56 +00:00
crl::on_main(crl::guard(this, [=] {
2019-01-05 11:08:02 +00:00
startTestingMicrophone();
}));
}
2019-01-11 10:07:56 +00:00
});
Platform::RequestPermission(
Platform::PermissionType::Microphone,
startTestingChecked);
2019-01-05 11:08:02 +00:00
} else {
2019-01-11 10:07:56 +00:00
const auto showSystemSettings = [] {
Platform::OpenSystemSettingsForPermission(
Platform::PermissionType::Microphone);
2019-01-05 11:08:02 +00:00
Ui::hideLayer();
2019-01-11 10:07:56 +00:00
};
Ui::show(Box<ConfirmBox>(
2019-06-19 15:09:03 +00:00
tr::lng_no_mic_permission(tr::now),
tr::lng_menu_settings(tr::now),
2019-01-11 10:07:56 +00:00
showSystemSettings));
2019-01-05 11:08:02 +00:00
}
}
2019-01-11 10:07:56 +00:00
void Calls::startTestingMicrophone() {
2019-06-19 15:09:03 +00:00
_micTestTextStream.fire(tr::lng_settings_call_stop_mic_test(tr::now));
2019-01-05 11:08:02 +00:00
_levelUpdateTimer.callEach(50);
2019-01-11 10:07:56 +00:00
_micTester = std::make_unique<tgvoip::AudioInputTester>(
Core::App().settings().callInputDeviceID().toStdString());
2019-01-05 11:08:02 +00:00
if (_micTester->Failed()) {
stopTestingMicrophone();
2019-06-19 15:09:03 +00:00
Ui::show(Box<InformBox>(tr::lng_call_error_audio_io(tr::now)));
2019-01-05 11:08:02 +00:00
}
}
2019-01-11 10:07:56 +00:00
void Calls::stopTestingMicrophone() {
2019-06-19 15:09:03 +00:00
_micTestTextStream.fire(tr::lng_settings_call_test_mic(tr::now));
2019-01-05 11:08:02 +00:00
_levelUpdateTimer.cancel();
_micTester.reset();
_micTestLevel->setValue(0.0f);
}
} // namespace Settings