Added label to call panel when recipient has low level of battery.

This commit is contained in:
23rd 2024-03-21 21:03:21 +03:00
parent 88dab47d2c
commit cb9adad660
5 changed files with 147 additions and 13 deletions

View file

@ -3512,6 +3512,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_call_cancel" = "Cancel"; "lng_call_cancel" = "Cancel";
"lng_call_microphone_off" = "{user}'s microphone is off"; "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" = "Voice Chat";
"lng_group_call_title_channel" = "Live Stream"; "lng_group_call_title_channel" = "Live Stream";

View file

@ -982,6 +982,16 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
updateRemoteMediaState(audio, video); 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<uint8_t> &data) { .signalingDataEmitted = [=](const std::vector<uint8_t> &data) {
const auto bytes = QByteArray( const auto bytes = QByteArray(
reinterpret_cast<const char*>(data.data()), reinterpret_cast<const char*>(data.data()),

View file

@ -162,6 +162,18 @@ public:
return _remoteVideoState.value(); return _remoteVideoState.value();
} }
enum class RemoteBatteryState {
Low,
Normal,
};
[[nodiscard]] RemoteBatteryState remoteBatteryState() const {
return _remoteBatteryState.current();
}
[[nodiscard]] auto remoteBatteryStateValue() const
-> rpl::producer<RemoteBatteryState> {
return _remoteBatteryState.value();
}
static constexpr auto kSignalBarStarting = -1; static constexpr auto kSignalBarStarting = -1;
static constexpr auto kSignalBarFinished = -2; static constexpr auto kSignalBarFinished = -2;
static constexpr auto kSignalBarCount = 4; static constexpr auto kSignalBarCount = 4;
@ -268,6 +280,8 @@ private:
rpl::variable<RemoteAudioState> _remoteAudioState = rpl::variable<RemoteAudioState> _remoteAudioState =
RemoteAudioState::Active; RemoteAudioState::Active;
rpl::variable<Webrtc::VideoState> _remoteVideoState; rpl::variable<Webrtc::VideoState> _remoteVideoState;
rpl::variable<RemoteBatteryState> _remoteBatteryState =
RemoteBatteryState::Normal;
rpl::event_stream<Error> _errors; rpl::event_stream<Error> _errors;
FinishType _finishAfterRequestingCall = FinishType::None; FinishType _finishAfterRequestingCall = FinishType::None;
bool _answerAfterDhConfigReceived = false; bool _answerAfterDhConfigReceived = false;

View file

@ -39,6 +39,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/empty_userpic.h" #include "ui/empty_userpic.h"
#include "ui/emoji_config.h" #include "ui/emoji_config.h"
#include "ui/painter.h" #include "ui/painter.h"
#include "ui/rect.h"
#include "core/application.h" #include "core/application.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "main/main_session.h" #include "main/main_session.h"
@ -57,8 +58,35 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtWidgets/QApplication> #include <QtWidgets/QApplication>
#include <QtGui/QWindow> #include <QtGui/QWindow>
#include <QtCore/QTimer> #include <QtCore/QTimer>
#include <QtSvg/QSvgRenderer>
namespace Calls { 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"(
<svg width=")" + width + R"(" height=")" + height
+ R"(" viewBox="0 0 )" + width + R"( )" + height + R"(" fill="none">
<rect x="1.33598" y="0.5" width="24" height="12" rx="4" stroke=")" + color + R"("/>
<path
d="M26.836 4.66666V8.66666C27.6407 8.32788 28.164 7.53979 28.164 6.66666C28.164 5.79352 27.6407 5.00543 26.836 4.66666Z"
fill=")" + color + R"("/>
<path
d="M 5.5 3.5 H 5.5 A 0.5 0.5 0 0 1 6 4 V 9 A 0.5 0.5 0 0 1 5.5 9.5 H 5.5 A 0.5 0.5 0 0 1 5 9 V 4 A 0.5 0.5 0 0 1 5.5 3.5 Z M 5 4 V 9 A 0.5 0.5 0 0 0 5.5 9.5 H 5.5 A 0.5 0.5 0 0 0 6 9 V 4 A 0.5 0.5 0 0 0 5.5 3.5 H 5.5 A 0.5 0.5 0 0 0 5 4 Z"
transform="matrix(1, 0, 0, 1, 0, 0)" + ")\" stroke=\"" + color + R"("/>
</svg>)";
}
} // namespace
Panel::Panel(not_null<Call*> call) Panel::Panel(not_null<Call*> call)
: _call(call) : _call(call)
@ -392,9 +420,7 @@ void Panel::reinitWithCall(Call *call) {
_user = _call->user(); _user = _call->user();
auto remoteMuted = _call->remoteAudioStateValue( auto remoteMuted = _call->remoteAudioStateValue(
) | rpl::map([=](Call::RemoteAudioState state) { ) | rpl::map(rpl::mappers::_1 == Call::RemoteAudioState::Muted);
return (state == Call::RemoteAudioState::Muted);
});
rpl::duplicate( rpl::duplicate(
remoteMuted remoteMuted
) | rpl::start_with_next([=](bool muted) { ) | rpl::start_with_next([=](bool muted) {
@ -402,6 +428,15 @@ void Panel::reinitWithCall(Call *call) {
createRemoteAudioMute(); createRemoteAudioMute();
} else { } else {
_remoteAudioMute.destroy(); _remoteAudioMute.destroy();
showRemoteLowBattery();
}
}, _callLifetime);
_call->remoteBatteryStateValue(
) | rpl::start_with_next([=](Call::RemoteBatteryState state) {
if (state == Call::RemoteBatteryState::Low) {
createRemoteLowBattery();
} else {
_remoteLowBattery.destroy();
} }
}, _callLifetime); }, _callLifetime);
_userpic = std::make_unique<Userpic>( _userpic = std::make_unique<Userpic>(
@ -550,7 +585,10 @@ void Panel::createRemoteAudioMute() {
widget(), widget(),
tr::lng_call_microphone_off( tr::lng_call_microphone_off(
lt_user, lt_user,
rpl::single(_user->shortName())), _user->session().changes().peerFlagsValue(
_user,
Data::PeerUpdate::Flag::Name
) | rpl::map([=] { return _user->shortName(); })),
st::callRemoteAudioMute), st::callRemoteAudioMute),
st::callTooltipPadding); st::callTooltipPadding);
_remoteAudioMute->setAttribute(Qt::WA_TransparentForMouseEvents); _remoteAudioMute->setAttribute(Qt::WA_TransparentForMouseEvents);
@ -558,12 +596,12 @@ void Panel::createRemoteAudioMute() {
_remoteAudioMute->paintRequest( _remoteAudioMute->paintRequest(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
auto p = QPainter(_remoteAudioMute); auto p = QPainter(_remoteAudioMute);
const auto height = _remoteAudioMute->height(); const auto r = _remoteAudioMute->rect();
auto hq = PainterHighQualityEnabler(p); auto hq = PainterHighQualityEnabler(p);
p.setBrush(st::videoPlayIconBg); p.setBrush(st::videoPlayIconBg);
p.setPen(Qt::NoPen); p.setPen(Qt::NoPen);
p.drawRoundedRect(_remoteAudioMute->rect(), height / 2, height / 2); p.drawRoundedRect(r, r.height() / 2, r.height() / 2);
st::callTooltipMutedIcon.paint( st::callTooltipMutedIcon.paint(
p, p,
@ -575,6 +613,71 @@ void Panel::createRemoteAudioMute() {
updateControlsGeometry(); updateControlsGeometry();
} }
void Panel::createRemoteLowBattery() {
_remoteLowBattery.create(
widget(),
object_ptr<Ui::FlatLabel>(
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() { void Panel::initLayout() {
initGeometry(); initGeometry();
@ -613,6 +716,7 @@ void Panel::showControls() {
if (_remoteAudioMute) { if (_remoteAudioMute) {
_remoteAudioMute->setVisible(shown); _remoteAudioMute->setVisible(shown);
} }
showRemoteLowBattery();
} }
void Panel::closeBeforeDestroy() { void Panel::closeBeforeDestroy() {
@ -749,6 +853,13 @@ void Panel::updateControlsGeometry() {
- st::callRemoteAudioMuteSkip - st::callRemoteAudioMuteSkip
- _remoteAudioMute->height())); - _remoteAudioMute->height()));
} }
if (_remoteLowBattery) {
_remoteLowBattery->moveToLeft(
(widget()->width() - _remoteLowBattery->width()) / 2,
(_buttonsTop
- st::callRemoteAudioMuteSkip
- _remoteLowBattery->height()));
}
if (_outgoingPreviewInBody) { if (_outgoingPreviewInBody) {
_outgoingVideoBubble->updateGeometry( _outgoingVideoBubble->updateGeometry(
@ -768,16 +879,10 @@ void Panel::updateControlsGeometry() {
void Panel::updateOutgoingVideoBubbleGeometry() { void Panel::updateOutgoingVideoBubbleGeometry() {
Expects(!_outgoingPreviewInBody); Expects(!_outgoingPreviewInBody);
const auto margins = QMargins{
st::callInnerPadding,
st::callInnerPadding,
st::callInnerPadding,
st::callInnerPadding,
};
const auto size = st::callOutgoingDefaultSize; const auto size = st::callOutgoingDefaultSize;
_outgoingVideoBubble->updateGeometry( _outgoingVideoBubble->updateGeometry(
VideoBubble::DragMode::SnapToCorners, VideoBubble::DragMode::SnapToCorners,
widget()->rect().marginsRemoved(margins), widget()->rect() - Margins(st::callInnerPadding),
size); size);
} }

View file

@ -122,6 +122,8 @@ private:
void refreshOutgoingPreviewInBody(State state); void refreshOutgoingPreviewInBody(State state);
void toggleFullScreen(bool fullscreen); void toggleFullScreen(bool fullscreen);
void createRemoteAudioMute(); void createRemoteAudioMute();
void createRemoteLowBattery();
void showRemoteLowBattery();
void refreshAnswerHangupRedialLabel(); void refreshAnswerHangupRedialLabel();
[[nodiscard]] QRect incomingFrameGeometry() const; [[nodiscard]] QRect incomingFrameGeometry() const;
@ -160,6 +162,8 @@ private:
object_ptr<Ui::FlatLabel> _status; object_ptr<Ui::FlatLabel> _status;
object_ptr<Ui::RpWidget> _fingerprint = { nullptr }; object_ptr<Ui::RpWidget> _fingerprint = { nullptr };
object_ptr<Ui::PaddingWrap<Ui::FlatLabel>> _remoteAudioMute = { nullptr }; object_ptr<Ui::PaddingWrap<Ui::FlatLabel>> _remoteAudioMute = { nullptr };
object_ptr<Ui::PaddingWrap<Ui::FlatLabel>> _remoteLowBattery =
{ nullptr };
std::unique_ptr<Userpic> _userpic; std::unique_ptr<Userpic> _userpic;
std::unique_ptr<VideoBubble> _outgoingVideoBubble; std::unique_ptr<VideoBubble> _outgoingVideoBubble;
QPixmap _bottomShadow; QPixmap _bottomShadow;