From cb9adad6607f3334006a9f8d40327b9ffb57ad60 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 21 Mar 2024 21:03:21 +0300 Subject: [PATCH] Added label to call panel when recipient has low level of battery. --- Telegram/Resources/langs/lang.strings | 1 + Telegram/SourceFiles/calls/calls_call.cpp | 10 ++ Telegram/SourceFiles/calls/calls_call.h | 14 +++ Telegram/SourceFiles/calls/calls_panel.cpp | 131 +++++++++++++++++++-- Telegram/SourceFiles/calls/calls_panel.h | 4 + 5 files changed, 147 insertions(+), 13 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 190958942..b70b8eacb 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -3512,6 +3512,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_call_cancel" = "Cancel"; "lng_call_microphone_off" = "{user}'s microphone is off"; +"lng_call_battery_level_low" = "{user}'s battery level is low"; "lng_group_call_title" = "Voice Chat"; "lng_group_call_title_channel" = "Live Stream"; diff --git a/Telegram/SourceFiles/calls/calls_call.cpp b/Telegram/SourceFiles/calls/calls_call.cpp index 205a4c2d7..6b459e2ad 100644 --- a/Telegram/SourceFiles/calls/calls_call.cpp +++ b/Telegram/SourceFiles/calls/calls_call.cpp @@ -982,6 +982,16 @@ void Call::createAndStartController(const MTPDphoneCall &call) { updateRemoteMediaState(audio, video); }); }, + .remoteBatteryLevelIsLowUpdated = [=](bool isLow) { +#ifdef _DEBUG + isLow = true; +#endif + crl::on_main(weak, [=] { + _remoteBatteryState = isLow + ? RemoteBatteryState::Low + : RemoteBatteryState::Normal; + }); + }, .signalingDataEmitted = [=](const std::vector &data) { const auto bytes = QByteArray( reinterpret_cast(data.data()), diff --git a/Telegram/SourceFiles/calls/calls_call.h b/Telegram/SourceFiles/calls/calls_call.h index 69c2d94da..55f48237a 100644 --- a/Telegram/SourceFiles/calls/calls_call.h +++ b/Telegram/SourceFiles/calls/calls_call.h @@ -162,6 +162,18 @@ public: return _remoteVideoState.value(); } + enum class RemoteBatteryState { + Low, + Normal, + }; + [[nodiscard]] RemoteBatteryState remoteBatteryState() const { + return _remoteBatteryState.current(); + } + [[nodiscard]] auto remoteBatteryStateValue() const + -> rpl::producer { + return _remoteBatteryState.value(); + } + static constexpr auto kSignalBarStarting = -1; static constexpr auto kSignalBarFinished = -2; static constexpr auto kSignalBarCount = 4; @@ -268,6 +280,8 @@ private: rpl::variable _remoteAudioState = RemoteAudioState::Active; rpl::variable _remoteVideoState; + rpl::variable _remoteBatteryState = + RemoteBatteryState::Normal; rpl::event_stream _errors; FinishType _finishAfterRequestingCall = FinishType::None; bool _answerAfterDhConfigReceived = false; diff --git a/Telegram/SourceFiles/calls/calls_panel.cpp b/Telegram/SourceFiles/calls/calls_panel.cpp index 05fdaaff2..4b92ee075 100644 --- a/Telegram/SourceFiles/calls/calls_panel.cpp +++ b/Telegram/SourceFiles/calls/calls_panel.cpp @@ -39,6 +39,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/empty_userpic.h" #include "ui/emoji_config.h" #include "ui/painter.h" +#include "ui/rect.h" #include "core/application.h" #include "lang/lang_keys.h" #include "main/main_session.h" @@ -57,8 +58,35 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include #include #include +#include namespace Calls { +namespace { + +[[nodiscard]] QByteArray BatterySvg( + const QSize &s, + const QColor &c) { + const auto color = u"rgb(%1,%2,%3)"_q + .arg(c.red()) + .arg(c.green()) + .arg(c.blue()) + .toUtf8(); + const auto width = QString::number(s.width()).toUtf8(); + const auto height = QString::number(s.height()).toUtf8(); + return R"( + + + + +)"; +} + +} // namespace Panel::Panel(not_null call) : _call(call) @@ -392,9 +420,7 @@ void Panel::reinitWithCall(Call *call) { _user = _call->user(); auto remoteMuted = _call->remoteAudioStateValue( - ) | rpl::map([=](Call::RemoteAudioState state) { - return (state == Call::RemoteAudioState::Muted); - }); + ) | rpl::map(rpl::mappers::_1 == Call::RemoteAudioState::Muted); rpl::duplicate( remoteMuted ) | rpl::start_with_next([=](bool muted) { @@ -402,6 +428,15 @@ void Panel::reinitWithCall(Call *call) { createRemoteAudioMute(); } else { _remoteAudioMute.destroy(); + showRemoteLowBattery(); + } + }, _callLifetime); + _call->remoteBatteryStateValue( + ) | rpl::start_with_next([=](Call::RemoteBatteryState state) { + if (state == Call::RemoteBatteryState::Low) { + createRemoteLowBattery(); + } else { + _remoteLowBattery.destroy(); } }, _callLifetime); _userpic = std::make_unique( @@ -550,7 +585,10 @@ void Panel::createRemoteAudioMute() { widget(), tr::lng_call_microphone_off( lt_user, - rpl::single(_user->shortName())), + _user->session().changes().peerFlagsValue( + _user, + Data::PeerUpdate::Flag::Name + ) | rpl::map([=] { return _user->shortName(); })), st::callRemoteAudioMute), st::callTooltipPadding); _remoteAudioMute->setAttribute(Qt::WA_TransparentForMouseEvents); @@ -558,12 +596,12 @@ void Panel::createRemoteAudioMute() { _remoteAudioMute->paintRequest( ) | rpl::start_with_next([=] { auto p = QPainter(_remoteAudioMute); - const auto height = _remoteAudioMute->height(); + const auto r = _remoteAudioMute->rect(); auto hq = PainterHighQualityEnabler(p); p.setBrush(st::videoPlayIconBg); p.setPen(Qt::NoPen); - p.drawRoundedRect(_remoteAudioMute->rect(), height / 2, height / 2); + p.drawRoundedRect(r, r.height() / 2, r.height() / 2); st::callTooltipMutedIcon.paint( p, @@ -575,6 +613,71 @@ void Panel::createRemoteAudioMute() { updateControlsGeometry(); } +void Panel::createRemoteLowBattery() { + _remoteLowBattery.create( + widget(), + object_ptr( + widget(), + tr::lng_call_battery_level_low( + lt_user, + _user->session().changes().peerFlagsValue( + _user, + Data::PeerUpdate::Flag::Name + ) | rpl::map([=] { return _user->shortName(); })), + st::callRemoteAudioMute), + st::callTooltipPadding); + _remoteLowBattery->setAttribute(Qt::WA_TransparentForMouseEvents); + + style::PaletteChanged( + ) | rpl::start_with_next([=] { + _remoteLowBattery.destroy(); + createRemoteLowBattery(); + }, _remoteLowBattery->lifetime()); + + constexpr auto kBatterySize = QSize(29, 13); + + const auto icon = [&] { + auto svg = QSvgRenderer( + BatterySvg(kBatterySize, st::videoPlayIconFg->c)); + auto image = QImage( + kBatterySize * style::DevicePixelRatio(), + QImage::Format_ARGB32_Premultiplied); + image.setDevicePixelRatio(style::DevicePixelRatio()); + image.fill(Qt::transparent); + { + auto p = QPainter(&image); + svg.render(&p, Rect(kBatterySize)); + } + return image; + }(); + + _remoteLowBattery->paintRequest( + ) | rpl::start_with_next([=] { + auto p = QPainter(_remoteLowBattery); + const auto r = _remoteLowBattery->rect(); + + auto hq = PainterHighQualityEnabler(p); + p.setBrush(st::videoPlayIconBg); + p.setPen(Qt::NoPen); + p.drawRoundedRect(r, r.height() / 2, r.height() / 2); + + p.drawImage( + st::callTooltipMutedIconPosition.x(), + (r.height() - kBatterySize.height()) / 2, + icon); + }, _remoteLowBattery->lifetime()); + + showControls(); + updateControlsGeometry(); +} + +void Panel::showRemoteLowBattery() { + if (_remoteLowBattery) { + _remoteLowBattery->setVisible(!_remoteAudioMute + || _remoteAudioMute->isHidden()); + } +} + void Panel::initLayout() { initGeometry(); @@ -613,6 +716,7 @@ void Panel::showControls() { if (_remoteAudioMute) { _remoteAudioMute->setVisible(shown); } + showRemoteLowBattery(); } void Panel::closeBeforeDestroy() { @@ -749,6 +853,13 @@ void Panel::updateControlsGeometry() { - st::callRemoteAudioMuteSkip - _remoteAudioMute->height())); } + if (_remoteLowBattery) { + _remoteLowBattery->moveToLeft( + (widget()->width() - _remoteLowBattery->width()) / 2, + (_buttonsTop + - st::callRemoteAudioMuteSkip + - _remoteLowBattery->height())); + } if (_outgoingPreviewInBody) { _outgoingVideoBubble->updateGeometry( @@ -768,16 +879,10 @@ void Panel::updateControlsGeometry() { void Panel::updateOutgoingVideoBubbleGeometry() { Expects(!_outgoingPreviewInBody); - const auto margins = QMargins{ - st::callInnerPadding, - st::callInnerPadding, - st::callInnerPadding, - st::callInnerPadding, - }; const auto size = st::callOutgoingDefaultSize; _outgoingVideoBubble->updateGeometry( VideoBubble::DragMode::SnapToCorners, - widget()->rect().marginsRemoved(margins), + widget()->rect() - Margins(st::callInnerPadding), size); } diff --git a/Telegram/SourceFiles/calls/calls_panel.h b/Telegram/SourceFiles/calls/calls_panel.h index c98537eb9..f6c8666d8 100644 --- a/Telegram/SourceFiles/calls/calls_panel.h +++ b/Telegram/SourceFiles/calls/calls_panel.h @@ -122,6 +122,8 @@ private: void refreshOutgoingPreviewInBody(State state); void toggleFullScreen(bool fullscreen); void createRemoteAudioMute(); + void createRemoteLowBattery(); + void showRemoteLowBattery(); void refreshAnswerHangupRedialLabel(); [[nodiscard]] QRect incomingFrameGeometry() const; @@ -160,6 +162,8 @@ private: object_ptr _status; object_ptr _fingerprint = { nullptr }; object_ptr> _remoteAudioMute = { nullptr }; + object_ptr> _remoteLowBattery = + { nullptr }; std::unique_ptr _userpic; std::unique_ptr _outgoingVideoBubble; QPixmap _bottomShadow;