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
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

View File

@ -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) {

View File

@ -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);
}

View File

@ -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;

View File

@ -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();

View File

@ -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);

View File

@ -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();

View File

@ -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;