mirror of
https://github.com/vale981/tdesktop
synced 2025-03-06 10:11:41 -05:00
Add "line busy" call state with a redial button.
This commit is contained in:
parent
d5ff728da6
commit
f42f79ea95
8 changed files with 109 additions and 30 deletions
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Add table
Reference in a new issue