Add "line busy" call state with a redial button.
This commit is contained in:
parent
d5ff728da6
commit
f42f79ea95
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -78,9 +78,10 @@ Call::Call(gsl::not_null<Delegate*> delegate, gsl::not_null<UserData*> 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<std::string, std::string> &data) {
|
||||
tgvoip::ServerConfig::GetSharedInstance()->Update(data);
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ public:
|
|||
virtual DhConfig getDhConfig() const = 0;
|
||||
virtual void callFinished(gsl::not_null<Call*> call) = 0;
|
||||
virtual void callFailed(gsl::not_null<Call*> call) = 0;
|
||||
virtual void callRedial(gsl::not_null<Call*> call) = 0;
|
||||
|
||||
};
|
||||
|
||||
|
@ -100,6 +101,7 @@ public:
|
|||
|
||||
void answer();
|
||||
void hangup();
|
||||
void redial();
|
||||
|
||||
bool isKeyShaForFingerprintReady() const;
|
||||
std::array<gsl::byte, kSha256Size> 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*> _delegate;
|
||||
gsl::not_null<UserData*> _user;
|
||||
|
|
|
@ -59,6 +59,12 @@ void Instance::callFailed(gsl::not_null<Call*> call) {
|
|||
destroyCall(call);
|
||||
}
|
||||
|
||||
void Instance::callRedial(gsl::not_null<Call*> call) {
|
||||
if (_currentCall.get() == call) {
|
||||
refreshDhConfig();
|
||||
}
|
||||
}
|
||||
|
||||
void Instance::destroyCall(gsl::not_null<Call*> call) {
|
||||
if (_currentCall.get() == call) {
|
||||
_currentCallPanel.reset();
|
||||
|
|
|
@ -54,6 +54,7 @@ private:
|
|||
}
|
||||
void callFinished(gsl::not_null<Call*> call) override;
|
||||
void callFailed(gsl::not_null<Call*> call) override;
|
||||
void callRedial(gsl::not_null<Call*> call) override;
|
||||
void createCall(gsl::not_null<UserData*> user, Call::Type type);
|
||||
void destroyCall(gsl::not_null<Call*> call);
|
||||
|
||||
|
|
|
@ -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) { call->answer(); });
|
||||
safeSetCallback(_redial, [](gsl::not_null<Call*> call) { call->redial(); });
|
||||
safeSetCallback(_hangup, [](gsl::not_null<Call*> call) { call->hangup(); });
|
||||
safeSetCallback(_cancel, [](gsl::not_null<Call*> 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();
|
||||
|
|
|
@ -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<Ui::IconButton> _close = { nullptr };
|
||||
object_ptr<Button> _answer = { nullptr };
|
||||
object_ptr<Button> _hangup;
|
||||
object_ptr<Button> _cancel = { nullptr };
|
||||
object_ptr<Button> _answer = { nullptr };
|
||||
object_ptr<Button> _redial = { nullptr };
|
||||
object_ptr<Ui::IconButton> _mute;
|
||||
object_ptr<Ui::FlatLabel> _name;
|
||||
object_ptr<Ui::FlatLabel> _status;
|
||||
|
|
Loading…
Reference in New Issue