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 "info/profile/info_profile_button.h"
|
|
|
|
#include "boxes/single_choice_box.h"
|
|
|
|
#include "boxes/confirm_box.h"
|
|
|
|
#include "platform/platform_specific.h"
|
|
|
|
#include "lang/lang_keys.h"
|
|
|
|
#include "storage/localstorage.h"
|
|
|
|
#include "layout.h"
|
|
|
|
#include "styles/style_settings.h"
|
|
|
|
#include "ui/widgets/continuous_sliders.h"
|
|
|
|
#include "calls/calls_instance.h"
|
|
|
|
|
|
|
|
#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 {
|
|
|
|
|
|
|
|
Calls::Calls(QWidget *parent, UserData *self)
|
|
|
|
: Section(parent) {
|
|
|
|
setupContent();
|
|
|
|
}
|
|
|
|
|
2019-01-11 10:07:56 +00:00
|
|
|
Calls::~Calls() {
|
2019-01-05 11:08:02 +00:00
|
|
|
if (_needWriteSettings) {
|
|
|
|
Local::writeUserSettings();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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() {
|
2019-01-11 10:07:56 +00:00
|
|
|
using namespace tgvoip;
|
|
|
|
|
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 QString::fromStdString(device.id);
|
|
|
|
};
|
|
|
|
const auto getName = [](const auto &device) {
|
|
|
|
return QString::fromStdString(device.displayName);
|
|
|
|
};
|
2019-01-05 11:08:02 +00:00
|
|
|
|
2019-01-11 10:07:56 +00:00
|
|
|
const auto currentOutputName = [&] {
|
|
|
|
if (Global::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
|
|
|
}
|
2019-01-11 10:07:56 +00:00
|
|
|
const auto &list = VoIPController::EnumerateAudioOutputs();
|
|
|
|
const auto i = ranges::find(
|
|
|
|
list,
|
|
|
|
Global::CallOutputDeviceID(),
|
|
|
|
getId);
|
|
|
|
return (i != end(list))
|
|
|
|
? getName(*i)
|
|
|
|
: Global::CallOutputDeviceID();
|
|
|
|
}();
|
2019-01-05 11:08:02 +00:00
|
|
|
|
2019-01-11 10:07:56 +00:00
|
|
|
const auto currentInputName = [&] {
|
|
|
|
if (Global::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
|
|
|
}
|
2019-01-11 10:07:56 +00:00
|
|
|
const auto &list = VoIPController::EnumerateAudioInputs();
|
|
|
|
const auto i = ranges::find(
|
|
|
|
list,
|
|
|
|
Global::CallInputDeviceID(),
|
|
|
|
getId);
|
|
|
|
return (i != end(list))
|
|
|
|
? getName(*i)
|
|
|
|
: Global::CallInputDeviceID();
|
|
|
|
}();
|
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 = VoIPController::EnumerateAudioOutputs();
|
|
|
|
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,
|
|
|
|
Global::CallOutputDeviceID(),
|
|
|
|
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";
|
|
|
|
Global::SetCallOutputDeviceID(QString::fromStdString(deviceId));
|
2019-01-05 11:08:02 +00:00
|
|
|
Local::writeUserSettings();
|
2019-01-11 10:07:56 +00:00
|
|
|
if (const auto call = ::Calls::Current().currentCall()) {
|
|
|
|
call->setCurrentAudioDevice(false, deviceId);
|
2019-01-05 11:08:02 +00:00
|
|
|
}
|
|
|
|
});
|
2019-01-11 10:07:56 +00:00
|
|
|
Ui::show(Box<SingleChoiceBox>(
|
2019-06-18 15:00:55 +00:00
|
|
|
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(
|
|
|
|
lng_settings_call_output_volume(lt_percent, percent));
|
|
|
|
};
|
|
|
|
const auto updateOutputVolume = [=](int value) {
|
|
|
|
_needWriteSettings = true;
|
|
|
|
updateOutputLabel(value);
|
|
|
|
Global::SetCallOutputVolume(value);
|
|
|
|
if (const auto call = ::Calls::Current().currentCall()) {
|
|
|
|
call->setAudioVolume(false, value / 100.0f);
|
|
|
|
}
|
2019-01-05 11:08:02 +00:00
|
|
|
};
|
|
|
|
outputSlider->resize(st::settingsAudioVolumeSlider.seekSize);
|
|
|
|
outputSlider->setPseudoDiscrete(
|
|
|
|
201,
|
2019-01-11 10:07:56 +00:00
|
|
|
[](int val) { return val; },
|
2019-01-05 11:08:02 +00:00
|
|
|
Global::CallOutputVolume(),
|
2019-01-11 10:07:56 +00:00
|
|
|
updateOutputVolume);
|
2019-01-05 11:08:02 +00:00
|
|
|
updateOutputLabel(Global::CallOutputVolume());
|
|
|
|
|
|
|
|
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 = VoIPController::EnumerateAudioInputs();
|
|
|
|
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,
|
|
|
|
Global::CallInputDeviceID(),
|
|
|
|
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";
|
|
|
|
Global::SetCallInputDeviceID(QString::fromStdString(deviceId));
|
2019-01-05 11:08:02 +00:00
|
|
|
Local::writeUserSettings();
|
|
|
|
if (_micTester) {
|
|
|
|
stopTestingMicrophone();
|
|
|
|
}
|
2019-01-11 10:07:56 +00:00
|
|
|
if (const auto call = ::Calls::Current().currentCall()) {
|
|
|
|
call->setCurrentAudioDevice(true, deviceId);
|
2019-01-05 11:08:02 +00:00
|
|
|
}
|
|
|
|
});
|
2019-01-11 10:07:56 +00:00
|
|
|
Ui::show(Box<SingleChoiceBox>(
|
2019-06-18 15:00:55 +00:00
|
|
|
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(
|
|
|
|
lng_settings_call_input_volume(lt_percent, percent));
|
|
|
|
};
|
|
|
|
const auto updateInputVolume = [=](int value) {
|
|
|
|
_needWriteSettings = true;
|
|
|
|
updateInputLabel(value);
|
|
|
|
Global::SetCallInputVolume(value);
|
|
|
|
::Calls::Call *currentCall = ::Calls::Current().currentCall();
|
|
|
|
if (currentCall) {
|
|
|
|
currentCall->setAudioVolume(true, value / 100.0f);
|
|
|
|
}
|
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; },
|
2019-01-05 11:08:02 +00:00
|
|
|
Global::CallInputVolume(),
|
2019-01-11 10:07:56 +00:00
|
|
|
updateInputVolume);
|
2019-01-05 11:08:02 +00:00
|
|
|
updateInputLabel(Global::CallInputVolume());
|
|
|
|
|
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
|
|
|
|
2019-03-20 10:31:32 +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(
|
|
|
|
rpl::single(Global::CallAudioDuckingEnabled())
|
|
|
|
)->toggledValue() | rpl::filter([](bool enabled) {
|
|
|
|
return (enabled != Global::CallAudioDuckingEnabled());
|
|
|
|
}) | rpl::start_with_next([](bool enabled) {
|
|
|
|
Global::SetCallAudioDuckingEnabled(enabled);
|
|
|
|
Local::writeUserSettings();
|
2019-01-11 10:07:56 +00:00
|
|
|
if (const auto call = ::Calls::Current().currentCall()) {
|
|
|
|
call->setAudioDuckingEnabled(enabled);
|
2019-01-05 11:08:02 +00:00
|
|
|
}
|
|
|
|
}, content->lifetime());
|
2019-03-20 10:31:32 +00:00
|
|
|
#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>(
|
|
|
|
Global::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
|
|
|
|
|