From 60ac7572af3c58b8a4697dd0ee5b1f6fef2ad021 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 25 Apr 2017 19:45:41 +0300 Subject: [PATCH] Display name, status and emoji in Call Panel. --- Telegram/Resources/langs/lang.strings | 12 ++ Telegram/SourceFiles/calls/calls.style | 29 ++++ Telegram/SourceFiles/calls/calls_call.cpp | 35 +++- Telegram/SourceFiles/calls/calls_call.h | 17 +- .../calls/calls_emoji_fingerprint.cpp | 157 ++++++++++++++++++ .../calls/calls_emoji_fingerprint.h | 29 ++++ Telegram/SourceFiles/calls/calls_panel.cpp | 112 +++++++++++-- Telegram/SourceFiles/calls/calls_panel.h | 16 +- .../SourceFiles/history/history_message.cpp | 13 +- Telegram/gyp/telegram_sources.txt | 2 + 10 files changed, 397 insertions(+), 25 deletions(-) create mode 100644 Telegram/SourceFiles/calls/calls_emoji_fingerprint.cpp create mode 100644 Telegram/SourceFiles/calls/calls_emoji_fingerprint.h diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index fea310e94..08f317d4a 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -676,6 +676,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org "lng_action_call_incoming_duration" = "Incoming call ({duration}) at {time}"; "lng_action_call_incoming_missed" = "Missed call at {time}"; "lng_action_call_outgoing_missed" = "Cancelled call at {time}"; +"lng_action_call_incoming_declined" = "Declined call at {time}"; "lng_action_payment_done" = "You have just successfully transferred {amount} to {user}"; "lng_action_payment_done_for" = "You have just successfully transferred {amount} to {user} for {invoice}"; @@ -1122,6 +1123,17 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org "lng_payments_invoice_label_test" = "Test invoice"; "lng_payments_receipt_button" = "Receipt"; +"lng_call_status_incoming" = "is calling you..."; +"lng_call_status_connecting" = "connecting..."; +"lng_call_status_exchanging" = "exchanging encryption keys..."; +"lng_call_status_waiting" = "waiting..."; +"lng_call_status_requesting" = "requesting..."; +"lng_call_status_hanging" = "hanging up..."; +"lng_call_status_ended" = "call ended"; +"lng_call_status_failed" = "failed to connect"; +"lng_call_status_ringing" = "ringing..."; +"lng_call_status_busy" = "line busy"; + // Not used "lng_topbar_info" = "Info"; diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style index 46f389802..abfbf9438 100644 --- a/Telegram/SourceFiles/calls/calls.style +++ b/Telegram/SourceFiles/calls/calls.style @@ -78,3 +78,32 @@ callUnmuteIcon: icon {{ "call_record_muted", callIconFg }}; callControlsTop: 84px; callControlsSkip: 8px; callMuteRight: 12px; + +callNameTop: 15px; +callName: FlatLabel(defaultFlatLabel) { + width: 260px; + maxHeight: 24px; + textFg: callNameFg; + align: align(top); + style: TextStyle(defaultTextStyle) { + font: font(21px semibold); + linkFont: font(21px semibold); + linkFontOver: font(21px semibold underline); + } +} +callStatusTop: 46px; +callStatus: FlatLabel(defaultFlatLabel) { + width: 260px; + maxHeight: 20px; + textFg: callStatusFg; + align: align(top); + style: TextStyle(defaultTextStyle) { + font: font(14px); + linkFont: font(14px); + linkFontOver: font(14px underline); + } +} + +callFingerprintPadding: margins(9px, 4px, 9px, 5px); +callFingerprintSkip: 3px; +callFingerprintBottom: 8px; diff --git a/Telegram/SourceFiles/calls/calls_call.cpp b/Telegram/SourceFiles/calls/calls_call.cpp index 560397cc6..bbb444e54 100644 --- a/Telegram/SourceFiles/calls/calls_call.cpp +++ b/Telegram/SourceFiles/calls/calls_call.cpp @@ -45,6 +45,8 @@ namespace { constexpr auto kMinLayer = 65; constexpr auto kMaxLayer = 65; // MTP::CurrentLayer? constexpr auto kHangupTimeoutMs = 5000; // TODO read from server config +constexpr auto kReceiveTimeoutMs = 5000; // TODO read from server config call_receive_timeout_ms +constexpr auto kRingTimeoutMs = 5000; // TODO read from server config call_ring_timeout_ms using tgvoip::Endpoint; @@ -148,7 +150,7 @@ void Call::startIncoming() { Expects(_type == Type::Incoming); request(MTPphone_ReceivedCall(MTP_inputPhoneCall(MTP_long(_id), MTP_long(_accessHash)))).done([this](const MTPBool &result) { - setState(State::Ringing); + setState(State::WaitingIncoming); }).fail([this](const RPCError &error) { setState(State::Failed); }).send(); @@ -179,16 +181,32 @@ void Call::setMute(bool mute) { if (_controller) { _controller->SetMicMute(_mute); } + _muteChanged.notify(_mute); +} + +TimeMs Call::getDurationMs() const { + return _startTime ? (getms(true) - _startTime) : 0; } void Call::hangup() { auto missed = (_state == State::Ringing || (_state == State::Waiting && _type == Type::Outgoing)); - auto reason = missed ? MTP_phoneCallDiscardReasonMissed() : MTP_phoneCallDiscardReasonHangup(); + auto declined = (_state == State::WaitingIncoming); + auto reason = missed ? MTP_phoneCallDiscardReasonMissed() : + declined ? MTP_phoneCallDiscardReasonBusy() : MTP_phoneCallDiscardReasonHangup(); finish(reason); } -void Call::decline() { - finish(MTP_phoneCallDiscardReasonBusy()); +bool Call::isKeyShaForFingerprintReady() const { + return (_keyFingerprint != 0); +} + +std::array Call::getKeyShaForFingerprint() const { + Expects(isKeyShaForFingerprintReady()); + Expects(!_ga.empty()); + auto encryptedChatAuthKey = base::byte_vector(_authKey.size() + _ga.size(), gsl::byte {}); + base::copy_bytes(gsl::make_span(encryptedChatAuthKey).subspan(0, _authKey.size()), _authKey); + base::copy_bytes(gsl::make_span(encryptedChatAuthKey).subspan(_authKey.size(), _ga.size()), _ga); + return openssl::Sha256(encryptedChatAuthKey); } bool Call::handleUpdate(const MTPPhoneCall &call) { @@ -232,6 +250,9 @@ bool Call::handleUpdate(const MTPPhoneCall &call) { if (data.vid.v != _id) { return false; } + if (_state == State::Waiting && data.vreceive_date.v != 0) { + setState(State::Ringing); + } } return true; case mtpc_phoneCall: { @@ -319,6 +340,7 @@ void Call::startConfirmedCall(const MTPDphoneCall &call) { setState(State::Failed); return; } + _ga = base::byte_vector(firstBytes.begin(), firstBytes.end()); auto computedAuthKey = MTP::CreateAuthKey(firstBytes, _randomPower, _dhConfig.p); if (computedAuthKey.empty()) { @@ -456,7 +478,8 @@ void Call::setState(State state) { _delegate->callFailed(this); break; case State::Busy: - _hangupByTimeoutTimer.call(kHangupTimeoutMs, [this] { setState(State::Ended); }); + setState(State::Ended); +// _hangupByTimeoutTimer.call(kHangupTimeoutMs, [this] { setState(State::Ended); }); // TODO play sound break; } @@ -478,7 +501,7 @@ void Call::finish(const MTPPhoneCallDiscardReason &reason) { } setState(State::HangingUp); - auto duration = _startTime ? static_cast((getms(true) - _startTime) / 1000) : 0; + auto duration = getDurationMs() / 1000; auto connectionId = _controller ? _controller->GetPreferredRelayID() : 0; _hangupByTimeoutTimer.call(kHangupTimeoutMs, [this] { setState(State::Ended); }); request(MTPphone_DiscardCall(MTP_inputPhoneCall(MTP_long(_id), MTP_long(_accessHash)), MTP_int(duration), reason, MTP_long(connectionId))).done([this](const MTPUpdates &result) { diff --git a/Telegram/SourceFiles/calls/calls_call.h b/Telegram/SourceFiles/calls/calls_call.h index 040bf541d..b856ea70d 100644 --- a/Telegram/SourceFiles/calls/calls_call.h +++ b/Telegram/SourceFiles/calls/calls_call.h @@ -48,6 +48,7 @@ public: }; static constexpr auto kRandomPowerSize = 256; + static constexpr auto kSha256Size = 32; enum class Type { Incoming, @@ -85,17 +86,26 @@ public: base::Observable &stateChanged() { return _stateChanged; } + void setMute(bool mute); + bool isMute() const { + return _mute; + } + base::Observable &muteChanged() { + return _muteChanged; + } + + TimeMs getDurationMs() const; void answer(); void hangup(); - void decline(); + + bool isKeyShaForFingerprintReady() const; + std::array getKeyShaForFingerprint() const; ~Call(); private: - static constexpr auto kSha256Size = 32; - void finish(const MTPPhoneCallDiscardReason &reason); void startOutgoing(); void startIncoming(); @@ -123,6 +133,7 @@ private: TimeMs _startTime = 0; base::DelayedCallTimer _hangupByTimeoutTimer; bool _mute = false; + base::Observable _muteChanged; DhConfig _dhConfig; std::vector _ga; diff --git a/Telegram/SourceFiles/calls/calls_emoji_fingerprint.cpp b/Telegram/SourceFiles/calls/calls_emoji_fingerprint.cpp new file mode 100644 index 000000000..ecaab45ae --- /dev/null +++ b/Telegram/SourceFiles/calls/calls_emoji_fingerprint.cpp @@ -0,0 +1,157 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org +*/ +#include "calls/calls_emoji_fingerprint.h" + +#include "calls/calls_call.h" + +namespace Calls { +namespace { + +ushort Data[] = { +0xd83d, 0xde09, 0xd83d, 0xde0d, 0xd83d, 0xde1b, 0xd83d, 0xde2d, 0xd83d, 0xde31, 0xd83d, 0xde21, +0xd83d, 0xde0e, 0xd83d, 0xde34, 0xd83d, 0xde35, 0xd83d, 0xde08, 0xd83d, 0xde2c, 0xd83d, 0xde07, +0xd83d, 0xde0f, 0xd83d, 0xdc6e, 0xd83d, 0xdc77, 0xd83d, 0xdc82, 0xd83d, 0xdc76, 0xd83d, 0xdc68, +0xd83d, 0xdc69, 0xd83d, 0xdc74, 0xd83d, 0xdc75, 0xd83d, 0xde3b, 0xd83d, 0xde3d, 0xd83d, 0xde40, +0xd83d, 0xdc7a, 0xd83d, 0xde48, 0xd83d, 0xde49, 0xd83d, 0xde4a, 0xd83d, 0xdc80, 0xd83d, 0xdc7d, +0xd83d, 0xdca9, 0xd83d, 0xdd25, 0xd83d, 0xdca5, 0xd83d, 0xdca4, 0xd83d, 0xdc42, 0xd83d, 0xdc40, +0xd83d, 0xdc43, 0xd83d, 0xdc45, 0xd83d, 0xdc44, 0xd83d, 0xdc4d, 0xd83d, 0xdc4e, 0xd83d, 0xdc4c, +0xd83d, 0xdc4a, 0x270c, 0x270b, 0xd83d, 0xdc50, 0xd83d, 0xdc46, 0xd83d, 0xdc47, 0xd83d, 0xdc49, +0xd83d, 0xdc48, 0xd83d, 0xde4f, 0xd83d, 0xdc4f, 0xd83d, 0xdcaa, 0xd83d, 0xdeb6, 0xd83c, 0xdfc3, +0xd83d, 0xdc83, 0xd83d, 0xdc6b, 0xd83d, 0xdc6a, 0xd83d, 0xdc6c, 0xd83d, 0xdc6d, 0xd83d, 0xdc85, +0xd83c, 0xdfa9, 0xd83d, 0xdc51, 0xd83d, 0xdc52, 0xd83d, 0xdc5f, 0xd83d, 0xdc5e, 0xd83d, 0xdc60, +0xd83d, 0xdc55, 0xd83d, 0xdc57, 0xd83d, 0xdc56, 0xd83d, 0xdc59, 0xd83d, 0xdc5c, 0xd83d, 0xdc53, +0xd83c, 0xdf80, 0xd83d, 0xdc84, 0xd83d, 0xdc9b, 0xd83d, 0xdc99, 0xd83d, 0xdc9c, 0xd83d, 0xdc9a, +0xd83d, 0xdc8d, 0xd83d, 0xdc8e, 0xd83d, 0xdc36, 0xd83d, 0xdc3a, 0xd83d, 0xdc31, 0xd83d, 0xdc2d, +0xd83d, 0xdc39, 0xd83d, 0xdc30, 0xd83d, 0xdc38, 0xd83d, 0xdc2f, 0xd83d, 0xdc28, 0xd83d, 0xdc3b, +0xd83d, 0xdc37, 0xd83d, 0xdc2e, 0xd83d, 0xdc17, 0xd83d, 0xdc34, 0xd83d, 0xdc11, 0xd83d, 0xdc18, +0xd83d, 0xdc3c, 0xd83d, 0xdc27, 0xd83d, 0xdc25, 0xd83d, 0xdc14, 0xd83d, 0xdc0d, 0xd83d, 0xdc22, +0xd83d, 0xdc1b, 0xd83d, 0xdc1d, 0xd83d, 0xdc1c, 0xd83d, 0xdc1e, 0xd83d, 0xdc0c, 0xd83d, 0xdc19, +0xd83d, 0xdc1a, 0xd83d, 0xdc1f, 0xd83d, 0xdc2c, 0xd83d, 0xdc0b, 0xd83d, 0xdc10, 0xd83d, 0xdc0a, +0xd83d, 0xdc2b, 0xd83c, 0xdf40, 0xd83c, 0xdf39, 0xd83c, 0xdf3b, 0xd83c, 0xdf41, 0xd83c, 0xdf3e, +0xd83c, 0xdf44, 0xd83c, 0xdf35, 0xd83c, 0xdf34, 0xd83c, 0xdf33, 0xd83c, 0xdf1e, 0xd83c, 0xdf1a, +0xd83c, 0xdf19, 0xd83c, 0xdf0e, 0xd83c, 0xdf0b, 0x26a1, 0x2614, 0x2744, 0x26c4, 0xd83c, 0xdf00, +0xd83c, 0xdf08, 0xd83c, 0xdf0a, 0xd83c, 0xdf93, 0xd83c, 0xdf86, 0xd83c, 0xdf83, 0xd83d, 0xdc7b, +0xd83c, 0xdf85, 0xd83c, 0xdf84, 0xd83c, 0xdf81, 0xd83c, 0xdf88, 0xd83d, 0xdd2e, 0xd83c, 0xdfa5, +0xd83d, 0xdcf7, 0xd83d, 0xdcbf, 0xd83d, 0xdcbb, 0x260e, 0xd83d, 0xdce1, 0xd83d, 0xdcfa, 0xd83d, +0xdcfb, 0xd83d, 0xdd09, 0xd83d, 0xdd14, 0x23f3, 0x23f0, 0x231a, 0xd83d, 0xdd12, 0xd83d, 0xdd11, +0xd83d, 0xdd0e, 0xd83d, 0xdca1, 0xd83d, 0xdd26, 0xd83d, 0xdd0c, 0xd83d, 0xdd0b, 0xd83d, 0xdebf, +0xd83d, 0xdebd, 0xd83d, 0xdd27, 0xd83d, 0xdd28, 0xd83d, 0xdeaa, 0xd83d, 0xdeac, 0xd83d, 0xdca3, +0xd83d, 0xdd2b, 0xd83d, 0xdd2a, 0xd83d, 0xdc8a, 0xd83d, 0xdc89, 0xd83d, 0xdcb0, 0xd83d, 0xdcb5, +0xd83d, 0xdcb3, 0x2709, 0xd83d, 0xdceb, 0xd83d, 0xdce6, 0xd83d, 0xdcc5, 0xd83d, 0xdcc1, 0x2702, +0xd83d, 0xdccc, 0xd83d, 0xdcce, 0x2712, 0x270f, 0xd83d, 0xdcd0, 0xd83d, 0xdcda, 0xd83d, 0xdd2c, +0xd83d, 0xdd2d, 0xd83c, 0xdfa8, 0xd83c, 0xdfac, 0xd83c, 0xdfa4, 0xd83c, 0xdfa7, 0xd83c, 0xdfb5, +0xd83c, 0xdfb9, 0xd83c, 0xdfbb, 0xd83c, 0xdfba, 0xd83c, 0xdfb8, 0xd83d, 0xdc7e, 0xd83c, 0xdfae, +0xd83c, 0xdccf, 0xd83c, 0xdfb2, 0xd83c, 0xdfaf, 0xd83c, 0xdfc8, 0xd83c, 0xdfc0, 0x26bd, 0x26be, +0xd83c, 0xdfbe, 0xd83c, 0xdfb1, 0xd83c, 0xdfc9, 0xd83c, 0xdfb3, 0xd83c, 0xdfc1, 0xd83c, 0xdfc7, +0xd83c, 0xdfc6, 0xd83c, 0xdfca, 0xd83c, 0xdfc4, 0x2615, 0xd83c, 0xdf7c, 0xd83c, 0xdf7a, 0xd83c, +0xdf77, 0xd83c, 0xdf74, 0xd83c, 0xdf55, 0xd83c, 0xdf54, 0xd83c, 0xdf5f, 0xd83c, 0xdf57, 0xd83c, +0xdf71, 0xd83c, 0xdf5a, 0xd83c, 0xdf5c, 0xd83c, 0xdf61, 0xd83c, 0xdf73, 0xd83c, 0xdf5e, 0xd83c, +0xdf69, 0xd83c, 0xdf66, 0xd83c, 0xdf82, 0xd83c, 0xdf70, 0xd83c, 0xdf6a, 0xd83c, 0xdf6b, 0xd83c, +0xdf6d, 0xd83c, 0xdf6f, 0xd83c, 0xdf4e, 0xd83c, 0xdf4f, 0xd83c, 0xdf4a, 0xd83c, 0xdf4b, 0xd83c, +0xdf52, 0xd83c, 0xdf47, 0xd83c, 0xdf49, 0xd83c, 0xdf53, 0xd83c, 0xdf51, 0xd83c, 0xdf4c, 0xd83c, +0xdf50, 0xd83c, 0xdf4d, 0xd83c, 0xdf46, 0xd83c, 0xdf45, 0xd83c, 0xdf3d, 0xd83c, 0xdfe1, 0xd83c, +0xdfe5, 0xd83c, 0xdfe6, 0x26ea, 0xd83c, 0xdff0, 0x26fa, 0xd83c, 0xdfed, 0xd83d, 0xddfb, 0xd83d, +0xddfd, 0xd83c, 0xdfa0, 0xd83c, 0xdfa1, 0x26f2, 0xd83c, 0xdfa2, 0xd83d, 0xdea2, 0xd83d, 0xdea4, +0x2693, 0xd83d, 0xde80, 0x2708, 0xd83d, 0xde81, 0xd83d, 0xde82, 0xd83d, 0xde8b, 0xd83d, 0xde8e, +0xd83d, 0xde8c, 0xd83d, 0xde99, 0xd83d, 0xde97, 0xd83d, 0xde95, 0xd83d, 0xde9b, 0xd83d, 0xdea8, +0xd83d, 0xde94, 0xd83d, 0xde92, 0xd83d, 0xde91, 0xd83d, 0xdeb2, 0xd83d, 0xdea0, 0xd83d, 0xde9c, +0xd83d, 0xdea6, 0x26a0, 0xd83d, 0xdea7, 0x26fd, 0xd83c, 0xdfb0, 0xd83d, 0xddff, 0xd83c, 0xdfaa, +0xd83c, 0xdfad, 0xd83c, 0xddef, 0xd83c, 0xddf5, 0xd83c, 0xddf0, 0xd83c, 0xddf7, 0xd83c, 0xdde9, +0xd83c, 0xddea, 0xd83c, 0xdde8, 0xd83c, 0xddf3, 0xd83c, 0xddfa, 0xd83c, 0xddf8, 0xd83c, 0xddeb, +0xd83c, 0xddf7, 0xd83c, 0xddea, 0xd83c, 0xddf8, 0xd83c, 0xddee, 0xd83c, 0xddf9, 0xd83c, 0xddf7, +0xd83c, 0xddfa, 0xd83c, 0xddec, 0xd83c, 0xdde7, 0x0031, 0x20e3, 0x0032, 0x20e3, 0x0033, 0x20e3, +0x0034, 0x20e3, 0x0035, 0x20e3, 0x0036, 0x20e3, 0x0037, 0x20e3, 0x0038, 0x20e3, 0x0039, 0x20e3, +0x0030, 0x20e3, 0xd83d, 0xdd1f, 0x2757, 0x2753, 0x2665, 0x2666, 0xd83d, 0xdcaf, 0xd83d, 0xdd17, +0xd83d, 0xdd31, 0xd83d, 0xdd34, 0xd83d, 0xdd35, 0xd83d, 0xdd36, 0xd83d, 0xdd37 }; + +ushort Offsets[] = { +0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, +24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, +48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, +72, 74, 76, 78, 80, 82, 84, 86, 87, 88, 90, 92, +94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, +118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, +142, 144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, +166, 168, 170, 172, 174, 176, 178, 180, 182, 184, 186, 188, +190, 192, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212, +214, 216, 218, 220, 222, 224, 226, 228, 230, 232, 234, 236, +238, 240, 242, 244, 246, 248, 250, 252, 254, 256, 258, 259, +260, 261, 262, 264, 266, 268, 270, 272, 274, 276, 278, 280, +282, 284, 286, 288, 290, 292, 294, 295, 297, 299, 301, 303, +305, 306, 307, 308, 310, 312, 314, 316, 318, 320, 322, 324, +326, 328, 330, 332, 334, 336, 338, 340, 342, 344, 346, 348, +350, 351, 353, 355, 357, 359, 360, 362, 364, 365, 366, 368, +370, 372, 374, 376, 378, 380, 382, 384, 386, 388, 390, 392, +394, 396, 398, 400, 402, 404, 406, 407, 408, 410, 412, 414, +416, 418, 420, 422, 424, 426, 427, 429, 431, 433, 435, 437, +439, 441, 443, 445, 447, 449, 451, 453, 455, 457, 459, 461, +463, 465, 467, 469, 471, 473, 475, 477, 479, 481, 483, 485, +487, 489, 491, 493, 495, 497, 499, 501, 503, 505, 507, 508, +510, 511, 513, 515, 517, 519, 521, 522, 524, 526, 528, 529, +531, 532, 534, 536, 538, 540, 542, 544, 546, 548, 550, 552, +554, 556, 558, 560, 562, 564, 566, 567, 569, 570, 572, 574, +576, 578, 582, 586, 590, 594, 598, 602, 606, 610, 614, 618, +620, 622, 624, 626, 628, 630, 632, 634, 636, 638, 640, 641, +642, 643, 644, 646, 648, 650, 652, 654, 656, 658 }; + +uint64 ComputeEmojiIndex(base::const_byte_span bytes) { + Expects(bytes.size() == 8); + return ((gsl::to_integer(bytes[0]) & 0x7F) << 56) + | (gsl::to_integer(bytes[1]) << 48) + | (gsl::to_integer(bytes[2]) << 40) + | (gsl::to_integer(bytes[3]) << 32) + | (gsl::to_integer(bytes[4]) << 24) + | (gsl::to_integer(bytes[5]) << 16) + | (gsl::to_integer(bytes[6]) << 8) + | (gsl::to_integer(bytes[7])); +} + +} // namespace + +std::vector ComputeEmojiFingerprint(gsl::not_null call) { + auto result = std::vector(); + constexpr auto EmojiCount = (base::array_size(Offsets) - 1); + for (auto index = 0; index != EmojiCount; ++index) { + auto offset = Offsets[index]; + auto size = Offsets[index + 1] - offset; + auto string = QString::fromRawData(reinterpret_cast(Data + offset), size); + auto emoji = Ui::Emoji::Find(string); + t_assert(emoji != nullptr); + } + if (call->isKeyShaForFingerprintReady()) { + auto sha256 = call->getKeyShaForFingerprint(); + constexpr auto kPartSize = 8; + for (auto partOffset = 0; partOffset != sha256.size(); partOffset += kPartSize) { + auto value = ComputeEmojiIndex(gsl::make_span(sha256).subspan(partOffset, kPartSize)); + auto index = value % EmojiCount; + auto offset = Offsets[index]; + auto size = Offsets[index + 1] - offset; + auto string = QString::fromRawData(reinterpret_cast(Data + offset), size); + auto emoji = Ui::Emoji::Find(string); + t_assert(emoji != nullptr); + result.push_back(emoji); + } + } + return result; + +} + +} // namespace Calls diff --git a/Telegram/SourceFiles/calls/calls_emoji_fingerprint.h b/Telegram/SourceFiles/calls/calls_emoji_fingerprint.h new file mode 100644 index 000000000..cb432a911 --- /dev/null +++ b/Telegram/SourceFiles/calls/calls_emoji_fingerprint.h @@ -0,0 +1,29 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org +*/ +#pragma once + +namespace Calls { + +class Call; + +std::vector ComputeEmojiFingerprint(gsl::not_null call); + +} // namespace Calls diff --git a/Telegram/SourceFiles/calls/calls_panel.cpp b/Telegram/SourceFiles/calls/calls_panel.cpp index 3d1cf7fa7..c4a1f8650 100644 --- a/Telegram/SourceFiles/calls/calls_panel.cpp +++ b/Telegram/SourceFiles/calls/calls_panel.cpp @@ -20,7 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org */ #include "calls/calls_panel.h" -#include "calls/calls_call.h" +#include "calls/calls_emoji_fingerprint.h" #include "styles/style_calls.h" #include "styles/style_history.h" #include "ui/widgets/buttons.h" @@ -28,8 +28,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "ui/effects/ripple_animation.h" #include "ui/widgets/shadow.h" #include "messenger.h" +#include "lang.h" #include "auth_session.h" #include "apiwrap.h" +#include "observer_peer.h" #include "platform/platform_specific.h" namespace Calls { @@ -95,25 +97,21 @@ Panel::Panel(gsl::not_null call) , _user(call->user()) , _hangup(this, st::callHangup) , _mute(this, st::callMuteToggle) -, _name(this) -, _status(this) { +, _name(this, st::callName) +, _status(this, st::callStatus) { initControls(); initLayout(); show(); } void Panel::initControls() { - subscribe(_call->stateChanged(), [this](Call::State state) { - if (state == Call::State::Failed || state == Call::State::Ended) { - callDestroyed(); - } - }); + subscribe(_call->stateChanged(), [this](State state) { stateChanged(state); }); _hangup->setClickedCallback([this] { if (_call) { _call->hangup(); } }); - if (_call->type() == Call::Type::Incoming) { + if (_call->type() == Type::Incoming) { _answer.create(this, st::callAnswer); _answer->setClickedCallback([this] { if (_call) { @@ -121,6 +119,26 @@ void Panel::initControls() { } }); } + _mute->setClickedCallback([this] { + _call->setMute(!_call->isMute()); + }); + subscribe(_call->muteChanged(), [this](bool mute) { + _mute->setIconOverride(mute ? &st::callUnmuteIcon : nullptr); + }); + _name->setText(App::peerName(_call->user())); + subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(Notify::PeerUpdate::Flag::NameChanged, [this](const Notify::PeerUpdate &update) { + if (!_call || update.peer != _call->user()) { + return; + } + _name->setText(App::peerName(_call->user())); + updateControlsGeometry(); + })); + updateStatusText(_call->state()); + _updateDurationTimer.setCallback([this] { + if (_call) { + updateStatusText(_call->state()); + } + }); } void Panel::initLayout() { @@ -214,6 +232,7 @@ void Panel::initGeometry() { auto rect = QRect(0, 0, st::callWidth, st::callHeight); setGeometry(rect.translated(center - rect.center()).marginsAdded(_padding)); createBottomImage(); + updateControlsGeometry(); } void Panel::createBottomImage() { @@ -268,6 +287,13 @@ void Panel::refreshCacheImageUserPhoto() { } void Panel::resizeEvent(QResizeEvent *e) { + updateControlsGeometry(); +} + +void Panel::updateControlsGeometry() { + _name->moveToLeft((width() - _name->width()) / 2, _contentTop + st::callNameTop); + updateStatusGeometry(); + auto controlsTop = _contentTop + st::callControlsTop; if (_answer) { auto bothWidth = _answer->width() + st::callControlsSkip + _hangup->width(); @@ -279,6 +305,10 @@ void Panel::resizeEvent(QResizeEvent *e) { _mute->moveToRight(_padding.right() + st::callMuteRight, controlsTop); } +void Panel::updateStatusGeometry() { + _status->moveToLeft((width() - _status->width()) / 2, _contentTop + st::callStatusTop); +} + void Panel::paintEvent(QPaintEvent *e) { Painter p(this); if (_useTransparency) { @@ -287,6 +317,21 @@ void Panel::paintEvent(QPaintEvent *e) { p.drawPixmapLeft(0, 0, width(), _userPhoto); p.fillRect(myrtlrect(0, st::callWidth, width(), height() - st::callWidth), st::callBg); } + + if (!_fingerprint.empty()) { + auto realSize = Ui::Emoji::Size(Ui::Emoji::Index() + 1); + auto size = realSize / cIntRetinaFactor(); + auto count = _fingerprint.size(); + auto rectWidth = count * size + (count - 1) * st::callFingerprintSkip; + auto rectHeight = size; + auto left = (width() - rectWidth) / 2; + auto top = _contentTop - st::callFingerprintBottom - st::callFingerprintPadding.bottom() - size; + App::roundRect(p, QRect(left, top, rectWidth, rectHeight).marginsAdded(st::callFingerprintPadding), st::callFingerprintBg, ImageRoundRadius::Small); + for (auto emoji : _fingerprint) { + p.drawPixmap(QPoint(left, top), App::emojiLarge(), QRect(emoji->x() * realSize, emoji->y() * realSize, realSize, realSize)); + left += st::callFingerprintSkip + size; + } + } } void Panel::mousePressEvent(QMouseEvent *e) { @@ -314,7 +359,54 @@ void Panel::mouseReleaseEvent(QMouseEvent *e) { } } -void Panel::callDestroyed() { +void Panel::stateChanged(State state) { + updateStatusText(state); + if (_answer + && state != State::WaitingIncoming + && state != State::WaitingInit + && state != State::WaitingInitAck) { + _answer.destroy(); + updateControlsGeometry(); + } + if (_fingerprint.empty() && _call && _call->isKeyShaForFingerprintReady()) { + _fingerprint = ComputeEmojiFingerprint(_call.get()); + update(); + } +} + +void Panel::updateStatusText(State state) { + auto statusText = [this, state]() -> QString { + switch (state) { + case State::WaitingInit: + case State::WaitingInitAck: return lang(lng_call_status_connecting); + case State::Established: { + if (_call) { + auto durationMs = _call->getDurationMs(); + auto durationSeconds = durationMs / 1000; + startDurationUpdateTimer(durationMs); + return formatDurationText(durationSeconds); + } + return lang(lng_call_status_ended); + } break; + case State::Failed: return lang(lng_call_status_failed); + case State::HangingUp: return lang(lng_call_status_hanging); + case State::Ended: return lang(lng_call_status_ended); + case State::ExchangingKeys: return lang(lng_call_status_exchanging); + case State::Waiting: return lang(lng_call_status_waiting); + case State::Requesting: return lang(lng_call_status_requesting); + case State::WaitingIncoming: return lang(lng_call_status_incoming); + case State::Ringing: return lang(lng_call_status_ringing); + case State::Busy: return lang(lng_call_status_busy); + } + Unexpected("State in stateChanged()"); + }; + _status->setText(statusText()); + updateStatusGeometry(); +} + +void Panel::startDurationUpdateTimer(TimeMs currentDuration) { + auto msTillNextSecond = 1000 - (currentDuration % 1000); + _updateDurationTimer.callOnce(msTillNextSecond + 5); } } // namespace Calls diff --git a/Telegram/SourceFiles/calls/calls_panel.h b/Telegram/SourceFiles/calls/calls_panel.h index bd1235ed0..3b5e5dd72 100644 --- a/Telegram/SourceFiles/calls/calls_panel.h +++ b/Telegram/SourceFiles/calls/calls_panel.h @@ -21,6 +21,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #pragma once #include "base/weak_unique_ptr.h" +#include "calls/calls_call.h" +#include "base/timer.h" namespace Ui { class IconButton; @@ -29,8 +31,6 @@ class FlatLabel; namespace Calls { -class Call; - class Panel : public TWidget, private base::Subscriber { public: Panel(gsl::not_null call); @@ -43,6 +43,9 @@ protected: void mouseMoveEvent(QMouseEvent *e) override; private: + using State = Call::State; + using Type = Call::Type; + void initControls(); void initLayout(); void initGeometry(); @@ -55,7 +58,11 @@ private: bool isGoodUserPhoto(PhotoData *photo); void createUserpicCache(ImagePtr image); - void callDestroyed(); + void updateControlsGeometry(); + void updateStatusGeometry(); + void stateChanged(State state); + void updateStatusText(State state); + void startDurationUpdateTimer(TimeMs currentDuration); base::weak_unique_ptr _call; gsl::not_null _user; @@ -77,11 +84,14 @@ private: object_ptr _status; std::vector _fingerprint; + base::Timer _updateDurationTimer; + QPixmap _userPhoto; PhotoId _userPhotoId = 0; bool _userPhotoFull = false; QPixmap _bottomCache; + QPixmap _cache; }; diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index f4cc4604a..70849ba34 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -1988,9 +1988,14 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { return lng_duration_seconds(lt_count, duration); })(); auto wasMissed = [&action] { - if (action.has_reason()) switch (action.vreason.type()) { - case mtpc_phoneCallDiscardReasonBusy: - case mtpc_phoneCallDiscardReasonMissed: return true; + if (action.has_reason()) { + return (action.vreason.type() == mtpc_phoneCallDiscardReasonMissed); + } + return false; + }; + auto wasBusy = [&action] { + if (action.has_reason()) { + return (action.vreason.type() == mtpc_phoneCallDiscardReasonBusy); } return false; }; @@ -2005,6 +2010,8 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { } else { if (wasMissed()) { result.text = lng_action_call_incoming_missed(lt_time, timeText); + } else if (wasBusy()) { + result.text = lng_action_call_incoming_declined(lt_time, timeText); } else if (duration) { result.text = lng_action_call_incoming_duration(lt_duration, durationText, lt_time, timeText); } else { diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index dc6e4082e..65e759b89 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -82,6 +82,8 @@ <(src_loc)/boxes/username_box.h <(src_loc)/calls/calls_call.cpp <(src_loc)/calls/calls_call.h +<(src_loc)/calls/calls_emoji_fingerprint.cpp +<(src_loc)/calls/calls_emoji_fingerprint.h <(src_loc)/calls/calls_instance.cpp <(src_loc)/calls/calls_instance.h <(src_loc)/calls/calls_panel.cpp