Add "line busy" call state with a redial button.

This commit is contained in:
John Preston 2017-04-29 21:00:27 +03:00
parent d5ff728da6
commit f42f79ea95
8 changed files with 109 additions and 30 deletions

View file

@ -530,6 +530,9 @@ callAnswerBg: #64c15b; // phone call popup answer button background
callAnswerRipple: #52b149; // phone call popup answer button ripple effect callAnswerRipple: #52b149; // phone call popup answer button ripple effect
callHangupBg: #d75a5a; // phone call popup hangup button background callHangupBg: #d75a5a; // phone call popup hangup button background
callHangupRipple: #c04646; // phone call popup hangup button ripple effect 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 callMuteRipple: #ffffff12; // phone call popup mute mic ripple effect
callBarBg: dialogsBgActive; // active phone call bar background callBarBg: dialogsBgActive; // active phone call bar background

View file

@ -42,7 +42,7 @@ callButton: IconButton {
width: 64px; width: 64px;
height: 64px; height: 64px;
iconPosition: point(20px, 20px); iconPosition: point(-1px, -1px);
rippleAreaPosition: point(8px, 8px); rippleAreaPosition: point(8px, 8px);
rippleAreaSize: 48px; rippleAreaSize: 48px;
@ -61,13 +61,21 @@ callAnswer: CallButton {
callHangup: CallButton { callHangup: CallButton {
button: IconButton(callButton) { button: IconButton(callButton) {
icon: icon {{ "call_discard", callIconFg }}; icon: icon {{ "call_discard", callIconFg }};
iconPosition: point(20px, 24px);
ripple: RippleAnimation(defaultRippleAnimation) { ripple: RippleAnimation(defaultRippleAnimation) {
color: callHangupRipple; color: callHangupRipple;
} }
} }
bg: callHangupBg; bg: callHangupBg;
} }
callCancel: CallButton {
button: IconButton(callButton) {
icon: icon {{ "box_button_close", callCancelFg }};
ripple: RippleAnimation(defaultRippleAnimation) {
color: callCancelRipple;
}
}
bg: callCancelBg;
}
callMuteToggle: IconButton(callButton) { callMuteToggle: IconButton(callButton) {
icon: icon {{ "call_record_active", callIconFg }}; icon: icon {{ "call_record_active", callIconFg }};
ripple: RippleAnimation(defaultRippleAnimation) { ripple: RippleAnimation(defaultRippleAnimation) {

View file

@ -78,9 +78,10 @@ Call::Call(gsl::not_null<Delegate*> delegate, gsl::not_null<UserData*> user, Typ
: _delegate(delegate) : _delegate(delegate)
, _user(user) , _user(user)
, _type(type) { , _type(type) {
_discardByTimeoutTimer.setCallback([this] { hangup(); });
if (_type == Type::Outgoing) { if (_type == Type::Outgoing) {
setState(State::Requesting); setState(State::Requesting);
_discardByTimeoutTimer.setCallback([this] { hangup(); });
} }
} }
@ -198,11 +199,26 @@ TimeMs Call::getDurationMs() const {
} }
void Call::hangup() { void Call::hangup() {
auto missed = (_state == State::Ringing || (_state == State::Waiting && _type == Type::Outgoing)); if (_state == State::Busy) {
auto declined = (_state == State::WaitingIncoming); // Cancel call instead of redial.
auto reason = missed ? MTP_phoneCallDiscardReasonMissed() : setState(Call::Ended);
declined ? MTP_phoneCallDiscardReasonBusy() : MTP_phoneCallDiscardReasonHangup(); } else {
finish(reason); 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 { bool Call::isKeyShaForFingerprintReady() const {
@ -490,8 +506,7 @@ void Call::setState(State state) {
_delegate->callFailed(this); _delegate->callFailed(this);
break; break;
case State::Busy: case State::Busy:
setState(State::Ended); destroyController();
// _hangupByTimeoutTimer.call(kHangupTimeoutMs, [this] { setState(State::Ended); });
// TODO play sound // TODO play sound
break; break;
} }
@ -552,7 +567,7 @@ void Call::handleControllerError(int error) {
setState(State::Failed); setState(State::Failed);
} }
Call::~Call() { void Call::destroyController() {
if (_controller) { if (_controller) {
DEBUG_LOG(("Call Info: Destroying call controller..")); DEBUG_LOG(("Call Info: Destroying call controller.."));
_controller.reset(); _controller.reset();
@ -560,6 +575,10 @@ Call::~Call() {
} }
} }
Call::~Call() {
destroyController();
}
void UpdateConfig(const std::map<std::string, std::string> &data) { void UpdateConfig(const std::map<std::string, std::string> &data) {
tgvoip::ServerConfig::GetSharedInstance()->Update(data); tgvoip::ServerConfig::GetSharedInstance()->Update(data);
} }

View file

@ -44,6 +44,7 @@ public:
virtual DhConfig getDhConfig() const = 0; virtual DhConfig getDhConfig() const = 0;
virtual void callFinished(gsl::not_null<Call*> call) = 0; virtual void callFinished(gsl::not_null<Call*> call) = 0;
virtual void callFailed(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 answer();
void hangup(); void hangup();
void redial();
bool isKeyShaForFingerprintReady() const; bool isKeyShaForFingerprintReady() const;
std::array<gsl::byte, kSha256Size> getKeyShaForFingerprint() const; std::array<gsl::byte, kSha256Size> getKeyShaForFingerprint() const;
@ -127,6 +129,7 @@ private:
void setState(State state); void setState(State state);
void setStateQueued(State state); void setStateQueued(State state);
void setFailedQueued(int error); void setFailedQueued(int error);
void destroyController();
gsl::not_null<Delegate*> _delegate; gsl::not_null<Delegate*> _delegate;
gsl::not_null<UserData*> _user; gsl::not_null<UserData*> _user;

View file

@ -59,6 +59,12 @@ void Instance::callFailed(gsl::not_null<Call*> call) {
destroyCall(call); destroyCall(call);
} }
void Instance::callRedial(gsl::not_null<Call*> call) {
if (_currentCall.get() == call) {
refreshDhConfig();
}
}
void Instance::destroyCall(gsl::not_null<Call*> call) { void Instance::destroyCall(gsl::not_null<Call*> call) {
if (_currentCall.get() == call) { if (_currentCall.get() == call) {
_currentCallPanel.reset(); _currentCallPanel.reset();

View file

@ -54,6 +54,7 @@ private:
} }
void callFinished(gsl::not_null<Call*> call) override; void callFinished(gsl::not_null<Call*> call) override;
void callFailed(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 createCall(gsl::not_null<UserData*> user, Call::Type type);
void destroyCall(gsl::not_null<Call*> call); void destroyCall(gsl::not_null<Call*> call);

View file

@ -71,6 +71,12 @@ void Panel::Button::paintEvent(QPaintEvent *e) {
auto down = isDown(); auto down = isDown();
auto position = _st.button.iconPosition; 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()); _st.button.icon.paint(p, position, width());
} }
@ -127,19 +133,12 @@ void Panel::hideDeactivated() {
void Panel::initControls() { void Panel::initControls() {
subscribe(_call->stateChanged(), [this](State state) { stateChanged(state); }); subscribe(_call->stateChanged(), [this](State state) { stateChanged(state); });
_hangup->setClickedCallback([this] {
if (_call) {
_call->hangup();
}
});
if (_call->type() == Type::Incoming) { if (_call->type() == Type::Incoming) {
_answer.create(this, st::callAnswer); _answer.create(this, st::callAnswer);
_answer->setClickedCallback([this] {
if (_call) {
_call->answer();
}
});
} }
refreshCallbacks();
_mute->setClickedCallback([this] { _mute->setClickedCallback([this] {
_call->setMute(!_call->isMute()); _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() { void Panel::initLayout() {
hide(); hide();
@ -316,11 +331,14 @@ void Panel::updateControlsGeometry() {
updateStatusGeometry(); updateStatusGeometry();
auto controlsTop = _contentTop + st::callControlsTop; auto controlsTop = _contentTop + st::callControlsTop;
if (_answer) { if (_answer || _redial) {
auto bothWidth = _answer->width() + st::callControlsSkip + _hangup->width(); auto bothWidth = (_answer ? _answer : _redial)->width() + st::callControlsSkip + (_hangup ? _hangup : _cancel)->width();
_hangup->moveToLeft((width() - bothWidth) / 2, controlsTop); if (_hangup) _hangup->moveToLeft((width() - bothWidth) / 2, controlsTop);
_answer->moveToRight((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 { } else {
t_assert(_hangup != nullptr);
_hangup->moveToLeft((width() - _hangup->width()) / 2, controlsTop); _hangup->moveToLeft((width() - _hangup->width()) / 2, controlsTop);
} }
_mute->moveToRight(_padding.right() + st::callMuteRight, controlsTop); _mute->moveToRight(_padding.right() + st::callMuteRight, controlsTop);
@ -382,12 +400,30 @@ void Panel::mouseReleaseEvent(QMouseEvent *e) {
void Panel::stateChanged(State state) { void Panel::stateChanged(State state) {
updateStatusText(state); updateStatusText(state);
if (_answer
&& state != State::Starting auto buttonsUpdated = false;
&& state != State::WaitingIncoming) { auto syncButton = [this, &buttonsUpdated](auto &&button, bool exists, auto &&style) {
_answer.destroy(); 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(); updateControlsGeometry();
} }
if (_fingerprint.empty() && _call && _call->isKeyShaForFingerprintReady()) { if (_fingerprint.empty() && _call && _call->isKeyShaForFingerprintReady()) {
_fingerprint = ComputeEmojiFingerprint(_call.get()); _fingerprint = ComputeEmojiFingerprint(_call.get());
update(); update();

View file

@ -52,6 +52,7 @@ private:
void initControls(); void initControls();
void initLayout(); void initLayout();
void initGeometry(); void initGeometry();
void refreshCallbacks();
void hideDeactivated(); void hideDeactivated();
void createBottomImage(); void createBottomImage();
void createDefaultCacheImage(); void createDefaultCacheImage();
@ -81,8 +82,10 @@ private:
class Button; class Button;
object_ptr<Ui::IconButton> _close = { nullptr }; object_ptr<Ui::IconButton> _close = { nullptr };
object_ptr<Button> _answer = { nullptr };
object_ptr<Button> _hangup; 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::IconButton> _mute;
object_ptr<Ui::FlatLabel> _name; object_ptr<Ui::FlatLabel> _name;
object_ptr<Ui::FlatLabel> _status; object_ptr<Ui::FlatLabel> _status;