diff --git a/Telegram/Resources/colors.palette b/Telegram/Resources/colors.palette index b90d21dab5..fb1a421a34 100644 --- a/Telegram/Resources/colors.palette +++ b/Telegram/Resources/colors.palette @@ -530,6 +530,9 @@ callAnswerBg: #64c15b; // phone call popup answer button background callAnswerRipple: #52b149; // phone call popup answer button ripple effect callHangupBg: #d75a5a; // phone call popup hangup button background callHangupRipple: #c04646; // phone call popup hangup button ripple effect +callCancelBg: #ffffff; // phone call popup line busy cancel button background +callCancelFg: #777777; // phone call popup line busy cancel button icon +callCancelRipple: #f1f1f1; // phone call popup line busy cancel button ripple effect callMuteRipple: #ffffff12; // phone call popup mute mic ripple effect callBarBg: dialogsBgActive; // active phone call bar background diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style index d120aa7bd8..e29a08c4ed 100644 --- a/Telegram/SourceFiles/calls/calls.style +++ b/Telegram/SourceFiles/calls/calls.style @@ -42,7 +42,7 @@ callButton: IconButton { width: 64px; height: 64px; - iconPosition: point(20px, 20px); + iconPosition: point(-1px, -1px); rippleAreaPosition: point(8px, 8px); rippleAreaSize: 48px; @@ -61,13 +61,21 @@ callAnswer: CallButton { callHangup: CallButton { button: IconButton(callButton) { icon: icon {{ "call_discard", callIconFg }}; - iconPosition: point(20px, 24px); ripple: RippleAnimation(defaultRippleAnimation) { color: callHangupRipple; } } bg: callHangupBg; } +callCancel: CallButton { + button: IconButton(callButton) { + icon: icon {{ "box_button_close", callCancelFg }}; + ripple: RippleAnimation(defaultRippleAnimation) { + color: callCancelRipple; + } + } + bg: callCancelBg; +} callMuteToggle: IconButton(callButton) { icon: icon {{ "call_record_active", callIconFg }}; ripple: RippleAnimation(defaultRippleAnimation) { diff --git a/Telegram/SourceFiles/calls/calls_call.cpp b/Telegram/SourceFiles/calls/calls_call.cpp index 6f6e8ff09d..08d2f098eb 100644 --- a/Telegram/SourceFiles/calls/calls_call.cpp +++ b/Telegram/SourceFiles/calls/calls_call.cpp @@ -78,9 +78,10 @@ Call::Call(gsl::not_null delegate, gsl::not_null user, Typ : _delegate(delegate) , _user(user) , _type(type) { + _discardByTimeoutTimer.setCallback([this] { hangup(); }); + if (_type == Type::Outgoing) { setState(State::Requesting); - _discardByTimeoutTimer.setCallback([this] { hangup(); }); } } @@ -198,11 +199,26 @@ TimeMs Call::getDurationMs() const { } void Call::hangup() { - auto missed = (_state == State::Ringing || (_state == State::Waiting && _type == Type::Outgoing)); - auto declined = (_state == State::WaitingIncoming); - auto reason = missed ? MTP_phoneCallDiscardReasonMissed() : - declined ? MTP_phoneCallDiscardReasonBusy() : MTP_phoneCallDiscardReasonHangup(); - finish(reason); + if (_state == State::Busy) { + // Cancel call instead of redial. + setState(Call::Ended); + } else { + auto missed = (_state == State::Ringing || (_state == State::Waiting && _type == Type::Outgoing)); + auto declined = (_state == State::WaitingIncoming); + auto reason = missed ? MTP_phoneCallDiscardReasonMissed() : + declined ? MTP_phoneCallDiscardReasonBusy() : MTP_phoneCallDiscardReasonHangup(); + finish(reason); + } +} + +void Call::redial() { + if (_state != State::Busy) { + return; + } + t_assert(_controller == nullptr); + _type = Type::Outgoing; + setState(State::Requesting); + _delegate->callRedial(this); } bool Call::isKeyShaForFingerprintReady() const { @@ -490,8 +506,7 @@ void Call::setState(State state) { _delegate->callFailed(this); break; case State::Busy: - setState(State::Ended); -// _hangupByTimeoutTimer.call(kHangupTimeoutMs, [this] { setState(State::Ended); }); + destroyController(); // TODO play sound break; } @@ -552,7 +567,7 @@ void Call::handleControllerError(int error) { setState(State::Failed); } -Call::~Call() { +void Call::destroyController() { if (_controller) { DEBUG_LOG(("Call Info: Destroying call controller..")); _controller.reset(); @@ -560,6 +575,10 @@ Call::~Call() { } } +Call::~Call() { + destroyController(); +} + void UpdateConfig(const std::map &data) { tgvoip::ServerConfig::GetSharedInstance()->Update(data); } diff --git a/Telegram/SourceFiles/calls/calls_call.h b/Telegram/SourceFiles/calls/calls_call.h index cbedd17fec..8fecf0cd1e 100644 --- a/Telegram/SourceFiles/calls/calls_call.h +++ b/Telegram/SourceFiles/calls/calls_call.h @@ -44,6 +44,7 @@ public: virtual DhConfig getDhConfig() const = 0; virtual void callFinished(gsl::not_null call) = 0; virtual void callFailed(gsl::not_null call) = 0; + virtual void callRedial(gsl::not_null call) = 0; }; @@ -100,6 +101,7 @@ public: void answer(); void hangup(); + void redial(); bool isKeyShaForFingerprintReady() const; std::array getKeyShaForFingerprint() const; @@ -127,6 +129,7 @@ private: void setState(State state); void setStateQueued(State state); void setFailedQueued(int error); + void destroyController(); gsl::not_null _delegate; gsl::not_null _user; diff --git a/Telegram/SourceFiles/calls/calls_instance.cpp b/Telegram/SourceFiles/calls/calls_instance.cpp index dbdf6d95dc..d3364b1460 100644 --- a/Telegram/SourceFiles/calls/calls_instance.cpp +++ b/Telegram/SourceFiles/calls/calls_instance.cpp @@ -59,6 +59,12 @@ void Instance::callFailed(gsl::not_null call) { destroyCall(call); } +void Instance::callRedial(gsl::not_null call) { + if (_currentCall.get() == call) { + refreshDhConfig(); + } +} + void Instance::destroyCall(gsl::not_null call) { if (_currentCall.get() == call) { _currentCallPanel.reset(); diff --git a/Telegram/SourceFiles/calls/calls_instance.h b/Telegram/SourceFiles/calls/calls_instance.h index ae5b21e585..d305b7ac7b 100644 --- a/Telegram/SourceFiles/calls/calls_instance.h +++ b/Telegram/SourceFiles/calls/calls_instance.h @@ -54,6 +54,7 @@ private: } void callFinished(gsl::not_null call) override; void callFailed(gsl::not_null call) override; + void callRedial(gsl::not_null call) override; void createCall(gsl::not_null user, Call::Type type); void destroyCall(gsl::not_null call); diff --git a/Telegram/SourceFiles/calls/calls_panel.cpp b/Telegram/SourceFiles/calls/calls_panel.cpp index 2442a265ab..498d201348 100644 --- a/Telegram/SourceFiles/calls/calls_panel.cpp +++ b/Telegram/SourceFiles/calls/calls_panel.cpp @@ -71,6 +71,12 @@ void Panel::Button::paintEvent(QPaintEvent *e) { auto down = isDown(); auto position = _st.button.iconPosition; + if (position.x() < 0) { + position.setX((width() - _st.button.icon.width()) / 2); + } + if (position.y() < 0) { + position.setY((height() - _st.button.icon.height()) / 2); + } _st.button.icon.paint(p, position, width()); } @@ -127,19 +133,12 @@ void Panel::hideDeactivated() { void Panel::initControls() { subscribe(_call->stateChanged(), [this](State state) { stateChanged(state); }); - _hangup->setClickedCallback([this] { - if (_call) { - _call->hangup(); - } - }); if (_call->type() == Type::Incoming) { _answer.create(this, st::callAnswer); - _answer->setClickedCallback([this] { - if (_call) { - _call->answer(); - } - }); } + + refreshCallbacks(); + _mute->setClickedCallback([this] { _call->setMute(!_call->isMute()); }); @@ -162,6 +161,22 @@ void Panel::initControls() { }); } +void Panel::refreshCallbacks() { + auto safeSetCallback = [this](auto &&button, auto &&callback) { + if (button) { + button->setClickedCallback([this, callback] { + if (_call) { + callback(_call.get()); + } + }); + }; + }; + safeSetCallback(_answer, [](gsl::not_null call) { call->answer(); }); + safeSetCallback(_redial, [](gsl::not_null call) { call->redial(); }); + safeSetCallback(_hangup, [](gsl::not_null call) { call->hangup(); }); + safeSetCallback(_cancel, [](gsl::not_null call) { call->hangup(); }); +} + void Panel::initLayout() { hide(); @@ -316,11 +331,14 @@ void Panel::updateControlsGeometry() { updateStatusGeometry(); auto controlsTop = _contentTop + st::callControlsTop; - if (_answer) { - auto bothWidth = _answer->width() + st::callControlsSkip + _hangup->width(); - _hangup->moveToLeft((width() - bothWidth) / 2, controlsTop); - _answer->moveToRight((width() - bothWidth) / 2, controlsTop); + if (_answer || _redial) { + auto bothWidth = (_answer ? _answer : _redial)->width() + st::callControlsSkip + (_hangup ? _hangup : _cancel)->width(); + if (_hangup) _hangup->moveToLeft((width() - bothWidth) / 2, controlsTop); + if (_cancel) _cancel->moveToLeft((width() - bothWidth) / 2, controlsTop); + if (_answer) _answer->moveToRight((width() - bothWidth) / 2, controlsTop); + if (_redial) _redial->moveToRight((width() - bothWidth) / 2, controlsTop); } else { + t_assert(_hangup != nullptr); _hangup->moveToLeft((width() - _hangup->width()) / 2, controlsTop); } _mute->moveToRight(_padding.right() + st::callMuteRight, controlsTop); @@ -382,12 +400,30 @@ void Panel::mouseReleaseEvent(QMouseEvent *e) { void Panel::stateChanged(State state) { updateStatusText(state); - if (_answer - && state != State::Starting - && state != State::WaitingIncoming) { - _answer.destroy(); + + auto buttonsUpdated = false; + auto syncButton = [this, &buttonsUpdated](auto &&button, bool exists, auto &&style) { + if (exists == (button != nullptr)) { + return; + } + if (exists) { + button.create(this, style); + button->show(); + } else { + button.destroy(); + } + buttonsUpdated = true; + }; + syncButton(_answer, (state == State::Starting) || (state == State::WaitingIncoming), st::callAnswer); + syncButton(_hangup, (state != State::Busy), st::callHangup); + syncButton(_redial, (state == State::Busy), st::callAnswer); + syncButton(_cancel, (state == State::Busy), st::callCancel); + + if (buttonsUpdated) { + refreshCallbacks(); updateControlsGeometry(); } + if (_fingerprint.empty() && _call && _call->isKeyShaForFingerprintReady()) { _fingerprint = ComputeEmojiFingerprint(_call.get()); update(); diff --git a/Telegram/SourceFiles/calls/calls_panel.h b/Telegram/SourceFiles/calls/calls_panel.h index e47bb9a94d..9706be3c03 100644 --- a/Telegram/SourceFiles/calls/calls_panel.h +++ b/Telegram/SourceFiles/calls/calls_panel.h @@ -52,6 +52,7 @@ private: void initControls(); void initLayout(); void initGeometry(); + void refreshCallbacks(); void hideDeactivated(); void createBottomImage(); void createDefaultCacheImage(); @@ -81,8 +82,10 @@ private: class Button; object_ptr _close = { nullptr }; - object_ptr