Add a hint to unmute your microphone.

This commit is contained in:
John Preston 2021-06-18 17:45:22 +04:00
parent 1cb1f1cbc1
commit 7a588be54f
7 changed files with 166 additions and 35 deletions

View File

@ -1251,7 +1251,7 @@ groupCallTooltip: Tooltip(defaultTooltip) {
} }
groupCallNiceTooltip: ImportantTooltip(defaultImportantTooltip) { groupCallNiceTooltip: ImportantTooltip(defaultImportantTooltip) {
bg: importantTooltipBg; bg: importantTooltipBg;
padding: margins(10px, 1px, 10px, 3px); padding: margins(10px, 3px, 10px, 5px);
radius: 4px; radius: 4px;
arrow: 4px; arrow: 4px;
} }

View File

@ -68,4 +68,13 @@ enum class Error {
DisabledNoScreen, DisabledNoScreen,
}; };
enum class StickedTooltip {
Camera = 0x01,
Microphone = 0x02,
};
constexpr inline bool is_flag_type(StickedTooltip) {
return true;
}
using StickedTooltips = base::flags<StickedTooltip>;
} // namespace Calls::Group } // namespace Calls::Group

View File

@ -54,6 +54,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h" // api().kickParticipant. #include "apiwrap.h" // api().kickParticipant.
#include "webrtc/webrtc_video_track.h" #include "webrtc/webrtc_video_track.h"
#include "webrtc/webrtc_media_devices.h" // UniqueDesktopCaptureSource. #include "webrtc/webrtc_media_devices.h" // UniqueDesktopCaptureSource.
#include "webrtc/webrtc_audio_input_tester.h"
#include "styles/style_calls.h" #include "styles/style_calls.h"
#include "styles/style_layers.h" #include "styles/style_layers.h"
@ -71,6 +72,10 @@ constexpr auto kRecordingOpacity = 0.6;
constexpr auto kStartNoConfirmation = TimeId(10); constexpr auto kStartNoConfirmation = TimeId(10);
constexpr auto kControlsBackgroundOpacity = 0.8; constexpr auto kControlsBackgroundOpacity = 0.8;
constexpr auto kOverrideActiveColorBgAlpha = 172; constexpr auto kOverrideActiveColorBgAlpha = 172;
constexpr auto kMicrophoneTooltipAfterLoudCount = 3;
constexpr auto kDropLoudAfterQuietCount = 5;
constexpr auto kMicrophoneTooltipLevelThreshold = 0.2;
constexpr auto kMicrophoneTooltipCheckInterval = crl::time(500);
} // namespace } // namespace
@ -84,6 +89,49 @@ struct Panel::ControlsBackgroundNarrow {
Ui::RpWidget blocker; Ui::RpWidget blocker;
}; };
class Panel::MicLevelTester final {
public:
explicit MicLevelTester(Fn<void()> show);
[[nodiscard]] bool showTooltip() const;
private:
void check();
Fn<void()> _show;
base::Timer _timer;
Webrtc::AudioInputTester _tester;
int _loudCount = 0;
int _quietCount = 0;
};
Panel::MicLevelTester::MicLevelTester(Fn<void()> show)
: _show(std::move(show))
, _timer([=] { check(); })
, _tester(
Core::App().settings().callAudioBackend(),
Core::App().settings().callInputDeviceId()) {
_timer.callEach(kMicrophoneTooltipCheckInterval);
}
bool Panel::MicLevelTester::showTooltip() const {
return (_loudCount >= kMicrophoneTooltipAfterLoudCount);
}
void Panel::MicLevelTester::check() {
const auto level = _tester.getAndResetLevel();
if (level >= kMicrophoneTooltipLevelThreshold) {
_quietCount = 0;
if (++_loudCount >= kMicrophoneTooltipAfterLoudCount) {
_show();
}
} else if (_loudCount > 0 && ++_quietCount >= kDropLoudAfterQuietCount) {
_quietCount = 0;
_loudCount = 0;
}
}
Panel::Panel(not_null<GroupCall*> call) Panel::Panel(not_null<GroupCall*> call)
: _call(call) : _call(call)
, _peer(call->peer()) , _peer(call->peer())
@ -112,6 +160,7 @@ Panel::Panel(not_null<GroupCall*> call)
: Ui::CallMuteButtonType::ScheduledSilent), : Ui::CallMuteButtonType::ScheduledSilent),
})) }))
, _hangup(widget(), st::groupCallHangup) , _hangup(widget(), st::groupCallHangup)
, _stickedTooltipsShown(Core::App().settings().hiddenGroupCallTooltips())
, _toasts(std::make_unique<Toasts>(this)) { , _toasts(std::make_unique<Toasts>(this)) {
_layerBg->setStyleOverrides(&st::groupCallBox, &st::groupCallLayerBox); _layerBg->setStyleOverrides(&st::groupCallBox, &st::groupCallLayerBox);
_layerBg->setHideByBackgroundClick(true); _layerBg->setHideByBackgroundClick(true);
@ -418,7 +467,9 @@ void Panel::initControls() {
} }
_call->stateValue( _call->stateValue(
) | rpl::filter([](State state) { ) | rpl::before_next([=] {
showStickedTooltip();
}) | rpl::filter([](State state) {
return (state == State::HangingUp) return (state == State::HangingUp)
|| (state == State::Ended) || (state == State::Ended)
|| (state == State::FailedHangingUp) || (state == State::FailedHangingUp)
@ -515,11 +566,6 @@ void Panel::refreshVideoButtons(std::optional<bool> overrideWideMode) {
} }
_video->setProgress(sharing ? 1. : 0.); _video->setProgress(sharing ? 1. : 0.);
}, _video->lifetime()); }, _video->lifetime());
_call->mutedValue(
) | rpl::start_with_next([=] {
updateButtonsGeometry();
showStickedTooltip();
}, _video->lifetime());
} }
if (!_screenShare) { if (!_screenShare) {
_screenShare.create(widget(), st::groupCallScreenShareSmall); _screenShare.create(widget(), st::groupCallScreenShareSmall);
@ -562,7 +608,8 @@ void Panel::hideStickedTooltip(
if (hide != StickedTooltipHide::Unavailable) { if (hide != StickedTooltipHide::Unavailable) {
_stickedTooltipsShown |= type; _stickedTooltipsShown |= type;
if (hide == StickedTooltipHide::Discarded) { if (hide == StickedTooltipHide::Discarded) {
// #TODO calls save to settings. Core::App().settings().setHiddenGroupCallTooltip(type);
Core::App().saveSettingsDelayed();
} }
} }
const auto control = (type == StickedTooltip::Camera) const auto control = (type == StickedTooltip::Camera)
@ -886,6 +933,9 @@ void Panel::raiseControls() {
} }
} }
_mute->raise(); _mute->raise();
if (_niceTooltip) {
_niceTooltip->raise();
}
} }
void Panel::setupVideo(not_null<Viewport*> viewport) { void Panel::setupVideo(not_null<Viewport*> viewport) {
@ -1052,6 +1102,18 @@ void Panel::subscribeToChanges(not_null<Data::GroupCall*> real) {
refreshTopButton(); refreshTopButton();
}, widget()->lifetime()); }, widget()->lifetime());
_call->mutedValue(
) | rpl::skip(1) | rpl::start_with_next([=](MuteState state) {
updateButtonsGeometry();
if (state == MuteState::Active
|| state == MuteState::PushToTalk) {
hideStickedTooltip(
StickedTooltip::Microphone,
StickedTooltipHide::Activated);
}
showStickedTooltip();
}, widget()->lifetime());
updateControlsGeometry(); updateControlsGeometry();
} }
@ -1407,7 +1469,10 @@ bool Panel::updateMode() {
_call->showVideoEndpointLarge({}); _call->showVideoEndpointLarge({});
} }
refreshVideoButtons(wide); refreshVideoButtons(wide);
_niceTooltip.destroy(); if (!_stickedTooltipClose
|| _niceTooltipControl.data() != _mute->outer().get()) {
_niceTooltip.destroy();
}
_mode = mode; _mode = mode;
if (_title) { if (_title) {
_title->setTextColorOverride(wide _title->setTextColorOverride(wide
@ -1631,7 +1696,10 @@ void Panel::trackControlOver(not_null<Ui::RpWidget*> control, bool over) {
void Panel::showStickedTooltip() { void Panel::showStickedTooltip() {
static const auto kHasCamera = !Webrtc::GetVideoInputList().empty(); static const auto kHasCamera = !Webrtc::GetVideoInputList().empty();
const auto callReady = (_call->state() == State::Joined
|| _call->state() == State::Connecting);
if (!(_stickedTooltipsShown & StickedTooltip::Camera) if (!(_stickedTooltipsShown & StickedTooltip::Camera)
&& callReady
&& (_mode.current() == PanelMode::Wide) && (_mode.current() == PanelMode::Wide)
&& _video && _video
&& _call->videoIsWorking() && _call->videoIsWorking()
@ -1645,13 +1713,25 @@ void Panel::showStickedTooltip() {
StickedTooltipHide::Unavailable); StickedTooltipHide::Unavailable);
if (!(_stickedTooltipsShown & StickedTooltip::Microphone) if (!(_stickedTooltipsShown & StickedTooltip::Microphone)
&& (_mode.current() == PanelMode::Wide) && callReady
&& _mute && _mute
&& !_call->mutedByAdmin() && !_call->mutedByAdmin()) {
&& false) { // Check if there is incoming sound. if (_stickedTooltipClose) {
showNiceTooltip(_mute->outer(), NiceTooltipType::Sticked); // Showing already.
return;
} else if (!_micLevelTester) {
// Check if there is incoming sound.
_micLevelTester = std::make_unique<MicLevelTester>([=] {
showStickedTooltip();
});
}
if (_micLevelTester->showTooltip()) {
_micLevelTester = nullptr;
showNiceTooltip(_mute->outer(), NiceTooltipType::Sticked);
}
return; return;
} }
_micLevelTester = nullptr;
hideStickedTooltip( hideStickedTooltip(
StickedTooltip::Microphone, StickedTooltip::Microphone,
StickedTooltipHide::Unavailable); StickedTooltipHide::Unavailable);
@ -1685,11 +1765,12 @@ void Panel::showNiceTooltip(
} }
return rpl::producer<QString>(); return rpl::producer<QString>();
}(); }();
if (!text if (!text || _stickedTooltipClose) {
|| _wideControlsAnimation.animating()
|| !_wideControlsShown
|| _stickedTooltipClose) {
return; return;
} else if (_wideControlsAnimation.animating() || !_wideControlsShown) {
if (type == NiceTooltipType::Normal) {
return;
}
} }
const auto inner = [&]() -> Ui::RpWidget* { const auto inner = [&]() -> Ui::RpWidget* {
const auto normal = (type == NiceTooltipType::Normal); const auto normal = (type == NiceTooltipType::Normal);

View File

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/timer.h" #include "base/timer.h"
#include "base/object_ptr.h" #include "base/object_ptr.h"
#include "calls/group/calls_group_call.h" #include "calls/group/calls_group_call.h"
#include "calls/group/calls_group_common.h"
#include "calls/group/calls_choose_join_as.h" #include "calls/group/calls_choose_join_as.h"
#include "calls/group/ui/desktop_capture_choose_source.h" #include "calls/group/ui/desktop_capture_choose_source.h"
#include "ui/effects/animations.h" #include "ui/effects/animations.h"
@ -64,6 +65,7 @@ class Toasts;
class Members; class Members;
class Viewport; class Viewport;
enum class PanelMode; enum class PanelMode;
enum class StickedTooltip;
class Panel final : private Ui::DesktopCapture::ChooseSourceDelegate { class Panel final : private Ui::DesktopCapture::ChooseSourceDelegate {
public: public:
@ -88,19 +90,12 @@ private:
Normal, Normal,
Sticked, Sticked,
}; };
enum class StickedTooltip {
Camera = 0x01,
Microphone = 0x02,
};
friend constexpr inline bool is_flag_type(StickedTooltip) {
return true;
};
using StickedTooltips = base::flags<StickedTooltip>;
enum class StickedTooltipHide { enum class StickedTooltipHide {
Unavailable, Unavailable,
Activated, Activated,
Discarded, Discarded,
}; };
class MicLevelTester;
std::unique_ptr<Ui::Window> createWindow(); std::unique_ptr<Ui::Window> createWindow();
[[nodiscard]] not_null<Ui::RpWidget*> widget() const; [[nodiscard]] not_null<Ui::RpWidget*> widget() const;
@ -236,6 +231,8 @@ private:
const std::unique_ptr<Toasts> _toasts; const std::unique_ptr<Toasts> _toasts;
base::weak_ptr<Ui::Toast::Instance> _lastToast; base::weak_ptr<Ui::Toast::Instance> _lastToast;
std::unique_ptr<MicLevelTester> _micLevelTester;
rpl::lifetime _peerLifetime; rpl::lifetime _peerLifetime;
}; };

View File

@ -493,8 +493,10 @@ void SettingsBox(
tr::now, tr::now,
lt_delay, lt_delay,
FormatDelay(delay))); FormatDelay(delay)));
Core::App().settings().setGroupCallPushToTalkDelay(delay); if (Core::App().settings().groupCallPushToTalkDelay() != delay) {
applyAndSave(); Core::App().settings().setGroupCallPushToTalkDelay(delay);
applyAndSave();
}
}; };
callback(value); callback(value);
const auto slider = pushToTalkInner->add( const auto slider = pushToTalkInner->add(

View File

@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/platform/base_platform_info.h" #include "base/platform/base_platform_info.h"
#include "webrtc/webrtc_create_adm.h" #include "webrtc/webrtc_create_adm.h"
#include "ui/gl/gl_detection.h" #include "ui/gl/gl_detection.h"
#include "calls/group/calls_group_common.h"
#include "facades.h" #include "facades.h"
namespace Core { namespace Core {
@ -94,23 +95,35 @@ QByteArray Settings::serialize() const {
+ sizeof(qint32) * 5 + sizeof(qint32) * 5
+ Serialize::stringSize(_downloadPath.current()) + Serialize::stringSize(_downloadPath.current())
+ Serialize::bytearraySize(_downloadPathBookmark) + Serialize::bytearraySize(_downloadPathBookmark)
+ sizeof(qint32) * 12 + sizeof(qint32) * 9
+ Serialize::stringSize(_callOutputDeviceId) + Serialize::stringSize(_callOutputDeviceId)
+ Serialize::stringSize(_callInputDeviceId) + Serialize::stringSize(_callInputDeviceId)
+ Serialize::stringSize(_callVideoInputDeviceId) + sizeof(qint32) * 5;
+ sizeof(qint32) * 5
+ Serialize::bytearraySize(proxy);
for (const auto &[key, value] : _soundOverrides) { for (const auto &[key, value] : _soundOverrides) {
size += Serialize::stringSize(key) + Serialize::stringSize(value); size += Serialize::stringSize(key) + Serialize::stringSize(value);
} }
size += sizeof(qint32) * 13
+ Serialize::bytearraySize(_videoPipGeometry)
+ sizeof(qint32)
+ (_dictionariesEnabled.current().size() * sizeof(quint64))
+ sizeof(qint32) * 12
+ Serialize::stringSize(_callVideoInputDeviceId)
+ sizeof(qint32) * 2
+ Serialize::bytearraySize(_groupCallPushToTalkShortcut)
+ sizeof(qint64)
+ sizeof(qint32) * 2
+ Serialize::bytearraySize(windowPosition)
+ sizeof(qint32);
for (const auto &[id, rating] : recentEmojiPreloadData) { for (const auto &[id, rating] : recentEmojiPreloadData) {
size += Serialize::stringSize(id) + sizeof(quint16); size += Serialize::stringSize(id) + sizeof(quint16);
} }
size += sizeof(qint32);
for (const auto &[id, variant] : _emojiVariants) { for (const auto &[id, variant] : _emojiVariants) {
size += Serialize::stringSize(id) + sizeof(quint8); size += Serialize::stringSize(id) + sizeof(quint8);
} }
size += Serialize::bytearraySize(_videoPipGeometry); size += sizeof(qint32) * 3
size += Serialize::bytearraySize(windowPosition); + Serialize::bytearraySize(proxy)
+ sizeof(qint32);
auto result = QByteArray(); auto result = QByteArray();
result.reserve(size); result.reserve(size);
@ -200,8 +213,9 @@ QByteArray Settings::serialize() const {
stream stream
<< qint32(_disableOpenGL ? 1 : 0) << qint32(_disableOpenGL ? 1 : 0)
<< qint32(_groupCallNoiseSuppression ? 1 : 0) << qint32(_groupCallNoiseSuppression ? 1 : 0)
<< _workMode.current() << qint32(_workMode.current())
<< proxy; << proxy
<< qint32(_hiddenGroupCallTooltips.value());
} }
return result; return result;
} }
@ -281,6 +295,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
qint32 groupCallNoiseSuppression = _groupCallNoiseSuppression ? 1 : 0; qint32 groupCallNoiseSuppression = _groupCallNoiseSuppression ? 1 : 0;
qint32 workMode = static_cast<qint32>(_workMode.current()); qint32 workMode = static_cast<qint32>(_workMode.current());
QByteArray proxy; QByteArray proxy;
qint32 hiddenGroupCallTooltips = qint32(_hiddenGroupCallTooltips.value());
stream >> themesAccentColors; stream >> themesAccentColors;
if (!stream.atEnd()) { if (!stream.atEnd()) {
@ -421,6 +436,9 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
if (!stream.atEnd()) { if (!stream.atEnd()) {
stream >> proxy; stream >> proxy;
} }
if (!stream.atEnd()) {
stream >> hiddenGroupCallTooltips;
}
if (stream.status() != QDataStream::Ok) { if (stream.status() != QDataStream::Ok) {
LOG(("App Error: " LOG(("App Error: "
"Bad data for Core::Settings::constructFromSerialized()")); "Bad data for Core::Settings::constructFromSerialized()"));
@ -540,6 +558,16 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
case WorkMode::TrayOnly: case WorkMode::TrayOnly:
case WorkMode::WindowOnly: _workMode = uncheckedWorkMode; break; case WorkMode::WindowOnly: _workMode = uncheckedWorkMode; break;
} }
_hiddenGroupCallTooltips = [&] {
using Tooltip = Calls::Group::StickedTooltip;
return Tooltip(0)
| ((hiddenGroupCallTooltips & int(Tooltip::Camera))
? Tooltip::Camera
: Tooltip(0))
| ((hiddenGroupCallTooltips & int(Tooltip::Microphone))
? Tooltip::Microphone
: Tooltip(0));
}();
} }
QString Settings::getSoundPath(const QString &key) const { QString Settings::getSoundPath(const QString &key) const {
@ -795,6 +823,7 @@ void Settings::resetOnLastLogout() {
_notifyFromAll = true; _notifyFromAll = true;
_tabbedReplacedWithInfo = false; // per-window _tabbedReplacedWithInfo = false; // per-window
_systemDarkModeEnabled = false; _systemDarkModeEnabled = false;
_hiddenGroupCallTooltips = 0;
_recentEmojiPreload.clear(); _recentEmojiPreload.clear();
_recentEmoji.clear(); _recentEmoji.clear();

View File

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/themes/window_themes_embedded.h" #include "window/themes/window_themes_embedded.h"
#include "ui/chat/attach/attach_send_files_way.h" #include "ui/chat/attach/attach_send_files_way.h"
#include "platform/platform_notifications_manager.h" #include "platform/platform_notifications_manager.h"
#include "base/flags.h"
#include "emoji.h" #include "emoji.h"
enum class RectPart; enum class RectPart;
@ -27,6 +28,10 @@ namespace Webrtc {
enum class Backend; enum class Backend;
} // namespace Webrtc } // namespace Webrtc
namespace Calls::Group {
enum class StickedTooltip;
} // namespace Calls::Group
namespace Core { namespace Core {
struct WindowPosition { struct WindowPosition {
@ -574,6 +579,13 @@ public:
_disableOpenGL = value; _disableOpenGL = value;
} }
[[nodiscard]] base::flags<Calls::Group::StickedTooltip> hiddenGroupCallTooltips() const {
return _hiddenGroupCallTooltips;
}
void setHiddenGroupCallTooltip(Calls::Group::StickedTooltip value) {
_hiddenGroupCallTooltips |= value;
}
[[nodiscard]] static bool ThirdColumnByDefault(); [[nodiscard]] static bool ThirdColumnByDefault();
[[nodiscard]] static float64 DefaultDialogsWidthRatio(); [[nodiscard]] static float64 DefaultDialogsWidthRatio();
[[nodiscard]] static qint32 SerializePlaybackSpeed(float64 speed) { [[nodiscard]] static qint32 SerializePlaybackSpeed(float64 speed) {
@ -671,6 +683,7 @@ private:
WindowPosition _windowPosition; // per-window WindowPosition _windowPosition; // per-window
bool _disableOpenGL = false; bool _disableOpenGL = false;
rpl::variable<WorkMode> _workMode = WorkMode::WindowAndTray; rpl::variable<WorkMode> _workMode = WorkMode::WindowAndTray;
base::flags<Calls::Group::StickedTooltip> _hiddenGroupCallTooltips;
bool _tabbedReplacedWithInfo = false; // per-window bool _tabbedReplacedWithInfo = false; // per-window
rpl::event_stream<bool> _tabbedReplacedWithInfoValue; // per-window rpl::event_stream<bool> _tabbedReplacedWithInfoValue; // per-window