Guarded users from instant calls with confirmation dialog.
This commit is contained in:
		
							parent
							
								
									30ae879fb3
								
							
						
					
					
						commit
						df176dd1d9
					
				
					 7 changed files with 102 additions and 23 deletions
				
			
		| 
						 | 
				
			
			@ -2572,6 +2572,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
			
		|||
"lng_call_status_failed" = "failed to connect";
 | 
			
		||||
"lng_call_status_ringing" = "ringing...";
 | 
			
		||||
"lng_call_status_busy" = "line busy";
 | 
			
		||||
"lng_call_status_sure" = "Click on the Camera icon if you want to start a video call.";
 | 
			
		||||
"lng_call_fingerprint_tooltip" = "If emoji on {user}'s screen are the same, this call is 100% secure";
 | 
			
		||||
 | 
			
		||||
"lng_call_error_not_available" = "Sorry, {user} doesn't accept calls.";
 | 
			
		||||
| 
						 | 
				
			
			@ -2612,6 +2613,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
			
		|||
"lng_call_rate_label" = "Please rate the quality of your call";
 | 
			
		||||
"lng_call_rate_comment" = "Comment (optional)";
 | 
			
		||||
 | 
			
		||||
"lng_call_start" = "Start Call";
 | 
			
		||||
"lng_call_start_video" = "Start Video";
 | 
			
		||||
"lng_call_stop_video" = "Stop Video";
 | 
			
		||||
"lng_call_screencast" = "Screencast";
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -117,6 +117,14 @@ callAnswer: CallButton {
 | 
			
		|||
	outerBg: callAnswerBgOuter;
 | 
			
		||||
	label: callButtonLabel;
 | 
			
		||||
}
 | 
			
		||||
callStartVideo: CallButton(callAnswer) {
 | 
			
		||||
	button: IconButton(callButton) {
 | 
			
		||||
		icon: icon {{ "calls/call_camera_active", callIconFg }};
 | 
			
		||||
		ripple: RippleAnimation(defaultRippleAnimation) {
 | 
			
		||||
			color: callAnswerRipple;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
callHangup: CallButton(callAnswer) {
 | 
			
		||||
	button: IconButton(callButton) {
 | 
			
		||||
		icon: icon {{ "calls/call_discard", callIconFg }};
 | 
			
		||||
| 
						 | 
				
			
			@ -292,7 +300,7 @@ callName: FlatLabel(defaultFlatLabel) {
 | 
			
		|||
}
 | 
			
		||||
callStatus: FlatLabel(defaultFlatLabel) {
 | 
			
		||||
	minWidth: 260px;
 | 
			
		||||
	maxHeight: 20px;
 | 
			
		||||
	maxHeight: 60px;
 | 
			
		||||
	textFg: callStatusFg;
 | 
			
		||||
	align: align(top);
 | 
			
		||||
	style: TextStyle(defaultTextStyle) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -220,7 +220,7 @@ Call::Call(
 | 
			
		|||
	std::make_unique<Webrtc::VideoTrack>(
 | 
			
		||||
		StartVideoState(video))) {
 | 
			
		||||
	if (_type == Type::Outgoing) {
 | 
			
		||||
		setState(State::Requesting);
 | 
			
		||||
		setState(State::WaitingUserConfirmation);
 | 
			
		||||
	} else {
 | 
			
		||||
		const auto &config = _user->session().serverConfig();
 | 
			
		||||
		_discardByTimeoutTimer.callOnce(config.callRingTimeoutMs);
 | 
			
		||||
| 
						 | 
				
			
			@ -344,6 +344,12 @@ void Call::startIncoming() {
 | 
			
		|||
	}).send();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Call::applyUserConfirmation() {
 | 
			
		||||
	if (_state.current() == State::WaitingUserConfirmation) {
 | 
			
		||||
		setState(State::Requesting);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Call::answer() {
 | 
			
		||||
	const auto video = isSharingVideo();
 | 
			
		||||
	_delegate->callRequestPermissionsOrFail(crl::guard(this, [=] {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -126,6 +126,7 @@ public:
 | 
			
		|||
		WaitingIncoming,
 | 
			
		||||
		Ringing,
 | 
			
		||||
		Busy,
 | 
			
		||||
		WaitingUserConfirmation,
 | 
			
		||||
	};
 | 
			
		||||
	[[nodiscard]] State state() const {
 | 
			
		||||
		return _state.current();
 | 
			
		||||
| 
						 | 
				
			
			@ -179,6 +180,7 @@ public:
 | 
			
		|||
	crl::time getDurationMs() const;
 | 
			
		||||
	float64 getWaitingSoundPeakValue() const;
 | 
			
		||||
 | 
			
		||||
	void applyUserConfirmation();
 | 
			
		||||
	void answer();
 | 
			
		||||
	void hangup();
 | 
			
		||||
	void redial();
 | 
			
		||||
| 
						 | 
				
			
			@ -257,7 +259,7 @@ private:
 | 
			
		|||
	const not_null<UserData*> _user;
 | 
			
		||||
	MTP::Sender _api;
 | 
			
		||||
	Type _type = Type::Outgoing;
 | 
			
		||||
	rpl::variable<State> _state = State::Starting;
 | 
			
		||||
	rpl::variable<State> _state = State::WaitingUserConfirmation;
 | 
			
		||||
	rpl::variable<RemoteAudioState> _remoteAudioState =
 | 
			
		||||
		RemoteAudioState::Active;
 | 
			
		||||
	rpl::variable<Webrtc::VideoState> _remoteVideoState;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -341,9 +341,14 @@ void Instance::createCall(not_null<UserData*> user, Call::Type type, bool video)
 | 
			
		|||
		_currentCallPanel = std::make_unique<Panel>(raw);
 | 
			
		||||
		_currentCall = std::move(call);
 | 
			
		||||
	}
 | 
			
		||||
	_currentCallChanges.fire_copy(raw);
 | 
			
		||||
	_currentCallPanel->startOutgoingRequests(
 | 
			
		||||
	) | rpl::start_with_next([=](bool video) {
 | 
			
		||||
		raw->applyUserConfirmation();
 | 
			
		||||
		raw->toggleCameraSharing(video);
 | 
			
		||||
		refreshServerConfig(&user->session());
 | 
			
		||||
		refreshDhConfig();
 | 
			
		||||
	}, raw->lifetime());
 | 
			
		||||
	_currentCallChanges.fire_copy(raw);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Instance::destroyGroupCall(not_null<GroupCall*> call) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -73,9 +73,22 @@ Panel::Panel(not_null<Call*> call)
 | 
			
		|||
, _answerHangupRedial(widget(), st::callAnswer, &st::callHangup)
 | 
			
		||||
, _decline(widget(), object_ptr<Ui::CallButton>(widget(), st::callHangup))
 | 
			
		||||
, _cancel(widget(), object_ptr<Ui::CallButton>(widget(), st::callCancel))
 | 
			
		||||
, _screencast(widget(), st::callScreencastOn, &st::callScreencastOff)
 | 
			
		||||
, _screencast(
 | 
			
		||||
	widget(),
 | 
			
		||||
	object_ptr<Ui::CallButton>(
 | 
			
		||||
		widget(),
 | 
			
		||||
		st::callScreencastOn,
 | 
			
		||||
		&st::callScreencastOff))
 | 
			
		||||
, _camera(widget(), st::callCameraMute, &st::callCameraUnmute)
 | 
			
		||||
, _mute(widget(), st::callMicrophoneMute, &st::callMicrophoneUnmute)
 | 
			
		||||
, _startVideo(
 | 
			
		||||
	widget(),
 | 
			
		||||
	object_ptr<Ui::CallButton>(widget(), st::callStartVideo))
 | 
			
		||||
, _mute(
 | 
			
		||||
	widget(),
 | 
			
		||||
	object_ptr<Ui::CallButton>(
 | 
			
		||||
		widget(),
 | 
			
		||||
		st::callMicrophoneMute,
 | 
			
		||||
		&st::callMicrophoneUnmute))
 | 
			
		||||
, _name(widget(), st::callName)
 | 
			
		||||
, _status(widget(), st::callStatus) {
 | 
			
		||||
	_layerBg->setStyleOverrides(&st::groupCallBox, &st::groupCallLayerBox);
 | 
			
		||||
| 
						 | 
				
			
			@ -214,12 +227,12 @@ void Panel::initWidget() {
 | 
			
		|||
 | 
			
		||||
void Panel::initControls() {
 | 
			
		||||
	_hangupShown = (_call->type() == Type::Outgoing);
 | 
			
		||||
	_mute->setClickedCallback([=] {
 | 
			
		||||
	_mute->entity()->setClickedCallback([=] {
 | 
			
		||||
		if (_call) {
 | 
			
		||||
			_call->setMuted(!_call->muted());
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
	_screencast->setClickedCallback([=] {
 | 
			
		||||
	_screencast->entity()->setClickedCallback([=] {
 | 
			
		||||
		if (!_call) {
 | 
			
		||||
			return;
 | 
			
		||||
		} else if (!Webrtc::DesktopCaptureAllowed()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -267,6 +280,7 @@ void Panel::initControls() {
 | 
			
		|||
			_call->redial();
 | 
			
		||||
		} else if (_call->isIncomingWaiting()) {
 | 
			
		||||
			_call->answer();
 | 
			
		||||
		} else if (state == State::WaitingUserConfirmation) {
 | 
			
		||||
		} else {
 | 
			
		||||
			_call->hangup();
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -278,6 +292,7 @@ void Panel::initControls() {
 | 
			
		|||
	};
 | 
			
		||||
	_decline->entity()->setClickedCallback(hangupCallback);
 | 
			
		||||
	_cancel->entity()->setClickedCallback(hangupCallback);
 | 
			
		||||
	_startVideo->entity()->setText(tr::lng_call_start_video());
 | 
			
		||||
 | 
			
		||||
	reinitWithCall(_call);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -318,6 +333,17 @@ rpl::lifetime &Panel::chooseSourceInstanceLifetime() {
 | 
			
		|||
	return lifetime();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
rpl::producer<bool> Panel::startOutgoingRequests() const {
 | 
			
		||||
	const auto filter = [=] {
 | 
			
		||||
		return _call && (_call->state() == State::WaitingUserConfirmation);
 | 
			
		||||
	};
 | 
			
		||||
	return rpl::merge(
 | 
			
		||||
		_startVideo->entity()->clicks(
 | 
			
		||||
		) | rpl::filter(filter) | rpl::map_to(true),
 | 
			
		||||
		_answerHangupRedial->clicks(
 | 
			
		||||
		) | rpl::filter(filter) | rpl::map_to(false));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Panel::chooseSourceAccepted(
 | 
			
		||||
		const QString &deviceId,
 | 
			
		||||
		bool withAudio) {
 | 
			
		||||
| 
						 | 
				
			
			@ -388,8 +414,8 @@ void Panel::reinitWithCall(Call *call) {
 | 
			
		|||
 | 
			
		||||
	_call->mutedValue(
 | 
			
		||||
	) | rpl::start_with_next([=](bool mute) {
 | 
			
		||||
		_mute->setProgress(mute ? 1. : 0.);
 | 
			
		||||
		_mute->setText(mute
 | 
			
		||||
		_mute->entity()->setProgress(mute ? 1. : 0.);
 | 
			
		||||
		_mute->entity()->setText(mute
 | 
			
		||||
			? tr::lng_call_unmute_audio()
 | 
			
		||||
			: tr::lng_call_mute_audio());
 | 
			
		||||
	}, _callLifetime);
 | 
			
		||||
| 
						 | 
				
			
			@ -405,8 +431,8 @@ void Panel::reinitWithCall(Call *call) {
 | 
			
		|||
		}
 | 
			
		||||
		{
 | 
			
		||||
			const auto active = _call->isSharingScreen();
 | 
			
		||||
			_screencast->setProgress(active ? 0. : 1.);
 | 
			
		||||
			_screencast->setText(tr::lng_call_screencast());
 | 
			
		||||
			_screencast->entity()->setProgress(active ? 0. : 1.);
 | 
			
		||||
			_screencast->entity()->setText(tr::lng_call_screencast());
 | 
			
		||||
			_outgoingVideoBubble->setMirrored(!active);
 | 
			
		||||
		}
 | 
			
		||||
	}, _callLifetime);
 | 
			
		||||
| 
						 | 
				
			
			@ -497,6 +523,7 @@ void Panel::reinitWithCall(Call *call) {
 | 
			
		|||
	_decline->raise();
 | 
			
		||||
	_cancel->raise();
 | 
			
		||||
	_camera->raise();
 | 
			
		||||
	_startVideo->raise();
 | 
			
		||||
	_mute->raise();
 | 
			
		||||
 | 
			
		||||
	_powerSaveBlocker = std::make_unique<base::PowerSaveBlocker>(
 | 
			
		||||
| 
						 | 
				
			
			@ -755,7 +782,10 @@ void Panel::updateHangupGeometry() {
 | 
			
		|||
	auto threeWidth = twoWidth + st::callCancel.button.width;
 | 
			
		||||
	auto rightFrom = (widget()->width() - threeWidth) / 2;
 | 
			
		||||
	auto rightTo = (widget()->width() - twoWidth) / 2;
 | 
			
		||||
	auto hangupProgress = _hangupShownProgress.value(_hangupShown ? 1. : 0.);
 | 
			
		||||
	auto hangupProgress = (_call
 | 
			
		||||
			&& _call->state() == State::WaitingUserConfirmation)
 | 
			
		||||
		? 0.
 | 
			
		||||
		: _hangupShownProgress.value(_hangupShown ? 1. : 0.);
 | 
			
		||||
	auto hangupRight = anim::interpolate(rightFrom, rightTo, hangupProgress);
 | 
			
		||||
	_answerHangupRedial->moveToRight(hangupRight, _buttonsTop);
 | 
			
		||||
	_answerHangupRedial->setProgress(hangupProgress);
 | 
			
		||||
| 
						 | 
				
			
			@ -764,6 +794,9 @@ void Panel::updateHangupGeometry() {
 | 
			
		|||
	_camera->moveToLeft(
 | 
			
		||||
		hangupRight - _mute->width() + _screencast->width(),
 | 
			
		||||
		_buttonsTop);
 | 
			
		||||
	if (_startVideo->toggled()) {
 | 
			
		||||
		_startVideo->moveToLeft(_camera->x(), _camera->y());
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Panel::updateStatusGeometry() {
 | 
			
		||||
| 
						 | 
				
			
			@ -811,33 +844,50 @@ void Panel::stateChanged(State state) {
 | 
			
		|||
		&& (state != State::EndedByOtherDevice)
 | 
			
		||||
		&& (state != State::FailedHangingUp)
 | 
			
		||||
		&& (state != State::Failed)) {
 | 
			
		||||
		if (state == State::Busy) {
 | 
			
		||||
		const auto isBusy = (state == State::Busy);
 | 
			
		||||
		const auto isWaitingUser = (state == State::WaitingUserConfirmation);
 | 
			
		||||
		if (isBusy) {
 | 
			
		||||
			_powerSaveBlocker = nullptr;
 | 
			
		||||
		}
 | 
			
		||||
		if (_startVideo->toggled() && !isWaitingUser) {
 | 
			
		||||
			_startVideo->toggle(false, anim::type::instant);
 | 
			
		||||
		} else if (!_startVideo->toggled() && isWaitingUser) {
 | 
			
		||||
			_startVideo->toggle(true, anim::type::instant);
 | 
			
		||||
		}
 | 
			
		||||
		_camera->setVisible(!_startVideo->toggled());
 | 
			
		||||
 | 
			
		||||
		auto toggleButton = [&](auto &&button, bool visible) {
 | 
			
		||||
		const auto toggleButton = [&](auto &&button, bool visible) {
 | 
			
		||||
			button->toggle(
 | 
			
		||||
				visible,
 | 
			
		||||
				window()->isHidden()
 | 
			
		||||
				? anim::type::instant
 | 
			
		||||
				: anim::type::normal);
 | 
			
		||||
		};
 | 
			
		||||
		auto incomingWaiting = _call->isIncomingWaiting();
 | 
			
		||||
		const auto incomingWaiting = _call->isIncomingWaiting();
 | 
			
		||||
		if (incomingWaiting) {
 | 
			
		||||
			_updateOuterRippleTimer.callEach(Call::kSoundSampleMs);
 | 
			
		||||
		}
 | 
			
		||||
		toggleButton(_decline, incomingWaiting);
 | 
			
		||||
		toggleButton(_cancel, (state == State::Busy));
 | 
			
		||||
		auto hangupShown = !_decline->toggled()
 | 
			
		||||
		toggleButton(_cancel, (isBusy || isWaitingUser));
 | 
			
		||||
		toggleButton(_mute, !isWaitingUser);
 | 
			
		||||
		toggleButton(_screencast, !isWaitingUser);
 | 
			
		||||
		const auto hangupShown = !_decline->toggled()
 | 
			
		||||
			&& !_cancel->toggled();
 | 
			
		||||
		if (_hangupShown != hangupShown) {
 | 
			
		||||
			_hangupShown = hangupShown;
 | 
			
		||||
			_hangupShownProgress.start([this] { updateHangupGeometry(); }, _hangupShown ? 0. : 1., _hangupShown ? 1. : 0., st::callPanelDuration, anim::sineInOut);
 | 
			
		||||
			_hangupShownProgress.start(
 | 
			
		||||
				[this] { updateHangupGeometry(); },
 | 
			
		||||
				_hangupShown ? 0. : 1.,
 | 
			
		||||
				_hangupShown ? 1. : 0.,
 | 
			
		||||
				st::callPanelDuration,
 | 
			
		||||
				anim::sineInOut);
 | 
			
		||||
		}
 | 
			
		||||
		const auto answerHangupRedialState = incomingWaiting
 | 
			
		||||
			? AnswerHangupRedialState::Answer
 | 
			
		||||
			: (state == State::Busy)
 | 
			
		||||
			: isBusy
 | 
			
		||||
			? AnswerHangupRedialState::Redial
 | 
			
		||||
			: isWaitingUser
 | 
			
		||||
			? AnswerHangupRedialState::StartCall
 | 
			
		||||
			: AnswerHangupRedialState::Hangup;
 | 
			
		||||
		if (_answerHangupRedialState != answerHangupRedialState) {
 | 
			
		||||
			_answerHangupRedialState = answerHangupRedialState;
 | 
			
		||||
| 
						 | 
				
			
			@ -860,6 +910,7 @@ void Panel::refreshAnswerHangupRedialLabel() {
 | 
			
		|||
		case AnswerHangupRedialState::Answer: return tr::lng_call_accept();
 | 
			
		||||
		case AnswerHangupRedialState::Hangup: return tr::lng_call_end_call();
 | 
			
		||||
		case AnswerHangupRedialState::Redial: return tr::lng_call_redial();
 | 
			
		||||
		case AnswerHangupRedialState::StartCall: return tr::lng_call_start();
 | 
			
		||||
		}
 | 
			
		||||
		Unexpected("AnswerHangupRedialState value.");
 | 
			
		||||
	}());
 | 
			
		||||
| 
						 | 
				
			
			@ -891,6 +942,7 @@ void Panel::updateStatusText(State state) {
 | 
			
		|||
		case State::WaitingIncoming: return tr::lng_call_status_incoming(tr::now);
 | 
			
		||||
		case State::Ringing: return tr::lng_call_status_ringing(tr::now);
 | 
			
		||||
		case State::Busy: return tr::lng_call_status_busy(tr::now);
 | 
			
		||||
		case State::WaitingUserConfirmation: return tr::lng_call_status_sure(tr::now);
 | 
			
		||||
		}
 | 
			
		||||
		Unexpected("State in stateChanged()");
 | 
			
		||||
	};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -77,6 +77,8 @@ public:
 | 
			
		|||
		bool withAudio) override;
 | 
			
		||||
	void chooseSourceStop() override;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] rpl::producer<bool> startOutgoingRequests() const;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] rpl::lifetime &lifetime();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
| 
						 | 
				
			
			@ -87,6 +89,7 @@ private:
 | 
			
		|||
		Answer,
 | 
			
		||||
		Hangup,
 | 
			
		||||
		Redial,
 | 
			
		||||
		StartCall,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] not_null<Ui::RpWindow*> window() const;
 | 
			
		||||
| 
						 | 
				
			
			@ -147,9 +150,10 @@ private:
 | 
			
		|||
	bool _outgoingPreviewInBody = false;
 | 
			
		||||
	std::optional<AnswerHangupRedialState> _answerHangupRedialState;
 | 
			
		||||
	Ui::Animations::Simple _hangupShownProgress;
 | 
			
		||||
	object_ptr<Ui::CallButton> _screencast;
 | 
			
		||||
	object_ptr<Ui::FadeWrap<Ui::CallButton>> _screencast;
 | 
			
		||||
	object_ptr<Ui::CallButton> _camera;
 | 
			
		||||
	object_ptr<Ui::CallButton> _mute;
 | 
			
		||||
	object_ptr<Ui::FadeWrap<Ui::CallButton>> _startVideo;
 | 
			
		||||
	object_ptr<Ui::FadeWrap<Ui::CallButton>> _mute;
 | 
			
		||||
	object_ptr<Ui::FlatLabel> _name;
 | 
			
		||||
	object_ptr<Ui::FlatLabel> _status;
 | 
			
		||||
	object_ptr<Ui::RpWidget> _fingerprint = { nullptr };
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue