Update to 8.7.1
|
@ -300,7 +300,7 @@ android {
|
|||
}
|
||||
}
|
||||
|
||||
defaultConfig.versionCode = 2622
|
||||
defaultConfig.versionCode = 2629
|
||||
|
||||
applicationVariants.all { variant ->
|
||||
variant.outputs.all { output ->
|
||||
|
@ -319,7 +319,7 @@ android {
|
|||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 30
|
||||
versionName "8.7.0"
|
||||
versionName "8.7.1"
|
||||
|
||||
vectorDrawables.generatedDensities = ['mdpi', 'hdpi', 'xhdpi', 'xxhdpi']
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ constexpr auto kServiceCauseResend = 2;
|
|||
|
||||
static constexpr uint8_t kAckId = uint8_t(-1);
|
||||
static constexpr uint8_t kEmptyId = uint8_t(-2);
|
||||
static constexpr uint8_t kCustomId = uint8_t(127);
|
||||
|
||||
void AppendSeq(rtc::CopyOnWriteBuffer &buffer, uint32_t seq) {
|
||||
const auto bytes = rtc::HostToNetwork32(seq);
|
||||
|
@ -68,6 +69,22 @@ bool ConstTimeIsDifferent(const void *a, const void *b, size_t size) {
|
|||
return different;
|
||||
}
|
||||
|
||||
rtc::CopyOnWriteBuffer SerializeRawMessageWithSeq(
|
||||
const rtc::CopyOnWriteBuffer &message,
|
||||
uint32_t seq,
|
||||
bool singleMessagePacket) {
|
||||
rtc::ByteBufferWriter writer;
|
||||
writer.WriteUInt32(seq);
|
||||
writer.WriteUInt8(kCustomId);
|
||||
writer.WriteUInt32((uint32_t)message.size());
|
||||
writer.WriteBytes((const char *)message.data(), message.size());
|
||||
|
||||
auto result = rtc::CopyOnWriteBuffer();
|
||||
result.AppendData(writer.Data(), writer.Length());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
EncryptedConnection::EncryptedConnection(
|
||||
|
@ -152,6 +169,25 @@ auto EncryptedConnection::prepareForSending(const Message &message)
|
|||
}
|
||||
const auto seq = *maybeSeq;
|
||||
auto serialized = SerializeMessageWithSeq(message, seq, singleMessagePacket);
|
||||
|
||||
return prepareForSendingMessageInternal(serialized, seq, messageRequiresAck);
|
||||
}
|
||||
|
||||
absl::optional<EncryptedConnection::EncryptedPacket> EncryptedConnection::prepareForSendingRawMessage(rtc::CopyOnWriteBuffer &message, bool messageRequiresAck) {
|
||||
// If message requires ack, then we can't serialize it as a single
|
||||
// message packet, because later it may be sent as a part of big packet.
|
||||
const auto singleMessagePacket = !haveAdditionalMessages() && !messageRequiresAck;
|
||||
const auto maybeSeq = computeNextSeq(messageRequiresAck, singleMessagePacket);
|
||||
if (!maybeSeq) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
const auto seq = *maybeSeq;
|
||||
auto serialized = SerializeRawMessageWithSeq(message, seq, singleMessagePacket);
|
||||
|
||||
return prepareForSendingMessageInternal(serialized, seq, messageRequiresAck);
|
||||
}
|
||||
|
||||
absl::optional<EncryptedConnection::EncryptedPacket> EncryptedConnection::prepareForSendingMessageInternal(rtc::CopyOnWriteBuffer &serialized, uint32_t seq, bool messageRequiresAck) {
|
||||
if (!enoughSpaceInPacket(serialized, 0)) {
|
||||
return LogError("Too large packet: ", std::to_string(serialized.size()));
|
||||
}
|
||||
|
@ -404,6 +440,41 @@ auto EncryptedConnection::handleIncomingPacket(const char *bytes, size_t size)
|
|||
return processPacket(decryptionBuffer, incomingSeq);
|
||||
}
|
||||
|
||||
absl::optional<EncryptedConnection::DecryptedRawPacket> EncryptedConnection::handleIncomingRawPacket(const char *bytes, size_t size) {
|
||||
if (size < 21 || size > kMaxIncomingPacketSize) {
|
||||
return LogError("Bad incoming packet size: ", std::to_string(size));
|
||||
}
|
||||
|
||||
const auto x = (_key.isOutgoing ? 8 : 0) + (_type == Type::Signaling ? 128 : 0);
|
||||
const auto key = _key.value->data();
|
||||
const auto msgKey = reinterpret_cast<const uint8_t*>(bytes);
|
||||
const auto encryptedData = msgKey + 16;
|
||||
const auto dataSize = size - 16;
|
||||
|
||||
auto aesKeyIv = PrepareAesKeyIv(key, msgKey, x);
|
||||
|
||||
auto decryptionBuffer = rtc::Buffer(dataSize);
|
||||
AesProcessCtr(
|
||||
MemorySpan{ encryptedData, dataSize },
|
||||
decryptionBuffer.data(),
|
||||
std::move(aesKeyIv));
|
||||
|
||||
const auto msgKeyLarge = ConcatSHA256(
|
||||
MemorySpan{ key + 88 + x, 32 },
|
||||
MemorySpan{ decryptionBuffer.data(), decryptionBuffer.size() });
|
||||
if (ConstTimeIsDifferent(msgKeyLarge.data() + 8, msgKey, 16)) {
|
||||
return LogError("Bad incoming data hash.");
|
||||
}
|
||||
|
||||
const auto incomingSeq = ReadSeq(decryptionBuffer.data());
|
||||
const auto incomingCounter = CounterFromSeq(incomingSeq);
|
||||
if (!registerIncomingCounter(incomingCounter)) {
|
||||
// We've received that packet already.
|
||||
return LogError("Already handled packet received.", std::to_string(incomingCounter));
|
||||
}
|
||||
return processRawPacket(decryptionBuffer, incomingSeq);
|
||||
}
|
||||
|
||||
auto EncryptedConnection::processPacket(
|
||||
const rtc::Buffer &fullBuffer,
|
||||
uint32_t packetSeq)
|
||||
|
@ -490,6 +561,98 @@ auto EncryptedConnection::processPacket(
|
|||
return result;
|
||||
}
|
||||
|
||||
auto EncryptedConnection::processRawPacket(
|
||||
const rtc::Buffer &fullBuffer,
|
||||
uint32_t packetSeq)
|
||||
-> absl::optional<DecryptedRawPacket> {
|
||||
assert(fullBuffer.size() >= 5);
|
||||
|
||||
auto additionalMessage = false;
|
||||
auto firstMessageRequiringAck = true;
|
||||
auto newRequiringAckReceived = false;
|
||||
|
||||
auto currentSeq = packetSeq;
|
||||
auto currentCounter = CounterFromSeq(currentSeq);
|
||||
rtc::ByteBufferReader reader(
|
||||
reinterpret_cast<const char*>(fullBuffer.data() + 4), // Skip seq.
|
||||
fullBuffer.size() - 4);
|
||||
|
||||
auto result = absl::optional<DecryptedRawPacket>();
|
||||
while (true) {
|
||||
const auto type = uint8_t(*reader.Data());
|
||||
const auto singleMessagePacket = ((currentSeq & kSingleMessagePacketSeqBit) != 0);
|
||||
if (singleMessagePacket && additionalMessage) {
|
||||
return LogError("Single message packet bit in not first message.");
|
||||
}
|
||||
|
||||
if (type == kEmptyId) {
|
||||
if (additionalMessage) {
|
||||
return LogError("Empty message should be only the first one in the packet.");
|
||||
}
|
||||
RTC_LOG(LS_INFO) << logHeader()
|
||||
<< "Got RECV:empty" << "#" << currentCounter;
|
||||
reader.Consume(1);
|
||||
} else if (type == kAckId) {
|
||||
if (!additionalMessage) {
|
||||
return LogError("Ack message must not be the first one in the packet.");
|
||||
}
|
||||
ackMyMessage(currentSeq);
|
||||
reader.Consume(1);
|
||||
} else if (type == kCustomId) {
|
||||
reader.Consume(1);
|
||||
|
||||
if (auto message = DeserializeRawMessage(reader, singleMessagePacket)) {
|
||||
const auto messageRequiresAck = ((currentSeq & kMessageRequiresAckSeqBit) != 0);
|
||||
const auto skipMessage = messageRequiresAck
|
||||
? !registerSentAck(currentCounter, firstMessageRequiringAck)
|
||||
: (additionalMessage && !registerIncomingCounter(currentCounter));
|
||||
if (messageRequiresAck) {
|
||||
firstMessageRequiringAck = false;
|
||||
if (!skipMessage) {
|
||||
newRequiringAckReceived = true;
|
||||
}
|
||||
sendAckPostponed(currentSeq);
|
||||
RTC_LOG(LS_INFO) << logHeader()
|
||||
<< (skipMessage ? "Repeated RECV:type" : "Got RECV:type") << type << "#" << currentCounter;
|
||||
}
|
||||
if (!skipMessage) {
|
||||
appendReceivedRawMessage(result, std::move(*message), currentSeq);
|
||||
}
|
||||
} else {
|
||||
return LogError("Could not parse message from packet, type: ", std::to_string(type));
|
||||
}
|
||||
} else {
|
||||
return LogError("Could not parse message from packet, type: ", std::to_string(type));
|
||||
}
|
||||
if (!reader.Length()) {
|
||||
break;
|
||||
} else if (singleMessagePacket) {
|
||||
return LogError("Single message didn't fill the entire packet.");
|
||||
} else if (reader.Length() < 5) {
|
||||
return LogError("Bad remaining data size: ", std::to_string(reader.Length()));
|
||||
}
|
||||
const auto success = reader.ReadUInt32(¤tSeq);
|
||||
assert(success);
|
||||
(void)success;
|
||||
currentCounter = CounterFromSeq(currentSeq);
|
||||
|
||||
additionalMessage = true;
|
||||
}
|
||||
|
||||
if (!_acksToSendSeqs.empty()) {
|
||||
if (newRequiringAckReceived) {
|
||||
_requestSendService(0, 0);
|
||||
} else if (!_sendAcksTimerActive) {
|
||||
_sendAcksTimerActive = true;
|
||||
_requestSendService(
|
||||
_delayIntervals.maxDelayBeforeAckResend,
|
||||
kServiceCauseAcks);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void EncryptedConnection::appendReceivedMessage(
|
||||
absl::optional<DecryptedPacket> &to,
|
||||
Message &&message,
|
||||
|
@ -505,6 +668,21 @@ void EncryptedConnection::appendReceivedMessage(
|
|||
}
|
||||
}
|
||||
|
||||
void EncryptedConnection::appendReceivedRawMessage(
|
||||
absl::optional<DecryptedRawPacket> &to,
|
||||
rtc::CopyOnWriteBuffer &&message,
|
||||
uint32_t incomingSeq) {
|
||||
auto decrypted = DecryptedRawMessage{
|
||||
std::move(message),
|
||||
CounterFromSeq(incomingSeq)
|
||||
};
|
||||
if (to) {
|
||||
to->additional.push_back(std::move(decrypted));
|
||||
} else {
|
||||
to = DecryptedRawPacket{ std::move(decrypted) };
|
||||
}
|
||||
}
|
||||
|
||||
const char *EncryptedConnection::logHeader() const {
|
||||
return (_type == Type::Signaling) ? "(signaling) " : "(transport) ";
|
||||
}
|
||||
|
|
|
@ -26,13 +26,19 @@ public:
|
|||
uint32_t counter = 0;
|
||||
};
|
||||
absl::optional<EncryptedPacket> prepareForSending(const Message &message);
|
||||
absl::optional<EncryptedPacket> prepareForSendingRawMessage(rtc::CopyOnWriteBuffer &serialized, bool messageRequiresAck);
|
||||
absl::optional<EncryptedPacket> prepareForSendingService(int cause);
|
||||
|
||||
struct DecryptedPacket {
|
||||
DecryptedMessage main;
|
||||
std::vector<DecryptedMessage> additional;
|
||||
};
|
||||
struct DecryptedRawPacket {
|
||||
DecryptedRawMessage main;
|
||||
std::vector<DecryptedRawMessage> additional;
|
||||
};
|
||||
absl::optional<DecryptedPacket> handleIncomingPacket(const char *bytes, size_t size);
|
||||
absl::optional<DecryptedRawPacket> handleIncomingRawPacket(const char *bytes, size_t size);
|
||||
|
||||
absl::optional<rtc::CopyOnWriteBuffer> encryptRawPacket(rtc::CopyOnWriteBuffer const &buffer);
|
||||
absl::optional<rtc::CopyOnWriteBuffer> decryptRawPacket(rtc::CopyOnWriteBuffer const &buffer);
|
||||
|
@ -57,6 +63,7 @@ private:
|
|||
EncryptedPacket encryptPrepared(const rtc::CopyOnWriteBuffer &buffer);
|
||||
bool registerIncomingCounter(uint32_t incomingCounter);
|
||||
absl::optional<DecryptedPacket> processPacket(const rtc::Buffer &fullBuffer, uint32_t packetSeq);
|
||||
absl::optional<DecryptedRawPacket> processRawPacket(const rtc::Buffer &fullBuffer, uint32_t packetSeq);
|
||||
bool registerSentAck(uint32_t counter, bool firstInPacket);
|
||||
void ackMyMessage(uint32_t counter);
|
||||
void sendAckPostponed(uint32_t incomingSeq);
|
||||
|
@ -66,6 +73,11 @@ private:
|
|||
absl::optional<DecryptedPacket> &to,
|
||||
Message &&message,
|
||||
uint32_t incomingSeq);
|
||||
void appendReceivedRawMessage(
|
||||
absl::optional<DecryptedRawPacket> &to,
|
||||
rtc::CopyOnWriteBuffer &&message,
|
||||
uint32_t incomingSeq);
|
||||
absl::optional<EncryptedPacket> prepareForSendingMessageInternal(rtc::CopyOnWriteBuffer &serialized, uint32_t seq, bool messageRequiresAck);
|
||||
|
||||
const char *logHeader() const;
|
||||
|
||||
|
|
|
@ -215,6 +215,7 @@ template <typename Implementation>
|
|||
bool Register();
|
||||
|
||||
struct Descriptor {
|
||||
std::string version;
|
||||
Config config;
|
||||
PersistentState persistentState;
|
||||
std::vector<Endpoint> endpoints;
|
||||
|
|
|
@ -378,4 +378,29 @@ absl::optional<Message> DeserializeMessage(
|
|||
: absl::nullopt;
|
||||
}
|
||||
|
||||
absl::optional<rtc::CopyOnWriteBuffer> DeserializeRawMessage(
|
||||
rtc::ByteBufferReader &reader,
|
||||
bool singleMessagePacket) {
|
||||
if (!reader.Length()) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
uint32_t length = 0;
|
||||
if (!reader.ReadUInt32(&length)) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
if (length < 0 || length > 1024 * 1024) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
rtc::CopyOnWriteBuffer result;
|
||||
result.SetSize(length);
|
||||
if (!reader.ReadBytes((char *)result.MutableData(), result.size())) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace tgcalls
|
||||
|
|
|
@ -18,13 +18,14 @@ enum class AudioState;
|
|||
struct PeerIceParameters {
|
||||
std::string ufrag;
|
||||
std::string pwd;
|
||||
bool supportsRenomination = false;
|
||||
|
||||
PeerIceParameters() = default;
|
||||
PeerIceParameters(const PeerIceParameters &other) = default;
|
||||
|
||||
PeerIceParameters(std::string ufrag_, std::string pwd_) :
|
||||
PeerIceParameters(std::string ufrag_, std::string pwd_, bool supportsRenomination_) :
|
||||
ufrag(ufrag_),
|
||||
pwd(pwd_) {
|
||||
pwd(pwd_),
|
||||
supportsRenomination(supportsRenomination_) {
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -127,12 +128,21 @@ rtc::CopyOnWriteBuffer SerializeMessageWithSeq(
|
|||
absl::optional<Message> DeserializeMessage(
|
||||
rtc::ByteBufferReader &reader,
|
||||
bool singleMessagePacket);
|
||||
absl::optional<rtc::CopyOnWriteBuffer> DeserializeRawMessage(
|
||||
rtc::ByteBufferReader &reader,
|
||||
bool singleMessagePacket);
|
||||
|
||||
struct DecryptedMessage {
|
||||
Message message;
|
||||
uint32_t counter = 0;
|
||||
};
|
||||
|
||||
struct DecryptedRawMessage {
|
||||
rtc::CopyOnWriteBuffer message;
|
||||
uint32_t counter = 0;
|
||||
};
|
||||
|
||||
|
||||
} // namespace tgcalls
|
||||
|
||||
#endif
|
||||
|
|
|
@ -88,7 +88,7 @@ _isOutgoing(encryptionKey.isOutgoing),
|
|||
_stateUpdated(std::move(stateUpdated)),
|
||||
_transportMessageReceived(std::move(transportMessageReceived)),
|
||||
_sendSignalingMessage(std::move(sendSignalingMessage)),
|
||||
_localIceParameters(rtc::CreateRandomString(cricket::ICE_UFRAG_LENGTH), rtc::CreateRandomString(cricket::ICE_PWD_LENGTH)) {
|
||||
_localIceParameters(rtc::CreateRandomString(cricket::ICE_UFRAG_LENGTH), rtc::CreateRandomString(cricket::ICE_PWD_LENGTH), false) {
|
||||
assert(_thread->IsCurrent());
|
||||
|
||||
_networkMonitorFactory = PlatformInterface::SharedInstance()->createNetworkMonitorFactory();
|
||||
|
@ -207,7 +207,7 @@ void NetworkManager::receiveSignalingMessage(DecryptedMessage &&message) {
|
|||
assert(list != nullptr);
|
||||
|
||||
if (!_remoteIceParameters.has_value()) {
|
||||
PeerIceParameters parameters(list->iceParameters.ufrag, list->iceParameters.pwd);
|
||||
PeerIceParameters parameters(list->iceParameters.ufrag, list->iceParameters.pwd, false);
|
||||
_remoteIceParameters = parameters;
|
||||
|
||||
cricket::IceParameters remoteIceParameters(
|
||||
|
|
|
@ -61,16 +61,20 @@ public:
|
|||
explicit ThreadsImpl(size_t i) {
|
||||
auto suffix = i == 0 ? "" : "#" + std::to_string(i);
|
||||
media_ = create("tgc-media" + suffix);
|
||||
//worker_ = create("tgc-work" + suffix);
|
||||
worker_ = create_network("tgc-work" + suffix);
|
||||
//network_ = create_network("tgc-net" + suffix);
|
||||
worker_ = create("tgc-work" + suffix);
|
||||
network_ = create_network("tgc-net" + suffix);
|
||||
|
||||
media_->AllowInvokesToThread(worker_.get());
|
||||
media_->AllowInvokesToThread(network_.get());
|
||||
worker_->AllowInvokesToThread(network_.get());
|
||||
|
||||
//network_->DisallowAllInvokes();
|
||||
//worker_->DisallowAllInvokes();
|
||||
//worker_->AllowInvokesToThread(network_.get());
|
||||
}
|
||||
|
||||
rtc::Thread *getNetworkThread() override {
|
||||
return worker_.get();
|
||||
return network_.get();
|
||||
}
|
||||
rtc::Thread *getMediaThread() override {
|
||||
return media_.get();
|
||||
|
@ -90,7 +94,7 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
//Thread network_;
|
||||
Thread network_;
|
||||
Thread media_;
|
||||
Thread worker_;
|
||||
rtc::scoped_refptr<webrtc::SharedModuleThread> shared_module_thread_;
|
||||
|
|
|
@ -318,7 +318,7 @@ _dataChannelMessageReceived(dataChannelMessageReceived),
|
|||
_audioActivityUpdated(audioActivityUpdated) {
|
||||
assert(_threads->getNetworkThread()->IsCurrent());
|
||||
|
||||
_localIceParameters = PeerIceParameters(rtc::CreateRandomString(cricket::ICE_UFRAG_LENGTH), rtc::CreateRandomString(cricket::ICE_PWD_LENGTH));
|
||||
_localIceParameters = PeerIceParameters(rtc::CreateRandomString(cricket::ICE_UFRAG_LENGTH), rtc::CreateRandomString(cricket::ICE_PWD_LENGTH), false);
|
||||
|
||||
_localCertificate = rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(rtc::KT_ECDSA), absl::nullopt);
|
||||
|
||||
|
@ -452,7 +452,7 @@ void GroupNetworkManager::stop() {
|
|||
_transportChannel.reset();
|
||||
_portAllocator.reset();
|
||||
|
||||
_localIceParameters = PeerIceParameters(rtc::CreateRandomString(cricket::ICE_UFRAG_LENGTH), rtc::CreateRandomString(cricket::ICE_PWD_LENGTH));
|
||||
_localIceParameters = PeerIceParameters(rtc::CreateRandomString(cricket::ICE_UFRAG_LENGTH), rtc::CreateRandomString(cricket::ICE_PWD_LENGTH), false);
|
||||
|
||||
_localCertificate = rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(rtc::KT_ECDSA), absl::nullopt);
|
||||
|
||||
|
|
|
@ -57,6 +57,23 @@
|
|||
namespace tgcalls {
|
||||
namespace {
|
||||
|
||||
enum class SignalingProtocolVersion {
|
||||
V1,
|
||||
V2
|
||||
};
|
||||
|
||||
SignalingProtocolVersion signalingProtocolVersion(std::string const &version) {
|
||||
if (version == "4.0.1") {
|
||||
return SignalingProtocolVersion::V1;
|
||||
} else if (version == "4.0.2") {
|
||||
return SignalingProtocolVersion::V2;
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "signalingProtocolVersion: unknown version " << version;
|
||||
|
||||
return SignalingProtocolVersion::V2;
|
||||
}
|
||||
}
|
||||
|
||||
static VideoCaptureInterfaceObject *GetVideoCaptureAssumingSameThread(VideoCaptureInterface *videoCapture) {
|
||||
return videoCapture
|
||||
? static_cast<VideoCaptureInterfaceImpl*>(videoCapture)->object()->getSyncAssumingSameThread()
|
||||
|
@ -777,6 +794,7 @@ struct NetworkBitrateLogRecord {
|
|||
class InstanceV2ImplInternal : public std::enable_shared_from_this<InstanceV2ImplInternal> {
|
||||
public:
|
||||
InstanceV2ImplInternal(Descriptor &&descriptor, std::shared_ptr<Threads> threads) :
|
||||
_signalingProtocolVersion(signalingProtocolVersion(descriptor.version)),
|
||||
_threads(threads),
|
||||
_rtcServers(descriptor.rtcServers),
|
||||
_proxy(std::move(descriptor.proxy)),
|
||||
|
@ -927,7 +945,7 @@ public:
|
|||
_threads->getNetworkThread()
|
||||
);
|
||||
|
||||
webrtc::Call::Config callConfig(_eventLog.get());
|
||||
webrtc::Call::Config callConfig(_eventLog.get(), _threads->getNetworkThread());
|
||||
callConfig.task_queue_factory = _taskQueueFactory.get();
|
||||
callConfig.trials = &_fieldTrials;
|
||||
callConfig.audio_state = _channelManager->media_engine()->voice().GetAudioState();
|
||||
|
@ -1033,28 +1051,87 @@ public:
|
|||
|
||||
void sendSignalingMessage(signaling::Message const &message) {
|
||||
auto data = message.serialize();
|
||||
sendRawSignalingMessage(data);
|
||||
}
|
||||
|
||||
void sendRawSignalingMessage(std::vector<uint8_t> const &data) {
|
||||
RTC_LOG(LS_INFO) << "sendSignalingMessage: " << std::string(data.begin(), data.end());
|
||||
|
||||
if (_signalingEncryption) {
|
||||
if (const auto encryptedData = _signalingEncryption->encryptOutgoing(data)) {
|
||||
_signalingDataEmitted(std::vector<uint8_t>(encryptedData->data(), encryptedData->data() + encryptedData->size()));
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "sendSignalingMessage: failed to encrypt payload";
|
||||
if (_signalingEncryptedConnection) {
|
||||
switch (_signalingProtocolVersion) {
|
||||
case SignalingProtocolVersion::V1: {
|
||||
if (const auto message = _signalingEncryptedConnection->encryptRawPacket(rtc::CopyOnWriteBuffer(data.data(), data.size()))) {
|
||||
_signalingDataEmitted(std::vector<uint8_t>(message.value().data(), message.value().data() + message.value().size()));
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "Could not encrypt signaling message";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SignalingProtocolVersion::V2: {
|
||||
rtc::CopyOnWriteBuffer message;
|
||||
message.AppendData(data.data(), data.size());
|
||||
|
||||
commitSendSignalingMessage(_signalingEncryptedConnection->prepareForSendingRawMessage(message, true));
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_signalingDataEmitted(data);
|
||||
RTC_LOG(LS_ERROR) << "sendSignalingMessage encryption not available";
|
||||
}
|
||||
}
|
||||
|
||||
void commitSendSignalingMessage(absl::optional<EncryptedConnection::EncryptedPacket> packet) {
|
||||
if (!packet) {
|
||||
return;
|
||||
}
|
||||
|
||||
_signalingDataEmitted(packet.value().bytes);
|
||||
}
|
||||
|
||||
void beginSignaling() {
|
||||
_signalingEncryption.reset(new SignalingEncryption(_encryptionKey));
|
||||
const auto weak = std::weak_ptr<InstanceV2ImplInternal>(shared_from_this());
|
||||
|
||||
_signalingEncryptedConnection = std::make_unique<EncryptedConnection>(
|
||||
EncryptedConnection::Type::Signaling,
|
||||
_encryptionKey,
|
||||
[weak, threads = _threads](int delayMs, int cause) {
|
||||
if (delayMs == 0) {
|
||||
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [weak, cause]() {
|
||||
const auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
}
|
||||
|
||||
strong->sendPendingSignalingServiceData(cause);
|
||||
});
|
||||
} else {
|
||||
threads->getMediaThread()->PostDelayedTask(RTC_FROM_HERE, [weak, cause]() {
|
||||
const auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
}
|
||||
|
||||
strong->sendPendingSignalingServiceData(cause);
|
||||
}, delayMs);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (_encryptionKey.isOutgoing) {
|
||||
sendInitialSetup();
|
||||
}
|
||||
}
|
||||
|
||||
void sendPendingSignalingServiceData(int cause) {
|
||||
commitSendSignalingMessage(_signalingEncryptedConnection->prepareForSendingService(cause));
|
||||
}
|
||||
|
||||
void createNegotiatedChannels() {
|
||||
const auto coordinatedState = _contentNegotiationContext->coordinatedState();
|
||||
if (!coordinatedState) {
|
||||
|
@ -1295,8 +1372,9 @@ public:
|
|||
auto localIceParams = networking->getLocalIceParameters();
|
||||
std::string ufrag = localIceParams.ufrag;
|
||||
std::string pwd = localIceParams.pwd;
|
||||
bool supportsRenomination = localIceParams.supportsRenomination;
|
||||
|
||||
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [weak, ufrag, pwd, hash, fingerprint, setup, localIceParams]() {
|
||||
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [weak, ufrag, pwd, supportsRenomination, hash, fingerprint, setup, localIceParams]() {
|
||||
const auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
|
@ -1306,6 +1384,7 @@ public:
|
|||
|
||||
data.ufrag = ufrag;
|
||||
data.pwd = pwd;
|
||||
data.supportsRenomination = supportsRenomination;
|
||||
|
||||
signaling::DtlsFingerprint dtlsFingerprint;
|
||||
dtlsFingerprint.hash = hash;
|
||||
|
@ -1335,21 +1414,41 @@ public:
|
|||
}
|
||||
|
||||
void receiveSignalingData(const std::vector<uint8_t> &data) {
|
||||
std::vector<uint8_t> decryptedData;
|
||||
if (_signalingEncryptedConnection) {
|
||||
switch (_signalingProtocolVersion) {
|
||||
case SignalingProtocolVersion::V1: {
|
||||
if (const auto message = _signalingEncryptedConnection->decryptRawPacket(rtc::CopyOnWriteBuffer(data.data(), data.size()))) {
|
||||
processSignalingMessage(message.value());
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "receiveSignalingData could not decrypt signaling data";
|
||||
}
|
||||
|
||||
if (_signalingEncryption) {
|
||||
const auto rawDecryptedData = _signalingEncryption->decryptIncoming(data);
|
||||
if (!rawDecryptedData) {
|
||||
RTC_LOG(LS_ERROR) << "receiveSignalingData: could not decrypt payload";
|
||||
break;
|
||||
}
|
||||
case SignalingProtocolVersion::V2: {
|
||||
if (const auto packet = _signalingEncryptedConnection->handleIncomingRawPacket((const char *)data.data(), data.size())) {
|
||||
processSignalingMessage(packet.value().main.message);
|
||||
|
||||
return;
|
||||
for (const auto &additional : packet.value().additional) {
|
||||
processSignalingMessage(additional.message);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
decryptedData = std::vector<uint8_t>(rawDecryptedData->data(), rawDecryptedData->data() + rawDecryptedData->size());
|
||||
} else {
|
||||
decryptedData = data;
|
||||
RTC_LOG(LS_ERROR) << "receiveSignalingData encryption not available";
|
||||
}
|
||||
}
|
||||
|
||||
void processSignalingMessage(rtc::CopyOnWriteBuffer const &data) {
|
||||
std::vector<uint8_t> decryptedData = std::vector<uint8_t>(data.data(), data.data() + data.size());
|
||||
processSignalingData(decryptedData);
|
||||
}
|
||||
|
||||
|
@ -1365,6 +1464,7 @@ public:
|
|||
PeerIceParameters remoteIceParameters;
|
||||
remoteIceParameters.ufrag = initialSetup->ufrag;
|
||||
remoteIceParameters.pwd = initialSetup->pwd;
|
||||
remoteIceParameters.supportsRenomination = initialSetup->supportsRenomination;
|
||||
|
||||
std::unique_ptr<rtc::SSLFingerprint> fingerprint;
|
||||
std::string sslSetup;
|
||||
|
@ -1828,6 +1928,7 @@ private:
|
|||
}
|
||||
|
||||
private:
|
||||
SignalingProtocolVersion _signalingProtocolVersion;
|
||||
std::shared_ptr<Threads> _threads;
|
||||
std::vector<RtcServer> _rtcServers;
|
||||
std::unique_ptr<Proxy> _proxy;
|
||||
|
@ -1843,7 +1944,7 @@ private:
|
|||
std::function<rtc::scoped_refptr<webrtc::AudioDeviceModule>(webrtc::TaskQueueFactory*)> _createAudioDeviceModule;
|
||||
FilePath _statsLogPath;
|
||||
|
||||
std::unique_ptr<SignalingEncryption> _signalingEncryption;
|
||||
std::unique_ptr<EncryptedConnection> _signalingEncryptedConnection;
|
||||
|
||||
int64_t _startTimestamp = 0;
|
||||
|
||||
|
@ -1993,6 +2094,7 @@ void InstanceV2Impl::setEchoCancellationStrength(int strength) {
|
|||
std::vector<std::string> InstanceV2Impl::GetVersions() {
|
||||
std::vector<std::string> result;
|
||||
result.push_back("4.0.1");
|
||||
result.push_back("4.0.2");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,21 +20,12 @@
|
|||
#include "SctpDataChannelProviderInterfaceImpl.h"
|
||||
#include "StaticThreads.h"
|
||||
#include "platform/PlatformInterface.h"
|
||||
#include "p2p/base/turn_port.h"
|
||||
|
||||
namespace tgcalls {
|
||||
|
||||
namespace {
|
||||
|
||||
NativeNetworkingImpl::ConnectionDescription::CandidateDescription connectionDescriptionFromCandidate(cricket::Candidate const &candidate) {
|
||||
NativeNetworkingImpl::ConnectionDescription::CandidateDescription result;
|
||||
|
||||
result.type = candidate.type();
|
||||
result.protocol = candidate.protocol();
|
||||
result.address = candidate.address().ToString();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
class CryptStringImpl : public rtc::CryptStringImpl {
|
||||
public:
|
||||
CryptStringImpl(std::string const &value) :
|
||||
|
@ -192,6 +183,16 @@ private:
|
|||
|
||||
}
|
||||
|
||||
NativeNetworkingImpl::ConnectionDescription::CandidateDescription NativeNetworkingImpl::connectionDescriptionFromCandidate(cricket::Candidate const &candidate) {
|
||||
NativeNetworkingImpl::ConnectionDescription::CandidateDescription result;
|
||||
|
||||
result.type = candidate.type();
|
||||
result.protocol = candidate.protocol();
|
||||
result.address = candidate.address().ToString();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
webrtc::CryptoOptions NativeNetworkingImpl::getDefaulCryptoOptions() {
|
||||
auto options = webrtc::CryptoOptions();
|
||||
options.srtp.enable_aes128_sha1_80_crypto_cipher = true;
|
||||
|
@ -215,7 +216,7 @@ _dataChannelStateUpdated(configuration.dataChannelStateUpdated),
|
|||
_dataChannelMessageReceived(configuration.dataChannelMessageReceived) {
|
||||
assert(_threads->getNetworkThread()->IsCurrent());
|
||||
|
||||
_localIceParameters = PeerIceParameters(rtc::CreateRandomString(cricket::ICE_UFRAG_LENGTH), rtc::CreateRandomString(cricket::ICE_PWD_LENGTH));
|
||||
_localIceParameters = PeerIceParameters(rtc::CreateRandomString(cricket::ICE_UFRAG_LENGTH), rtc::CreateRandomString(cricket::ICE_PWD_LENGTH), true);
|
||||
|
||||
_localCertificate = rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(rtc::KT_ECDSA), absl::nullopt);
|
||||
|
||||
|
@ -310,7 +311,7 @@ void NativeNetworkingImpl::resetDtlsSrtpTransport() {
|
|||
}
|
||||
}
|
||||
|
||||
_portAllocator->SetConfiguration(stunServers, turnServers, 2, webrtc::NO_PRUNE, _turnCustomizer.get());
|
||||
_portAllocator->SetConfiguration(stunServers, turnServers, 0, webrtc::NO_PRUNE, _turnCustomizer.get());
|
||||
|
||||
|
||||
_transportChannel = cricket::P2PTransportChannel::Create("transport", 0, _portAllocator.get(), _asyncResolverFactory.get());
|
||||
|
@ -324,7 +325,7 @@ void NativeNetworkingImpl::resetDtlsSrtpTransport() {
|
|||
cricket::IceParameters localIceParameters(
|
||||
_localIceParameters.ufrag,
|
||||
_localIceParameters.pwd,
|
||||
false
|
||||
_localIceParameters.supportsRenomination
|
||||
);
|
||||
|
||||
_transportChannel->SetIceParameters(localIceParameters);
|
||||
|
@ -334,7 +335,6 @@ void NativeNetworkingImpl::resetDtlsSrtpTransport() {
|
|||
_transportChannel->SignalCandidateGathered.connect(this, &NativeNetworkingImpl::candidateGathered);
|
||||
_transportChannel->SignalIceTransportStateChanged.connect(this, &NativeNetworkingImpl::transportStateChanged);
|
||||
_transportChannel->SignalCandidatePairChanged.connect(this, &NativeNetworkingImpl::candidatePairChanged);
|
||||
_transportChannel->SignalReadPacket.connect(this, &NativeNetworkingImpl::transportPacketReceived);
|
||||
_transportChannel->SignalNetworkRouteChanged.connect(this, &NativeNetworkingImpl::transportRouteChanged);
|
||||
|
||||
webrtc::CryptoOptions cryptoOptions = NativeNetworkingImpl::getDefaulCryptoOptions();
|
||||
|
@ -384,7 +384,7 @@ void NativeNetworkingImpl::start() {
|
|||
_threads
|
||||
));
|
||||
|
||||
_lastNetworkActivityMs = rtc::TimeMillis();
|
||||
_lastDisconnectedTimestamp = rtc::TimeMillis();
|
||||
checkConnectionTimeout();
|
||||
}
|
||||
|
||||
|
@ -404,7 +404,7 @@ void NativeNetworkingImpl::stop() {
|
|||
_transportChannel.reset();
|
||||
_portAllocator.reset();
|
||||
|
||||
_localIceParameters = PeerIceParameters(rtc::CreateRandomString(cricket::ICE_UFRAG_LENGTH), rtc::CreateRandomString(cricket::ICE_PWD_LENGTH));
|
||||
_localIceParameters = PeerIceParameters(rtc::CreateRandomString(cricket::ICE_UFRAG_LENGTH), rtc::CreateRandomString(cricket::ICE_PWD_LENGTH), true);
|
||||
|
||||
_localCertificate = rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(rtc::KT_ECDSA), absl::nullopt);
|
||||
|
||||
|
@ -429,7 +429,7 @@ void NativeNetworkingImpl::setRemoteParams(PeerIceParameters const &remoteIcePar
|
|||
cricket::IceParameters parameters(
|
||||
remoteIceParameters.ufrag,
|
||||
remoteIceParameters.pwd,
|
||||
false
|
||||
remoteIceParameters.supportsRenomination
|
||||
);
|
||||
|
||||
_transportChannel->SetRemoteIceParameters(parameters);
|
||||
|
@ -474,7 +474,9 @@ void NativeNetworkingImpl::checkConnectionTimeout() {
|
|||
int64_t currentTimestamp = rtc::TimeMillis();
|
||||
const int64_t maxTimeout = 20000;
|
||||
|
||||
if (strong->_lastNetworkActivityMs + maxTimeout < currentTimestamp) {
|
||||
if (!strong->_isConnected && strong->_lastDisconnectedTimestamp + maxTimeout < currentTimestamp) {
|
||||
RTC_LOG(LS_INFO) << "NativeNetworkingImpl timeout " << (currentTimestamp - strong->_lastDisconnectedTimestamp) << " ms";
|
||||
|
||||
strong->_isFailed = true;
|
||||
strong->notifyStateUpdated();
|
||||
}
|
||||
|
@ -527,13 +529,6 @@ void NativeNetworkingImpl::transportReadyToSend(cricket::IceTransportInternal *t
|
|||
assert(_threads->getNetworkThread()->IsCurrent());
|
||||
}
|
||||
|
||||
void NativeNetworkingImpl::transportPacketReceived(rtc::PacketTransportInternal *transport, const char *bytes, size_t size, const int64_t ×tamp, int unused) {
|
||||
assert(_threads->getNetworkThread()->IsCurrent());
|
||||
|
||||
_lastNetworkActivityMs = rtc::TimeMillis();
|
||||
_isFailed = false;
|
||||
}
|
||||
|
||||
void NativeNetworkingImpl::transportRouteChanged(absl::optional<rtc::NetworkRoute> route) {
|
||||
assert(_threads->getNetworkThread()->IsCurrent());
|
||||
|
||||
|
@ -606,6 +601,10 @@ void NativeNetworkingImpl::UpdateAggregateStates_n() {
|
|||
if (_isConnected != isConnected) {
|
||||
_isConnected = isConnected;
|
||||
|
||||
if (!isConnected) {
|
||||
_lastDisconnectedTimestamp = rtc::TimeMillis();
|
||||
}
|
||||
|
||||
notifyStateUpdated();
|
||||
|
||||
if (_dataChannelInterface) {
|
||||
|
|
|
@ -143,6 +143,7 @@ public:
|
|||
};
|
||||
|
||||
static webrtc::CryptoOptions getDefaulCryptoOptions();
|
||||
static ConnectionDescription::CandidateDescription connectionDescriptionFromCandidate(cricket::Candidate const &candidate);
|
||||
|
||||
NativeNetworkingImpl(Configuration &&configuration);
|
||||
~NativeNetworkingImpl();
|
||||
|
@ -168,7 +169,6 @@ private:
|
|||
void OnTransportReceivingState_n(rtc::PacketTransportInternal *transport);
|
||||
void transportStateChanged(cricket::IceTransportInternal *transport);
|
||||
void transportReadyToSend(cricket::IceTransportInternal *transport);
|
||||
void transportPacketReceived(rtc::PacketTransportInternal *transport, const char *bytes, size_t size, const int64_t ×tamp, int unused);
|
||||
void transportRouteChanged(absl::optional<rtc::NetworkRoute> route);
|
||||
void candidatePairChanged(cricket::CandidatePairChangeEvent const &event);
|
||||
void DtlsReadyToSend(bool DtlsReadyToSend);
|
||||
|
@ -214,7 +214,7 @@ private:
|
|||
|
||||
bool _isConnected = false;
|
||||
bool _isFailed = false;
|
||||
int64_t _lastNetworkActivityMs = 0;
|
||||
int64_t _lastDisconnectedTimestamp = 0;
|
||||
absl::optional<RouteDescription> _currentRouteDescription;
|
||||
absl::optional<ConnectionDescription> _currentConnectionDescription;
|
||||
};
|
||||
|
|
|
@ -366,6 +366,7 @@ std::vector<uint8_t> InitialSetupMessage_serialize(const InitialSetupMessage * c
|
|||
object.insert(std::make_pair("@type", json11::Json("InitialSetup")));
|
||||
object.insert(std::make_pair("ufrag", json11::Json(message->ufrag)));
|
||||
object.insert(std::make_pair("pwd", json11::Json(message->pwd)));
|
||||
object.insert(std::make_pair("renomination", json11::Json(message->supportsRenomination)));
|
||||
|
||||
json11::Json::array jsonFingerprints;
|
||||
for (const auto &fingerprint : message->fingerprints) {
|
||||
|
@ -393,6 +394,11 @@ absl::optional<InitialSetupMessage> InitialSetupMessage_parse(json11::Json::obje
|
|||
RTC_LOG(LS_ERROR) << "Signaling: pwd must be a string";
|
||||
return absl::nullopt;
|
||||
}
|
||||
const auto renomination = object.find("renomination");
|
||||
bool renominationValue = false;
|
||||
if (renomination != object.end() && renomination->second.is_bool()) {
|
||||
renominationValue = renomination->second.bool_value();
|
||||
}
|
||||
const auto fingerprints = object.find("fingerprints");
|
||||
if (fingerprints == object.end() || !fingerprints->second.is_array()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: fingerprints must be an array";
|
||||
|
@ -431,6 +437,7 @@ absl::optional<InitialSetupMessage> InitialSetupMessage_parse(json11::Json::obje
|
|||
InitialSetupMessage message;
|
||||
message.ufrag = ufrag->second.string_value();
|
||||
message.pwd = pwd->second.string_value();
|
||||
message.supportsRenomination = renominationValue;
|
||||
message.fingerprints = std::move(parsedFingerprints);
|
||||
|
||||
return message;
|
||||
|
|
|
@ -138,6 +138,7 @@ struct MediaContent {
|
|||
struct InitialSetupMessage {
|
||||
std::string ufrag;
|
||||
std::string pwd;
|
||||
bool supportsRenomination = false;
|
||||
std::vector<DtlsFingerprint> fingerprints;
|
||||
};
|
||||
|
||||
|
|
|
@ -20,8 +20,8 @@ public class BuildVars {
|
|||
public static boolean USE_CLOUD_STRINGS = true;
|
||||
public static boolean CHECK_UPDATES = true;
|
||||
public static boolean NO_SCOPED_STORAGE = Build.VERSION.SDK_INT <= 29;
|
||||
public static int BUILD_VERSION = 2622;
|
||||
public static String BUILD_VERSION_STRING = "8.7.0";
|
||||
public static int BUILD_VERSION = 2629;
|
||||
public static String BUILD_VERSION_STRING = "8.7.1";
|
||||
public static int APP_ID = 4;
|
||||
public static String APP_HASH = "014b35b6184100b085b0d0572f9b5103";
|
||||
|
||||
|
|
|
@ -1356,7 +1356,7 @@ public class DownloadController extends BaseController implements NotificationCe
|
|||
for (int i = 0; i < messageObjects.size(); i++) {
|
||||
boolean found = false;
|
||||
for (int j = 0; j < recentDownloadingFiles.size(); j++) {
|
||||
if (messageObjects.get(i).getId() == recentDownloadingFiles.get(j).getId()) {
|
||||
if (messageObjects.get(i).getId() == recentDownloadingFiles.get(j).getId() && recentDownloadingFiles.get(j).getDialogId() == messageObjects.get(i).getDialogId()) {
|
||||
recentDownloadingFiles.remove(j);
|
||||
found = true;
|
||||
break;
|
||||
|
@ -1364,7 +1364,7 @@ public class DownloadController extends BaseController implements NotificationCe
|
|||
}
|
||||
if (!found) {
|
||||
for (int j = 0; j < downloadingFiles.size(); j++) {
|
||||
if (messageObjects.get(i).getId() == downloadingFiles.get(j).getId()) {
|
||||
if (messageObjects.get(i).getId() == downloadingFiles.get(j).getId() && downloadingFiles.get(j).getDialogId() == messageObjects.get(i).getDialogId()) {
|
||||
downloadingFiles.remove(j);
|
||||
found = true;
|
||||
break;
|
||||
|
|
|
@ -4912,6 +4912,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
|
|||
info.mediaEntities,
|
||||
info.isPhoto,
|
||||
info.cropState,
|
||||
info.roundVideo,
|
||||
callback);
|
||||
|
||||
|
||||
|
|
|
@ -6417,7 +6417,7 @@ public class MessageObject {
|
|||
for (int a = 0; a < getDocument().attributes.size(); a++) {
|
||||
TLRPC.DocumentAttribute attribute = getDocument().attributes.get(a);
|
||||
if (attribute instanceof TLRPC.TL_documentAttributeAudio) {
|
||||
if (attribute.duration < MessagesController.getInstance(currentAccount).ringtoneDurationMax * 2) {
|
||||
if (attribute.duration < 60) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -835,7 +835,7 @@ public class MessagesController extends BaseController implements NotificationCe
|
|||
showFiltersTooltip = mainPreferences.getBoolean("showFiltersTooltip", false);
|
||||
autoarchiveAvailable = mainPreferences.getBoolean("autoarchiveAvailable", false);
|
||||
groupCallVideoMaxParticipants = mainPreferences.getInt("groipCallVideoMaxParticipants", 30);
|
||||
chatReadMarkSizeThreshold = mainPreferences.getInt("chatReadMarkSizeThreshold", 50);
|
||||
chatReadMarkSizeThreshold = mainPreferences.getInt("chatReadMarkSizeThreshold", 100);
|
||||
chatReadMarkExpirePeriod = mainPreferences.getInt("chatReadMarkExpirePeriod", 7 * 86400);
|
||||
ringtoneDurationMax = mainPreferences.getInt("ringtoneDurationMax", 5);
|
||||
ringtoneSizeMax = mainPreferences.getInt("ringtoneSizeMax", 1024_00);
|
||||
|
@ -10696,19 +10696,22 @@ public class MessagesController extends BaseController implements NotificationCe
|
|||
arr.add(obj);
|
||||
}
|
||||
|
||||
AndroidUtilities.runOnUIThread(() -> {
|
||||
for (int a = 0; a < messages.size(); a++) {
|
||||
long key = messages.keyAt(a);
|
||||
ArrayList<MessageObject> value = messages.valueAt(a);
|
||||
updateInterfaceWithMessages(key, value, false);
|
||||
}
|
||||
getNotificationCenter().postNotificationName(NotificationCenter.dialogsNeedReload);
|
||||
});
|
||||
getMessagesStorage().getStorageQueue().postRunnable(() -> {
|
||||
if (!pushMessages.isEmpty()) {
|
||||
AndroidUtilities.runOnUIThread(() -> getNotificationsController().processNewMessages(pushMessages, !(res instanceof TLRPC.TL_updates_differenceSlice), false, null));
|
||||
}
|
||||
getMessagesStorage().putMessages(res.new_messages, true, false, false, getDownloadController().getAutodownloadMask(), false);
|
||||
|
||||
for (int a = 0; a < messages.size(); a++) {
|
||||
long dialogId = messages.keyAt(a);
|
||||
ArrayList<MessageObject> arr = messages.valueAt(a);
|
||||
getMediaDataController().loadReplyMessagesForMessages(arr, dialogId, false, () -> {
|
||||
AndroidUtilities.runOnUIThread(() -> {
|
||||
updateInterfaceWithMessages(dialogId, arr, false);
|
||||
getNotificationCenter().postNotificationName(NotificationCenter.dialogsNeedReload);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
getSecretChatHelper().processPendingEncMessages();
|
||||
|
@ -13086,6 +13089,11 @@ public class MessagesController extends BaseController implements NotificationCe
|
|||
updatesOnMainThread = new ArrayList<>();
|
||||
}
|
||||
updatesOnMainThread.add(baseUpdate);
|
||||
} else if (baseUpdate instanceof TLRPC.TL_updateBotMenuButton) {
|
||||
if (updatesOnMainThread == null) {
|
||||
updatesOnMainThread = new ArrayList<>();
|
||||
}
|
||||
updatesOnMainThread.add(baseUpdate);
|
||||
} else if (baseUpdate instanceof TLRPC.TL_updateReadChannelDiscussionInbox) {
|
||||
if (updatesOnMainThread == null) {
|
||||
updatesOnMainThread = new ArrayList<>();
|
||||
|
@ -13721,6 +13729,9 @@ public class MessagesController extends BaseController implements NotificationCe
|
|||
getNotificationCenter().postNotificationName(NotificationCenter.webViewResultSent, resultSent.query_id);
|
||||
} else if (baseUpdate instanceof TLRPC.TL_updateAttachMenuBots) {
|
||||
getMediaDataController().loadAttachMenuBots(false, true);
|
||||
} else if (baseUpdate instanceof TLRPC.TL_updateBotMenuButton) {
|
||||
TLRPC.TL_updateBotMenuButton updateBotMenuButton = (TLRPC.TL_updateBotMenuButton) baseUpdate;
|
||||
getNotificationCenter().postNotificationName(NotificationCenter.updateBotMenuButton, updateBotMenuButton.bot_id, updateBotMenuButton.button);
|
||||
} else if (baseUpdate instanceof TLRPC.TL_updateReadChannelDiscussionInbox) {
|
||||
TLRPC.TL_updateReadChannelDiscussionInbox update = (TLRPC.TL_updateReadChannelDiscussionInbox) baseUpdate;
|
||||
getNotificationCenter().postNotificationName(NotificationCenter.threadMessagesRead, -update.channel_id, update.top_msg_id, update.read_max_id, 0);
|
||||
|
|
|
@ -189,6 +189,8 @@ public class NotificationCenter {
|
|||
public static final int filterSettingsUpdated = totalEvents++;
|
||||
public static final int suggestedFiltersLoaded = totalEvents++;
|
||||
|
||||
public static final int updateBotMenuButton = totalEvents++;
|
||||
|
||||
//global
|
||||
public static final int pushMessagesUpdated = totalEvents++;
|
||||
public static final int stopEncodingService = totalEvents++;
|
||||
|
|
|
@ -3438,7 +3438,7 @@ public class NotificationsController extends BaseController {
|
|||
MessageObject messageObject = pushMessages.get(0);
|
||||
boolean[] text = new boolean[1];
|
||||
String message = lastMessage = getStringForMessage(messageObject, false, text, null);
|
||||
silent = messageObject.messageOwner.silent ? 1 : 0;
|
||||
silent = isSilentMessage(messageObject) ? 1 : 0;
|
||||
if (message == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -3469,7 +3469,7 @@ public class NotificationsController extends BaseController {
|
|||
}
|
||||
if (silent == 2) {
|
||||
lastMessage = message;
|
||||
silent = messageObject.messageOwner.silent ? 1 : 0;
|
||||
silent = isSilentMessage(messageObject) ? 1 : 0;
|
||||
}
|
||||
if (pushDialogs.size() == 1) {
|
||||
if (replace) {
|
||||
|
@ -3859,6 +3859,10 @@ public class NotificationsController extends BaseController {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean isSilentMessage(MessageObject messageObject) {
|
||||
return messageObject.messageOwner.silent || messageObject.isReactionPush;
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
private void setNotificationChannel(Notification mainNotification, NotificationCompat.Builder builder, boolean useSummaryNotification) {
|
||||
if (useSummaryNotification) {
|
||||
|
|
|
@ -21,13 +21,15 @@ public class SharedPrefsHelper {
|
|||
}
|
||||
|
||||
public static void cleanupAccount(int account) {
|
||||
SharedPreferences.Editor editor = webViewBotsPrefs.edit();
|
||||
for (String key : webViewBotsPrefs.getAll().keySet()) {
|
||||
if (key.startsWith("confirm_shown_" + account + "_")) {
|
||||
editor.remove(key);
|
||||
if (webViewBotsPrefs != null) {
|
||||
SharedPreferences.Editor editor = webViewBotsPrefs.edit();
|
||||
for (String key : webViewBotsPrefs.getAll().keySet()) {
|
||||
if (key.startsWith("confirm_shown_" + account + "_")) {
|
||||
editor.remove(key);
|
||||
}
|
||||
}
|
||||
editor.apply();
|
||||
}
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public static SharedPreferences getWebViewBotsPrefs() {
|
||||
|
|
|
@ -37,7 +37,7 @@ public class RingtoneDataStore {
|
|||
public final ArrayList<CachedTone> userRingtones = new ArrayList<>();
|
||||
private boolean loaded;
|
||||
|
||||
public final static HashSet<String> ringtoneSupportedMimeType = new HashSet<>(Arrays.asList("audio/mpeg", "audio/ogg", "audio/m4a"));
|
||||
public final static HashSet<String> ringtoneSupportedMimeType = new HashSet<>(Arrays.asList("audio/mpeg3", "audio/mpeg", "audio/ogg", "audio/m4a"));
|
||||
|
||||
public RingtoneDataStore(int currentAccount) {
|
||||
this.currentAccount = currentAccount;
|
||||
|
@ -49,7 +49,9 @@ public class RingtoneDataStore {
|
|||
} catch (Exception e) {
|
||||
FileLog.e(e);
|
||||
}
|
||||
loadUserRingtones();
|
||||
AndroidUtilities.runOnUIThread(() -> {
|
||||
loadUserRingtones();
|
||||
});
|
||||
}
|
||||
|
||||
public void loadUserRingtones() {
|
||||
|
@ -251,7 +253,7 @@ public class RingtoneDataStore {
|
|||
File file = FileLoader.getPathToAttach(document);
|
||||
if (file == null || !file.exists()) {
|
||||
AndroidUtilities.runOnUIThread(() -> {
|
||||
FileLoader.getInstance(currentAccount).loadFile(document, null, 0, 0);
|
||||
FileLoader.getInstance(currentAccount).loadFile(document, document, 0, 0);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,10 +50,11 @@ public class MediaCodecVideoConvertor {
|
|||
ArrayList<VideoEditedInfo.MediaEntity> mediaEntities,
|
||||
boolean isPhoto,
|
||||
MediaController.CropState cropState,
|
||||
boolean isRound,
|
||||
MediaController.VideoConvertorListener callback) {
|
||||
this.callback = callback;
|
||||
return convertVideoInternal(videoPath, cacheFile, rotationValue, isSecret, originalWidth, originalHeight,
|
||||
resultWidth, resultHeight, framerate, bitrate, originalBitrate, startTime, endTime, avatarStartTime, duration, needCompress, false, savedFilterState, paintPath, mediaEntities, isPhoto, cropState);
|
||||
resultWidth, resultHeight, framerate, bitrate, originalBitrate, startTime, endTime, avatarStartTime, duration, needCompress, false, savedFilterState, paintPath, mediaEntities, isPhoto, cropState, isRound);
|
||||
}
|
||||
|
||||
public long getLastFrameTimestamp() {
|
||||
|
@ -73,7 +74,8 @@ public class MediaCodecVideoConvertor {
|
|||
String paintPath,
|
||||
ArrayList<VideoEditedInfo.MediaEntity> mediaEntities,
|
||||
boolean isPhoto,
|
||||
MediaController.CropState cropState) {
|
||||
MediaController.CropState cropState,
|
||||
boolean isRound) {
|
||||
|
||||
long time = System.currentTimeMillis();
|
||||
boolean error = false;
|
||||
|
@ -407,7 +409,9 @@ public class MediaCodecVideoConvertor {
|
|||
|
||||
decoder = MediaCodec.createDecoderByType(videoFormat.getString(MediaFormat.KEY_MIME));
|
||||
outputSurface = new OutputSurface(savedFilterState, null, paintPath, mediaEntities, cropState, resultWidth, resultHeight, originalWidth, originalHeight, rotationValue, framerate, false);
|
||||
outputSurface.changeFragmentShader(createFragmentShader(originalWidth, originalHeight, resultWidth, resultHeight, true), createFragmentShader(originalWidth, originalHeight, resultWidth, resultHeight, false));
|
||||
if (!isRound && Math.max(resultHeight, resultHeight) / (float) Math.max(originalHeight, originalWidth) < 0.9f) {
|
||||
outputSurface.changeFragmentShader(createFragmentShader(originalWidth, originalHeight, resultWidth, resultHeight, true), createFragmentShader(originalWidth, originalHeight, resultWidth, resultHeight, false));
|
||||
}
|
||||
decoder.configure(videoFormat, outputSurface.getSurface(), null, 0);
|
||||
decoder.start();
|
||||
|
||||
|
@ -800,7 +804,7 @@ public class MediaCodecVideoConvertor {
|
|||
originalWidth, originalHeight,
|
||||
resultWidth, resultHeight, framerate, bitrate, originalBitrate, startTime, endTime, avatarStartTime, duration,
|
||||
needCompress, true, savedFilterState, paintPath, mediaEntities,
|
||||
isPhoto, cropState);
|
||||
isPhoto, cropState, isRound);
|
||||
}
|
||||
|
||||
long timeLeft = System.currentTimeMillis() - time;
|
||||
|
|
|
@ -14,10 +14,7 @@ import android.view.View;
|
|||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.google.android.exoplayer2.util.Log;
|
||||
|
||||
import org.telegram.messenger.AndroidUtilities;
|
||||
import org.telegram.messenger.R;
|
||||
import org.telegram.ui.Adapters.FiltersView;
|
||||
import org.telegram.ui.Components.RLottieDrawable;
|
||||
|
||||
|
@ -94,15 +91,7 @@ public class ActionBarMenu extends LinearLayout {
|
|||
}
|
||||
|
||||
public ActionBarMenuItem addItem(int id, int icon, CharSequence text, int backgroundColor, Drawable drawable, int width, CharSequence title, Theme.ResourcesProvider resourcesProvider) {
|
||||
ActionBarMenuItem menuItem = new ActionBarMenuItem(getContext(), this, backgroundColor, isActionMode ? parentActionBar.itemsActionModeColor : parentActionBar.itemsColor, text != null, resourcesProvider) {
|
||||
@Override
|
||||
public void setVisibility(int visibility) {
|
||||
super.setVisibility(visibility);
|
||||
if (icon == R.drawable.ic_ab_other && (id == 0 || id == 14)) {
|
||||
Log.d("kek", id + " " + (View.VISIBLE == visibility));
|
||||
}
|
||||
}
|
||||
};
|
||||
ActionBarMenuItem menuItem = new ActionBarMenuItem(getContext(), this, backgroundColor, isActionMode ? parentActionBar.itemsActionModeColor : parentActionBar.itemsColor, text != null, resourcesProvider);
|
||||
menuItem.setTag(id);
|
||||
if (text != null) {
|
||||
menuItem.textView.setText(text);
|
||||
|
|
|
@ -1914,9 +1914,9 @@ public class ActionBarMenuItem extends FrameLayout {
|
|||
}
|
||||
}
|
||||
|
||||
public View addColoredGap() {
|
||||
public ActionBarPopupWindow.GapView addColoredGap() {
|
||||
createPopupLayout();
|
||||
View gap = new ActionBarPopupWindow.GapView(getContext(), Theme.key_graySection);
|
||||
ActionBarPopupWindow.GapView gap = new ActionBarPopupWindow.GapView(getContext(), Theme.key_graySection);
|
||||
gap.setTag(R.id.fit_width_tag, 1);
|
||||
popupLayout.addView(gap, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 8));
|
||||
return gap;
|
||||
|
|
|
@ -551,6 +551,10 @@ public class ActionBarPopupWindow extends PopupWindow {
|
|||
public void setTopView(View topView) {
|
||||
this.topView = topView;
|
||||
}
|
||||
|
||||
public void setSwipeBackForegroundColor(int color) {
|
||||
getSwipeBack().setForegroundColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
public ActionBarPopupWindow() {
|
||||
|
@ -866,6 +870,7 @@ public class ActionBarPopupWindow extends PopupWindow {
|
|||
|
||||
Paint paint = new Paint();
|
||||
String colorKey;
|
||||
int color = 0;
|
||||
public GapView(Context context, String colorKey) {
|
||||
super(context);
|
||||
this.colorKey = colorKey;
|
||||
|
@ -873,8 +878,16 @@ public class ActionBarPopupWindow extends PopupWindow {
|
|||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
paint.setColor(Theme.getColor(colorKey));
|
||||
if (color == 0) {
|
||||
paint.setColor(Theme.getColor(colorKey));
|
||||
} else {
|
||||
paint.setColor(color);
|
||||
}
|
||||
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), paint);
|
||||
}
|
||||
|
||||
public void setColor(int color) {
|
||||
this.color = color;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,7 +17,6 @@ import android.app.Dialog;
|
|||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
|
@ -920,9 +919,6 @@ public class BottomSheet extends Dialog {
|
|||
super.onCreate(savedInstanceState);
|
||||
|
||||
Window window = getWindow();
|
||||
/*if (Build.VERSION.SDK_INT >= 30) {
|
||||
window.setDecorFitsSystemWindows(true);
|
||||
}*/
|
||||
window.setWindowAnimations(R.style.DialogNoAnimation);
|
||||
setContentView(container, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
|
||||
|
|
|
@ -312,7 +312,9 @@ public class CalendarActivity extends BaseFragment {
|
|||
@Override
|
||||
public void run(boolean forAll) {
|
||||
finishFragment();
|
||||
|
||||
if (parentLayout == null) {
|
||||
return;
|
||||
}
|
||||
if (parentLayout.fragmentsStack.size() >= 2) {
|
||||
BaseFragment fragment = parentLayout.fragmentsStack.get(parentLayout.fragmentsStack.size() - 2);
|
||||
if (fragment instanceof ChatActivity) {
|
||||
|
|
|
@ -31,7 +31,6 @@ import android.text.Layout;
|
|||
import android.text.Spannable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.StaticLayout;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.ClickableSpan;
|
||||
import android.util.SparseArray;
|
||||
|
@ -39,9 +38,7 @@ import android.util.TypedValue;
|
|||
import android.view.Gravity;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
@ -207,6 +204,7 @@ public class CameraScanActivity extends BaseFragment {
|
|||
actionBarLayout[0] = null;
|
||||
}
|
||||
};
|
||||
bottomSheet.setUseLightStatusBar(false);
|
||||
AndroidUtilities.setLightNavigationBar(bottomSheet.getWindow(), false);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
bottomSheet.getWindow().setNavigationBarColor(0xff000000);
|
||||
|
@ -436,6 +434,7 @@ public class CameraScanActivity extends BaseFragment {
|
|||
} else {
|
||||
actionBar.setBackgroundDrawable(null);
|
||||
actionBar.setAddToContainer(false);
|
||||
actionBar.setTitleColor(0xffffffff);
|
||||
actionBar.setItemsColor(0xffffffff, false);
|
||||
actionBar.setItemsBackgroundColor(0x22ffffff, false);
|
||||
viewGroup.setBackgroundColor(Theme.getColor(Theme.key_wallet_blackBackground));
|
||||
|
|
|
@ -1056,8 +1056,7 @@ public class DialogCell extends BaseCell {
|
|||
checkMessage = false;
|
||||
messageString = formatArchivedDialogNames();
|
||||
} else if (message.messageOwner instanceof TLRPC.TL_messageService) {
|
||||
if (ChatObject.isChannelAndNotMegaGroup(chat) && (message.messageOwner.action instanceof TLRPC.TL_messageActionHistoryClear ||
|
||||
message.messageOwner.action instanceof TLRPC.TL_messageActionChannelMigrateFrom)) {
|
||||
if (ChatObject.isChannelAndNotMegaGroup(chat) && (message.messageOwner.action instanceof TLRPC.TL_messageActionChannelMigrateFrom)) {
|
||||
messageString = "";
|
||||
showChecks = false;
|
||||
} else {
|
||||
|
|
|
@ -2763,9 +2763,18 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
|
||||
@Override
|
||||
public void muteFor(int timeInSeconds) {
|
||||
getNotificationsController().muteUntil(dialog_id, timeInSeconds);
|
||||
if (BulletinFactory.canShowBulletin(ChatActivity.this)) {
|
||||
BulletinFactory.createMuteBulletin(ChatActivity.this, NotificationsController.SETTING_MUTE_CUSTOM, timeInSeconds, getResourceProvider()).show();
|
||||
if (timeInSeconds == 0) {
|
||||
if (getMessagesController().isDialogMuted(dialog_id)) {
|
||||
ChatActivity.this.toggleMute(true);
|
||||
}
|
||||
if (BulletinFactory.canShowBulletin(ChatActivity.this)) {
|
||||
BulletinFactory.createMuteBulletin(ChatActivity.this, NotificationsController.SETTING_MUTE_UNMUTE, timeInSeconds, getResourceProvider()).show();
|
||||
}
|
||||
} else {
|
||||
getNotificationsController().muteUntil(dialog_id, timeInSeconds);
|
||||
if (BulletinFactory.canShowBulletin(ChatActivity.this)) {
|
||||
BulletinFactory.createMuteBulletin(ChatActivity.this, NotificationsController.SETTING_MUTE_CUSTOM, timeInSeconds, getResourceProvider()).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4081,7 +4090,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
greetingsViewContainer.setBackground(Theme.createServiceDrawable(AndroidUtilities.dp(10), greetingsViewContainer, contentView, getThemedPaint(Theme.key_paint_chatActionBackground)));
|
||||
emptyViewContainer.addView(greetingsViewContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL, 68, 0, 68, 0));
|
||||
} else if (currentEncryptedChat == null) {
|
||||
if (!isThreadChat() && chatMode == 0 && (currentUser != null && currentUser.self || currentChat != null && currentChat.creator)) {
|
||||
if (!isThreadChat() && chatMode == 0 && ((currentUser != null && currentUser.self) || (currentChat != null && currentChat.creator && !ChatObject.isChannelAndNotMegaGroup(currentChat)))) {
|
||||
bigEmptyView = new ChatBigEmptyView(context, contentView, currentChat != null ? ChatBigEmptyView.EMPTY_VIEW_TYPE_GROUP : ChatBigEmptyView.EMPTY_VIEW_TYPE_SAVED, themeDelegate);
|
||||
emptyViewContainer.addView(bigEmptyView, new FrameLayout.LayoutParams(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER));
|
||||
if (currentChat != null) {
|
||||
|
@ -14517,7 +14526,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
endReached[0] = cacheEndReached[0] = true;
|
||||
forwardEndReached[0] = forwardEndReached[0] = true;
|
||||
}
|
||||
if (ChatObject.isChannel(currentChat) && !getMessagesController().dialogs_dict.containsKey(dialog_id) && load_type == 2 && loadIndex == 0) {
|
||||
if (!isThreadChat() && ChatObject.isChannel(currentChat) && !getMessagesController().dialogs_dict.containsKey(dialog_id) && load_type == 2 && loadIndex == 0) {
|
||||
forwardEndReached[0] = false;
|
||||
hideForwardEndReached = true;
|
||||
}
|
||||
|
@ -19652,7 +19661,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
}
|
||||
if (pinnedText != null) {
|
||||
if (pinnedText instanceof Spannable) {
|
||||
MediaDataController.addTextStyleRuns(pinnedMessageObject, (Spannable) pinnedText);
|
||||
MediaDataController.addTextStyleRuns(pinnedMessageObject, (Spannable) pinnedText, TextStyleSpan.FLAG_STYLE_SPOILER | TextStyleSpan.FLAG_STYLE_STRIKE);
|
||||
}
|
||||
messageTextView.setText(pinnedText);
|
||||
}
|
||||
|
@ -21668,7 +21677,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
} else {
|
||||
isReactionsAvailable = !isSecretChat() && !isInScheduleMode() && message.isReactionsAvailable() && (chatInfo != null && !chatInfo.available_reactions.isEmpty() || (chatInfo == null && !ChatObject.isChannel(currentChat)) || currentUser != null) && !availableReacts.isEmpty();
|
||||
}
|
||||
boolean showMessageSeen = !isReactionsViewAvailable && !isInScheduleMode() && currentChat != null && message.isOutOwner() && message.isSent() && !message.isEditing() && !message.isSending() && !message.isSendError() && !message.isContentUnread() && !message.isUnread() && (ConnectionsManager.getInstance(currentAccount).getCurrentTime() - message.messageOwner.date < getMessagesController().chatReadMarkExpirePeriod) && (ChatObject.isMegagroup(currentChat) || !ChatObject.isChannel(currentChat)) && chatInfo != null && chatInfo.participants_count < getMessagesController().chatReadMarkSizeThreshold && !(message.messageOwner.action instanceof TLRPC.TL_messageActionChatJoinedByRequest) && (v instanceof ChatMessageCell);
|
||||
boolean showMessageSeen = !isReactionsViewAvailable && !isInScheduleMode() && currentChat != null && message.isOutOwner() && message.isSent() && !message.isEditing() && !message.isSending() && !message.isSendError() && !message.isContentUnread() && !message.isUnread() && (ConnectionsManager.getInstance(currentAccount).getCurrentTime() - message.messageOwner.date < getMessagesController().chatReadMarkExpirePeriod) && (ChatObject.isMegagroup(currentChat) || !ChatObject.isChannel(currentChat)) && chatInfo != null && chatInfo.participants_count <= getMessagesController().chatReadMarkSizeThreshold && !(message.messageOwner.action instanceof TLRPC.TL_messageActionChatJoinedByRequest) && (v instanceof ChatMessageCell);
|
||||
|
||||
int flags = 0;
|
||||
if (isReactionsViewAvailable || showMessageSeen) {
|
||||
|
@ -21988,7 +21997,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
addGap = true;
|
||||
}
|
||||
|
||||
if (message.probablyRingtone()) {
|
||||
if (message.probablyRingtone() && currentEncryptedChat == null) {
|
||||
ActionBarMenuSubItem cell = new ActionBarMenuSubItem(getParentActivity(), true, false, themeDelegate);
|
||||
cell.setMinimumWidth(AndroidUtilities.dp(200));
|
||||
cell.setTextAndIcon(LocaleController.getString("SaveForNotifications", R.string.SaveForNotifications), R.drawable.msg_tone_add);
|
||||
|
@ -22000,7 +22009,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
|
||||
@Override
|
||||
public void run() {
|
||||
if (clicked || dialog_id == 0 || getUserConfig().clientUserId == dialog_id) {
|
||||
if (clicked) {
|
||||
return;
|
||||
}
|
||||
clicked = true;
|
||||
|
@ -23474,6 +23483,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
} else if (chatActivityEnterView != null && chatActivityEnterView.isPopupShowing()) {
|
||||
chatActivityEnterView.hidePopup(true);
|
||||
return false;
|
||||
} else if (chatActivityEnterView != null && chatActivityEnterView.hasBotWebView() && chatActivityEnterView.botCommandsMenuIsShowing() && chatActivityEnterView.onBotWebViewBackPressed()) {
|
||||
return false;
|
||||
} else if (chatActivityEnterView != null && chatActivityEnterView.botCommandsMenuIsShowing()) {
|
||||
chatActivityEnterView.hideBotCommands();
|
||||
return false;
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
package org.telegram.ui;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.telegram.messenger.LocaleController;
|
||||
import org.telegram.messenger.R;
|
||||
import org.telegram.ui.ActionBar.ActionBarMenuItem;
|
||||
import org.telegram.ui.ActionBar.ActionBarMenuSubItem;
|
||||
import org.telegram.ui.ActionBar.ActionBarPopupWindow;
|
||||
import org.telegram.ui.Components.PopupSwipeBackLayout;
|
||||
|
||||
public class ChooseSpeedLayout {
|
||||
|
||||
ActionBarPopupWindow.ActionBarPopupWindowLayout speedSwipeBackLayout;
|
||||
|
||||
ActionBarMenuSubItem[] speedItems = new ActionBarMenuSubItem[5];
|
||||
public ChooseSpeedLayout(Context context, PopupSwipeBackLayout swipeBackLayout, Callback callback) {
|
||||
speedSwipeBackLayout = new ActionBarPopupWindow.ActionBarPopupWindowLayout(context, 0, null);
|
||||
speedSwipeBackLayout.setFitItems(true);
|
||||
|
||||
ActionBarMenuSubItem backItem = ActionBarMenuItem.addItem(speedSwipeBackLayout, R.drawable.msg_arrow_back, LocaleController.getString("Back", R.string.Back), false, null);
|
||||
backItem.setOnClickListener(view -> {
|
||||
swipeBackLayout.closeForeground();
|
||||
});
|
||||
backItem.setColors(0xfffafafa, 0xfffafafa);
|
||||
|
||||
ActionBarMenuSubItem item = ActionBarMenuItem.addItem(speedSwipeBackLayout, R.drawable.msg_speed_0_2, LocaleController.getString("SpeedVerySlow", R.string.SpeedVerySlow), false, null);
|
||||
item.setColors(0xfffafafa, 0xfffafafa);
|
||||
item.setOnClickListener((view) -> {
|
||||
callback.onSpeedSelected(0.25f);
|
||||
});
|
||||
speedItems[0] = item;
|
||||
|
||||
item = ActionBarMenuItem.addItem(speedSwipeBackLayout, R.drawable.msg_speed_0_5, LocaleController.getString("SpeedSlow", R.string.SpeedSlow), false, null);
|
||||
item.setColors(0xfffafafa, 0xfffafafa);
|
||||
item.setOnClickListener((view) -> {
|
||||
callback.onSpeedSelected(0.5f);
|
||||
});
|
||||
speedItems[1] = item;
|
||||
|
||||
item = ActionBarMenuItem.addItem(speedSwipeBackLayout, R.drawable.msg_speed_1, LocaleController.getString("SpeedNormal", R.string.SpeedNormal), false, null);
|
||||
item.setColors(0xfffafafa, 0xfffafafa);
|
||||
item.setOnClickListener((view) -> {
|
||||
callback.onSpeedSelected(1f);
|
||||
});
|
||||
speedItems[2] = item;
|
||||
|
||||
item = ActionBarMenuItem.addItem(speedSwipeBackLayout, R.drawable.msg_speed_1_5, LocaleController.getString("SpeedFast", R.string.SpeedFast), false, null);
|
||||
item.setColors(0xfffafafa, 0xfffafafa);
|
||||
item.setOnClickListener((view) -> {
|
||||
callback.onSpeedSelected(1.5f);
|
||||
});
|
||||
speedItems[3] = item;
|
||||
|
||||
item = ActionBarMenuItem.addItem(speedSwipeBackLayout, R.drawable.msg_speed_2, LocaleController.getString("SpeedVeryFast", R.string.SpeedVeryFast), false, null);
|
||||
item.setColors(0xfffafafa, 0xfffafafa);
|
||||
item.setOnClickListener((view) -> {
|
||||
callback.onSpeedSelected(2f);
|
||||
});
|
||||
speedItems[4] = item;
|
||||
}
|
||||
|
||||
public void update(float currentVideoSpeed) {
|
||||
for (int a = 0; a < speedItems.length; a++) {
|
||||
if (a == 0 && Math.abs(currentVideoSpeed - 0.25f) < 0.001f ||
|
||||
a == 1 && Math.abs(currentVideoSpeed - 0.5f) < 0.001f ||
|
||||
a == 2 && Math.abs(currentVideoSpeed - 1.0f) < 0.001f ||
|
||||
a == 3 && Math.abs(currentVideoSpeed - 1.5f) < 0.001f ||
|
||||
a == 4 && Math.abs(currentVideoSpeed - 2.0f) < 0.001f) {
|
||||
speedItems[a].setColors(0xff6BB6F9, 0xff6BB6F9);
|
||||
} else {
|
||||
speedItems[a].setColors(0xfffafafa, 0xfffafafa);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface Callback {
|
||||
void onSpeedSelected(float speed);
|
||||
}
|
||||
}
|
|
@ -1442,7 +1442,11 @@ public class AlertsCreator {
|
|||
if (UserObject.isUserSelf(user)) {
|
||||
messageTextView.setText(AndroidUtilities.replaceTags(LocaleController.getString("DeleteAllMessagesSavedAlert", R.string.DeleteAllMessagesSavedAlert)));
|
||||
} else {
|
||||
messageTextView.setText(AndroidUtilities.replaceTags(LocaleController.getString("DeleteAllMessagesAlert", R.string.DeleteAllMessagesAlert)));
|
||||
if (chat != null && ChatObject.isChannelAndNotMegaGroup(chat)) {
|
||||
messageTextView.setText(AndroidUtilities.replaceTags(LocaleController.getString("DeleteAllMessagesChannelAlert", R.string.DeleteAllMessagesChannelAlert)));
|
||||
} else {
|
||||
messageTextView.setText(AndroidUtilities.replaceTags(LocaleController.getString("DeleteAllMessagesAlert", R.string.DeleteAllMessagesAlert)));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (clear) {
|
||||
|
@ -1512,7 +1516,7 @@ public class AlertsCreator {
|
|||
if (clearingCache) {
|
||||
actionText = LocaleController.getString("ClearHistoryCache", R.string.ClearHistoryCache);
|
||||
} else {
|
||||
actionText = LocaleController.getString("ClearHistory", R.string.ClearHistory);
|
||||
actionText = LocaleController.getString("ClearForMe", R.string.ClearForMe);
|
||||
}
|
||||
} else {
|
||||
if (admin) {
|
||||
|
@ -1616,8 +1620,12 @@ public class AlertsCreator {
|
|||
if (user != null) {
|
||||
messageTextView.setText(AndroidUtilities.replaceTags(LocaleController.formatString("AreYouSureClearHistoryWithUser", R.string.AreYouSureClearHistoryWithUser, UserObject.getUserName(user))));
|
||||
} else {
|
||||
if (chat != null && canDeleteHistory) {
|
||||
messageTextView.setText(AndroidUtilities.replaceTags(LocaleController.formatString("AreYouSureClearHistoryWithChat", R.string.AreYouSureClearHistoryWithChat, chat.title)));
|
||||
if (canDeleteHistory) {
|
||||
if (ChatObject.isChannelAndNotMegaGroup(chat)) {
|
||||
messageTextView.setText(AndroidUtilities.replaceTags(LocaleController.formatString("AreYouSureClearHistoryWithChannel", R.string.AreYouSureClearHistoryWithChannel, chat.title)));
|
||||
} else {
|
||||
messageTextView.setText(AndroidUtilities.replaceTags(LocaleController.formatString("AreYouSureClearHistoryWithChat", R.string.AreYouSureClearHistoryWithChat, chat.title)));
|
||||
}
|
||||
} else if (chat.megagroup) {
|
||||
messageTextView.setText(LocaleController.getString("AreYouSureClearHistoryGroup", R.string.AreYouSureClearHistoryGroup));
|
||||
} else {
|
||||
|
@ -1633,7 +1641,7 @@ public class AlertsCreator {
|
|||
if (chat != null && canDeleteHistory && !TextUtils.isEmpty(chat.username)) {
|
||||
deleteForAll[0] = true;
|
||||
}
|
||||
if ((user != null && user.id != selfUserId) || (chat != null && canDeleteHistory && TextUtils.isEmpty(chat.username))) {
|
||||
if ((user != null && user.id != selfUserId) || (chat != null && canDeleteHistory && TextUtils.isEmpty(chat.username) && !ChatObject.isChannelAndNotMegaGroup(chat))) {
|
||||
cell[0] = new CheckBoxCell(context, 1, resourcesProvider);
|
||||
cell[0].setBackgroundDrawable(Theme.getSelectorDrawable(false));
|
||||
if (chat != null) {
|
||||
|
@ -1653,7 +1661,11 @@ public class AlertsCreator {
|
|||
});
|
||||
}
|
||||
|
||||
builder.setPositiveButton(LocaleController.getString("Delete", R.string.Delete), (dialogInterface, i) -> {
|
||||
String deleteText = LocaleController.getString("Delete", R.string.Delete);
|
||||
if (chat != null && canDeleteHistory && !TextUtils.isEmpty(chat.username) && !ChatObject.isChannelAndNotMegaGroup(chat)) {
|
||||
deleteText = LocaleController.getString("ClearForAll", R.string.ClearForAll);
|
||||
}
|
||||
builder.setPositiveButton(deleteText, (dialogInterface, i) -> {
|
||||
onProcessRunnable.run(deleteForAll[0]);
|
||||
});
|
||||
builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null);
|
||||
|
@ -2803,7 +2815,6 @@ public class AlertsCreator {
|
|||
numberPicker.setMaxValue(values.length - 1);
|
||||
numberPicker.setTextColor(datePickerColors.textColor);
|
||||
numberPicker.setValue(0);
|
||||
numberPicker.setWrapSelectorWheel(false);
|
||||
numberPicker.setFormatter(index -> {
|
||||
if (values[index] == 0) {
|
||||
return LocaleController.getString("AutoDeleteNever", R.string.AutoDeleteNever);
|
||||
|
@ -3048,18 +3059,59 @@ public class AlertsCreator {
|
|||
BottomSheet.Builder builder = new BottomSheet.Builder(context, false);
|
||||
builder.setApplyBottomPadding(false);
|
||||
|
||||
final NumberPicker dayPicker = new NumberPicker(context);
|
||||
dayPicker.setTextColor(datePickerColors.textColor);
|
||||
dayPicker.setTextOffset(AndroidUtilities.dp(10));
|
||||
dayPicker.setItemCount(5);
|
||||
final NumberPicker hourPicker = new NumberPicker(context) {
|
||||
int[] values = new int[]{
|
||||
0,
|
||||
60 * 24,
|
||||
2 * 60 * 24,
|
||||
3 * 60 * 24,
|
||||
4 * 60 * 24,
|
||||
5 * 60 * 24,
|
||||
6 * 60 * 24,
|
||||
7 * 60 * 24,
|
||||
2 * 7 * 60 * 24,
|
||||
3 * 7 * 60 * 24,
|
||||
31 * 60 * 24,
|
||||
2 * 31 * 60 * 24,
|
||||
3 * 31 * 60 * 24,
|
||||
4 * 31 * 60 * 24,
|
||||
5 * 31 * 60 * 24,
|
||||
6 * 31 * 60 * 24,
|
||||
365 * 60 * 24
|
||||
};
|
||||
|
||||
final NumberPicker numberPicker = new NumberPicker(context) {
|
||||
@Override
|
||||
protected CharSequence getContentDescription(int value) {
|
||||
return LocaleController.formatPluralString("Hours", value);
|
||||
protected CharSequence getContentDescription(int index) {
|
||||
if (values[index] == 0) {
|
||||
return LocaleController.getString("MuteNever", R.string.MuteNever);
|
||||
} else if (values[index] < 7 * 60 * 24) {
|
||||
return LocaleController.formatPluralString("Days", values[index] / (60 * 24));
|
||||
} else if (values[index] < 31 * 60 * 24) {
|
||||
return LocaleController.formatPluralString("Weeks", values[index] / (60 * 24));
|
||||
} else if (values[index] < 365 * 60 * 24) {
|
||||
return LocaleController.formatPluralString("Months", values[index] / (7 * 60 * 24));
|
||||
} else {
|
||||
return LocaleController.formatPluralString("Years", values[index] * 5 / 31 * 60 * 24);
|
||||
}
|
||||
}
|
||||
};
|
||||
hourPicker.setItemCount(5);
|
||||
hourPicker.setTextColor(datePickerColors.textColor);
|
||||
numberPicker.setMinValue(0);
|
||||
numberPicker.setMaxValue(values.length - 1);
|
||||
numberPicker.setTextColor(datePickerColors.textColor);
|
||||
numberPicker.setValue(0);
|
||||
numberPicker.setFormatter(index -> {
|
||||
if (values[index] == 0) {
|
||||
return LocaleController.getString("MuteNever", R.string.MuteNever);
|
||||
} else if (values[index] < 7 * 60 * 24) {
|
||||
return LocaleController.formatPluralString("Days", values[index] / (60 * 24));
|
||||
} else if (values[index] < 31 * 60 * 24) {
|
||||
return LocaleController.formatPluralString("Weeks", values[index] / (7 * 60 * 24));
|
||||
} else if (values[index] < 365 * 60 * 24) {
|
||||
return LocaleController.formatPluralString("Months", values[index] / (31 * 60 * 24));
|
||||
} else {
|
||||
return LocaleController.formatPluralString("Years", values[index] / (365 * 60 * 24));
|
||||
}
|
||||
});
|
||||
|
||||
LinearLayout container = new LinearLayout(context) {
|
||||
|
||||
|
@ -3074,10 +3126,8 @@ public class AlertsCreator {
|
|||
} else {
|
||||
count = 5;
|
||||
}
|
||||
dayPicker.setItemCount(count);
|
||||
hourPicker.setItemCount(count);
|
||||
dayPicker.getLayoutParams().height = AndroidUtilities.dp(NumberPicker.DEFAULT_SIZE_PER_COUNT) * count;
|
||||
hourPicker.getLayoutParams().height = AndroidUtilities.dp(NumberPicker.DEFAULT_SIZE_PER_COUNT) * count;
|
||||
numberPicker.setItemCount(count);
|
||||
numberPicker.getLayoutParams().height = AndroidUtilities.dp(NumberPicker.DEFAULT_SIZE_PER_COUNT) * count;
|
||||
ignoreLayout = false;
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
|
@ -3115,26 +3165,15 @@ public class AlertsCreator {
|
|||
}
|
||||
};
|
||||
|
||||
linearLayout.addView(dayPicker, LayoutHelper.createLinear(0, 54 * 5, 0.5f));
|
||||
dayPicker.setMinValue(0);
|
||||
dayPicker.setMaxValue(365);
|
||||
dayPicker.setWrapSelectorWheel(false);
|
||||
dayPicker.setFormatter(value -> LocaleController.formatPluralString("Days", value));
|
||||
linearLayout.addView(numberPicker, LayoutHelper.createLinear(0, 54 * 5, 1f));
|
||||
final NumberPicker.OnValueChangeListener onValueChangeListener = (picker, oldVal, newVal) -> {
|
||||
try {
|
||||
container.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
|
||||
} catch (Exception ignore) {
|
||||
|
||||
}
|
||||
checkMuteForButton(dayPicker, hourPicker, buttonTextView, true);
|
||||
};
|
||||
dayPicker.setOnValueChangedListener(onValueChangeListener);
|
||||
|
||||
hourPicker.setMinValue(0);
|
||||
hourPicker.setMaxValue(23);
|
||||
linearLayout.addView(hourPicker, LayoutHelper.createLinear(0, 54 * 5, 0.5f));
|
||||
hourPicker.setFormatter(value -> LocaleController.formatPluralString("Hours", value));
|
||||
hourPicker.setOnValueChangedListener(onValueChangeListener);
|
||||
numberPicker.setOnValueChangedListener(onValueChangeListener);
|
||||
|
||||
buttonTextView.setPadding(AndroidUtilities.dp(34), 0, AndroidUtilities.dp(34), 0);
|
||||
buttonTextView.setGravity(Gravity.CENTER);
|
||||
|
@ -3142,10 +3181,10 @@ public class AlertsCreator {
|
|||
buttonTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
|
||||
buttonTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
|
||||
buttonTextView.setBackgroundDrawable(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(4), datePickerColors.buttonBackgroundColor, datePickerColors.buttonBackgroundPressedColor));
|
||||
buttonTextView.setText(LocaleController.getString("SetTimeLimit", R.string.SetTimeLimit));
|
||||
buttonTextView.setText(LocaleController.getString("AutoDeleteConfirm", R.string.AutoDeleteConfirm));
|
||||
container.addView(buttonTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT | Gravity.BOTTOM, 16, 15, 16, 16));
|
||||
buttonTextView.setOnClickListener(v -> {
|
||||
int time = hourPicker.getValue() * 60 + dayPicker.getValue() * 60 * 24;
|
||||
int time = values[numberPicker.getValue()] * 60;
|
||||
datePickerDelegate.didSelectDate(true, time);
|
||||
builder.getDismissRunnable().run();
|
||||
});
|
||||
|
@ -3153,7 +3192,7 @@ public class AlertsCreator {
|
|||
builder.setCustomView(container);
|
||||
BottomSheet bottomSheet = builder.show();
|
||||
bottomSheet.setBackgroundColor(datePickerColors.backgroundColor);
|
||||
checkMuteForButton(dayPicker, hourPicker, buttonTextView, false);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
@ -3191,46 +3230,6 @@ public class AlertsCreator {
|
|||
}
|
||||
}
|
||||
|
||||
private static void checkAutoDeleteButton(NumberPicker dayPicker, NumberPicker hourPicker, NumberPicker minutePicker, TextView buttonTextView, boolean animated) {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
if (dayPicker.getValue() != 0) {
|
||||
stringBuilder.append(dayPicker.getValue()).append(LocaleController.getString("SecretChatTimerDays", R.string.SecretChatTimerDays));
|
||||
}
|
||||
if (hourPicker.getValue() != 0) {
|
||||
if (stringBuilder.length() > 0) {
|
||||
stringBuilder.append(" ");
|
||||
}
|
||||
stringBuilder.append(hourPicker.getValue()).append(LocaleController.getString("SecretChatTimerHours", R.string.SecretChatTimerHours));
|
||||
}
|
||||
if (minutePicker.getValue() != 0) {
|
||||
if (stringBuilder.length() > 0) {
|
||||
stringBuilder.append(" ");
|
||||
}
|
||||
stringBuilder.append(minutePicker.getValue() * 5).append(LocaleController.getString("SecretChatTimerMinutes", R.string.SecretChatTimerMinutes));
|
||||
}
|
||||
if (stringBuilder.length() == 0) {
|
||||
buttonTextView.setText(LocaleController.formatString("ChooseTimeForAutoDelete", R.string.ChooseTimeForAutoDelete));
|
||||
if (buttonTextView.isEnabled()) {
|
||||
buttonTextView.setEnabled(false);
|
||||
if (animated) {
|
||||
buttonTextView.animate().alpha(0.5f);
|
||||
} else {
|
||||
buttonTextView.setAlpha(0.5f);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
buttonTextView.setText(LocaleController.formatString("AutoDeleteAfter", R.string.AutoDeleteAfter, stringBuilder.toString()));
|
||||
if (!buttonTextView.isEnabled()) {
|
||||
buttonTextView.setEnabled(true);
|
||||
if (animated) {
|
||||
buttonTextView.animate().alpha(1f);
|
||||
} else {
|
||||
buttonTextView.setAlpha(1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkCalendarDate(long minDate, NumberPicker dayPicker, NumberPicker monthPicker, NumberPicker yearPicker) {
|
||||
int day = dayPicker.getValue();
|
||||
int month = monthPicker.getValue();
|
||||
|
|
|
@ -206,6 +206,9 @@ public class BotCommandsMenuView extends View {
|
|||
}
|
||||
|
||||
public void setMenuText(String menuText) {
|
||||
if (menuText == null) {
|
||||
menuText = LocaleController.getString(R.string.BotsMenuTitle);
|
||||
}
|
||||
this.menuText = menuText;
|
||||
menuTextLayout = null;
|
||||
requestLayout();
|
||||
|
|
|
@ -20,8 +20,10 @@ import android.graphics.drawable.Drawable;
|
|||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.webkit.GeolocationPermissions;
|
||||
import android.webkit.JavascriptInterface;
|
||||
import android.webkit.PermissionRequest;
|
||||
|
@ -31,14 +33,18 @@ import android.webkit.WebSettings;
|
|||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
import androidx.core.util.Consumer;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.telegram.messenger.AndroidUtilities;
|
||||
import org.telegram.messenger.ApplicationLoader;
|
||||
import org.telegram.messenger.FileLog;
|
||||
import org.telegram.messenger.ImageLocation;
|
||||
import org.telegram.messenger.ImageReceiver;
|
||||
|
@ -57,6 +63,7 @@ import org.telegram.ui.ActionBar.AlertDialog;
|
|||
import org.telegram.ui.ActionBar.Theme;
|
||||
import org.telegram.ui.Components.voip.CellFlickerDrawable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Arrays;
|
||||
|
@ -64,6 +71,7 @@ import java.util.List;
|
|||
import java.util.Objects;
|
||||
|
||||
public class BotWebViewContainer extends FrameLayout implements NotificationCenter.NotificationCenterDelegate {
|
||||
private final static boolean WEB_VIEW_CAN_GO_BACK = false;
|
||||
private final static String DURGER_KING_USERNAME = "DurgerKingBot";
|
||||
private final static int REQUEST_CODE_WEB_VIEW_FILE = 3000, REQUEST_CODE_WEB_PERMISSION = 4000;
|
||||
|
||||
|
@ -75,6 +83,9 @@ public class BotWebViewContainer extends FrameLayout implements NotificationCent
|
|||
private WebViewScrollListener webViewScrollListener;
|
||||
private Theme.ResourcesProvider resourcesProvider;
|
||||
|
||||
private TextView webViewNotAvailableText;
|
||||
private boolean webViewNotAvailable;
|
||||
|
||||
private CellFlickerDrawable flickerDrawable = new CellFlickerDrawable();
|
||||
private BackupImageView flickerView;
|
||||
private boolean isFlickeringCenter;
|
||||
|
@ -83,13 +94,12 @@ public class BotWebViewContainer extends FrameLayout implements NotificationCent
|
|||
|
||||
private ValueCallback<Uri[]> mFilePathCallback;
|
||||
|
||||
private int lastButtonColor = Theme.getColor(Theme.key_featuredStickers_addButton);
|
||||
private int lastButtonTextColor = Theme.getColor(Theme.key_featuredStickers_buttonText);
|
||||
private int lastButtonColor = getColor(Theme.key_featuredStickers_addButton);
|
||||
private int lastButtonTextColor = getColor(Theme.key_featuredStickers_buttonText);
|
||||
private String lastButtonText = "";
|
||||
private String buttonData;
|
||||
|
||||
private boolean isPageLoaded;
|
||||
private int viewPortOffset;
|
||||
private boolean lastExpanded;
|
||||
|
||||
private boolean hasUserPermissions;
|
||||
|
@ -98,7 +108,6 @@ public class BotWebViewContainer extends FrameLayout implements NotificationCent
|
|||
|
||||
private Activity parentActivity;
|
||||
|
||||
@SuppressLint({"SetJavaScriptEnabled", "AddJavascriptInterface"})
|
||||
public BotWebViewContainer(@NonNull Context context, Theme.ResourcesProvider resourcesProvider, int backgroundColor) {
|
||||
super(context);
|
||||
this.resourcesProvider = resourcesProvider;
|
||||
|
@ -139,11 +148,47 @@ public class BotWebViewContainer extends FrameLayout implements NotificationCent
|
|||
}
|
||||
}
|
||||
};
|
||||
flickerView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_windowBackgroundGray), PorterDuff.Mode.SRC_IN));
|
||||
flickerView.setColorFilter(new PorterDuffColorFilter(getColor(Theme.key_dialogSearchHint), PorterDuff.Mode.SRC_IN));
|
||||
flickerView.getImageReceiver().setAspectFit(true);
|
||||
addView(flickerView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP));
|
||||
|
||||
webView = new WebView(context) {
|
||||
webViewNotAvailableText = new TextView(context);
|
||||
webViewNotAvailableText.setText(LocaleController.getString(R.string.BotWebViewNotAvailablePlaceholder));
|
||||
webViewNotAvailableText.setTextColor(getColor(Theme.key_windowBackgroundWhiteGrayText));
|
||||
webViewNotAvailableText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
|
||||
webViewNotAvailableText.setGravity(Gravity.CENTER);
|
||||
webViewNotAvailableText.setVisibility(GONE);
|
||||
int padding = AndroidUtilities.dp(16);
|
||||
webViewNotAvailableText.setPadding(padding, padding, padding, padding);
|
||||
addView(webViewNotAvailableText, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER));
|
||||
|
||||
setFocusable(false);
|
||||
}
|
||||
|
||||
private void checkCreateWebView() {
|
||||
if (webView == null && !webViewNotAvailable) {
|
||||
try {
|
||||
setupWebView();
|
||||
} catch (Throwable t) {
|
||||
FileLog.e(t);
|
||||
|
||||
flickerView.setVisibility(GONE);
|
||||
webViewNotAvailable = true;
|
||||
webViewNotAvailableText.setVisibility(VISIBLE);
|
||||
if (webView != null) {
|
||||
removeView(webView);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint({"SetJavaScriptEnabled", "AddJavascriptInterface"})
|
||||
private void setupWebView() {
|
||||
if (webView != null) {
|
||||
webView.destroy();
|
||||
removeView(webView);
|
||||
}
|
||||
webView = new WebView(getContext()) {
|
||||
private int prevScrollX, prevScrollY;
|
||||
|
||||
@Override
|
||||
|
@ -172,7 +217,7 @@ public class BotWebViewContainer extends FrameLayout implements NotificationCent
|
|||
|
||||
@Override
|
||||
public boolean onCheckIsTextEditor() {
|
||||
return true;
|
||||
return BotWebViewContainer.this.isFocusable();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -180,12 +225,20 @@ public class BotWebViewContainer extends FrameLayout implements NotificationCent
|
|||
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY));
|
||||
}
|
||||
};
|
||||
webView.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite));
|
||||
webView.setBackgroundColor(getColor(Theme.key_windowBackgroundWhite));
|
||||
WebSettings settings = webView.getSettings();
|
||||
settings.setJavaScriptEnabled(true);
|
||||
settings.setGeolocationEnabled(true);
|
||||
settings.setDomStorageEnabled(true);
|
||||
settings.setDatabaseEnabled(true);
|
||||
|
||||
File databaseStorage = new File(ApplicationLoader.getFilesDirFixed(), "webview_database");
|
||||
if (databaseStorage.exists() && databaseStorage.isDirectory() || databaseStorage.mkdirs()) {
|
||||
settings.setDatabasePath(databaseStorage.getAbsolutePath());
|
||||
}
|
||||
GeolocationPermissions.getInstance().clearAll();
|
||||
|
||||
webView.setVerticalScrollBarEnabled(false);
|
||||
webView.setWebViewClient(new WebViewClient() {
|
||||
@Override
|
||||
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
||||
|
@ -197,19 +250,31 @@ public class BotWebViewContainer extends FrameLayout implements NotificationCent
|
|||
override = true;
|
||||
|
||||
if (WHITELISTED_SCHEMES.contains(uriNew.getScheme())) {
|
||||
new AlertDialog.Builder(context, resourcesProvider)
|
||||
.setTitle(LocaleController.getString(R.string.OpenUrlTitle))
|
||||
.setMessage(LocaleController.formatString(R.string.OpenUrlAlert2, uriNew.toString()))
|
||||
.setPositiveButton(LocaleController.getString(R.string.Open), (dialog, which) -> {
|
||||
boolean[] forceBrowser = {false};
|
||||
boolean internal = Browser.isInternalUri(uriNew, forceBrowser);
|
||||
Browser.openUrl(getContext(), uriNew, true, false);
|
||||
if (internal && delegate != null) {
|
||||
delegate.onCloseRequested();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(LocaleController.getString(R.string.Cancel), null)
|
||||
.show();
|
||||
boolean[] forceBrowser = {false};
|
||||
boolean internal = Browser.isInternalUri(uriNew, forceBrowser);
|
||||
|
||||
if (internal) {
|
||||
if (delegate != null) {
|
||||
setDescendantFocusability(FOCUS_BLOCK_DESCENDANTS);
|
||||
BotWebViewContainer.this.setFocusable(false);
|
||||
webView.setFocusable(false);
|
||||
webView.setDescendantFocusability(FOCUS_BLOCK_DESCENDANTS);
|
||||
webView.clearFocus();
|
||||
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
|
||||
|
||||
delegate.onCloseRequested(()-> Browser.openUrl(getContext(), uriNew, true, false));
|
||||
} else {
|
||||
Browser.openUrl(getContext(), uriNew, true, false);
|
||||
}
|
||||
} else {
|
||||
new AlertDialog.Builder(getContext(), resourcesProvider)
|
||||
.setTitle(LocaleController.getString(R.string.OpenUrlTitle))
|
||||
.setMessage(LocaleController.formatString(R.string.OpenUrlAlert2, uriNew.toString()))
|
||||
.setPositiveButton(LocaleController.getString(R.string.Open), (dialog, which) -> Browser.openUrl(getContext(), uriNew, true, false))
|
||||
.setNegativeButton(LocaleController.getString(R.string.Cancel), null)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
override = false;
|
||||
|
@ -375,6 +440,36 @@ public class BotWebViewContainer extends FrameLayout implements NotificationCent
|
|||
}
|
||||
}
|
||||
|
||||
public static int getMainButtonRippleColor(int buttonColor) {
|
||||
return ColorUtils.calculateLuminance(buttonColor) >= 0.3f ? 0x12000000 : 0x16FFFFFF;
|
||||
}
|
||||
|
||||
public static Drawable getMainButtonRippleDrawable(int buttonColor) {
|
||||
return Theme.createSelectorWithBackgroundDrawable(buttonColor, getMainButtonRippleColor(buttonColor));
|
||||
}
|
||||
|
||||
public void updateFlickerBackgroundColor(int backgroundColor) {
|
||||
flickerDrawable.setColors(backgroundColor, 0x99, 0xCC);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If this press was consumed
|
||||
*/
|
||||
public boolean onBackPressed() {
|
||||
if (!WEB_VIEW_CAN_GO_BACK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (webView == null) {
|
||||
return false;
|
||||
}
|
||||
if (webView.canGoBack()) {
|
||||
webView.goBack();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void setPageLoaded(String url) {
|
||||
if (isPageLoaded) {
|
||||
return;
|
||||
|
@ -393,6 +488,8 @@ public class BotWebViewContainer extends FrameLayout implements NotificationCent
|
|||
set.start();
|
||||
mUrl = url;
|
||||
isPageLoaded = true;
|
||||
BotWebViewContainer.this.setFocusable(true);
|
||||
delegate.onWebAppReady();
|
||||
}
|
||||
|
||||
public boolean hasUserPermissions() {
|
||||
|
@ -419,6 +516,10 @@ public class BotWebViewContainer extends FrameLayout implements NotificationCent
|
|||
}
|
||||
}
|
||||
|
||||
public boolean isPageLoaded() {
|
||||
return isPageLoaded;
|
||||
}
|
||||
|
||||
public void setParentActivity(Activity parentActivity) {
|
||||
this.parentActivity = parentActivity;
|
||||
}
|
||||
|
@ -467,11 +568,6 @@ public class BotWebViewContainer extends FrameLayout implements NotificationCent
|
|||
}
|
||||
}
|
||||
|
||||
public void setViewPortOffset(int viewPortOffset) {
|
||||
this.viewPortOffset = viewPortOffset;
|
||||
invalidateViewPortHeight(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
|
@ -487,6 +583,7 @@ public class BotWebViewContainer extends FrameLayout implements NotificationCent
|
|||
}
|
||||
|
||||
public void invalidateViewPortHeight(boolean isStable, boolean force) {
|
||||
invalidate();
|
||||
if (!isPageLoaded && !force) {
|
||||
return;
|
||||
}
|
||||
|
@ -498,7 +595,7 @@ public class BotWebViewContainer extends FrameLayout implements NotificationCent
|
|||
lastExpanded = swipeContainer.getSwipeOffsetY() == -swipeContainer.getOffsetY() + swipeContainer.getTopActionBarOffsetY();
|
||||
}
|
||||
|
||||
int viewPortHeight = (int) (swipeContainer.getMeasuredHeight() - swipeContainer.getOffsetY() - swipeContainer.getSwipeOffsetY() + swipeContainer.getTopActionBarOffsetY() + viewPortOffset);
|
||||
int viewPortHeight = (int) (swipeContainer.getMeasuredHeight() - swipeContainer.getOffsetY() - swipeContainer.getSwipeOffsetY() + swipeContainer.getTopActionBarOffsetY());
|
||||
try {
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("height", viewPortHeight / AndroidUtilities.density);
|
||||
|
@ -529,6 +626,14 @@ public class BotWebViewContainer extends FrameLayout implements NotificationCent
|
|||
invalidate();
|
||||
return draw;
|
||||
}
|
||||
if (child == webViewNotAvailableText) {
|
||||
canvas.save();
|
||||
View parent = (View) BotWebViewContainer.this.getParent();
|
||||
canvas.translate(0, (ActionBar.getCurrentActionBarHeight() - parent.getTranslationY()) / 2f);
|
||||
boolean draw = super.drawChild(canvas, child, drawingTime);
|
||||
canvas.restore();
|
||||
return draw;
|
||||
}
|
||||
return super.drawChild(canvas, child, drawingTime);
|
||||
}
|
||||
|
||||
|
@ -552,7 +657,7 @@ public class BotWebViewContainer extends FrameLayout implements NotificationCent
|
|||
if (user.username != null && Objects.equals(user.username, DURGER_KING_USERNAME)) {
|
||||
flickerView.setVisibility(VISIBLE);
|
||||
flickerView.setAlpha(1f);
|
||||
flickerView.setImageDrawable(SvgHelper.getDrawable(R.raw.durgerking_placeholder, Theme.getColor(Theme.key_windowBackgroundGray)));
|
||||
flickerView.setImageDrawable(SvgHelper.getDrawable(R.raw.durgerking_placeholder, getColor(Theme.key_windowBackgroundGray)));
|
||||
setupFlickerParams(false);
|
||||
return;
|
||||
}
|
||||
|
@ -616,11 +721,25 @@ public class BotWebViewContainer extends FrameLayout implements NotificationCent
|
|||
flickerView.requestLayout();
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
checkCreateWebView();
|
||||
|
||||
isPageLoaded = false;
|
||||
hasUserPermissions = false;
|
||||
if (webView != null) {
|
||||
webView.reload();
|
||||
}
|
||||
}
|
||||
|
||||
public void loadUrl(String url) {
|
||||
checkCreateWebView();
|
||||
|
||||
isPageLoaded = false;
|
||||
hasUserPermissions = false;
|
||||
mUrl = url;
|
||||
webView.loadUrl(url);
|
||||
if (webView != null) {
|
||||
webView.loadUrl(url);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -642,11 +761,18 @@ public class BotWebViewContainer extends FrameLayout implements NotificationCent
|
|||
}
|
||||
|
||||
public void destroyWebView() {
|
||||
webView.destroy();
|
||||
if (webView != null) {
|
||||
webView.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public void evaluateJs(String script) {
|
||||
checkCreateWebView();
|
||||
if (webView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
webView.evaluateJavascript(script, value -> {});
|
||||
} else {
|
||||
|
@ -661,7 +787,11 @@ public class BotWebViewContainer extends FrameLayout implements NotificationCent
|
|||
@Override
|
||||
public void didReceivedNotification(int id, int account, Object... args) {
|
||||
if (id == NotificationCenter.didSetNewTheme) {
|
||||
evaluateJs("window.Telegram.WebView.receiveEvent('theme_changed', {theme_params: " + buildThemeParams() + "});");
|
||||
if (webView != null) {
|
||||
webView.setBackgroundColor(getColor(Theme.key_windowBackgroundWhite));
|
||||
}
|
||||
flickerView.setColorFilter(new PorterDuffColorFilter(getColor(Theme.key_dialogSearchHint), PorterDuff.Mode.SRC_IN));
|
||||
notifyThemeChanged();
|
||||
} else if (id == NotificationCenter.onActivityResultReceived) {
|
||||
onActivityResult((int) args[0], (int) args[1], (Intent) args[2]);
|
||||
} else if (id == NotificationCenter.onRequestPermissionResultReceived) {
|
||||
|
@ -669,6 +799,10 @@ public class BotWebViewContainer extends FrameLayout implements NotificationCent
|
|||
}
|
||||
}
|
||||
|
||||
private void notifyThemeChanged() {
|
||||
evaluateJs("window.Telegram.WebView.receiveEvent('theme_changed', {theme_params: " + buildThemeParams() + "});");
|
||||
}
|
||||
|
||||
public void setWebViewScrollListener(WebViewScrollListener webViewScrollListener) {
|
||||
this.webViewScrollListener = webViewScrollListener;
|
||||
}
|
||||
|
@ -678,13 +812,21 @@ public class BotWebViewContainer extends FrameLayout implements NotificationCent
|
|||
}
|
||||
|
||||
private void onEventReceived(String eventType, String eventData) {
|
||||
if (webView == null || delegate == null) {
|
||||
return;
|
||||
}
|
||||
switch (eventType) {
|
||||
case "web_app_close": {
|
||||
delegate.onCloseRequested();
|
||||
delegate.onCloseRequested(null);
|
||||
break;
|
||||
}
|
||||
case "web_app_data_send": {
|
||||
delegate.onSendWebViewData(eventData);
|
||||
try {
|
||||
JSONObject jsonData = new JSONObject(eventData);
|
||||
delegate.onSendWebViewData(jsonData.optString("data"));
|
||||
} catch (JSONException e) {
|
||||
FileLog.e(e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "web_app_expand": {
|
||||
|
@ -696,6 +838,10 @@ public class BotWebViewContainer extends FrameLayout implements NotificationCent
|
|||
invalidateViewPortHeight(!hasSwipeInProgress, true);
|
||||
break;
|
||||
}
|
||||
case "web_app_request_theme": {
|
||||
notifyThemeChanged();
|
||||
break;
|
||||
}
|
||||
case "web_app_ready": {
|
||||
setPageLoaded(webView.getUrl());
|
||||
break;
|
||||
|
@ -740,11 +886,16 @@ public class BotWebViewContainer extends FrameLayout implements NotificationCent
|
|||
}
|
||||
}
|
||||
|
||||
private String formatColor(String colorKey) {
|
||||
private int getColor(String colorKey) {
|
||||
Integer color = resourcesProvider != null ? resourcesProvider.getColor(colorKey) : Theme.getColor(colorKey);
|
||||
if (color == null) {
|
||||
color = Theme.getColor(colorKey);
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
private String formatColor(String colorKey) {
|
||||
int color = getColor(colorKey);
|
||||
return "#" + hexFixed(Color.red(color)) + hexFixed(Color.green(color)) + hexFixed(Color.blue(color));
|
||||
}
|
||||
|
||||
|
@ -778,14 +929,14 @@ public class BotWebViewContainer extends FrameLayout implements NotificationCent
|
|||
/**
|
||||
* Called when WebView requests to close itself
|
||||
*/
|
||||
void onCloseRequested();
|
||||
void onCloseRequested(@Nullable Runnable callback);
|
||||
|
||||
/**
|
||||
* Called when WebView requests to send custom data
|
||||
*
|
||||
* @param data Custom data to send
|
||||
*/
|
||||
void onSendWebViewData(String data);
|
||||
default void onSendWebViewData(String data) {}
|
||||
|
||||
/**
|
||||
* Called when WebView requests to expand viewport
|
||||
|
@ -796,5 +947,10 @@ public class BotWebViewContainer extends FrameLayout implements NotificationCent
|
|||
* Setups main button
|
||||
*/
|
||||
void onSetupMainButton(boolean isVisible, boolean isActive, String text, int color, int textColor, boolean isProgressVisible);
|
||||
|
||||
/**
|
||||
* Called when WebView is ready (Called web_app_ready or page load finished)
|
||||
*/
|
||||
default void onWebAppReady() {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,6 @@ import org.telegram.messenger.MessageObject;
|
|||
import org.telegram.messenger.MessagesController;
|
||||
import org.telegram.messenger.NotificationCenter;
|
||||
import org.telegram.messenger.R;
|
||||
import org.telegram.messenger.Utilities;
|
||||
import org.telegram.tgnet.ConnectionsManager;
|
||||
import org.telegram.tgnet.TLRPC;
|
||||
import org.telegram.ui.ActionBar.ActionBar;
|
||||
|
@ -45,21 +44,7 @@ public class BotWebViewMenuContainer extends FrameLayout implements Notification
|
|||
private final static SimpleFloatPropertyCompat<BotWebViewMenuContainer> ACTION_BAR_TRANSITION_PROGRESS_VALUE = new SimpleFloatPropertyCompat<BotWebViewMenuContainer>("actionBarTransitionProgress", obj -> obj.actionBarTransitionProgress, (obj, value) -> {
|
||||
obj.actionBarTransitionProgress = value;
|
||||
obj.invalidate();
|
||||
|
||||
int subtitleColor = ColorUtils.blendARGB(obj.getColor(Theme.key_actionBarDefaultSubtitle), obj.getColor(Theme.key_windowBackgroundWhiteGrayText), value);
|
||||
ChatActivity chatActivity = obj.parentEnterView.getParentFragment();
|
||||
ActionBar actionBar = chatActivity.getActionBar();
|
||||
actionBar.setBackgroundColor(ColorUtils.blendARGB(obj.getColor(Theme.key_actionBarDefault), obj.getColor(Theme.key_windowBackgroundWhite), value));
|
||||
actionBar.setItemsColor(ColorUtils.blendARGB(obj.getColor(Theme.key_actionBarDefaultIcon), obj.getColor(Theme.key_windowBackgroundWhiteBlackText), value), false);
|
||||
actionBar.setItemsBackgroundColor(ColorUtils.blendARGB(obj.getColor(Theme.key_actionBarDefaultSelector), obj.getColor(Theme.key_actionBarWhiteSelector), value), false);
|
||||
actionBar.setSubtitleColor(subtitleColor);
|
||||
|
||||
ChatAvatarContainer chatAvatarContainer = chatActivity.getAvatarContainer();
|
||||
chatAvatarContainer.getTitleTextView().setTextColor(ColorUtils.blendARGB(obj.getColor(Theme.key_actionBarDefaultTitle), obj.getColor(Theme.key_windowBackgroundWhiteBlackText), value));
|
||||
chatAvatarContainer.getSubtitleTextView().setTextColor(subtitleColor);
|
||||
chatAvatarContainer.setOverrideSubtitleColor(value == 0 ? null : subtitleColor);
|
||||
|
||||
obj.updateLightStatusBar();
|
||||
obj.invalidateActionBar();
|
||||
}).setMultiplier(100f);
|
||||
|
||||
private float actionBarTransitionProgress;
|
||||
|
@ -133,33 +118,16 @@ public class BotWebViewMenuContainer extends FrameLayout implements Notification
|
|||
actionBarOnItemClick = actionBar.getActionBarMenuOnItemClick();
|
||||
|
||||
webViewContainer = new BotWebViewContainer(context, parentEnterView.getParentFragment().getResourceProvider(), getColor(Theme.key_windowBackgroundWhite));
|
||||
webViewContainer.setViewPortOffset(-AndroidUtilities.dp(5));
|
||||
webViewContainer.setDelegate(webViewDelegate = new BotWebViewContainer.Delegate() {
|
||||
private boolean sentWebViewData;
|
||||
|
||||
@Override
|
||||
public void onCloseRequested() {
|
||||
dismiss();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSendWebViewData(String data) {
|
||||
if (sentWebViewData) {
|
||||
return;
|
||||
}
|
||||
sentWebViewData = true;
|
||||
|
||||
TLRPC.TL_messages_sendWebViewData sendWebViewData = new TLRPC.TL_messages_sendWebViewData();
|
||||
sendWebViewData.bot = MessagesController.getInstance(currentAccount).getInputUser(botId);
|
||||
sendWebViewData.random_id = Utilities.random.nextLong();
|
||||
sendWebViewData.button_text = "Menu";
|
||||
sendWebViewData.data = data;
|
||||
ConnectionsManager.getInstance(currentAccount).sendRequest(sendWebViewData, (response, error) -> AndroidUtilities.runOnUIThread(()-> dismiss()));
|
||||
public void onCloseRequested(Runnable callback) {
|
||||
dismiss(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWebAppExpand() {
|
||||
if (System.currentTimeMillis() - lastSwipeTime <= 1000 || swipeContainer.isSwipeInProgress()) {
|
||||
if (/* System.currentTimeMillis() - lastSwipeTime <= 1000 || */ swipeContainer.isSwipeInProgress()) {
|
||||
return;
|
||||
}
|
||||
swipeContainer.stickTo(-swipeContainer.getOffsetY() + swipeContainer.getTopActionBarOffsetY());
|
||||
|
@ -181,7 +149,6 @@ public class BotWebViewMenuContainer extends FrameLayout implements Notification
|
|||
linePaint.setStrokeCap(Paint.Cap.ROUND);
|
||||
|
||||
dimPaint.setColor(0x40000000);
|
||||
backgroundPaint.setColor(getColor(Theme.key_windowBackgroundWhite));
|
||||
|
||||
swipeContainer = new ChatAttachAlertBotWebViewLayout.WebViewSwipeContainer(context) {
|
||||
@Override
|
||||
|
@ -207,7 +174,7 @@ public class BotWebViewMenuContainer extends FrameLayout implements Notification
|
|||
if (AndroidUtilities.isTablet() && !AndroidUtilities.isInMultiwindow && !AndroidUtilities.isSmallTablet()) {
|
||||
widthMeasureSpec = MeasureSpec.makeMeasureSpec((int) (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.8f), MeasureSpec.EXACTLY);
|
||||
}
|
||||
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec) - ActionBar.getCurrentActionBarHeight() - AndroidUtilities.statusBarHeight + AndroidUtilities.dp(24), MeasureSpec.EXACTLY));
|
||||
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec) - ActionBar.getCurrentActionBarHeight() - AndroidUtilities.statusBarHeight + AndroidUtilities.dp(24) - AndroidUtilities.dp(5), MeasureSpec.EXACTLY));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -218,7 +185,6 @@ public class BotWebViewMenuContainer extends FrameLayout implements Notification
|
|||
super.requestLayout();
|
||||
}
|
||||
};
|
||||
swipeContainer.setWebView(webViewContainer.getWebView());
|
||||
swipeContainer.setScrollListener(() -> {
|
||||
if (swipeContainer.getSwipeOffsetY() > 0) {
|
||||
dimPaint.setAlpha((int) (0x40 * (1f - Math.min(swipeContainer.getSwipeOffsetY(), swipeContainer.getHeight()) / (float)swipeContainer.getHeight())));
|
||||
|
@ -265,6 +231,27 @@ public class BotWebViewMenuContainer extends FrameLayout implements Notification
|
|||
setWillNotDraw(false);
|
||||
}
|
||||
|
||||
private void invalidateActionBar() {
|
||||
int subtitleColor = ColorUtils.blendARGB(getColor(Theme.key_actionBarDefaultSubtitle), getColor(Theme.key_windowBackgroundWhiteGrayText), actionBarTransitionProgress);
|
||||
ChatActivity chatActivity = parentEnterView.getParentFragment();
|
||||
ActionBar actionBar = chatActivity.getActionBar();
|
||||
actionBar.setBackgroundColor(ColorUtils.blendARGB(getColor(Theme.key_actionBarDefault), getColor(Theme.key_windowBackgroundWhite), actionBarTransitionProgress));
|
||||
actionBar.setItemsColor(ColorUtils.blendARGB(getColor(Theme.key_actionBarDefaultIcon), getColor(Theme.key_windowBackgroundWhiteBlackText), actionBarTransitionProgress), false);
|
||||
actionBar.setItemsBackgroundColor(ColorUtils.blendARGB(getColor(Theme.key_actionBarDefaultSelector), getColor(Theme.key_actionBarWhiteSelector), actionBarTransitionProgress), false);
|
||||
actionBar.setSubtitleColor(subtitleColor);
|
||||
|
||||
ChatAvatarContainer chatAvatarContainer = chatActivity.getAvatarContainer();
|
||||
chatAvatarContainer.getTitleTextView().setTextColor(ColorUtils.blendARGB(getColor(Theme.key_actionBarDefaultTitle), getColor(Theme.key_windowBackgroundWhiteBlackText), actionBarTransitionProgress));
|
||||
chatAvatarContainer.getSubtitleTextView().setTextColor(subtitleColor);
|
||||
chatAvatarContainer.setOverrideSubtitleColor(actionBarTransitionProgress == 0 ? null : subtitleColor);
|
||||
|
||||
updateLightStatusBar();
|
||||
}
|
||||
|
||||
public boolean onBackPressed() {
|
||||
return webViewContainer.onBackPressed();
|
||||
}
|
||||
|
||||
private void animateBotButton(boolean isVisible) {
|
||||
ChatActivityBotWebViewButton botWebViewButton = parentEnterView.getBotWebViewButton();
|
||||
if (botWebViewButtonAnimator != null) {
|
||||
|
@ -316,7 +303,7 @@ public class BotWebViewMenuContainer extends FrameLayout implements Notification
|
|||
chatAvatarContainer.getAvatarImageView().setClickable(value == 0);
|
||||
|
||||
ActionBar actionBar = chatActivity.getActionBar();
|
||||
if (value == 100) {
|
||||
if (value == 100 && parentEnterView.hasBotWebView()) {
|
||||
chatActivity.showHeaderItem(false);
|
||||
botMenuItem.setVisibility(VISIBLE);
|
||||
actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() {
|
||||
|
@ -325,11 +312,19 @@ public class BotWebViewMenuContainer extends FrameLayout implements Notification
|
|||
if (id == -1) {
|
||||
dismiss();
|
||||
} else if (id == R.id.menu_reload_page) {
|
||||
webViewContainer.getWebView().animate().cancel();
|
||||
webViewContainer.getWebView().animate().alpha(0).start();
|
||||
if (webViewContainer.getWebView() != null) {
|
||||
webViewContainer.getWebView().animate().cancel();
|
||||
webViewContainer.getWebView().animate().alpha(0).start();
|
||||
}
|
||||
|
||||
isLoaded = false;
|
||||
loadWebView();
|
||||
progressView.setLoadProgress(0);
|
||||
progressView.setAlpha(1f);
|
||||
progressView.setVisibility(VISIBLE);
|
||||
|
||||
webViewContainer.setBotUser(MessagesController.getInstance(currentAccount).getUser(botId));
|
||||
webViewContainer.loadFlicker(currentAccount, botId);
|
||||
webViewContainer.reload();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -341,6 +336,7 @@ public class BotWebViewMenuContainer extends FrameLayout implements Notification
|
|||
});
|
||||
}
|
||||
NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.webViewResultSent);
|
||||
NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.didSetNewTheme);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -351,7 +347,9 @@ public class BotWebViewMenuContainer extends FrameLayout implements Notification
|
|||
springAnimation.cancel();
|
||||
springAnimation = null;
|
||||
}
|
||||
actionBarTransitionProgress = 0f;
|
||||
NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.webViewResultSent);
|
||||
NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.didSetNewTheme);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -377,24 +375,30 @@ public class BotWebViewMenuContainer extends FrameLayout implements Notification
|
|||
webViewScrollAnimator = null;
|
||||
}
|
||||
|
||||
int fromY = webViewContainer.getWebView().getScrollY();
|
||||
int toY = fromY + (oldh - contentHeight);
|
||||
webViewScrollAnimator = ValueAnimator.ofInt(fromY, toY).setDuration(250);
|
||||
webViewScrollAnimator.setInterpolator(ChatListItemAnimator.DEFAULT_INTERPOLATOR);
|
||||
webViewScrollAnimator.addUpdateListener(animation -> {
|
||||
int val = (int) animation.getAnimatedValue();
|
||||
webViewContainer.getWebView().setScrollY(val);
|
||||
});
|
||||
webViewScrollAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
webViewContainer.getWebView().setScrollY(toY);
|
||||
if (animation == webViewScrollAnimator) {
|
||||
webViewScrollAnimator = null;
|
||||
if (webViewContainer.getWebView() != null) {
|
||||
int fromY = webViewContainer.getWebView().getScrollY();
|
||||
int toY = fromY + (oldh - contentHeight);
|
||||
webViewScrollAnimator = ValueAnimator.ofInt(fromY, toY).setDuration(250);
|
||||
webViewScrollAnimator.setInterpolator(ChatListItemAnimator.DEFAULT_INTERPOLATOR);
|
||||
webViewScrollAnimator.addUpdateListener(animation -> {
|
||||
int val = (int) animation.getAnimatedValue();
|
||||
if (webViewContainer.getWebView() != null) {
|
||||
webViewContainer.getWebView().setScrollY(val);
|
||||
}
|
||||
}
|
||||
});
|
||||
webViewScrollAnimator.start();
|
||||
});
|
||||
webViewScrollAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (webViewContainer.getWebView() != null) {
|
||||
webViewContainer.getWebView().setScrollY(toY);
|
||||
}
|
||||
if (animation == webViewScrollAnimator) {
|
||||
webViewScrollAnimator = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
webViewScrollAnimator.start();
|
||||
}
|
||||
}
|
||||
|
||||
public void onPanTransitionEnd() {
|
||||
|
@ -426,6 +430,7 @@ public class BotWebViewMenuContainer extends FrameLayout implements Notification
|
|||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
|
||||
backgroundPaint.setColor(getColor(Theme.key_windowBackgroundWhite));
|
||||
AndroidUtilities.rectTmp.set(0, 0, getWidth(), getHeight());
|
||||
canvas.drawRect(AndroidUtilities.rectTmp, dimPaint);
|
||||
|
||||
|
@ -437,7 +442,7 @@ public class BotWebViewMenuContainer extends FrameLayout implements Notification
|
|||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN && event.getY() <= AndroidUtilities.lerp(swipeContainer.getTranslationY(), 0, actionBarTransitionProgress)) {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN && event.getY() <= AndroidUtilities.lerp(swipeContainer.getTranslationY() + AndroidUtilities.dp(24), 0, actionBarTransitionProgress)) {
|
||||
dismiss();
|
||||
return true;
|
||||
}
|
||||
|
@ -448,7 +453,7 @@ public class BotWebViewMenuContainer extends FrameLayout implements Notification
|
|||
public void draw(Canvas canvas) {
|
||||
super.draw(canvas);
|
||||
|
||||
linePaint.setColor(Theme.getColor(Theme.key_dialogGrayLine));
|
||||
linePaint.setColor(getColor(Theme.key_dialogGrayLine));
|
||||
linePaint.setAlpha((int) (linePaint.getAlpha() * (1f - Math.min(0.5f, actionBarTransitionProgress) / 0.5f)));
|
||||
|
||||
canvas.save();
|
||||
|
@ -499,7 +504,10 @@ public class BotWebViewMenuContainer extends FrameLayout implements Notification
|
|||
.setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
|
||||
.setStiffness(500.0f)
|
||||
)
|
||||
.addEndListener((animation, canceled, value, velocity) -> webViewContainer.restoreButtonData())
|
||||
.addEndListener((animation, canceled, value, velocity) -> {
|
||||
webViewContainer.restoreButtonData();
|
||||
webViewContainer.invalidateViewPortHeight(true);
|
||||
})
|
||||
.start();
|
||||
}
|
||||
});
|
||||
|
@ -544,6 +552,7 @@ public class BotWebViewMenuContainer extends FrameLayout implements Notification
|
|||
TLRPC.TL_webViewResultUrl resultUrl = (TLRPC.TL_webViewResultUrl) response;
|
||||
queryId = resultUrl.query_id;
|
||||
webViewContainer.loadUrl(resultUrl.url);
|
||||
swipeContainer.setWebView(webViewContainer.getWebView());
|
||||
|
||||
AndroidUtilities.runOnUIThread(pollRunnable, POLL_PERIOD);
|
||||
}
|
||||
|
@ -594,7 +603,6 @@ public class BotWebViewMenuContainer extends FrameLayout implements Notification
|
|||
swipeContainer.removeView(webViewContainer);
|
||||
|
||||
webViewContainer = new BotWebViewContainer(getContext(), parentEnterView.getParentFragment().getResourceProvider(), getColor(Theme.key_windowBackgroundWhite));
|
||||
webViewContainer.setViewPortOffset(-AndroidUtilities.dp(5));
|
||||
webViewContainer.setDelegate(webViewDelegate);
|
||||
webViewContainer.setWebViewProgressListener(progress -> {
|
||||
progressView.setLoadProgressAnimated(progress);
|
||||
|
@ -612,7 +620,6 @@ public class BotWebViewMenuContainer extends FrameLayout implements Notification
|
|||
}
|
||||
});
|
||||
swipeContainer.addView(webViewContainer);
|
||||
swipeContainer.setWebView(webViewContainer.getWebView());
|
||||
isLoaded = false;
|
||||
|
||||
AndroidUtilities.cancelRunOnUIThread(pollRunnable);
|
||||
|
@ -652,6 +659,11 @@ public class BotWebViewMenuContainer extends FrameLayout implements Notification
|
|||
if (this.queryId == queryId) {
|
||||
dismiss();
|
||||
}
|
||||
} else if (id == NotificationCenter.didSetNewTheme) {
|
||||
webViewContainer.updateFlickerBackgroundColor(getColor(Theme.key_windowBackgroundWhite));
|
||||
invalidate();
|
||||
invalidateActionBar();
|
||||
AndroidUtilities.runOnUIThread(this::invalidateActionBar, 300);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import android.widget.TextView;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
import androidx.core.math.MathUtils;
|
||||
import androidx.dynamicanimation.animation.SpringAnimation;
|
||||
import androidx.dynamicanimation.animation.SpringForce;
|
||||
|
||||
|
@ -161,19 +162,17 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi
|
|||
}
|
||||
};
|
||||
webViewContainer = new BotWebViewContainer(context, resourcesProvider, getColor(Theme.key_windowBackgroundWhite));
|
||||
|
||||
webViewContainer.getWebView().setVerticalScrollBarEnabled(false);
|
||||
webViewContainer.setDelegate(new BotWebViewContainer.Delegate() {
|
||||
private boolean sentWebViewData;
|
||||
|
||||
@Override
|
||||
public void onCloseRequested() {
|
||||
dismiss();
|
||||
public void onCloseRequested(Runnable callback) {
|
||||
dismiss(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSendWebViewData(String data) {
|
||||
if (sentWebViewData) {
|
||||
if (queryId != 0 || sentWebViewData) {
|
||||
return;
|
||||
}
|
||||
sentWebViewData = true;
|
||||
|
@ -188,7 +187,7 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi
|
|||
|
||||
@Override
|
||||
public void onWebAppExpand() {
|
||||
if (System.currentTimeMillis() - lastSwipeTime <= 1000 || swipeContainer.isSwipeInProgress()) {
|
||||
if (/* System.currentTimeMillis() - lastSwipeTime <= 1000 || */ swipeContainer.isSwipeInProgress()) {
|
||||
return;
|
||||
}
|
||||
swipeContainer.stickTo(-swipeContainer.getOffsetY() + swipeContainer.getTopActionBarOffsetY());
|
||||
|
@ -199,7 +198,7 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi
|
|||
mainButton.setClickable(isActive);
|
||||
mainButton.setText(text);
|
||||
mainButton.setTextColor(textColor);
|
||||
mainButton.setBackground(Theme.createSelectorWithBackgroundDrawable(color, Theme.getColor(Theme.key_listSelector)));
|
||||
mainButton.setBackground(BotWebViewContainer.getMainButtonRippleDrawable(color));
|
||||
if (isVisible != mainButtonWasVisible) {
|
||||
mainButtonWasVisible = isVisible;
|
||||
mainButton.animate().cancel();
|
||||
|
@ -245,7 +244,6 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi
|
|||
linePaint.setStrokeWidth(AndroidUtilities.dp(4));
|
||||
linePaint.setStrokeCap(Paint.Cap.ROUND);
|
||||
|
||||
backgroundPaint.setColor(getColor(Theme.key_windowBackgroundWhite));
|
||||
dimPaint.setColor(0x40000000);
|
||||
frameLayout = new SizeNotifierFrameLayout(context) {
|
||||
{
|
||||
|
@ -256,6 +254,7 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi
|
|||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
|
||||
backgroundPaint.setColor(getColor(Theme.key_windowBackgroundWhite));
|
||||
AndroidUtilities.rectTmp.set(0, 0, getWidth(), getHeight());
|
||||
canvas.drawRect(AndroidUtilities.rectTmp, dimPaint);
|
||||
|
||||
|
@ -287,7 +286,7 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi
|
|||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN && event.getY() < AndroidUtilities.lerp(swipeContainer.getTranslationY(), 0, actionBarTransitionProgress)) {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN && event.getY() <= AndroidUtilities.lerp(swipeContainer.getTranslationY() + AndroidUtilities.dp(24), 0, actionBarTransitionProgress)) {
|
||||
dismiss();
|
||||
return true;
|
||||
}
|
||||
|
@ -327,10 +326,8 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi
|
|||
|
||||
actionBar = new ActionBar(context, resourcesProvider);
|
||||
actionBar.setBackgroundColor(Color.TRANSPARENT);
|
||||
actionBar.setTitleColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText));
|
||||
actionBar.setItemsColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText), false);
|
||||
actionBar.setItemsBackgroundColor(Theme.getColor(Theme.key_actionBarWhiteSelector), false);
|
||||
actionBar.setBackButtonImage(R.drawable.ic_ab_back);
|
||||
updateActionBarColors();
|
||||
actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() {
|
||||
@Override
|
||||
public void onItemClick(int id) {
|
||||
|
@ -360,10 +357,9 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi
|
|||
});
|
||||
|
||||
swipeContainer.addView(webViewContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
|
||||
swipeContainer.setWebView(webViewContainer.getWebView());
|
||||
swipeContainer.setScrollListener(()->{
|
||||
if (swipeContainer.getSwipeOffsetY() > 0) {
|
||||
dimPaint.setAlpha((int) (0x40 * (1f - swipeContainer.getSwipeOffsetY() / (float)swipeContainer.getHeight())));
|
||||
dimPaint.setAlpha((int) (0x40 * (1f - MathUtils.clamp(swipeContainer.getSwipeOffsetY() / (float)swipeContainer.getHeight(), 0, 1))));
|
||||
} else {
|
||||
dimPaint.setAlpha(0x40);
|
||||
}
|
||||
|
@ -394,6 +390,16 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi
|
|||
this.parentActivity = parentActivity;
|
||||
}
|
||||
|
||||
private void updateActionBarColors() {
|
||||
actionBar.setTitleColor(getColor(Theme.key_windowBackgroundWhiteBlackText));
|
||||
actionBar.setItemsColor(getColor(Theme.key_windowBackgroundWhiteBlackText), false);
|
||||
actionBar.setItemsBackgroundColor(getColor(Theme.key_actionBarWhiteSelector), false);
|
||||
actionBar.setPopupBackgroundColor(getColor(Theme.key_actionBarDefaultSubmenuBackground), false);
|
||||
actionBar.setPopupItemsColor(getColor(Theme.key_actionBarDefaultSubmenuItem), false, false);
|
||||
actionBar.setPopupItemsColor(getColor(Theme.key_actionBarDefaultSubmenuItemIcon), true, false);
|
||||
actionBar.setPopupItemsSelectorColor(getColor(Theme.key_dialogButtonSelector), false);
|
||||
}
|
||||
|
||||
private void updateLightStatusBar() {
|
||||
int color = Theme.getColor(Theme.key_windowBackgroundWhite, null, true);
|
||||
boolean lightStatusBar = ColorUtils.calculateLuminance(color) >= 0.9 && actionBarTransitionProgress >= 0.85f;
|
||||
|
@ -454,6 +460,8 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi
|
|||
int color = Theme.getColor(Theme.key_windowBackgroundWhite, null, true);
|
||||
AndroidUtilities.setLightNavigationBar(window, ColorUtils.calculateLuminance(color) >= 0.9);
|
||||
}
|
||||
|
||||
NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.didSetNewTheme);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -507,9 +515,18 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi
|
|||
}
|
||||
dismiss();
|
||||
} else if (id == R.id.menu_reload_page) {
|
||||
webViewContainer.getWebView().animate().cancel();
|
||||
webViewContainer.getWebView().animate().alpha(0).start();
|
||||
requestWebView(currentAccount, peerId, botId, buttonText, buttonUrl, simple, replyToMsgId, silent);
|
||||
if (webViewContainer.getWebView() != null) {
|
||||
webViewContainer.getWebView().animate().cancel();
|
||||
webViewContainer.getWebView().animate().alpha(0).start();
|
||||
}
|
||||
|
||||
progressView.setLoadProgress(0);
|
||||
progressView.setAlpha(1f);
|
||||
progressView.setVisibility(View.VISIBLE);
|
||||
|
||||
webViewContainer.setBotUser(MessagesController.getInstance(currentAccount).getUser(botId));
|
||||
webViewContainer.loadFlicker(currentAccount, botId);
|
||||
webViewContainer.reload();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -545,7 +562,9 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi
|
|||
ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(()->{
|
||||
if (response instanceof TLRPC.TL_simpleWebViewResultUrl) {
|
||||
TLRPC.TL_simpleWebViewResultUrl resultUrl = (TLRPC.TL_simpleWebViewResultUrl) response;
|
||||
queryId = 0;
|
||||
webViewContainer.loadUrl(resultUrl.url);
|
||||
swipeContainer.setWebView(webViewContainer.getWebView());
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
|
@ -573,6 +592,7 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi
|
|||
TLRPC.TL_webViewResultUrl resultUrl = (TLRPC.TL_webViewResultUrl) response;
|
||||
queryId = resultUrl.query_id;
|
||||
webViewContainer.loadUrl(resultUrl.url);
|
||||
swipeContainer.setWebView(webViewContainer.getWebView());
|
||||
|
||||
AndroidUtilities.runOnUIThread(pollRunnable, POLL_PERIOD);
|
||||
}
|
||||
|
@ -612,8 +632,20 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi
|
|||
super.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (webViewContainer.onBackPressed()) {
|
||||
return;
|
||||
}
|
||||
super.onBackPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dismiss() {
|
||||
dismiss(null);
|
||||
}
|
||||
|
||||
public void dismiss(Runnable callback) {
|
||||
if (dismissed) {
|
||||
return;
|
||||
}
|
||||
|
@ -622,8 +654,14 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi
|
|||
|
||||
webViewContainer.destroyWebView();
|
||||
NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.webViewResultSent);
|
||||
NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.didSetNewTheme);
|
||||
|
||||
swipeContainer.stickTo(swipeContainer.getHeight() + frameLayout.measureKeyboardHeight(), super::dismiss);
|
||||
swipeContainer.stickTo(swipeContainer.getHeight() + frameLayout.measureKeyboardHeight(), ()->{
|
||||
super.dismiss();
|
||||
if (callback != null) {
|
||||
callback.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -634,6 +672,11 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi
|
|||
if (this.queryId == queryId) {
|
||||
dismiss();
|
||||
}
|
||||
} else if (id == NotificationCenter.didSetNewTheme) {
|
||||
frameLayout.invalidate();
|
||||
webViewContainer.updateFlickerBackgroundColor(getColor(Theme.key_windowBackgroundWhite));
|
||||
updateActionBarColors();
|
||||
updateLightStatusBar();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,18 +42,18 @@ public class ChatActivityBotWebViewButton extends FrameLayout {
|
|||
textView.setAlpha(0f);
|
||||
textView.setGravity(Gravity.CENTER);
|
||||
textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
|
||||
addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT, 0, 0, 0, 4));
|
||||
addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT, 0, 0, 0, 0));
|
||||
|
||||
progressView = new RadialProgressView(context);
|
||||
progressView.setSize(AndroidUtilities.dp(18));
|
||||
progressView.setAlpha(0f);
|
||||
progressView.setScaleX(0);
|
||||
progressView.setScaleY(0);
|
||||
addView(progressView, LayoutHelper.createFrame(28, 28, Gravity.RIGHT | Gravity.CENTER_VERTICAL, 0, 0, 12, 4));
|
||||
addView(progressView, LayoutHelper.createFrame(28, 28, Gravity.RIGHT | Gravity.CENTER_VERTICAL, 0, 0, 12, 0));
|
||||
|
||||
rippleView = new View(context);
|
||||
rippleView.setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_listSelector), 2));
|
||||
addView(rippleView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT, 0, -4, 0, 0));
|
||||
rippleView.setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_featuredStickers_addButtonPressed), 2));
|
||||
addView(rippleView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT, 0, 0, 0, 0));
|
||||
|
||||
setWillNotDraw(false);
|
||||
}
|
||||
|
@ -70,6 +70,8 @@ public class ChatActivityBotWebViewButton extends FrameLayout {
|
|||
textView.setTextColor(textColor);
|
||||
buttonColor = color;
|
||||
|
||||
rippleView.setBackground(Theme.createSelectorDrawable(BotWebViewContainer.getMainButtonRippleColor(buttonColor), 2));
|
||||
|
||||
progressView.setProgressColor(textColor);
|
||||
if (progressWasVisible != isProgressVisible) {
|
||||
progressWasVisible = isProgressVisible;
|
||||
|
@ -91,6 +93,7 @@ public class ChatActivityBotWebViewButton extends FrameLayout {
|
|||
}
|
||||
}).start();
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setProgress(float progress) {
|
||||
|
@ -110,9 +113,10 @@ public class ChatActivityBotWebViewButton extends FrameLayout {
|
|||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
canvas.save();
|
||||
float menuY = (getHeight() - AndroidUtilities.dp(32)) / 2f;
|
||||
float offset = Math.max(getWidth() - menuButtonWidth - AndroidUtilities.dp(4), getHeight()) * progress;
|
||||
float rad = AndroidUtilities.dp(16) + offset;
|
||||
AndroidUtilities.rectTmp.set(AndroidUtilities.dp(14) - offset, AndroidUtilities.dp(8) - offset, AndroidUtilities.dp(6) + menuButtonWidth + offset, getHeight() - AndroidUtilities.dp(12) + offset);
|
||||
AndroidUtilities.rectTmp.set(AndroidUtilities.dp(14) - offset, menuY + AndroidUtilities.dp(4) - offset, AndroidUtilities.dp(6) + menuButtonWidth + offset, getHeight() - AndroidUtilities.dp(12) + offset);
|
||||
|
||||
path.rewind();
|
||||
path.addRoundRect(AndroidUtilities.rectTmp, rad, rad, Path.Direction.CW);
|
||||
|
@ -120,7 +124,7 @@ public class ChatActivityBotWebViewButton extends FrameLayout {
|
|||
canvas.drawColor(backgroundColor);
|
||||
|
||||
canvas.saveLayerAlpha(AndroidUtilities.rectTmp, (int) ((1f - Math.min(0.5f, progress) / 0.5f) * 0xFF), Canvas.ALL_SAVE_FLAG);
|
||||
canvas.translate(AndroidUtilities.dp(10), AndroidUtilities.dp(4));
|
||||
canvas.translate(AndroidUtilities.dp(10), menuY);
|
||||
if (menuButton != null) {
|
||||
menuButton.setDrawBackgroundDrawable(false);
|
||||
menuButton.draw(canvas);
|
||||
|
|
|
@ -1720,6 +1720,7 @@ public class ChatActivityEnterView extends BlurredFrameLayout implements Notific
|
|||
NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.messageReceivedByServer);
|
||||
NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.sendingMessagesChanged);
|
||||
NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.audioRecordTooShort);
|
||||
NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.updateBotMenuButton);
|
||||
NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.emojiLoaded);
|
||||
|
||||
parentActivity = context;
|
||||
|
@ -4180,6 +4181,7 @@ public class ChatActivityEnterView extends BlurredFrameLayout implements Notific
|
|||
NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.messageReceivedByServer);
|
||||
NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.sendingMessagesChanged);
|
||||
NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.audioRecordTooShort);
|
||||
NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.updateBotMenuButton);
|
||||
NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.emojiLoaded);
|
||||
if (emojiView != null) {
|
||||
emojiView.onDestroy();
|
||||
|
@ -6882,7 +6884,11 @@ public class ChatActivityEnterView extends BlurredFrameLayout implements Notific
|
|||
}
|
||||
}
|
||||
|
||||
private boolean hasBotWebView() {
|
||||
public boolean onBotWebViewBackPressed() {
|
||||
return botWebViewMenuContainer != null && botWebViewMenuContainer.onBackPressed();
|
||||
}
|
||||
|
||||
public boolean hasBotWebView() {
|
||||
return botMenuButtonType == BotMenuButtonType.WEB_VIEW;
|
||||
}
|
||||
|
||||
|
@ -8356,6 +8362,24 @@ public class ChatActivityEnterView extends BlurredFrameLayout implements Notific
|
|||
}
|
||||
} else if (id == NotificationCenter.audioRecordTooShort) {
|
||||
updateRecordIntefrace(RECORD_STATE_CANCEL_BY_TIME);
|
||||
} else if (id == NotificationCenter.updateBotMenuButton) {
|
||||
long botId = (long) args[0];
|
||||
TLRPC.BotMenuButton botMenuButton = (TLRPC.BotMenuButton) args[1];
|
||||
|
||||
if (botId == dialog_id) {
|
||||
if (botMenuButton instanceof TLRPC.TL_botMenuButton) {
|
||||
TLRPC.TL_botMenuButton webViewButton = (TLRPC.TL_botMenuButton) botMenuButton;
|
||||
botMenuWebViewTitle = webViewButton.text;
|
||||
botMenuWebViewUrl = webViewButton.url;
|
||||
botMenuButtonType = BotMenuButtonType.WEB_VIEW;
|
||||
} else if (hasBotCommands) {
|
||||
botMenuButtonType = BotMenuButtonType.COMMANDS;
|
||||
} else {
|
||||
botMenuButtonType = BotMenuButtonType.NO_BUTTON;
|
||||
}
|
||||
|
||||
updateBotButton(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9187,7 +9211,7 @@ public class ChatActivityEnterView extends BlurredFrameLayout implements Notific
|
|||
if (botCommandsMenuButton != null) {
|
||||
botWebViewButton.setMeasuredButtonWidth(botCommandsMenuButton.getMeasuredWidth());
|
||||
}
|
||||
botWebViewButton.getLayoutParams().height = messageEditText.getMeasuredHeight();
|
||||
botWebViewButton.getLayoutParams().height = getMeasuredHeight() - AndroidUtilities.dp(2);
|
||||
measureChild(botWebViewButton, widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
if (botWebViewMenuContainer != null) {
|
||||
|
@ -9219,13 +9243,13 @@ public class ChatActivityEnterView extends BlurredFrameLayout implements Notific
|
|||
if (botInfo.size() == 1 && botInfo.valueAt(0).user_id == dialog_id) {
|
||||
TLRPC.BotInfo info = botInfo.valueAt(0);
|
||||
TLRPC.BotMenuButton menuButton = info.menu_button;
|
||||
if (menuButton instanceof TLRPC.TL_botMenuButtonCommands || menuButton instanceof TLRPC.TL_botMenuButtonDefault) {
|
||||
botMenuButtonType = info.commands.isEmpty() ? BotMenuButtonType.NO_BUTTON : BotMenuButtonType.COMMANDS;
|
||||
} else if (menuButton instanceof TLRPC.TL_botMenuButton) {
|
||||
if (menuButton instanceof TLRPC.TL_botMenuButton) {
|
||||
TLRPC.TL_botMenuButton webViewButton = (TLRPC.TL_botMenuButton) menuButton;
|
||||
botMenuWebViewTitle = webViewButton.text;
|
||||
botMenuWebViewUrl = webViewButton.url;
|
||||
botMenuButtonType = BotMenuButtonType.WEB_VIEW;
|
||||
} else if (!info.commands.isEmpty()) {
|
||||
botMenuButtonType = BotMenuButtonType.COMMANDS;
|
||||
} else {
|
||||
botMenuButtonType = BotMenuButtonType.NO_BUTTON;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import android.graphics.Rect;
|
|||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Vibrator;
|
||||
import android.text.Editable;
|
||||
import android.text.TextPaint;
|
||||
|
@ -136,16 +137,19 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N
|
|||
botAttachLayouts.put(id, webViewLayout);
|
||||
botAttachLayouts.get(id).setDelegate(new BotWebViewContainer.Delegate() {
|
||||
@Override
|
||||
public void onCloseRequested() {
|
||||
public void onCloseRequested(Runnable callback) {
|
||||
if (currentAttachLayout != webViewLayout) {
|
||||
return;
|
||||
}
|
||||
dismiss();
|
||||
}
|
||||
ChatAttachAlert.this.setFocusable(false);
|
||||
ChatAttachAlert.this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
|
||||
|
||||
@Override
|
||||
public void onSendWebViewData(String data) {
|
||||
// Empty as it's delegate from attachments menu. Data is only available for buttons
|
||||
dismiss();
|
||||
AndroidUtilities.runOnUIThread(()->{
|
||||
if (callback != null) {
|
||||
callback.run();
|
||||
}
|
||||
}, 150);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -167,7 +171,7 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N
|
|||
botMainButtonTextView.setClickable(isActive);
|
||||
botMainButtonTextView.setText(text);
|
||||
botMainButtonTextView.setTextColor(textColor);
|
||||
botMainButtonTextView.setBackground(Theme.createSelectorWithBackgroundDrawable(color, Theme.getColor(Theme.key_listSelector)));
|
||||
botMainButtonTextView.setBackground(BotWebViewContainer.getMainButtonRippleDrawable(color));
|
||||
if (botButtonWasVisible != isVisible) {
|
||||
ValueAnimator animator = ValueAnimator.ofFloat(isVisible ? 0 : 1, isVisible ? 1 : 0).setDuration(250);
|
||||
animator.addUpdateListener(animation -> {
|
||||
|
@ -184,6 +188,11 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N
|
|||
if (isVisible) {
|
||||
botMainButtonTextView.setAlpha(0f);
|
||||
botMainButtonTextView.setVisibility(View.VISIBLE);
|
||||
|
||||
int offsetY = AndroidUtilities.dp(36);
|
||||
for (int i = 0; i < botAttachLayouts.size(); i++) {
|
||||
botAttachLayouts.valueAt(i).setMeasureOffsetY(offsetY);
|
||||
}
|
||||
} else {
|
||||
buttonsRecyclerView.setAlpha(0f);
|
||||
buttonsRecyclerView.setVisibility(View.VISIBLE);
|
||||
|
@ -915,7 +924,7 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N
|
|||
|
||||
TLRPC.Document iconDoc = icon.icon;
|
||||
imageView.getImageReceiver().setAllowStartLottieAnimation(false);
|
||||
imageView.setImage(ImageLocation.getForDocument(iconDoc), "32_32", animated ? "tgs" : "svg", null, icon);
|
||||
imageView.setImage(ImageLocation.getForDocument(iconDoc), String.valueOf(bot.bot_id), animated ? "tgs" : "svg", null, bot);
|
||||
}
|
||||
|
||||
imageView.setSize(AndroidUtilities.dp(28), AndroidUtilities.dp(28));
|
||||
|
@ -1444,7 +1453,7 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N
|
|||
return;
|
||||
}
|
||||
int color1 = getThemedColor(forceDarkTheme ? Theme.key_voipgroup_listViewBackground : Theme.key_dialogBackground);
|
||||
int finalColor = Color.argb((int) (255 * actionBar.getAlpha()), (int) (Color.red(color1) * 0.8f), (int) (Color.green(color1) * 0.8f), (int) (Color.blue(color1) * 0.8f));
|
||||
int finalColor = Color.argb((int) (255 * actionBar.getAlpha()), Color.red(color1), Color.green(color1), Color.blue(color1));
|
||||
Theme.dialogs_onlineCirclePaint.setColor(finalColor);
|
||||
canvas.drawRect(backgroundPaddingLeft, currentPanTranslationY, getMeasuredWidth() - backgroundPaddingLeft, AndroidUtilities.statusBarHeight + currentPanTranslationY, Theme.dialogs_onlineCirclePaint);
|
||||
}
|
||||
|
@ -1524,21 +1533,24 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N
|
|||
actionBar = new ActionBar(context, resourcesProvider) {
|
||||
@Override
|
||||
public void setAlpha(float alpha) {
|
||||
float oldAlpha = getAlpha();
|
||||
super.setAlpha(alpha);
|
||||
containerView.invalidate();
|
||||
if (frameLayout2 != null && buttonsRecyclerView != null) {
|
||||
if (frameLayout2.getTag() == null) {
|
||||
if (currentAttachLayout == null || currentAttachLayout.shouldHideBottomButtons()) {
|
||||
buttonsRecyclerView.setAlpha(1.0f - alpha);
|
||||
shadow.setAlpha(1.0f - alpha);
|
||||
buttonsRecyclerView.setTranslationY(AndroidUtilities.dp(44) * alpha);
|
||||
}
|
||||
frameLayout2.setTranslationY(AndroidUtilities.dp(48) * alpha);
|
||||
shadow.setTranslationY(AndroidUtilities.dp(84) * alpha + botMainButtonOffsetY);
|
||||
} else if (currentAttachLayout == null) {
|
||||
float value = alpha == 0.0f ? 1.0f : 0.0f;
|
||||
if (buttonsRecyclerView.getAlpha() != value) {
|
||||
buttonsRecyclerView.setAlpha(value);
|
||||
if (oldAlpha != alpha) {
|
||||
containerView.invalidate();
|
||||
if (frameLayout2 != null && buttonsRecyclerView != null) {
|
||||
if (frameLayout2.getTag() == null) {
|
||||
if (currentAttachLayout == null || currentAttachLayout.shouldHideBottomButtons()) {
|
||||
buttonsRecyclerView.setAlpha(1.0f - alpha);
|
||||
shadow.setAlpha(1.0f - alpha);
|
||||
buttonsRecyclerView.setTranslationY(AndroidUtilities.dp(44) * alpha);
|
||||
}
|
||||
frameLayout2.setTranslationY(AndroidUtilities.dp(48) * alpha);
|
||||
shadow.setTranslationY(AndroidUtilities.dp(84) * alpha + botMainButtonOffsetY);
|
||||
} else if (currentAttachLayout == null) {
|
||||
float value = alpha == 0.0f ? 1.0f : 0.0f;
|
||||
if (buttonsRecyclerView.getAlpha() != value) {
|
||||
buttonsRecyclerView.setAlpha(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2324,11 +2336,24 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (baseFragment != null) {
|
||||
AndroidUtilities.setLightStatusBar(getWindow(), baseFragment.isLightStatusBar());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isLightStatusBar() {
|
||||
int color = getThemedColor(forceDarkTheme ? Theme.key_voipgroup_listViewBackground : Theme.key_dialogBackground);
|
||||
return ColorUtils.calculateLuminance(color) > 0.7f;
|
||||
}
|
||||
|
||||
public void onLongClickBotButton(TLRPC.TL_attachMenuBot attachMenuBot, TLRPC.User currentUser) {
|
||||
String botName = attachMenuBot != null ? attachMenuBot.short_name : UserObject.getUserName(currentUser);
|
||||
new AlertDialog.Builder(getContext())
|
||||
.setTitle(LocaleController.getString(attachMenuBot != null ? R.string.BotRemoveFromMenuTitle : R.string.AppName))
|
||||
.setMessage(AndroidUtilities.replaceTags(attachMenuBot != null ? LocaleController.formatString("BotRemoveFromMenu", R.string.BotRemoveFromMenu, botName) : LocaleController.formatString("ChatHintsDelete", R.string.ChatHintsDelete, botName)))
|
||||
.setTitle(LocaleController.getString(R.string.BotRemoveFromMenuTitle))
|
||||
.setMessage(AndroidUtilities.replaceTags(attachMenuBot != null ? LocaleController.formatString("BotRemoveFromMenu", R.string.BotRemoveFromMenu, botName) : LocaleController.formatString("BotRemoveInlineFromMenu", R.string.BotRemoveInlineFromMenu, botName)))
|
||||
.setPositiveButton(LocaleController.getString("OK", R.string.OK), (dialogInterface, i) -> {
|
||||
if (attachMenuBot != null) {
|
||||
TLRPC.TL_messages_toggleBotInAttachMenu req = new TLRPC.TL_messages_toggleBotInAttachMenu();
|
||||
|
@ -3192,6 +3217,14 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N
|
|||
} else if (typeButtonsAvailable && frameLayout2.getTag() == null) {
|
||||
buttonsRecyclerView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
if (getWindow() != null && baseFragment != null) {
|
||||
if (show) {
|
||||
AndroidUtilities.setLightStatusBar(getWindow(), isLightStatusBar());
|
||||
} else {
|
||||
AndroidUtilities.setLightStatusBar(getWindow(), baseFragment.isLightStatusBar());
|
||||
}
|
||||
}
|
||||
if (animated) {
|
||||
actionBarAnimation = new AnimatorSet();
|
||||
actionBarAnimation.setDuration((long) (180 * Math.abs((show ? 1.0f : 0.0f) - actionBar.getAlpha())));
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.animation.Animator;
|
|||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.os.Build;
|
||||
|
@ -114,9 +115,18 @@ public class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert
|
|||
parentAlert.baseFragment.presentFragment(new ChatActivity(bundle));
|
||||
parentAlert.dismiss();
|
||||
} else if (id == R.id.menu_reload_page) {
|
||||
webViewContainer.getWebView().animate().cancel();
|
||||
webViewContainer.getWebView().animate().alpha(0).start();
|
||||
requestWebView(currentAccount, peerId, botId, silent, replyToMsgId, startCommand);
|
||||
if (webViewContainer.getWebView() != null) {
|
||||
webViewContainer.getWebView().animate().cancel();
|
||||
webViewContainer.getWebView().animate().alpha(0).start();
|
||||
}
|
||||
|
||||
progressView.setLoadProgress(0);
|
||||
progressView.setAlpha(1f);
|
||||
progressView.setVisibility(VISIBLE);
|
||||
|
||||
webViewContainer.setBotUser(MessagesController.getInstance(currentAccount).getUser(botId));
|
||||
webViewContainer.loadFlicker(currentAccount, botId);
|
||||
webViewContainer.reload();
|
||||
} else if (id == R.id.menu_delete_bot) {
|
||||
for (TLRPC.TL_attachMenuBot bot : MediaDataController.getInstance(currentAccount).getAttachMenuBots().bots) {
|
||||
if (bot.bot_id == botId) {
|
||||
|
@ -140,7 +150,6 @@ public class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert
|
|||
return super.dispatchTouchEvent(ev);
|
||||
}
|
||||
};
|
||||
webViewContainer.getWebView().setVerticalScrollBarEnabled(false);
|
||||
swipeContainer = new WebViewSwipeContainer(context) {
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
|
@ -148,7 +157,6 @@ public class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert
|
|||
}
|
||||
};
|
||||
swipeContainer.addView(webViewContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
|
||||
swipeContainer.setWebView(webViewContainer.getWebView());
|
||||
swipeContainer.setScrollListener(() -> {
|
||||
parentAlert.updateLayout(this, true, 0);
|
||||
webViewContainer.invalidateViewPortHeight();
|
||||
|
@ -173,12 +181,16 @@ public class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert
|
|||
}
|
||||
});
|
||||
animator.start();
|
||||
|
||||
requestEnableKeyboard();
|
||||
}
|
||||
});
|
||||
|
||||
NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.didSetNewTheme);
|
||||
}
|
||||
|
||||
public boolean canExpandByRequest() {
|
||||
return System.currentTimeMillis() - lastSwipeTime > 1000 && !swipeContainer.isSwipeInProgress();
|
||||
return /* System.currentTimeMillis() - lastSwipeTime > 1000 && */ !swipeContainer.isSwipeInProgress();
|
||||
}
|
||||
|
||||
public void setMeasureOffsetY(int measureOffsetY) {
|
||||
|
@ -214,24 +226,30 @@ public class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert
|
|||
webViewScrollAnimator = null;
|
||||
}
|
||||
|
||||
int fromY = webViewContainer.getWebView().getScrollY();
|
||||
int toY = fromY + (oldh - contentHeight);
|
||||
webViewScrollAnimator = ValueAnimator.ofInt(fromY, toY).setDuration(250);
|
||||
webViewScrollAnimator.setInterpolator(ChatListItemAnimator.DEFAULT_INTERPOLATOR);
|
||||
webViewScrollAnimator.addUpdateListener(animation -> {
|
||||
int val = (int) animation.getAnimatedValue();
|
||||
webViewContainer.getWebView().setScrollY(val);
|
||||
});
|
||||
webViewScrollAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
webViewContainer.getWebView().setScrollY(toY);
|
||||
if (animation == webViewScrollAnimator) {
|
||||
webViewScrollAnimator = null;
|
||||
if (webViewContainer.getWebView() != null) {
|
||||
int fromY = webViewContainer.getWebView().getScrollY();
|
||||
int toY = fromY + (oldh - contentHeight);
|
||||
webViewScrollAnimator = ValueAnimator.ofInt(fromY, toY).setDuration(250);
|
||||
webViewScrollAnimator.setInterpolator(ChatListItemAnimator.DEFAULT_INTERPOLATOR);
|
||||
webViewScrollAnimator.addUpdateListener(animation -> {
|
||||
int val = (int) animation.getAnimatedValue();
|
||||
if (webViewContainer.getWebView() != null) {
|
||||
webViewContainer.getWebView().setScrollY(val);
|
||||
}
|
||||
}
|
||||
});
|
||||
webViewScrollAnimator.start();
|
||||
});
|
||||
webViewScrollAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (webViewContainer.getWebView() != null) {
|
||||
webViewContainer.getWebView().setScrollY(toY);
|
||||
}
|
||||
if (animation == webViewScrollAnimator) {
|
||||
webViewScrollAnimator = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
webViewScrollAnimator.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -244,7 +262,9 @@ public class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert
|
|||
void onShow(ChatAttachAlert.AttachAlertLayout previousLayout) {
|
||||
parentAlert.actionBar.setTitle(UserObject.getUserName(MessagesController.getInstance(currentAccount).getUser(botId)));
|
||||
swipeContainer.setSwipeOffsetY(0);
|
||||
webViewContainer.getWebView().scrollTo(0, 0);
|
||||
if (webViewContainer.getWebView() != null) {
|
||||
webViewContainer.getWebView().scrollTo(0, 0);
|
||||
}
|
||||
if (parentAlert.getBaseFragment() != null) {
|
||||
webViewContainer.setParentActivity(parentAlert.getBaseFragment().getParentActivity());
|
||||
}
|
||||
|
@ -253,7 +273,9 @@ public class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert
|
|||
|
||||
@Override
|
||||
void onShown() {
|
||||
requestEnableKeyboard();
|
||||
if (webViewContainer.isPageLoaded()) {
|
||||
requestEnableKeyboard();
|
||||
}
|
||||
|
||||
swipeContainer.setSwipeOffsetAnimationDisallowed(false);
|
||||
AndroidUtilities.runOnUIThread(() -> webViewContainer.restoreButtonData());
|
||||
|
@ -263,13 +285,13 @@ public class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert
|
|||
BaseFragment fragment = parentAlert.getBaseFragment();
|
||||
if (fragment instanceof ChatActivity && ((ChatActivity) fragment).contentView.measureKeyboardHeight() > AndroidUtilities.dp(20)) {
|
||||
AndroidUtilities.hideKeyboard(parentAlert.baseFragment.getFragmentView());
|
||||
AndroidUtilities.runOnUIThread(this::requestEnableKeyboard, 150);
|
||||
AndroidUtilities.runOnUIThread(this::requestEnableKeyboard, 250);
|
||||
return;
|
||||
}
|
||||
|
||||
parentAlert.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
|
||||
setFocusable(true);
|
||||
parentAlert.setFocusable(true);
|
||||
parentAlert.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -345,6 +367,7 @@ public class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert
|
|||
TLRPC.TL_webViewResultUrl resultUrl = (TLRPC.TL_webViewResultUrl) response;
|
||||
queryId = resultUrl.query_id;
|
||||
webViewContainer.loadUrl(resultUrl.url);
|
||||
swipeContainer.setWebView(webViewContainer.getWebView());
|
||||
|
||||
AndroidUtilities.runOnUIThread(pollRunnable);
|
||||
}
|
||||
|
@ -356,6 +379,7 @@ public class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert
|
|||
@Override
|
||||
void onDestroy() {
|
||||
NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.webViewResultSent);
|
||||
NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.didSetNewTheme);
|
||||
|
||||
ActionBarMenu menu = parentAlert.actionBar.createMenu();
|
||||
otherItem.removeAllSubItems();
|
||||
|
@ -422,6 +446,11 @@ public class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert
|
|||
return AndroidUtilities.dp(56);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean onBackPressed() {
|
||||
return webViewContainer.onBackPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestLayout() {
|
||||
if (ignoreLayout) {
|
||||
|
@ -467,6 +496,8 @@ public class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert
|
|||
needReload = true;
|
||||
parentAlert.dismiss();
|
||||
}
|
||||
} else if (id == NotificationCenter.didSetNewTheme) {
|
||||
webViewContainer.updateFlickerBackgroundColor(getThemedColor(Theme.key_dialogBackground));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -492,6 +523,8 @@ public class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert
|
|||
|
||||
private SpringAnimation scrollAnimator;
|
||||
|
||||
private int swipeStickyRange;
|
||||
|
||||
public WebViewSwipeContainer(@NonNull Context context) {
|
||||
super(context);
|
||||
|
||||
|
@ -502,10 +535,10 @@ public class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert
|
|||
if (isSwipeDisallowed) {
|
||||
return false;
|
||||
}
|
||||
if (velocityY >= 700 && webView.getScrollY() == 0) {
|
||||
if (velocityY >= 700 && (webView == null || webView.getScrollY() == 0)) {
|
||||
flingInProgress = true;
|
||||
|
||||
if (swipeOffsetY >= AndroidUtilities.dp(64)) {
|
||||
if (swipeOffsetY >= swipeStickyRange) {
|
||||
if (delegate != null) {
|
||||
delegate.onDismiss();
|
||||
}
|
||||
|
@ -524,7 +557,7 @@ public class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert
|
|||
@Override
|
||||
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
|
||||
if (!isScrolling && !isSwipeDisallowed) {
|
||||
if (Math.abs(distanceY) >= touchSlop && Math.abs(distanceY) * 1.5f >= Math.abs(distanceX) && (swipeOffsetY != -offsetY + topActionBarOffsetY || distanceY < 0 && webView.getScrollY() == 0)) {
|
||||
if (Math.abs(distanceY) >= touchSlop && Math.abs(distanceY) * 1.5f >= Math.abs(distanceX) && (swipeOffsetY != -offsetY + topActionBarOffsetY || webView == null || distanceY < 0 && webView.getScrollY() == 0)) {
|
||||
isScrolling = true;
|
||||
|
||||
MotionEvent ev = MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0, 0, 0);
|
||||
|
@ -534,7 +567,7 @@ public class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert
|
|||
ev.recycle();
|
||||
|
||||
return true;
|
||||
} else if (webView.canScrollHorizontally(distanceX >= 0 ? 1 : -1)) {
|
||||
} else if (webView != null && webView.canScrollHorizontally(distanceX >= 0 ? 1 : -1)) {
|
||||
isSwipeDisallowed = true;
|
||||
}
|
||||
}
|
||||
|
@ -542,18 +575,20 @@ public class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert
|
|||
if (distanceY < 0) {
|
||||
if (swipeOffsetY > -offsetY + topActionBarOffsetY) {
|
||||
swipeOffsetY -= distanceY;
|
||||
} else {
|
||||
} else if (webView != null) {
|
||||
float newWebScrollY = webView.getScrollY() + distanceY;
|
||||
webView.setScrollY((int) MathUtils.clamp(newWebScrollY, 0, Math.max(webView.getContentHeight(), webView.getHeight()) - topActionBarOffsetY));
|
||||
|
||||
if (newWebScrollY < 0) {
|
||||
swipeOffsetY -= newWebScrollY;
|
||||
}
|
||||
} else {
|
||||
swipeOffsetY -= distanceY;
|
||||
}
|
||||
} else {
|
||||
swipeOffsetY = swipeOffsetY - distanceY;
|
||||
swipeOffsetY -= distanceY;
|
||||
|
||||
if (swipeOffsetY < -offsetY + topActionBarOffsetY) {
|
||||
if (webView != null && swipeOffsetY < -offsetY + topActionBarOffsetY) {
|
||||
float newWebScrollY = webView.getScrollY() - (swipeOffsetY + offsetY - topActionBarOffsetY);
|
||||
webView.setScrollY((int) MathUtils.clamp(newWebScrollY, 0, Math.max(webView.getContentHeight(), webView.getHeight()) - topActionBarOffsetY));
|
||||
}
|
||||
|
@ -567,6 +602,17 @@ public class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert
|
|||
return true;
|
||||
}
|
||||
});
|
||||
updateStickyRange();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
updateStickyRange();
|
||||
}
|
||||
|
||||
private void updateStickyRange() {
|
||||
swipeStickyRange = AndroidUtilities.dp(AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y ? 8 : 64);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -677,9 +723,9 @@ public class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert
|
|||
if (flingInProgress) {
|
||||
flingInProgress = false;
|
||||
} else {
|
||||
if (swipeOffsetY <= -AndroidUtilities.dp(64)) {
|
||||
if (swipeOffsetY <= -swipeStickyRange) {
|
||||
stickTo(-offsetY + topActionBarOffsetY);
|
||||
} else if (swipeOffsetY > -AndroidUtilities.dp(64) && swipeOffsetY <= AndroidUtilities.dp(64)) {
|
||||
} else if (swipeOffsetY > -swipeStickyRange && swipeOffsetY <= swipeStickyRange) {
|
||||
stickTo(0);
|
||||
} else {
|
||||
if (delegate != null) {
|
||||
|
|
|
@ -69,19 +69,20 @@ public class ChatNotificationsPopupWrapper {
|
|||
ActionBarMenuSubItem item = ActionBarMenuItem.addItem(windowLayout, R.drawable.msg_mute_period, LocaleController.getString("MuteForPopup", R.string.MuteForPopup), false, resourcesProvider);
|
||||
item.setOnClickListener(view -> {
|
||||
dismiss();
|
||||
AlertsCreator.createMuteForPickerDialog(context, (notify, inMinutes) -> {
|
||||
AlertsCreator.createMuteForPickerDialog(context, (notify, inSecond) -> {
|
||||
AndroidUtilities.runOnUIThread(() -> {
|
||||
SharedPreferences sharedPreferences = MessagesController.getNotificationsSettings(currentAccount);
|
||||
int time1 = sharedPreferences.getInt(LAST_SELECTED_TIME_KEY_1, 0);
|
||||
int time2;
|
||||
int timeInSeconds = inMinutes * 60;
|
||||
time2 = time1;
|
||||
time1 = timeInSeconds;
|
||||
sharedPreferences.edit()
|
||||
.putInt(LAST_SELECTED_TIME_KEY_1, time1)
|
||||
.putInt(LAST_SELECTED_TIME_KEY_2, time2)
|
||||
.apply();
|
||||
callback.muteFor(timeInSeconds);
|
||||
if (inSecond != 0) {
|
||||
SharedPreferences sharedPreferences = MessagesController.getNotificationsSettings(currentAccount);
|
||||
int time1 = sharedPreferences.getInt(LAST_SELECTED_TIME_KEY_1, 0);
|
||||
int time2;
|
||||
time2 = time1;
|
||||
time1 = inSecond;
|
||||
sharedPreferences.edit()
|
||||
.putInt(LAST_SELECTED_TIME_KEY_1, time1)
|
||||
.putInt(LAST_SELECTED_TIME_KEY_2, time2)
|
||||
.apply();
|
||||
}
|
||||
callback.muteFor(inSecond);
|
||||
}, 16);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -74,7 +74,7 @@ public class CrossfadeDrawable extends Drawable {
|
|||
|
||||
@Override
|
||||
public void setColorFilter(ColorFilter colorFilter) {
|
||||
|
||||
topDrawable.setColorFilter(colorFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -324,7 +324,7 @@ public class MotionBackgroundDrawable extends Drawable {
|
|||
} else {
|
||||
gradientDrawable = null;
|
||||
}
|
||||
if (colors[0] == c1 || colors[1] == c2 || colors[2] == c3 || colors[3] == c4) {
|
||||
if (colors[0] == c1 && colors[1] == c2 && colors[2] == c3 && colors[3] == c4) {
|
||||
return;
|
||||
}
|
||||
colors[0] = c1;
|
||||
|
|
|
@ -41,6 +41,7 @@ public class PopupSwipeBackLayout extends FrameLayout {
|
|||
private boolean isSwipeDisallowed;
|
||||
private Paint overlayPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private Paint foregroundPaint = new Paint();
|
||||
private int foregroundColor = 0;
|
||||
|
||||
private Path mPath = new Path();
|
||||
private RectF mRect = new RectF();
|
||||
|
@ -128,7 +129,11 @@ public class PopupSwipeBackLayout extends FrameLayout {
|
|||
int i = indexOfChild(child);
|
||||
int s = canvas.save();
|
||||
if (i != 0) {
|
||||
foregroundPaint.setColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuBackground, resourcesProvider));
|
||||
if (foregroundColor == 0) {
|
||||
foregroundPaint.setColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuBackground, resourcesProvider));
|
||||
} else {
|
||||
foregroundPaint.setColor(foregroundColor);
|
||||
}
|
||||
canvas.drawRect(child.getX(), 0, child.getX() + child.getMeasuredWidth(), getMeasuredHeight(), foregroundPaint);
|
||||
}
|
||||
boolean b = super.drawChild(canvas, child, drawingTime);
|
||||
|
@ -444,6 +449,10 @@ public class PopupSwipeBackLayout extends FrameLayout {
|
|||
foregroundAnimator = animator;
|
||||
}
|
||||
|
||||
public void setForegroundColor(int color) {
|
||||
foregroundColor = color;
|
||||
}
|
||||
|
||||
public interface OnSwipeBackProgressListener {
|
||||
void onSwipeBackProgress(PopupSwipeBackLayout layout, float toProgress, float progress);
|
||||
}
|
||||
|
|
|
@ -77,6 +77,8 @@ public class SearchDownloadsContainer extends FrameLayout implements Notificatio
|
|||
Runnable lastSearchRunnable;
|
||||
RecyclerItemsEnterAnimator itemsEnterAnimator;
|
||||
|
||||
boolean checkingFilesExist;
|
||||
|
||||
public SearchDownloadsContainer(BaseFragment fragment, int currentAccount) {
|
||||
super(fragment.getParentActivity());
|
||||
this.parentFragment = fragment;
|
||||
|
@ -177,7 +179,46 @@ public class SearchDownloadsContainer extends FrameLayout implements Notificatio
|
|||
recyclerListView.setEmptyView(emptyView);
|
||||
|
||||
FileLoader.getInstance(currentAccount).getCurrentLoadingFiles(currentLoadingFiles);
|
||||
update(false);
|
||||
}
|
||||
|
||||
private void checkFilesExist() {
|
||||
if (checkingFilesExist) {
|
||||
return;
|
||||
}
|
||||
checkingFilesExist = true;
|
||||
Utilities.searchQueue.postRunnable(() -> {
|
||||
ArrayList<MessageObject> currentLoadingFiles = new ArrayList<>();
|
||||
ArrayList<MessageObject> recentLoadingFiles = new ArrayList<>();
|
||||
|
||||
ArrayList<MessageObject> moveToRecent = new ArrayList<>();
|
||||
ArrayList<MessageObject> removeFromRecent = new ArrayList<>();
|
||||
|
||||
FileLoader.getInstance(currentAccount).getCurrentLoadingFiles(currentLoadingFiles);
|
||||
FileLoader.getInstance(currentAccount).getRecentLoadingFiles(recentLoadingFiles);
|
||||
|
||||
for (int i = 0; i < currentLoadingFiles.size(); i++) {
|
||||
if (FileLoader.getPathToMessage(currentLoadingFiles.get(i).messageOwner).exists()) {
|
||||
moveToRecent.add(currentLoadingFiles.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < recentLoadingFiles.size(); i++) {
|
||||
if (!FileLoader.getPathToMessage(recentLoadingFiles.get(i).messageOwner).exists()) {
|
||||
removeFromRecent.add(recentLoadingFiles.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
AndroidUtilities.runOnUIThread(() -> {
|
||||
for (int i = 0; i < moveToRecent.size(); i++) {
|
||||
DownloadController.getInstance(currentAccount).onDownloadComplete(moveToRecent.get(i));
|
||||
}
|
||||
if (!removeFromRecent.isEmpty()) {
|
||||
DownloadController.getInstance(currentAccount).deleteRecentFiles(removeFromRecent);
|
||||
}
|
||||
checkingFilesExist = false;
|
||||
update(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public void update(boolean animated) {
|
||||
|
@ -185,6 +226,10 @@ public class SearchDownloadsContainer extends FrameLayout implements Notificatio
|
|||
if (rowCount == 0) {
|
||||
itemsEnterAnimator.showItemsAnimated(0);
|
||||
}
|
||||
if (checkingFilesExist) {
|
||||
currentLoadingFilesTmp.clear();
|
||||
recentLoadingFilesTmp.clear();
|
||||
}
|
||||
FileLoader.getInstance(currentAccount).getCurrentLoadingFiles(currentLoadingFilesTmp);
|
||||
FileLoader.getInstance(currentAccount).getRecentLoadingFiles(recentLoadingFilesTmp);
|
||||
|
||||
|
@ -583,6 +628,8 @@ public class SearchDownloadsContainer extends FrameLayout implements Notificatio
|
|||
if (getVisibility() == View.VISIBLE) {
|
||||
DownloadController.getInstance(currentAccount).clearUnviewedDownloads();
|
||||
}
|
||||
checkFilesExist();
|
||||
update(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -6581,6 +6581,18 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter
|
|||
return newColumnsCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
|
||||
if (child == fragmentContextView) {
|
||||
canvas.save();
|
||||
canvas.clipRect(0, mediaPages[0].getTop(), child.getMeasuredWidth(),mediaPages[0].getTop() + child.getMeasuredHeight() + AndroidUtilities.dp(12));
|
||||
boolean b = super.drawChild(canvas, child, drawingTime);
|
||||
canvas.restore();
|
||||
return b;
|
||||
}
|
||||
return super.drawChild(canvas, child, drawingTime);
|
||||
}
|
||||
|
||||
private class ScrollSlidingTextTabStripInner extends ScrollSlidingTextTabStrip {
|
||||
|
||||
protected Paint backgroundPaint;
|
||||
|
|
|
@ -150,8 +150,9 @@ public class TimerDrawable extends Drawable {
|
|||
currentTtlIcon.setColorFilter(new PorterDuffColorFilter(iconColor, PorterDuff.Mode.MULTIPLY));
|
||||
}
|
||||
}
|
||||
AndroidUtilities.rectTmp2.set(getBounds());
|
||||
AndroidUtilities.rectTmp2.inset(AndroidUtilities.dp(1f), AndroidUtilities.dp(1f));
|
||||
AndroidUtilities.rectTmp2.set((int) (getBounds().centerX() - AndroidUtilities.dp(10.5f)), (int) (getBounds().centerY() - AndroidUtilities.dp(10.5f)),
|
||||
(int) (getBounds().centerX() - AndroidUtilities.dp(10.5f)) + currentTtlIcon.getIntrinsicWidth(),
|
||||
(int) (getBounds().centerY() - AndroidUtilities.dp(10.5f)) + currentTtlIcon.getIntrinsicHeight());
|
||||
currentTtlIcon.setBounds(AndroidUtilities.rectTmp2);
|
||||
currentTtlIcon.draw(canvas);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ package org.telegram.ui;
|
|||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.view.View;
|
||||
|
||||
import org.telegram.messenger.AndroidUtilities;
|
||||
|
@ -34,6 +36,7 @@ public class DownloadProgressIcon extends View implements NotificationCenter.Not
|
|||
RLottieDrawable downloadCompleteDrawable;
|
||||
boolean showCompletedIcon;
|
||||
boolean hasUnviewedDownloads;
|
||||
int currentColor;
|
||||
|
||||
public DownloadProgressIcon(int currentAccount, Context context) {
|
||||
super(context);
|
||||
|
@ -65,9 +68,13 @@ public class DownloadProgressIcon extends View implements NotificationCenter.Not
|
|||
return;
|
||||
}
|
||||
|
||||
paint.setColor(Theme.getColor(Theme.key_actionBarDefaultIcon));
|
||||
paint2.setColor(Theme.getColor(Theme.key_actionBarDefaultIcon));
|
||||
paint2.setAlpha(100);
|
||||
if (currentColor != Theme.getColor(Theme.key_actionBarDefaultIcon)) {
|
||||
currentColor = Theme.getColor(Theme.key_actionBarDefaultIcon);
|
||||
paint.setColor(Theme.getColor(Theme.key_actionBarDefaultIcon));
|
||||
paint2.setColor(Theme.getColor(Theme.key_actionBarDefaultIcon));
|
||||
downloadImageReceiver.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_actionBarDefaultIcon), PorterDuff.Mode.MULTIPLY));
|
||||
paint2.setAlpha(100);
|
||||
}
|
||||
|
||||
if (currentProgress != progress) {
|
||||
currentProgress += progressDt;
|
||||
|
|
|
@ -242,7 +242,11 @@ public class IntroActivity extends BaseFragment implements NotificationCenter.No
|
|||
Intro.setPage(currentViewPagerPage);
|
||||
Intro.setDate(time);
|
||||
Intro.onDrawFrame(0);
|
||||
eglThread.egl10.eglSwapBuffers(eglThread.eglDisplay, eglThread.eglSurface);
|
||||
if (eglThread != null && eglThread.isAlive() && eglThread.eglDisplay != null && eglThread.eglSurface != null) {
|
||||
try {
|
||||
eglThread.egl10.eglSwapBuffers(eglThread.eglDisplay, eglThread.eglSurface);
|
||||
} catch (Exception ignored) {} // If display or surface already destroyed
|
||||
}
|
||||
});
|
||||
eglThread.postRunnable(eglThread.drawRunnable);
|
||||
}
|
||||
|
|
|
@ -275,12 +275,12 @@ public class LaunchActivity extends BasePermissionsActivity implements ActionBar
|
|||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
try {
|
||||
setTaskDescription(new ActivityManager.TaskDescription(null, null, Theme.getColor(Theme.key_actionBarDefault) | 0xff000000));
|
||||
} catch (Exception ignore) {
|
||||
} catch (Throwable ignore) {
|
||||
|
||||
}
|
||||
try {
|
||||
getWindow().setNavigationBarColor(0xff000000);
|
||||
} catch (Exception ignore) {
|
||||
} catch (Throwable ignore) {
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -2941,6 +2941,8 @@ public class LaunchActivity extends BasePermissionsActivity implements ActionBar
|
|||
.setNegativeButton(LocaleController.getString(R.string.Cancel), null)
|
||||
.show();
|
||||
}
|
||||
} else {
|
||||
BulletinFactory.of(mainFragmentsStack.get(mainFragmentsStack.size() - 1)).createErrorBulletin(LocaleController.getString(R.string.BotCantAddToAttachMenu)).show();
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
|
|
|
@ -4749,8 +4749,8 @@ public class LoginActivity extends BaseFragment {
|
|||
codeField[a].setPadding(padding, padding, padding, padding);
|
||||
if (stage == 0) {
|
||||
codeField[a].setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||
codeField[a].setTransformationMethod(PasswordTransformationMethod.getInstance());
|
||||
}
|
||||
codeField[a].setTransformationMethod(PasswordTransformationMethod.getInstance());
|
||||
codeField[a].setTypeface(Typeface.DEFAULT);
|
||||
codeField[a].setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT);
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ import org.telegram.messenger.AndroidUtilities;
|
|||
import org.telegram.messenger.ChatObject;
|
||||
import org.telegram.messenger.ContactsController;
|
||||
import org.telegram.messenger.DialogObject;
|
||||
import org.telegram.messenger.FileLoader;
|
||||
import org.telegram.messenger.FileLog;
|
||||
import org.telegram.messenger.LocaleController;
|
||||
import org.telegram.messenger.NotificationCenter;
|
||||
|
@ -1247,7 +1248,11 @@ public class NotificationsCustomSettingsActivity extends BaseFragment implements
|
|||
}
|
||||
if (documentId != 0) {
|
||||
TLRPC.Document document = getMediaDataController().ringtoneDataStore.getDocument(documentId);
|
||||
value = NotificationsSoundActivity.trimTitle(document, document.file_name_fixed);
|
||||
if (document == null) {
|
||||
value = LocaleController.getString("CustomSound", R.string.CustomSound);
|
||||
} else {
|
||||
value = NotificationsSoundActivity.trimTitle(document, FileLoader.getDocumentFileName(document));
|
||||
}
|
||||
} else if (value.equals("NoSound")) {
|
||||
value = LocaleController.getString("NoSound", R.string.NoSound);
|
||||
} else if (value.equals("Default")) {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package org.telegram.ui;
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.content.ClipData;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
|
@ -284,8 +283,10 @@ public class NotificationsSoundActivity extends BaseFragment implements ChatAtta
|
|||
avatarContainer.setTitle(chatLocal.title);
|
||||
} else {
|
||||
TLRPC.User user = getMessagesController().getUser(dialogId);
|
||||
avatarContainer.setUserAvatar(user);
|
||||
avatarContainer.setTitle(ContactsController.formatName(user.first_name, user.last_name));
|
||||
if (user != null) {
|
||||
avatarContainer.setUserAvatar(user);
|
||||
avatarContainer.setTitle(ContactsController.formatName(user.first_name, user.last_name));
|
||||
}
|
||||
}
|
||||
avatarContainer.setSubtitle(LocaleController.getString("NotificationsSound", R.string.NotificationsSound));
|
||||
}
|
||||
|
@ -323,7 +324,7 @@ public class NotificationsSoundActivity extends BaseFragment implements ChatAtta
|
|||
}
|
||||
if (view instanceof ToneCell) {
|
||||
ToneCell cell = (ToneCell) view;
|
||||
if (actionBar.isActionModeShowed()) {
|
||||
if (actionBar.isActionModeShowed() || cell.tone == null) {
|
||||
checkSelection(cell.tone);
|
||||
return;
|
||||
}
|
||||
|
@ -357,14 +358,13 @@ public class NotificationsSoundActivity extends BaseFragment implements ChatAtta
|
|||
lastPlayedRingtone = r;
|
||||
r.play();
|
||||
} else {
|
||||
getFileLoader().loadFile(cell.tone.document, null, 2, 0);
|
||||
getFileLoader().loadFile(cell.tone.document, cell.tone.document, 2, 0);
|
||||
}
|
||||
}
|
||||
startSelectedTone = null;
|
||||
selectedTone = cell.tone;
|
||||
selectedToneChanged = true;
|
||||
adapter.notifyItemRangeChanged(0, adapter.getItemCount());
|
||||
checkDisabledBySystem();
|
||||
}
|
||||
|
||||
});
|
||||
|
@ -415,6 +415,9 @@ public class NotificationsSoundActivity extends BaseFragment implements ChatAtta
|
|||
|
||||
private void loadTones() {
|
||||
getMediaDataController().ringtoneDataStore.loadUserRingtones();
|
||||
serverTones.clear();
|
||||
systemTones.clear();
|
||||
|
||||
for (int i = 0; i < getMediaDataController().ringtoneDataStore.userRingtones.size(); i++) {
|
||||
RingtoneDataStore.CachedTone cachedTone = getMediaDataController().ringtoneDataStore.userRingtones.get(i);
|
||||
Tone tone = new Tone();
|
||||
|
@ -439,7 +442,7 @@ public class NotificationsSoundActivity extends BaseFragment implements ChatAtta
|
|||
manager.setType(RingtoneManager.TYPE_NOTIFICATION);
|
||||
Cursor cursor = manager.getCursor();
|
||||
|
||||
systemTones.clear();
|
||||
|
||||
|
||||
|
||||
Tone noSoundTone = new Tone();
|
||||
|
@ -942,15 +945,4 @@ public class NotificationsSoundActivity extends BaseFragment implements ChatAtta
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void checkDisabledBySystem() {
|
||||
NotificationManager manager = (NotificationManager) ApplicationLoader.applicationContext.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
boolean notificationsEnabled = true;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
notificationsEnabled = manager.areNotificationsEnabled();
|
||||
}
|
||||
// if (!notificationsEnabled) {
|
||||
// BulletinFactory.of(this).createErrorBulletin(LocaleController.getString()).show();
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -307,16 +307,15 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat
|
|||
private TextView docNameTextView;
|
||||
private TextView docInfoTextView;
|
||||
private ActionBarMenuItem menuItem;
|
||||
private ActionBarMenuItem menuItemSpeed;
|
||||
private ActionBarMenuSubItem allMediaItem;
|
||||
private ActionBarMenuSubItem speedItem;
|
||||
private ActionBarMenuSubItem[] speedItems = new ActionBarMenuSubItem[5];
|
||||
private View speedGap;
|
||||
private ActionBarPopupWindow.GapView speedGap;
|
||||
private ActionBarMenuItem sendItem;
|
||||
private ActionBarMenuItem pipItem;
|
||||
private ActionBarMenuItem masksItem;
|
||||
private ActionBarMenuItem shareItem;
|
||||
private LinearLayout itemsLayout;
|
||||
ChooseSpeedLayout chooseSpeedLayout;
|
||||
private Map<View, Boolean> actionBarItemsVisibility = new HashMap<>(3);
|
||||
private LinearLayout bottomButtonsLayout;
|
||||
private ImageView shareButton;
|
||||
|
@ -412,7 +411,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat
|
|||
@Override
|
||||
public void run() {
|
||||
if (videoPlayerControlVisible && isPlaying && !ApplicationLoader.mainInterfacePaused) {
|
||||
if (menuItem != null && menuItem.isSubMenuShowing() || menuItemSpeed != null && menuItemSpeed.isSubMenuShowing()) {
|
||||
if (menuItem != null && menuItem.isSubMenuShowing()) {
|
||||
return;
|
||||
}
|
||||
if (captionScrollView != null && captionScrollView.getScrollY() != 0) {
|
||||
|
@ -1204,12 +1203,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat
|
|||
private final static int gallery_menu_edit_avatar = 17;
|
||||
private final static int gallery_menu_share2 = 18;
|
||||
private final static int gallery_menu_speed = 19;
|
||||
private final static int gallery_menu_gap = 20;
|
||||
private final static int gallery_menu_speed_veryslow = 21;
|
||||
private final static int gallery_menu_speed_slow = 22;
|
||||
private final static int gallery_menu_speed_normal = 23;
|
||||
private final static int gallery_menu_speed_fast = 24;
|
||||
private final static int gallery_menu_speed_veryfast = 25;
|
||||
|
||||
private static DecelerateInterpolator decelerateInterpolator;
|
||||
private static Paint progressPaint;
|
||||
|
@ -4189,20 +4182,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat
|
|||
}
|
||||
} else if (id == gallery_menu_share || id == gallery_menu_share2) {
|
||||
onSharePressed();
|
||||
} else if (id == gallery_menu_speed) {
|
||||
menuItemSpeed.setVisibility(View.VISIBLE);
|
||||
menuItemSpeed.toggleSubMenu();
|
||||
for (int a = 0; a < speedItems.length; a++) {
|
||||
if (a == 0 && Math.abs(currentVideoSpeed - 0.25f) < 0.001f ||
|
||||
a == 1 && Math.abs(currentVideoSpeed - 0.5f) < 0.001f ||
|
||||
a == 2 && Math.abs(currentVideoSpeed - 1.0f) < 0.001f ||
|
||||
a == 3 && Math.abs(currentVideoSpeed - 1.5f) < 0.001f ||
|
||||
a == 4 && Math.abs(currentVideoSpeed - 2.0f) < 0.001f) {
|
||||
speedItems[a].setColors(0xff6BB6F9, 0xff6BB6F9);
|
||||
} else {
|
||||
speedItems[a].setColors(0xfffafafa, 0xfffafafa);
|
||||
}
|
||||
}
|
||||
} else if (id == gallery_menu_openin) {
|
||||
try {
|
||||
if (isEmbedVideo) {
|
||||
|
@ -4403,7 +4382,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat
|
|||
|
||||
@Override
|
||||
public boolean canOpenMenu() {
|
||||
menuItemSpeed.setVisibility(View.INVISIBLE);
|
||||
if (currentMessageObject != null || currentSecureDocument != null) {
|
||||
return true;
|
||||
} else if (currentFileLocationVideo != null) {
|
||||
|
@ -4428,62 +4406,41 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat
|
|||
shareItem.setContentDescription(LocaleController.getString("ShareFile", R.string.ShareFile));
|
||||
|
||||
menuItem = menu.addItem(0, R.drawable.ic_ab_other);
|
||||
menuItemSpeed = new ActionBarMenuItem(parentActivity, null, 0, 0, resourcesProvider);
|
||||
menuItemSpeed.setDelegate(id -> {
|
||||
if (id >= gallery_menu_speed_veryslow && id <= gallery_menu_speed_veryfast) {
|
||||
switch (id) {
|
||||
case gallery_menu_speed_veryslow:
|
||||
currentVideoSpeed = 0.25f;
|
||||
break;
|
||||
case gallery_menu_speed_slow:
|
||||
currentVideoSpeed = 0.5f;
|
||||
break;
|
||||
case gallery_menu_speed_normal:
|
||||
currentVideoSpeed = 1.0f;
|
||||
break;
|
||||
case gallery_menu_speed_fast:
|
||||
currentVideoSpeed = 1.5f;
|
||||
break;
|
||||
case gallery_menu_speed_veryfast:
|
||||
currentVideoSpeed = 2.0f;
|
||||
break;
|
||||
}
|
||||
if (currentMessageObject != null) {
|
||||
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("playback_speed", Activity.MODE_PRIVATE);
|
||||
if (Math.abs(currentVideoSpeed - 1.0f) < 0.001f) {
|
||||
preferences.edit().remove("speed" + currentMessageObject.getDialogId() + "_" + currentMessageObject.getId()).commit();
|
||||
} else {
|
||||
preferences.edit().putFloat("speed" + currentMessageObject.getDialogId() + "_" + currentMessageObject.getId(), currentVideoSpeed).commit();
|
||||
|
||||
menuItem.getPopupLayout().swipeBackGravityRight = true;
|
||||
chooseSpeedLayout = new ChooseSpeedLayout(activityContext, menuItem.getPopupLayout().getSwipeBack(), new ChooseSpeedLayout.Callback() {
|
||||
|
||||
@Override
|
||||
public void onSpeedSelected(float speed) {
|
||||
menuItem.toggleSubMenu();
|
||||
if (speed != currentVideoSpeed) {
|
||||
currentVideoSpeed = speed;
|
||||
if (currentMessageObject != null) {
|
||||
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("playback_speed", Activity.MODE_PRIVATE);
|
||||
if (Math.abs(currentVideoSpeed - 1.0f) < 0.001f) {
|
||||
preferences.edit().remove("speed" + currentMessageObject.getDialogId() + "_" + currentMessageObject.getId()).commit();
|
||||
} else {
|
||||
preferences.edit().putFloat("speed" + currentMessageObject.getDialogId() + "_" + currentMessageObject.getId(), currentVideoSpeed).commit();
|
||||
}
|
||||
}
|
||||
if (videoPlayer != null) {
|
||||
videoPlayer.setPlaybackSpeed(currentVideoSpeed);
|
||||
}
|
||||
if (photoViewerWebView != null) {
|
||||
photoViewerWebView.setPlaybackSpeed(currentVideoSpeed);
|
||||
}
|
||||
setMenuItemIcon();
|
||||
}
|
||||
if (videoPlayer != null) {
|
||||
videoPlayer.setPlaybackSpeed(currentVideoSpeed);
|
||||
}
|
||||
if (photoViewerWebView != null) {
|
||||
photoViewerWebView.setPlaybackSpeed(currentVideoSpeed);
|
||||
}
|
||||
setMenuItemIcon();
|
||||
menuItemSpeed.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
});
|
||||
menuItem.addView(menuItemSpeed);
|
||||
menuItemSpeed.setVisibility(View.INVISIBLE);
|
||||
|
||||
speedItem = menuItem.addSubItem(gallery_menu_speed, R.drawable.msg_speed, null, LocaleController.getString("Speed", R.string.Speed), true, false);
|
||||
speedItem = menuItem.addSwipeBackItem(R.drawable.msg_speed, null, LocaleController.getString("Speed", R.string.Speed), chooseSpeedLayout.speedSwipeBackLayout);
|
||||
menuItem.getPopupLayout().setSwipeBackForegroundColor(0xff222222);
|
||||
speedItem.setSubtext(LocaleController.getString("SpeedNormal", R.string.SpeedNormal));
|
||||
speedItem.setItemHeight(56);
|
||||
speedItem.setTag(R.id.width_tag, 240);
|
||||
speedItem.setColors(0xfffafafa, 0xfffafafa);
|
||||
speedItem.setRightIcon(R.drawable.msg_arrowright);
|
||||
speedGap = menuItem.addGap(gallery_menu_gap);
|
||||
speedGap = menuItem.addColoredGap();
|
||||
speedGap.setColor(0xff181818);
|
||||
menuItem.getPopupLayout().setFitItems(true);
|
||||
|
||||
speedItems[0] = menuItemSpeed.addSubItem(gallery_menu_speed_veryslow, R.drawable.msg_speed_0_2, LocaleController.getString("SpeedVerySlow", R.string.SpeedVerySlow)).setColors(0xfffafafa, 0xfffafafa);
|
||||
speedItems[1] = menuItemSpeed.addSubItem(gallery_menu_speed_slow, R.drawable.msg_speed_0_5, LocaleController.getString("SpeedSlow", R.string.SpeedSlow)).setColors(0xfffafafa, 0xfffafafa);
|
||||
speedItems[2] = menuItemSpeed.addSubItem(gallery_menu_speed_normal, R.drawable.msg_speed_1, LocaleController.getString("SpeedNormal", R.string.SpeedNormal)).setColors(0xfffafafa, 0xfffafafa);
|
||||
speedItems[3] = menuItemSpeed.addSubItem(gallery_menu_speed_fast, R.drawable.msg_speed_1_5, LocaleController.getString("SpeedFast", R.string.SpeedFast)).setColors(0xfffafafa, 0xfffafafa);
|
||||
speedItems[4] = menuItemSpeed.addSubItem(gallery_menu_speed_veryfast, R.drawable.msg_speed_2, LocaleController.getString("SpeedVeryFast", R.string.SpeedVeryFast)).setColors(0xfffafafa, 0xfffafafa);
|
||||
|
||||
menuItem.addSubItem(gallery_menu_openin, R.drawable.msg_openin, LocaleController.getString("OpenInExternalApp", R.string.OpenInExternalApp)).setColors(0xfffafafa, 0xfffafafa);
|
||||
menuItem.setContentDescription(LocaleController.getString("AccDescrMoreOptions", R.string.AccDescrMoreOptions));
|
||||
allMediaItem = menuItem.addSubItem(gallery_menu_showall, R.drawable.msg_media, LocaleController.getString("ShowAllMedia", R.string.ShowAllMedia));
|
||||
|
@ -4498,7 +4455,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat
|
|||
menuItem.addSubItem(gallery_menu_delete, R.drawable.msg_delete, LocaleController.getString("Delete", R.string.Delete)).setColors(0xfffafafa, 0xfffafafa);
|
||||
menuItem.addSubItem(gallery_menu_cancel_loading, R.drawable.msg_cancel, LocaleController.getString("StopDownload", R.string.StopDownload)).setColors(0xfffafafa, 0xfffafafa);
|
||||
menuItem.redrawPopup(0xf9222222);
|
||||
menuItemSpeed.redrawPopup(0xf9222222);
|
||||
setMenuItemIcon();
|
||||
|
||||
menuItem.setSubMenuDelegate(new ActionBarMenuItem.ActionBarSubMenuItemDelegate() {
|
||||
|
@ -6161,6 +6117,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat
|
|||
menuItem.setIcon(R.drawable.msg_more_2);
|
||||
speedItem.setSubtext(LocaleController.getString("SpeedVeryFast", R.string.SpeedVeryFast));
|
||||
}
|
||||
chooseSpeedLayout.update(currentVideoSpeed);
|
||||
}
|
||||
|
||||
private boolean checkInlinePermissions() {
|
||||
|
@ -9976,7 +9933,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat
|
|||
menuItem.hideSubItem(gallery_menu_edit_avatar);
|
||||
menuItem.hideSubItem(gallery_menu_set_as_main);
|
||||
menuItem.hideSubItem(gallery_menu_delete);
|
||||
menuItem.hideSubItem(gallery_menu_speed);
|
||||
speedItem.setVisibility(View.GONE);
|
||||
speedGap.setVisibility(View.GONE);
|
||||
actionBar.setTranslationY(0);
|
||||
|
||||
|
@ -10398,6 +10355,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat
|
|||
}
|
||||
menuItem.checkHideMenuItem();
|
||||
} else {
|
||||
speedItem.setVisibility(View.GONE);
|
||||
speedGap.setVisibility(View.GONE);
|
||||
menuItem.hideSubItem(gallery_menu_openin);
|
||||
menuItem.checkHideMenuItem();
|
||||
|
@ -10989,10 +10947,11 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat
|
|||
}
|
||||
}
|
||||
if (isVideo || isEmbedVideo) {
|
||||
speedItem.setVisibility(View.VISIBLE);
|
||||
speedGap.setVisibility(View.VISIBLE);
|
||||
menuItem.showSubItem(gallery_menu_speed);
|
||||
} else {
|
||||
menuItem.hideSubItem(gallery_menu_speed);
|
||||
speedItem.setVisibility(View.GONE);
|
||||
speedGap.setVisibility(View.GONE);
|
||||
menuItem.checkHideMenuItem();
|
||||
}
|
||||
|
@ -11453,7 +11412,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat
|
|||
Theme.createChatResources(null, true);
|
||||
CharSequence str;
|
||||
if (messageObject != null && !messageObject.messageOwner.entities.isEmpty()) {
|
||||
Spannable spannableString = SpannableString.valueOf(caption);
|
||||
Spannable spannableString = new SpannableString(caption);
|
||||
messageObject.addEntitiesToText(spannableString, true, false);
|
||||
if (messageObject.isVideo()) {
|
||||
MessageObject.addUrlsByPattern(messageObject.isOutOwner(), spannableString, false, 3, messageObject.getDuration(), false);
|
||||
|
|
|
@ -60,6 +60,7 @@ import android.view.accessibility.AccessibilityNodeInfo;
|
|||
import android.view.animation.AccelerateInterpolator;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.webkit.WebStorage;
|
||||
import android.webkit.WebView;
|
||||
import android.widget.EditText;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
|
@ -1525,6 +1526,14 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter.
|
|||
}
|
||||
return super.onTouchEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemsColor(int color, boolean isActionMode) {
|
||||
super.setItemsColor(color, isActionMode);
|
||||
if (!isActionMode && ttlIconView != null) {
|
||||
ttlIconView.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY));
|
||||
}
|
||||
}
|
||||
};
|
||||
actionBar.setBackgroundColor(Color.TRANSPARENT);
|
||||
actionBar.setItemsBackgroundColor(AvatarDrawable.getButtonColorForId(userId != 0 || ChatObject.isChannel(chatId, currentAccount) && !currentChat.megagroup ? 5 : chatId), false);
|
||||
|
@ -2432,6 +2441,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter.
|
|||
editItem.setContentDescription(LocaleController.getString("Edit", R.string.Edit));
|
||||
otherItem = menu.addItem(10, R.drawable.ic_ab_other);
|
||||
ttlIconView = new ImageView(context);
|
||||
ttlIconView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_actionBarDefaultIcon), PorterDuff.Mode.MULTIPLY));
|
||||
AndroidUtilities.updateViewVisibilityAnimated(ttlIconView, false, 0.8f, false);
|
||||
ttlIconView.setImageResource(R.drawable.msg_mini_autodelete_timer);
|
||||
otherItem.addView(ttlIconView, LayoutHelper.createFrame(12, 12, Gravity.CENTER_VERTICAL | Gravity.LEFT, 8, 2, 0, 0));
|
||||
|
@ -2729,12 +2739,21 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter.
|
|||
|
||||
@Override
|
||||
public void muteFor(int timeInSeconds) {
|
||||
getNotificationsController().muteUntil(did, timeInSeconds);
|
||||
if (BulletinFactory.canShowBulletin(ProfileActivity.this)) {
|
||||
BulletinFactory.createMuteBulletin(ProfileActivity.this, NotificationsController.SETTING_MUTE_CUSTOM, timeInSeconds, getResourceProvider()).show();
|
||||
}
|
||||
if (notificationsRow >= 0) {
|
||||
listAdapter.notifyItemChanged(notificationsRow);
|
||||
if (timeInSeconds == 0) {
|
||||
if (getMessagesController().isDialogMuted(did)) {
|
||||
toggleMute();
|
||||
}
|
||||
if (BulletinFactory.canShowBulletin(ProfileActivity.this)) {
|
||||
BulletinFactory.createMuteBulletin(ProfileActivity.this, NotificationsController.SETTING_MUTE_UNMUTE, timeInSeconds, getResourceProvider()).show();
|
||||
}
|
||||
} else {
|
||||
getNotificationsController().muteUntil(did, timeInSeconds);
|
||||
if (BulletinFactory.canShowBulletin(ProfileActivity.this)) {
|
||||
BulletinFactory.createMuteBulletin(ProfileActivity.this, NotificationsController.SETTING_MUTE_CUSTOM, timeInSeconds, getResourceProvider()).show();
|
||||
}
|
||||
if (notificationsRow >= 0) {
|
||||
listAdapter.notifyItemChanged(notificationsRow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2919,7 +2938,8 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter.
|
|||
BuildVars.DEBUG_PRIVATE_VERSION ? "Clean app update" : null,
|
||||
BuildVars.DEBUG_PRIVATE_VERSION ? "Reset suggestions" : null,
|
||||
BuildVars.DEBUG_PRIVATE_VERSION ? LocaleController.getString(SharedConfig.forceRtmpStream ? R.string.DebugMenuDisableForceRtmpStreamFlag : R.string.DebugMenuEnableForceRtmpStreamFlag) : null,
|
||||
BuildVars.DEBUG_PRIVATE_VERSION ? LocaleController.getString(R.string.DebugMenuClearWebViewCache) : null
|
||||
BuildVars.DEBUG_PRIVATE_VERSION ? LocaleController.getString(R.string.DebugMenuClearWebViewCache) : null,
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT ? LocaleController.getString(R.string.DebugMenuEnableWebViewDebug) : null
|
||||
};
|
||||
builder.setItems(items, (dialog, which) -> {
|
||||
if (which == 0) {
|
||||
|
@ -2996,6 +3016,11 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter.
|
|||
ApplicationLoader.applicationContext.deleteDatabase("webview.db");
|
||||
ApplicationLoader.applicationContext.deleteDatabase("webviewCache.db");
|
||||
WebStorage.getInstance().deleteAllData();
|
||||
} else if (which == 19) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
WebView.setWebContentsDebuggingEnabled(true);
|
||||
Toast.makeText(getParentActivity(), LocaleController.getString(R.string.DebugMenuWebViewDebugEnabled), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null);
|
||||
|
|
|
@ -504,7 +504,11 @@ public class ProfileNotificationsActivity extends BaseFragment implements Notifi
|
|||
@Override
|
||||
public void didReceivedNotification(int id, int account, Object... args) {
|
||||
if (id == NotificationCenter.notificationsSettingsUpdated) {
|
||||
adapter.notifyDataSetChanged();
|
||||
try {
|
||||
adapter.notifyDataSetChanged();
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -520,8 +524,13 @@ public class ProfileNotificationsActivity extends BaseFragment implements Notifi
|
|||
RecyclerListView.Holder holder = (RecyclerListView.Holder) listView.getChildViewHolder(child);
|
||||
int type = holder.getItemViewType();
|
||||
int position = holder.getAdapterPosition();
|
||||
if (position != customRow && position != enableRow && type != 0) {
|
||||
if (position != customRow && position != enableRow) {
|
||||
switch (type) {
|
||||
case 0: {
|
||||
HeaderCell textCell = (HeaderCell) holder.itemView;
|
||||
textCell.setEnabled(customEnabled && notificationsEnabled, animators);
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
TextSettingsCell textCell = (TextSettingsCell) holder.itemView;
|
||||
textCell.setEnabled(customEnabled && notificationsEnabled, animators);
|
||||
|
@ -681,7 +690,11 @@ public class ProfileNotificationsActivity extends BaseFragment implements Notifi
|
|||
long documentId = preferences.getLong("sound_document_id_" + dialogId, 0);
|
||||
if (documentId != 0) {
|
||||
TLRPC.Document document = getMediaDataController().ringtoneDataStore.getDocument(documentId);
|
||||
value = NotificationsSoundActivity.trimTitle(document, document.file_name_fixed);
|
||||
if (document == null) {
|
||||
value = LocaleController.getString("CustomSound", R.string.CustomSound);
|
||||
} else {
|
||||
value = NotificationsSoundActivity.trimTitle(document, document.file_name_fixed);
|
||||
}
|
||||
} else if (value.equals("NoSound")) {
|
||||
value = LocaleController.getString("NoSound", R.string.NoSound);
|
||||
} else if (value.equals("Default")) {
|
||||
|
@ -840,35 +853,38 @@ public class ProfileNotificationsActivity extends BaseFragment implements Notifi
|
|||
|
||||
@Override
|
||||
public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
|
||||
if (holder.getItemViewType() != 0) {
|
||||
switch (holder.getItemViewType()) {
|
||||
case 1: {
|
||||
TextSettingsCell textCell = (TextSettingsCell) holder.itemView;
|
||||
textCell.setEnabled(customEnabled && notificationsEnabled, null);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
TextInfoPrivacyCell textCell = (TextInfoPrivacyCell) holder.itemView;
|
||||
textCell.setEnabled(customEnabled && notificationsEnabled, null);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
TextColorCell textCell = (TextColorCell) holder.itemView;
|
||||
textCell.setEnabled(customEnabled && notificationsEnabled, null);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
RadioCell radioCell = (RadioCell) holder.itemView;
|
||||
radioCell.setEnabled(customEnabled && notificationsEnabled, null);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
TextCheckCell checkCell = (TextCheckCell) holder.itemView;
|
||||
if (holder.getAdapterPosition() == previewRow) {
|
||||
checkCell.setEnabled(customEnabled && notificationsEnabled, null);
|
||||
} else {
|
||||
checkCell.setEnabled(true, null);
|
||||
}
|
||||
switch (holder.getItemViewType()) {
|
||||
case 0: {
|
||||
HeaderCell textCell = (HeaderCell) holder.itemView;
|
||||
textCell.setEnabled(customEnabled && notificationsEnabled, null);
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
TextSettingsCell textCell = (TextSettingsCell) holder.itemView;
|
||||
textCell.setEnabled(customEnabled && notificationsEnabled, null);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
TextInfoPrivacyCell textCell = (TextInfoPrivacyCell) holder.itemView;
|
||||
textCell.setEnabled(customEnabled && notificationsEnabled, null);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
TextColorCell textCell = (TextColorCell) holder.itemView;
|
||||
textCell.setEnabled(customEnabled && notificationsEnabled, null);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
RadioCell radioCell = (RadioCell) holder.itemView;
|
||||
radioCell.setEnabled(customEnabled && notificationsEnabled, null);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
TextCheckCell checkCell = (TextCheckCell) holder.itemView;
|
||||
if (holder.getAdapterPosition() == previewRow) {
|
||||
checkCell.setEnabled(customEnabled && notificationsEnabled, null);
|
||||
} else {
|
||||
checkCell.setEnabled(true, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -904,11 +904,6 @@ public class SessionsActivity extends BaseFragment implements NotificationCenter
|
|||
colors[6] = 0x212020;
|
||||
colors[7] = Theme.getColor(Theme.key_windowBackgroundWhite);
|
||||
|
||||
// imageView.replaceColors(colors);
|
||||
// imageView.setAnimation(R.raw.qr_login, 230, 230, colors);
|
||||
// imageView.setScaleType(ImageView.ScaleType.CENTER);
|
||||
// imageView.playAnimation();
|
||||
|
||||
textView = new TextView(context);
|
||||
addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 36, 152, 36, 0));
|
||||
textView.setGravity(Gravity.CENTER_HORIZONTAL);
|
||||
|
|
Before Width: | Height: | Size: 755 B After Width: | Height: | Size: 898 B |
Before Width: | Height: | Size: 638 B After Width: | Height: | Size: 809 B |
Before Width: | Height: | Size: 509 B After Width: | Height: | Size: 586 B |
Before Width: | Height: | Size: 444 B After Width: | Height: | Size: 532 B |
Before Width: | Height: | Size: 984 B After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 829 B After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.4 KiB |
|
@ -186,6 +186,7 @@
|
|||
<string name="AutoDelete1Hour">1 hour</string>
|
||||
<string name="AutoDeleteInfo">Automatically delete new messages sent in this chat after a certain period of time.</string>
|
||||
<string name="AutoDeleteNever">Never</string>
|
||||
<string name="MuteNever">Never</string>
|
||||
<string name="AutoDeleteSet">Set for this chat</string>
|
||||
<string name="AutoDeleteSetInfo">Messages in this chat are automatically\ndeleted %1$s after they are sent.</string>
|
||||
<string name="AutoDeleteHintOnText">New messages in this chat will be automatically deleted in %1$s.</string>
|
||||
|
@ -230,7 +231,7 @@
|
|||
<string name="CompatibilityChat">%1$s is using an older version of Telegram, so secret photos will be shown in compatibility mode.\n\nOnce %2$s updates Telegram, photos with timers for 1 minute or less will start working in \'Tap and hold to view\' mode, and you will be notified whenever the other party takes a screenshot.</string>
|
||||
<string name="SearchMessages">Messages</string>
|
||||
<string name="Search">Search</string>
|
||||
<string name="MuteNotifications">Mute notifications</string>
|
||||
<string name="MuteNotifications">Mute forever</string>
|
||||
<string name="MuteFor">Mute for %1$s</string>
|
||||
<string name="MuteFor1h">Mute for 1 hour</string>
|
||||
<string name="UnmuteNotifications">Unmute</string>
|
||||
|
@ -2150,6 +2151,8 @@
|
|||
<string name="DebugMenuEnableForceRtmpStreamFlag">Enable forced RTMP Stream Flag</string>
|
||||
<string name="DebugMenuDisableForceRtmpStreamFlag">Disable forced RTMP Stream Flag</string>
|
||||
<string name="DebugMenuClearWebViewCache">Clear WebView cache</string>
|
||||
<string name="DebugMenuEnableWebViewDebug">Enable WebView debug</string>
|
||||
<string name="DebugMenuWebViewDebugEnabled">Debug enabled!</string>
|
||||
<string name="ChangeLanguageLater">You can change your language later in Settings.</string>
|
||||
<string name="ChooseYourLanguage">Choose your language</string>
|
||||
<string name="ChooseYourLanguageOther">Other</string>
|
||||
|
@ -3335,6 +3338,7 @@
|
|||
<string name="UserChannelTooMuchJoin">Sorry, the target user is a member of too many groups and channels. Please ask them to leave some first.</string>
|
||||
<!--Alert messages-->
|
||||
<string name="DeleteAllMessagesAlert">Warning! This will **delete all messages** in this chat for **both** participants.</string>
|
||||
<string name="DeleteAllMessagesChannelAlert">Warning! This will **delete all messages** in this channel.</string>
|
||||
<string name="DeleteAllMessagesSavedAlert">Warning! This will **delete all messages** in this chat.</string>
|
||||
<string name="DeleteAll">Delete All</string>
|
||||
<string name="StopLoading">Stop loading?</string>
|
||||
|
@ -3406,7 +3410,8 @@
|
|||
<string name="StopVerification">Do you want to stop the verification process?</string>
|
||||
<string name="AreYouSureClearHistoryWithUser">Are you sure you want to clear your chat history with **%1$s**?</string>
|
||||
<string name="AreYouSureClearHistoryWithSecretUser">Are you sure you want to clear your secret chat history with **%1$s**?</string>
|
||||
<string name="AreYouSureClearHistoryWithChat">Are you sure you want to clear the chat history in **%1$s**?</string>
|
||||
<string name="AreYouSureClearHistoryWithChat">Are you sure you want to clear the chat history in **%1$s** for all users?</string>
|
||||
<string name="AreYouSureClearHistoryWithChannel">Are you sure you want to clear the channel history in **%1$s**?</string>
|
||||
<string name="AreYouSureClearHistory">Are you sure you want to delete all messages in this chat?</string>
|
||||
<string name="AreYouSureClearHistorySavedMessages">Are you sure you want to clear **Saved Messages**?</string>
|
||||
<string name="AreYouSureClearHistoryChannel">Delete all cached text and media from this channel?</string>
|
||||
|
@ -5067,38 +5072,38 @@
|
|||
<string name="NobodyViewed">Nobody viewed</string>
|
||||
<string name="QrCode">QR Code</string>
|
||||
|
||||
<string name="PushReactText">%1$s: %2$s to your "%3$s"</string>
|
||||
<string name="PushReactNoText">%1$s: %2$s to your message</string>
|
||||
<string name="PushReactPhoto">%1$s: %2$s to your photo</string>
|
||||
<string name="PushReactVideo">%1$s: %2$s to your video</string>
|
||||
<string name="PushReactRound">%1$s: %2$s to your video message</string>
|
||||
<string name="PushReactDoc">%1$s: %2$s to your file</string>
|
||||
<string name="PushReactSticker">%1$s: %2$s to your %3$s sticker</string>
|
||||
<string name="PushReactAudio">%1$s: %2$s to your voice message</string>
|
||||
<string name="PushReactContect">%1$s: %2$s to your contact %3$s</string>
|
||||
<string name="PushReactGeo">%1$s: %2$s to your map</string>
|
||||
<string name="PushReactGeoLocation">%1$s: %2$s to your live location</string>
|
||||
<string name="PushReactPoll">%1$s: %2$s to your poll %3$s</string>
|
||||
<string name="PushReactQuiz">%1$s: %2$s to your quiz %3$s</string>
|
||||
<string name="PushReactGame">%1$s: %2$s to your game</string>
|
||||
<string name="PushReactInvoice">%1$s: %2$s to your invoice</string>
|
||||
<string name="PushReactGif">%1$s: %2$s to your GIF</string>
|
||||
<string name="PushChatReactText">%1$s: %3$s in %2$s to your "%4$s"</string>
|
||||
<string name="PushChatReactNotext">%1$s: %3$s to your message in %2$s</string>
|
||||
<string name="PushChatReactPhoto">%1$s: %3$s to your photo in %2$s</string>
|
||||
<string name="PushChatReactVideo">%1$s: %3$s to your video in %2$s</string>
|
||||
<string name="PushChatReactRound">%1$s: %3$s to your video message in %2$s</string>
|
||||
<string name="PushChatReactDoc">%1$s: %3$s to your file in %2$s</string>
|
||||
<string name="PushChatReactSticker">%1$s: %3$s to your %4$s sticker in %2$s</string>
|
||||
<string name="PushChatReactAudio">%1$s: %3$s to your voice message in %2$s</string>
|
||||
<string name="PushChatReactContact">%1$s: %3$s to your contact %4$s in %2$s</string>
|
||||
<string name="PushChatReactGeo">%1$s: %3$s to your map in %2$s</string>
|
||||
<string name="PushChatReactGeoLive">%1$s: %3$s to your live location in %2$s</string>
|
||||
<string name="PushChatReactPoll">%1$s: %3$s to your poll %4$s in %2$s</string>
|
||||
<string name="PushChatReactQuiz">%1$s: %3$s to your quiz %4$s in %2$s</string>
|
||||
<string name="PushChatReactGame">%1$s: %3$s to your game in %2$s</string>
|
||||
<string name="PushChatReactInvoice">%1$s: %3$s to your invoice in %2$s</string>
|
||||
<string name="PushChatReactGif">%1$s: %3$s to your GIF in %2$s</string>
|
||||
<string name="PushReactText">%2$s to your "%3$s"</string>
|
||||
<string name="PushReactNoText">%2$s to your message</string>
|
||||
<string name="PushReactPhoto">%2$s to your photo</string>
|
||||
<string name="PushReactVideo">%2$s to your video</string>
|
||||
<string name="PushReactRound">%2$s to your video message</string>
|
||||
<string name="PushReactDoc">%2$s to your file</string>
|
||||
<string name="PushReactSticker">%2$s to your %3$s sticker</string>
|
||||
<string name="PushReactAudio">%2$s to your voice message</string>
|
||||
<string name="PushReactContect">%2$s to your contact %3$s</string>
|
||||
<string name="PushReactGeo">%2$s to your map</string>
|
||||
<string name="PushReactGeoLocation">%2$s to your live location</string>
|
||||
<string name="PushReactPoll">%2$s to your poll %3$s</string>
|
||||
<string name="PushReactQuiz">%2$s to your quiz %3$s</string>
|
||||
<string name="PushReactGame">%2$s to your game</string>
|
||||
<string name="PushReactInvoice">%2$s to your invoice</string>
|
||||
<string name="PushReactGif">%2$s to your GIF</string>
|
||||
<string name="PushChatReactText">%3$s in %2$s to your "%4$s"</string>
|
||||
<string name="PushChatReactNotext">%3$s to your message in %2$s</string>
|
||||
<string name="PushChatReactPhoto">%3$s to your photo in %2$s</string>
|
||||
<string name="PushChatReactVideo">%3$s to your video in %2$s</string>
|
||||
<string name="PushChatReactRound">%3$s to your video message in %2$s</string>
|
||||
<string name="PushChatReactDoc">%3$s to your file in %2$s</string>
|
||||
<string name="PushChatReactSticker">%3$s to your %4$s sticker in %2$s</string>
|
||||
<string name="PushChatReactAudio">%3$s to your voice message in %2$s</string>
|
||||
<string name="PushChatReactContact">%3$s to your contact %4$s in %2$s</string>
|
||||
<string name="PushChatReactGeo">%3$s to your map in %2$s</string>
|
||||
<string name="PushChatReactGeoLive">%3$s to your live location in %2$s</string>
|
||||
<string name="PushChatReactPoll">%3$s to your poll %4$s in %2$s</string>
|
||||
<string name="PushChatReactQuiz">%3$s to your quiz %4$s in %2$s</string>
|
||||
<string name="PushChatReactGame">%3$s to your game in %2$s</string>
|
||||
<string name="PushChatReactInvoice">%3$s to your invoice in %2$s</string>
|
||||
<string name="PushChatReactGif">%3$s to your GIF in %2$s</string>
|
||||
<string name="BlurInChat">Blur in chat</string>
|
||||
|
||||
<string name="AllowBackgroundActivity">Allow background activity</string>
|
||||
|
@ -5186,6 +5191,7 @@
|
|||
<string name="BotCantAddToAttachMenu">This bot can\'t be added to the attachment menu.</string>
|
||||
<string name="BotAlreadyAddedToAttachMenu">This bot is already in your attachment menu.</string>
|
||||
<string name="BotRemoveFromMenu">Remove **%1$s** from the attachment menu?</string>
|
||||
<string name="BotRemoveInlineFromMenu">Remove **%1$s** from suggestions?</string>
|
||||
<string name="BotWebViewRequestGeolocationPermission">Allow **%1$s** to access to your location?\n\nThe developer of **%1$s** will be able to access your location when this web app is open.</string>
|
||||
<string name="BotWebViewRequestGeolocationPermissionWithHint">Allow **%1$s** to access to your location?\n\nThe developer of **%1$s** will be able to access your location when this web app is open.\n\nGo to Settings > Permissions and turn **Location** on to share location data.</string>
|
||||
<string name="BotWebViewRequestMicrophonePermission">Allow **%1$s** to access to your microphone?\n\nThe developer of **%1$s** will be able to access your microphone when this web app is open.</string>
|
||||
|
@ -5202,4 +5208,8 @@
|
|||
<string name="DeleteTonesMessage_few">Do you want to delete **%d notification sounds**?</string>
|
||||
<string name="DeleteTonesMessage_many">Do you want to delete **%d notification sounds**?</string>
|
||||
<string name="DeleteTonesMessage_other">Do you want to delete **%d notification sounds**?</string>
|
||||
<string name="CustomSound">Custom Sound</string>
|
||||
<string name="ClearForAll">Clear for all</string>
|
||||
<string name="ClearForMe">Clear for me</string>
|
||||
<string name="BotWebViewNotAvailablePlaceholder">WebView not available. Please update it to use WebView bots.</string>
|
||||
</resources>
|
||||
|
|