diff --git a/TMessagesProj/build.gradle b/TMessagesProj/build.gradle index c9d34b6a23..61318786e3 100644 --- a/TMessagesProj/build.gradle +++ b/TMessagesProj/build.gradle @@ -3,12 +3,12 @@ import cn.hutool.core.util.RuntimeUtil apply plugin: "com.android.application" apply plugin: "kotlin-android" -def verName = "9.5.8" -def verCode = 1128 +def verName = "9.7.6" +def verCode = 1130 -def officialVer = "9.5.8" -def officialCode = 3252 +def officialVer = "9.7.6" +def officialCode = 3721 def serviceAccountCredentialsFile = rootProject.file("service_account_credentials.json") diff --git a/TMessagesProj/jni/CMakeLists.txt b/TMessagesProj/jni/CMakeLists.txt index 9582f24698..0474a9dc21 100644 --- a/TMessagesProj/jni/CMakeLists.txt +++ b/TMessagesProj/jni/CMakeLists.txt @@ -494,7 +494,7 @@ target_include_directories(breakpad PUBLIC #voip include(${CMAKE_HOME_DIRECTORY}/voip/CMakeLists.txt) -set(NATIVE_LIB "tmessages.44") +set(NATIVE_LIB "tmessages.45") #tmessages add_library(${NATIVE_LIB} SHARED diff --git a/TMessagesProj/jni/TgNetWrapper.cpp b/TMessagesProj/jni/TgNetWrapper.cpp index 19ed6db338..51bffcb82a 100644 --- a/TMessagesProj/jni/TgNetWrapper.cpp +++ b/TMessagesProj/jni/TgNetWrapper.cpp @@ -153,6 +153,10 @@ void cancelRequest(JNIEnv *env, jclass c, jint instanceNum, jint token, jboolean return ConnectionsManager::getInstance(instanceNum).cancelRequest(token, notifyServer); } +void failNotRunningRequest(JNIEnv *env, jclass c, jint instanceNum, jint token) { + return ConnectionsManager::getInstance(instanceNum).failNotRunningRequest(token); +} + void cleanUp(JNIEnv *env, jclass c, jint instanceNum, jboolean resetKeys) { return ConnectionsManager::getInstance(instanceNum).cleanUp(resetKeys, -1); } @@ -230,7 +234,7 @@ void resumeNetwork(JNIEnv *env, jclass c, jint instanceNum, jboolean partial) { } void updateDcSettings(JNIEnv *env, jclass c, jint instanceNum) { - ConnectionsManager::getInstance(instanceNum).updateDcSettings(0, false); + ConnectionsManager::getInstance(instanceNum).updateDcSettings(0, false, false); } void setIpStrategy(JNIEnv *env, jclass c, jint instanceNum, jbyte value) { @@ -405,6 +409,10 @@ onHostNameResolved(JNIEnv *env, jclass c, jstring host, jlong address, jstring i socket->onHostNameResolved(h, i, ipv6); } +void discardConnection(JNIEnv *env, jclass c, jint instanceNum, jint datacenerId, jint connectionType) { + ConnectionsManager::getInstance(instanceNum).reconnect(datacenerId, connectionType); +} + void setLangCode(JNIEnv *env, jclass c, jint instanceNum, jstring langCode) { const char *langCodeStr = env->GetStringUTFChars(langCode, 0); @@ -530,21 +538,22 @@ static JNINativeMethod ConnectionsManagerMethods[] = { {"native_getConnectionState", "(I)I", (void *) getConnectionState}, {"native_setUserId", "(IJ)V", (void *) setUserId}, {"native_init", "(IIIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IJZZII)V", (void *) init}, - {"native_setLangCode", "(ILjava/lang/String;)V", (void *) setLangCode}, - {"native_setRegId", "(ILjava/lang/String;)V", (void *) setRegId}, - {"native_setSystemLangCode", "(ILjava/lang/String;)V", (void *) setSystemLangCode}, - {"native_switchBackend", "(IZ)V", (void *) switchBackend}, - {"native_pauseNetwork", "(I)V", (void *) pauseNetwork}, - {"native_resumeNetwork", "(IZ)V", (void *) resumeNetwork}, - {"native_updateDcSettings", "(I)V", (void *) updateDcSettings}, - {"native_setIpStrategy", "(IB)V", (void *) setIpStrategy}, - {"native_setNetworkAvailable", "(IZIZ)V", (void *) setNetworkAvailable}, - {"native_setPushConnectionEnabled", "(IZ)V", (void *) setPushConnectionEnabled}, - {"native_setJava", "(Z)V", (void *) setJava}, - {"native_setJava", "(I)V", (void *) setJava1}, - {"native_applyDnsConfig", "(IJLjava/lang/String;I)V", (void *) applyDnsConfig}, - {"native_checkProxy", "(ILjava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Lorg/telegram/tgnet/RequestTimeDelegate;)J", (void *) checkProxy}, - {"native_onHostNameResolved", "(Ljava/lang/String;JLjava/lang/String;Z)V", (void *) onHostNameResolved} + {"native_setLangCode", "(ILjava/lang/String;)V", (void *) setLangCode}, + {"native_setRegId", "(ILjava/lang/String;)V", (void *) setRegId}, + {"native_setSystemLangCode", "(ILjava/lang/String;)V", (void *) setSystemLangCode}, + {"native_switchBackend", "(IZ)V", (void *) switchBackend}, + {"native_pauseNetwork", "(I)V", (void *) pauseNetwork}, + {"native_resumeNetwork", "(IZ)V", (void *) resumeNetwork}, + {"native_updateDcSettings", "(I)V", (void *) updateDcSettings}, + {"native_setIpStrategy", "(IB)V", (void *) setIpStrategy}, + {"native_setNetworkAvailable", "(IZIZ)V", (void *) setNetworkAvailable}, + {"native_setPushConnectionEnabled", "(IZ)V", (void *) setPushConnectionEnabled}, + {"native_setJava", "(Z)V", (void *) setJava}, + {"native_applyDnsConfig", "(IJLjava/lang/String;I)V", (void *) applyDnsConfig}, + {"native_checkProxy", "(ILjava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Lorg/telegram/tgnet/RequestTimeDelegate;)J", (void *) checkProxy}, + {"native_onHostNameResolved", "(Ljava/lang/String;JLjava/lang/String;Z)V", (void *) onHostNameResolved}, + {"native_discardConnection", "(III)V", (void *) discardConnection}, + {"native_failNotRunningRequest", "(II)V", (void *) failNotRunningRequest}, }; inline int registerNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, diff --git a/TMessagesProj/jni/ffmpeg b/TMessagesProj/jni/ffmpeg deleted file mode 160000 index 4bc4cafaef..0000000000 --- a/TMessagesProj/jni/ffmpeg +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4bc4cafaef8a55462138d7b6f7579c1522de26dc diff --git a/TMessagesProj/jni/gifvideo.cpp b/TMessagesProj/jni/gifvideo.cpp index 5de5d4c34e..b5a460b495 100644 --- a/TMessagesProj/jni/gifvideo.cpp +++ b/TMessagesProj/jni/gifvideo.cpp @@ -40,6 +40,7 @@ static const std::string av_make_error_str(int errnum) { jclass jclass_AnimatedFileDrawableStream; jmethodID jclass_AnimatedFileDrawableStream_read; jmethodID jclass_AnimatedFileDrawableStream_cancel; +jmethodID jclass_AnimatedFileDrawableStream_isCanceled; jmethodID jclass_AnimatedFileDrawableStream_isFinishedLoadingFile; jmethodID jclass_AnimatedFileDrawableStream_getFinishedFilePath; @@ -792,7 +793,7 @@ int readCallback(void *opaque, uint8_t *buf, int buf_size) { javaVm->DetachCurrentThread(); } if (buf_size == 0) { - return AVERROR_EOF; + return AVERROR_EXIT; } int ret = (int) read(info->fd, buf, (size_t) buf_size); if (ret <= 0) { @@ -1035,6 +1036,26 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_telegram_ui_Components_AnimatedFileD dataArr[2] = 0; } dataArr[4] = (int32_t) (info->fmt_ctx->duration * 1000 / AV_TIME_BASE); + int video_stream_index = -1; + double fps = 30.0; + for (int i = 0; i < info->fmt_ctx->nb_streams; i++) { + if (info->fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + video_stream_index = i; + break; + } + } + if (video_stream_index != -1) { + AVStream* video_stream = info->fmt_ctx->streams[video_stream_index]; + if (video_stream->avg_frame_rate.den && video_stream->avg_frame_rate.num) { + fps = av_q2d(video_stream->avg_frame_rate); + } else if(video_stream->r_frame_rate.den && video_stream->r_frame_rate.num) { + fps = av_q2d(video_stream->r_frame_rate); + } else { + int ticks = video_stream->codec->ticks_per_frame; + fps = 1.0 / (ticks * av_q2d(video_stream->time_base)); + } + } + dataArr[5] = (int32_t) fps; //(int32_t) (1000 * info->video_stream->duration * av_q2d(info->video_stream->time_base)); env->ReleaseIntArrayElements(data, dataArr, 0); } @@ -1102,7 +1123,7 @@ extern "C" JNIEXPORT void JNICALL Java_org_telegram_ui_Components_AnimatedFileDr return; } int got_frame = 0; - int32_t tries = 1000; + int32_t tries = 100; while (tries > 0) { if (info->pkt.size == 0) { ret = av_read_frame(info->fmt_ctx, &info->pkt); @@ -1235,6 +1256,26 @@ extern "C" JNIEXPORT int JNICALL Java_org_telegram_ui_Components_AnimatedFileDra int32_t tries = 1000; bool readNextPacket = true; while (tries > 0) { + if (info->stream != nullptr) { + JNIEnv *jniEnv = nullptr; + JavaVMAttachArgs jvmArgs; + jvmArgs.version = JNI_VERSION_1_6; + + bool attached; + if (JNI_EDETACHED == javaVm->GetEnv((void **) &jniEnv, JNI_VERSION_1_6)) { + javaVm->AttachCurrentThread(&jniEnv, &jvmArgs); + attached = true; + } else { + attached = false; + } + jboolean canceled = jniEnv->CallBooleanMethod(info->stream, jclass_AnimatedFileDrawableStream_isCanceled); + if (attached) { + javaVm->DetachCurrentThread(); + } + if (canceled) { + return 0; + } + } if (info->pkt.size == 0 && readNextPacket) { ret = av_read_frame(info->fmt_ctx, &info->pkt); if (ret >= 0) { @@ -1309,6 +1350,26 @@ extern "C" JNIEXPORT jint JNICALL Java_org_telegram_ui_Components_AnimatedFileDr int32_t triesCount = preview ? 50 : 6; //info->has_decoded_frames = false; while (!info->stopped && triesCount != 0) { + if (info->stream != nullptr) { + JNIEnv *jniEnv = nullptr; + JavaVMAttachArgs jvmArgs; + jvmArgs.version = JNI_VERSION_1_6; + + bool attached; + if (JNI_EDETACHED == javaVm->GetEnv((void **) &jniEnv, JNI_VERSION_1_6)) { + javaVm->AttachCurrentThread(&jniEnv, &jvmArgs); + attached = true; + } else { + attached = false; + } + jboolean canceled = jniEnv->CallBooleanMethod(info->stream, jclass_AnimatedFileDrawableStream_isCanceled); + if (attached) { + javaVm->DetachCurrentThread(); + } + if (canceled) { + return 0; + } + } if (info->pkt.size == 0) { ret = av_read_frame(info->fmt_ctx, &info->pkt); if (ret >= 0) { @@ -1400,6 +1461,10 @@ extern "C" jint videoOnJNILoad(JavaVM *vm, JNIEnv *env) { if (jclass_AnimatedFileDrawableStream_isFinishedLoadingFile == 0) { return JNI_FALSE; } + jclass_AnimatedFileDrawableStream_isCanceled = env->GetMethodID(jclass_AnimatedFileDrawableStream, "isCanceled", "()Z"); + if (jclass_AnimatedFileDrawableStream_isCanceled == 0) { + return JNI_FALSE; + } jclass_AnimatedFileDrawableStream_getFinishedFilePath = env->GetMethodID(jclass_AnimatedFileDrawableStream, "getFinishedFilePath", "()Ljava/lang/String;"); if (jclass_AnimatedFileDrawableStream_getFinishedFilePath == 0) { return JNI_FALSE; diff --git a/TMessagesProj/jni/lottie.cpp b/TMessagesProj/jni/lottie.cpp index d5e2a6ef90..c86d7d13dc 100644 --- a/TMessagesProj/jni/lottie.cpp +++ b/TMessagesProj/jni/lottie.cpp @@ -163,6 +163,31 @@ JNIEXPORT jlong Java_org_telegram_ui_Components_RLottieDrawable_create(JNIEnv *e return (jlong) (intptr_t) info; } +JNIEXPORT jlong Java_org_telegram_ui_Components_RLottieDrawable_getFramesCount(JNIEnv *env, jclass clazz, jstring src, jstring json) { + auto info = new LottieInfo(); + char const *srcString = env->GetStringUTFChars(src, nullptr); + info->path = srcString; + if (json != nullptr) { + char const *jsonString = env->GetStringUTFChars(json, nullptr); + if (jsonString) { + info->animation = rlottie::Animation::loadFromData(jsonString, info->path, nullptr, FitzModifier::None); + env->ReleaseStringUTFChars(json, jsonString); + } + } else { + info->animation = rlottie::Animation::loadFromFile(info->path, nullptr, FitzModifier::None); + } + if (srcString) { + env->ReleaseStringUTFChars(src, srcString); + } + if (info->animation == nullptr) { + delete info; + return 0; + } + long frameCount = info->animation->totalFrame(); + delete info; + return (jlong) frameCount; +} + JNIEXPORT jlong Java_org_telegram_ui_Components_RLottieDrawable_createWithJson(JNIEnv *env, jclass clazz, jstring json, jstring name, jintArray data, jintArray colorReplacement) { std::map *colors = nullptr; if (colorReplacement != nullptr) { diff --git a/TMessagesProj/jni/tgnet/ApiScheme.cpp b/TMessagesProj/jni/tgnet/ApiScheme.cpp index 2c0881e048..9e61cfb0ff 100644 --- a/TMessagesProj/jni/tgnet/ApiScheme.cpp +++ b/TMessagesProj/jni/tgnet/ApiScheme.cpp @@ -515,11 +515,15 @@ void TL_user::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &er usernames.push_back(std::unique_ptr(object)); } } + if ((flags2 & 32) != 0) { + stories_max_id = stream->readInt32(&error); + } } void TL_user::serializeToStream(NativeByteBuffer *stream) { stream->writeInt32(constructor); stream->writeInt32(flags); + stream->writeInt32(flags2); stream->writeInt64(id); if ((flags & 1) != 0) { stream->writeInt64(access_hash); diff --git a/TMessagesProj/jni/tgnet/ApiScheme.h b/TMessagesProj/jni/tgnet/ApiScheme.h index fe1e9cfd99..17926c7cf3 100644 --- a/TMessagesProj/jni/tgnet/ApiScheme.h +++ b/TMessagesProj/jni/tgnet/ApiScheme.h @@ -335,6 +335,7 @@ class User : public TLObject { std::string bot_inline_placeholder; std::string lang_code; std::vector> usernames; + int32_t stories_max_id; static User *TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error); }; @@ -351,7 +352,7 @@ class TL_userEmpty : public User { class TL_user : public User { public: - static const uint32_t constructor = 0x8f97c628; + static const uint32_t constructor = 0xabb5f120; void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error); void serializeToStream(NativeByteBuffer *stream); diff --git a/TMessagesProj/jni/tgnet/ConnectionsManager.cpp b/TMessagesProj/jni/tgnet/ConnectionsManager.cpp index b775eef6f5..e88b31ba97 100644 --- a/TMessagesProj/jni/tgnet/ConnectionsManager.cpp +++ b/TMessagesProj/jni/tgnet/ConnectionsManager.cpp @@ -295,7 +295,7 @@ void ConnectionsManager::select() { sendPing(datacenter, false); } if (abs((int32_t) (now / 1000) - lastDcUpdateTime) >= DC_UPDATE_TIME) { - updateDcSettings(0, false); + updateDcSettings(0, false, false); } processRequestQueue(0, 0); } else if (!datacenter->isHandshakingAny()) { @@ -403,6 +403,9 @@ void ConnectionsManager::loadConfig() { auto datacenter = new Datacenter(instanceNum, buffer); datacenters[datacenter->getDatacenterId()] = datacenter; if (LOGS_ENABLED) DEBUG_D("datacenter(%p) %u loaded (hasAuthKey = %d, 0x%" PRIx64 ")", datacenter, datacenter->getDatacenterId(), (int) datacenter->hasPermanentAuthKey(), datacenter->getPermanentAuthKeyId()); + if (datacenter->isCdnDatacenter && !datacenter->hasPermanentAuthKey()) { + datacenter->clearAuthKey(HandshakeTypePerm); + } } } } @@ -668,6 +671,9 @@ void ConnectionsManager::cleanUp(bool resetKeys, int32_t datacenterId) { } void ConnectionsManager::onConnectionClosed(Connection *connection, int reason) { + if (reason == 1) { + lastProtocolUsefullData = false; + } Datacenter *datacenter = connection->getDatacenter(); if ((connection->getConnectionType() == ConnectionTypeGeneric || connection->getConnectionType() == ConnectionTypeGenericMedia) && datacenter->isHandshakingAny()) { datacenter->onHandshakeConnectionClosed(connection); @@ -823,7 +829,7 @@ void ConnectionsManager::onConnectionDataReceived(Connection *connection, Native delegate->onProxyError(instanceNum); } } else if (code == -404 && (datacenter->isCdnDatacenter || PFS_ENABLED)) { - if (!datacenter->isHandshaking(connection->isMediaConnection)) { + if (!datacenter->isHandshaking(connection->isMediaConnection) || datacenter->isCdnDatacenter) { datacenter->clearAuthKey(connection->isMediaConnection ? HandshakeTypeMediaTemp : HandshakeTypeTemp); datacenter->beginHandshake(connection->isMediaConnection ? HandshakeTypeMediaTemp : HandshakeTypeTemp, true); if (LOGS_ENABLED) DEBUG_D("connection(%p, account%u, dc%u, type %d) reset auth key due to -404 error", connection, instanceNum, datacenter->getDatacenterId(), connection->getConnectionType()); @@ -1295,6 +1301,12 @@ void ConnectionsManager::processServerResponse(TLObject *message, int64_t messag request->serverFailureCount++; } discardResponse = true; + } else if (error->error_code == -504) { + discardResponse = (request->requestFlags & RequestFlagIgnoreFloodWait) == 0; + request->failedByFloodWait = 2; + request->startTime = 0; + request->startTimeMillis = 0; + request->minStartTime = (int32_t) (getCurrentTimeMonotonicMillis() / 1000 + 2); } else if (error->error_code == 420) { int32_t waitTime = 2; static std::string floodWait = "FLOOD_WAIT_"; @@ -1758,11 +1770,6 @@ void ConnectionsManager::detachConnection(ConnectionSocket *connection) { } int32_t ConnectionsManager::sendRequestInternal(TLObject *object, onCompleteFunc onComplete, onQuickAckFunc onQuickAck, uint32_t flags, uint32_t datacenterId, ConnectionType connetionType, bool immediate) { - if (!currentUserId && !(flags & RequestFlagWithoutLogin)) { - if (LOGS_ENABLED) DEBUG_D("can't do request without login %s", typeid(*object).name()); - delete object; - return 0; - } auto request = new Request(instanceNum, lastRequestToken++, connetionType, flags, datacenterId, onComplete, onQuickAck, nullptr); request->rawRequest = object; request->rpcRequest = wrapInLayer(object, getDatacenterWithId(datacenterId), request); @@ -1773,9 +1780,14 @@ int32_t ConnectionsManager::sendRequestInternal(TLObject *object, onCompleteFunc delete request; return request->requestToken; } - requestsQueue.push_back(std::unique_ptr(request)); - if (immediate) { - processRequestQueue(0, 0); + if (!currentUserId && !(flags & RequestFlagWithoutLogin)) { + if (LOGS_ENABLED) DEBUG_D("can't do request without login %s, reschedule token %d", typeid(*object).name(), request->requestToken); + waitingLoginRequests.push_back(std::unique_ptr(request)); + } else { + requestsQueue.push_back(std::unique_ptr(request)); + if (immediate) { + processRequestQueue(0, 0); + } } return request->requestToken; } @@ -1786,11 +1798,6 @@ int32_t ConnectionsManager::sendRequest(TLObject *object, onCompleteFunc onCompl } int32_t ConnectionsManager::sendRequest(TLObject *object, onCompleteFunc onComplete, onQuickAckFunc onQuickAck, uint32_t flags, uint32_t datacenterId, ConnectionType connetionType, bool immediate, int32_t requestToken) { - if (!currentUserId && !(flags & RequestFlagWithoutLogin)) { - if (LOGS_ENABLED) DEBUG_D("can't do request without login %s", typeid(*object).name()); - delete object; - return 0; - } if (requestToken == 0) { requestToken = lastRequestToken++; } @@ -1804,9 +1811,14 @@ int32_t ConnectionsManager::sendRequest(TLObject *object, onCompleteFunc onCompl tokensToBeCancelled.erase(cancelledIterator); delete request; } - requestsQueue.push_back(std::unique_ptr(request)); - if (immediate) { - processRequestQueue(0, 0); + if (!currentUserId && !(flags & RequestFlagWithoutLogin)) { + if (LOGS_ENABLED) DEBUG_D("can't do request without login %s, reschedule token %d", typeid(*object).name(), requestToken); + waitingLoginRequests.push_back(std::unique_ptr(request)); + } else { + requestsQueue.push_back(std::unique_ptr(request)); + if (immediate) { + processRequestQueue(0, 0); + } } }); return requestToken; @@ -1814,31 +1826,6 @@ int32_t ConnectionsManager::sendRequest(TLObject *object, onCompleteFunc onCompl #ifdef ANDROID void ConnectionsManager::sendRequest(TLObject *object, onCompleteFunc onComplete, onQuickAckFunc onQuickAck, onWriteToSocketFunc onWriteToSocket, uint32_t flags, uint32_t datacenterId, ConnectionType connetionType, bool immediate, int32_t requestToken, jobject ptr1, jobject ptr2, jobject ptr3) { - if (!currentUserId && !(flags & RequestFlagWithoutLogin)) { - if (LOGS_ENABLED) DEBUG_D("can't do request without login %s", typeid(*object).name()); - delete object; - JNIEnv *env = 0; - if (javaVm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) { - if (LOGS_ENABLED) DEBUG_E("can't get jnienv"); - exit(1); - } - if (ptr1 != nullptr) { - DEBUG_DELREF("connectionsmanager ptr1"); - env->DeleteGlobalRef(ptr1); - ptr1 = nullptr; - } - if (ptr2 != nullptr) { - DEBUG_DELREF("connectionsmanager ptr2"); - env->DeleteGlobalRef(ptr2); - ptr2 = nullptr; - } - if (ptr3 != nullptr) { - DEBUG_DELREF("connectionsmanager ptr3"); - env->DeleteGlobalRef(ptr3); - ptr3 = nullptr; - } - return; - } scheduleTask([&, requestToken, object, onComplete, onQuickAck, onWriteToSocket, flags, datacenterId, connetionType, immediate, ptr1, ptr2, ptr3] { if (LOGS_ENABLED) DEBUG_D("send request %p - %s", object, typeid(*object).name()); auto request = new Request(instanceNum, requestToken, connetionType, flags, datacenterId, onComplete, onQuickAck, onWriteToSocket); @@ -1855,9 +1842,14 @@ void ConnectionsManager::sendRequest(TLObject *object, onCompleteFunc onComplete delete request; return; } - requestsQueue.push_back(std::unique_ptr(request)); - if (immediate) { - processRequestQueue(0, 0); + if (!currentUserId && !(flags & RequestFlagWithoutLogin)) { + if (LOGS_ENABLED) DEBUG_D("can't do request without login %s, reschedule token %d", typeid(*object).name(), request->requestToken); + waitingLoginRequests.push_back(std::unique_ptr(request)); + } else { + requestsQueue.push_back(std::unique_ptr(request)); + if (immediate) { + processRequestQueue(0, 0); + } } }); } @@ -1903,7 +1895,7 @@ void ConnectionsManager::setUserId(int64_t userId) { registerForInternalPushUpdates(); } if (currentUserId != userId && userId != 0) { - updateDcSettings(0, false); + updateDcSettings(0, false, false); } if (currentUserId != 0 && pushConnectionEnabled) { Datacenter *datacenter = getDatacenterWithId(currentDatacenterId); @@ -1912,6 +1904,16 @@ void ConnectionsManager::setUserId(int64_t userId) { sendPing(datacenter, true); } } + if (LOGS_ENABLED) DEBUG_D("set user %lld", userId); + if (currentUserId != 0 && !waitingLoginRequests.empty()) { + for (auto iter = waitingLoginRequests.begin(); iter != waitingLoginRequests.end(); iter++) { + Request *request = iter->get(); + if (LOGS_ENABLED) DEBUG_D("run rescheduled request %d", request->requestToken); + requestsQueue.push_back(std::move(*iter)); + } + processRequestQueue(0, 0); + waitingLoginRequests.clear(); + } }); } @@ -1966,6 +1968,19 @@ bool ConnectionsManager::cancelRequestInternal(int32_t token, int64_t messageId, } } + for (auto iter = waitingLoginRequests.begin(); iter != waitingLoginRequests.end(); iter++) { + Request *request = iter->get(); + if ((token != 0 && request->requestToken == token) || (messageId != 0 && request->respondsToMessageId(messageId))) { + request->cancelled = true; + if (LOGS_ENABLED) DEBUG_D("cancelled waiting login rpc request %p - %s", request->rawRequest, typeid(*request->rawRequest).name()); + waitingLoginRequests.erase(iter); + if (removeFromClass) { + removeRequestFromGuid(token); + } + return true; + } + } + for (auto iter = runningRequests.begin(); iter != runningRequests.end(); iter++) { Request *request = iter->get(); if ((token != 0 && request->requestToken == token) || (messageId != 0 && request->respondsToMessageId(messageId))) { @@ -2001,6 +2016,30 @@ void ConnectionsManager::cancelRequest(int32_t token, bool notifyServer) { }); } +void ConnectionsManager::failNotRunningRequest(int32_t token) { + if (token == 0) { + return; + } + + scheduleTask([&, token] { + for (auto iter = requestsQueue.begin(); iter != requestsQueue.end(); iter++) { + Request *request = iter->get(); + if ((token != 0 && request->requestToken == token)) { + auto error = new TL_error(); + error->code = -2000; + error->text = "CANCELLED_REQUEST"; + request->onComplete(nullptr, error, 0, 0, request->messageId); + + request->cancelled = true; + if (LOGS_ENABLED) DEBUG_D("cancelled queued rpc request %p - %s", request->rawRequest, typeid(*request->rawRequest).name()); + requestsQueue.erase(iter); + removeRequestFromGuid(token); + return true; + } + } + }); +} + void ConnectionsManager::onDatacenterHandshakeComplete(Datacenter *datacenter, HandshakeType type, int32_t timeDiff) { saveConfig(); uint32_t datacenterId = datacenter->getDatacenterId(); @@ -2762,7 +2801,7 @@ void ConnectionsManager::processRequestQueue(uint32_t connectionTypes, uint32_t } if (!unknownDatacenterIds.empty()) { - updateDcSettings(0, false); + updateDcSettings(0, false, false); } size_t count = neededDatacenters.size(); @@ -2993,7 +3032,7 @@ inline std::string decodeSecret(std::string secret) { return base64UrlDecode(secret); } -void ConnectionsManager::updateDcSettings(uint32_t dcNum, bool workaround) { +void ConnectionsManager::updateDcSettings(uint32_t dcNum, bool workaround, bool ifLoadingTryAgain) { if (workaround) { if (updatingDcSettingsWorkaround) { return; @@ -3001,6 +3040,10 @@ void ConnectionsManager::updateDcSettings(uint32_t dcNum, bool workaround) { updatingDcSettingsWorkaround = true; } else { if (updatingDcSettings) { + if (ifLoadingTryAgain) { + updatingDcSettingsAgain = true; + updatingDcSettingsAgainDcNum = dcNum; + } return; } updatingDcSettings = true; @@ -3012,6 +3055,14 @@ void ConnectionsManager::updateDcSettings(uint32_t dcNum, bool workaround) { if ((!workaround && !updatingDcSettings) || (workaround && !updatingDcSettingsWorkaround)) { return; } + if (!workaround && updatingDcSettingsAgain) { + updatingDcSettingsAgain = false; + for (auto & datacenter : datacenters) { + datacenter.second->resetInitVersion(); + } + updateDcSettings(updatingDcSettingsAgainDcNum, false, false); + return; + } if (response != nullptr) { auto config = (TL_config *) response; @@ -3126,27 +3177,13 @@ void ConnectionsManager::moveToDatacenter(uint32_t datacenterId) { Datacenter *currentDatacenter = getDatacenterWithId(currentDatacenterId); clearRequestsForDatacenter(currentDatacenter, HandshakeTypeAll); - - if (currentUserId) { - auto request = new TL_auth_exportAuthorization(); - request->dc_id = datacenterId; - sendRequest(request, [&, datacenterId](TLObject *response, TL_error *error, int32_t networkType, int64_t responseTime, int64_t msgId) { - if (error == nullptr) { - movingAuthorization = std::move(((TL_auth_exportedAuthorization *) response)->bytes); - authorizeOnMovingDatacenter(); - } else { - moveToDatacenter(datacenterId); - } - }, nullptr, RequestFlagWithoutLogin, DEFAULT_DATACENTER_ID, ConnectionTypeGeneric, true); - } else { - authorizeOnMovingDatacenter(); - } + authorizeOnMovingDatacenter(); } void ConnectionsManager::authorizeOnMovingDatacenter() { Datacenter *datacenter = getDatacenterWithId(movingToDatacenterId); if (datacenter == nullptr) { - updateDcSettings(0, false); + updateDcSettings(0, false, false); return; } datacenter->recreateSessions(HandshakeTypeAll); @@ -3197,7 +3234,7 @@ void ConnectionsManager::applyDatacenterAddress(uint32_t datacenterId, std::stri if (datacenter->isHandshakingAny()) { datacenter->beginHandshake(HandshakeTypeCurrent, true); } - updateDcSettings(datacenterId, false); + updateDcSettings(datacenterId, false, false); } }); } @@ -3290,7 +3327,7 @@ void ConnectionsManager::applyDnsConfig(NativeByteBuffer *buffer, std::string ph if (datacenter->isHandshakingAny()) { datacenter->beginHandshake(HandshakeTypeCurrent, true); } - updateDcSettings(rule->dc_id, true); + updateDcSettings(rule->dc_id, true, false); } } else { if (LOGS_ENABLED) DEBUG_D("config datacenter %d not found", rule->dc_id); @@ -3376,7 +3413,7 @@ void ConnectionsManager::init(uint32_t version, int32_t layer, int32_t apiId, st pthread_create(&networkThread, nullptr, (ConnectionsManager::ThreadProc), this); if (needLoadConfig) { - updateDcSettings(0, false); + updateDcSettings(0, false, false); } } @@ -3442,7 +3479,7 @@ void ConnectionsManager::setRegId(std::string regId) { for (auto & datacenter : datacenters) { datacenter.second->resetInitVersion(); } - updateDcSettings(0, false); + updateDcSettings(0, false, true); saveConfig(); }); } @@ -3457,7 +3494,7 @@ void ConnectionsManager::setSystemLangCode(std::string langCode) { datacenter.second->resetInitVersion(); } saveConfig(); - updateDcSettings(0, false); + updateDcSettings(0, false, false); }); } @@ -3534,6 +3571,7 @@ void ConnectionsManager::setNetworkAvailable(bool value, int32_t type, bool slow void ConnectionsManager::setIpStrategy(uint8_t value) { scheduleTask([&, value] { + lastProtocolUsefullData = false; ipStrategy = value; }); } @@ -3624,4 +3662,23 @@ void ConnectionsManager::useJavaVM(JavaVM *vm, bool useJavaByteBuffers) { if (LOGS_ENABLED) DEBUG_D("using java ByteBuffer"); } } + +void ConnectionsManager::reconnect(int32_t dcId, int32_t connectionType) { + scheduleTask([&, dcId, connectionType] { + scheduleTask([&, dcId, connectionType] { + Datacenter *datacenter = getDatacenterWithId(dcId); + if (datacenter != nullptr) { + Connection *connection = datacenter->getConnectionByType(connectionType, false, + 0); + if (connection != nullptr) { + if (LOGS_ENABLED) + DEBUG_D("discard connection dcId=%d connectionType=%d", dcId, + connectionType); + connection->suspendConnection(true); + } + } + }); + }); +} + #endif diff --git a/TMessagesProj/jni/tgnet/ConnectionsManager.h b/TMessagesProj/jni/tgnet/ConnectionsManager.h index 74f21ba3d5..456057727a 100644 --- a/TMessagesProj/jni/tgnet/ConnectionsManager.h +++ b/TMessagesProj/jni/tgnet/ConnectionsManager.h @@ -69,7 +69,7 @@ class ConnectionsManager { void setLangCode(std::string langCode); void setRegId(std::string regId); void setSystemLangCode(std::string langCode); - void updateDcSettings(uint32_t datacenterId, bool workaround); + void updateDcSettings(uint32_t datacenterId, bool workaround, bool ifLoadingTryAgain); void setPushConnectionEnabled(bool value); void applyDnsConfig(NativeByteBuffer *buffer, std::string phone, int32_t date); void moveToDatacenter(uint32_t datacenterId); @@ -81,6 +81,9 @@ class ConnectionsManager { static void useJavaVM(JavaVM *vm, bool useJavaByteBuffers); #endif + void reconnect(int32_t datacentrId, int32_t connectionType); + void failNotRunningRequest(int32_t token); + private: static void *ThreadProc(void *data); static std::vector _instances; @@ -153,6 +156,8 @@ class ConnectionsManager { bool sendingPushPing = false; bool sendingPing = false; bool updatingDcSettings = false; + bool updatingDcSettingsAgain = false; + uint32_t updatingDcSettingsAgainDcNum = 0; bool updatingDcSettingsWorkaround = false; int32_t disconnectTimeoutAmount = 0; bool requestingSecondAddressByTlsHashMismatch = false; @@ -203,6 +208,7 @@ class ConnectionsManager { int *pipeFd = nullptr; NativeByteBuffer *networkBuffer; + requestsList waitingLoginRequests; requestsList requestsQueue; requestsList runningRequests; std::vector requestingSaltsForDc; @@ -252,6 +258,7 @@ class ConnectionsManager { friend class Config; friend class FileLog; friend class Handshake; + }; #ifdef ANDROID diff --git a/TMessagesProj/jni/tgnet/FileLog.cpp b/TMessagesProj/jni/tgnet/FileLog.cpp index d81cfb49ee..9671ee05da 100644 --- a/TMessagesProj/jni/tgnet/FileLog.cpp +++ b/TMessagesProj/jni/tgnet/FileLog.cpp @@ -47,8 +47,10 @@ void FileLog::fatal(const char *message, ...) { } va_list argptr; va_start(argptr, message); - time_t t = time(0); - struct tm *now = localtime(&t); + + struct timeval time_now; + gettimeofday(&time_now, NULL); + struct tm *now = localtime(&time_now.tv_sec); #ifdef ANDROID __android_log_vprint(ANDROID_LOG_FATAL, "tgnet", message, argptr); va_end(argptr); @@ -63,7 +65,7 @@ void FileLog::fatal(const char *message, ...) { #endif FILE *logFile = getInstance().logFile; if (logFile) { - fprintf(logFile, "%d-%d %02d:%02d:%02d FATAL ERROR: ", now->tm_mon + 1, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec); + fprintf(logFile, "%d-%d %02d:%02d:%02d.%03d FATAL ERROR: ", now->tm_mon + 1, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec, (int) (time_now.tv_usec / 1000)); vfprintf(logFile, message, argptr); fprintf(logFile, "\n"); fflush(logFile); @@ -82,8 +84,9 @@ void FileLog::e(const char *message, ...) { } va_list argptr; va_start(argptr, message); - time_t t = time(0); - struct tm *now = localtime(&t); + struct timeval time_now; + gettimeofday(&time_now, NULL); + struct tm *now = localtime(&time_now.tv_sec); #ifdef ANDROID __android_log_vprint(ANDROID_LOG_ERROR, "tgnet", message, argptr); va_end(argptr); @@ -98,7 +101,7 @@ void FileLog::e(const char *message, ...) { #endif FILE *logFile = getInstance().logFile; if (logFile) { - fprintf(logFile, "%d-%d %02d:%02d:%02d error: ", now->tm_mon + 1, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec); + fprintf(logFile, "%d-%d %02d:%02d:%02d.%03d error: ", now->tm_mon + 1, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec, (int) (time_now.tv_usec / 1000)); vfprintf(logFile, message, argptr); fprintf(logFile, "\n"); fflush(logFile); @@ -113,8 +116,9 @@ void FileLog::w(const char *message, ...) { } va_list argptr; va_start(argptr, message); - time_t t = time(0); - struct tm *now = localtime(&t); + struct timeval time_now; + gettimeofday(&time_now, NULL); + struct tm *now = localtime(&time_now.tv_sec); #ifdef ANDROID __android_log_vprint(ANDROID_LOG_WARN, "tgnet", message, argptr); va_end(argptr); @@ -129,7 +133,7 @@ void FileLog::w(const char *message, ...) { #endif FILE *logFile = getInstance().logFile; if (logFile) { - fprintf(logFile, "%d-%d %02d:%02d:%02d warning: ", now->tm_mon + 1, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec); + fprintf(logFile, "%d-%d %02d:%02d:%02d.%03d warning: ", now->tm_mon + 1, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec, (int) (time_now.tv_usec / 1000)); vfprintf(logFile, message, argptr); fprintf(logFile, "\n"); fflush(logFile); @@ -144,8 +148,10 @@ void FileLog::d(const char *message, ...) { } va_list argptr; va_start(argptr, message); - time_t t = time(0); - struct tm *now = localtime(&t); + + struct timeval time_now; + gettimeofday(&time_now, NULL); + struct tm *now = localtime(&time_now.tv_sec); #ifdef ANDROID __android_log_vprint(ANDROID_LOG_DEBUG, "tgnet", message, argptr); va_end(argptr); @@ -160,7 +166,7 @@ void FileLog::d(const char *message, ...) { #endif FILE *logFile = getInstance().logFile; if (logFile) { - fprintf(logFile, "%d-%d %02d:%02d:%02d debug: ", now->tm_mon + 1, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec); + fprintf(logFile, "%d-%d %02d:%02d:%02d.%03d debug: ", now->tm_mon + 1, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec, (int) (time_now.tv_usec / 1000)); vfprintf(logFile, message, argptr); fprintf(logFile, "\n"); fflush(logFile); @@ -177,8 +183,6 @@ void FileLog::ref(const char *message, ...) { } va_list argptr; va_start(argptr, message); - time_t t = time(0); - struct tm *now = localtime(&t); refsCount++; #ifdef ANDROID std::ostringstream s; @@ -196,8 +200,6 @@ void FileLog::delref(const char *message, ...) { } va_list argptr; va_start(argptr, message); - time_t t = time(0); - struct tm *now = localtime(&t); refsCount--; #ifdef ANDROID std::ostringstream s; diff --git a/TMessagesProj/jni/tgnet/Handshake.cpp b/TMessagesProj/jni/tgnet/Handshake.cpp index f3ccb1c9de..7daba100df 100644 --- a/TMessagesProj/jni/tgnet/Handshake.cpp +++ b/TMessagesProj/jni/tgnet/Handshake.cpp @@ -943,32 +943,33 @@ void Handshake::loadCdnConfig(Datacenter *datacenter) { if (loadingCdnKeys) { return; } - if (cdnPublicKeysFingerprints.empty()) { - if (cdnConfig == nullptr) { - cdnConfig = new Config(datacenter->instanceNum, "cdnkeys.dat"); - } - NativeByteBuffer *buffer = cdnConfig->readConfig(); - if (buffer != nullptr) { - uint32_t version = buffer->readUint32(nullptr); - if (version >= 1) { - size_t count = buffer->readUint32(nullptr); - for (uint32_t a = 0; a < count; a++) { - int dcId = buffer->readInt32(nullptr); - cdnPublicKeys[dcId] = buffer->readString(nullptr); - cdnPublicKeysFingerprints[dcId] = buffer->readUint64(nullptr); - } - } - buffer->reuse(); - if (!cdnPublicKeysFingerprints.empty()) { - size_t count = cdnWaitingDatacenters.size(); - for (uint32_t a = 0; a < count; a++) { - cdnWaitingDatacenters[a]->beginHandshake(HandshakeTypeCurrent, false); - } - cdnWaitingDatacenters.clear(); - return; - } - } - } + if (LOGS_ENABLED) DEBUG_D("account%u dc%u loadCdnConfig", datacenter->instanceNum, datacenter->datacenterId); +// if (cdnPublicKeysFingerprints.empty()) { +// if (cdnConfig == nullptr) { +// cdnConfig = new Config(datacenter->instanceNum, "cdnkeys.dat"); +// } +// NativeByteBuffer *buffer = cdnConfig->readConfig(); +// if (buffer != nullptr) { +// uint32_t version = buffer->readUint32(nullptr); +// if (version >= 1) { +// size_t count = buffer->readUint32(nullptr); +// for (uint32_t a = 0; a < count; a++) { +// int dcId = buffer->readInt32(nullptr); +// cdnPublicKeys[dcId] = buffer->readString(nullptr); +// cdnPublicKeysFingerprints[dcId] = buffer->readUint64(nullptr); +// } +// } +// buffer->reuse(); +// if (!cdnPublicKeysFingerprints.empty()) { +// size_t count = cdnWaitingDatacenters.size(); +// for (uint32_t a = 0; a < count; a++) { +// cdnWaitingDatacenters[a]->beginHandshake(HandshakeTypeCurrent, false); +// } +// cdnWaitingDatacenters.clear(); +// return; +// } +// } +// } loadingCdnKeys = true; auto request = new TL_help_getCdnConfig(); @@ -1011,6 +1012,7 @@ void Handshake::loadCdnConfig(Datacenter *datacenter) { buffer->reuse(); BIO_free(keyBio); count = cdnWaitingDatacenters.size(); + if (LOGS_ENABLED) DEBUG_D("account%u dc%u cdnConfig loaded begin handshake", datacenter->instanceNum, datacenter->datacenterId); for (uint32_t a = 0; a < count; a++) { cdnWaitingDatacenters[a]->beginHandshake(HandshakeTypeCurrent, false); } diff --git a/TMessagesProj/jni/tgnet/NativeByteBuffer.cpp b/TMessagesProj/jni/tgnet/NativeByteBuffer.cpp index b7e1d69606..81bfb7ba87 100644 --- a/TMessagesProj/jni/tgnet/NativeByteBuffer.cpp +++ b/TMessagesProj/jni/tgnet/NativeByteBuffer.cpp @@ -77,6 +77,7 @@ NativeByteBuffer::~NativeByteBuffer() { delete[] buffer; buffer = nullptr; } + _limit = _capacity = 0; } uint32_t NativeByteBuffer::position() { @@ -441,7 +442,7 @@ void NativeByteBuffer::writeDouble(double d) { } int32_t NativeByteBuffer::readInt32(bool *error) { - if (_position + 4 > _limit) { + if (_position + 4 > _limit || calculateSizeOnly) { if (error != nullptr) { *error = true; } @@ -501,7 +502,7 @@ int64_t NativeByteBuffer::readInt64(bool *error) { } uint8_t NativeByteBuffer::readByte(bool *error) { - if (_position + 1 > _limit) { + if (_position + 1 > _limit || calculateSizeOnly) { if (error != nullptr) { *error = true; } @@ -526,7 +527,7 @@ bool NativeByteBuffer::readBool(bool *error) { } void NativeByteBuffer::readBytes(uint8_t *b, uint32_t length, bool *error) { - if (length > _limit - _position) { + if (length > _limit - _position || calculateSizeOnly) { if (error != nullptr) { *error = true; } @@ -538,7 +539,7 @@ void NativeByteBuffer::readBytes(uint8_t *b, uint32_t length, bool *error) { } ByteArray *NativeByteBuffer::readBytes(uint32_t length, bool *error) { - if (length > _limit - _position) { + if (length > _limit - _position || calculateSizeOnly) { if (error != nullptr) { *error = true; } @@ -553,7 +554,7 @@ ByteArray *NativeByteBuffer::readBytes(uint32_t length, bool *error) { std::string NativeByteBuffer::readString(bool *error) { uint32_t sl = 1; - if (_position + 1 > _limit) { + if (_position + 1 > _limit || calculateSizeOnly) { if (error != nullptr) { *error = true; } @@ -591,7 +592,7 @@ std::string NativeByteBuffer::readString(bool *error) { ByteArray *NativeByteBuffer::readByteArray(bool *error) { uint32_t sl = 1; - if (_position + 1 > _limit) { + if (_position + 1 > _limit || calculateSizeOnly) { if (error != nullptr) { *error = true; } @@ -630,7 +631,7 @@ ByteArray *NativeByteBuffer::readByteArray(bool *error) { NativeByteBuffer *NativeByteBuffer::readByteBuffer(bool copy, bool *error) { uint32_t sl = 1; - if (_position + 1 > _limit) { + if (_position + 1 > _limit || calculateSizeOnly) { if (error != nullptr) { *error = true; } diff --git a/TMessagesProj/jni/voip/tgcalls/v2_4_0_0/InstanceV2_4_0_0Impl.cpp b/TMessagesProj/jni/voip/tgcalls/v2_4_0_0/InstanceV2_4_0_0Impl.cpp index 69ce1e6e6a..3e9c3cead9 100644 --- a/TMessagesProj/jni/voip/tgcalls/v2_4_0_0/InstanceV2_4_0_0Impl.cpp +++ b/TMessagesProj/jni/voip/tgcalls/v2_4_0_0/InstanceV2_4_0_0Impl.cpp @@ -1210,6 +1210,7 @@ class InstanceV2_4_0_0ImplInternal : public std::enable_shared_from_this threads) : _threads(threads), _rtcServers(descriptor.rtcServers), + _enableP2P(descriptor.config.enableP2P), _encryptionKey(std::move(descriptor.encryptionKey)), _stateUpdated(descriptor.stateUpdated), _signalBarsUpdated(descriptor.signalBarsUpdated), @@ -1252,12 +1253,12 @@ class InstanceV2_4_0_0ImplInternal : public std::enable_shared_from_this(shared_from_this()); - _networking.reset(new ThreadLocalObject(_threads->getNetworkThread(), [weak, threads = _threads, isOutgoing = _encryptionKey.isOutgoing, rtcServers = _rtcServers]() { + _networking.reset(new ThreadLocalObject(_threads->getNetworkThread(), [weak, threads = _threads, isOutgoing = _encryptionKey.isOutgoing, rtcServers = _rtcServers, enableP2P = _enableP2P]() { return new NativeNetworkingImpl(NativeNetworkingImpl::Configuration{ .isOutgoing = isOutgoing, .enableStunMarking = false, .enableTCP = false, - .enableP2P = true, + .enableP2P = enableP2P, .rtcServers = rtcServers, .stateUpdated = [threads, weak](const NativeNetworkingImpl::State &state) { threads->getMediaThread()->PostTask([=] { @@ -2073,6 +2074,7 @@ class InstanceV2_4_0_0ImplInternal : public std::enable_shared_from_this _threads; std::vector _rtcServers; + bool _enableP2P = false; EncryptionKey _encryptionKey; std::function _stateUpdated; std::function _signalBarsUpdated; diff --git a/TMessagesProj/src/main/AndroidManifest.xml b/TMessagesProj/src/main/AndroidManifest.xml index e3ec28348b..fa5015dd5f 100644 --- a/TMessagesProj/src/main/AndroidManifest.xml +++ b/TMessagesProj/src/main/AndroidManifest.xml @@ -513,6 +513,7 @@ + listener.onSurfaceSizeChanged(width, height)); + if (workerQueue != null) { + workerQueue.postRunnable(() -> { + listeners.sendEvent( + EVENT_SURFACE_SIZE_CHANGED, listener -> listener.onSurfaceSizeChanged(width, height)); + }); + } else { + listeners.sendEvent( + EVENT_SURFACE_SIZE_CHANGED, listener -> listener.onSurfaceSizeChanged(width, height)); + } } } @@ -2965,12 +2986,33 @@ public void surfaceDestroyed(SurfaceHolder holder) { @Override public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) { + if (workerQueue != null) { + workerQueue.postRunnable(() -> { + onSurfaceTextureAvailableInternal(surfaceTexture, width, height); + }); + } else { + onSurfaceTextureAvailableInternal(surfaceTexture, width, height); + } + } + + public void onSurfaceTextureAvailableInternal(SurfaceTexture surfaceTexture, int width, int height) { setSurfaceTextureInternal(surfaceTexture); maybeNotifySurfaceSizeChanged(width, height); } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height) { + if (workerQueue != null) { + workerQueue.postRunnable(() -> { + onSurfaceTextureSizeChangedInternal(surfaceTexture, width, height); + }); + } else { + onSurfaceTextureSizeChangedInternal(surfaceTexture, width, height); + } + } + + + public void onSurfaceTextureSizeChangedInternal(SurfaceTexture surfaceTexture, int width, int height) { maybeNotifySurfaceSizeChanged(width, height); } @@ -2981,13 +3023,33 @@ public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) { return false; } } + if (workerQueue != null) { + workerQueue.postRunnable(() -> { + onSurfaceTextureDestroyedInternal(surfaceTexture); + }); + } else { + onSurfaceTextureDestroyedInternal(surfaceTexture); + } + return true; + } + + public void onSurfaceTextureDestroyedInternal(SurfaceTexture surfaceTexture) { setVideoOutputInternal(/* videoOutput= */ null); maybeNotifySurfaceSizeChanged(/* width= */ 0, /* height= */ 0); - return true; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { + if (workerQueue != null) { + workerQueue.postRunnable(() -> { + onSurfaceTextureUpdatedInternal(surfaceTexture); + }); + } else { + onSurfaceTextureUpdatedInternal(surfaceTexture); + } + } + + public void onSurfaceTextureUpdatedInternal(SurfaceTexture surfaceTexture) { for (com.google.android.exoplayer2.video.VideoListener videoListener : videoListeners) { videoListener.onSurfaceTextureUpdated(surfaceTexture); } diff --git a/TMessagesProj/src/main/java/com/google/android/exoplayer2/MediaPeriodQueue.java b/TMessagesProj/src/main/java/com/google/android/exoplayer2/MediaPeriodQueue.java index 0651dd8bf8..b21266ec7c 100644 --- a/TMessagesProj/src/main/java/com/google/android/exoplayer2/MediaPeriodQueue.java +++ b/TMessagesProj/src/main/java/com/google/android/exoplayer2/MediaPeriodQueue.java @@ -368,22 +368,25 @@ public boolean updateQueuedPeriods( newPeriodInfo.copyWithRequestedContentPositionUs( oldPeriodInfo.requestedContentPositionUs); - if (!areDurationsCompatible(oldPeriodInfo.durationUs, newPeriodInfo.durationUs)) { - // The period duration changed. Remove all subsequent periods and check whether we read - // beyond the new duration. - periodHolder.updateClipping(); - long newDurationInRendererTime = - newPeriodInfo.durationUs == C.TIME_UNSET - ? Long.MAX_VALUE - : periodHolder.toRendererTime(newPeriodInfo.durationUs); - boolean isReadingAndReadBeyondNewDuration = - periodHolder == reading - && !periodHolder.info.isFollowedByTransitionToSameStream - && (maxRendererReadPositionUs == C.TIME_END_OF_SOURCE - || maxRendererReadPositionUs >= newDurationInRendererTime); - boolean readingPeriodRemoved = removeAfter(periodHolder); - return !readingPeriodRemoved && !isReadingAndReadBeyondNewDuration; - } + //@xaxtix + //comment cause lead to infinit seek loop in end of video + +// if (!areDurationsCompatible(oldPeriodInfo.durationUs, newPeriodInfo.durationUs)) { +// // The period duration changed. Remove all subsequent periods and check whether we read +// // beyond the new duration. +// periodHolder.updateClipping(); +// long newDurationInRendererTime = +// newPeriodInfo.durationUs == C.TIME_UNSET +// ? Long.MAX_VALUE +// : periodHolder.toRendererTime(newPeriodInfo.durationUs); +// boolean isReadingAndReadBeyondNewDuration = +// periodHolder == reading +// && !periodHolder.info.isFollowedByTransitionToSameStream +// && (maxRendererReadPositionUs == C.TIME_END_OF_SOURCE +// || maxRendererReadPositionUs >= newDurationInRendererTime); +// boolean readingPeriodRemoved = removeAfter(periodHolder); +// return !readingPeriodRemoved && !isReadingAndReadBeyondNewDuration; +// } previousPeriodHolder = periodHolder; periodHolder = periodHolder.getNext(); diff --git a/TMessagesProj/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java b/TMessagesProj/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java index ac916eb781..03ff3105f8 100644 --- a/TMessagesProj/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java +++ b/TMessagesProj/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java @@ -24,10 +24,12 @@ import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.TextureView; + import androidx.annotation.IntRange; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.annotation.VisibleForTesting; + import com.google.android.exoplayer2.analytics.AnalyticsCollector; import com.google.android.exoplayer2.analytics.AnalyticsListener; import com.google.android.exoplayer2.audio.AudioAttributes; @@ -52,7 +54,7 @@ import com.google.android.exoplayer2.video.spherical.CameraMotionListener; import com.google.errorprone.annotations.CanIgnoreReturnValue; -import org.telegram.messenger.Utilities; +import org.telegram.messenger.DispatchQueue; import java.util.List; @@ -794,6 +796,11 @@ public void removeListener(Listener listener) { return player.getPlaybackSuppressionReason(); } + @Override + public void setWorkerQueue(DispatchQueue dispatchQueue) { + + } + @Override @Nullable public ExoPlaybackException getPlayerError() { diff --git a/TMessagesProj/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java b/TMessagesProj/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java index 264b31a9a9..bf9d7b3f6a 100644 --- a/TMessagesProj/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java +++ b/TMessagesProj/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java @@ -319,7 +319,7 @@ private static String buildCustomDiagnosticInfo(int errorCode) { private float targetPlaybackSpeed; @Nullable private MediaCodecAdapter codec; @Nullable private Format codecInputFormat; - @Nullable private MediaFormat codecOutputMediaFormat; + @Nullable public MediaFormat codecOutputMediaFormat; private boolean codecOutputMediaFormatChanged; private float codecOperatingRate; @Nullable private ArrayDeque availableCodecInfos; diff --git a/TMessagesProj/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java b/TMessagesProj/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java index e608a02657..37b3a7da01 100644 --- a/TMessagesProj/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java +++ b/TMessagesProj/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java @@ -693,6 +693,7 @@ private void setOutput(@Nullable Object output) throws ExoPlaybackException { try { setOutputSurfaceV23(codec, surface); } catch (Throwable e) { + e.printStackTrace(); throw new SurfaceNotValidException(e); } } else { diff --git a/TMessagesProj/src/main/java/org/telegram/DispatchQueuePriority.java b/TMessagesProj/src/main/java/org/telegram/DispatchQueuePriority.java index 021ad1db74..29d67c7fc9 100644 --- a/TMessagesProj/src/main/java/org/telegram/DispatchQueuePriority.java +++ b/TMessagesProj/src/main/java/org/telegram/DispatchQueuePriority.java @@ -1,6 +1,9 @@ package org.telegram; +import org.telegram.messenger.FileLog; + import java.util.Comparator; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.PriorityBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -21,8 +24,21 @@ public int compare(Runnable o1, Runnable o2) { } return priority2 - priority1; } - })); + })) { + @Override + protected void beforeExecute(Thread t, Runnable r) { + CountDownLatch latch = pauseLatch; + if (latch != null) { + try { + latch.await(); + } catch (InterruptedException e) { + FileLog.e(e); + } + } + } + }; + private volatile CountDownLatch pauseLatch; public DispatchQueuePriority(String threadName) { @@ -41,14 +57,11 @@ public void postRunnable(Runnable runnable) { } public Runnable postRunnable(Runnable runnable, int priority) { - if (priority == 1) { - postRunnable(runnable); - return runnable; - } else { - PriorityRunnable priorityRunnable = new PriorityRunnable(priority, runnable); - threadPoolExecutor.execute(priorityRunnable); - return priorityRunnable; + if (priority != 1) { + runnable = new PriorityRunnable(priority, runnable); } + postRunnable(runnable); + return runnable; } public void cancelRunnable(Runnable runnable) { @@ -56,7 +69,20 @@ public void cancelRunnable(Runnable runnable) { return; } threadPoolExecutor.remove(runnable); + } + + public void pause() { + if (pauseLatch == null) { + pauseLatch = new CountDownLatch(1); + } + } + public void resume() { + CountDownLatch latch = pauseLatch; + if (latch != null) { + latch.countDown(); + pauseLatch = null; + } } private static class PriorityRunnable implements Runnable { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java b/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java index eaf4fa02b8..79bd799f30 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java @@ -72,12 +72,15 @@ import android.text.style.URLSpan; import android.text.util.Linkify; import android.util.DisplayMetrics; +import android.util.Pair; import android.util.StateSet; import android.util.TypedValue; import android.view.Display; import android.view.Gravity; import android.view.MotionEvent; +import android.view.PixelCopy; import android.view.Surface; +import android.view.SurfaceView; import android.view.TextureView; import android.view.View; import android.view.ViewConfiguration; @@ -104,6 +107,7 @@ import android.widget.TextView; import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; import androidx.core.content.ContextCompat; import androidx.core.content.FileProvider; import androidx.core.graphics.ColorUtils; @@ -111,11 +115,13 @@ import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.SpringAnimation; import androidx.dynamicanimation.animation.SpringForce; +import androidx.exifinterface.media.ExifInterface; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.viewpager.widget.ViewPager; import com.android.internal.telephony.ITelephony; +import com.google.android.exoplayer2.util.Consumer; import org.telegram.PhoneFormat.PhoneFormat; @@ -132,6 +138,7 @@ import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Cells.TextDetailSettingsCell; import org.telegram.ui.ChatActivity; +import org.telegram.ui.ChatBackgroundDrawable; import org.telegram.ui.Components.AlertsCreator; import org.telegram.ui.Components.BackgroundGradientDrawable; import org.telegram.ui.Components.Bulletin; @@ -169,6 +176,8 @@ import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.net.IDN; +import java.nio.ByteBuffer; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.text.SimpleDateFormat; @@ -185,6 +194,7 @@ import java.util.Locale; import java.util.concurrent.atomic.AtomicInteger; import java.util.Map; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Matcher; @@ -235,6 +245,7 @@ public class AndroidUtilities { public static float density = 1; public static Point displaySize = new Point(); public static float screenRefreshRate = 60; + public static float screenRefreshTime = 1000 / screenRefreshRate; public static int roundMessageSize; public static int roundPlayingMessageSize; public static int roundMessageInset; @@ -256,9 +267,6 @@ public class AndroidUtilities { private static int adjustOwnerClassGuid = 0; private static int altFocusableClassGuid = 0; - private static Paint roundPaint; - private static RectF bitmapRect; - public static final RectF rectTmp = new RectF(); public static final Rect rectTmp2 = new Rect(); @@ -461,10 +469,10 @@ public static Activity findActivity(Context context) { } public static CharSequence replaceSingleTag(String str, Runnable runnable) { - return replaceSingleTag(str, null, 0, runnable); + return replaceSingleTag(str, -1, 0, runnable); } - public static CharSequence replaceSingleTag(String str, String colorKey, int type, Runnable runnable) { + public static SpannableStringBuilder replaceSingleTag(String str, int colorKey, int type, Runnable runnable) { int startIndex = str.indexOf("**"); int endIndex = str.indexOf("**", startIndex + 1); str = str.replace("**", ""); @@ -483,7 +491,7 @@ public static CharSequence replaceSingleTag(String str, String colorKey, int typ public void updateDrawState(@NonNull TextPaint ds) { super.updateDrawState(ds); ds.setUnderlineText(false); - if (colorKey != null) { + if (colorKey >= 0) { ds.setColor(Theme.getColor(colorKey)); } } @@ -522,9 +530,17 @@ public static void recycleBitmaps(List bitmapToRecycle) { return; } if (bitmapToRecycle != null && !bitmapToRecycle.isEmpty()) { + ArrayList> bitmapsToRecycleRef = new ArrayList<>(); + for (int i = 0; i < bitmapToRecycle.size(); i++) { + Bitmap bitmap = bitmapToRecycle.get(i); + if (bitmap != null && !bitmap.isRecycled()) { + bitmapsToRecycleRef.add(new WeakReference<>(bitmap)); + } + } AndroidUtilities.runOnUIThread(() -> Utilities.globalQueue.postRunnable(() -> { - for (int i = 0; i < bitmapToRecycle.size(); i++) { - Bitmap bitmap = bitmapToRecycle.get(i); + for (int i = 0; i < bitmapsToRecycleRef.size(); i++) { + Bitmap bitmap = bitmapsToRecycleRef.get(i).get(); + bitmapsToRecycleRef.get(i).clear(); if (bitmap != null && !bitmap.isRecycled()) { try { bitmap.recycle(); @@ -541,17 +557,55 @@ public static boolean isMapsInstalled(BaseFragment baseFragment) { return true; } + public static void googleVoiceClientService_performAction(Intent intent, boolean isVerified, Bundle options) { + if (!isVerified) { + return; + } + AndroidUtilities.runOnUIThread(() -> { + try { + int currentAccount = UserConfig.selectedAccount; + ApplicationLoader.postInitApplication(); + if (AndroidUtilities.needShowPasscode() || SharedConfig.isWaitingForPasscodeEnter) { + return; + } + String text = intent.getStringExtra("android.intent.extra.TEXT"); + if (!TextUtils.isEmpty(text)) { + String contactUri = intent.getStringExtra("com.google.android.voicesearch.extra.RECIPIENT_CONTACT_URI"); + String id = intent.getStringExtra("com.google.android.voicesearch.extra.RECIPIENT_CONTACT_CHAT_ID"); + long uid = Long.parseLong(id); + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(uid); + if (user == null) { + user = MessagesStorage.getInstance(currentAccount).getUserSync(uid); + if (user != null) { + MessagesController.getInstance(currentAccount).putUser(user, true); + } + } + if (user != null) { + ContactsController.getInstance(currentAccount).markAsContacted(contactUri); + SendMessagesHelper.getInstance(currentAccount).sendMessage(SendMessagesHelper.SendMessageParams.of(text, user.id, null, null, null, true, null, null, null, true, 0, null, false)); + } + } + } catch (Exception e) { + FileLog.e(e); + } + }); + } + public static void recycleBitmap(Bitmap image) { recycleBitmaps(Collections.singletonList(image)); } public static boolean findClickableView(ViewGroup container, float x, float y) { + if (container == null) { + return false; + } for (int i = 0; i < container.getChildCount(); i++) { View child = container.getChildAt(i); if (child.getVisibility() != View.VISIBLE) { continue; } - if (child.isClickable()) { + child.getHitRect(AndroidUtilities.rectTmp2); + if (AndroidUtilities.rectTmp2.contains((int) x, (int) y) && child.isClickable()) { return true; } else if (child instanceof ViewGroup && findClickableView((ViewGroup) child, x - child.getX(), y - child.getY())) { return true; @@ -561,7 +615,7 @@ public static boolean findClickableView(ViewGroup container, float x, float y) { } public static void removeFromParent(View child) { - if (child.getParent() != null) { + if (child != null && child.getParent() != null) { ((ViewGroup) child.getParent()).removeView(child); } } @@ -599,6 +653,97 @@ public static File getLogsDir() { return null; } + public static String formatVideoDurationFast(int minutes, int seconds) { + StringBuilder stringBuilder = new StringBuilder(); + if (minutes >= 60) { + normalizeTimePart(stringBuilder, minutes / 60); + stringBuilder.append(":"); + normalizeTimePart(stringBuilder, minutes % 60); + stringBuilder.append(":"); + normalizeTimePart(stringBuilder,seconds); + } else { + normalizeTimePart(stringBuilder, minutes); + stringBuilder.append(":"); + normalizeTimePart(stringBuilder, seconds); + } + return stringBuilder.toString(); + } + + public static String formatTimerDurationFast(long seconds, int ms) { + StringBuilder stringBuilder = new StringBuilder(); + long minutes = seconds / 60; + if (minutes >= 60) { + stringBuilder.append(minutes / 60).append(":"); + normalizeTimePart(stringBuilder, minutes % 60); + stringBuilder.append(":"); + normalizeTimePart(stringBuilder, seconds % 60); + stringBuilder.append("," ).append(ms / 10); + } else { + stringBuilder.append(minutes).append(":"); + normalizeTimePart(stringBuilder, seconds % 60); + stringBuilder.append(",").append(ms / 10); + } + return stringBuilder.toString(); + } + + public static void normalizeTimePart(StringBuilder stringBuilder, int time) { + if (time < 10) { + stringBuilder + .append("0") + .append(time); + } else { + stringBuilder.append(time); + } + } + + public static void normalizeTimePart(StringBuilder stringBuilder, long time) { + if (time < 10) { + stringBuilder + .append("0") + .append(time); + } else { + stringBuilder.append(time); + } + } + + public static void getViewPositionInParent(View view, ViewGroup parent, float[] pointPosition) { + pointPosition[0] = 0; + pointPosition[1] = 0; + View currentView = view; + while (currentView != parent) { + //fix strange offset inside view pager + if (!(currentView.getParent() instanceof ViewPager)) { + pointPosition[0] += currentView.getX(); + pointPosition[1] += currentView.getY(); + } + currentView = (View) currentView.getParent(); + } + } + + public static MotionEvent emptyMotionEvent() { + return MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0, 0, 0); + } + + public static boolean isDarkColor(int color) { + return AndroidUtilities.computePerceivedBrightness(color) < 0.721f; + } + + @RequiresApi(api = Build.VERSION_CODES.N) + public static void getBitmapFromSurface(SurfaceView surfaceView, Bitmap surfaceBitmap) { + if (surfaceView == null || !surfaceView.getHolder().getSurface().isValid()) { + return; + } + CountDownLatch countDownLatch = new CountDownLatch(1); + PixelCopy.request(surfaceView, surfaceBitmap, copyResult -> { + countDownLatch.countDown(); + }, Utilities.searchQueue.getHandler()); + try { + countDownLatch.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + private static class LinkSpec { String url; int start; @@ -749,7 +894,11 @@ public static boolean addLinks(Spannable text, int mask, boolean internalOnly, b } } } - text.setSpan(new URLSpan(link.url), link.start, link.end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + String url = link.url; + if (url != null) { + url = url.replaceAll("∕|⁄|%E2%81%84|%E2%88%95", "/"); + } + text.setSpan(new URLSpan(url), link.start, link.end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } return true; } @@ -863,6 +1012,10 @@ public static int calcBitmapColor(Bitmap bitmap) { } public static int[] calcDrawableColor(Drawable drawable) { + if (drawable instanceof ChatBackgroundDrawable) { + ChatBackgroundDrawable chatBackgroundDrawable = (ChatBackgroundDrawable) drawable; + return calcDrawableColor(chatBackgroundDrawable.getDrawable()); + } int bitmapColor = 0xff000000; int[] result = new int[4]; try { @@ -1287,6 +1440,20 @@ public static void lockOrientation(Activity activity) { } } + + @SuppressLint("WrongConstant") + public static void lockOrientation(Activity activity, int orientation) { + if (activity == null) { + return; + } + try { + prevOrientation = activity.getRequestedOrientation(); + activity.setRequestedOrientation(orientation); + } catch (Exception e) { + FileLog.e(e); + } + } + @SuppressLint("WrongConstant") public static void unlockOrientation(Activity activity) { if (activity == null) { @@ -2031,6 +2198,7 @@ public static void checkDisplaySize(Context context, Configuration newConfigurat display.getMetrics(displayMetrics); display.getSize(displaySize); screenRefreshRate = display.getRefreshRate(); + screenRefreshTime = 1000 / screenRefreshRate; } } if (configuration.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED) { @@ -2594,6 +2762,39 @@ public static SpannableStringBuilder replaceTags(String str, int flag, Object... return new SpannableStringBuilder(str); } + private static Pattern linksPattern; + public static SpannableStringBuilder replaceLinks(String str, Theme.ResourcesProvider resourcesProvider) { + if (linksPattern == null) { + linksPattern = Pattern.compile("\\[(.+?)\\]\\((.+?)\\)"); + } + SpannableStringBuilder spannable = new SpannableStringBuilder(); + Matcher matcher = linksPattern.matcher(str); + int lastMatchEnd = 0; + while (matcher.find()) { + spannable.append(str, lastMatchEnd, matcher.start()); + String linkText = matcher.group(1); + String url = matcher.group(2); + spannable.append(linkText); + int start = spannable.length() - linkText.length(); + int end = spannable.length(); + spannable.setSpan(new ClickableSpan() { + @Override + public void onClick(@NonNull View widget) { + Browser.openUrl(ApplicationLoader.applicationContext, url); + } + @Override + public void updateDrawState(@NonNull TextPaint ds) { + ds.setColor(Theme.getColor(Theme.key_chat_messageLinkIn, resourcesProvider)); + ds.setUnderlineText(false); + } + }, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + + lastMatchEnd = matcher.end(); + } + spannable.append(str, lastMatchEnd, str.length()); + return spannable; + } + public static class LinkMovementMethodMy extends LinkMovementMethod { @Override public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) { @@ -3018,10 +3219,10 @@ public static File generateVideoPath(boolean secretChat) { } public static String formatFileSize(long size) { - return formatFileSize(size, false); + return formatFileSize(size, false, false); } - public static String formatFileSize(long size, boolean removeZero) { + public static String formatFileSize(long size, boolean removeZero, boolean makeShort) { if (size == 0) { return String.format("%d KB", 0); } else if (size < 1024) { @@ -3044,6 +3245,8 @@ public static String formatFileSize(long size, boolean removeZero) { float value = (int) (size / 1024L / 1024L) / 1000.0f; if (removeZero && (value - (int) value) * 10 == 0) { return String.format("%d GB", (int) value); + } else if (makeShort) { + return String.format("%.1f GB", value); } else { return String.format("%.2f GB", value); } @@ -3560,7 +3763,7 @@ public static boolean isBannedForever(TLRPC.TL_chatBannedRights rights) { return rights == null || Math.abs(rights.until_date - System.currentTimeMillis() / 1000) > 5 * 365 * 24 * 60 * 60; } - public static void setRectToRect(Matrix matrix, RectF src, RectF dst, int rotation, boolean translate) { + public static void setRectToRect(Matrix matrix, RectF src, RectF dst, int rotation, int invert, boolean translate) { float tx, sx; float ty, sy; boolean xLarger = false; @@ -3582,12 +3785,27 @@ public static void setRectToRect(Matrix matrix, RectF src, RectF dst, int rotati } if (rotation == 90) { matrix.preRotate(90); + if (invert == 1) { + matrix.preScale(-1, 1); + } else if (invert == 2) { + matrix.preScale(1, -1); + } matrix.preTranslate(0, -dst.width()); } else if (rotation == 180) { matrix.preRotate(180); + if (invert == 1) { + matrix.preScale(-1, 1); + } else if (invert == 2) { + matrix.preScale(1, -1); + } matrix.preTranslate(-dst.width(), -dst.height()); } else if (rotation == 270) { matrix.preRotate(270); + if (invert == 1) { + matrix.preScale(-1, 1); + } else if (invert == 2) { + matrix.preScale(1, -1); + } matrix.preTranslate(-dst.height(), 0); } @@ -3762,7 +3980,7 @@ protected void onDetachedFromWindow() { ConnectionsManager.getInstance(UserConfig.selectedAccount).checkProxy(address, Integer.parseInt(port), user, password, secret, time -> AndroidUtilities.runOnUIThread(() -> { if (time == -1) { cell.getTextView().setText(LocaleController.getString(R.string.Unavailable)); - cell.getTextView().setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText4)); + cell.getTextView().setTextColor(Theme.getColor(Theme.key_text_RedRegular)); } else { cell.getTextView().setText(LocaleController.getString(R.string.Available) + ", " + LocaleController.formatString(R.string.Ping, time)); cell.getTextView().setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGreenText)); @@ -3770,7 +3988,7 @@ protected void onDetachedFromWindow() { })); } catch (NumberFormatException ignored) { cell.getTextView().setText(LocaleController.getString(R.string.Unavailable)); - cell.getTextView().setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText4)); + cell.getTextView().setTextColor(Theme.getColor(Theme.key_text_RedRegular)); } } } @@ -3896,7 +4114,7 @@ public static void showVmessAlert(Context activity, final SharedConfig.VmessProx @Override public void run(long time) { int c = count.getAndIncrement(); - String colorKey; + int colorKey; if (time != -1) { info.stop(); cell.setTextAndValue(LocaleController.getString("Available", R.string.Available), LocaleController.formatString("Ping", R.string.Ping, time), true); @@ -4016,7 +4234,7 @@ public static void showTrojanAlert(Context activity, final SharedConfig.VmessPro @Override public void run(long time) { int c = count.getAndIncrement(); - String colorKey; + int colorKey; if (time != -1) { info.stop(); cell.setTextAndValue(LocaleController.getString("Available", R.string.Available), LocaleController.formatString("Ping", R.string.Ping, time), true); @@ -4131,7 +4349,7 @@ public static void showShadowsocksAlert(Context activity, final SharedConfig.Sha @Override public void run(long time) { int c = count.getAndIncrement(); - String colorKey; + int colorKey; if (time != -1) { info.stop(); cell.setTextAndValue(LocaleController.getString("Available", R.string.Available), LocaleController.formatString("Ping", R.string.Ping, time), true); @@ -4259,7 +4477,7 @@ public static void showShadowsocksRAlert(Context activity, final SharedConfig.Sh @Override public void run(long time) { int c = count.getAndIncrement(); - String colorKey; + int colorKey; if (time != -1) { info.stop(); cell.setTextAndValue(LocaleController.getString("Available", R.string.Available), LocaleController.formatString("Ping", R.string.Ping, time), true); @@ -4365,7 +4583,7 @@ public static void showWsAlert(Context activity, final SharedConfig.WsProxy info @Override public void run(long time) { int c = count.getAndIncrement(); - String colorKey; + int colorKey; if (time != -1) { info.stop(); cell.setTextAndValue(LocaleController.getString("Available", R.string.Available), LocaleController.formatString("Ping", R.string.Ping, time), true); @@ -4748,10 +4966,10 @@ public static float lerp(float[] ab, float f) { public static void lerp(RectF a, RectF b, float f, RectF to) { if (to != null) { to.set( - AndroidUtilities.lerp(a.left, b.left, f), - AndroidUtilities.lerp(a.top, b.top, f), - AndroidUtilities.lerp(a.right, b.right, f), - AndroidUtilities.lerp(a.bottom, b.bottom, f) + lerp(a.left, b.left, f), + lerp(a.top, b.top, f), + lerp(a.right, b.right, f), + lerp(a.bottom, b.bottom, f) ); } } @@ -4759,10 +4977,10 @@ public static void lerp(RectF a, RectF b, float f, RectF to) { public static void lerp(Rect a, Rect b, float f, Rect to) { if (to != null) { to.set( - AndroidUtilities.lerp(a.left, b.left, f), - AndroidUtilities.lerp(a.top, b.top, f), - AndroidUtilities.lerp(a.right, b.right, f), - AndroidUtilities.lerp(a.bottom, b.bottom, f) + lerp(a.left, b.left, f), + lerp(a.top, b.top, f), + lerp(a.right, b.right, f), + lerp(a.bottom, b.bottom, f) ); } } @@ -4781,33 +4999,6 @@ public static float computeDampingRatio(float tension /* stiffness */, float fri return friction / (2f * (float) Math.sqrt(mass * tension)); } - private static WeakReference flagSecureFragment; - - public static boolean hasFlagSecureFragment() { - return flagSecureFragment != null; - } - - public static void setFlagSecure(BaseFragment parentFragment, boolean set) { - if (parentFragment == null || parentFragment.getParentActivity() == null) { - return; - } - if (set && !NekoXConfig.disableFlagSecure) { - try { - parentFragment.getParentActivity().getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE); - flagSecureFragment = new WeakReference<>(parentFragment); - } catch (Exception ignore) { - - } - } else if (flagSecureFragment != null && flagSecureFragment.get() == parentFragment) { - try { - parentFragment.getParentActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE); - } catch (Exception ignore) { - - } - flagSecureFragment = null; - } - } - public static void openSharing(BaseFragment fragment, String url) { if (fragment == null || fragment.getParentActivity() == null) { return; @@ -4933,12 +5124,14 @@ public static boolean getLightNavigationBar(Window window) { public static void setLightNavigationBar(View view, boolean enable) { if (view != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { int flags = view.getSystemUiVisibility(); - if (enable) { - flags |= View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; - } else { - flags &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; + if (((flags & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) > 0) != enable) { + if (enable) { + flags |= View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; + } else { + flags &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; + } + view.setSystemUiVisibility(flags); } - view.setSystemUiVisibility(flags); } } @@ -4963,6 +5156,9 @@ public static void setNavigationBarColor(Window window, int color, boolean anima } public static void setNavigationBarColor(Window window, int color, boolean animated, IntColorCallback onUpdate) { + if (window == null) { + return; + } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (navigationBarColorAnimators != null) { ValueAnimator animator = navigationBarColorAnimators.get(window); @@ -5132,10 +5328,14 @@ public static void updateImageViewImageAnimated(ImageView imageView, Drawable ne } public static void updateViewVisibilityAnimated(View view, boolean show) { - updateViewVisibilityAnimated(view, show, 1f, true); + updateViewVisibilityAnimated(view, show, 1f, true, true); } public static void updateViewVisibilityAnimated(View view, boolean show, float scaleFactor, boolean animated) { + updateViewVisibilityAnimated(view, show, scaleFactor, true, animated); + } + + public static void updateViewVisibilityAnimated(View view, boolean show, float scaleFactor, boolean goneOnHide, boolean animated) { if (view == null) { return; } @@ -5145,7 +5345,7 @@ public static void updateViewVisibilityAnimated(View view, boolean show, float s if (!animated) { view.animate().setListener(null).cancel(); - view.setVisibility(show ? View.VISIBLE : View.GONE); + view.setVisibility(show ? View.VISIBLE : (goneOnHide ? View.GONE : View.INVISIBLE)); view.setTag(show ? 1 : null); view.setAlpha(1f); view.setScaleX(1f); @@ -5162,7 +5362,7 @@ public static void updateViewVisibilityAnimated(View view, boolean show, float s view.setTag(1); } else if (!show && view.getTag() != null) { view.animate().setListener(null).cancel(); - view.animate().alpha(0).scaleY(scaleFactor).scaleX(scaleFactor).setListener(new HideViewAfterAnimation(view)).setDuration(150).start(); + view.animate().alpha(0).scaleY(scaleFactor).scaleX(scaleFactor).setListener(new HideViewAfterAnimation(view, goneOnHide)).setDuration(150).start(); view.setTag(null); } } @@ -5296,7 +5496,7 @@ public static Uri getBitmapShareUri(Bitmap bitmap, String fileName, Bitmap.Compr } File file = new File(cachePath, fileName); try (FileOutputStream out = new FileOutputStream(file)) { - bitmap.compress(format, 100, out); + bitmap.compress(format, 87, out); out.close(); return FileProvider.getUriForFile(ApplicationLoader.applicationContext, ApplicationLoader.getApplicationId() + ".provider", file); } catch (Exception e) { @@ -5403,15 +5603,10 @@ public static Bitmap makeBlurBitmap(View view, float downscale, int maxRadius) { return bitmap; } - public static void makeGlobalBlurBitmap(Utilities.Callback onBitmapDone, float amount) { - if (onBitmapDone == null) { - return; - } - - List views = null; + public static List allGlobalViews() { try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - views = WindowInspector.getGlobalWindowViews(); + return WindowInspector.getGlobalWindowViews(); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { Class wmgClass = Class.forName("android.view.WindowManagerGlobal"); Object wmgInstance = wmgClass.getMethod("getInstance").invoke(null, (Object[]) null); @@ -5420,10 +5615,11 @@ public static void makeGlobalBlurBitmap(Utilities.Callback onBitmapDone, Method getRootView = wmgClass.getMethod("getRootView", String.class); String[] rootViewNames = (String[]) getViewRootNames.invoke(wmgInstance, (Object[]) null); - views = new ArrayList<>(); + List views = new ArrayList<>(); for (String viewName : rootViewNames) { views.add((View) getRootView.invoke(wmgInstance, viewName)); } + return views; } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH && Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { Class wmiClass = Class.forName("android.view.WindowManagerImpl"); Object wmiInstance = wmiClass.getMethod("getDefault").invoke(null); @@ -5433,15 +5629,24 @@ public static void makeGlobalBlurBitmap(Utilities.Callback onBitmapDone, Object viewsObject = viewsField.get(wmiInstance); if (viewsObject instanceof List) { - views = (List) viewsField.get(wmiInstance); + return (List) viewsField.get(wmiInstance); } else if (viewsObject instanceof View[]) { - views = Arrays.asList((View[]) viewsField.get(wmiInstance)); + return Arrays.asList((View[]) viewsField.get(wmiInstance)); } } } catch (Exception e) { - FileLog.e("makeGlobalBlurBitmap()", e); + FileLog.e("allGlobalViews()", e); + } + return null; + } + + public static void makeGlobalBlurBitmap(Utilities.Callback onBitmapDone, float amount) { + if (onBitmapDone == null) { + return; } + List views = allGlobalViews(); + if (views == null) { onBitmapDone.run(null); return; @@ -5614,4 +5819,159 @@ public static Long getSysInfoLong(String path) { } return null; } + + public static boolean isActivityRunning(Activity activity) { + if (activity == null) { + return false; + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + return !activity.isDestroyed() && !activity.isFinishing(); + } else { + return !activity.isFinishing(); + } + } + + public static Pair getImageOrientation(InputStream is) { + try { + return getImageOrientation(new ExifInterface(is)); + } catch (Exception e) { + FileLog.e(e); + } + return new Pair<>(0, 0); + } + public static Pair getImageOrientation(File file) { + try { + return getImageOrientation(new ExifInterface(file)); + } catch (Exception e) { + FileLog.e(e); + } + return new Pair<>(0, 0); + } + public static Pair getImageOrientation(String path) { + try { + return getImageOrientation(new ExifInterface(path)); + } catch (Exception ignore) {} + return new Pair<>(0, 0); + } + + public static Pair getImageOrientation(ExifInterface exif) { + try { + int orientation = 0, invert = 0; + final int exifvalue = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); + switch (exifvalue) { + case ExifInterface.ORIENTATION_NORMAL: + break; + case ExifInterface.ORIENTATION_FLIP_HORIZONTAL: + invert = 1; + break; + case ExifInterface.ORIENTATION_ROTATE_180: + orientation = 180; + break; + case ExifInterface.ORIENTATION_FLIP_VERTICAL: + invert = 2; + break; + case ExifInterface.ORIENTATION_TRANSPOSE: + invert = 2; + orientation = 270; + break; + case ExifInterface.ORIENTATION_ROTATE_90: + orientation = 90; + break; + case ExifInterface.ORIENTATION_TRANSVERSE: + invert = 1; + orientation = 270; + break; + case ExifInterface.ORIENTATION_ROTATE_270: + orientation = 270; + break; + } + return new Pair<>(orientation, invert); + } catch (Exception e) { + FileLog.e(e); + } + return new Pair<>(0, 0); + } + + public static void forEachViews(RecyclerView recyclerView, Consumer consumer) { + for (int i = 0; i < recyclerView.getChildCount(); i++) { + consumer.accept(recyclerView.getChildAt(i)); + } + for (int i = 0; i < recyclerView.getCachedChildCount(); i++) { + consumer.accept(recyclerView.getCachedChildAt(i)); + } + for (int i = 0; i < recyclerView.getHiddenChildCount(); i++) { + consumer.accept(recyclerView.getHiddenChildAt(i)); + } + for (int i = 0; i < recyclerView.getAttachedScrapChildCount(); i++) { + consumer.accept(recyclerView.getAttachedScrapChildAt(i)); + } + } + + public static int getDominantColor(Bitmap bitmap) { + if (bitmap == null) { + return Color.WHITE; + } + float stepH = (bitmap.getHeight() - 1) / 10f; + float stepW = (bitmap.getWidth() - 1) / 10f; + int r = 0, g = 0, b = 0; + int amount = 0; + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + int x = (int) (stepW * i); + int y = (int) (stepH * j); + int pixel = bitmap.getPixel(x, y); + if (Color.alpha(pixel) > 200) { + r += Color.red(pixel); + g += Color.green(pixel); + b += Color.blue(pixel); + amount++; + } + } + } + if (amount == 0) { + return 0; + } + return Color.argb(255, r / amount, g / amount, b / amount); + } + + @NonNull + public static String translitSafe(String str) { + try { + if (str != null) { + str = str.toLowerCase(); + } + String s = LocaleController.getInstance().getTranslitString(str, false); + if (s == null) { + return ""; + } + return s; + } catch (Exception ignore) {} + return ""; + } + + public static void quietSleep(long millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException ignored) { + + } + } + + public static ByteBuffer cloneByteBuffer(ByteBuffer original) { + ByteBuffer clone; + try { + clone = ByteBuffer.allocate(original.capacity()); + } catch (OutOfMemoryError error) { + System.gc(); + clone = ByteBuffer.allocate(original.capacity()); + } + int position = original.position(); + original.rewind(); + clone.put(original); + original.rewind(); + clone.flip(); + clone.position(position); + return clone; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/AnimatedFileDrawableStream.java b/TMessagesProj/src/main/java/org/telegram/messenger/AnimatedFileDrawableStream.java index 55c2fa5641..8e2b380079 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/AnimatedFileDrawableStream.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/AnimatedFileDrawableStream.java @@ -1,12 +1,15 @@ package org.telegram.messenger; +import com.google.android.exoplayer2.ExoPlayer; +import com.google.android.exoplayer2.util.Log; + import org.telegram.tgnet.TLRPC; import java.util.concurrent.CountDownLatch; public class AnimatedFileDrawableStream implements FileLoadOperationStream { - private final FileLoadOperation loadOperation; + private FileLoadOperation loadOperation; private CountDownLatch countDownLatch; private TLRPC.Document document; private ImageLocation location; @@ -46,7 +49,7 @@ public int read(int offset, int readLength) { synchronized (sync) { if (canceled) { debugCanceledCount++; - if (!debugReportSend && debugCanceledCount > 100) { + if (!debugReportSend && debugCanceledCount > 200) { debugReportSend = true; if (BuildVars.DEBUG_PRIVATE_VERSION) { throw new RuntimeException("infinity stream reading!!!"); @@ -76,22 +79,30 @@ public int read(int offset, int readLength) { return 0; } } + countDownLatch = new CountDownLatch(1); if (loadOperation.isPaused() || lastOffset != offset || preview) { - FileLoader.getInstance(currentAccount).loadStreamFile(this, document, location, parentObject, offset, preview, loadingPriority); + FileLoadOperation loadOperation = FileLoader.getInstance(currentAccount).loadStreamFile(this, document, location, parentObject, offset, preview, loadingPriority); + if (this.loadOperation != loadOperation) { + this.loadOperation.removeStreamListener(this); + this.loadOperation = loadOperation; + } + lastOffset = offset + availableLength; } synchronized (sync) { if (canceled) { + countDownLatch = null; cancelLoadingInternal(); return 0; } - countDownLatch = new CountDownLatch(1); } if (!preview) { FileLoader.getInstance(currentAccount).setLoadingVideo(document, false, true); } - waitingForLoad = true; - countDownLatch.await(); - waitingForLoad = false; + if (countDownLatch != null) { + waitingForLoad = true; + countDownLatch.await(); + waitingForLoad = false; + } } } lastOffset = offset + availableLength; @@ -113,10 +124,17 @@ public void cancel(boolean removeLoading) { synchronized (sync) { if (countDownLatch != null) { countDownLatch.countDown(); + countDownLatch = null; if (removeLoading && !canceled && !preview) { FileLoader.getInstance(currentAccount).removeLoadingVideo(document, false, true); } } + if (parentObject instanceof MessageObject) { + MessageObject messageObject = (MessageObject) parentObject; + if (DownloadController.getInstance(messageObject.currentAccount).isDownloading(messageObject.getId())) { + removeLoading = false; + } + } if (removeLoading) { cancelLoadingInternal(); } @@ -165,6 +183,11 @@ public boolean isWaitingForLoad() { public void newDataAvailable() { if (countDownLatch != null) { countDownLatch.countDown(); + countDownLatch = null; } } + + public boolean isCanceled() { + return canceled; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/AnimationNotificationsLocker.java b/TMessagesProj/src/main/java/org/telegram/messenger/AnimationNotificationsLocker.java new file mode 100644 index 0000000000..4fb4c254db --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/AnimationNotificationsLocker.java @@ -0,0 +1,35 @@ +package org.telegram.messenger; + +public class AnimationNotificationsLocker { + + int currentAccount = UserConfig.selectedAccount; + int notificationsIndex = -1; + int globalNotificationsIndex = -1; + + final int[] allowedNotifications; + + public AnimationNotificationsLocker() { + this(null); + } + + public AnimationNotificationsLocker(int[] allowedNotifications) { + this.allowedNotifications = allowedNotifications; + } + + public void lock() { + int currentAccount = UserConfig.selectedAccount; + if (this.currentAccount != currentAccount) { + NotificationCenter.getInstance(currentAccount).onAnimationFinish(notificationsIndex); + notificationsIndex = -1; + this.currentAccount = currentAccount; + } + notificationsIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(notificationsIndex, allowedNotifications); + globalNotificationsIndex = NotificationCenter.getGlobalInstance().setAnimationInProgress(globalNotificationsIndex, allowedNotifications); + } + + public void unlock() { + NotificationCenter.getInstance(currentAccount).onAnimationFinish(notificationsIndex); + NotificationCenter.getGlobalInstance().onAnimationFinish(globalNotificationsIndex); + } + +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java index 2b02b9da4f..ee32ea49d6 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java @@ -623,4 +623,12 @@ protected void startAppCenterInternal(Activity context) { } + public static void logDualCamera(boolean success, boolean vendor) { + applicationLoaderInstance.logDualCameraInternal(success, vendor); + } + + protected void logDualCameraInternal(boolean success, boolean vendor) { + + } + } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/AutoDeleteMediaTask.java b/TMessagesProj/src/main/java/org/telegram/messenger/AutoDeleteMediaTask.java index e0568a159c..8f9d2f4bda 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/AutoDeleteMediaTask.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/AutoDeleteMediaTask.java @@ -1,7 +1,11 @@ package org.telegram.messenger; +import static org.telegram.messenger.CacheByChatsController.KEEP_MEDIA_TYPE_STORIES; + import android.util.SparseArray; +import com.google.android.exoplayer2.util.Log; + import java.io.File; import java.util.ArrayList; import java.util.Collections; @@ -37,10 +41,10 @@ public static void run() { } } - int[] keepMediaByTypes = new int[3]; + int[] keepMediaByTypes = new int[4]; boolean allKeepMediaTypesForever = true; long keepMediaMinSeconds = Long.MAX_VALUE; - for (int i = 0; i < 3; i++) { + for (int i = 0; i < 4; i++) { keepMediaByTypes[i] = SharedConfig.getPreferences().getInt("keep_media_type_" + i, CacheByChatsController.getDefault(i)); if (keepMediaByTypes[i] != CacheByChatsController.KEEP_MEDIA_FOREVER) { allKeepMediaTypesForever = false; @@ -60,10 +64,13 @@ public static void run() { long deletedFilesBySizeSize = 0; int skippedFiles = 0; - if (!allKeepMediaTypesForever) { + //if (!allKeepMediaTypesForever) { //long currentTime = time - 60 * 60 * 24 * days; final SparseArray paths = ImageLoader.getInstance().createMediaPaths(); for (int a = 0; a < paths.size(); a++) { + if (allKeepMediaTypesForever && (paths.keyAt(a) == FileLoader.MEDIA_DIR_AUDIO || paths.keyAt(a) == FileLoader.MEDIA_DIR_DOCUMENT)) { + continue; + } boolean isCacheDir = false; if (paths.keyAt(a) == FileLoader.MEDIA_DIR_CACHE) { isCacheDir = true; @@ -85,24 +92,31 @@ public static void run() { } for (int i = 0; i < keepMediaFiles.size(); i++) { CacheByChatsController.KeepMediaFile file = keepMediaFiles.get(i); - if (file.keepMedia == CacheByChatsController.KEEP_MEDIA_FOREVER) { - continue; - } - long seconds; - if (file.keepMedia >= 0) { - seconds = CacheByChatsController.getDaysInSeconds(file.keepMedia); - } else if (file.dialogType >= 0) { - seconds = CacheByChatsController.getDaysInSeconds(keepMediaByTypes[file.dialogType]); - } else if (isCacheDir) { - continue; + long timeLocal; + if (file.isStory) { + long seconds = CacheByChatsController.getDaysInSeconds(keepMediaByTypes[KEEP_MEDIA_TYPE_STORIES]); + timeLocal = time - seconds; } else { - seconds = keepMediaMinSeconds; - } - if (seconds == Long.MAX_VALUE) { - continue; + if (file.keepMedia == CacheByChatsController.KEEP_MEDIA_FOREVER) { + continue; + } + long seconds; + if (file.keepMedia >= 0) { + seconds = CacheByChatsController.getDaysInSeconds(file.keepMedia); + } else if (file.dialogType >= 0) { + seconds = CacheByChatsController.getDaysInSeconds(keepMediaByTypes[file.dialogType]); + } else if (isCacheDir) { + continue; + } else { + seconds = keepMediaMinSeconds; + } + if (seconds == Long.MAX_VALUE) { + continue; + } + + timeLocal = time - seconds; } long lastUsageTime = Utilities.getLastUsageFileTime(file.file.getAbsolutePath()); - long timeLocal = time - seconds; boolean needDelete = lastUsageTime > 316000000 && lastUsageTime < timeLocal && !usingFilePaths.contains(file.file.getPath()); if (needDelete) { try { @@ -111,7 +125,7 @@ public static void run() { autoDeletedFilesSize += file.file.length(); } if (BuildVars.DEBUG_PRIVATE_VERSION) { - FileLog.d("delete file " + file.file.getPath() + " last_usage_time=" + lastUsageTime + " time_local=" + timeLocal); + FileLog.d("delete file " + file.file.getPath() + " last_usage_time=" + lastUsageTime + " time_local=" + timeLocal + " story=" + file.isStory); } file.file.delete(); } catch (Exception exception) { @@ -123,7 +137,7 @@ public static void run() { FileLog.e(e); } } - } + //} int maxCacheGb = SharedConfig.getPreferences().getInt("cache_limit", Integer.MAX_VALUE); if (maxCacheGb != Integer.MAX_VALUE) { @@ -133,7 +147,6 @@ public static void run() { } else { maxCacheSize = maxCacheGb * 1024L * 1024L * 1000L; } - final SparseArray paths = ImageLoader.getInstance().createMediaPaths(); long totalSize = 0; for (int a = 0; a < paths.size(); a++) { totalSize += Utilities.getDirSize(paths.valueAt(a).getAbsolutePath(), 0, true); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/AutoMessageHeardReceiver.java b/TMessagesProj/src/main/java/org/telegram/messenger/AutoMessageHeardReceiver.java index 41f4a0474b..29f60ff7a3 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/AutoMessageHeardReceiver.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/AutoMessageHeardReceiver.java @@ -34,6 +34,7 @@ public void onReceive(Context context, Intent intent) { AndroidUtilities.runOnUIThread(() -> { accountInstance.getMessagesController().putUser(user1, true); MessagesController.getInstance(currentAccount).markDialogAsRead(dialogId, maxId, maxId, 0, false, 0, 0, true, 0); + MessagesController.getInstance(currentAccount).markReactionsAsRead(dialogId, 0); }); }); return; @@ -46,11 +47,13 @@ public void onReceive(Context context, Intent intent) { AndroidUtilities.runOnUIThread(() -> { accountInstance.getMessagesController().putChat(chat1, true); MessagesController.getInstance(currentAccount).markDialogAsRead(dialogId, maxId, maxId, 0, false, 0, 0, true, 0); + MessagesController.getInstance(currentAccount).markReactionsAsRead(dialogId, 0); }); }); return; } } MessagesController.getInstance(currentAccount).markDialogAsRead(dialogId, maxId, maxId, 0, false, 0, 0, true, 0); + MessagesController.getInstance(currentAccount).markReactionsAsRead(dialogId, 0); } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/AutoMessageReplyReceiver.java b/TMessagesProj/src/main/java/org/telegram/messenger/AutoMessageReplyReceiver.java index 8430e7f537..10c99c1f98 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/AutoMessageReplyReceiver.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/AutoMessageReplyReceiver.java @@ -35,7 +35,7 @@ public void onReceive(Context context, Intent intent) { if (dialogId == 0 || maxId == 0 || !UserConfig.isValidAccount(currentAccount)) { return; } - SendMessagesHelper.getInstance(currentAccount).sendMessage(text.toString(), dialogId, null, null, null, true, null, null, null, true, 0, null, false); + SendMessagesHelper.getInstance(currentAccount).sendMessage(SendMessagesHelper.SendMessageParams.of(text.toString(), dialogId, null, null, null, true, null, null, null, true, 0, null, false)); MessagesController.getInstance(currentAccount).markDialogAsRead(dialogId, maxId, maxId, 0, false, 0, 0, true, 0); } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/BillingController.java b/TMessagesProj/src/main/java/org/telegram/messenger/BillingController.java index 36d3b31a89..3bdcf29583 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/BillingController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/BillingController.java @@ -9,21 +9,20 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.util.Consumer; +import androidx.core.util.Pair; import com.google.android.exoplayer2.util.Util; -import org.json.JSONObject; +import org.telegram.messenger.utils.BillingUtilities; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLRPC; import org.telegram.ui.PremiumPreviewFragment; -import java.io.InputStream; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Currency; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; @@ -66,12 +65,12 @@ public String formatCurrency(long amount, String currency, int exp) { if (cur != null) { NumberFormat numberFormat = NumberFormat.getCurrencyInstance(); numberFormat.setCurrency(cur); - return numberFormat.format(amount / Math.pow(10, exp)); } return amount + " " + currency; } + @SuppressWarnings("ConstantConditions") public int getCurrencyExp(String currency) { return 0; } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/CacheByChatsController.java b/TMessagesProj/src/main/java/org/telegram/messenger/CacheByChatsController.java index 93a8c7a39e..c182fab7da 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/CacheByChatsController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/CacheByChatsController.java @@ -17,20 +17,22 @@ public class CacheByChatsController { public static int KEEP_MEDIA_ONE_DAY = 3; public static int KEEP_MEDIA_ONE_WEEK = 0; public static int KEEP_MEDIA_ONE_MONTH = 1; + public static int KEEP_MEDIA_TWO_DAY = 6; //TEST VALUE public static int KEEP_MEDIA_ONE_MINUTE = 5; public static final int KEEP_MEDIA_TYPE_USER = 0; public static final int KEEP_MEDIA_TYPE_GROUP = 1; public static final int KEEP_MEDIA_TYPE_CHANNEL = 2; + public static final int KEEP_MEDIA_TYPE_STORIES = 3; private final int currentAccount; - int[] keepMediaByTypes = {-1, -1, -1}; + int[] keepMediaByTypes = {-1, -1, -1, -1}; public CacheByChatsController(int currentAccount) { this.currentAccount = currentAccount; - for (int i = 0; i < 3; i++) { + for (int i = 0; i < 4; i++) { keepMediaByTypes[i] = SharedConfig.getPreferences().getInt("keep_media_type_" + i, getDefault(i)); } } @@ -42,6 +44,8 @@ public static int getDefault(int type) { return KEEP_MEDIA_ONE_MONTH; } else if (type == KEEP_MEDIA_TYPE_CHANNEL) { return KEEP_MEDIA_ONE_WEEK; + } else if (type == KEEP_MEDIA_TYPE_STORIES) { + return KEEP_MEDIA_TWO_DAY; } return SharedConfig.keepMedia; } @@ -51,6 +55,8 @@ public static String getKeepMediaString(int keepMedia) { return LocaleController.formatPluralString("Minutes", 1); } else if (keepMedia == KEEP_MEDIA_ONE_DAY) { return LocaleController.formatPluralString("Days", 1); + } else if (keepMedia == KEEP_MEDIA_TWO_DAY) { + return LocaleController.formatPluralString("Days", 2); } else if (keepMedia == KEEP_MEDIA_ONE_WEEK) { return LocaleController.formatPluralString("Weeks", 1); } else if (keepMedia == KEEP_MEDIA_ONE_MONTH) { @@ -67,7 +73,9 @@ public static long getDaysInSeconds(int keepMedia) { seconds = 60L * 60L * 24L * 30L; } else if (keepMedia == CacheByChatsController.KEEP_MEDIA_ONE_DAY) { seconds = 60L * 60L * 24L; - } else if (keepMedia == CacheByChatsController.KEEP_MEDIA_ONE_MINUTE && BuildVars.DEBUG_PRIVATE_VERSION) { //one min + } else if (keepMedia == CacheByChatsController.KEEP_MEDIA_TWO_DAY) { + seconds = 60L * 60L * 24L * 2; + }else if (keepMedia == CacheByChatsController.KEEP_MEDIA_ONE_MINUTE && BuildVars.DEBUG_PRIVATE_VERSION) { //one min seconds = 60L; } else { seconds = Long.MAX_VALUE; @@ -187,6 +195,7 @@ public static class KeepMediaFile { final File file; int keepMedia = -1; int dialogType = KEEP_MEDIA_TYPE_CHANNEL; + boolean isStory; public KeepMediaFile(File file) { this.file = file; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/Fetcher.java b/TMessagesProj/src/main/java/org/telegram/messenger/CacheFetcher.java similarity index 89% rename from TMessagesProj/src/main/java/org/telegram/messenger/Fetcher.java rename to TMessagesProj/src/main/java/org/telegram/messenger/CacheFetcher.java index 9db4bd018f..d8eec004a7 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/Fetcher.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/CacheFetcher.java @@ -5,9 +5,9 @@ import java.util.ArrayList; import java.util.HashMap; -public abstract class Fetcher { +public abstract class CacheFetcher { - protected void getRemote(int currentAccount, Args arguments, long hash, Utilities.Callback3 onResult) { + protected void getRemote(int currentAccount, Args arguments, long hash, Utilities.Callback4 onResult) { // Implement this function } @@ -50,8 +50,10 @@ public void fetch(int currentAccount, Args arguments, Utilities.Callback onRe saveCallback(key, onResult); getLocal(currentAccount, arguments, (hash, data) -> { if (shouldRequest(key)) { - saveLastRequested(key); - getRemote(currentAccount, arguments, hash, (notModified, remoteData, newHash) -> { + getRemote(currentAccount, arguments, hash, (notModified, remoteData, newHash, requestSuccess) -> { + if (requestSuccess) { + saveLastRequested(key); + } if (notModified) { cacheResult(key, data); callCallbacks(key, data); @@ -99,6 +101,13 @@ private boolean shouldRequest(Pair key) { return lastRequested == null || System.currentTimeMillis() - lastRequested >= requestRemotelyTimeout; } + public void forceRequest(int currentAccount, Args args) { + if (lastRequestedRemotely == null) { + return; + } + lastRequestedRemotely.remove(new Pair<>(currentAccount, args)); + } + private boolean isLoading(Pair key) { return loadingCallbacks != null && loadingCallbacks.get(key) != null; } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ChatMessageSharedResources.java b/TMessagesProj/src/main/java/org/telegram/messenger/ChatMessageSharedResources.java new file mode 100644 index 0000000000..72e85a460b --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ChatMessageSharedResources.java @@ -0,0 +1,32 @@ +package org.telegram.messenger; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; + +public class ChatMessageSharedResources { + + public Context context; + public Drawable chat_msgAvatarLiveLocationDrawable; + public Drawable chat_redLocationIcon; + + public ChatMessageSharedResources(Context context) { + this.context = context; + } + + public Drawable getRedLocationIcon() { + if (chat_redLocationIcon == null) { + Resources resources = context.getResources(); + chat_redLocationIcon = resources.getDrawable(R.drawable.map_pin).mutate(); + } + return chat_redLocationIcon; + } + + public Drawable getAvatarLiveLocation() { + if (chat_msgAvatarLiveLocationDrawable == null) { + Resources resources = context.getResources(); + chat_msgAvatarLiveLocationDrawable = resources.getDrawable(R.drawable.livepin).mutate(); + } + return chat_msgAvatarLiveLocationDrawable; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ChatMessagesMetadataController.java b/TMessagesProj/src/main/java/org/telegram/messenger/ChatMessagesMetadataController.java index ecc0615f14..e847d92229 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ChatMessagesMetadataController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ChatMessagesMetadataController.java @@ -1,15 +1,24 @@ package org.telegram.messenger; +import com.google.android.exoplayer2.util.Log; + +import org.checkerframework.checker.units.qual.A; +import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.RequestDelegate; +import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ChatActivity; +import org.telegram.ui.Stories.StoriesStorage; import java.util.ArrayList; +import java.util.Collections; public class ChatMessagesMetadataController { final ChatActivity chatActivity; private ArrayList reactionsToCheck = new ArrayList<>(10); private ArrayList extendedMediaToCheck = new ArrayList<>(10); + private ArrayList storiesToCheck = new ArrayList<>(10); ArrayList reactionsRequests = new ArrayList<>(); ArrayList extendedMediaRequests = new ArrayList<>(); @@ -32,6 +41,7 @@ public void checkMessages(ChatActivity.ChatActivityAdapter chatAdapter, int maxA } reactionsToCheck.clear(); extendedMediaToCheck.clear(); + storiesToCheck.clear(); for (int i = from; i < to; i++) { MessageObject messageObject = messages.get(i); if (chatActivity.getThreadMessage() != messageObject && messageObject.getId() > 0 && messageObject.messageOwner.action == null && (currentTime - messageObject.reactionsLastCheckTime) > 15000L) { @@ -42,9 +52,78 @@ public void checkMessages(ChatActivity.ChatActivityAdapter chatAdapter, int maxA messageObject.extendedMediaLastCheckTime = currentTime; extendedMediaToCheck.add(messageObject); } + if (messageObject.type == MessageObject.TYPE_STORY || messageObject.type == MessageObject.TYPE_STORY_MENTION || messageObject.messageOwner.replyStory != null) { + TLRPC.StoryItem storyItem = messageObject.type == MessageObject.TYPE_STORY || messageObject.type == MessageObject.TYPE_STORY_MENTION ? messageObject.messageOwner.media.storyItem : messageObject.messageOwner.replyStory; + if (storyItem == null || storyItem instanceof TLRPC.TL_storyItemDeleted) { + continue; + } + if (currentTime - storyItem.lastUpdateTime > 1000 * 5 * 60) { + storyItem.lastUpdateTime = currentTime; + storiesToCheck.add(messageObject); + } + } } loadReactionsForMessages(chatActivity.getDialogId(), reactionsToCheck); loadExtendedMediaForMessages(chatActivity.getDialogId(), extendedMediaToCheck); + loadStoriesForMessages(chatActivity.getDialogId(), storiesToCheck); + } + } + + private void loadStoriesForMessages(long dialogId, ArrayList visibleObjects) { + if (visibleObjects.isEmpty()) { + return; + } + for (int i = 0; i < visibleObjects.size(); i++) { + TLRPC.TL_stories_getStoriesByID req = new TLRPC.TL_stories_getStoriesByID(); + MessageObject messageObject = visibleObjects.get(i); + TLRPC.StoryItem storyItem = new TLRPC.TL_storyItem(); + if (messageObject.type == MessageObject.TYPE_STORY || messageObject.type == MessageObject.TYPE_STORY_MENTION) { + storyItem = messageObject.messageOwner.media.storyItem; + storyItem.dialogId = messageObject.messageOwner.media.user_id; + } else if (messageObject.messageOwner.reply_to != null) { + storyItem = messageObject.messageOwner.replyStory; + storyItem.dialogId = messageObject.messageOwner.reply_to.user_id; + } else { + continue; + } + long storyDialogId = storyItem.dialogId; + req.user_id = chatActivity.getMessagesController().getInputUser(storyDialogId); + req.id.add(storyItem.id); + int storyId = storyItem.id; + int reqId = chatActivity.getConnectionsManager().sendRequest(req, (response, error) -> { + TLRPC.StoryItem newStoryItem = null; + if (response != null) { + TLRPC.TL_stories_stories stories = (TLRPC.TL_stories_stories) response; + if (stories.stories.size() > 0) { + newStoryItem = stories.stories.get(0); + } + if (newStoryItem == null) { + newStoryItem = new TLRPC.TL_storyItemDeleted(); + } + newStoryItem.lastUpdateTime = System.currentTimeMillis(); + newStoryItem.id = storyId; + TLRPC.StoryItem finalNewStoryItem = newStoryItem; + AndroidUtilities.runOnUIThread(() -> { + boolean wasExpired = messageObject.isExpiredStory(); + StoriesStorage.applyStory(chatActivity.getCurrentAccount(), storyDialogId, messageObject, finalNewStoryItem); + ArrayList messageObjects = new ArrayList<>(); + messageObject.forceUpdate = true; + messageObjects.add(messageObject); + chatActivity.getMessagesStorage().getStorageQueue().postRunnable(() -> { + chatActivity.getMessagesController().getStoriesController().getStoriesStorage().updateMessagesWithStories(messageObjects); + }); + if (!wasExpired && messageObject.isExpiredStory() && messageObject.type == MessageObject.TYPE_STORY_MENTION) { + chatActivity.updateMessages(messageObjects, true); + } else { + chatActivity.updateMessages(messageObjects, false); + } + }); + } + }); + extendedMediaRequests.add(reqId); + } + if (extendedMediaRequests.size() > 10) { + chatActivity.getConnectionsManager().cancelRequest(extendedMediaRequests.remove(0), false); } } @@ -91,7 +170,7 @@ public void loadExtendedMediaForMessages(long dialogId, ArrayList } }); extendedMediaRequests.add(reqId); - if (extendedMediaRequests.size() > 5) { + if (extendedMediaRequests.size() > 10) { chatActivity.getConnectionsManager().cancelRequest(extendedMediaRequests.remove(0), false); } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ChatThemeController.java b/TMessagesProj/src/main/java/org/telegram/messenger/ChatThemeController.java index ed72a4f17d..c0f445adcd 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ChatThemeController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ChatThemeController.java @@ -13,6 +13,8 @@ import org.telegram.tgnet.SerializedData; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.EmojiThemes; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.ChatBackgroundDrawable; import java.io.File; import java.io.FileOutputStream; @@ -65,7 +67,7 @@ public static void requestAllChatThemes(final ResultCallback> } boolean needReload = System.currentTimeMillis() - lastReloadTimeMs > reloadTimeoutMs; - if (allChatThemes == null || allChatThemes.isEmpty() || needReload) { + if (true || allChatThemes == null || allChatThemes.isEmpty() || needReload) { TLRPC.TL_account_getChatThemes request = new TLRPC.TL_account_getChatThemes(); request.hash = themesHash; ConnectionsManager.getInstance(UserConfig.selectedAccount).sendRequest(request, (response, error) -> chatThemeQueue.postRunnable(() -> { @@ -94,7 +96,11 @@ public static void requestAllChatThemes(final ResultCallback> } editor.apply(); } else if (response instanceof TLRPC.TL_account_themesNotModified) { - chatThemes = getAllChatThemesFromPrefs(); + // if (allChatThemes == null || allChatThemes.isEmpty()) { + chatThemes = getAllChatThemesFromPrefs(); +// } else { +// // return; +// } } else { chatThemes = null; isError = true; @@ -113,7 +119,8 @@ public static void requestAllChatThemes(final ResultCallback> }); } })); - } else { + } + if (allChatThemes != null && !allChatThemes.isEmpty()) { List chatThemes = new ArrayList<>(allChatThemes); if (withDefault && !chatThemes.get(0).showAsDefaultStub) { chatThemes.add(0, EmojiThemes.createChatThemesDefault()); @@ -200,6 +207,19 @@ public ChatThemeController(int num) { super(num); } + public static boolean equals(TLRPC.WallPaper wallPaper, TLRPC.WallPaper oldWallpaper) { + if (wallPaper == null && oldWallpaper == null) { + return true; + } + if (wallPaper != null && oldWallpaper != null) { + if (wallPaper.uploadingImage != null) { + return TextUtils.equals(oldWallpaper.uploadingImage, wallPaper.uploadingImage); + } + return wallPaper.id == oldWallpaper.id && TextUtils.equals(ChatBackgroundDrawable.hash(wallPaper.settings), ChatBackgroundDrawable.hash(oldWallpaper.settings)); + } + return false; + } + public void setDialogTheme(long dialogId, String emoticon, boolean sendRequest) { String oldEmoticon = dialogEmoticonsMap.get(dialogId); if (TextUtils.equals(oldEmoticon, emoticon)) { @@ -212,6 +232,20 @@ public void setDialogTheme(long dialogId, String emoticon, boolean sendRequest) dialogEmoticonsMap.put(dialogId, emoticon); } + if (dialogId >= 0) { + TLRPC.UserFull userFull = getMessagesController().getUserFull(dialogId); + if (userFull != null) { + userFull.theme_emoticon = emoticon; + getMessagesStorage().updateUserInfo(userFull, true); + } + } else { + TLRPC.ChatFull chatFull = getMessagesController().getChatFull(-dialogId); + if (chatFull != null) { + chatFull.theme_emoticon = emoticon; + getMessagesStorage().updateChatInfo(chatFull, true); + } + } + getEmojiSharedPreferences().edit() .putString("chatTheme_" + currentAccount + "_" + dialogId, emoticon) .apply(); @@ -240,6 +274,45 @@ public EmojiThemes getDialogTheme(long dialogId) { return null; } + public void saveChatWallpaper(long dialogId, TLRPC.WallPaper wallPaper) { + if (dialogId < 0) { + return; + } + if (wallPaper != null) { + SerializedData data = new SerializedData(wallPaper.getObjectSize()); + wallPaper.serializeToStream(data); + String wallpaperString = Utilities.bytesToHex(data.toByteArray()); + + getEmojiSharedPreferences().edit() + .putString("chatWallpaper_" + currentAccount + "_" + dialogId, wallpaperString) + .apply(); + } else { + getEmojiSharedPreferences().edit() + .remove("chatWallpaper_" + currentAccount + "_" + dialogId) + .apply(); + } + } + + public TLRPC.WallPaper getDialogWallpaper(long dialogId) { + if (dialogId < 0) { + return null; + } + TLRPC.UserFull userFull = getMessagesController().getUserFull(dialogId); + if (userFull != null) { + return userFull.wallpaper; + } + String wallpaperString = getEmojiSharedPreferences().getString("chatWallpaper_" + currentAccount + "_" + dialogId, null); + if (wallpaperString != null) { + SerializedData serializedData = new SerializedData(Utilities.hexToBytes(wallpaperString)); + try { + return TLRPC.WallPaper.TLdeserialize(serializedData, serializedData.readInt32(true), true); + } catch (Throwable e) { + FileLog.e(e); + } + } + return null; + } + public static void preloadAllWallpaperImages(boolean isDark) { for (EmojiThemes chatTheme : allChatThemes) { TLRPC.TL_theme theme = chatTheme.getTlTheme(isDark ? 1 : 0); @@ -330,4 +403,137 @@ public void clearCache() { lastReloadTimeMs = 0; getSharedPreferences().edit().clear().apply(); } + + public void clearWallpaper(long dialogId, boolean notify) { + TLRPC.TL_messages_setChatWallPaper req = new TLRPC.TL_messages_setChatWallPaper(); + if (dialogId > 0) { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); + req.peer = MessagesController.getInputPeer(user); + TLRPC.UserFull userFull = getMessagesController().getUserFull(dialogId); + if (userFull != null) { + userFull.wallpaper = null; + userFull.flags &= ~16777216; + getMessagesStorage().updateUserInfo(userFull, false); + } + saveChatWallpaper(dialogId, null); + if (notify) { + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.userInfoDidLoad, dialogId, userFull); + } + } else { + TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-dialogId); + req.peer = MessagesController.getInputPeer(chat); + } + + getConnectionsManager().sendRequest(req, (response, error) -> { + + }); + } + + public int setWallpaperToUser(long dialogId, String wallpaperLocalPath, Theme.OverrideWallpaperInfo wallpaperInfo, MessageObject serverWallpaper, Runnable callback) { + TLRPC.TL_messages_setChatWallPaper req = new TLRPC.TL_messages_setChatWallPaper(); + if (dialogId > 0) { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); + req.peer = MessagesController.getInputPeer(user); + } else { + //chat not supported yet + TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-dialogId); + req.peer = MessagesController.getInputPeer(chat); + } + boolean applyOnRequest = true; + if (serverWallpaper != null && serverWallpaper.messageOwner.action instanceof TLRPC.TL_messageActionSetChatWallPaper) { + applyOnRequest = false; + req.flags |= 2; + req.id = serverWallpaper.getId(); + + TLRPC.UserFull userFull = MessagesController.getInstance(currentAccount).getUserFull(dialogId); + if (userFull != null) { + TLRPC.TL_messageActionSetChatWallPaper action = (TLRPC.TL_messageActionSetChatWallPaper) serverWallpaper.messageOwner.action; + TLRPC.WallPaper wallPaper = new TLRPC.TL_wallPaper(); + wallPaper.id = action.wallpaper.id; + wallPaper.document = action.wallpaper.document; + wallPaper.settings = new TLRPC.TL_wallPaperSettings(); + wallPaper.settings.intensity = (int) (wallpaperInfo.intensity * 100); + wallPaper.settings.motion = wallpaperInfo.isMotion; + wallPaper.settings.blur = wallpaperInfo.isBlurred; + wallPaper.settings.background_color = wallpaperInfo.color; + wallPaper.settings.second_background_color = wallpaperInfo.gradientColor1; + wallPaper.settings.third_background_color = wallpaperInfo.gradientColor2; + wallPaper.settings.fourth_background_color = wallpaperInfo.gradientColor3; + wallPaper.settings.rotation = wallpaperInfo.rotation; + wallPaper.uploadingImage = wallpaperLocalPath; + if (userFull.wallpaper != null && userFull.wallpaper.uploadingImage != null && userFull.wallpaper.uploadingImage.equals(wallPaper.uploadingImage)) { + wallPaper.stripedThumb = userFull.wallpaper.stripedThumb; + } + + wallPaper.settings.flags |= 1; + wallPaper.settings.flags |= 8; + wallPaper.settings.flags |= 16; + wallPaper.settings.flags |= 32; + wallPaper.settings.flags |= 64; + + userFull.wallpaper = new TLRPC.TL_wallPaper(); + userFull.wallpaper.pattern = action.wallpaper.pattern; + userFull.wallpaper.id = action.wallpaper.id; + userFull.wallpaper.document = action.wallpaper.document; + userFull.wallpaper.flags = action.wallpaper.flags; + userFull.wallpaper.creator = action.wallpaper.creator; + userFull.wallpaper.dark = action.wallpaper.dark; + userFull.wallpaper.isDefault = action.wallpaper.isDefault; + userFull.wallpaper.slug = action.wallpaper.slug; + userFull.wallpaper.access_hash = action.wallpaper.access_hash; + userFull.wallpaper.stripedThumb = action.wallpaper.stripedThumb; + userFull.wallpaper.settings = wallPaper.settings; + userFull.wallpaper.flags |= 4; + userFull.flags |= 16777216; + + getMessagesStorage().updateUserInfo(userFull, false); + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.userInfoDidLoad, dialogId, userFull); + if (callback != null) { + callback.run(); + } + } + } else { + req.flags |= 1; + req.wallpaper = MessagesController.getInputWallpaper(wallpaperInfo); + } + req.flags |= 4; + req.settings = MessagesController.getWallpaperSetting(wallpaperInfo); + + + boolean finalApplyOnRequest = applyOnRequest; + return ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + if (response instanceof TLRPC.Updates) { + TLRPC.Updates res = (TLRPC.Updates) response; + TLRPC.UserFull userFull = MessagesController.getInstance(currentAccount).getUserFull(dialogId); + if (userFull != null) { + for (int i = 0; i < res.updates.size(); i++) { + if (res.updates.get(i) instanceof TLRPC.TL_updateNewMessage) { + TLRPC.Message message = ((TLRPC.TL_updateNewMessage) res.updates.get(i)).message; + if (message.action instanceof TLRPC.TL_messageActionSetChatWallPaper) { + if (finalApplyOnRequest) { + TLRPC.TL_messageActionSetChatWallPaper actionSetChatWallPaper = (TLRPC.TL_messageActionSetChatWallPaper) message.action; + actionSetChatWallPaper.wallpaper.uploadingImage = wallpaperLocalPath; + if (userFull.wallpaper != null && userFull.wallpaper.uploadingImage != null && userFull.wallpaper.uploadingImage.equals(actionSetChatWallPaper.wallpaper.uploadingImage)) { + actionSetChatWallPaper.wallpaper.stripedThumb = userFull.wallpaper.stripedThumb; + } + userFull.wallpaper = actionSetChatWallPaper.wallpaper; + userFull.flags |= 16777216; + + saveChatWallpaper(dialogId, userFull.wallpaper); + getMessagesStorage().updateUserInfo(userFull, false); + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.userInfoDidLoad, dialogId, userFull); + } + break; + } + } + } + } + MessagesController.getInstance(currentAccount).processUpdateArray(res.updates, res.users, res.chats, false, res.date); + if (callback != null) { + callback.run(); + } + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.wallpaperSettedToUser); + } + })); + } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/CompoundEmoji.java b/TMessagesProj/src/main/java/org/telegram/messenger/CompoundEmoji.java new file mode 100644 index 0000000000..930bc16af4 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/CompoundEmoji.java @@ -0,0 +1,314 @@ +package org.telegram.messenger; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.Rect; +import android.util.Pair; +import android.util.SparseArray; +import android.view.View; + +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.CubicBezierInterpolator; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.Objects; + +public class CompoundEmoji { + + public static List skinTones = Arrays.asList("🏻", "🏼", "🏽", "🏾", "🏿"); + + public static int getSkinTone(String string) { + if (string == null) { + return -1; + } + return skinTones.indexOf(string.substring(string.length() - 2)); + } + + public static CompoundEmojiDrawable getCompoundEmojiDrawable(String string) { + return getCompoundEmojiDrawable(string, null, null); + } + + public static CompoundEmojiDrawable getCompoundEmojiDrawable(String string, Integer overrideSkinTone1, Integer overrideSkinTone2) { + if (string == null) { + return null; + } + + Pair handshakeTones = isHandshake(string); + if (handshakeTones != null) { + return new CompoundEmojiDrawable( + new DrawableInfo(0, overrideSkinTone1 != null ? overrideSkinTone1 : handshakeTones.first, 0), + new DrawableInfo(0, overrideSkinTone2 != null ? overrideSkinTone2 : handshakeTones.second, 1) + ); + } + + return null; + } + + public static Pair isHandshake(String code) { + + int skinTone1 = -1, skinTone2 = -1; + + // simple handshake 🤝 + if ( + code.startsWith("\uD83E\uDD1D") && // 🤝 + (code.length() == 2 || code.length() == 4 && (skinTone1 = getSkinTone(code)) >= 0) + ) { + return new Pair(skinTone1, skinTone1); + } + + // compound handshake 🫱 + ZWJ + 🫲 + String[] parts = code.split("\u200D"); // ZWJ + if ( + parts.length == 2 && + parts[0].startsWith("\uD83E\uDEF1") && // 🫱 + parts[1].startsWith("\uD83E\uDEF2") && // 🫲 + (parts[0].length() == 2 || parts[0].length() == 4 && (skinTone1 = getSkinTone(parts[0])) >= 0) && + (parts[1].length() == 2 || parts[1].length() == 4 && (skinTone2 = getSkinTone(parts[1])) >= 0) + ) { + return new Pair(skinTone1, skinTone2); + } + + return null; + } + + public static String applyColor(String code, String color) { + if (isHandshake(code) == null) { + return code; + } + + if (color == null) { + return "\uD83E\uDD1D"; + } else if (color.contains("\u200D")) { + String[] parts = color.split("\u200D"); + return "\uD83E\uDEF1" + (parts.length >= 1 ? parts[0] : "") + "\u200D" + "\uD83E\uDEF2" + (parts.length >= 2 ? parts[1] : ""); + } else { + return "\uD83E\uDD1D" + color; + } + } + + public static boolean isCompound(String string) { + return getCompoundEmojiDrawable(string) != null; + } + + private static class DrawableInfo { + + private static final SparseArray bitmaps = new SparseArray<>(); + private static final ArrayList loading = new ArrayList<>(); + + int emoji; // HANDSHAKE = 0 + int skin; // YELLOW = -1, TONES = 0...4 + int place; // LEFT = 0, RIGHT = 1 + int hash; + + boolean placeholder; + + public DrawableInfo(int emoji, int skin, int place) { + if (skin == -2) { + skin = -1; + placeholder = true; + } + this.hash = Objects.hash(this.emoji = emoji, this.skin = skin, this.place = place); + } + + public DrawableInfo updateSkin(int newSkin) { + if (skin == newSkin) { + return this; + } + return new DrawableInfo(emoji, newSkin, place); + } + + @Override + public int hashCode() { + return hash; + } + + public boolean isLoaded() { + return bitmaps.indexOfKey(hash) >= 0; + } + + public void load() { + if (isLoaded() || loading.contains(hash)) { + return; + } + loading.add(hash); + Utilities.globalQueue.postRunnable(() -> { + final Bitmap bitmap = Emoji.loadBitmap("emoji/compound/" + emoji + "_" + skin + "_" + place + ".png"); + if (bitmap != null) { + bitmaps.put(hash, bitmap); + AndroidUtilities.cancelRunOnUIThread(Emoji.invalidateUiRunnable); + AndroidUtilities.runOnUIThread(Emoji.invalidateUiRunnable); + } + loading.remove((Integer) hash); + }); + } + + public Bitmap getBitmap() { + return bitmaps.get(hash); + } + } + + private static Paint placeholderPaint = new Paint(Paint.FILTER_BITMAP_FLAG); + public static void setPlaceholderColor(int color) { + placeholderPaint.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN)); + } + + public static class CompoundEmojiDrawable extends Emoji.EmojiDrawable { + private DrawableInfo left, right; + private static Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG); + private static Rect rect = new Rect(); + + private View parent; + private AnimatedFloat leftUpdateT, rightUpdateT; + private DrawableInfo newLeft, newRight; + + public CompoundEmojiDrawable(DrawableInfo left, DrawableInfo right) { + this.left = left; + this.right = right; + } + + public Rect getDrawRect() { + Rect original = getBounds(); + int cX = original.centerX(), cY = original.centerY(); + rect.left = cX - (fullSize ? Emoji.bigImgSize : Emoji.drawImgSize) / 2; + rect.right = cX + (fullSize ? Emoji.bigImgSize : Emoji.drawImgSize) / 2; + rect.top = cY - (fullSize ? Emoji.bigImgSize : Emoji.drawImgSize) / 2; + rect.bottom = cY + (fullSize ? Emoji.bigImgSize : Emoji.drawImgSize) / 2; + return rect; + } + + @Override + public void draw(Canvas canvas) { + if (!isLoaded()) { + preload(); + Emoji.placeholderPaint.setColor(placeholderColor); + Rect bounds = getBounds(); + canvas.drawCircle(bounds.centerX(), bounds.centerY(), bounds.width() * .4f, Emoji.placeholderPaint); + return; + } + + Rect b; + if (fullSize) { + b = getDrawRect(); + } else { + b = getBounds(); + } + + if (!canvas.quickReject(b.left, b.top, b.right, b.bottom, Canvas.EdgeType.AA)) { + if (newLeft != null) { + if (leftUpdateT == null) { + leftUpdateT = new AnimatedFloat(0, this::invalidate, 0, 320, CubicBezierInterpolator.EASE_OUT); + } + float t = leftUpdateT.set(1); + + drawDrawableInfo(canvas, newLeft, b, Math.min(1, 1.5f * t)); + drawDrawableInfo(canvas, left, b, 1f - t); + + if (t >= 1) { + left = newLeft; + newLeft = null; + } + } else { + drawDrawableInfo(canvas, left, b, 1); + } + + if (newRight != null) { + if (rightUpdateT == null) { + rightUpdateT = new AnimatedFloat(0, this::invalidate, 0, 320, CubicBezierInterpolator.EASE_OUT); + } + float t = rightUpdateT.set(1); + + drawDrawableInfo(canvas, newRight, b, Math.min(1, 1.5f * t)); + drawDrawableInfo(canvas, right, b, 1f - t); + + if (t >= 1) { + right = newRight; + newRight = null; + } + } else { + drawDrawableInfo(canvas, right, b, 1); + } + } + } + + private void invalidate() { + if (parent != null) { + parent.invalidate(); + } + invalidateSelf(); + } + + public void update(int skinTone1, int skinTone2) { + if (left.skin != skinTone1) { + if (newLeft != null) { + left = newLeft; + } + newLeft = left.updateSkin(skinTone1); + if (leftUpdateT != null) { + leftUpdateT.set(0, true); + } + } + if (right.skin != skinTone2) { + if (newRight != null) { + right = newRight; + } + newRight = right.updateSkin(skinTone2); + if (rightUpdateT != null) { + rightUpdateT.set(0, true); + } + } + invalidate(); + } + + private void drawDrawableInfo(Canvas canvas, DrawableInfo info, Rect bounds, float alpha) { + final Bitmap leftBitmap = info.getBitmap(); + if (leftBitmap != null) { + Paint currentPaint = info.placeholder ? placeholderPaint : paint; + int wasAlpha = 0xFF; + if (alpha < 1) { + wasAlpha = currentPaint.getAlpha(); + currentPaint.setAlpha((int) (wasAlpha * alpha)); + } + canvas.drawBitmap(leftBitmap, null, bounds, currentPaint); + if (alpha < 1) { + currentPaint.setAlpha(wasAlpha); + } + } + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSPARENT; + } + + @Override + public void setAlpha(int alpha) { + paint.setAlpha(alpha); + } + + @Override + public void setColorFilter(ColorFilter cf) {} + + @Override + public boolean isLoaded() { + return left.isLoaded() && right.isLoaded(); + } + + @Override + public void preload() { + if (!isLoaded()) { + left.load(); + right.load(); + } + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java b/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java index e04727757c..cab15a7cfa 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java @@ -8,6 +8,7 @@ package org.telegram.messenger; +import android.Manifest; import android.accounts.Account; import android.accounts.AccountManager; import android.content.ContentProviderOperation; @@ -32,6 +33,7 @@ import org.telegram.PhoneFormat.PhoneFormat; import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; import org.telegram.ui.Components.Bulletin; @@ -78,6 +80,7 @@ public class ContactsController extends BaseController { private ArrayList callPrivacyRules; private ArrayList p2pPrivacyRules; private ArrayList profilePhotoPrivacyRules; + private ArrayList bioPrivacyRules; private ArrayList forwardsPrivacyRules; private ArrayList phonePrivacyRules; private ArrayList addedByPhonePrivacyRules; @@ -93,8 +96,9 @@ public class ContactsController extends BaseController { public final static int PRIVACY_RULES_TYPE_PHONE = 6; public final static int PRIVACY_RULES_TYPE_ADDED_BY_PHONE = 7; public final static int PRIVACY_RULES_TYPE_VOICE_MESSAGES = 8; + public final static int PRIVACY_RULES_TYPE_BIO = 9; - public final static int PRIVACY_RULES_TYPE_COUNT = 9; + public final static int PRIVACY_RULES_TYPE_COUNT = 10; private class MyContentObserver extends ContentObserver { @@ -327,6 +331,7 @@ public void cleanup() { callPrivacyRules = null; p2pPrivacyRules = null; profilePhotoPrivacyRules = null; + bioPrivacyRules = null; forwardsPrivacyRules = null; phonePrivacyRules = null; @@ -1565,20 +1570,23 @@ public void processLoadedContacts(final ArrayList contactsArr, getUserConfig().saveConfig(false); } + boolean reloadContacts = false; for (int a = 0; a < contactsArr.size(); a++) { TLRPC.TL_contact contact = contactsArr.get(a); - if (usersDict.get(contact.user_id) == null && contact.user_id != getUserConfig().getClientUserId()) { - loadContacts(false, 0); - if (BuildVars.LOGS_ENABLED) { - FileLog.d("contacts are broken, load from server"); - } - AndroidUtilities.runOnUIThread(() -> { - doneLoadingContacts = true; - getNotificationCenter().postNotificationName(NotificationCenter.contactsDidLoad); - }); - return; + if (MessagesController.getInstance(currentAccount).getUser(contact.user_id) == null && contact.user_id != getUserConfig().getClientUserId()) { + contactsArr.remove(a); + a--; + reloadContacts = true; } } +// loadContacts(false, 0); +// if (BuildVars.LOGS_ENABLED) { +// FileLog.d("contacts are broken, load from server"); +// } +// AndroidUtilities.runOnUIThread(() -> { +// doneLoadingContacts = true; +// getNotificationCenter().postNotificationName(NotificationCenter.contactsDidLoad); +// }); if (from != 1) { getMessagesStorage().putUsersAndChats(usersArr, null, true, true); @@ -1675,6 +1683,7 @@ public void processLoadedContacts(final ArrayList contactsArr, return collator.compare(s, s2); }); + boolean finalReloadContacts = reloadContacts; AndroidUtilities.runOnUIThread(() -> { contacts = contactsArr; contactsDict = contactsDictionary; @@ -1698,6 +1707,9 @@ public void processLoadedContacts(final ArrayList contactsArr, } else { reloadContactsStatusesMaybe(); } + if (finalReloadContacts) { + loadContacts(false, 0); + } }); if (!delayedContactsUpdate.isEmpty() && contactsLoaded && contactsBookLoaded) { @@ -1958,12 +1970,19 @@ private boolean hasContactsPermission() { return true; } + private boolean hasContactsWritePermission() { + if (Build.VERSION.SDK_INT >= 23) { + return ApplicationLoader.applicationContext.checkSelfPermission(Manifest.permission.WRITE_CONTACTS) == PackageManager.PERMISSION_GRANTED; + } + return true; + } + private void performWriteContactsToPhoneBookInternal(ArrayList contactsArray) { Cursor cursor = null; long time = System.currentTimeMillis(); try { Account account = systemAccount; - if (!hasContactsPermission() || account == null) { + if (!hasContactsPermission() || account == null || !hasContactsWritePermission()) { return; } final SharedPreferences settings = MessagesController.getMainSettings(currentAccount); @@ -2379,6 +2398,7 @@ public void deleteContact(final ArrayList users, boolean showBulleti final ArrayList uids = new ArrayList<>(); for (int a = 0, N = users.size(); a < N; a++) { TLRPC.User user = users.get(a); + getMessagesController().getStoriesController().removeContact(user.id); TLRPC.InputUser inputUser = getMessagesController().getInputUser(user); if (inputUser == null) { continue; @@ -2481,6 +2501,22 @@ private void reloadContactsStatuses() { }); } + public void loadGlobalPrivacySetting() { + if (loadingGlobalSettings == 0) { + loadingGlobalSettings = 1; + TLRPC.TL_account_getGlobalPrivacySettings req = new TLRPC.TL_account_getGlobalPrivacySettings(); + getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + if (error == null) { + globalPrivacySettings = (TLRPC.TL_globalPrivacySettings) response; + loadingGlobalSettings = 2; + } else { + loadingGlobalSettings = 0; + } + getNotificationCenter().postNotificationName(NotificationCenter.privacyRulesUpdated); + })); + } + } + public void loadPrivacySettings() { if (loadingDeleteInfo == 0) { loadingDeleteInfo = 1; @@ -2496,19 +2532,7 @@ public void loadPrivacySettings() { getNotificationCenter().postNotificationName(NotificationCenter.privacyRulesUpdated); })); } - if (loadingGlobalSettings == 0) { - loadingGlobalSettings = 1; - TLRPC.TL_account_getGlobalPrivacySettings req = new TLRPC.TL_account_getGlobalPrivacySettings(); - getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { - if (error == null) { - globalPrivacySettings = (TLRPC.TL_globalPrivacySettings) response; - loadingGlobalSettings = 2; - } else { - loadingGlobalSettings = 0; - } - getNotificationCenter().postNotificationName(NotificationCenter.privacyRulesUpdated); - })); - } + loadGlobalPrivacySetting(); for (int a = 0; a < loadingPrivacyInfo.length; a++) { if (loadingPrivacyInfo[a] != 0) { continue; @@ -2534,6 +2558,9 @@ public void loadPrivacySettings() { case PRIVACY_RULES_TYPE_PHOTO: req.key = new TLRPC.TL_inputPrivacyKeyProfilePhoto(); break; + case PRIVACY_RULES_TYPE_BIO: + req.key = new TLRPC.TL_inputPrivacyKeyAbout(); + break; case PRIVACY_RULES_TYPE_FORWARDS: req.key = new TLRPC.TL_inputPrivacyKeyForwards(); break; @@ -2571,6 +2598,9 @@ public void loadPrivacySettings() { case PRIVACY_RULES_TYPE_PHOTO: profilePhotoPrivacyRules = rules.rules; break; + case PRIVACY_RULES_TYPE_BIO: + bioPrivacyRules = rules.rules; + break; case PRIVACY_RULES_TYPE_FORWARDS: forwardsPrivacyRules = rules.rules; break; @@ -2631,6 +2661,8 @@ public ArrayList getPrivacyRules(int type) { return p2pPrivacyRules; case PRIVACY_RULES_TYPE_PHOTO: return profilePhotoPrivacyRules; + case PRIVACY_RULES_TYPE_BIO: + return bioPrivacyRules; case PRIVACY_RULES_TYPE_FORWARDS: return forwardsPrivacyRules; case PRIVACY_RULES_TYPE_PHONE: @@ -2660,6 +2692,9 @@ public void setPrivacyRules(ArrayList rules, int type) { case PRIVACY_RULES_TYPE_PHOTO: profilePhotoPrivacyRules = rules; break; + case PRIVACY_RULES_TYPE_BIO: + bioPrivacyRules = rules; + break; case PRIVACY_RULES_TYPE_FORWARDS: forwardsPrivacyRules = rules; break; @@ -2806,6 +2841,17 @@ public void deleteConnectionServiceContact() { } } + public static String formatName(TLObject object) { + if (object instanceof TLRPC.User) { + return formatName((TLRPC.User) object); + } else if (object instanceof TLRPC.Chat) { + TLRPC.Chat chat = (TLRPC.Chat) object; + return chat.title; + } else { + return "DELETED"; + } + } + public static String formatName(TLRPC.User user) { return formatName(user.first_name, user.last_name, 0); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/DatabaseMigrationHelper.java b/TMessagesProj/src/main/java/org/telegram/messenger/DatabaseMigrationHelper.java index 88f48947bd..5058d08f31 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/DatabaseMigrationHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/DatabaseMigrationHelper.java @@ -14,6 +14,7 @@ import java.util.Locale; public class DatabaseMigrationHelper { + public static int migrate(MessagesStorage messagesStorage, int version) throws Exception { SQLiteDatabase database = messagesStorage.getDatabase(); if (version < 4) { @@ -1251,13 +1252,83 @@ public static int migrate(MessagesStorage messagesStorage, int version) throws E database.executeFast("CREATE INDEX IF NOT EXISTS idx_to_reply_messages_v2 ON messages_v2(reply_to_message_id, mid);").stepThis().dispose(); database.executeFast("CREATE INDEX IF NOT EXISTS idx_to_reply_scheduled_messages_v2 ON scheduled_messages_v2(reply_to_message_id, mid);").stepThis().dispose(); database.executeFast("CREATE INDEX IF NOT EXISTS idx_to_reply_messages_topics ON messages_topics(reply_to_message_id, mid);").stepThis().dispose(); - database.executeFast("PRAGMA user_version = 116").stepThis().dispose(); - version = 116; + database.executeFast("PRAGMA user_version = 117").stepThis().dispose(); + version = 117; + } + + if (version == 116 || version == 117 || version == 118) { + database.executeFast("DROP TABLE IF EXISTS stories").stepThis().dispose(); + database.executeFast("DROP TABLE IF EXISTS stories_counter").stepThis().dispose(); + + database.executeFast("CREATE TABLE stories (dialog_id INTEGER, story_id INTEGER, data BLOB, local_path TEXT, local_thumb_path TEXT, PRIMARY KEY (dialog_id, story_id));").stepThis().dispose(); + database.executeFast("CREATE TABLE stories_counter (dialog_id INTEGER PRIMARY KEY, count INTEGER, max_read INTEGER);").stepThis().dispose(); + database.executeFast("PRAGMA user_version = 119").stepThis().dispose(); + messagesStorage.getMessagesController().getStoriesController().cleanup(); + version = 119; + } + + if (version == 119) { + database.executeFast("ALTER TABLE messages_v2 ADD COLUMN reply_to_story_id INTEGER default 0").stepThis().dispose(); + database.executeFast("ALTER TABLE messages_topics ADD COLUMN reply_to_story_id INTEGER default 0").stepThis().dispose(); + + database.executeFast("PRAGMA user_version = 120").stepThis().dispose(); + version = 120; + } + + if (version == 120) { + database.executeFast("CREATE TABLE profile_stories (dialog_id INTEGER, story_id INTEGER, data BLOB, PRIMARY KEY(dialog_id, story_id));").stepThis().dispose(); + database.executeFast("CREATE TABLE archived_stories (story_id INTEGER PRIMARY KEY, data BLOB);").stepThis().dispose(); + + database.executeFast("PRAGMA user_version = 121").stepThis().dispose(); + version = 121; + } + + if (version == 121) { + database.executeFast("CREATE TABLE story_drafts (id INTEGER PRIMARY KEY, date INTEGER, data BLOB);").stepThis().dispose(); + + database.executeFast("PRAGMA user_version = 122").stepThis().dispose(); + version = 122; + } + + if (version == 122) { + database.executeFast("ALTER TABLE chat_settings_v2 ADD COLUMN participants_count INTEGER default 0").stepThis().dispose(); + + database.executeFast("PRAGMA user_version = 123").stepThis().dispose(); + version = 123; + } + + if (version == 123) { + database.executeFast("CREATE TABLE story_pushes (uid INTEGER PRIMARY KEY, minId INTEGER, maxId INTEGER, date INTEGER, localName TEXT);").stepThis().dispose(); + + database.executeFast("PRAGMA user_version = 124").stepThis().dispose(); + version = 124; } + + if (version == 124) { + database.executeFast("DROP TABLE IF EXISTS story_pushes;").stepThis().dispose(); + database.executeFast("CREATE TABLE story_pushes (uid INTEGER, sid INTEGER, date INTEGER, localName TEXT, PRIMARY KEY(uid, sid));").stepThis().dispose(); + + database.executeFast("PRAGMA user_version = 125").stepThis().dispose(); + version = 125; + } + + if (version == 125) { + database.executeFast("ALTER TABLE story_pushes ADD COLUMN flags INTEGER default 0").stepThis().dispose(); + + database.executeFast("PRAGMA user_version = 126").stepThis().dispose(); + version = 126; + } + + if (version == 126) { + database.executeFast("ALTER TABLE story_pushes ADD COLUMN expire_date INTEGER default 0").stepThis().dispose(); + + database.executeFast("PRAGMA user_version = 127").stepThis().dispose(); + version = 127; + } + return version; } - public static boolean recoverDatabase(File oldDatabaseFile, File oldDatabaseWall, File oldDatabaseShm, int currentAccount) { File filesDir = ApplicationLoader.getFilesDirFixed(); filesDir = new File(filesDir, "recover_database_" + currentAccount + "/"); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueue.java b/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueue.java index 07bc903660..f118c0e924 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueue.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueue.java @@ -11,17 +11,21 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.os.Process; import android.os.SystemClock; import java.util.concurrent.CountDownLatch; public class DispatchQueue extends Thread { + private static final int THREAD_PRIORITY_DEFAULT = -1000; + private volatile Handler handler = null; private CountDownLatch syncLatch = new CountDownLatch(1); private long lastTaskTime; private static int indexPointer = 0; public final int index = indexPointer++; + private int threadPriority = THREAD_PRIORITY_DEFAULT; public DispatchQueue(final String threadName) { this(threadName, true); @@ -34,6 +38,14 @@ public DispatchQueue(final String threadName, boolean start) { } } + public DispatchQueue(final String threadName, boolean start, int priority) { + this.threadPriority = priority; + setName(threadName); + if (start) { + start(); + } + } + public void sendMessage(Message msg, int delay) { try { syncLatch.await(); @@ -69,7 +81,7 @@ public void cancelRunnables(Runnable[] runnables) { public boolean postRunnable(Runnable runnable) { lastTaskTime = SystemClock.elapsedRealtime(); - return postRunnable(runnable, 0); + return postRunnable(runnable, 0); } public boolean postRunnable(Runnable runnable, long delay) { @@ -114,6 +126,9 @@ public void run() { return true; }); syncLatch.countDown(); + if (threadPriority != THREAD_PRIORITY_DEFAULT) { + Process.setThreadPriority(threadPriority); + } Looper.loop(); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueuePool.java b/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueuePool.java index 58479eecdb..2a6d1ecc6e 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueuePool.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueuePool.java @@ -5,6 +5,8 @@ import androidx.annotation.UiThread; +import org.telegram.ui.Components.Reactions.HwEmojis; + import java.util.LinkedList; public class DispatchQueuePool { @@ -68,6 +70,11 @@ public void execute(Runnable runnable) { busyQueues.add(queue); int count = busyQueuesMap.get(queue.index, 0); busyQueuesMap.put(queue.index, count + 1); + if (HwEmojis.isHwEnabled()) { + queue.setPriority(Thread.MIN_PRIORITY); + } else if (queue.getPriority() != Thread.MAX_PRIORITY) { + queue.setPriority(Thread.MAX_PRIORITY); + } queue.postRunnable(() -> { runnable.run(); AndroidUtilities.runOnUIThread(() -> { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueuePoolBackground.java b/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueuePoolBackground.java index 185143c822..75a272e95e 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueuePoolBackground.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueuePoolBackground.java @@ -5,6 +5,8 @@ import androidx.annotation.UiThread; +import org.telegram.ui.Components.Reactions.HwEmojis; + import java.util.ArrayList; public class DispatchQueuePoolBackground { @@ -77,6 +79,11 @@ private void execute(ArrayList runnables) { busyQueues.add(queue); int count = busyQueuesMap.get(queue.index, 0); busyQueuesMap.put(queue.index, count + 1); + if(HwEmojis.isHwEnabled()) { + queue.setPriority(Thread.MIN_PRIORITY); + } else if (queue.getPriority() != Thread.MAX_PRIORITY) { + queue.setPriority(Thread.MAX_PRIORITY); + } queue.postRunnable(() -> { runnable.run(); Utilities.globalQueue.postRunnable(() -> { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/DocumentObject.java b/TMessagesProj/src/main/java/org/telegram/messenger/DocumentObject.java index e93c240cde..10ce541445 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/DocumentObject.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/DocumentObject.java @@ -45,7 +45,7 @@ public ThemeDocument(TLRPC.ThemeSettings settings) { } } - public static SvgHelper.SvgDrawable getSvgThumb(ArrayList sizes, String colorKey, float alpha) { + public static SvgHelper.SvgDrawable getSvgThumb(ArrayList sizes, int colorKey, float alpha) { int w = 0; int h = 0; TLRPC.TL_photoPathSize photoPathSize = null; @@ -68,11 +68,11 @@ public static SvgHelper.SvgDrawable getSvgThumb(ArrayList sizes return null; } - public static SvgHelper.SvgDrawable getCircleThumb(float radius, String colorKey, float alpha) { + public static SvgHelper.SvgDrawable getCircleThumb(float radius, int colorKey, float alpha) { return getCircleThumb(radius, colorKey, null, alpha); } - public static SvgHelper.SvgDrawable getCircleThumb(float radius, String colorKey, Theme.ResourcesProvider resourcesProvider, float alpha) { + public static SvgHelper.SvgDrawable getCircleThumb(float radius, int colorKey, Theme.ResourcesProvider resourcesProvider, float alpha) { try { SvgHelper.SvgDrawable drawable = new SvgHelper.SvgDrawable(); SvgHelper.Circle circle = new SvgHelper.Circle(256, 256, radius * 512); @@ -88,11 +88,11 @@ public static SvgHelper.SvgDrawable getCircleThumb(float radius, String colorKey } } - public static SvgHelper.SvgDrawable getSvgThumb(TLRPC.Document document, String colorKey, float alpha) { - return getSvgThumb(document, colorKey, alpha, 1.0f); + public static SvgHelper.SvgDrawable getSvgThumb(TLRPC.Document document, int colorKey, float alpha) { + return getSvgThumb(document, colorKey, alpha, 1.0f, null); } - public static SvgHelper.SvgDrawable getSvgRectThumb(String colorKey, float alpha) { + public static SvgHelper.SvgDrawable getSvgRectThumb(int colorKey, float alpha) { Path path = new Path(); path.addRect(0, 0, 512, 512, Path.Direction.CW); path.close(); @@ -105,7 +105,7 @@ public static SvgHelper.SvgDrawable getSvgRectThumb(String colorKey, float alpha return drawable; } - public static SvgHelper.SvgDrawable getSvgThumb(TLRPC.Document document, String colorKey, float alpha, float zoom) { + public static SvgHelper.SvgDrawable getSvgThumb(TLRPC.Document document, int colorKey, float alpha, float zoom, Theme.ResourcesProvider resourcesProvider) { if (document == null) { return null; } @@ -128,7 +128,7 @@ public static SvgHelper.SvgDrawable getSvgThumb(TLRPC.Document document, String if (w != 0 && h != 0) { pathThumb = SvgHelper.getDrawableByPath(((TLRPC.TL_photoPathSize) size).svgPath, (int) (w * zoom), (int) (h * zoom)); if (pathThumb != null) { - pathThumb.setupGradient(colorKey, alpha, false); + pathThumb.setupGradient(colorKey, resourcesProvider, alpha, false); } } break; @@ -137,7 +137,7 @@ public static SvgHelper.SvgDrawable getSvgThumb(TLRPC.Document document, String return pathThumb; } - public static SvgHelper.SvgDrawable getSvgThumb(int resourceId, String colorKey, float alpha) { + public static SvgHelper.SvgDrawable getSvgThumb(int resourceId, int colorKey, float alpha) { SvgHelper.SvgDrawable pathThumb = SvgHelper.getDrawable(resourceId, 0xffff0000); if (pathThumb != null) { pathThumb.setupGradient(colorKey, alpha, false); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/DownloadController.java b/TMessagesProj/src/main/java/org/telegram/messenger/DownloadController.java index 42f5df3514..c261e9c581 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/DownloadController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/DownloadController.java @@ -85,15 +85,16 @@ public interface FileDownloadProgressListener { public final SparseArray unviewedDownloads = new SparseArray<>(); public static class Preset { - public int[] mask = new int[4]; + public int[] mask = new int[5]; public long[] sizes = new long[4]; public boolean preloadVideo; public boolean preloadMusic; + public boolean preloadStories; public boolean lessCallData; public boolean enabled; public int maxVideoBitrate; - public Preset(int[] m, long p, long v, long f, boolean pv, boolean pm, boolean e, boolean l, int bitrate) { + public Preset(int[] m, long p, long v, long f, boolean pv, boolean pm, boolean e, boolean l, int bitrate, boolean preloadStories) { System.arraycopy(m, 0, mask, 0, mask.length); sizes[PRESET_SIZE_NUM_PHOTO] = p; sizes[PRESET_SIZE_NUM_VIDEO] = v; @@ -104,6 +105,7 @@ public Preset(int[] m, long p, long v, long f, boolean pv, boolean pm, boolean e lessCallData = l; maxVideoBitrate = bitrate; enabled = e; + this.preloadStories = preloadStories; } public Preset(String str, String deafultValue) { @@ -136,6 +138,15 @@ public Preset(String str, String deafultValue) { } maxVideoBitrate = Utilities.parseInt(defaultArgs[12]); } + + if (args.length >= 14) { + preloadStories = Utilities.parseInt(args[13]) == 1; + } else { + if (defaultArgs == null) { + defaultArgs = deafultValue.split("_"); + } + preloadStories = Utilities.parseInt(defaultArgs[13]) == 1; + } } } @@ -146,6 +157,7 @@ public void set(Preset preset) { preloadMusic = preset.preloadMusic; lessCallData = preset.lessCallData; maxVideoBitrate = preset.maxVideoBitrate; + preloadStories = preset.preloadStories; } public void set(TLRPC.TL_autoDownloadSettings settings) { @@ -173,6 +185,9 @@ public void set(TLRPC.TL_autoDownloadSettings settings) { mask[a] &=~ AUTODOWNLOAD_TYPE_DOCUMENT; } } + //TODO stories + // fill flag from server + preloadStories = true; } @Override @@ -186,7 +201,8 @@ public String toString() { "_" + (preloadMusic ? 1 : 0) + "_" + (enabled ? 1 : 0) + "_" + (lessCallData ? 1 : 0) + - "_" + maxVideoBitrate; + "_" + maxVideoBitrate + + "_" + (preloadStories ? 1 : 0); } public boolean equals(Preset obj) { @@ -200,7 +216,8 @@ public boolean equals(Preset obj) { sizes[3] == obj.sizes[3] && preloadVideo == obj.preloadVideo && preloadMusic == obj.preloadMusic && - maxVideoBitrate == obj.maxVideoBitrate; + maxVideoBitrate == obj.maxVideoBitrate && + preloadStories == obj.preloadStories; } public boolean isEnabled() { @@ -241,10 +258,11 @@ public static DownloadController getInstance(int num) { public DownloadController(int instance) { super(instance); SharedPreferences preferences = MessagesController.getMainSettings(currentAccount); - String defaultLow = "1_1_1_1_1048576_512000_512000_524288_0_0_1_1_50"; - String defaultMedium = "13_13_13_13_1048576_10485760_1048576_524288_1_1_1_0_100"; - String defaultHigh = "13_13_13_13_1048576_15728640_3145728_524288_1_1_1_0_100"; + String defaultLow = "1_1_1_1_1048576_512000_512000_524288_0_0_1_1_50_0"; + String defaultMedium = "13_13_13_13_1048576_10485760_1048576_524288_1_1_1_0_100_1"; + String defaultHigh = "13_13_13_13_1048576_15728640_3145728_524288_1_1_1_0_100_1"; lowPreset = new Preset(preferences.getString("preset0", defaultLow), defaultLow); + lowPreset.preloadStories = false; mediumPreset = new Preset(preferences.getString("preset1", defaultMedium), defaultMedium); highPreset = new Preset(preferences.getString("preset2", defaultHigh), defaultHigh); boolean newConfig; @@ -287,9 +305,9 @@ public DownloadController(int instance) { roamingMaxFileSize[3] = preferences.getLong("roamingMaxDownloadSize" + 3, lowPreset.sizes[PRESET_SIZE_NUM_DOCUMENT]); boolean globalAutodownloadEnabled = preferences.getBoolean("globalAutodownloadEnabled", true); - mobilePreset = new Preset(mobileDataDownloadMask, mediumPreset.sizes[PRESET_SIZE_NUM_PHOTO], mobileMaxFileSize[2], mobileMaxFileSize[3], true, true, globalAutodownloadEnabled, false, 100); - wifiPreset = new Preset(wifiDownloadMask, highPreset.sizes[PRESET_SIZE_NUM_PHOTO], wifiMaxFileSize[2], wifiMaxFileSize[3], true, true, globalAutodownloadEnabled, false, 100); - roamingPreset = new Preset(roamingDownloadMask, lowPreset.sizes[PRESET_SIZE_NUM_PHOTO], roamingMaxFileSize[2], roamingMaxFileSize[3], false, false, globalAutodownloadEnabled, true, 50); + mobilePreset = new Preset(mobileDataDownloadMask, mediumPreset.sizes[PRESET_SIZE_NUM_PHOTO], mobileMaxFileSize[2], mobileMaxFileSize[3], true, true, globalAutodownloadEnabled, false, 100, false); + wifiPreset = new Preset(wifiDownloadMask, highPreset.sizes[PRESET_SIZE_NUM_PHOTO], wifiMaxFileSize[2], wifiMaxFileSize[3], true, true, globalAutodownloadEnabled, false, 100, true); + roamingPreset = new Preset(roamingDownloadMask, lowPreset.sizes[PRESET_SIZE_NUM_PHOTO], roamingMaxFileSize[2], roamingMaxFileSize[3], false, false, globalAutodownloadEnabled, true, 50, true); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("newConfig", true); @@ -339,6 +357,7 @@ public void loadAutoDownloadConfig(boolean force) { if (response != null) { TLRPC.TL_account_autoDownloadSettings res = (TLRPC.TL_account_autoDownloadSettings) response; lowPreset.set(res.low); + lowPreset.preloadStories = false; mediumPreset.set(res.medium); highPreset.set(res.high); for (int a = 0; a < 3; a++) { @@ -352,6 +371,7 @@ public void loadAutoDownloadConfig(boolean force) { } if (preset.equals(lowPreset)) { preset.set(res.low); + preset.preloadStories = false; } else if (preset.equals(mediumPreset)) { preset.set(res.medium); } else if (preset.equals(highPreset)) { @@ -623,8 +643,8 @@ public boolean canDownloadMedia(int type, long size) { } public int canDownloadMedia(TLRPC.Message message) { - if (message == null) { - return 0; + if (message == null || message.media instanceof TLRPC.TL_messageMediaStory) { + return canPreloadStories() ? 2 : 0; } int type; boolean isVideo; @@ -1466,4 +1486,27 @@ public boolean isDownloading(int messageId) { } return false; } + + public boolean canPreloadStories() { + Preset preset; + int networkType = ApplicationLoader.getAutodownloadNetworkType(); + if (networkType == StatsController.TYPE_WIFI) { + if (!wifiPreset.enabled) { + return false; + } + preset = getCurrentWiFiPreset(); + + } else if (networkType == StatsController.TYPE_ROAMING) { + if (!roamingPreset.enabled) { + return false; + } + preset = getCurrentRoamingPreset(); + } else { + if (!mobilePreset.enabled) { + return false; + } + preset = getCurrentMobilePreset(); + } + return preset.preloadStories; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/Emoji.java b/TMessagesProj/src/main/java/org/telegram/messenger/Emoji.java index 7911255a8d..b331b85cc2 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/Emoji.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/Emoji.java @@ -28,13 +28,7 @@ import android.view.ViewGroup; import android.widget.TextView; -import java.io.File; - -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Locale; +import org.telegram.ui.Components.AnimatedEmojiSpan; import java.io.File; import java.io.FileInputStream; @@ -44,8 +38,6 @@ import java.util.HashMap; import java.util.Locale; -import org.telegram.ui.Components.AnimatedEmojiSpan; -import tw.nekomimi.nekogram.NekoXConfig; import tw.nekomimi.nekogram.NekoConfig; import tw.nekomimi.nekogram.helpers.remote.EmojiHelper; import xyz.nextalone.nagram.NaConfig; @@ -53,19 +45,19 @@ public class Emoji { private static HashMap rects = new HashMap<>(); - private static int drawImgSize; - private static int bigImgSize; + public static int drawImgSize; + public static int bigImgSize; private static boolean inited = false; - private static Paint placeholderPaint; + public static Paint placeholderPaint; private static int[] emojiCounts = new int[]{ - EmojiData.data[0].length, - EmojiData.data[1].length, - EmojiData.data[2].length, - EmojiData.data[3].length, - EmojiData.data[4].length, - EmojiData.data[5].length, - EmojiData.data[6].length, - EmojiData.data[7].length + EmojiData.data[0].length, + EmojiData.data[1].length, + EmojiData.data[2].length, + EmojiData.data[3].length, + EmojiData.data[4].length, + EmojiData.data[5].length, + EmojiData.data[6].length, + EmojiData.data[7].length }; private static Bitmap[][] emojiBmp = new Bitmap[8][]; private static boolean[][] loadingEmoji = new boolean[8][]; @@ -74,17 +66,17 @@ public class Emoji { public static ArrayList recentEmoji = new ArrayList<>(); public static HashMap emojiColor = new HashMap<>(); private static boolean recentEmojiLoaded; - private static Runnable invalidateUiRunnable = () -> NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.emojiLoaded); + public static Runnable invalidateUiRunnable = () -> NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.emojiLoaded); public static float emojiDrawingYOffset; public static boolean emojiDrawingUseAlpha = true; private static String[] DEFAULT_RECENT = new String[]{ - "\uD83D\uDE02", "\uD83D\uDE18", "\u2764", "\uD83D\uDE0D", "\uD83D\uDE0A", "\uD83D\uDE01", - "\uD83D\uDC4D", "\u263A", "\uD83D\uDE14", "\uD83D\uDE04", "\uD83D\uDE2D", "\uD83D\uDC8B", - "\uD83D\uDE12", "\uD83D\uDE33", "\uD83D\uDE1C", "\uD83D\uDE48", "\uD83D\uDE09", "\uD83D\uDE03", - "\uD83D\uDE22", "\uD83D\uDE1D", "\uD83D\uDE31", "\uD83D\uDE21", "\uD83D\uDE0F", "\uD83D\uDE1E", - "\uD83D\uDE05", "\uD83D\uDE1A", "\uD83D\uDE4A", "\uD83D\uDE0C", "\uD83D\uDE00", "\uD83D\uDE0B", - "\uD83D\uDE06", "\uD83D\uDC4C", "\uD83D\uDE10", "\uD83D\uDE15" + "\uD83D\uDE02", "\uD83D\uDE18", "\u2764", "\uD83D\uDE0D", "\uD83D\uDE0A", "\uD83D\uDE01", + "\uD83D\uDC4D", "\u263A", "\uD83D\uDE14", "\uD83D\uDE04", "\uD83D\uDE2D", "\uD83D\uDC8B", + "\uD83D\uDE12", "\uD83D\uDE33", "\uD83D\uDE1C", "\uD83D\uDE48", "\uD83D\uDE09", "\uD83D\uDE03", + "\uD83D\uDE22", "\uD83D\uDE1D", "\uD83D\uDE31", "\uD83D\uDE21", "\uD83D\uDE0F", "\uD83D\uDE1E", + "\uD83D\uDE05", "\uD83D\uDE1A", "\uD83D\uDE4A", "\uD83D\uDE0C", "\uD83D\uDE00", "\uD83D\uDE0B", + "\uD83D\uDE06", "\uD83D\uDC4C", "\uD83D\uDE10", "\uD83D\uDE15" }; private final static int MAX_RECENT_EMOJI_COUNT = 48; @@ -116,7 +108,6 @@ public static void reloadEmoji() { } } - static { drawImgSize = AndroidUtilities.dp(20); bigImgSize = AndroidUtilities.dp(AndroidUtilities.isTablet() ? 40 : 34); @@ -150,23 +141,7 @@ private static void loadEmoji(final byte page, final short page2) { } loadingEmoji[page][page2] = true; Utilities.globalQueue.postRunnable(() -> { - loadEmojiInternal(page, page2); - loadingEmoji[page][page2] = false; - }); - } - } - - private static void loadEmojiInternal(final byte page, final short page2) { - try { - int imageResize; - if (AndroidUtilities.density <= 1.0f) { - imageResize = 2; - } else { - imageResize = 1; - } - - Bitmap bitmap = null; - try { + final Bitmap bitmap; if (NekoConfig.useSystemEmoji.Bool() || isSelectedCustomEmojiPack) { int emojiSize = 66; bitmap = Bitmap.createBitmap(emojiSize, emojiSize, Bitmap.Config.ARGB_8888); @@ -180,31 +155,53 @@ private static void loadEmojiInternal(final byte page, final short page2) { emojiSize ); } else { - InputStream is; if (isSelectedEmojiPack) { - is = new FileInputStream(new File(emojiFile, String.format(Locale.US, "%d_%d.png", page, page2))); + bitmap = loadBitmap(emojiFile.getAbsolutePath() + "/" + String.format(Locale.US, "%d_%d.png", page, page2), false); } else { - is = ApplicationLoader.applicationContext.getAssets().open("emoji/" + String.format(Locale.US, "%d_%d.png", page, page2)); + bitmap = loadBitmap("emoji/" + String.format(Locale.US, "%d_%d.png", page, page2)); } - BitmapFactory.Options opts = new BitmapFactory.Options(); - opts.inJustDecodeBounds = false; - opts.inSampleSize = imageResize; - bitmap = BitmapFactory.decodeStream(is, null, opts); - is.close(); } + if (bitmap != null) { + emojiBmp[page][page2] = bitmap; + AndroidUtilities.cancelRunOnUIThread(invalidateUiRunnable); + AndroidUtilities.runOnUIThread(invalidateUiRunnable); + } + loadingEmoji[page][page2] = false; + }); + } + } + + public static Bitmap loadBitmap(String path) { + return loadBitmap(path, true); + } + + public static Bitmap loadBitmap(String path, boolean assets) { + try { + int imageResize; + if (AndroidUtilities.density <= 1.0f) { + imageResize = 2; + } else { + imageResize = 1; + } + + Bitmap bitmap = null; + try { + InputStream is = assets ? ApplicationLoader.applicationContext.getAssets().open(path) : new FileInputStream(path); + BitmapFactory.Options opts = new BitmapFactory.Options(); + opts.inJustDecodeBounds = false; + opts.inSampleSize = imageResize; + bitmap = BitmapFactory.decodeStream(is, null, opts); + is.close(); } catch (Throwable e) { FileLog.e(e); } - - final Bitmap finalBitmap = bitmap; - emojiBmp[page][page2] = finalBitmap; - AndroidUtilities.cancelRunOnUIThread(invalidateUiRunnable); - AndroidUtilities.runOnUIThread(invalidateUiRunnable); + return bitmap; } catch (Throwable x) { if (BuildVars.LOGS_ENABLED) { FileLog.e("Error loading emoji", x); } } + return null; } public static void invalidateAll(View view) { @@ -253,9 +250,16 @@ public static String fixEmoji(String emoji) { public static EmojiDrawable getEmojiDrawable(CharSequence code) { DrawableInfo info = getDrawableInfo(code); if (info == null) { + if (code != null) { + CompoundEmoji.CompoundEmojiDrawable compoundEmojiDrawable = CompoundEmoji.getCompoundEmojiDrawable(code.toString()); + if (compoundEmojiDrawable != null) { + compoundEmojiDrawable.setBounds(0, 0, drawImgSize, drawImgSize); + return compoundEmojiDrawable; + } + } return null; } - EmojiDrawable ed = new EmojiDrawable(info); + EmojiDrawable ed = new SimpleEmojiDrawable(info); ed.setBounds(0, 0, drawImgSize, drawImgSize); return ed; } @@ -286,7 +290,15 @@ public static boolean isValidEmoji(CharSequence code) { } public static Drawable getEmojiBigDrawable(String code) { - EmojiDrawable ed = getEmojiDrawable(code); + EmojiDrawable ed = null; + CompoundEmoji.CompoundEmojiDrawable compoundEmojiDrawable = CompoundEmoji.getCompoundEmojiDrawable(code); + if (compoundEmojiDrawable != null) { + compoundEmojiDrawable.setBounds(0, 0, drawImgSize, drawImgSize); + ed = compoundEmojiDrawable; + } + if (ed == null) { + ed = getEmojiDrawable(code); + } if (ed == null) { CharSequence newCode = EmojiData.emojiAliasMap.get(code); if (newCode != null) { @@ -301,15 +313,22 @@ public static Drawable getEmojiBigDrawable(String code) { return ed; } - public static class EmojiDrawable extends Drawable { + public static abstract class EmojiDrawable extends Drawable { + boolean fullSize = false; + int placeholderColor = 0x10000000; + + public boolean isLoaded() { + return false; + } + public void preload() {} + } + + public static class SimpleEmojiDrawable extends EmojiDrawable { private DrawableInfo info; - private boolean fullSize = false; - private static final Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG); - private static final Rect rect = new Rect(); - private static final TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); - public int placeholderColor = 0x10000000; + private static Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG); + private static Rect rect = new Rect(); - public EmojiDrawable(DrawableInfo i) { + public SimpleEmojiDrawable(DrawableInfo i) { info = i; } @@ -364,10 +383,12 @@ public void setColorFilter(ColorFilter cf) { } + @Override public boolean isLoaded() { return emojiBmp[info.page][info.page2] != null; } + @Override public void preload() { if (!isLoaded()) { loadEmoji(info.page, info.page2); @@ -387,15 +408,6 @@ public DrawableInfo(byte p, short p2, int index) { } } - private static boolean inArray(char c, char[] a) { - for (char cc : a) { - if (cc == c) { - return true; - } - } - return false; - } - public static class EmojiSpanRange { public EmojiSpanRange(int start, int end, CharSequence code) { this.start = start; @@ -428,9 +440,10 @@ public static ArrayList parseEmojis(CharSequence cs, int[] emoji int startLength = 0; int previousGoodIndex = 0; StringBuilder emojiCode = new StringBuilder(16); - int length = cs.length(); + final int length = cs.length(); boolean doneEmoji = false; boolean notOnlyEmoji; + boolean resetStartIndex = false; try { for (int i = 0; i < length; i++) { @@ -439,17 +452,16 @@ public static ArrayList parseEmojis(CharSequence cs, int[] emoji if (c >= 0xD83C && c <= 0xD83E || (buf != 0 && (buf & 0xFFFFFFFF00000000L) == 0 && (buf & 0xFFFF) == 0xD83C && (c >= 0xDDE6 && c <= 0xDDFF))) { if (startIndex == -1) { startIndex = i; + } else if (resetStartIndex) { + startIndex = i; + startLength = 0; + resetStartIndex = false; } emojiCode.append(c); startLength++; buf <<= 16; buf |= c; - } else if (emojiCode.length() > 0 && (c == 0x2640 || c == 0x2642 || c == 0x2695)) { - emojiCode.append(c); - startLength++; - buf = 0; - doneEmoji = true; - } else if (buf > 0 && (c & 0xF000) == 0xD000) { + } else if (emojiCode.length() > 0 && (c == 0x2640 || c == 0x2642 || c == 0x2695) || buf > 0 && (c & 0xF000) == 0xD000) { emojiCode.append(c); startLength++; buf = 0; @@ -463,11 +475,16 @@ public static ArrayList parseEmojis(CharSequence cs, int[] emoji emojiCode.append(c2); emojiCode.append(c); doneEmoji = true; + resetStartIndex = false; } } } else if ((c == 0x00A9 || c == 0x00AE || c >= 0x203C && c <= 0x3299) && EmojiData.dataCharsMap.containsKey(c)) { if (startIndex == -1) { startIndex = i; + } else if (resetStartIndex) { + startIndex = i; + startLength = 0; + resetStartIndex = false; } startLength++; emojiCode.append(c); @@ -477,6 +494,7 @@ public static ArrayList parseEmojis(CharSequence cs, int[] emoji startIndex = -1; startLength = 0; doneEmoji = false; + resetStartIndex = false; } else if (c != 0xfe0f && c != '\n' && c != ' ' && c != '\t') { notOnlyEmoji = true; } @@ -492,7 +510,10 @@ public static ArrayList parseEmojis(CharSequence cs, int[] emoji } else if (emojiCode.length() >= 2 && emojiCode.charAt(0) == 0xD83C && emojiCode.charAt(1) == 0xDFF4 && next == 0xDB40) { i++; while (true) { - emojiCode.append(cs.charAt(i)).append(cs.charAt(i + 1)); + emojiCode.append(cs.charAt(i)); + if (i + 1 >= cs.length()) { + emojiCode.append(cs.charAt(i + 1)); + } startLength += 2; i += 2; if (i >= cs.length() || cs.charAt(i) != 0xDB40) { @@ -515,7 +536,17 @@ public static ArrayList parseEmojis(CharSequence cs, int[] emoji startLength++; doneEmoji = false; } - } else if (startIndex != -1 || prevCh == '*' || prevCh == '#' || prevCh >= '0' && prevCh <= '9') { + } else if (prevCh == '*' || prevCh == '#' || prevCh >= '0' && prevCh <= '9') { + if (c >= 0xFE00 && c <= 0xFE0F) { + startIndex = previousGoodIndex; + resetStartIndex = true; + i++; + startLength++; + if (!doneEmoji) { + doneEmoji = i + 1 >= length; + } + } + } else if (startIndex != -1) { if (c >= 0xFE00 && c <= 0xFE0F) { i++; startLength++; @@ -542,11 +573,14 @@ public static ArrayList parseEmojis(CharSequence cs, int[] emoji if (emojiOnly != null) { emojiOnly[0]++; } - emojis.add(new EmojiSpanRange(startIndex, startIndex + startLength, emojiCode.subSequence(0, emojiCode.length()))); + if (startIndex >= 0 && startIndex + startLength <= length) { + emojis.add(new EmojiSpanRange(startIndex, startIndex + startLength, emojiCode.subSequence(0, emojiCode.length()))); + } startLength = 0; startIndex = -1; emojiCode.setLength(0); doneEmoji = false; + resetStartIndex = false; } } } catch (Exception e) { @@ -625,6 +659,7 @@ public static CharSequence replaceEmoji(CharSequence cs, Paint.FontMetricsInt fo public static class EmojiSpan extends ImageSpan { public Paint.FontMetricsInt fontMetrics; + public float scale = 1f; public int size = AndroidUtilities.dp(20); public String emoji; @@ -660,6 +695,7 @@ public int getSize(Paint paint, CharSequence text, int start, int end, Paint.Fon fm = new Paint.FontMetricsInt(); } + int scaledSize = (int) (scale * size); if (fontMetrics == null) { int sz = super.getSize(paint, text, start, end, fm); @@ -681,9 +717,9 @@ public int getSize(Paint paint, CharSequence text, int start, int end, Paint.Fon fm.bottom = fontMetrics.bottom; } if (getDrawable() != null) { - getDrawable().setBounds(0, 0, size, size); + getDrawable().setBounds(0, 0, scaledSize, scaledSize); } - return size; + return scaledSize; } } @@ -692,7 +728,7 @@ public int getSize(Paint paint, CharSequence text, int start, int end, Paint.Fon @Override public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) { - lastDrawX = x + size / 2f; + lastDrawX = x + scale * size / 2f; lastDrawY = top + (bottom - top) / 2f; drawn = true; @@ -702,10 +738,11 @@ public void draw(Canvas canvas, CharSequence text, int start, int end, float x, getDrawable().setAlpha(paint.getAlpha()); } boolean needRestore = false; - if (emojiDrawingYOffset != 0) { + float ty = emojiDrawingYOffset - (size - scale * size) / 2; + if (ty != 0) { needRestore = true; canvas.save(); - canvas.translate(0, emojiDrawingYOffset); + canvas.translate(0, ty); } super.draw(canvas, text, start, end, x, top, y, bottom, paint); if (needRestore) { @@ -879,44 +916,4 @@ public static void saveEmojiColors() { } preferences.edit().putString("color", stringBuilder.toString()).commit(); } - - /** - * NekoX: This function tries to fix incomplete emoji display shown in AvatarDrawable - * In AvatarDrawable, only the first char is used to draw "avatar". - * - * @return The first char or the first emoji - */ - public static String getFirstCharSafely(String source) { - source = source.trim(); - if (source.length() <= 1) - return source; - StringBuilder sb = new StringBuilder(); - boolean nextEmoji = false; - int code = source.codePointAt(0); - sb.appendCodePoint(code); // append the first "char" - for (int offset = code > 0xFFFF ? 2 : 1; offset < source.length(); offset++) { - code = source.codePointAt(offset); - if (code > 0xFFFF) offset++; - if (nextEmoji || code == 0xFE0F || code == 0x20E3 || (code >= 0x1F3FB && code <= 0x1F3FF)) { - // 0xFE0F: VARIATION SELECTOR-16, 20E3: Keycap, 0x1F3FB ~ 0x1F3FF: skin tone - sb.appendCodePoint(code); - nextEmoji = false; - continue; - } else if ((code >= 0x1F1E6 && code <= 0x1F1FF)) { - sb.appendCodePoint(code); - break; - // 0x1F1E6 ~ 0x1F1FF: regional indicator symbol letter a to z - // These unicode are also used in the first "char" of country flag emoji, so break immediately to prevent appending two emoji - } else if (code == 0x200D) { - // 0x200D: ZWJ - sb.appendCodePoint(code); - nextEmoji = true; - continue; - } - if (!nextEmoji) - break; - } - return sb.toString(); - } - } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ExtendedBitmapDrawable.java b/TMessagesProj/src/main/java/org/telegram/messenger/ExtendedBitmapDrawable.java index 09c2eb2aea..057eb896f5 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ExtendedBitmapDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ExtendedBitmapDrawable.java @@ -5,17 +5,25 @@ public class ExtendedBitmapDrawable extends BitmapDrawable { - private boolean canInvert; + private int invert; private int orientation; - public ExtendedBitmapDrawable(Bitmap bitmap, boolean invert, int orient) { + public ExtendedBitmapDrawable(Bitmap bitmap, int orient, int invrt) { super(bitmap); - canInvert = invert; + invert = invrt; orientation = orient; } - public boolean isCanInvert() { - return canInvert; + public boolean invertHorizontally() { + return (invert & 1) != 0; + } + + public boolean invertVertically() { + return (invert & 2) != 0; + } + + public int getInvert() { + return invert; } public int getOrientation() { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java index 6482bfb69b..d44e0b7e59 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java @@ -8,6 +8,10 @@ package org.telegram.messenger; +import android.os.Build; + +import com.google.android.exoplayer2.util.Log; + import org.telegram.messenger.utils.ImmutableByteArrayOutputStream; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.NativeByteBuffer; @@ -33,6 +37,7 @@ public class FileLoadOperation { + private final boolean FULL_LOGS = false; private final static int FINISH_CODE_DEFAULT = 0; private final static int FINISH_CODE_FILE_ALREADY_EXIST = 1; public boolean preFinished; @@ -45,19 +50,47 @@ public class FileLoadOperation { public static ImmutableByteArrayOutputStream filesQueueByteBuffer; private boolean forceSmallChunk; private Runnable fileWriteRunnable; + public boolean isStory; public void setStream(FileLoadOperationStream stream, boolean streamPriority, long streamOffset) { this.stream = stream; this.streamOffset = streamOffset; this.streamPriority = streamPriority; + Utilities.stageQueue.postRunnable(() -> { + if (streamListeners == null) { + streamListeners = new ArrayList<>(); + } + if (stream != null && !streamListeners.contains(stream)) { + streamListeners.add(stream); + } + if (stream != null && state != stateDownloading && state != stateIdle) { + stream.newDataAvailable(); + } + }); } public int getPositionInQueue() { return getQueue().getPosition(this); } + public boolean checkPrefixPreloadFinished() { + if (preloadPrefixSize > 0 && downloadedBytes > preloadPrefixSize) { + long minStart = Long.MAX_VALUE; + for (int b = 0; b < notLoadedBytesRanges.size(); b++) { + Range range = notLoadedBytesRanges.get(b); + minStart = Math.min(minStart, range.start); + } + if (minStart > preloadPrefixSize) { + return true; + } + } + return false; + } + protected static class RequestInfo { public long requestStartTime; + public int chunkSize; + public int connectionType; private int requestToken; private long offset; private TLRPC.TL_upload_file response; @@ -74,6 +107,14 @@ private Range(long s, long e) { start = s; end = e; } + + @Override + public String toString() { + return "Range{" + + "start=" + start + + ", end=" + end + + '}'; + } } private static final Object lockObject = new Object(); @@ -129,6 +170,7 @@ private PreloadRange(long o, long l) { private int moovFound; private byte[] preloadTempBuffer = new byte[24]; private int preloadTempBufferCount; + private int preloadPrefixSize; private boolean nextPartWasPreloaded; @@ -162,6 +204,7 @@ private PreloadRange(long o, long l) { private int currentMaxDownloadRequests; private int requestsCount; private int renameRetryCount; + private static int globalRequestPointer; private boolean encryptFile; private boolean allowDisordererFileSave; @@ -222,7 +265,7 @@ public interface FileLoadOperationDelegate { } private void updateParams() { - if ((MessagesController.getInstance(currentAccount).getfileExperimentalParams || NekoConfig.enhancedFileLoader.Bool() && !forceSmallChunk)) { + if ((preloadPrefixSize > 0 || MessagesController.getInstance(currentAccount).getfileExperimentalParams || NekoConfig.enhancedFileLoader.Bool()) && !forceSmallChunk) { downloadChunkSizeBig = 1024 * 512; maxDownloadRequests = 8; maxDownloadRequestsBig = 8; @@ -367,6 +410,7 @@ public FileLoadOperation(TLRPC.Document documentLocation, Object parent) { for (int a = 0, N = documentLocation.attributes.size(); a < N; a++) { if (documentLocation.attributes.get(a) instanceof TLRPC.TL_documentAttributeVideo) { supportsPreloading = true; + preloadPrefixSize = documentLocation.attributes.get(a).preload_prefix_size; break; } } @@ -490,6 +534,7 @@ private void removePart(ArrayList ranges, long start, long end) { if (!modified) { ranges.add(new Range(start, end)); } + } long totalTime; @@ -595,7 +640,7 @@ protected File getCurrentFile() { final CountDownLatch countDownLatch = new CountDownLatch(1); final File[] result = new File[1]; Utilities.stageQueue.postRunnable(() -> { - if (state == stateFinished) { + if (state == stateFinished && !preloadFinished) { result[0] = cacheFileFinal; } else { result[0] = cacheFileTemp; @@ -610,6 +655,14 @@ protected File getCurrentFile() { return result[0]; } + protected File getCurrentFileFast() { + if (state == stateFinished && !preloadFinished) { + return cacheFileFinal; + } else { + return cacheFileTemp; + } + } + private long getDownloadedLengthFromOffsetInternal(ArrayList ranges, final long offset, final long length) { if (ranges == null || state == stateFinished || ranges.isEmpty()) { if (state == stateFinished) { @@ -657,7 +710,12 @@ protected long[] getDownloadedLengthFromOffset(final long offset, final long len final CountDownLatch countDownLatch = new CountDownLatch(1); final long[] result = new long[2]; Utilities.stageQueue.postRunnable(() -> { - result[0] = getDownloadedLengthFromOffsetInternal(notLoadedBytesRanges, offset, length); + try { + result[0] = getDownloadedLengthFromOffsetInternal(notLoadedBytesRanges, offset, length); + } catch (Throwable e) { + FileLog.e(e); + result[0] = 0; + } if (state == stateFinished) { result[1] = 1; } @@ -696,6 +754,18 @@ public void pause() { return; } paused = true; + Utilities.stageQueue.postRunnable(() -> { + if (isStory) { + if (BuildVars.LOGS_ENABLED) { + FileLog.d("debug_loading:" + cacheFileFinal.getName() + " pause operation, clear requests"); + } + clearOperaion(null, false); + } else { + for (int i = 0; i < requestInfos.size(); i++) { + ConnectionsManager.getInstance(currentAccount).failNotRunningRequest(requestInfos.get(i).requestToken); + } + } + }); } public boolean start() { @@ -705,13 +775,24 @@ public boolean start() { public boolean start(final FileLoadOperationStream stream, final long streamOffset, final boolean streamPriority) { startTime = System.currentTimeMillis(); updateParams(); + isStory = parentObject instanceof TLRPC.TL_storyItem; if (currentDownloadChunkSize == 0) { - if (isStream) { + if (forceSmallChunk) { + if (BuildVars.LOGS_ENABLED) { + FileLog.d("debug_loading: restart with small chunk"); + } + currentDownloadChunkSize = 1024 * 32; + currentMaxDownloadRequests = 4; + } else if (isStory) { + currentDownloadChunkSize = downloadChunkSizeBig; + currentMaxDownloadRequests = maxDownloadRequestsBig; + } else if (isStream) { currentDownloadChunkSize = downloadChunkSizeAnimation; currentMaxDownloadRequests = maxDownloadRequestsAnimation; } else { - currentDownloadChunkSize = totalBytesCount >= bigFileSizeFrom ? downloadChunkSizeBig : downloadChunkSize; - currentMaxDownloadRequests = totalBytesCount >= bigFileSizeFrom ? maxDownloadRequestsBig : maxDownloadRequests; + boolean bigChunk = totalBytesCount >= bigFileSizeFrom; + currentDownloadChunkSize = bigChunk ? downloadChunkSizeBig : downloadChunkSize; + currentMaxDownloadRequests = bigChunk ? maxDownloadRequestsBig : maxDownloadRequests; } } final boolean alreadyStarted = state != stateIdle; @@ -729,7 +810,7 @@ public boolean start(final FileLoadOperationStream stream, final long streamOffs requestedBytesCount -= currentDownloadChunkSize; removePart(notRequestedBytesRanges, priorityRequestInfo.offset, priorityRequestInfo.offset + currentDownloadChunkSize); if (priorityRequestInfo.requestToken != 0) { - ConnectionsManager.getInstance(currentAccount).cancelRequest(priorityRequestInfo.requestToken, false); + ConnectionsManager.getInstance(currentAccount).cancelRequest(priorityRequestInfo.requestToken, true); requestsCount--; } if (BuildVars.DEBUG_VERSION) { @@ -743,19 +824,23 @@ public boolean start(final FileLoadOperationStream stream, final long streamOffs } else { streamStartOffset = streamOffset / currentDownloadChunkSize * currentDownloadChunkSize; } - streamListeners.add(stream); +// if (!streamListeners.contains(stream)) { +// streamListeners.add(stream); +// } if (alreadyStarted) { if (preloadedBytesRanges != null && getDownloadedLengthFromOffsetInternal(notLoadedBytesRanges, streamStartOffset, 1) == 0) { if (preloadedBytesRanges.get(streamStartOffset) != null) { nextPartWasPreloaded = true; } } - startDownloadRequest(); + startDownloadRequest(-1); nextPartWasPreloaded = false; } }); - } else if (wasPaused && alreadyStarted) { - Utilities.stageQueue.postRunnable(this::startDownloadRequest); + } else if (alreadyStarted) { + Utilities.stageQueue.postRunnable(() -> { + startDownloadRequest(-1); + }); } if (alreadyStarted) { return wasPaused; @@ -858,7 +943,10 @@ public boolean start(final FileLoadOperationStream stream, final long streamOffs } } boolean finalFileExist = cacheFileFinal.exists(); - if (finalFileExist && (parentObject instanceof TLRPC.TL_theme || totalBytesCount != 0 && totalBytesCount != cacheFileFinal.length())) { + if (finalFileExist && (parentObject instanceof TLRPC.TL_theme || (totalBytesCount != 0 && !ungzip && totalBytesCount != cacheFileFinal.length()))) { + if (BuildVars.LOGS_ENABLED) { + FileLog.d("debug_loading: delete existing file cause file size mismatch " + cacheFileFinal.getName() + " totalSize=" + totalBytesCount + " existingFileSize=" + cacheFileFinal.length()); + } if (!delegate.hasAnotherRefOnFile(cacheFileFinal.toString())) { cacheFileFinal.delete(); } @@ -987,6 +1075,9 @@ public boolean start(final FileLoadOperationStream stream, final long streamOffs if (fileNameParts != null) { cacheFileParts = new File(tempPath, fileNameParts); + if (!cacheFileTemp.exists()) { + cacheFileParts.delete(); + } try { filePartsStream = new RandomAccessFile(cacheFileParts, "rws"); long len = filePartsStream.length(); @@ -1012,6 +1103,7 @@ public boolean start(final FileLoadOperationStream stream, final long streamOffs FileLoader.getInstance(currentAccount).getFileDatabase().saveFileDialogId(cacheFileTemp, fileMetadata); } + if (cacheFileTemp.exists()) { if (newKeyGenerated) { cacheFileTemp.delete(); @@ -1020,7 +1112,7 @@ public boolean start(final FileLoadOperationStream stream, final long streamOffs if (fileNameIv != null && (totalDownloadedLen % currentDownloadChunkSize) != 0) { requestedBytesCount = 0; } else { - requestedBytesCount = downloadedBytes = (cacheFileTemp.length()) / ((long) currentDownloadChunkSize) * currentDownloadChunkSize; + requestedBytesCount = downloadedBytes = floorDiv(cacheFileTemp.length(), currentDownloadChunkSize) * currentDownloadChunkSize; } if (notLoadedBytesRanges != null && notLoadedBytesRanges.isEmpty()) { notLoadedBytesRanges.add(new Range(downloadedBytes, totalBytesCount)); @@ -1103,20 +1195,22 @@ public boolean start(final FileLoadOperationStream stream, final long streamOffs } started = true; Utilities.stageQueue.postRunnable(() -> { - if (totalBytesCount != 0 && (isPreloadVideoOperation && preloaded[0] || downloadedBytes == totalBytesCount)) { + boolean videoPreloaded = isPreloadVideoOperation && preloaded[0]; + boolean preloadedByPrefixSize = preloadPrefixSize > 0 && downloadedBytes >= preloadPrefixSize && canFinishPreload(); + if (totalBytesCount != 0 && (videoPreloaded || downloadedBytes == totalBytesCount || preloadedByPrefixSize)) { try { - onFinishLoadingFile(false, FINISH_CODE_FILE_ALREADY_EXIST); + onFinishLoadingFile(false, FINISH_CODE_FILE_ALREADY_EXIST, true); } catch (Exception e) { onFail(true, 0); } } else { - startDownloadRequest(); + startDownloadRequest(-1); } }); } else { started = true; try { - onFinishLoadingFile(false, FINISH_CODE_FILE_ALREADY_EXIST); + onFinishLoadingFile(false, FINISH_CODE_FILE_ALREADY_EXIST, false); if (pathSaveData != null) { delegate.saveFilePath(pathSaveData, cacheFileFinal); } @@ -1162,7 +1256,7 @@ public void setIsPreloadVideoOperation(boolean value) { requestedBytesCount = 0; clearOperaion(null, true); isPreloadVideoOperation = value; - startDownloadRequest(); + startDownloadRequest(-1); }); } else { isPreloadVideoOperation = value; @@ -1242,12 +1336,23 @@ private void cancel(boolean deleteFiles) { private void cancelRequests() { if (requestInfos != null) { + int[] waitingDownloadSize = new int[2]; for (int a = 0; a < requestInfos.size(); a++) { RequestInfo requestInfo = requestInfos.get(a); if (requestInfo.requestToken != 0) { - ConnectionsManager.getInstance(currentAccount).cancelRequest(requestInfo.requestToken, false); + ConnectionsManager.getInstance(currentAccount).cancelRequest(requestInfo.requestToken, true); + int index = requestInfo.connectionType == ConnectionsManager.ConnectionTypeDownload ? 0 : 1; + waitingDownloadSize[index] += requestInfo.chunkSize; + } + } + for (int i = 0; i < 2; i++) { + int connectionType = i == 0 ? ConnectionsManager.ConnectionTypeDownload : ConnectionsManager.ConnectionTypeDownload2; + if (waitingDownloadSize[i] > 512 * 1024 * 2) { + int datacenterId = isCdn ? cdnDatacenterId : this.datacenterId; + ConnectionsManager.getInstance(currentAccount).discardConnection(datacenterId, connectionType); } } + } } @@ -1333,20 +1438,20 @@ private void cleanup() { } } - private void onFinishLoadingFile(final boolean increment, int finishCode) { + private void onFinishLoadingFile(final boolean increment, int finishCode, boolean preload) { if (state != stateDownloading) { return; } state = stateFinished; notifyStreamListeners(); cleanup(); - if (isPreloadVideoOperation) { + if (isPreloadVideoOperation || preload) { preloadFinished = true; if (BuildVars.DEBUG_VERSION) { if (finishCode == FINISH_CODE_FILE_ALREADY_EXIST) { FileLog.d("file already exist " + cacheFileTemp); } else { - FileLog.d("finished preloading file to " + cacheFileTemp + " loaded " + totalPreloadedBytes + " of " + totalBytesCount); + FileLog.d("finished preloading file to " + cacheFileTemp + " loaded " + downloadedBytes + " of " + totalBytesCount + " prefSize=" + preloadPrefixSize); } } if (fileMetadata != null) { @@ -1357,6 +1462,7 @@ private void onFinishLoadingFile(final boolean increment, int finishCode) { FileLoader.getInstance(currentAccount).getFileDatabase().removeFiles(Collections.singletonList(new CacheModel.FileInfo(cacheFileParts))); } } + delegate.didPreFinishLoading(FileLoadOperation.this, cacheFileFinal); delegate.didFinishLoadingFile(FileLoadOperation.this, cacheFileFinal); } else { final File cacheIvTempFinal = cacheIvTemp; @@ -1426,6 +1532,16 @@ private void onFinishLoadingFile(final boolean increment, int finishCode) { FileLog.e(e); } } + if (!renameResult && renameRetryCount == 3) { + try { + renameResult = AndroidUtilities.copyFile(cacheFileTempLocal, cacheFileFinal); + if (renameResult) { + cacheFileFinal.delete(); + } + } catch (Throwable e) { + FileLog.e(e); + } + } if (!renameResult) { if (BuildVars.LOGS_ENABLED) { FileLog.e("unable to rename temp = " + cacheFileTempLocal + " to final = " + cacheFileFinal + " retry = " + renameRetryCount); @@ -1435,7 +1551,7 @@ private void onFinishLoadingFile(final boolean increment, int finishCode) { state = stateDownloading; Utilities.stageQueue.postRunnable(() -> { try { - onFinishLoadingFile(increment, FINISH_CODE_DEFAULT); + onFinishLoadingFile(increment, FINISH_CODE_DEFAULT, false); } catch (Exception e) { onFail(false, 0); } @@ -1614,7 +1730,7 @@ protected boolean processRequestResult(RequestInfo requestInfo, TLRPC.TL_error e bytes = null; } if (bytes == null || bytes.limit() == 0) { - onFinishLoadingFile(true, FINISH_CODE_DEFAULT); + onFinishLoadingFile(true, FINISH_CODE_DEFAULT, false); return false; } int currentBytesSize = bytes.limit(); @@ -1639,6 +1755,7 @@ protected boolean processRequestResult(RequestInfo requestInfo, TLRPC.TL_error e } boolean finishedDownloading; + boolean finishPreload = false; if (isPreloadVideoOperation) { preloadStream.writeLong(requestInfo.offset); preloadStream.writeLong(currentBytesSize); @@ -1688,10 +1805,16 @@ protected boolean processRequestResult(RequestInfo requestInfo, TLRPC.TL_error e } else { downloadedBytes += currentBytesSize; if (totalBytesCount > 0) { - finishedDownloading = downloadedBytes >= totalBytesCount; + finishedDownloading = downloadedBytes >= totalBytesCount || (preloadPrefixSize > 0 && downloadedBytes >= preloadPrefixSize && canFinishPreload() && requestInfos.isEmpty()); + if (downloadedBytes < totalBytesCount) { + finishPreload = true; + } } else { finishedDownloading = currentBytesSize != currentDownloadChunkSize || (totalBytesCount == downloadedBytes || downloadedBytes % currentDownloadChunkSize != 0) && (totalBytesCount <= 0 || totalBytesCount <= downloadedBytes); } + if (BuildVars.LOGS_ENABLED && FULL_LOGS) { + FileLog.d(cacheFileFinal.getName() + " downloadedBytes=" + downloadedBytes + " total=" + totalBytesCount + " " + finishedDownloading + " " + finishPreload); + } if (key != null) { Utilities.aesIgeEncryption(bytes.buffer, key, iv, false, true, 0, bytes.limit()); if (finishedDownloading && bytesCountPadding != 0) { @@ -1720,6 +1843,10 @@ protected boolean processRequestResult(RequestInfo requestInfo, TLRPC.TL_error e FileChannel channel = fileOutputStream.getChannel(); channel.write(bytes.buffer); addPart(notLoadedBytesRanges, requestInfo.offset, requestInfo.offset + currentBytesSize, true); + if (BuildVars.LOGS_ENABLED && FULL_LOGS) { + FileLog.d(fileName + " add part " + requestInfo.offset + " " + (requestInfo.offset + currentBytesSize)); + FileLog.d(fileName + " notLoadedBytesRanges=" + notLoadedBytesRanges); + } if (isCdn) { long cdnCheckPart = requestInfo.offset / cdnChunkCheckSize; @@ -1806,9 +1933,9 @@ protected boolean processRequestResult(RequestInfo requestInfo, TLRPC.TL_error e } if (finishedDownloading) { - onFinishLoadingFile(true, FINISH_CODE_DEFAULT); + onFinishLoadingFile(true, FINISH_CODE_DEFAULT, finishPreload); } else if (state != stateCanceled) { - startDownloadRequest(); + startDownloadRequest(requestInfo.connectionType); } } catch (Exception e) { FileLog.e(e, !AndroidUtilities.isFilNotFoundException(e) && !AndroidUtilities.isENOSPC(e)); @@ -1823,12 +1950,13 @@ protected boolean processRequestResult(RequestInfo requestInfo, TLRPC.TL_error e } } else { if (error.text.contains("LIMIT_INVALID") && !requestInfo.forceSmallChunk) { + removePart(notRequestedBytesRanges, requestInfo.offset, requestInfo.offset + requestInfo.chunkSize); if (!forceSmallChunk) { forceSmallChunk = true; - currentDownloadChunkSize = 0; - pause(); - start(); + currentDownloadChunkSize = 1024 * 32; + currentMaxDownloadRequests = 4; } + startDownloadRequest(requestInfo.connectionType); } else if (error.text.contains("FILE_MIGRATE_")) { String errorMsg = error.text.replace("FILE_MIGRATE_", ""); Scanner scanner = new Scanner(errorMsg); @@ -1844,12 +1972,12 @@ protected boolean processRequestResult(RequestInfo requestInfo, TLRPC.TL_error e } else { datacenterId = val; requestedBytesCount = downloadedBytes = 0; - startDownloadRequest(); + startDownloadRequest(requestInfo.connectionType); } } else if (error.text.contains("OFFSET_INVALID")) { if (downloadedBytes % currentDownloadChunkSize == 0) { try { - onFinishLoadingFile(true, FINISH_CODE_DEFAULT); + onFinishLoadingFile(true, FINISH_CODE_DEFAULT, false); } catch (Exception e) { FileLog.e(e); onFail(false, 0); @@ -1873,6 +2001,10 @@ protected boolean processRequestResult(RequestInfo requestInfo, TLRPC.TL_error e return false; } + private boolean canFinishPreload() { + return isStory && priority < FileLoader.PRIORITY_HIGH; + } + protected void onFail(boolean thread, final int reason) { cleanup(); state = reason == 1 ? stateCanceled : stateFailed; @@ -1885,29 +2017,45 @@ protected void onFail(boolean thread, final int reason) { FileLog.d("failed downloading file to " + cacheFileFinal + " reason = " + reason + " time = " + time + " dc = " + datacenterId + " size = " + AndroidUtilities.formatFileSize(totalBytesCount)); } } - if (thread) { - Utilities.stageQueue.postRunnable(() -> delegate.didFailedLoadingFile(FileLoadOperation.this, reason)); - } else { + } + if (thread) { + Utilities.stageQueue.postRunnable(() -> { + if (delegate != null) { + delegate.didFailedLoadingFile(FileLoadOperation.this, reason); + } + notifyStreamListeners(); + }); + } else { + if (delegate != null) { delegate.didFailedLoadingFile(FileLoadOperation.this, reason); } + notifyStreamListeners(); } } private void clearOperaion(RequestInfo currentInfo, boolean preloadChanged) { long minOffset = Long.MAX_VALUE; + int[] waitingDownloadSize = new int[2]; for (int a = 0; a < requestInfos.size(); a++) { RequestInfo info = requestInfos.get(a); minOffset = Math.min(info.offset, minOffset); if (isPreloadVideoOperation) { requestedPreloadedBytesRanges.remove(info.offset); } else { - removePart(notRequestedBytesRanges, info.offset, info.offset + currentDownloadChunkSize); + removePart(notRequestedBytesRanges, info.offset, info.offset + info.chunkSize); } if (currentInfo == info) { continue; } if (info.requestToken != 0) { - ConnectionsManager.getInstance(currentAccount).cancelRequest(info.requestToken, false); + ConnectionsManager.getInstance(currentAccount).cancelRequest(info.requestToken, true); + } + } + for (int i = 0; i < 2; i++) { + int connectionType = i == 0 ? ConnectionsManager.ConnectionTypeDownload : ConnectionsManager.ConnectionTypeDownload2; + if (waitingDownloadSize[i] > 512 * 1024 * 2) { + int datacenterId = isCdn ? cdnDatacenterId : this.datacenterId; + ConnectionsManager.getInstance(currentAccount).discardConnection(datacenterId, connectionType); } } requestInfos.clear(); @@ -1916,7 +2064,7 @@ private void clearOperaion(RequestInfo currentInfo, boolean preloadChanged) { if (isPreloadVideoOperation) { requestedPreloadedBytesRanges.remove(info.offset); } else { - removePart(notRequestedBytesRanges, info.offset, info.offset + currentDownloadChunkSize); + removePart(notRequestedBytesRanges, info.offset, info.offset + info.chunkSize); } if (info.response != null) { info.response.disableFree = false; @@ -1957,27 +2105,39 @@ private void requestReference(RequestInfo requestInfo) { FileRefController.getInstance(currentAccount).requestReference(parentObject, location, this, requestInfo); } - protected void startDownloadRequest() { + protected void startDownloadRequest(int useConnectionType) { if (BuildVars.DEBUG_PRIVATE_VERSION) { if (Utilities.stageQueue != null && Utilities.stageQueue.getHandler() != null && Thread.currentThread() != Utilities.stageQueue.getHandler().getLooper().getThread()) { throw new RuntimeException("Wrong thread!!!"); } } + if (BuildVars.LOGS_ENABLED && FULL_LOGS) { + FileLog.d(fileName + " startDownloadRequest"); + } if (paused || reuploadingCdn || state != stateDownloading || requestingReference || - streamPriorityStartOffset == 0 && ( - !nextPartWasPreloaded && (requestInfos.size() + delayedRequestInfos.size() >= currentMaxDownloadRequests) || - isPreloadVideoOperation && (requestedBytesCount > preloadMaxBytes || moovFound != 0 && requestInfos.size() > 0))) { + (!isStory && streamPriorityStartOffset == 0 && (!nextPartWasPreloaded && (requestInfos.size() + delayedRequestInfos.size() >= currentMaxDownloadRequests))) || + (isPreloadVideoOperation && (requestedBytesCount > preloadMaxBytes || moovFound != 0 && requestInfos.size() > 0))) { + if (BuildVars.LOGS_ENABLED && FULL_LOGS) { + FileLog.d(fileName + "can't start request wrong state: paused=" + paused + " reuploadingCdn=" + reuploadingCdn + " state=" + state + " requestingReference=" + requestingReference); + } return; } int count = 1; - if (streamPriorityStartOffset == 0 && !nextPartWasPreloaded && (!isPreloadVideoOperation || moovFound != 0) && totalBytesCount > 0) { + if (isStory) { count = Math.max(0, currentMaxDownloadRequests - requestInfos.size()); + } else { + if (streamPriorityStartOffset == 0 && !nextPartWasPreloaded && (!isPreloadVideoOperation || moovFound != 0) && totalBytesCount > 0) { + count = Math.max(0, currentMaxDownloadRequests - requestInfos.size()); + } } for (int a = 0; a < count; a++) { long downloadOffset; if (isPreloadVideoOperation) { if (moovFound != 0 && preloadNotRequestedBytesCount <= 0) { + if (BuildVars.LOGS_ENABLED && FULL_LOGS) { + FileLog.d(fileName + " can't start request: waiting moov"); + } return; } if (nextPreloadDownloadOffset == -1) { @@ -1999,7 +2159,7 @@ protected void startDownloadRequest() { tries--; } if (!found && requestInfos.isEmpty()) { - onFinishLoadingFile(false, FINISH_CODE_DEFAULT); + onFinishLoadingFile(false, FINISH_CODE_DEFAULT, false); } } else { downloadOffset = nextPreloadDownloadOffset; @@ -2037,22 +2197,45 @@ protected void startDownloadRequest() { } else if (minStart != Long.MAX_VALUE) { downloadOffset = minStart; } else { + if (BuildVars.LOGS_ENABLED && FULL_LOGS) { + FileLog.d(fileName + " can't start request ranges finished" + notRequestedBytesRanges); + } break; } } else { downloadOffset = requestedBytesCount; } } + if (preloadPrefixSize > 0 && downloadOffset >= preloadPrefixSize && canFinishPreload()) { + if (BuildVars.LOGS_ENABLED && FULL_LOGS) { + FileLog.d(fileName + " can't start request: preload finished"); + } + break; + } + if (totalBytesCount > 0 && downloadOffset > 0 && downloadOffset >= totalBytesCount) { + if (BuildVars.LOGS_ENABLED && FULL_LOGS) { + FileLog.d(fileName + " can't start request: loading finished"); + } + break; + } if (!isPreloadVideoOperation && notRequestedBytesRanges != null) { addPart(notRequestedBytesRanges, downloadOffset, downloadOffset + currentDownloadChunkSize, false); + if (BuildVars.LOGS_ENABLED && FULL_LOGS) { + FileLog.d(fileName + " add part " + downloadOffset + " " + (downloadOffset + currentDownloadChunkSize)); + FileLog.d(fileName + " notRequestedBytesRanges=" + notRequestedBytesRanges); + } } - if (totalBytesCount > 0 && downloadOffset >= totalBytesCount) { - break; - } boolean isLast = totalBytesCount <= 0 || a == count - 1 || totalBytesCount > 0 && downloadOffset + currentDownloadChunkSize >= totalBytesCount; final TLObject request; - int connectionType = requestsCount % 2 == 0 ? ConnectionsManager.ConnectionTypeDownload : ConnectionsManager.ConnectionTypeDownload2; + int connectionType; + if (useConnectionType == -1) { + connectionType = requestsCount % 2 == 0 ? ConnectionsManager.ConnectionTypeDownload : ConnectionsManager.ConnectionTypeDownload2; + //globalRequestPointer++; + } else { + connectionType = useConnectionType; + } + int flags = (isForceRequest ? ConnectionsManager.RequestFlagForceDownload : 0); if (isCdn) { TLRPC.TL_upload_getCdnFile req = new TLRPC.TL_upload_getCdnFile(); @@ -2081,7 +2264,9 @@ protected void startDownloadRequest() { final RequestInfo requestInfo = new RequestInfo(); requestInfos.add(requestInfo); requestInfo.offset = downloadOffset; + requestInfo.chunkSize = currentDownloadChunkSize; requestInfo.forceSmallChunk = forceSmallChunk; + requestInfo.connectionType = connectionType; if (!isPreloadVideoOperation && supportsPreloading && preloadStream != null && preloadedBytesRanges != null) { PreloadRange range = preloadedBytesRanges.get(requestInfo.offset); @@ -2140,14 +2325,19 @@ protected void startDownloadRequest() { priorityRequestInfo = null; } if (error != null) { - if (FileRefController.isFileRefError(error.text)) { + if (error.code == -2000) { + requestInfos.remove(requestInfo); + requestedBytesCount -= requestInfo.chunkSize; + removePart(notRequestedBytesRanges, requestInfo.offset, requestInfo.offset + requestInfo.chunkSize); + return; + } else if (FileRefController.isFileRefError(error.text)) { requestReference(requestInfo); return; } else if (request instanceof TLRPC.TL_upload_getCdnFile) { if (error.text.equals("FILE_TOKEN_INVALID")) { isCdn = false; clearOperaion(requestInfo, false); - startDownloadRequest(); + startDownloadRequest(connectionType); return; } } @@ -2179,7 +2369,7 @@ protected void startDownloadRequest() { cdnKey = res.encryption_key; cdnToken = res.file_token; clearOperaion(requestInfo, false); - startDownloadRequest(); + startDownloadRequest(connectionType); } } else if (response instanceof TLRPC.TL_upload_cdnFileReuploadNeeded) { if (!reuploadingCdn) { @@ -2202,12 +2392,12 @@ protected void startDownloadRequest() { cdnHashes.put(hash.offset, hash); } } - startDownloadRequest(); + startDownloadRequest(connectionType); } else { if (error1.text.equals("FILE_TOKEN_INVALID") || error1.text.equals("REQUEST_TOKEN_INVALID")) { isCdn = false; clearOperaion(requestInfo, false); - startDownloadRequest(); + startDownloadRequest(connectionType); } else { onFail(false, 0); } @@ -2244,7 +2434,7 @@ protected void startDownloadRequest() { } }, null, null, flags, datacenterId, connectionType, isLast); if (BuildVars.LOGS_ENABLED) { - FileLog.d("debug_loading: " + cacheFileFinal.getName() + " send reqId " + requestInfo.requestToken); + FileLog.d("debug_loading: " + cacheFileFinal.getName() + " dc=" + datacenterId + " send reqId " + requestInfo.requestToken + " offset=" + requestInfo.offset + " conType=" + connectionType + " priority="); } requestsCount++; } @@ -2253,4 +2443,17 @@ protected void startDownloadRequest() { public void setDelegate(FileLoadOperationDelegate delegate) { this.delegate = delegate; } + + public static long floorDiv(long x, long y) { + long r = x / y; + // if the signs are different and modulo not zero, round down + if ((x ^ y) < 0 && (r * y != x)) { + r--; + } + return r; + } + + public boolean isFinished() { + return state == stateFinished; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java index c4de350800..3939d9b461 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java @@ -11,6 +11,8 @@ import android.text.TextUtils; import android.util.SparseArray; +import com.google.android.exoplayer2.util.Log; + import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; import org.telegram.ui.LaunchActivity; @@ -70,6 +72,12 @@ public static FilePathDatabase.FileMeta getFileMetadataFromParent(int currentAcc fileMeta.messageType = messageObject.type; fileMeta.messageSize = messageObject.getSize(); return fileMeta; + } else if (parentObject instanceof TLRPC.StoryItem) { + TLRPC.StoryItem storyItem = (TLRPC.StoryItem) parentObject; + FilePathDatabase.FileMeta fileMeta = new FilePathDatabase.FileMeta(); + fileMeta.dialogId = storyItem.dialogId; + fileMeta.messageType = MessageObject.TYPE_STORY; + return fileMeta; } return null; } @@ -101,12 +109,12 @@ private int getPriorityValue(int priorityType) { return Integer.MAX_VALUE; } else if (priorityType == PRIORITY_HIGH) { priorityIncreasePointer++; - return (1 << 20) + priorityIncreasePointer; + return FileLoaderPriorityQueue.PRIORITY_VALUE_MAX + priorityIncreasePointer; } else if (priorityType == PRIORITY_NORMAL_UP) { priorityIncreasePointer++; - return (1 << 16) + priorityIncreasePointer; + return FileLoaderPriorityQueue.PRIORITY_VALUE_NORMAL + priorityIncreasePointer; } else if (priorityType == PRIORITY_NORMAL) { - return 1 << 16; + return FileLoaderPriorityQueue.PRIORITY_VALUE_NORMAL; } else { return 0; } @@ -137,6 +145,7 @@ public interface FileLoaderDelegate { public static final int MEDIA_DIR_DOCUMENT = 3; public static final int MEDIA_DIR_CACHE = 4; public static final int MEDIA_DIR_FILES = 5; + public static final int MEDIA_DIR_STORIES = 6; public static final int MEDIA_DIR_IMAGE_PUBLIC = 100; public static final int MEDIA_DIR_VIDEO_PUBLIC = 101; @@ -214,8 +223,8 @@ public FileLoader(int instance) { super(instance); filePathDatabase = new FilePathDatabase(instance); for (int i = 0; i < smallFilesQueue.length; i++) { - smallFilesQueue[i] = new FileLoaderPriorityQueue("smallFilesQueue dc" + (i + 1), 5); - largeFilesQueue[i] = new FileLoaderPriorityQueue("largeFilesQueue dc" + (i + 1), 1); + smallFilesQueue[i] = new FileLoaderPriorityQueue(instance, "smallFilesQueue dc" + (i + 1), FileLoaderPriorityQueue.TYPE_SMALL, fileLoaderQueue); + largeFilesQueue[i] = new FileLoaderPriorityQueue(instance, "largeFilesQueue dc" + (i + 1), FileLoaderPriorityQueue.TYPE_LARGE, fileLoaderQueue); } dumpFilesQueue(); } @@ -239,7 +248,7 @@ public static File getDirectory(int type) { dir.mkdirs(); } } catch (Exception e) { - //don't promt + //don't prompt } } return dir; @@ -329,6 +338,10 @@ public void cancelFileUpload(final String location, final boolean enc) { } public void checkUploadNewDataAvailable(final String location, final boolean encrypted, final long newAvailableSize, final long finalSize) { + checkUploadNewDataAvailable(location, encrypted, newAvailableSize, finalSize, null); + } + + public void checkUploadNewDataAvailable(final String location, final boolean encrypted, final long newAvailableSize, final long finalSize, final Float progress) { fileLoaderQueue.postRunnable(() -> { FileUploadOperation operation; if (encrypted) { @@ -337,7 +350,7 @@ public void checkUploadNewDataAvailable(final String location, final boolean enc operation = uploadOperationPaths.get(location); } if (operation != null) { - operation.checkNewDataAvailable(newAvailableSize, finalSize); + operation.checkNewDataAvailable(newAvailableSize, finalSize, progress); } else if (finalSize != 0) { uploadSizes.put(location, finalSize); } @@ -498,6 +511,7 @@ public void setForceStreamLoadingFile(TLRPC.FileLocation location, String ext) { } operation.setForceRequest(true); operation.setPriority(getPriorityValue(PRIORITY_STREAM)); + operation.getQueue().remove(operation); operation.getQueue().add(operation); operation.getQueue().checkLoadingOperations(); } @@ -582,6 +596,39 @@ private void cancelLoadFile(final TLRPC.Document document, final SecureDocument } } + public void changePriority(int priority, final TLRPC.Document document, final SecureDocument secureDocument, final WebFile webDocument, final TLRPC.FileLocation location, final String locationExt, String name) { + if (location == null && document == null && webDocument == null && secureDocument == null && TextUtils.isEmpty(name)) { + return; + } + final String fileName; + if (location != null) { + fileName = getAttachFileName(location, locationExt); + } else if (document != null) { + fileName = getAttachFileName(document); + } else if (secureDocument != null) { + fileName = getAttachFileName(secureDocument); + } else if (webDocument != null) { + fileName = getAttachFileName(webDocument); + } else { + fileName = name; + } + fileLoaderQueue.postRunnable(() -> { + FileLoadOperation operation = loadOperationPaths.get(fileName); + if (operation != null) { + int newPriority = getPriorityValue(priority); + if (operation.getPriority() == newPriority) { + return; + } + operation.setPriority(newPriority); + FileLoaderPriorityQueue queue = operation.getQueue(); + queue.remove(operation); + queue.add(operation); + queue.checkLoadingOperations(); + FileLog.d("update priority " + fileName + " position in queue " + operation.getPositionInQueue() + " account=" + currentAccount); + } + }); + } + public void cancelLoadAllFiles() { for (String fileName : loadOperationPathsUI.keySet()) { @@ -670,6 +717,10 @@ private FileLoadOperation loadFileInternal(final TLRPC.Document document, final if (fileName == null || fileName.contains("" + Integer.MIN_VALUE)) { return null; } + if (fileName.startsWith("0_0")) { + FileLog.e(new RuntimeException("cant get hash from " + document)); + return null; + } if (cacheType != 10 && !TextUtils.isEmpty(fileName) && !fileName.contains("" + Integer.MIN_VALUE)) { loadOperationPathsUI.put(fileName, new LoadOperationUIObject()); } @@ -688,12 +739,18 @@ private FileLoadOperation loadFileInternal(final TLRPC.Document document, final operation.setIsPreloadVideoOperation(false); } operation.setForceRequest(priority > 0); - operation.setPriority(priority); operation.setStream(stream, streamPriority, streamOffset); + boolean priorityChanged = false; + if (operation.getPriority() != priority) { + priorityChanged = true; + operation.setPriority(priority); + } operation.getQueue().add(operation); operation.updateProgress(); - operation.getQueue().checkLoadingOperations(); - FileLog.d("load operation update position fileName=" + finalFileName + " position in queue " + operation.getPositionInQueue() + " account=" + currentAccount); + if (priorityChanged) { + operation.getQueue().checkLoadingOperations(); + } + FileLog.d("load operation update position fileName=" + finalFileName + " position in queue " + operation.getPositionInQueue() + " preloadFinish " + operation.isPreloadFinished()); return operation; } @@ -708,7 +765,7 @@ private FileLoadOperation loadFileInternal(final TLRPC.Document document, final type = MEDIA_DIR_DOCUMENT; } else if (location != null) { documentId = location.volume_id; - dcId = location.dc_id; + dcId = location.dc_id + (location.local_id << 16); operation = new FileLoadOperation(imageLocation, parentObject, locationExt, locationSize); type = MEDIA_DIR_IMAGE; } else if (document != null) { @@ -748,7 +805,8 @@ private FileLoadOperation loadFileInternal(final TLRPC.Document document, final } FileLoaderPriorityQueue loaderQueue; int index = Utilities.clamp(operation.getDatacenterId() - 1, 4, 0); - if (operation.totalBytesCount > 20 * 1024 * 1024) {//20mb + boolean isStory = parentObject instanceof TLRPC.StoryItem; + if (operation.totalBytesCount > 20 * 1024 * 1024 || isStory) { loaderQueue = largeFilesQueue[index]; } else { loaderQueue = smallFilesQueue[index]; @@ -756,7 +814,7 @@ private FileLoadOperation loadFileInternal(final TLRPC.Document document, final String storeFileName = fileName; - if (cacheType == 0 || cacheType == 10) { + if (cacheType == 0 || cacheType == 10 || isStory) { if (documentId != 0) { String path = getFileDatabase().getPath(documentId, dcId, type, true); boolean customPath = false; @@ -773,7 +831,13 @@ private FileLoadOperation loadFileInternal(final TLRPC.Document document, final storeDir = getDirectory(type); boolean saveCustomPath = false; - if ((type == MEDIA_DIR_IMAGE || type == MEDIA_DIR_VIDEO) && canSaveToPublicStorage(parentObject)) { + if (isStory) { + File newDir = getDirectory(MEDIA_DIR_STORIES); + if (newDir != null) { + storeDir = newDir; + saveCustomPath = true; + } + } else if ((type == MEDIA_DIR_IMAGE || type == MEDIA_DIR_VIDEO) && canSaveToPublicStorage(parentObject)) { File newDir; if (type == MEDIA_DIR_IMAGE) { newDir = getDirectory(MEDIA_DIR_IMAGE_PUBLIC); @@ -816,18 +880,15 @@ private FileLoadOperation loadFileInternal(final TLRPC.Document document, final public void didPreFinishLoading(FileLoadOperation operation, File finalFile) { FileLoaderPriorityQueue queue = operation.getQueue(); fileLoaderQueue.postRunnable(() -> { - FileLoadOperation currentOperation = loadOperationPaths.get(fileName); - if (currentOperation != null) { - currentOperation.preFinished = true; - queue.checkLoadingOperations(); - } + operation.preFinished = true; + queue.checkLoadingOperations(); }); - checkDownloadQueue(operation.getQueue(), fileName); } @Override public void didFinishLoadingFile(FileLoadOperation operation, File finalFile) { if (!operation.isPreloadVideoOperation() && operation.isPreloadFinished()) { + checkDownloadQueue(operation, operation.getQueue(), 0); return; } FilePathDatabase.FileMeta fileMeta = getFileMetadataFromParent(currentAccount, parentObject); @@ -848,13 +909,13 @@ public void didFinishLoadingFile(FileLoadOperation operation, File finalFile) { } } - checkDownloadQueue(operation.getQueue(), fileName); + checkDownloadQueue(operation, operation.getQueue(), 0); } @Override public void didFailedLoadingFile(FileLoadOperation operation, int reason) { loadOperationPathsUI.remove(fileName); - checkDownloadQueue(operation.getQueue(), fileName); + checkDownloadQueue(operation, operation.getQueue()); if (getDelegate() != null) { delegate.fileDidFailedLoad(fileName, reason); } @@ -887,10 +948,12 @@ public boolean hasAnotherRefOnFile(String path) { loadOperationPaths.put(finalFileName, operation); operation.setPriority(priority); - operation.setStream(stream, streamPriority, streamOffset); + if (stream != null) { + operation.setStream(stream, streamPriority, streamOffset); + } loaderQueue.add(operation); - loaderQueue.checkLoadingOperations(); + loaderQueue.checkLoadingOperations(operation.isStory && priority >= PRIORITY_HIGH); if (BuildVars.LOGS_ENABLED) { FileLog.d("create load operation fileName=" + finalFileName + " documentName=" + getDocumentFileName(document) + "size=" + AndroidUtilities.formatFileSize(operation.totalBytesCount) + " position in queue " + operation.getPositionInQueue() + " account=" + currentAccount); @@ -992,20 +1055,36 @@ protected FileLoadOperation loadStreamFile(final FileLoadOperationStream stream, result[0] = loadFileInternal(document, null, null, document == null && location != null ? location.location : null, location, parentObject, document == null && location != null ? "mp4" : null, document == null && location != null ? location.currentSize : 0, loadingPriority, stream, offset, priority, document == null ? 1 : 0); semaphore.countDown(); }); + awaitFileLoadOperation(semaphore, true); + return result[0]; + } + + /** + * Necessary to wait of the FileLoadOperation object, despite the interruption of the thread. + * Thread can be interrupted by {@link ImageLoader.CacheOutTask#cancel}. + * For cases when two {@link ImageReceiver} require loading of the same file and the first {@link ImageReceiver} decides to cancel the operation. + * For example, to autoplay a video after sending a message. + */ + private void awaitFileLoadOperation(CountDownLatch latch, boolean ignoreInterruption) { try { - semaphore.await(); + latch.await(); } catch (Exception e) { FileLog.e(e, false); + if (ignoreInterruption) awaitFileLoadOperation(latch, false); } - return result[0]; } - private void checkDownloadQueue(FileLoaderPriorityQueue queue, String fileName) { + private void checkDownloadQueue(FileLoadOperation operation, FileLoaderPriorityQueue queue) { + checkDownloadQueue(operation, queue, 0); + } + + private void checkDownloadQueue(FileLoadOperation operation, FileLoaderPriorityQueue queue, long delay) { fileLoaderQueue.postRunnable(() -> { - FileLoadOperation operation = loadOperationPaths.remove(fileName); - queue.remove(operation); - queue.checkLoadingOperations(); - }); + if (queue.remove(operation)) { + loadOperationPaths.remove(operation.getFileName()); + queue.checkLoadingOperations(); + } + }, delay); } public static String getMessageFileName(TLRPC.Message message) { @@ -1164,8 +1243,7 @@ public File getPathToAttach(TLObject attach, String size, String ext, boolean fo dir = getDirectory(type = MEDIA_DIR_IMAGE); } documentId = photoSize.location.volume_id; - dcId = photoSize.location.dc_id; - + dcId = photoSize.location.dc_id + (photoSize.location.local_id << 16); } else if (attach instanceof TLRPC.TL_videoSize) { TLRPC.TL_videoSize videoSize = (TLRPC.TL_videoSize) attach; if (videoSize.location == null || videoSize.location.key != null || videoSize.location.volume_id == Integer.MIN_VALUE && videoSize.location.local_id < 0 || videoSize.size < 0) { @@ -1174,14 +1252,14 @@ public File getPathToAttach(TLObject attach, String size, String ext, boolean fo dir = getDirectory(type = MEDIA_DIR_IMAGE); } documentId = videoSize.location.volume_id; - dcId = videoSize.location.dc_id; + dcId = videoSize.location.dc_id + (videoSize.location.local_id << 16); } else if (attach instanceof TLRPC.FileLocation) { TLRPC.FileLocation fileLocation = (TLRPC.FileLocation) attach; if (fileLocation.key != null || fileLocation.volume_id == Integer.MIN_VALUE && fileLocation.local_id < 0) { dir = getDirectory(MEDIA_DIR_CACHE); } else { documentId = fileLocation.volume_id; - dcId = fileLocation.dc_id; + dcId = fileLocation.dc_id + (fileLocation.local_id << 16); dir = getDirectory(type = MEDIA_DIR_IMAGE); } } else if (attach instanceof TLRPC.UserProfilePhoto || attach instanceof TLRPC.ChatPhoto) { @@ -1709,6 +1787,9 @@ public static long bytesToLong(byte[] bytes) { for (int i = 0; i < smallFilesQueue.length; i++) { if (smallFilesQueue[i].getCount() > 0 || largeFilesQueue[i].getCount() > 0) { FileLog.d("download queue: dc" + (i + 1) + " account=" + currentAccount + " small_operations=" + smallFilesQueue[i].getCount() + " large_operations=" + largeFilesQueue[i].getCount()); +// if (!largeFilesQueue[i].allOperations.isEmpty()) { +// largeFilesQueue[i].allOperations.get(0).dump(); +// } } } dumpFilesQueue(); @@ -1719,6 +1800,6 @@ public void dumpFilesQueue() { return; } fileLoaderQueue.cancelRunnable(dumpFilesQueueRunnable); - fileLoaderQueue.postRunnable(dumpFilesQueueRunnable, 10_000); + fileLoaderQueue.postRunnable(dumpFilesQueueRunnable, 10000); } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoaderPriorityQueue.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoaderPriorityQueue.java index 5cacee6a27..820b2c4e44 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoaderPriorityQueue.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoaderPriorityQueue.java @@ -1,21 +1,38 @@ package org.telegram.messenger; +import com.google.android.exoplayer2.util.Log; + import java.util.ArrayList; public class FileLoaderPriorityQueue { - private final int maxActiveOperationsCount; + public static final int TYPE_SMALL = 0; + public static final int TYPE_LARGE = 1; String name; + int type; + int currentAccount; + + public ArrayList allOperations = new ArrayList<>(); + public ArrayList tmpListOperations = new ArrayList<>(); + + public final static int PRIORITY_VALUE_MAX = (1 << 20); + public final static int PRIORITY_VALUE_NORMAL = (1 << 16); + public final static int PRIORITY_VALUE_LOW = 0; + + final DispatchQueue workerQueue; - private ArrayList allOperations = new ArrayList<>(); + boolean checkOperationsScheduled = false; - private int PRIORITY_VALUE_MAX = (1 << 20); - private int PRIORITY_VALUE_NORMAL = (1 << 16); - private int PRIORITY_VALUE_LOW = 0; + Runnable checkOperationsRunnable = () -> { + checkLoadingOperationInternal(); + checkOperationsScheduled = false; + }; - FileLoaderPriorityQueue(String name, int maxActiveOperationsCount) { + FileLoaderPriorityQueue(int currentAccount, String name, int type, DispatchQueue workerQueue) { + this.currentAccount = currentAccount; this.name = name; - this.maxActiveOperationsCount = maxActiveOperationsCount; + this.type = type; + this.workerQueue = workerQueue; } public void add(FileLoadOperation operation) { @@ -52,13 +69,38 @@ public void cancel(FileLoadOperation operation) { } public void checkLoadingOperations() { + checkLoadingOperations(false); + } + + public void checkLoadingOperations(boolean immediate) { + if (immediate) { + workerQueue.cancelRunnable(checkOperationsRunnable); + checkOperationsRunnable.run(); + return; + } + if (checkOperationsScheduled) { + return; + } + checkOperationsScheduled = true; + workerQueue.cancelRunnable(checkOperationsRunnable); + workerQueue.postRunnable(checkOperationsRunnable, 20); + } + + private void checkLoadingOperationInternal() { int activeCount = 0; int lastPriority = 0; boolean pauseAllNextOperations = false; - int max = maxActiveOperationsCount; + int max = type == TYPE_LARGE ? MessagesController.getInstance(currentAccount).largeQueueMaxActiveOperations : MessagesController.getInstance(currentAccount).smallQueueMaxActiveOperations; + tmpListOperations.clear(); for (int i = 0; i < allOperations.size(); i++) { + FileLoadOperation prevOperation = i > 0 ? allOperations.get(i - 1) : null; FileLoadOperation operation = allOperations.get(i); if (i > 0 && !pauseAllNextOperations) { + if (type == TYPE_LARGE) { + if (prevOperation != null && prevOperation.isStory && prevOperation.getPriority() >= PRIORITY_VALUE_MAX) { + pauseAllNextOperations = true; + } + } if (lastPriority > PRIORITY_VALUE_LOW && operation.getPriority() == PRIORITY_VALUE_LOW) { pauseAllNextOperations = true; } @@ -67,8 +109,9 @@ public void checkLoadingOperations() { //operation will not use connections //just skip max++; + continue; } else if (!pauseAllNextOperations && i < max) { - operation.start(); + tmpListOperations.add(operation); activeCount++; } else { if (operation.wasStarted()) { @@ -77,13 +120,16 @@ public void checkLoadingOperations() { } lastPriority = operation.getPriority(); } + for (int i = 0; i < tmpListOperations.size(); i++) { + tmpListOperations.get(i).start(); + } } - public void remove(FileLoadOperation operation) { + public boolean remove(FileLoadOperation operation) { if (operation == null) { - return; + return false; } - allOperations.remove(operation); + return allOperations.remove(operation); } public int getCount() { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileLog.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileLog.java index 6b0890cc85..cf3ab78e6f 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileLog.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileLog.java @@ -21,6 +21,7 @@ import org.telegram.messenger.video.MediaCodecVideoConvertor; import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; +import org.telegram.ui.Components.AnimatedFileDrawable; import org.telegram.ui.LaunchActivity; import java.io.File; @@ -36,6 +37,7 @@ public class FileLog { private OutputStreamWriter streamWriter = null; private FastDateFormat dateFormat = null; + private FastDateFormat fileDateFormat = null; private DispatchQueue logQueue = null; private File currentFile = null; @@ -77,7 +79,7 @@ public FileLog() { private static HashSet excludeRequests; public static void dumpResponseAndRequest(TLObject request, TLObject response, TLRPC.TL_error error, long requestMsgId, long startRequestTimeInMillis, int requestToken) { - if (!BuildVars.DEBUG_PRIVATE_VERSION || !BuildVars.LOGS_ENABLED || request == null || SharedConfig.getDevicePerformanceClass() == SharedConfig.PERFORMANCE_CLASS_LOW) { + if (!BuildVars.DEBUG_PRIVATE_VERSION || !BuildVars.LOGS_ENABLED || request == null) { return; } String requestSimpleName = request.getClass().getSimpleName(); @@ -121,7 +123,7 @@ public static void dumpResponseAndRequest(TLObject request, TLObject response, T } public static void dumpUnparsedMessage(TLObject message, long messageId) { - if (!BuildVars.DEBUG_PRIVATE_VERSION || !BuildVars.LOGS_ENABLED || message == null || SharedConfig.getDevicePerformanceClass() == SharedConfig.PERFORMANCE_CLASS_LOW) { + if (!BuildVars.DEBUG_PRIVATE_VERSION || !BuildVars.LOGS_ENABLED || message == null) { return; } try { @@ -166,13 +168,14 @@ private static void checkGson() { privateFields.add("networkType"); privateFields.add("disableFree"); privateFields.add("mContext"); + privateFields.add("priority"); //exclude file loading excludeRequests = new HashSet<>(); excludeRequests.add("TL_upload_getFile"); - excludeRequests.add("TL_upload_getWebFile"); + excludeRequests.add("TL_upload_a"); - gson = new GsonBuilder().addSerializationExclusionStrategy(new ExclusionStrategy() { + ExclusionStrategy strategy = new ExclusionStrategy() { @Override public boolean shouldSkipField(FieldAttributes f) { @@ -184,12 +187,10 @@ public boolean shouldSkipField(FieldAttributes f) { @Override public boolean shouldSkipClass(Class clazz) { - if (clazz.isInstance(ColorStateList.class) || clazz.isInstance(Context.class)) { - return true; - } - return false; + return clazz.isInstance(DispatchQueue.class) || clazz.isInstance(AnimatedFileDrawable.class) || clazz.isInstance(ColorStateList.class) || clazz.isInstance(Context.class); } - }).create(); + }; + gson = new GsonBuilder().addSerializationExclusionStrategy(strategy).registerTypeAdapterFactory(RuntimeClassNameTypeAdapterFactory.of(TLObject.class, "type_", strategy)).create(); } } @@ -198,8 +199,9 @@ public void init() { if (initied) { return; } - dateFormat = FastDateFormat.getInstance("yyyy_MM_dd-HH_mm_ss", Locale.US); - String date = dateFormat.format(System.currentTimeMillis()); + dateFormat = FastDateFormat.getInstance("yyyy_MM_dd-HH_mm_ss.SSS", Locale.US); + fileDateFormat = FastDateFormat.getInstance("yyyy_MM_dd-HH_mm_ss", Locale.US); + String date = fileDateFormat.format(System.currentTimeMillis()); try { File dir = AndroidUtilities.getLogsDir(); if (dir == null) { @@ -241,7 +243,7 @@ public static String getNetworkLogPath() { if (dir == null) { return ""; } - getInstance().networkFile = new File(dir, getInstance().dateFormat.format(System.currentTimeMillis()) + "_net.txt"); + getInstance().networkFile = new File(dir, getInstance().fileDateFormat.format(System.currentTimeMillis()) + "_net.txt"); return getInstance().networkFile.getAbsolutePath(); } catch (Throwable e) { e.printStackTrace(); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FilePathDatabase.java b/TMessagesProj/src/main/java/org/telegram/messenger/FilePathDatabase.java index 56fbd728dc..c09f367f1a 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FilePathDatabase.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FilePathDatabase.java @@ -442,6 +442,7 @@ public LongSparseArray> lookupFi list = new ArrayList<>(); filesByDialogId.put(fileMeta.dialogId, list); } + keepMediaFiles.get(i).isStory = fileMeta.messageType == MessageObject.TYPE_STORY; list.add(keepMediaFiles.get(i)); } } @@ -469,6 +470,7 @@ private void ensureQueueExist() { synchronized (this) { if (dispatchQueue == null) { dispatchQueue = new DispatchQueue("files_database_queue_" + currentAccount); + dispatchQueue.setPriority(Thread.MAX_PRIORITY); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileRefController.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileRefController.java index 85bea974b2..0c3c5c7534 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileRefController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileRefController.java @@ -3,6 +3,7 @@ import android.os.SystemClock; import android.util.SparseArray; +import org.checkerframework.checker.units.qual.A; import org.telegram.tgnet.RequestDelegate; import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; @@ -10,6 +11,7 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; public class FileRefController extends BaseController { @@ -69,7 +71,14 @@ public FileRefController(int instance) { } public static String getKeyForParentObject(Object parentObject) { - if (parentObject instanceof TLRPC.TL_help_premiumPromo) { + if (parentObject instanceof TLRPC.StoryItem) { + TLRPC.StoryItem storyItem = (TLRPC.StoryItem) parentObject; + if (storyItem.dialogId == 0) { + FileLog.d("failed request reference can't find dialogId"); + return null; + } + return "story_" + storyItem.dialogId + "_" + storyItem.id; + } else if (parentObject instanceof TLRPC.TL_help_premiumPromo) { return "premium_promo"; } else if (parentObject instanceof TLRPC.TL_availableReaction) { return "available_reaction_" + ((TLRPC.TL_availableReaction) parentObject).reaction; @@ -126,7 +135,12 @@ public void requestReference(Object parentObject, Object... args) { if (BuildVars.LOGS_ENABLED) { FileLog.d("start loading request reference parent " + getObjectString(parentObject) + " args = " + args[0]); } - if (args[0] instanceof TLRPC.TL_inputSingleMedia) { + if (args[0] instanceof TLRPC.TL_storyItem) { + TLRPC.TL_storyItem storyItem = (TLRPC.TL_storyItem) args[0]; + locationKey = "story_" + storyItem.id; + location = new TLRPC.TL_inputDocumentFileLocation(); + location.id = storyItem.media.document.id; + } else if (args[0] instanceof TLRPC.TL_inputSingleMedia) { TLRPC.TL_inputSingleMedia req = (TLRPC.TL_inputSingleMedia) args[0]; if (req.media instanceof TLRPC.TL_inputMediaDocument) { TLRPC.TL_inputMediaDocument mediaDocument = (TLRPC.TL_inputMediaDocument) req.media; @@ -313,6 +327,10 @@ private String getObjectString(Object parentObject) { if (parentObject instanceof String) { return (String) parentObject; } + if (parentObject instanceof TLRPC.StoryItem) { + TLRPC.StoryItem storyItem = (TLRPC.StoryItem) parentObject; + return "story(dialogId=" + storyItem.dialogId + " id=" + storyItem.id + ")"; + } if (parentObject instanceof MessageObject) { MessageObject messageObject = (MessageObject) parentObject; return "message(dialogId=" + messageObject.getDialogId() + "messageId" + messageObject.getId() + ")"; @@ -332,7 +350,15 @@ private void broadcastWaitersData(ArrayList waiters, TLObject response, } private void requestReferenceFromServer(Object parentObject, String locationKey, String parentKey, Object[] args) { - if (parentObject instanceof TLRPC.TL_help_premiumPromo) { + if (parentObject instanceof TLRPC.StoryItem) { + TLRPC.StoryItem storyItem = (TLRPC.StoryItem) parentObject; + TLRPC.TL_stories_getStoriesByID req = new TLRPC.TL_stories_getStoriesByID(); + req.user_id = getMessagesController().getInputUser(storyItem.dialogId); + req.id.add(storyItem.id); + getConnectionsManager().sendRequest(req, (response, error) -> { + onRequestComplete(locationKey, parentKey, response, error, true, false); + }); + } else if (parentObject instanceof TLRPC.TL_help_premiumPromo) { TLRPC.TL_help_getPremiumPromo req = new TLRPC.TL_help_getPremiumPromo(); getConnectionsManager().sendRequest(req, (response, error) -> { int date = (int) (System.currentTimeMillis() / 1000); @@ -521,7 +547,11 @@ private boolean onUpdateObjectReference(Requester requester, byte[] file_referen if (BuildVars.DEBUG_VERSION) { FileLog.d("fileref updated for " + requester.args[0] + " " + requester.locationKey); } - if (requester.args[0] instanceof TLRPC.TL_inputSingleMedia) { + if (requester.args[0] instanceof TLRPC.TL_storyItem) { + TLRPC.TL_storyItem storyItem = (TLRPC.TL_storyItem) requester.args[0]; + storyItem.media.document.file_reference = file_reference; + return true; + } else if (requester.args[0] instanceof TLRPC.TL_inputSingleMedia) { TLRPC.TL_messages_sendMultiMedia multiMedia = (TLRPC.TL_messages_sendMultiMedia) requester.args[1]; Object[] objects = multiMediaCache.get(multiMedia); if (objects == null) { @@ -666,7 +696,7 @@ private boolean onUpdateObjectReference(Requester requester, byte[] file_referen if (BuildVars.LOGS_ENABLED) { FileLog.d("debug_loading: " + fileLoadOperation.getCacheFileFinal().getName() + " " + oldRef + " " + newRef + " reference updated resume download"); } - fileLoadOperation.startDownloadRequest(); + fileLoadOperation.startDownloadRequest(-1); } return true; } @@ -698,7 +728,7 @@ private void sendErrorToObject(Object[] args, int reason) { if (args[1] instanceof FileLoadOperation) { FileLoadOperation fileLoadOperation = (FileLoadOperation) args[1]; fileLoadOperation.requestingReference = false; - FileLog.e("debug_loading: " + fileLoadOperation.getCacheFileFinal().getName() + "reference can't update: fail operation "); + FileLog.e("debug_loading: " + fileLoadOperation.getCacheFileFinal().getName() + " reference can't update: fail operation "); fileLoadOperation.onFail(false, 0); } } @@ -1013,6 +1043,41 @@ private boolean onRequestComplete(String locationKey, String parentKey, TLObject break; } } + } else if (response instanceof TLRPC.TL_stories_stories) { + TLRPC.TL_stories_stories stories = (TLRPC.TL_stories_stories) response; + TLRPC.StoryItem newStoryItem = null; + if (!stories.stories.isEmpty()) { + if (stories.stories.get(0).media != null) { + newStoryItem = stories.stories.get(0); + if (stories.stories.get(0).media.photo != null) { + result = getFileReference(stories.stories.get(0).media.photo, requester.location, needReplacement, locationReplacement); + } + if (stories.stories.get(0).media.document != null) { + result = getFileReference(stories.stories.get(0).media.document, requester.location, needReplacement, locationReplacement); + } + } + } + Object arg = requester.args[1]; + if (arg instanceof FileLoadOperation) { + FileLoadOperation operation = (FileLoadOperation) requester.args[1]; + if (operation.parentObject instanceof TLRPC.StoryItem) { + TLRPC.StoryItem storyItem = (TLRPC.StoryItem) operation.parentObject; + if (newStoryItem == null) { + TLRPC.TL_updateStory story = new TLRPC.TL_updateStory(); + story.user_id = storyItem.dialogId; + story.story = new TLRPC.TL_storyItemDeleted(); + story.story.id = storyItem.id; + ArrayList updates = new ArrayList<>(); + updates.add(story); + getMessagesController().processUpdateArray(updates, null, null, false, 0); + } else { + TLRPC.User user = getMessagesController().getUser(storyItem.dialogId); + if (user != null && user.contact) { + MessagesController.getInstance(currentAccount).getStoriesController().getStoriesStorage().updateStoryItem(storyItem.dialogId, newStoryItem); + } + } + } + } } if (result != null) { if (onUpdateObjectReference(requester, result, locationReplacement != null ? locationReplacement[0] : null, fromCache)) { @@ -1236,7 +1301,11 @@ private byte[] getFileReference(TLRPC.WebPage webpage, TLRPC.InputFileLocation l } if (!webpage.attributes.isEmpty()) { for (int a = 0, size1 = webpage.attributes.size(); a < size1; a++) { - TLRPC.TL_webPageAttributeTheme attribute = webpage.attributes.get(a); + TLRPC.WebPageAttribute attribute_ = webpage.attributes.get(a); + if (!(attribute_ instanceof TLRPC.TL_webPageAttributeTheme)) { + continue; + } + TLRPC.TL_webPageAttributeTheme attribute = (TLRPC.TL_webPageAttributeTheme) attribute_; for (int b = 0, size2 = attribute.documents.size(); b < size2; b++) { result = getFileReference(attribute.documents.get(b), location, needReplacement, replacement); if (result != null) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileStreamLoadOperation.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileStreamLoadOperation.java index 1b339c5e6c..18f019fc4a 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileStreamLoadOperation.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileStreamLoadOperation.java @@ -9,18 +9,23 @@ package org.telegram.messenger; import android.net.Uri; + import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.upstream.BaseDataSource; import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.TransferListener; +import com.google.android.exoplayer2.util.Log; import org.telegram.tgnet.TLRPC; import java.io.EOFException; +import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; public class FileStreamLoadOperation extends BaseDataSource implements FileLoadOperationStream { @@ -36,6 +41,9 @@ public class FileStreamLoadOperation extends BaseDataSource implements FileLoadO private TLRPC.Document document; private Object parentObject; private int currentAccount; + File currentFile; + + private static final ConcurrentHashMap priorityMap = new ConcurrentHashMap<>(); public FileStreamLoadOperation() { super(/* isNetwork= */ false); @@ -49,9 +57,21 @@ public FileStreamLoadOperation(@Nullable TransferListener listener) { } } + public static int getStreamPrioriy(TLRPC.Document document) { + if (document == null) { + return FileLoader.PRIORITY_HIGH; + } + Integer integer = priorityMap.get(document.id); + if (integer == null) { + return FileLoader.PRIORITY_HIGH; + } + return integer; + } + @Override public long open(DataSpec dataSpec) throws IOException { uri = dataSpec.uri; + transferInitializing(dataSpec); currentAccount = Utilities.parseInt(uri.getQueryParameter("account")); parentObject = FileLoader.getInstance(currentAccount).getParentObject(Utilities.parseInt(uri.getQueryParameter("rid"))); document = new TLRPC.TL_document(); @@ -69,7 +89,7 @@ public long open(DataSpec dataSpec) throws IOException { } else if (document.mime_type.startsWith("audio")) { document.attributes.add(new TLRPC.TL_documentAttributeAudio()); } - loadOperation = FileLoader.getInstance(currentAccount).loadStreamFile(this, document, null, parentObject, currentOffset = dataSpec.position, false, FileLoader.PRIORITY_HIGH); + loadOperation = FileLoader.getInstance(currentAccount).loadStreamFile(this, document, null, parentObject, currentOffset = dataSpec.position, false, getCurrentPriority()); bytesRemaining = dataSpec.length == C.LENGTH_UNSET ? document.size - dataSpec.position : dataSpec.length; if (bytesRemaining < 0) { throw new EOFException(); @@ -77,12 +97,26 @@ public long open(DataSpec dataSpec) throws IOException { opened = true; transferStarted(dataSpec); if (loadOperation != null) { - file = new RandomAccessFile(loadOperation.getCurrentFile(), "r"); - file.seek(currentOffset); + currentFile = loadOperation.getCurrentFile(); + if (currentFile != null) { + try { + file = new RandomAccessFile(currentFile, "r"); + file.seek(currentOffset); + } catch (Throwable e) { + } + } } return bytesRemaining; } + private int getCurrentPriority() { + Integer priority = priorityMap.getOrDefault(document.id, null); + if (priority != null) { + return priority; + } + return FileLoader.PRIORITY_HIGH; + } + @Override public int read(byte[] buffer, int offset, int readLength) throws IOException { if (readLength == 0) { @@ -91,29 +125,58 @@ public int read(byte[] buffer, int offset, int readLength) throws IOException { return C.RESULT_END_OF_INPUT; } else { int availableLength = 0; + int bytesRead; try { if (bytesRemaining < readLength) { readLength = (int) bytesRemaining; } - while (availableLength == 0 && opened) { + while ((availableLength == 0 && opened) || file == null) { availableLength = (int) loadOperation.getDownloadedLengthFromOffset(currentOffset, readLength)[0]; if (availableLength == 0) { - FileLoader.getInstance(currentAccount).loadStreamFile(this, document, null, parentObject, currentOffset, false, FileLoader.PRIORITY_HIGH); countDownLatch = new CountDownLatch(1); - countDownLatch.await(); + FileLoadOperation loadOperation = FileLoader.getInstance(currentAccount).loadStreamFile(this, document, null, parentObject, currentOffset, false, getCurrentPriority()); + if (this.loadOperation != loadOperation) { + this.loadOperation.removeStreamListener(this); + this.loadOperation = loadOperation; + } + if (countDownLatch != null) { + countDownLatch.await(); + countDownLatch = null; + } + } + File currentFileFast = loadOperation.getCurrentFileFast(); + if (file == null || !Objects.equals(currentFile, currentFileFast)) { + if (file != null) { + try { + file.close(); + } catch (Exception ignore) { + + } + } + currentFile = currentFileFast; + if (currentFile != null) { + try { + file = new RandomAccessFile(currentFile, "r"); + file.seek(currentOffset); + } catch (Throwable e) { + + } + } } } if (!opened) { return 0; } - file.readFully(buffer, offset, availableLength); - currentOffset += availableLength; - bytesRemaining -= availableLength; - bytesTransferred(availableLength); + bytesRead = file.read(buffer, offset, availableLength); + if (bytesRead > 0) { + currentOffset += bytesRead; + bytesRemaining -= bytesRead; + bytesTransferred(bytesRead); + } } catch (Exception e) { throw new IOException(e); } - return availableLength; + return bytesRead; } } @@ -141,7 +204,9 @@ public void close() { transferEnded(); } if (countDownLatch != null) { + // FileLog.d("FileStreamLoadOperation count down"); countDownLatch.countDown(); + countDownLatch = null; } } @@ -149,6 +214,13 @@ public void close() { public void newDataAvailable() { if (countDownLatch != null) { countDownLatch.countDown(); + countDownLatch = null; + } + } + + public static void setPriorityForDocument(TLRPC.Document document, int priority) { + if (document != null) { + priorityMap.put(document.id, priority); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileUploadOperation.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileUploadOperation.java index e88cd350bd..be307bf112 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileUploadOperation.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileUploadOperation.java @@ -11,7 +11,7 @@ import android.annotation.SuppressLint; import android.app.Activity; import android.content.SharedPreferences; -import android.net.Uri; +import android.util.Log; import android.util.SparseArray; import android.util.SparseIntArray; @@ -83,6 +83,7 @@ private static class UploadCachedResult { private long availableSize; private boolean uploadFirstPartLater; private SparseArray cachedResults = new SparseArray<>(); + private boolean[] recalculatedEstimatedSize = {false, false}; protected long lastProgressUpdateTime; public interface FileUploadOperationDelegate { @@ -201,8 +202,23 @@ private void cleanup() { AutoDeleteMediaTask.unlockFile(uploadingFilePath); } - protected void checkNewDataAvailable(final long newAvailableSize, final long finalSize) { + protected void checkNewDataAvailable(final long newAvailableSize, final long finalSize, final Float progress) { Utilities.stageQueue.postRunnable(() -> { + if (progress != null && estimatedSize != 0 && finalSize == 0) { + boolean needRecalculation = false; + if (progress > 0.75f && !recalculatedEstimatedSize[0]) { + recalculatedEstimatedSize[0] = true; + needRecalculation = true; + } + if (progress > 0.95f && !recalculatedEstimatedSize[1]) { + recalculatedEstimatedSize[1] = true; + needRecalculation = true; + } + if (needRecalculation) { + estimatedSize = (long) (newAvailableSize / progress); + } + } + if (estimatedSize != 0 && finalSize != 0) { estimatedSize = 0; totalFileSize = finalSize; @@ -528,11 +544,15 @@ private void startUploadRequest() { } else { connectionType = ConnectionsManager.ConnectionTypeUpload | ((requestNumFinal % 4) << 16); } - - int requestToken = ConnectionsManager.getInstance(currentAccount).sendRequest(finalRequest, (response, error) -> { + long time = System.currentTimeMillis(); + int[] requestToken = new int[1]; + requestToken[0] = ConnectionsManager.getInstance(currentAccount).sendRequest(finalRequest, (response, error) -> { if (currentOperationGuid != operationGuid) { return; } + if (BuildVars.LOGS_ENABLED) { + FileLog.d("debug_uploading: " + " response reqId " + requestToken[0] + " time" + uploadingFilePath); + } int networkType = response != null ? response.networkType : ApplicationLoader.getCurrentNetworkType(); if (currentType == ConnectionsManager.FileTypeAudio) { StatsController.getInstance(currentAccount).incrementSentBytesCount(networkType, StatsController.TYPE_AUDIOS, requestSize); @@ -653,6 +673,9 @@ private void startUploadRequest() { startUploadRequest(); } }), forceSmallFile ? ConnectionsManager.RequestFlagCanCompress : 0, ConnectionsManager.DEFAULT_DATACENTER_ID, connectionType, true); - requestTokens.put(requestNumFinal, requestToken); + if (BuildVars.LOGS_ENABLED) { + FileLog.d("debug_uploading: " + " send reqId " + requestToken[0] + " " + uploadingFilePath); + } + requestTokens.put(requestNumFinal, requestToken[0]); } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FlagSecureReason.java b/TMessagesProj/src/main/java/org/telegram/messenger/FlagSecureReason.java index e13b1c1e68..1398a2c5e7 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FlagSecureReason.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FlagSecureReason.java @@ -5,12 +5,14 @@ import java.util.HashMap; +import tw.nekomimi.nekogram.NekoXConfig; + public class FlagSecureReason { private static HashMap currentSecureReasons; - private Window window; - private FlagSecureCondition condition; + private final Window window; + private final FlagSecureCondition condition; public FlagSecureReason(Window window, FlagSecureCondition condition) { this.window = window; @@ -72,10 +74,9 @@ private static void updateWindowSecure(Window window) { } public static boolean isSecuredNow(Window window) { - return currentSecureReasons != null && currentSecureReasons.get(window) != null; + return currentSecureReasons != null && currentSecureReasons.get(window) != null && !NekoXConfig.disableFlagSecure; } - public interface FlagSecureCondition { boolean run(); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ImageLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/ImageLoader.java index 9b24f75343..5362b3e768 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ImageLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ImageLoader.java @@ -19,18 +19,23 @@ import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; +import android.media.MediaMetadataRetriever; import android.net.Uri; import android.os.AsyncTask; import android.os.Build; import android.os.SystemClock; import android.provider.MediaStore; import android.text.TextUtils; +import android.util.Pair; import android.util.SparseArray; import androidx.annotation.RequiresApi; -import androidx.exifinterface.media.ExifInterface; +import androidx.core.graphics.ColorUtils; import org.json.JSONArray; import org.json.JSONObject; @@ -42,6 +47,8 @@ import org.telegram.tgnet.TLRPC; import org.telegram.ui.Cells.ChatMessageCell; import org.telegram.ui.Components.AnimatedFileDrawable; +import org.telegram.ui.Components.BackgroundGradientDrawable; +import org.telegram.ui.Components.MotionBackgroundDrawable; import org.telegram.ui.Components.Point; import org.telegram.ui.Components.RLottieDrawable; import org.telegram.ui.Components.SlotsDrawable; @@ -91,10 +98,14 @@ * g - autoplay * lastframe - return lastframe for Lottie animation * lastreactframe - return lastframe for Lottie animation + some scale ReactionLastFrame magic - * firstframe - return firstframe for Lottie animation + * firstframe - return firstframe for Lottie or Video animation + * ignoreOrientation - do not extract EXIF orientation and do not apply it to an imagereceiver + * exif — check exif contents of invert/orientation */ public class ImageLoader { + private static final boolean DEBUG_MODE = false; + private HashMap bitmapUseCounts = new HashMap<>(); private LruCache smallImagesMemCache; private LruCache memCache; @@ -102,6 +113,7 @@ public class ImageLoader { private LruCache lottieMemCache; ArrayList cachedAnimatedFileDrawables = new ArrayList<>(); private HashMap imageLoadingByUrl = new HashMap<>(); + private HashMap imageLoadingByUrlPframe = new HashMap<>(); private HashMap imageLoadingByKeys = new HashMap<>(); private SparseArray imageLoadingByTag = new SparseArray<>(); private HashMap waitingForQualityThumb = new HashMap<>(); @@ -146,13 +158,27 @@ public static boolean hasAutoplayFilter(String s) { } String[] words = s.split("_"); for (int i = 0; i < words.length; ++i) { - if (AUTOPLAY_FILTER.equals(words[i])) { + if (AUTOPLAY_FILTER.equals(words[i]) || "pframe".equals(words[i])) { return true; } } return false; } + public static Drawable createStripedBitmap(ArrayList thumbs) { + for (int i = 0; i < thumbs.size(); i++) { + if (thumbs.get(i) instanceof TLRPC.TL_photoStrippedSize) { + TLRPC.TL_photoStrippedSize size = (TLRPC.TL_photoStrippedSize) thumbs.get(i); + return new BitmapDrawable(ApplicationLoader.applicationContext.getResources(), getStrippedPhotoBitmap(size.bytes, "b")); + } + } + return null; + } + + public static boolean isSdCardPath(File videoFile) { + return !TextUtils.isEmpty(SharedConfig.storageCacheDir) && videoFile.getAbsolutePath().startsWith(SharedConfig.storageCacheDir); + } + public void moveToFront(String key) { if (key == null) { return; @@ -868,6 +894,9 @@ public void run() { } catch (Throwable e) { FileLog.e(e); } + if (bitmap != null && !TextUtils.isEmpty(cacheImage.filter) && cacheImage.filter.contains("wallpaper") && cacheImage.parentObject instanceof TLRPC.WallPaper) { + bitmap = applyWallpaperSetting(bitmap, (TLRPC.WallPaper) cacheImage.parentObject); + } onPostExecute(bitmap != null ? new BitmapDrawable(bitmap) : null); } else if (cacheImage.imageType == FileLoader.IMAGE_TYPE_LOTTIE) { int w = Math.min(512, AndroidUtilities.dp(170.6f)); @@ -1011,6 +1040,7 @@ public void run() { boolean precache = false; boolean fistFrame = false; boolean notCreateStream = false; + boolean pframe = false; if (cacheImage.filter != null) { String[] args = cacheImage.filter.split("_"); if (args.length >= 2) { @@ -1030,11 +1060,32 @@ public void run() { if ("nostream".equals(args[i])) { notCreateStream = true; } + if ("pframe".equals(args[i])) { + pframe = true; + } } if (fistFrame) { notCreateStream = true; } } + if (pframe) { + Bitmap bitmap = null; + try { + MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever(); + mediaMetadataRetriever.setDataSource(cacheImage.finalFilePath.getAbsolutePath()); + bitmap = mediaMetadataRetriever.getFrameAtTime(MediaMetadataRetriever.OPTION_CLOSEST_SYNC); + mediaMetadataRetriever.release(); + } catch (Exception e) { + e.printStackTrace(); + } + Thread.interrupted(); + if (bitmap == null) { + onPostExecute(null); + } else { + onPostExecute(new BitmapDrawable(bitmap)); + } + return; + } BitmapsCache.CacheOptions cacheOptions = null; if (precache && !fistFrame) { cacheOptions = new BitmapsCache.CacheOptions(); @@ -1084,6 +1135,7 @@ public void run() { boolean mediaIsVideo = false; Bitmap image = null; boolean needInvert = false; + int invert = 0; int orientation = 0; File cacheFileFinal = cacheImage.finalFilePath; boolean inEncryptedFile = cacheImage.secureDocument != null || cacheImage.encryptionKeyPath != null && cacheFileFinal != null && cacheFileFinal.getAbsolutePath().endsWith(".enc"); @@ -1186,6 +1238,8 @@ public void run() { } if (cacheImage.filter.contains("f")) { force8888 = true; + } else if (cacheImage.filter.contains("F")) { + force8888 = false; } if (!useNativeWebpLoader && w_filter != 0 && h_filter != 0) { opts.inJustDecodeBounds = true; @@ -1444,24 +1498,10 @@ public void run() { } else { is = new FileInputStream(cacheFileFinal); } - if (cacheImage.imageLocation.document instanceof TLRPC.TL_document) { - try { - ExifInterface exif = new ExifInterface(is); - int attribute = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1); - switch (attribute) { - case ExifInterface.ORIENTATION_ROTATE_90: - orientation = 90; - break; - case ExifInterface.ORIENTATION_ROTATE_180: - orientation = 180; - break; - case ExifInterface.ORIENTATION_ROTATE_270: - orientation = 270; - break; - } - } catch (Throwable ignore) { - - } + if (cacheImage.imageLocation.document instanceof TLRPC.TL_document || cacheImage.filter != null && cacheImage.filter.contains("exif")) { + Pair orientationValues = AndroidUtilities.getImageOrientation(is); + orientation = orientationValues.first; + invert = orientationValues.second; if (secureDocumentKey != null || cacheImage.encryptionKeyPath != null) { is.close(); if (secureDocumentKey != null) { @@ -1580,14 +1620,62 @@ public void run() { if (BuildVars.LOGS_ENABLED && inEncryptedFile) { FileLog.e("Image Loader image is empty = " + (image == null) + " " + cacheFileFinal); } - if (needInvert || orientation != 0) { - onPostExecute(image != null ? new ExtendedBitmapDrawable(image, needInvert, orientation) : null); + if (image != null && !TextUtils.isEmpty(cacheImage.filter) && cacheImage.filter.contains("wallpaper") && cacheImage.parentObject instanceof TLRPC.WallPaper) { + image = applyWallpaperSetting(image, (TLRPC.WallPaper) cacheImage.parentObject); + } + if ((cacheImage == null || cacheImage.filter == null || !cacheImage.filter.contains("ignoreOrientation")) && (needInvert || orientation != 0 || invert != 0)) { + onPostExecute(image != null ? new ExtendedBitmapDrawable(image, orientation, invert) : null); } else { onPostExecute(image != null ? new BitmapDrawable(image) : null); } } } + private Bitmap applyWallpaperSetting(Bitmap bitmap, TLRPC.WallPaper wallPaper) { + if (!wallPaper.pattern || wallPaper.settings == null) { + if (wallPaper.settings != null && wallPaper.settings.blur) { + return Utilities.blurWallpaper(bitmap); + } + return bitmap; + } + Bitmap finalBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(finalBitmap); + int patternColor; + + boolean applyPattern = true; + if (wallPaper.settings.second_background_color == 0) { //one color + patternColor = AndroidUtilities.getPatternColor(wallPaper.settings.background_color); + canvas.drawColor(ColorUtils.setAlphaComponent(wallPaper.settings.background_color, 255)); + } else if (wallPaper.settings.third_background_color == 0) { //two color + int color1 = ColorUtils.setAlphaComponent(wallPaper.settings.background_color, 255); + int color2 = ColorUtils.setAlphaComponent(wallPaper.settings.second_background_color, 255); + patternColor = AndroidUtilities.getAverageColor(color1, color2); + GradientDrawable gradientDrawable = new GradientDrawable(BackgroundGradientDrawable.getGradientOrientation(wallPaper.settings.rotation), new int[]{color1, color2}); + gradientDrawable.setBounds(0, 0, finalBitmap.getWidth(), finalBitmap.getHeight()); + gradientDrawable.draw(canvas); + } else { + int color1 = ColorUtils.setAlphaComponent(wallPaper.settings.background_color, 255); + int color2 = ColorUtils.setAlphaComponent(wallPaper.settings.second_background_color, 255); + int color3 = ColorUtils.setAlphaComponent(wallPaper.settings.third_background_color, 255); + int color4 = wallPaper.settings.fourth_background_color == 0 ? 0 : ColorUtils.setAlphaComponent(wallPaper.settings.fourth_background_color, 255); + patternColor = MotionBackgroundDrawable.getPatternColor(color1, color2, color3, color4); + MotionBackgroundDrawable motionBackgroundDrawable = new MotionBackgroundDrawable(); + motionBackgroundDrawable.setColors(color1, color2, color3, color4); + motionBackgroundDrawable.setBounds(0, 0, finalBitmap.getWidth(), finalBitmap.getHeight()); + motionBackgroundDrawable.setPatternBitmap(wallPaper.settings.intensity, bitmap); + motionBackgroundDrawable.draw(canvas); + applyPattern = false; + } + + if (applyPattern) { + Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG); + paint.setColorFilter(new PorterDuffColorFilter(patternColor, PorterDuff.Mode.SRC_IN)); + paint.setAlpha((int) (wallPaper.settings.intensity / 100.0f * 255)); + canvas.drawBitmap(bitmap, 0, 0, paint); + } + return finalBitmap; + } + private void loadLastFrame(RLottieDrawable lottieDrawable, int w, int h, boolean lastFrame, boolean reaction) { Bitmap bitmap; Canvas canvas; @@ -1704,6 +1792,10 @@ private boolean isAnimatedAvatar(String filter) { return filter != null && filter.endsWith("avatar"); } + private boolean isPFrame(String filter) { + return filter != null && filter.endsWith("pframe"); + } + public BitmapDrawable getFromMemCache(String key) { BitmapDrawable drawable = memCache.get(key); if (drawable == null) { @@ -1733,7 +1825,9 @@ public static Bitmap getStrippedPhotoBitmap(byte[] photoBytes, String filter) { data[164] = photoBytes[1]; data[166] = photoBytes[2]; - Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, len); + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inPreferredConfig = SharedConfig.deviceIsHigh() ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565; + Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, len, options); if (bitmap != null && !TextUtils.isEmpty(filter) && filter.contains("b")) { Utilities.blurBitmap(bitmap, 3, 1, bitmap.getWidth(), bitmap.getHeight(), bitmap.getRowBytes()); } @@ -1744,6 +1838,7 @@ private class CacheImage { public int priority = 1; public Runnable runningTask; + public boolean isPFrame; protected String key; protected String url; protected String filter; @@ -1862,12 +1957,36 @@ public void removeImageReceiver(ImageReceiver imageReceiver) { if (url != null) { imageLoadingByUrl.remove(url); } + if (url != null) { + imageLoadingByUrlPframe.remove(url); + } if (key != null) { imageLoadingByKeys.remove(key); } } } + void changePriority(int newFileLoaderPriority) { + if (imageLocation != null) { + TLRPC.Document document = null; + SecureDocument secureDocument = null; + WebFile webDocument = null; + TLRPC.FileLocation location = null; + String locationExt = null; + if (imageLocation.location != null) { + location= imageLocation.location; + locationExt = ext; + } else if (imageLocation.document != null) { + document = imageLocation.document; + } else if (imageLocation.secureDocument != null) { + secureDocument = imageLocation.secureDocument; + } else if (imageLocation.webFile != null) { + webDocument = imageLocation.webFile; + } + FileLoader.getInstance(currentAccount).changePriority(newFileLoaderPriority, document, secureDocument, webDocument, location, locationExt, null); + } + } + public void setImageAndClear(final Drawable image, String decrementKey) { if (image != null) { final ArrayList finalImageReceiverArray = new ArrayList<>(imageReceiverArray); @@ -1912,6 +2031,9 @@ public void setImageAndClear(final Drawable image, String decrementKey) { if (url != null) { imageLoadingByUrl.remove(url); } + if (url != null) { + imageLoadingByUrlPframe.remove(url); + } if (key != null) { imageLoadingByKeys.remove(key); } @@ -1943,15 +2065,15 @@ public ImageLoader() { } else { maxSize = 15; } - int cacheSize = Math.min(maxSize, memoryClass / 7) * 1024 * 1024; + int cacheSize = DEBUG_MODE ? 1 : Math.min(maxSize, memoryClass / 7) * 1024 * 1024; - int commonCacheSize = (int) (cacheSize * 0.8f); - int smallImagesCacheSize = (int) (cacheSize * 0.2f); + int commonCacheSize = DEBUG_MODE ? 1 : (int) (cacheSize * 0.8f); + int smallImagesCacheSize = DEBUG_MODE ? 1 : (int) (cacheSize * 0.2f); memCache = new LruCache(commonCacheSize) { @Override protected int sizeOf(String key, BitmapDrawable value) { - return value.getBitmap().getByteCount(); + return sizeOfBitmapDrawable(value); } @Override @@ -1973,7 +2095,7 @@ protected void entryRemoved(boolean evicted, String key, final BitmapDrawable ol smallImagesMemCache = new LruCache(smallImagesCacheSize) { @Override protected int sizeOf(String key, BitmapDrawable value) { - return value.getBitmap().getByteCount(); + return sizeOfBitmapDrawable(value); } @Override @@ -1992,18 +2114,18 @@ protected void entryRemoved(boolean evicted, String key, final BitmapDrawable ol } } }; - wallpaperMemCache = new LruCache(cacheSize / 4) { + wallpaperMemCache = new LruCache(DEBUG_MODE ? 1 : cacheSize / 4) { @Override protected int sizeOf(String key, BitmapDrawable value) { - return value.getBitmap().getByteCount(); + return sizeOfBitmapDrawable(value); } }; - lottieMemCache = new LruCache(512 * 512 * 2 * 4 * 5) { + lottieMemCache = new LruCache(DEBUG_MODE ? 1 : 512 * 512 * 2 * 4 * 5) { @Override protected int sizeOf(String key, BitmapDrawable value) { - return value.getIntrinsicWidth() * value.getIntrinsicHeight() * 4 * 2; + return sizeOfBitmapDrawable(value); } @Override @@ -2117,9 +2239,57 @@ public void fileDidFailedLoad(final String location, final int canceled) { @Override public void fileLoadProgressChanged(FileLoadOperation operation, final String location, long uploadedSize, long totalSize) { fileProgresses.put(location, new long[]{uploadedSize, totalSize}); + + if (!imageLoadingByUrlPframe.isEmpty() && operation.checkPrefixPreloadFinished()) { + imageLoadQueue.postRunnable(() -> { + CacheImage img = imageLoadingByUrlPframe.remove(location); + if (img == null) { + return; + } + imageLoadingByUrl.remove(location); + ArrayList tasks = new ArrayList<>(); + for (int a = 0; a < img.imageReceiverArray.size(); a++) { + String key = img.keys.get(a); + String filter = img.filters.get(a); + int type = img.types.get(a); + ImageReceiver imageReceiver = img.imageReceiverArray.get(a); + int guid = img.imageReceiverGuidsArray.get(a); + CacheImage cacheImage = imageLoadingByKeys.get(key); + if (cacheImage == null) { + cacheImage = new CacheImage(); + cacheImage.priority = img.priority; + cacheImage.secureDocument = img.secureDocument; + cacheImage.currentAccount = img.currentAccount; + cacheImage.finalFilePath = operation.getCurrentFile(); + cacheImage.parentObject = img.parentObject; + cacheImage.isPFrame = img.isPFrame; + cacheImage.key = key; + cacheImage.imageLocation = img.imageLocation; + cacheImage.type = type; + cacheImage.ext = img.ext; + cacheImage.encryptionKeyPath = img.encryptionKeyPath; + cacheImage.cacheTask = new CacheOutTask(cacheImage); + cacheImage.filter = filter; + cacheImage.imageType = img.imageType; + imageLoadingByKeys.put(key, cacheImage); + tasks.add(cacheImage.cacheTask); + } + cacheImage.addImageReceiver(imageReceiver, key, filter, type, guid); + } + for (int a = 0; a < tasks.size(); a++) { + CacheOutTask task = tasks.get(a); + if (task.cacheImage.type == ImageReceiver.TYPE_THUMB) { + cacheThumbOutQueue.postRunnable(task); + } else { + cacheOutQueue.postRunnable(task, task.cacheImage.priority); + } + } + }); + } long currentTime = SystemClock.elapsedRealtime(); if (operation.lastProgressUpdateTime == 0 || operation.lastProgressUpdateTime < currentTime - 500 || uploadedSize == 0) { operation.lastProgressUpdateTime = currentTime; + AndroidUtilities.runOnUIThread(() -> NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.fileLoadProgressChanged, location, uploadedSize, totalSize)); } } @@ -2163,6 +2333,18 @@ public void onReceive(Context arg0, Intent intent) { checkMediaPaths(); } + private int sizeOfBitmapDrawable(BitmapDrawable value) { + if (value instanceof AnimatedFileDrawable) { + AnimatedFileDrawable animatedFileDrawable = (AnimatedFileDrawable) value; + int maxSize = animatedFileDrawable.getRenderingHeight() * animatedFileDrawable.getRenderingWidth() * 4 * 3; + maxSize = Math.max(animatedFileDrawable.getIntrinsicHeight() * value.getIntrinsicWidth() * 4 * 3, maxSize); + return maxSize; + } if (value instanceof RLottieDrawable) { + return value.getIntrinsicWidth() * value.getIntrinsicHeight() * 4 * 2; + } + return value.getBitmap().getByteCount(); + } + public void checkMediaPaths() { checkMediaPaths(null); } @@ -2268,6 +2450,20 @@ public SparseArray createMediaPaths() { } catch (Exception e) { FileLog.e(e); } + + try { + File storyPath = new File(telegramPath, "stories"); + FileUtil.initDir(storyPath); + if (storyPath.isDirectory() && canMoveFiles(cachePath, storyPath, FileLoader.MEDIA_DIR_STORIES)) { + AndroidUtilities.createEmptyFile(new File(storyPath, ".nomedia")); + mediaDirs.put(FileLoader.MEDIA_DIR_STORIES, storyPath); + if (BuildVars.LOGS_ENABLED) { + FileLog.d("stories path = " + storyPath); + } + } + } catch (Exception e) { + FileLog.e(e); + } } SharedConfig.checkSaveToGalleryFiles(); } catch (Exception e) { @@ -2299,7 +2495,7 @@ private boolean canMoveFiles(File from, File to, int type) { if (type == FileLoader.MEDIA_DIR_IMAGE) { srcFile = new File(from, "000000000_999999_temp.f"); dstFile = new File(to, "000000000_999999.f"); - } else if (type == FileLoader.MEDIA_DIR_DOCUMENT || type == FileLoader.MEDIA_DIR_FILES) { + } else if (type == FileLoader.MEDIA_DIR_DOCUMENT || type == FileLoader.MEDIA_DIR_FILES || type == FileLoader.MEDIA_DIR_STORIES) { srcFile = new File(from, "000000000_999999_temp.f"); dstFile = new File(to, "000000000_999999.f"); } else if (type == FileLoader.MEDIA_DIR_AUDIO) { @@ -2458,6 +2654,32 @@ private void removeFromWaitingForThumb(int TAG, ImageReceiver imageReceiver) { } } + public void changeFileLoadingPriorityForImageReceiver(ImageReceiver imageReceiver) { + if (imageReceiver == null) { + return; + } + int newPriority = imageReceiver.getFileLoadingPriority(); + imageLoadQueue.postRunnable(() -> { + for (int a = 0; a < 3; a++) { + int type; + if (a == 0) { + type = ImageReceiver.TYPE_THUMB; + } else if (a == 1) { + type = ImageReceiver.TYPE_IMAGE; + } else { + type = ImageReceiver.TYPE_MEDIA; + } + int TAG = imageReceiver.getTag(type); + if (TAG != 0) { + CacheImage ei = imageLoadingByTag.get(TAG); + if (ei != null) { + ei.changePriority(newPriority); + } + } + } + }); + } + public void cancelLoadingForImageReceiver(final ImageReceiver imageReceiver, final boolean cancelAll) { if (imageReceiver == null) { return; @@ -2845,6 +3067,7 @@ private void createLoadOperationForImageReceiver(final ImageReceiver imageReceiv if (hasAutoplayFilter(filter) || isAnimatedAvatar(filter)) { img.imageType = FileLoader.IMAGE_TYPE_ANIMATION; img.size = fileSize; + img.isPFrame = isPFrame(filter); if (AUTOPLAY_FILTER.equals(filter) || isAnimatedAvatar(filter)) { onlyCache = true; } @@ -2881,6 +3104,9 @@ private void createLoadOperationForImageReceiver(final ImageReceiver imageReceiv img.url = url; imageLoadingByUrl.put(url, img); + if (img.isPFrame) { + imageLoadingByUrlPframe.put(url, img); + } if (imageLocation.path != null) { String file = Utilities.MD5(imageLocation.path); File cacheDir = FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE); @@ -2954,6 +3180,9 @@ public void preloadArtwork(String athumbUrl) { } public void loadImageForImageReceiver(ImageReceiver imageReceiver) { + loadImageForImageReceiver(imageReceiver, null); + } + public void loadImageForImageReceiver(ImageReceiver imageReceiver, List preloadReceiver) { if (imageReceiver == null) { return; } @@ -2963,24 +3192,26 @@ public void loadImageForImageReceiver(ImageReceiver imageReceiver) { int guid = imageReceiver.getNewGuid(); if (mediaKey != null) { ImageLocation mediaLocation = imageReceiver.getMediaLocation(); - Drawable drawable; - if (useLottieMemCache(mediaLocation, mediaKey)) { - drawable = getFromLottieCache(mediaKey); - } else { - drawable = memCache.get(mediaKey); - if (drawable != null) { - memCache.moveToFront(mediaKey); - } - if (drawable == null) { - drawable = smallImagesMemCache.get(mediaKey); + Drawable drawable = findInPreloadImageReceivers(mediaKey, preloadReceiver); + if (drawable == null) { + if (useLottieMemCache(mediaLocation, mediaKey)) { + drawable = getFromLottieCache(mediaKey); + } else { + drawable = memCache.get(mediaKey); if (drawable != null) { - smallImagesMemCache.moveToFront(mediaKey); + memCache.moveToFront(mediaKey); } - } - if (drawable == null) { - drawable = wallpaperMemCache.get(mediaKey); - if (drawable != null) { - wallpaperMemCache.moveToFront(mediaKey); + if (drawable == null) { + drawable = smallImagesMemCache.get(mediaKey); + if (drawable != null) { + smallImagesMemCache.moveToFront(mediaKey); + } + } + if (drawable == null) { + drawable = wallpaperMemCache.get(mediaKey); + if (drawable != null) { + wallpaperMemCache.moveToFront(mediaKey); + } } } } @@ -3005,8 +3236,8 @@ public void loadImageForImageReceiver(ImageReceiver imageReceiver) { String imageKey = imageReceiver.getImageKey(); if (!imageSet && imageKey != null) { ImageLocation imageLocation = imageReceiver.getImageLocation(); - Drawable drawable = null; - if (useLottieMemCache(imageLocation, imageKey)) { + Drawable drawable = findInPreloadImageReceivers(imageKey, preloadReceiver); + if (drawable == null && useLottieMemCache(imageLocation, imageKey)) { drawable = getFromLottieCache(imageKey); } if (drawable == null) { @@ -3262,6 +3493,22 @@ public void loadImageForImageReceiver(ImageReceiver imageReceiver) { } } + private Drawable findInPreloadImageReceivers(String imageKey, List receivers) { + if (receivers == null) { + return null; + } + for (int i = 0; i < receivers.size(); i++) { + ImageReceiver receiver = receivers.get(i); + if (imageKey.equals(receiver.getImageKey())) { + return receiver.getImageDrawable(); + } + if (imageKey.equals(receiver.getMediaKey())) { + return receiver.getMediaDrawable(); + } + } + return null; + } + private BitmapDrawable getFromLottieCache(String imageKey) { BitmapDrawable drawable = lottieMemCache.get(imageKey); if (drawable instanceof AnimatedFileDrawable) { @@ -3326,6 +3573,7 @@ private void fileDidLoaded(final String location, final File finalFile, final in return; } imageLoadingByUrl.remove(location); + imageLoadingByUrlPframe.remove(location); ArrayList tasks = new ArrayList<>(); for (int a = 0; a < img.imageReceiverArray.size(); a++) { String key = img.keys.get(a); @@ -3341,6 +3589,7 @@ private void fileDidLoaded(final String location, final File finalFile, final in cacheImage.currentAccount = img.currentAccount; cacheImage.finalFilePath = finalFile; cacheImage.parentObject = img.parentObject; + cacheImage.isPFrame = img.isPFrame; cacheImage.key = key; cacheImage.imageLocation = img.imageLocation; cacheImage.type = type; @@ -3566,35 +3815,28 @@ public static Bitmap loadBitmap(String path, Uri uri, float maxWidth, float maxH Matrix matrix = null; try { - int orientation = 0; - if (path != null) { - ExifInterface exif = new ExifInterface(path); - orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); - } else if (uri != null) { - try (InputStream stream = ApplicationLoader.applicationContext.getContentResolver().openInputStream(uri)) { - ExifInterface exif = new ExifInterface(stream); - orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); + Pair orientation = AndroidUtilities.getImageOrientation(path); + if (orientation.first == 0 && orientation.second == 0) { + InputStream stream = null; + try { + stream = ApplicationLoader.applicationContext.getContentResolver().openInputStream(uri); + orientation = AndroidUtilities.getImageOrientation(stream); } catch (Throwable ignore) { + } finally { + if (stream != null) { + stream.close(); + } } } - switch (orientation) { - case ExifInterface.ORIENTATION_ROTATE_90: - matrix = new Matrix(); - matrix.postRotate(90); - break; - case ExifInterface.ORIENTATION_ROTATE_180: - matrix = new Matrix(); - matrix.postRotate(180); - break; - case ExifInterface.ORIENTATION_ROTATE_270: - matrix = new Matrix(); - matrix.postRotate(270); - break; + if (orientation.first != 0 || orientation.second != 0) { + matrix = new Matrix(); + if (orientation.second != 0) + matrix.postScale(orientation.second == 1 ? -1 : 1, orientation.second == 2 ? -1 : 1); + if (orientation.first != 0) + matrix.postRotate(orientation.first); } - } catch (Throwable ignore) { - - } + } catch (Throwable ignore) {} scaleFactor /= bmOptions.inSampleSize; if (scaleFactor > 1) { @@ -3930,11 +4172,13 @@ private static TLRPC.PhotoSize findPhotoCachedSize(TLRPC.Message message) { } } } else if (message.media instanceof TLRPC.TL_messageMediaDocument) { - for (int a = 0, count = message.media.document.thumbs.size(); a < count; a++) { - TLRPC.PhotoSize size = message.media.document.thumbs.get(a); - if (size instanceof TLRPC.TL_photoCachedSize) { - photoSize = size; - break; + if (message.media.document != null) { + for (int a = 0, count = message.media.document.thumbs.size(); a < count; a++) { + TLRPC.PhotoSize size = message.media.document.thumbs.get(a); + if (size instanceof TLRPC.TL_photoCachedSize) { + photoSize = size; + break; + } } } } else if (message.media instanceof TLRPC.TL_messageMediaWebPage) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ImageReceiver.java b/TMessagesProj/src/main/java/org/telegram/messenger/ImageReceiver.java index 74dc845c6e..601c2eb933 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ImageReceiver.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ImageReceiver.java @@ -12,6 +12,7 @@ import android.graphics.BitmapShader; import android.graphics.BlendMode; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.ComposeShader; import android.graphics.Matrix; @@ -43,23 +44,40 @@ import org.telegram.ui.Components.VectorAvatarThumbDrawable; import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import xyz.nextalone.nagram.NaConfig; public class ImageReceiver implements NotificationCenter.NotificationCenterDelegate { + List preloadReceivers; + private boolean allowCrossfadeWithImage = true; + public boolean updateThumbShaderMatrix() { if (currentThumbDrawable != null && thumbShader != null) { - drawDrawable(null, currentThumbDrawable, 255, thumbShader, 0, 0, null); + drawDrawable(null, currentThumbDrawable, 255, thumbShader, 0, 0, 0, null); return true; } - if (staticThumbDrawable != null && thumbShader != null) { - drawDrawable(null, staticThumbDrawable, 255, thumbShader, 0, 0, null); + if (staticThumbDrawable != null && staticThumbShader != null) { + drawDrawable(null, staticThumbDrawable, 255, staticThumbShader, 0, 0, 0, null); return true; } return false; } + public void setPreloadingReceivers(List preloadReceivers) { + this.preloadReceivers = preloadReceivers; + } + + public Drawable getImageDrawable() { + return currentImageDrawable; + } + + public Drawable getMediaDrawable() { + return currentMediaDrawable; + } + public interface ImageReceiverDelegate { void didSetImage(ImageReceiver imageReceiver, boolean set, boolean thumb, boolean memCache); @@ -218,7 +236,7 @@ private void clear() { private int imageTag; private Drawable currentImageDrawable; private BitmapShader imageShader; - protected int imageOrientation; + protected int imageOrientation, imageInvert; private ImageLocation currentThumbLocation; private String currentThumbFilter; @@ -226,7 +244,8 @@ private void clear() { private int thumbTag; private Drawable currentThumbDrawable; public BitmapShader thumbShader; - private int thumbOrientation; + public BitmapShader staticThumbShader; + private int thumbOrientation, thumbInvert; private ImageLocation currentMediaLocation; private String currentMediaFilter; @@ -271,20 +290,20 @@ private void clear() { private float imageX, imageY, imageW, imageH; private float sideClip; - private RectF drawRegion = new RectF(); + private final RectF drawRegion = new RectF(); private boolean isVisible = true; private boolean isAspectFit; private boolean forcePreview; private boolean forceCrossfade; - private int[] roundRadius = new int[4]; + private final int[] roundRadius = new int[4]; private boolean isRoundRect = true; + private Object mark; private Paint roundPaint; - private RectF roundRect = new RectF(); - private RectF bitmapRect = new RectF(); - private Matrix shaderMatrix = new Matrix(); - private Path roundPath = new Path(); - private static float[] radii = new float[8]; + private final RectF roundRect = new RectF(); + private final Matrix shaderMatrix = new Matrix(); + private final Path roundPath = new Path(); + private static final float[] radii = new float[8]; private float overrideAlpha = 1.0f; private int isPressed; private boolean centerRotation; @@ -373,10 +392,10 @@ public void setForUserOrChat(TLObject object, Drawable avatarDrawable) { setForUserOrChat(object, avatarDrawable, null); } public void setForUserOrChat(TLObject object, Drawable avatarDrawable, Object parentObject) { - setForUserOrChat(object, avatarDrawable, parentObject, false, 0); + setForUserOrChat(object, avatarDrawable, parentObject, false, 0, false); } - public void setForUserOrChat(TLObject object, Drawable avatarDrawable, Object parentObject, boolean animationEnabled, int vectorType) { + public void setForUserOrChat(TLObject object, Drawable avatarDrawable, Object parentObject, boolean animationEnabled, int vectorType, boolean big) { if (parentObject == null) { parentObject = object; } @@ -440,8 +459,15 @@ public void setForUserOrChat(TLObject object, Drawable avatarDrawable, Object pa VectorAvatarThumbDrawable drawable = new VectorAvatarThumbDrawable(vectorImageMarkup, isPremium, vectorType); setImageBitmap(drawable); } else { - ImageLocation location = ImageLocation.getForUserOrChat(object, ImageLocation.TYPE_SMALL); - String filter = "50_50"; + ImageLocation location; + String filter; + if (!big) { + location = ImageLocation.getForUserOrChat(object, ImageLocation.TYPE_SMALL); + filter = "50_50"; + } else { + location = ImageLocation.getForUserOrChat(object, ImageLocation.TYPE_BIG); + filter = "100_100"; + } if (videoLocation != null) { setImage(videoLocation, "avatar", location, filter, null, null, strippedBitmap, 0, null, parentObject, 0); animatedFileDrawableRepeatMaxCount = 3; @@ -531,9 +557,6 @@ public void setImage(ImageLocation mediaLocation, String mediaFilter, ImageLocat previousAlpha = 1f; currentSize = 0; - if (staticThumbDrawable instanceof SvgHelper.SvgDrawable) { - ((SvgHelper.SvgDrawable) staticThumbDrawable).setParent(this); - } updateDrawableRadius(staticThumbDrawable); ImageLoader.getInstance().cancelLoadingForImageReceiver(this, true); @@ -643,7 +666,7 @@ public void setImage(ImageLocation mediaLocation, String mediaFilter, ImageLocat recycleBitmap(thumbKey, TYPE_THUMB); recycleBitmap(null, TYPE_CROSSFDADE); recycleBitmap(mediaKey, TYPE_MEDIA); - crossfadeShader = thumbShader; + crossfadeShader = staticThumbShader; crossfadeImage = staticThumbDrawable; crossfadingWithThumb = false; crossfadeKey = null; @@ -682,6 +705,7 @@ public void setImage(ImageLocation mediaLocation, String mediaFilter, ImageLocat imageShader = null; composeShader = null; thumbShader = null; + staticThumbShader = null; mediaShader = null; legacyShader = null; legacyCanvas = null; @@ -693,13 +717,6 @@ public void setImage(ImageLocation mediaLocation, String mediaFilter, ImageLocat currentAlpha = 1.0f; previousAlpha = 1f; - if (staticThumbDrawable instanceof ClipRoundedDrawable) { - if (((ClipRoundedDrawable) staticThumbDrawable).getDrawable() instanceof SvgHelper.SvgDrawable) { - ((SvgHelper.SvgDrawable) ((ClipRoundedDrawable) staticThumbDrawable).getDrawable()).setParent(this); - } - } else if (staticThumbDrawable instanceof SvgHelper.SvgDrawable) { - ((SvgHelper.SvgDrawable) staticThumbDrawable).setParent(this); - } updateDrawableRadius(staticThumbDrawable); if (delegate != null) { @@ -710,7 +727,7 @@ public void setImage(ImageLocation mediaLocation, String mediaFilter, ImageLocat } private void loadImage() { - ImageLoader.getInstance().loadImageForImageReceiver(this); + ImageLoader.getInstance().loadImageForImageReceiver(this, preloadReceivers); invalidate(); } @@ -735,6 +752,10 @@ public boolean getPressed() { } public void setOrientation(int angle, boolean center) { + setOrientation(angle, 0, center); + } + + public void setOrientation(int angle, int invert, boolean center) { while (angle < 0) { angle += 360; } @@ -742,6 +763,7 @@ public void setOrientation(int angle, boolean center) { angle -= 360; } imageOrientation = thumbOrientation = angle; + imageInvert = thumbInvert = invert; centerRotation = center; } @@ -762,6 +784,10 @@ public int getOrientation() { return imageOrientation; } + public int getInvert() { + return imageInvert; + } + public void setLayerNum(int value) { currentLayerNum = value; if (attachedToWindow) { @@ -803,7 +829,7 @@ public void setImageBitmap(Drawable bitmap, boolean notify) { recycleBitmap(null, TYPE_THUMB); recycleBitmap(null, TYPE_CROSSFDADE); recycleBitmap(null, TYPE_MEDIA); - crossfadeShader = thumbShader; + crossfadeShader = staticThumbShader; crossfadeImage = staticThumbDrawable; crossfadingWithThumb = true; crossfadeKey = null; @@ -847,6 +873,7 @@ public void setImageBitmap(Drawable bitmap, boolean notify) { } fileDrawable.setAllowDecodeSingleFrame(true); } + staticThumbShader = null; thumbShader = null; roundPaint.setShader(null); setStaticDrawable(bitmap); @@ -921,8 +948,10 @@ private void setStaticDrawable(Drawable bitmap) { } private void setDrawableShader(Drawable drawable, BitmapShader shader) { - if (drawable == currentThumbDrawable || drawable == staticThumbDrawable) { + if (drawable == currentThumbDrawable) { thumbShader = shader; + } else if (drawable == staticThumbDrawable) { + staticThumbShader = shader; } else if (drawable == currentMediaDrawable) { mediaShader = shader; } else if (drawable == currentImageDrawable) { @@ -977,7 +1006,7 @@ private void updateDrawableRadius(Drawable drawable) { AnimatedFileDrawable animatedFileDrawable = (AnimatedFileDrawable) drawable; animatedFileDrawable.setRoundRadius(roundRadius); } else if (bitmapDrawable.getBitmap() != null) { - setDrawableShader(drawable, new BitmapShader(bitmapDrawable.getBitmap(), Shader.TileMode.REPEAT, Shader.TileMode.REPEAT)); + setDrawableShader(drawable, new BitmapShader(bitmapDrawable.getBitmap(), Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)); } } } else { @@ -1024,10 +1053,10 @@ public void onDetachedFromWindow() { if (staticThumbDrawable != null) { setStaticDrawable(null); - thumbShader = null; - roundPaint.setShader(null); + staticThumbShader = null; } clearImage(); + roundPaint.setShader(null); if (isPressed == 0) { pressedProgress = 0f; } @@ -1046,6 +1075,12 @@ public boolean setBackupImage() { if (setImageBackup != null && setImageBackup.isSet()) { SetImageBackup temp = setImageBackup; setImageBackup = null; + if (temp.thumb instanceof BitmapDrawable) { + BitmapDrawable bitmapDrawable = (BitmapDrawable) temp.thumb; + if (!(bitmapDrawable instanceof RLottieDrawable) && !(bitmapDrawable instanceof AnimatedFileDrawable) && bitmapDrawable.getBitmap() != null && bitmapDrawable.getBitmap().isRecycled()) { + temp.thumb = null; + } + } setImage(temp.mediaLocation, temp.mediaFilter, temp.imageLocation, temp.imageFilter, temp.thumbLocation, temp.thumbFilter, temp.thumb, temp.size, temp.ext, temp.parentObject, temp.cacheType); temp.clear(); setImageBackup = temp; @@ -1120,7 +1155,7 @@ public boolean onAttachedToWindow() { return false; } - private void drawDrawable(Canvas canvas, Drawable drawable, int alpha, BitmapShader shader, int orientation, BackgroundThreadDrawHolder backgroundThreadDrawHolder) { + private void drawDrawable(Canvas canvas, Drawable drawable, int alpha, BitmapShader shader, int orientation, int invert, BackgroundThreadDrawHolder backgroundThreadDrawHolder) { if (isPressed == 0 && pressedProgress != 0) { pressedProgress -= 16 / 150f; if (pressedProgress < 0) { @@ -1133,10 +1168,10 @@ private void drawDrawable(Canvas canvas, Drawable drawable, int alpha, BitmapSha animateFromIsPressed = isPressed; } if (pressedProgress == 0 || pressedProgress == 1f) { - drawDrawable(canvas, drawable, alpha, shader, orientation, isPressed, backgroundThreadDrawHolder); + drawDrawable(canvas, drawable, alpha, shader, orientation, invert, isPressed, backgroundThreadDrawHolder); } else { - drawDrawable(canvas, drawable, alpha, shader, orientation, isPressed, backgroundThreadDrawHolder); - drawDrawable(canvas, drawable, (int) (alpha * pressedProgress), shader, orientation, animateFromIsPressed, backgroundThreadDrawHolder); + drawDrawable(canvas, drawable, alpha, shader, orientation, invert, isPressed, backgroundThreadDrawHolder); + drawDrawable(canvas, drawable, (int) (alpha * pressedProgress), shader, orientation, invert, animateFromIsPressed, backgroundThreadDrawHolder); } } @@ -1144,7 +1179,7 @@ public void setUseRoundForThumbDrawable(boolean value) { useRoundForThumb = value; } - protected void drawDrawable(Canvas canvas, Drawable drawable, int alpha, BitmapShader shader, int orientation, int isPressed, BackgroundThreadDrawHolder backgroundThreadDrawHolder) { + protected void drawDrawable(Canvas canvas, Drawable drawable, int alpha, BitmapShader shader, int orientation, int invert, int isPressed, BackgroundThreadDrawHolder backgroundThreadDrawHolder) { float imageX, imageY, imageH, imageW; RectF drawRegion; ColorFilter colorFilter; @@ -1260,8 +1295,21 @@ protected void drawDrawable(Canvas canvas, Drawable drawable, int alpha, BitmapS if (isVisible) { shaderMatrix.reset(); shaderMatrix.setTranslate((int) drawRegion.left, (int) drawRegion.top); - float toScale = 1.0f / scale; - shaderMatrix.preScale(1.0f / scale, 1.0f / scale); + if (invert != 0) { + shaderMatrix.preScale(invert == 1 ? -1 : 1, invert == 2 ? -1 : 1, drawRegion.width() / 2f, drawRegion.height() / 2f); + } + if (orientation == 90) { + shaderMatrix.preRotate(90); + shaderMatrix.preTranslate(0, -drawRegion.width()); + } else if (orientation == 180) { + shaderMatrix.preRotate(180); + shaderMatrix.preTranslate(-drawRegion.width(), -drawRegion.height()); + } else if (orientation == 270) { + shaderMatrix.preRotate(270); + shaderMatrix.preTranslate(-drawRegion.height(), 0); + } + final float toScale = 1.0f / scale; + shaderMatrix.preScale(toScale, toScale); shader.setLocalMatrix(shaderMatrix); roundPaint.setShader(shader); @@ -1325,9 +1373,12 @@ protected void drawDrawable(Canvas canvas, Drawable drawable, int alpha, BitmapS if (isVisible) { shaderMatrix.reset(); if (reactionLastFrame) { - shaderMatrix.setTranslate((int) (drawRegion.left + sideClip) - (drawRegion.width() * ReactionLastFrame.LAST_FRAME_SCALE - drawRegion.width()) / 2f, (int) (drawRegion.top + sideClip) - (drawRegion.height() * ReactionLastFrame.LAST_FRAME_SCALE - drawRegion.height()) / 2f); + shaderMatrix.setTranslate((drawRegion.left + sideClip) - (drawRegion.width() * ReactionLastFrame.LAST_FRAME_SCALE - drawRegion.width()) / 2f, drawRegion.top + sideClip - (drawRegion.height() * ReactionLastFrame.LAST_FRAME_SCALE - drawRegion.height()) / 2f); } else { - shaderMatrix.setTranslate((int) (drawRegion.left + sideClip), (int) (drawRegion.top + sideClip)); + shaderMatrix.setTranslate(drawRegion.left + sideClip, drawRegion.top + sideClip); + } + if (invert != 0) { + shaderMatrix.preScale(invert == 1 ? -1 : 1, invert == 2 ? -1 : 1, drawRegion.width() / 2f, drawRegion.height() / 2f); } if (orientation == 90) { shaderMatrix.preRotate(90); @@ -1454,6 +1505,11 @@ protected void drawDrawable(Canvas canvas, Drawable drawable, int alpha, BitmapS canvas.clipRect(imageX, imageY, imageX + imageW, imageY + imageH); } + if (invert == 1) { + canvas.scale(-1, 1, imageW / 2, imageH / 2); + } else if (invert == 2) { + canvas.scale(1, -1, imageW / 2, imageH / 2); + } if (orientation % 360 != 0) { if (centerRotation) { canvas.rotate(orientation, imageW / 2, imageH / 2); @@ -1504,6 +1560,11 @@ protected void drawDrawable(Canvas canvas, Drawable drawable, int alpha, BitmapS canvas.restore(); } else { canvas.save(); + if (invert == 1) { + canvas.scale(-1, 1, imageW / 2, imageH / 2); + } else if (invert == 2) { + canvas.scale(1, -1, imageW / 2, imageH / 2); + } if (orientation % 360 != 0) { if (centerRotation) { canvas.rotate(orientation, imageW / 2, imageH / 2); @@ -1575,10 +1636,15 @@ protected void drawDrawable(Canvas canvas, Drawable drawable, int alpha, BitmapS drawable.setBounds((int) drawRegion.left, (int) drawRegion.top, (int) drawRegion.right, (int) drawRegion.bottom); } if (isVisible && canvas != null) { + SvgHelper.SvgDrawable svgDrawable = null; + if (drawable instanceof SvgHelper.SvgDrawable) { + svgDrawable = (SvgHelper.SvgDrawable) drawable; + svgDrawable.setParent(this); + } try { drawable.setAlpha(alpha); if (backgroundThreadDrawHolder != null) { - if (drawable instanceof SvgHelper.SvgDrawable) { + if (svgDrawable != null) { long time = backgroundThreadDrawHolder.time; if (time == 0) { time = System.currentTimeMillis(); @@ -1593,6 +1659,9 @@ protected void drawDrawable(Canvas canvas, Drawable drawable, int alpha, BitmapS } catch (Exception e) { FileLog.e(e); } + if (svgDrawable != null) { + svgDrawable.setParent(null); + } } } } @@ -1712,23 +1781,6 @@ public void skipDraw() { // lottieDrawable.updateCurrentFrame(); // } } - - protected boolean customDraw( - Canvas canvas, - AnimatedFileDrawable animation, - RLottieDrawable lottieAnimation, - Drawable currentMediaDrawable, BitmapShader currentMediaShader, - Drawable currentImageDrawable, BitmapShader currentImageShader, - Drawable currentThumbDrawable, BitmapShader currentThumbShader, - boolean crossfadeWithOldImage, boolean crossfadingWithThumb, - Drawable crossfadeImage, BitmapShader crossfadeShader, - Drawable staticThumbDrawable, - float currentAlpha, float previousAlpha, float overrideAlpha, - int[] roundRadius, - BackgroundThreadDrawHolder backgroundThreadDrawHolder - ) { - return false; - } public boolean draw(Canvas canvas) { return draw(canvas, null); @@ -1751,6 +1803,7 @@ public boolean draw(Canvas canvas, BackgroundThreadDrawHolder backgroundThreadDr BitmapShader imageShader; Drawable currentThumbDrawable; BitmapShader thumbShader; + BitmapShader staticThumbShader; boolean crossfadeWithOldImage; boolean crossfadingWithThumb; @@ -1763,6 +1816,7 @@ public boolean draw(Canvas canvas, BackgroundThreadDrawHolder backgroundThreadDr int[] roundRadius; boolean animationNotReady; + ColorFilter colorFilter; boolean drawInBackground = backgroundThreadDrawHolder != null; if (drawInBackground) { animation = backgroundThreadDrawHolder.animation; @@ -1773,6 +1827,7 @@ public boolean draw(Canvas canvas, BackgroundThreadDrawHolder backgroundThreadDr currentImageDrawable = backgroundThreadDrawHolder.imageDrawable; imageShader = backgroundThreadDrawHolder.imageShader; thumbShader = backgroundThreadDrawHolder.thumbShader; + staticThumbShader = backgroundThreadDrawHolder.staticThumbShader; crossfadeImage = backgroundThreadDrawHolder.crossfadeImage; crossfadeWithOldImage = backgroundThreadDrawHolder.crossfadeWithOldImage; crossfadingWithThumb = backgroundThreadDrawHolder.crossfadingWithThumb; @@ -1783,6 +1838,7 @@ public boolean draw(Canvas canvas, BackgroundThreadDrawHolder backgroundThreadDr crossfadeShader = backgroundThreadDrawHolder.crossfadeShader; animationNotReady = backgroundThreadDrawHolder.animationNotReady; overrideAlpha = backgroundThreadDrawHolder.overrideAlpha; + colorFilter = backgroundThreadDrawHolder.colorFilter; } else { animation = getAnimation(); lottieDrawable = getLottieAnimation(); @@ -1793,6 +1849,7 @@ public boolean draw(Canvas canvas, BackgroundThreadDrawHolder backgroundThreadDr imageShader = this.imageShader; currentThumbDrawable = this.currentThumbDrawable; thumbShader = this.thumbShader; + staticThumbShader = this.staticThumbShader; crossfadeWithOldImage = this.crossfadeWithOldImage; crossfadingWithThumb = this.crossfadingWithThumb; crossfadeImage = this.crossfadeImage; @@ -1802,21 +1859,7 @@ public boolean draw(Canvas canvas, BackgroundThreadDrawHolder backgroundThreadDr crossfadeShader = this.crossfadeShader; overrideAlpha = this.overrideAlpha; animationNotReady = animation != null && !animation.hasBitmap() || lottieDrawable != null && !lottieDrawable.hasBitmap(); - } - - if (customDraw( - canvas, - animation, lottieDrawable, - currentMediaDrawable, mediaShader, - currentImageDrawable, imageShader, - currentThumbDrawable, thumbShader, - crossfadeWithOldImage, crossfadingWithThumb, - crossfadeImage, crossfadeShader, - staticThumbDrawable, - currentAlpha, previousAlpha, overrideAlpha, - roundRadius, backgroundThreadDrawHolder - )) { - return true; + colorFilter = this.colorFilter; } if (animation != null) { @@ -1831,33 +1874,38 @@ public boolean draw(Canvas canvas, BackgroundThreadDrawHolder backgroundThreadDr delegate.onAnimationReady(this); } } - int orientation = 0; + int orientation = 0, invert = 0; BitmapShader shaderToUse = null; if (!forcePreview && currentMediaDrawable != null && !animationNotReady) { drawable = currentMediaDrawable; shaderToUse = mediaShader; orientation = imageOrientation; + invert = imageInvert; } else if (!forcePreview && currentImageDrawable != null && (!animationNotReady || currentMediaDrawable != null)) { drawable = currentImageDrawable; shaderToUse = imageShader; orientation = imageOrientation; + invert = imageInvert; animationNotReady = false; } else if (crossfadeImage != null && !crossfadingWithThumb) { drawable = crossfadeImage; shaderToUse = crossfadeShader; orientation = imageOrientation; + invert = imageInvert; + } else if (currentThumbDrawable != null) { + drawable = currentThumbDrawable; + shaderToUse = thumbShader; + orientation = thumbOrientation; + invert = thumbInvert; } else if (staticThumbDrawable instanceof BitmapDrawable) { drawable = staticThumbDrawable; - if (useRoundForThumb && thumbShader == null) { + if (useRoundForThumb && staticThumbShader == null) { updateDrawableRadius(staticThumbDrawable); - thumbShader = this.thumbShader; + staticThumbShader = this.staticThumbShader; } - shaderToUse = thumbShader; - orientation = thumbOrientation; - } else if (currentThumbDrawable != null) { - drawable = currentThumbDrawable; - shaderToUse = thumbShader; + shaderToUse = staticThumbShader; orientation = thumbOrientation; + invert = thumbInvert; } float crossfadeProgress = currentAlpha; @@ -1868,17 +1916,17 @@ public boolean draw(Canvas canvas, BackgroundThreadDrawHolder backgroundThreadDr if (drawable != null) { if (crossfadeAlpha != 0) { if (previousAlpha != 1f && (drawable == currentImageDrawable || drawable == currentMediaDrawable) && staticThumbDrawable != null) { - if (useRoundForThumb && thumbShader == null) { + if (useRoundForThumb && staticThumbShader == null) { updateDrawableRadius(staticThumbDrawable); - thumbShader = this.thumbShader; + staticThumbShader = this.staticThumbShader; } - drawDrawable(canvas, staticThumbDrawable, (int) (overrideAlpha * 255), thumbShader, orientation, backgroundThreadDrawHolder); + drawDrawable(canvas, staticThumbDrawable, (int) (overrideAlpha * 255), staticThumbShader, orientation, invert, backgroundThreadDrawHolder); } if (crossfadeWithThumb && animationNotReady) { - drawDrawable(canvas, drawable, (int) (overrideAlpha * 255), shaderToUse, orientation, backgroundThreadDrawHolder); + drawDrawable(canvas, drawable, (int) (overrideAlpha * 255), shaderToUse, orientation, invert, backgroundThreadDrawHolder); } else { + Drawable thumbDrawable = null; if (crossfadeWithThumb && currentAlpha != 1.0f) { - Drawable thumbDrawable = null; BitmapShader thumbShaderToUse = null; if (drawable == currentImageDrawable || drawable == currentMediaDrawable) { if (crossfadeImage != null) { @@ -1889,20 +1937,20 @@ public boolean draw(Canvas canvas, BackgroundThreadDrawHolder backgroundThreadDr thumbShaderToUse = thumbShader; } else if (staticThumbDrawable != null) { thumbDrawable = staticThumbDrawable; - if (useRoundForThumb && thumbShader == null) { + if (useRoundForThumb && staticThumbShader == null) { updateDrawableRadius(staticThumbDrawable); - thumbShader = this.thumbShader; + staticThumbShader = this.staticThumbShader; } - thumbShaderToUse = thumbShader; + thumbShaderToUse = staticThumbShader; } } else if (drawable == currentThumbDrawable || drawable == crossfadeImage) { if (staticThumbDrawable != null) { thumbDrawable = staticThumbDrawable; - if (useRoundForThumb && thumbShader == null) { + if (useRoundForThumb && staticThumbShader == null) { updateDrawableRadius(staticThumbDrawable); - thumbShader = this.thumbShader; + staticThumbShader = this.staticThumbShader; } - thumbShaderToUse = thumbShader; + thumbShaderToUse = staticThumbShader; } } else if (drawable == staticThumbDrawable) { if (crossfadeImage != null) { @@ -1917,7 +1965,7 @@ public boolean draw(Canvas canvas, BackgroundThreadDrawHolder backgroundThreadDr } else { alpha = (int) (overrideAlpha * previousAlpha * 255); } - drawDrawable(canvas, thumbDrawable, alpha, thumbShaderToUse, thumbOrientation, backgroundThreadDrawHolder); + drawDrawable(canvas, thumbDrawable, alpha, thumbShaderToUse, thumbOrientation, thumbInvert, backgroundThreadDrawHolder); if (alpha != 255 && thumbDrawable instanceof Emoji.EmojiDrawable) { thumbDrawable.setAlpha(255); } @@ -1938,13 +1986,13 @@ public boolean draw(Canvas canvas, BackgroundThreadDrawHolder backgroundThreadDr float s = 1f + crossfadeByScale * (1f - CubicBezierInterpolator.EASE_IN.getInterpolation(crossfadeProgress)); canvas.scale(s, s, getCenterX(), getCenterY()); } - drawDrawable(canvas, drawable, (int) (overrideAlpha * currentAlpha * 255), shaderToUse, orientation, backgroundThreadDrawHolder); + drawDrawable(canvas, drawable, (int) (overrideAlpha * currentAlpha * 255), shaderToUse, orientation, invert, backgroundThreadDrawHolder); if (restore) { canvas.restore(); } } } else { - drawDrawable(canvas, drawable, (int) (overrideAlpha * 255), shaderToUse, orientation, backgroundThreadDrawHolder); + drawDrawable(canvas, drawable, (int) (overrideAlpha * 255), shaderToUse, orientation, invert, backgroundThreadDrawHolder); } checkAlphaAnimation(animationNotReady && crossfadeWithThumb, backgroundThreadDrawHolder); @@ -1953,7 +2001,7 @@ public boolean draw(Canvas canvas, BackgroundThreadDrawHolder backgroundThreadDr if (staticThumbDrawable instanceof VectorAvatarThumbDrawable) { ((VectorAvatarThumbDrawable) staticThumbDrawable).setParent(this); } - drawDrawable(canvas, staticThumbDrawable, (int) (overrideAlpha * 255), null, thumbOrientation, backgroundThreadDrawHolder); + drawDrawable(canvas, staticThumbDrawable, (int) (overrideAlpha * 255), null, thumbOrientation, thumbInvert, backgroundThreadDrawHolder); checkAlphaAnimation(animationNotReady, backgroundThreadDrawHolder); result = true; } else { @@ -2251,6 +2299,15 @@ public void setImageCoords(Rect bounds) { } } + public void setImageCoords(RectF bounds) { + if (bounds != null) { + imageX = bounds.left; + imageY = bounds.top; + imageW = bounds.width(); + imageH = bounds.height(); + } + } + public void setSideClip(float value) { sideClip = value; } @@ -2400,6 +2457,14 @@ public void setRoundRadius(int[] value) { } } + public void setMark(Object mark) { + this.mark = mark; + } + + public Object getMark() { + return mark; + } + public void setCurrentAccount(int value) { currentAccount = value; } @@ -2620,6 +2685,7 @@ protected boolean setImageBitmapByKey(Drawable drawable, String key, int type, b if (drawable instanceof ExtendedBitmapDrawable) { imageOrientation = ((ExtendedBitmapDrawable) drawable).getOrientation(); + imageInvert = ((ExtendedBitmapDrawable) drawable).getInvert(); } updateDrawableRadius(drawable); @@ -2708,6 +2774,7 @@ protected boolean setImageBitmapByKey(Drawable drawable, String key, int type, b currentThumbDrawable = drawable; if (drawable instanceof ExtendedBitmapDrawable) { thumbOrientation = ((ExtendedBitmapDrawable) drawable).getOrientation(); + thumbInvert = ((ExtendedBitmapDrawable) drawable).getInvert(); } updateDrawableRadius(drawable); @@ -2770,7 +2837,7 @@ public void setMediaStartEndTime(long startTime, long endTime) { } } - private void recycleBitmap(String newKey, int type) { + public void recycleBitmap(String newKey, int type) { String key; Drawable image; if (type == TYPE_MEDIA) { @@ -2936,6 +3003,7 @@ public void startCrossfadeFromStaticThumb(Drawable thumb) { currentThumbKey = null; currentThumbDrawable = null; thumbShader = null; + staticThumbShader = null; roundPaint.setShader(null); setStaticDrawable(thumb); crossfadeWithThumb = true; @@ -3008,7 +3076,16 @@ public void setCurrentTime(long time) { } public void setFileLoadingPriority(int fileLoadingPriority) { - this.fileLoadingPriority = fileLoadingPriority; + if (this.fileLoadingPriority != fileLoadingPriority) { + this.fileLoadingPriority = fileLoadingPriority; + if (attachedToWindow) { + ImageLoader.getInstance().changeFileLoadingPriorityForImageReceiver(this); + } + } + } + + public void bumpPriority() { + ImageLoader.getInstance().changeFileLoadingPriorityForImageReceiver(this); } public int getFileLoadingPriority() { @@ -3031,6 +3108,7 @@ public BackgroundThreadDrawHolder setDrawInBackgroundThread(BackgroundThreadDraw holder.imageShader = imageShader; holder.thumbDrawable = currentThumbDrawable; holder.thumbShader = thumbShader; + holder.staticThumbShader = staticThumbShader; holder.staticThumbDrawable = staticThumbDrawable; holder.crossfadeImage = crossfadeImage; holder.colorFilter = colorFilter; @@ -3053,6 +3131,7 @@ public static class BackgroundThreadDrawHolder { public float overrideAlpha; public long time; public int threadIndex; + public BitmapShader staticThumbShader; private AnimatedFileDrawable animation; private RLottieDrawable lottieDrawable; private int[] roundRadius = new int[4]; @@ -3087,6 +3166,7 @@ public void release() { imageShader = null; thumbDrawable = null; thumbShader = null; + staticThumbShader = null; staticThumbDrawable = null; crossfadeImage = null; colorFilter = null; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/LiteMode.java b/TMessagesProj/src/main/java/org/telegram/messenger/LiteMode.java index 3f17d5e9d3..aa63062572 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/LiteMode.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/LiteMode.java @@ -304,7 +304,10 @@ private static void onFlagsUpdate(int oldValue, int newValue) { AnimatedEmojiDrawable.updateAll(); } if ((changedFlags & FLAG_CHAT_BACKGROUND) > 0) { - Theme.reloadWallpaper(); + SvgHelper.SvgDrawable.updateLiteValues(); + } + if ((changedFlags & FLAG_CHAT_BACKGROUND) > 0) { + Theme.reloadWallpaper(true); } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/LocaleController.java b/TMessagesProj/src/main/java/org/telegram/messenger/LocaleController.java index fd1260be49..ea92a518a8 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/LocaleController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/LocaleController.java @@ -21,7 +21,6 @@ import android.telephony.TelephonyManager; import android.text.TextUtils; import android.text.format.DateFormat; -import android.util.Log; import android.util.Xml; import android.view.Gravity; @@ -32,6 +31,7 @@ import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; +import org.telegram.ui.RestrictedLanguagesSelectActivity; import org.xmlpull.v1.XmlPullParser; import java.io.BufferedWriter; @@ -48,7 +48,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Locale; -import java.util.Map; import java.util.TimeZone; import tw.nekomimi.nekogram.NekoConfig; @@ -1123,6 +1122,7 @@ public int applyLanguage(final LocaleInfo localeInfo, boolean override, boolean } else { NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.reloadInterface); } + RestrictedLanguagesSelectActivity.invalidateRestrictedLanguages(); if (onDone != null) { onDone.run(); } @@ -1265,7 +1265,7 @@ public static String formatPluralStringComma(String key, int plural, char symbol } String param = getInstance().stringForQuantity(getInstance().currentPluralRules.quantityForNumber(plural)); param = key + "_" + param; - StringBuilder stringBuilder = new StringBuilder(String.format(Locale.US, "%d", plural)); + StringBuilder stringBuilder = new StringBuilder(String.format("%d", plural)); for (int a = stringBuilder.length() - 3; a > 0; a -= 3) { stringBuilder.insert(a, symbol); } @@ -1808,6 +1808,37 @@ public static String formatSeenDate(long date) { return "LOC_ERR"; } + public static String formatStoryDate(long date) { + try { + date *= 1000; + Calendar rightNow = Calendar.getInstance(); + int day = rightNow.get(Calendar.DAY_OF_YEAR); + int year = rightNow.get(Calendar.YEAR); + long timeInMillis = rightNow.getTimeInMillis(); + rightNow.setTimeInMillis(date); + int dateDay = rightNow.get(Calendar.DAY_OF_YEAR); + int dateYear = rightNow.get(Calendar.YEAR); + + if (timeInMillis - date < 1000 * 60) { + return LocaleController.getString("RightNow", R.string.RightNow); + } else if (timeInMillis - date < 1000 * 60 * 60) { + int minutesAgo = (int) ((timeInMillis - date) / (1000 * 60)); + return LocaleController.formatPluralString("MinutesAgo", minutesAgo, minutesAgo); + } else if (dateDay == day && year == dateYear) { + return LocaleController.formatString("TodayAtFormattedWithToday", R.string.TodayAtFormattedWithToday, getInstance().formatterDay.format(new Date(date))); + } else if (dateDay + 1 == day && year == dateYear) { + return LocaleController.formatString("YesterdayAtFormatted", R.string.YesterdayAtFormatted, getInstance().formatterDay.format(new Date(date))); + } else if (Math.abs(System.currentTimeMillis() - date) < 31536000000L) { + return LocaleController.formatString("formatDateAtTime", R.string.formatDateAtTime, getInstance().formatterDayMonth.format(new Date(date)), getInstance().formatterDay.format(new Date(date))); + } else { + return LocaleController.formatString("formatDateAtTime", R.string.formatDateAtTime, getInstance().formatterYear.format(new Date(date)), getInstance().formatterDay.format(new Date(date))); + } + } catch (Exception e) { + FileLog.e(e); + } + return "LOC_ERR"; + } + public static String formatDateCallLog(long date) { try { date *= 1000; @@ -2435,6 +2466,8 @@ public void saveRemoteLocaleStrings(LocaleInfo localeInfo, final TLRPC.TL_langPa FileLog.e("update locale to " + config.locale); ApplicationLoader.applicationContext.getResources().updateConfiguration(config, ApplicationLoader.applicationContext.getResources().getDisplayMetrics()); changingConfiguration = false; + + RestrictedLanguagesSelectActivity.invalidateRestrictedLanguages(); } else { FileLog.d("saveRemoteLocaleStrings: currentLocaleInfo != localeInfo, do nothing"); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java index 3e25aca517..21fa9c09e1 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java @@ -37,6 +37,7 @@ import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioRecord; +import android.media.MediaCodec; import android.media.MediaCodecInfo; import android.media.MediaCodecList; import android.media.MediaExtractor; @@ -53,7 +54,7 @@ import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; import android.text.TextUtils; -import android.util.Log; +import android.util.Pair; import android.util.SparseArray; import android.view.HapticFeedbackConstants; import android.view.TextureView; @@ -71,6 +72,7 @@ import org.telegram.messenger.audioinfo.AudioInfo; import org.telegram.messenger.video.MediaCodecVideoConvertor; import org.telegram.messenger.voip.VoIPService; +import org.telegram.tgnet.AbstractSerializedData; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; @@ -100,10 +102,10 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; -import java.util.List; import java.util.Locale; import java.util.Timer; import java.util.TimerTask; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import tw.nekomimi.nekogram.NekoConfig; @@ -121,7 +123,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, public static native int isOpusFile(String path); - public native byte[] getWaveform(String path); + public static native byte[] getWaveform(String path); public native byte[] getWaveform2(short[] array, int length); @@ -220,6 +222,88 @@ public static class SavedFilterState { public org.telegram.ui.Components.Point blurExcludePoint; public float blurExcludeBlurSize; public float blurAngle; + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeFloat(enhanceValue); + stream.writeFloat(softenSkinValue); + stream.writeFloat(exposureValue); + stream.writeFloat(contrastValue); + stream.writeFloat(warmthValue); + stream.writeFloat(saturationValue); + stream.writeFloat(fadeValue); + stream.writeInt32(tintShadowsColor); + stream.writeInt32(tintHighlightsColor); + stream.writeFloat(highlightsValue); + stream.writeFloat(shadowsValue); + stream.writeFloat(vignetteValue); + stream.writeFloat(grainValue); + stream.writeInt32(blurType); + stream.writeFloat(sharpenValue); + curvesToolValue.serializeToStream(stream); + stream.writeFloat(blurExcludeSize); + if (blurExcludePoint == null) { + stream.writeInt32(0x56730bcc); + } else { + stream.writeInt32(0xDEADBEEF); + stream.writeFloat(blurExcludePoint.x); + stream.writeFloat(blurExcludePoint.y); + } + stream.writeFloat(blurExcludeBlurSize); + stream.writeFloat(blurAngle); + } + + public void readParams(AbstractSerializedData stream, boolean exception) { + enhanceValue = stream.readFloat(exception); + softenSkinValue = stream.readFloat(exception); + exposureValue = stream.readFloat(exception); + contrastValue = stream.readFloat(exception); + warmthValue = stream.readFloat(exception); + saturationValue = stream.readFloat(exception); + fadeValue = stream.readFloat(exception); + tintShadowsColor = stream.readInt32(exception); + tintHighlightsColor = stream.readInt32(exception); + highlightsValue = stream.readFloat(exception); + shadowsValue = stream.readFloat(exception); + vignetteValue = stream.readFloat(exception); + grainValue = stream.readFloat(exception); + blurType = stream.readInt32(exception); + sharpenValue = stream.readFloat(exception); + curvesToolValue.readParams(stream, exception); + blurExcludeSize = stream.readFloat(exception); + int magic = stream.readInt32(exception); + if (magic == 0x56730bcc) { + blurExcludePoint = null; + } else { + if (blurExcludePoint == null) { + blurExcludePoint = new org.telegram.ui.Components.Point(); + } + blurExcludePoint.x = stream.readFloat(exception); + blurExcludePoint.y = stream.readFloat(exception); + } + blurExcludeBlurSize = stream.readFloat(exception); + blurAngle = stream.readFloat(exception); + } + + public boolean isEmpty() { + return ( + Math.abs(enhanceValue) < 0.1f && + Math.abs(softenSkinValue) < 0.1f && + Math.abs(exposureValue) < 0.1f && + Math.abs(contrastValue) < 0.1f && + Math.abs(warmthValue) < 0.1f && + Math.abs(saturationValue) < 0.1f && + Math.abs(fadeValue) < 0.1f && + tintShadowsColor == 0 && + tintHighlightsColor == 0 && + Math.abs(highlightsValue) < 0.1f && + Math.abs(shadowsValue) < 0.1f && + Math.abs(vignetteValue) < 0.1f && + Math.abs(grainValue) < 0.1f && + blurType == 0 && + Math.abs(sharpenValue) < 0.1f && + Math.abs(blurExcludeSize) < 0.1f + ); + } } public static class CropState { @@ -242,6 +326,8 @@ public static class CropState { public boolean freeform; public float lockedAspectRatio; + public Matrix useMatrix; + public boolean initied; @Override @@ -268,8 +354,16 @@ public CropState clone() { cloned.lockedAspectRatio = this.lockedAspectRatio; cloned.initied = this.initied; + cloned.useMatrix = this.useMatrix; return cloned; } + + public boolean isEmpty() { + return (matrix == null || matrix.isIdentity()) && (useMatrix == null || useMatrix.isIdentity()) && cropPw == 1 && cropPh == 1 && + cropScale == 1 && cropRotate == 0 && transformWidth == 0 && transformHeight == 0 && + transformRotation == 0 && !mirrored && stateScale == 0 && scale == 0 && width == 0 && height == 0 && !freeform && lockedAspectRatio == 0; + + } } public static class MediaEditState { @@ -356,6 +450,7 @@ public static class PhotoEntry extends MediaEditState { public long size; public String path; public int orientation; + public int invert; public boolean isVideo; public boolean isMuted; public boolean canDeleteAfter; @@ -365,7 +460,9 @@ public static class PhotoEntry extends MediaEditState { public boolean isAttachSpoilerRevealed; public TLRPC.VideoSize emojiMarkup; - public PhotoEntry(int bucketId, int imageId, long dateTaken, String path, int orientation, boolean isVideo, int width, int height, long size) { + public int gradientTopColor, gradientBottomColor; + + public PhotoEntry(int bucketId, int imageId, long dateTaken, String path, int orientationOrDuration, boolean isVideo, int width, int height, long size) { this.bucketId = bucketId; this.imageId = imageId; this.dateTaken = dateTaken; @@ -374,13 +471,25 @@ public PhotoEntry(int bucketId, int imageId, long dateTaken, String path, int or this.height = height; this.size = size; if (isVideo) { - this.duration = orientation; + this.duration = orientationOrDuration; } else { - this.orientation = orientation; + this.orientation = orientationOrDuration; } this.isVideo = isVideo; } + public PhotoEntry setOrientation(Pair rotationAndInvert) { + this.orientation = rotationAndInvert.first; + this.invert = rotationAndInvert.second; + return this; + } + + public PhotoEntry setOrientation(int rotation, int invert) { + this.orientation = rotation; + this.invert = invert; + return this; + } + @Override public void copyFrom(MediaEditState state) { super.copyFrom(state); @@ -515,16 +624,19 @@ public String getPathToAttach() { private static final int AUDIO_NO_FOCUS_NO_DUCK = 0; private static final int AUDIO_NO_FOCUS_CAN_DUCK = 1; private static final int AUDIO_FOCUSED = 2; + private static final ConcurrentHashMap cachedEncoderBitrates = new ConcurrentHashMap<>(); private static class VideoConvertMessage { public MessageObject messageObject; public VideoEditedInfo videoEditedInfo; public int currentAccount; + public boolean foreground; - public VideoConvertMessage(MessageObject object, VideoEditedInfo info) { + public VideoConvertMessage(MessageObject object, VideoEditedInfo info, boolean foreground) { messageObject = object; currentAccount = messageObject.currentAccount; videoEditedInfo = info; + this.foreground = foreground; } } @@ -546,6 +658,7 @@ public VideoConvertMessage(MessageObject object, VideoEditedInfo info) { public boolean isSilent = false; private boolean isPaused = false; + private boolean wasPlayingAudioBeforePause = false; private VideoPlayer audioPlayer = null; private VideoPlayer emojiSoundPlayer = null; private int emojiSoundPlayerNum = 0; @@ -616,6 +729,7 @@ public void run() { private long recordDialogId; private MessageObject recordReplyingMsg; private MessageObject recordReplyingTopMsg; + private TLRPC.StoryItem recordReplyingStory; private short[] recordSamples = new short[1024]; private long samplesCount; @@ -1478,8 +1592,12 @@ public void didReceivedNotification(int id, int account, Object... args) { } } else if (id == NotificationCenter.playerDidStartPlaying) { VideoPlayer p = (VideoPlayer) args[0]; - if (!MediaController.getInstance().isCurrentPlayer(p)) { - MediaController.getInstance().pauseMessage(MediaController.getInstance().getPlayingMessageObject()); + if (!isCurrentPlayer(p)) { + MessageObject message = getPlayingMessageObject(); + if(message != null && isPlayingMessage(message) && !isMessagePaused() && (message.isMusic() || message.isVoice())){ + wasPlayingAudioBeforePause = true; + } + pauseMessage(message); } } } @@ -1496,6 +1614,33 @@ public boolean isRecordingOrListeningByProximity() { return proximityTouched && (isRecordingAudio() || playingMessageObject != null && (playingMessageObject.isVoice() || playingMessageObject.isRoundVideo())); } + private boolean forbidRaiseToListen() { + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + AudioDeviceInfo[] devices = NotificationsController.audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS); + for (AudioDeviceInfo device : devices) { + final int type = device.getType(); + if (( + type == AudioDeviceInfo.TYPE_BLUETOOTH_A2DP || + type == AudioDeviceInfo.TYPE_BLUETOOTH_SCO || + type == AudioDeviceInfo.TYPE_BLE_HEADSET || + type == AudioDeviceInfo.TYPE_BLE_SPEAKER || + type == AudioDeviceInfo.TYPE_WIRED_HEADPHONES || + type == AudioDeviceInfo.TYPE_WIRED_HEADSET + ) && device.isSink()) { + return true; + } + } + return false; + } else { + return NotificationsController.audioManager.isWiredHeadsetOn() || NotificationsController.audioManager.isBluetoothA2dpOn() || NotificationsController.audioManager.isBluetoothScoOn(); + } + } catch (Exception e) { + FileLog.e(e); + } + return false; + } + @Override public void onSensorChanged(SensorEvent event) { if (!sensorsStarted || VoIPService.getSharedInstance() != null) { @@ -1653,7 +1798,7 @@ public void onSensorChanged(SensorEvent event) { if (raisedToBack == minCount || accelerometerVertical) { lastAccelerometerDetected = System.currentTimeMillis(); } - if (proximityTouched && (raisedToBack == minCount || accelerometerVertical || System.currentTimeMillis() - lastAccelerometerDetected < 60) && !NotificationsController.audioManager.isWiredHeadsetOn() && !NotificationsController.audioManager.isBluetoothA2dpOn() && !VoIPService.isAnyKindOfCallActive() && !manualRecording) { + if (proximityTouched && (raisedToBack == minCount || accelerometerVertical || System.currentTimeMillis() - lastAccelerometerDetected < 60) && !VoIPService.isAnyKindOfCallActive() && !manualRecording && !forbidRaiseToListen()) { if (SharedConfig.enabledRaiseTo(true) && playingMessageObject == null && recordStartRunnable == null && recordingAudio == null && !PhotoViewer.getInstance().isVisible() && ApplicationLoader.isScreenOn && !inputFieldHasText && allowStartRecord && raiseChat != null && !callInProgress) { if (!raiseToEarRecord) { if (BuildVars.LOGS_ENABLED) { @@ -1663,7 +1808,7 @@ public void onSensorChanged(SensorEvent event) { if (!raiseChat.playFirstUnreadVoiceMessage()) { raiseToEarRecord = true; useFrontSpeaker = false; - startRecording(raiseChat.getCurrentAccount(), raiseChat.getDialogId(), null, raiseChat.getThreadMessage(), raiseChat.getClassGuid(), false); + startRecording(raiseChat.getCurrentAccount(), raiseChat.getDialogId(), null, raiseChat.getThreadMessage(), null, raiseChat.getClassGuid(), false); } if (useFrontSpeaker) { setUseFrontSpeaker(true); @@ -1692,7 +1837,7 @@ public void onSensorChanged(SensorEvent event) { countLess = 0; } else if (proximityTouched && ((accelerometerSensor == null || linearSensor == null) && gravitySensor == null || ignoreAccelerometerGestures()) && !VoIPService.isAnyKindOfCallActive()) { if (playingMessageObject != null && !ApplicationLoader.mainInterfacePaused && (playingMessageObject.isVoice() || playingMessageObject.isRoundVideo()) && SharedConfig.enabledRaiseTo(false)) { - if (!useFrontSpeaker && !NotificationsController.audioManager.isWiredHeadsetOn() && !NotificationsController.audioManager.isBluetoothA2dpOn() && !manualRecording) { + if (!useFrontSpeaker && !manualRecording && !forbidRaiseToListen()) { if (BuildVars.LOGS_ENABLED) { FileLog.d("start listen by proximity only"); } @@ -1752,7 +1897,7 @@ public void startRecordingIfFromSpeaker() { return; } raiseToEarRecord = true; - startRecording(raiseChat.getCurrentAccount(), raiseChat.getDialogId(), null, raiseChat.getThreadMessage(), raiseChat.getClassGuid(), false); + startRecording(raiseChat.getCurrentAccount(), raiseChat.getDialogId(), null, raiseChat.getThreadMessage(), null, raiseChat.getClassGuid(), false); ignoreOnPause = true; } @@ -1811,10 +1956,10 @@ public void startRaiseToEarSensors(ChatActivity chatActivity) { if (chatActivity == null || accelerometerSensor == null && (gravitySensor == null || linearAcceleration == null) || proximitySensor == null) { return; } - raiseChat = chatActivity; - if (!SharedConfig.enabledRaiseTo(true) && (playingMessageObject == null || !playingMessageObject.isVoice() && !playingMessageObject.isRoundVideo())) { + if (!SharedConfig.enabledRaiseTo(false) && (playingMessageObject == null || !playingMessageObject.isVoice() && !playingMessageObject.isRoundVideo())) { return; } + raiseChat = chatActivity; if (!sensorsStarted) { gravity[0] = gravity[1] = gravity[2] = 0; linearAcceleration[0] = linearAcceleration[1] = linearAcceleration[2] = 0; @@ -1841,12 +1986,14 @@ public void startRaiseToEarSensors(ChatActivity chatActivity) { } } - public void stopRaiseToEarSensors(ChatActivity chatActivity, boolean fromChat) { + public void stopRaiseToEarSensors(ChatActivity chatActivity, boolean fromChat, boolean stopRecording) { if (ignoreOnPause) { ignoreOnPause = false; return; } - stopRecording(fromChat ? 2 : 0, false, 0); + if (stopRecording) { + stopRecording(fromChat ? 2 : 0, false, 0); + } if (!sensorsStarted || ignoreOnPause || accelerometerSensor == null && (gravitySensor == null || linearAcceleration == null) || proximitySensor == null || raiseChat != chatActivity) { return; } @@ -1952,7 +2099,7 @@ public void onAnimationEnd(Animator animation) { isPaused = false; if (!useFrontSpeaker && !SharedConfig.enabledRaiseTo(true)) { ChatActivity chat = raiseChat; - stopRaiseToEarSensors(raiseChat, false); + stopRaiseToEarSensors(raiseChat, false, false); raiseChat = chat; } if (proximityWakeLock != null && proximityWakeLock.isHeld() && !proximityTouched) { @@ -2855,7 +3002,7 @@ public void playEmojiSound(AccountInstance accountInstance, String emoji, Messag if (emojiSoundPlayer != null) { emojiSoundPlayer.releasePlayer(true); } - emojiSoundPlayer = new VideoPlayer(false); + emojiSoundPlayer = new VideoPlayer(false, false); emojiSoundPlayer.setDelegate(new VideoPlayer.VideoPlayerDelegate() { @Override public void onStateChanged(boolean playWhenReady, int playbackState) { @@ -3425,7 +3572,7 @@ public boolean needUpdate() { return true; } - private boolean ignoreAccelerometerGestures() { + public static boolean ignoreAccelerometerGestures() { return Build.MANUFACTURER.equalsIgnoreCase("samsung"); } @@ -3471,6 +3618,14 @@ public boolean isCurrentPlayer(VideoPlayer player) { return videoPlayer == player || audioPlayer == player; } + public void tryResumePausedAudio() { + MessageObject message = getPlayingMessageObject(); + if (message!= null && isMessagePaused() && wasPlayingAudioBeforePause && (message.isVoice() || message.isMusic())) { + playMessage(message); + } + wasPlayingAudioBeforePause = false; + } + public boolean pauseMessage(MessageObject messageObject) { if (audioPlayer == null && videoPlayer == null || messageObject == null || playingMessageObject == null || !isSamePlayingMessage(messageObject)) { return false; @@ -3580,9 +3735,10 @@ public boolean isDownloadingCurrentMessage() { return downloadingCurrentMessage; } - public void setReplyingMessage(MessageObject replyToMsg, MessageObject replyToTopMsg) { + public void setReplyingMessage(MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.StoryItem storyItem) { recordReplyingMsg = replyToMsg; recordReplyingTopMsg = replyToTopMsg; + recordReplyingStory = storyItem; } public void requestAudioFocus(boolean request) { @@ -3601,7 +3757,7 @@ public void requestAudioFocus(boolean request) { } } - public void startRecording(int currentAccount, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, int guid, boolean manual) { + public void startRecording(int currentAccount, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.StoryItem replyStory, int guid, boolean manual) { boolean paused = false; if (playingMessageObject != null && isPlayingMessage(playingMessageObject) && !isMessagePaused()) { paused = true; @@ -3674,6 +3830,7 @@ public boolean delete() { recordingCurrentAccount = currentAccount; recordReplyingMsg = replyToMsg; recordReplyingTopMsg = replyToTopMsg; + recordReplyingStory = replyStory; fileBuffer.rewind(); AudioEnhance.INSTANCE.initVoiceEnhance(audioRecorder); @@ -3783,7 +3940,9 @@ private void stopRecordingInternal(final int send, boolean notify, int scheduleD if (duration > 700) { NotificationCenter.getInstance(recordingCurrentAccount).postNotificationName(NotificationCenter.beforeAudioDidSent, recordingGuid, send == 2 ? audioToSend : null, send == 2 ? recordingAudioFileToSend.getAbsolutePath() : null); if (send == 1) { - SendMessagesHelper.getInstance(recordingCurrentAccount).sendMessage(audioToSend, null, recordingAudioFileToSend.getAbsolutePath(), recordDialogId, recordReplyingMsg, recordReplyingTopMsg, null, null, null, null, notify, scheduleDate, 0, null, null, false); + SendMessagesHelper.SendMessageParams params = SendMessagesHelper.SendMessageParams.of(audioToSend, null, recordingAudioFileToSend.getAbsolutePath(), recordDialogId, recordReplyingMsg, recordReplyingTopMsg, null, null, null, null, notify, scheduleDate, 0, null, null, false); + params.replyToStoryItem = recordReplyingStory; + SendMessagesHelper.getInstance(recordingCurrentAccount).sendMessage(params); } NotificationCenter.getInstance(recordingCurrentAccount).postNotificationName(NotificationCenter.audioDidSent, recordingGuid, send == 2 ? audioToSend : null, send == 2 ? recordingAudioFileToSend.getAbsolutePath() : null); } else { @@ -4112,7 +4271,11 @@ public static void saveFile(String fullPath, Context context, final int type, fi saveFile(fullPath, context, type, name, mime, null); } - public static void saveFile(String fullPath, Context context, final int type, final String name, final String mime, final Runnable onSaved) { + public static void saveFile(String fullPath, Context context, final int type, final String name, final String mime, final Utilities.Callback onSaved) { + saveFile(fullPath, context, type, name, mime, onSaved, true); + } + + public static void saveFile(String fullPath, Context context, final int type, final String name, final String mime, final Utilities.Callback onSaved, boolean showProgress) { if (fullPath == null || context == null) { return; } @@ -4154,11 +4317,12 @@ public static void saveFile(String fullPath, Context context, final int type, fi new Thread(() -> { try { - + Uri uri; boolean result = true; final String folderName = NekoConfig.customSavePath.String(); if (Build.VERSION.SDK_INT >= 29) { - result = saveFileInternal(type, sourceFile, name); + uri = saveFileInternal(type, sourceFile, name); + result = uri != null; } else { File destFile; if (type == 0) { @@ -4240,9 +4404,10 @@ public static void saveFile(String fullPath, Context context, final int type, fi AndroidUtilities.addMediaToGallery(destFile.getAbsoluteFile()); } } + uri = Uri.fromFile(destFile); } if (result && onSaved != null) { - AndroidUtilities.runOnUIThread(onSaved); + AndroidUtilities.runOnUIThread(() -> onSaved.run(uri)); } } catch (Exception e) { FileLog.e(e); @@ -4262,7 +4427,7 @@ public static void saveFile(String fullPath, Context context, final int type, fi } @RequiresApi(api = Build.VERSION_CODES.Q) - private static boolean saveFileInternal(int type, File sourceFile, String filename) { + private static Uri saveFileInternal(int type, File sourceFile, String filename) { try { int selectedType = type; ContentValues contentValues = new ContentValues(); @@ -4325,10 +4490,10 @@ private static boolean saveFileInternal(int type, File sourceFile, String filena AndroidUtilities.copyFile(fileInputStream, outputStream); fileInputStream.close(); } - return true; + return dstUri; } catch (Exception e) { FileLog.e(e); - return false; + return null; } } @@ -4756,10 +4921,10 @@ private static void broadcastNewPhotos(final int guid, final ArrayList= 0 && extractor.getTrackFormat(videoIndex).getString(MediaFormat.KEY_MIME).equals(MediaController.VIDEO_MIME_TYPE); + } catch (Exception e) { + FileLog.e(e); + } finally { + extractor.release(); + } + return false; + } + private void didWriteData(final VideoConvertMessage message, final File file, final boolean last, final long lastFrameTimestamp, long availableSize, final boolean error, final float progress) { final boolean firstWrite = message.videoEditedInfo.videoConvertFirstWrite; if (firstWrite) { @@ -4994,7 +5175,7 @@ private boolean convertVideo(final VideoConvertMessage convertMessage) { int framerate = info.framerate; int bitrate = info.bitrate; int originalBitrate = info.originalBitrate; - boolean isSecret = DialogObject.isEncryptedDialog(messageObject.getDialogId()); + boolean isSecret = DialogObject.isEncryptedDialog(messageObject.getDialogId()) || info.forceFragmenting; final File cacheFile = new File(messageObject.messageOwner.attachPath); if (cacheFile.exists()) { cacheFile.delete(); @@ -5086,7 +5267,14 @@ public void didWriteData(long availableSize, float progress) { info.isPhoto, info.cropState, info.roundVideo, - callback); + callback, + info.gradientTopColor, + info.gradientBottomColor, + info.muted, + info.isStory, + info.hdrInfo, + info.parts + ); boolean canceled = info.canceled; @@ -5157,6 +5345,39 @@ public static int makeVideoBitrate(int originalHeight, int originalWidth, int or return Math.max(remeasuredBitrate, minBitrate); } + /** + * Some encoders(e.g. OMX.Exynos) can forcibly raise bitrate during encoder initialization. + */ + public static int extractRealEncoderBitrate(int width, int height, int bitrate, boolean tryHevc) { + String cacheKey = width + "" + height + "" + bitrate; + Integer cachedBitrate = cachedEncoderBitrates.get(cacheKey); + if (cachedBitrate != null) return cachedBitrate; + try { + MediaCodec encoder = null; + if (tryHevc) { + try { + encoder = MediaCodec.createEncoderByType("video/hevc"); + } catch (Exception ignore) {} + } + if (encoder == null) { + encoder = MediaCodec.createEncoderByType(MediaController.VIDEO_MIME_TYPE); + } + MediaFormat outputFormat = MediaFormat.createVideoFormat(MediaController.VIDEO_MIME_TYPE, width, height); + outputFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); + outputFormat.setInteger("max-bitrate", bitrate); + outputFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate); + outputFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 30); + outputFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1); + encoder.configure(outputFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); + int encoderBitrate = (int) (encoder.getOutputFormat().getInteger(MediaFormat.KEY_BIT_RATE)); + cachedEncoderBitrates.put(cacheKey, encoderBitrate); + encoder.release(); + return encoderBitrate; + } catch (Exception e) { + return bitrate; + } + } + private static int getVideoBitrateWithFactor(float f) { return (int) (f * 2000f * 1000f * 1.13f); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java index f36533e8b9..3601fb6d67 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java @@ -76,6 +76,7 @@ import org.telegram.ui.Components.URLSpanUserMention; import org.telegram.ui.LaunchActivity; import org.telegram.ui.PremiumPreviewFragment; +import org.telegram.ui.Stories.StoriesStorage; import java.io.File; import java.util.ArrayList; @@ -2040,9 +2041,9 @@ private long calcFeaturedStickersHash(boolean emoji, ArrayList> 21; - hash ^= id << 35; - hash ^= id >> 4; + hash ^= hash >> 21; + hash ^= hash << 35; + hash ^= hash >> 4; return hash + id; } @@ -2973,7 +2974,7 @@ public void toggleStickerSet(Context context, TLObject stickerSetObject, int tog } else if (!showTooltip || baseFragment == null) { toggleStickerSetInternal(context, toggle, baseFragment, showSettings, stickerSetObject, stickerSet, type, false); } else { - StickerSetBulletinLayout bulletinLayout = new StickerSetBulletinLayout(context, stickerSetObject, toggle); + StickerSetBulletinLayout bulletinLayout = new StickerSetBulletinLayout(context, stickerSetObject, toggle, null, baseFragment == null ? null : baseFragment.getResourceProvider()); int finalCurrentIndex = NekoConfig.enableStickerPin.Bool() && type == TYPE_IMAGE && PinnedStickerHelper.getInstance(UserConfig.selectedAccount).isPinned(stickerSet.id) ? PinnedStickerHelper.getInstance(UserConfig.selectedAccount).pinnedList.size() : currentIndex; @@ -3116,7 +3117,7 @@ private void toggleStickerSetInternal(Context context, int toggle, BaseFragment markSetInstalling(stickerSet.id, false); }); if (error == null && showTooltip && baseFragment != null) { - Bulletin.make(baseFragment, new StickerSetBulletinLayout(context, stickerSetObject, StickerSetBulletinLayout.TYPE_ADDED), Bulletin.DURATION_SHORT).show(); + Bulletin.make(baseFragment, new StickerSetBulletinLayout(context, stickerSetObject, StickerSetBulletinLayout.TYPE_ADDED, null, baseFragment.getResourceProvider()), Bulletin.DURATION_SHORT).show(); } })); } else { @@ -3496,6 +3497,7 @@ public String getLastSearchQuery() { public final static int MEDIA_PHOTOS_ONLY = 6; public final static int MEDIA_VIDEOS_ONLY = 7; public final static int MEDIA_TYPES_COUNT = 8; + public final static int MEDIA_STORIES = 8; public void loadMedia(long dialogId, int count, int max_id, int min_id, int type, int topicId, int fromCache, int classGuid, int requestIndex) { @@ -3570,9 +3572,9 @@ public void loadMedia(long dialogId, int count, int max_id, int min_id, int type public void getMediaCounts(long dialogId, int topicId, int classGuid) { getMessagesStorage().getStorageQueue().postRunnable(() -> { try { - int[] counts = new int[]{-1, -1, -1, -1, -1, -1, -1, -1}; - int[] countsFinal = new int[]{-1, -1, -1, -1, -1, -1, -1, -1}; - int[] old = new int[]{0, 0, 0, 0, 0, 0, 0, 0}; + int[] counts = new int[]{-1, -1, -1, -1, -1, -1, -1, -1, -1}; + int[] countsFinal = new int[]{-1, -1, -1, -1, -1, -1, -1, -1, -1}; + int[] old = new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0}; SQLiteCursor cursor; if (topicId != 0) { cursor = getMessagesStorage().getDatabase().queryFinalized(String.format(Locale.US, "SELECT type, count, old FROM media_counts_topics WHERE uid = %d AND topic_id = %d", dialogId, topicId)); @@ -4177,6 +4179,7 @@ private void putMediaDatabase(long uid, int topicId, int type, ArrayList arrayLis SQLitePreparedStatement state = getMessagesStorage().getDatabase().executeFast("REPLACE INTO chat_pinned_v2 VALUES(?, ?, ?)"); for (int a = 0, N = arrayList.size(); a < N; a++) { TLRPC.Message message = arrayList.get(a); + MessageObject.normalizeFlags(message); NativeByteBuffer data = new NativeByteBuffer(message.getObjectSize()); message.serializeToStream(data); state.requery(); @@ -5367,6 +5371,7 @@ public void loadReplyMessagesForMessages(ArrayList messages, long for (int b = 0; b < arrayList.size(); b++) { MessageObject object = arrayList.get(b); object.replyMessageObject = messageObject; + object.applyTimestampsHighlightForReplyMsg(); object.messageOwner.reply_to = new TLRPC.TL_messageReplyHeader(); object.messageOwner.reply_to.reply_to_msg_id = messageObject.getId(); } @@ -5396,12 +5401,45 @@ public void loadReplyMessagesForMessages(ArrayList messages, long } else { LongSparseArray>> replyMessageOwners = new LongSparseArray<>(); LongSparseArray> dialogReplyMessagesIds = new LongSparseArray<>(); + LongSparseArray> messagesWithUnknownStories = null; for (int a = 0; a < messages.size(); a++) { MessageObject messageObject = messages.get(a); if (messageObject == null) { continue; } - if (messageObject.getId() > 0 && messageObject.isReply()) { + if (messageObject.type == MessageObject.TYPE_STORY || messageObject.type == MessageObject.TYPE_STORY_MENTION) { + if (messageObject.messageOwner.media.storyItem == null) { + long storyDialogId = messageObject.messageOwner.media.user_id; + if (messagesWithUnknownStories == null) { + messagesWithUnknownStories = new LongSparseArray<>(); + } + ArrayList array = messagesWithUnknownStories.get(storyDialogId); + if (array == null) { + array = new ArrayList<>(); + messagesWithUnknownStories.put(storyDialogId, array); + } + array.add(messageObject); + } else { + long storyDialogId = messageObject.messageOwner.media.user_id; + messageObject.messageOwner.media.storyItem = StoriesStorage.checkExpiredStateLocal(currentAccount, storyDialogId, messageObject.messageOwner.media.storyItem); + } + } else if (messageObject.getId() > 0 && messageObject.isReplyToStory()) { + if (messageObject.messageOwner.replyStory == null) { + long storyDialogId = messageObject.messageOwner.reply_to.user_id; + if (messagesWithUnknownStories == null) { + messagesWithUnknownStories = new LongSparseArray<>(); + } + ArrayList array = messagesWithUnknownStories.get(storyDialogId); + if (array == null) { + array = new ArrayList<>(); + messagesWithUnknownStories.put(storyDialogId, array); + } + array.add(messageObject); + } else { + long storyDialogId = messageObject.messageOwner.reply_to.user_id; + messageObject.messageOwner.replyStory = StoriesStorage.checkExpiredStateLocal(currentAccount, storyDialogId, messageObject.messageOwner.replyStory); + } + } else if (messageObject.getId() > 0 && messageObject.isReply()) { int messageId = messageObject.messageOwner.reply_to.reply_to_msg_id; if (messageId == threadMessageId) { continue; @@ -5444,16 +5482,65 @@ public void loadReplyMessagesForMessages(ArrayList messages, long } arrayList.add(messageObject); } + if ( + messageObject.type == MessageObject.TYPE_TEXT && + messageObject.messageOwner != null && + messageObject.messageOwner.media != null && + messageObject.messageOwner.media.webpage != null && + messageObject.messageOwner.media.webpage.attributes != null + ) { + for (int i = 0; i < messageObject.messageOwner.media.webpage.attributes.size(); ++i) { + TLRPC.WebPageAttribute attr = messageObject.messageOwner.media.webpage.attributes.get(i); + if (attr instanceof TLRPC.TL_webPageAttributeStory) { + TLRPC.TL_webPageAttributeStory attrStory = (TLRPC.TL_webPageAttributeStory) attr; + if (attrStory.storyItem == null) { + long storyDialogId = attrStory.user_id; + if (messagesWithUnknownStories == null) { + messagesWithUnknownStories = new LongSparseArray<>(); + } + ArrayList array = messagesWithUnknownStories.get(storyDialogId); + if (array == null) { + array = new ArrayList<>(); + messagesWithUnknownStories.put(storyDialogId, array); + } + array.add(messageObject); + } else { + long storyDialogId = attrStory.user_id; + attrStory.storyItem = StoriesStorage.checkExpiredStateLocal(currentAccount, storyDialogId, attrStory.storyItem); + } + } + } + } } - if (replyMessageOwners.isEmpty()) { + if (replyMessageOwners.isEmpty() && messagesWithUnknownStories == null) { if (callback != null) { callback.run(); } return; } + LongSparseArray> finalMessagesWithUnknownStories = messagesWithUnknownStories; + + int[] requestsCount = new int[] {2}; getMessagesStorage().getStorageQueue().postRunnable(() -> { try { + getMessagesController().getStoriesController().fillMessagesWithStories(finalMessagesWithUnknownStories, () -> { + requestsCount[0]--; + if (requestsCount[0] == 0) { + if (callback != null) { + AndroidUtilities.runOnUIThread(callback); + } + } + }); + if (replyMessageOwners.isEmpty()) { + requestsCount[0]--; + if (requestsCount[0] == 0) { + if (callback != null) { + AndroidUtilities.runOnUIThread(callback); + } + } + return; + } ArrayList result = new ArrayList<>(); ArrayList users = new ArrayList<>(); ArrayList chats = new ArrayList<>(); @@ -5574,8 +5661,11 @@ public void loadReplyMessagesForMessages(ArrayList messages, long saveReplyMessages(replyMessageOwners, messagesRes.messages, scheduled); } } - if (callback != null) { - AndroidUtilities.runOnUIThread(callback); + requestsCount[0]--; + if (requestsCount[0] == 0) { + if (callback != null) { + AndroidUtilities.runOnUIThread(callback); + } } }); } else if (channelId != 0) { @@ -5597,8 +5687,11 @@ public void loadReplyMessagesForMessages(ArrayList messages, long getMessagesStorage().putUsersAndChats(messagesRes.users, messagesRes.chats, true, true); saveReplyMessages(replyMessageOwners, messagesRes.messages, scheduled); } - if (callback != null) { - AndroidUtilities.runOnUIThread(callback); + requestsCount[0]--; + if (requestsCount[0] == 0) { + if (callback != null) { + AndroidUtilities.runOnUIThread(callback); + } } }); } else { @@ -5618,18 +5711,27 @@ public void loadReplyMessagesForMessages(ArrayList messages, long getMessagesStorage().putUsersAndChats(messagesRes.users, messagesRes.chats, true, true); saveReplyMessages(replyMessageOwners, messagesRes.messages, scheduled); } - if (callback != null) { - AndroidUtilities.runOnUIThread(callback); + requestsCount[0]--; + if (requestsCount[0] == 0) { + if (callback != null) { + AndroidUtilities.runOnUIThread(callback); + } } }); } } } else { - if (callback != null) { - AndroidUtilities.runOnUIThread(callback); + requestsCount[0]--; + if (requestsCount[0] == 0) { + if (callback != null) { + AndroidUtilities.runOnUIThread(callback); + } } } } catch (Exception e) { + if (callback != null) { + AndroidUtilities.runOnUIThread(callback); + } FileLog.e(e); } }); @@ -5657,6 +5759,7 @@ private void saveReplyMessages(LongSparseArray messageObjects = sparseArray.get(message.id); if (messageObjects != null) { + MessageObject.normalizeFlags(message); NativeByteBuffer data = new NativeByteBuffer(message.getObjectSize()); message.serializeToStream(data); for (int b = 0; b < messageObjects.size(); b++) { @@ -5719,6 +5822,7 @@ private void broadcastReplyMessages(ArrayList result, LongSparseA for (int b = 0; b < arrayList.size(); b++) { MessageObject m = arrayList.get(b); m.replyMessageObject = messageObject; + m.applyTimestampsHighlightForReplyMsg(); if (m.messageOwner.action instanceof TLRPC.TL_messageActionPinMessage) { m.generatePinMessageText(null, null); } else if (m.messageOwner.action instanceof TLRPC.TL_messageActionGameScore) { @@ -5971,6 +6075,9 @@ public static ArrayList getTextStyleRuns(ArrayList { + fillWithAnimatedEmoji(result, maxAnimatedPerEmoji, allowTopicIcons, forcePremium, () -> { if (sync != null) { callback.run(result, aliasFinal); sync.countDown(); @@ -7442,7 +7554,7 @@ public void getEmojiSuggestions(String[] langCodes, String keyword, boolean full private boolean triedLoadingEmojipacks = false; - public void fillWithAnimatedEmoji(ArrayList result, Integer maxAnimatedPerEmojiInput, boolean allowTopicIcons, Runnable onDone) { + public void fillWithAnimatedEmoji(ArrayList result, Integer maxAnimatedPerEmojiInput, boolean allowTopicIcons, boolean forcePremium, Runnable onDone) { if (result == null || result.isEmpty()) { if (onDone != null) { onDone.run(); @@ -7457,7 +7569,7 @@ public void fillWithAnimatedEmoji(ArrayList result, Integer maxAn ArrayList animatedEmoji = new ArrayList<>(); final int maxAnimatedPerEmoji = maxAnimatedPerEmojiInput == null ? (result.size() > 5 ? 1 : (result.size() > 2 ? 2 : 3)) : maxAnimatedPerEmojiInput; int len = maxAnimatedPerEmojiInput == null ? Math.min(15, result.size()) : result.size(); - boolean isPremium = UserConfig.getInstance(currentAccount).isPremium(); + boolean isPremium = UserConfig.getInstance(currentAccount).isPremium() || forcePremium; String topicIconsName = null; if (allowTopicIcons) { topicIconsName = UserConfig.getInstance(currentAccount).defaultTopicIcons; @@ -7476,7 +7588,7 @@ public void fillWithAnimatedEmoji(ArrayList result, Integer maxAn } for (int i = 0; i < len; ++i) { String emoji = result.get(i).emoji; - if (emoji == null) { + if (TextUtils.isEmpty(emoji)) { continue; } animatedEmoji.clear(); @@ -7486,8 +7598,9 @@ public void fillWithAnimatedEmoji(ArrayList result, Integer maxAn try { long documentId = Long.parseLong(Emoji.recentEmoji.get(j).substring(9)); TLRPC.Document document = AnimatedEmojiDrawable.findDocument(currentAccount, documentId); + String emoticon = MessageObject.findAnimatedEmojiEmoticon(document, null); if (document != null && - emoji.equals(MessageObject.findAnimatedEmojiEmoticon(document, null)) && + emoticon != null && emoticon.contains(emoji) && (isPremium || MessageObject.isFreeEmoji(document)) ) { animatedEmoji.add(document); @@ -7503,7 +7616,39 @@ public void fillWithAnimatedEmoji(ArrayList result, Integer maxAn if (animatedEmoji.size() < maxAnimatedPerEmoji && emojiPacks[0] != null) { for (int j = 0; j < emojiPacks[0].size(); ++j) { TLRPC.TL_messages_stickerSet set = emojiPacks[0].get(j); - if (set != null && set.documents != null) { + if (set != null && set.packs != null) { + for (int k = 0; k < set.packs.size(); ++k) { + TLRPC.TL_stickerPack pack = set.packs.get(k); + if (pack != null && pack.emoticon != null && pack.emoticon.contains(emoji)) { + for (int d = 0; d < pack.documents.size(); ++d) { + long documentId = pack.documents.get(d); + TLRPC.Document document = null; + for (int d2 = 0; d2 < set.documents.size(); ++d2) { + TLRPC.Document doc = set.documents.get(d2); + if (doc != null && doc.id == documentId) { + document = doc; + break; + } + } + if (document != null && document.attributes != null && !animatedEmoji.contains(document)) { + boolean duplicate = false; + for (int l = 0; l < animatedEmoji.size(); ++l) { + if (animatedEmoji.get(l).id == document.id) { + duplicate = true; + break; + } + } + if (!duplicate) { + animatedEmoji.add(document); + if (animatedEmoji.size() >= maxAnimatedPerEmoji) { + break; + } + } + } + } + } + } + } else if (set != null && set.documents != null) { for (int d = 0; d < set.documents.size(); ++d) { TLRPC.Document document = set.documents.get(d); if (document != null && document.attributes != null && !animatedEmoji.contains(document)) { @@ -7516,7 +7661,7 @@ public void fillWithAnimatedEmoji(ArrayList result, Integer maxAn } } - if (attribute != null && emoji.equals(attribute.alt) && (isPremium || attribute.free || set.set != null && set.set.short_name != null && set.set.short_name.equals(topicIconsName))) { + if (attribute != null && !TextUtils.isEmpty(attribute.alt) && attribute.alt.contains(emoji) && (isPremium || attribute.free || set.set != null && set.set.short_name != null && set.set.short_name.equals(topicIconsName))) { boolean duplicate = false; for (int l = 0; l < animatedEmoji.size(); ++l) { if (animatedEmoji.get(l).id == document.id) { @@ -7561,7 +7706,7 @@ public void fillWithAnimatedEmoji(ArrayList result, Integer maxAn } } - if (attribute != null && emoji.equals(attribute.alt) && (isPremium || attribute.free || set.set != null && set.set.short_name != null && set.set.short_name.equals(topicIconsName))) { + if (attribute != null && !TextUtils.isEmpty(attribute.alt) && attribute.alt.contains(emoji) && (isPremium || attribute.free || set.set != null && set.set.short_name != null && set.set.short_name.equals(topicIconsName))) { boolean duplicate = false; for (int l = 0; l < animatedEmoji.size(); ++l) { if (animatedEmoji.get(l).id == document.id) { @@ -7708,7 +7853,7 @@ public ArrayList getDefaultEmojiStatuses() { final int type = 1; // default if (!emojiStatusesFromCacheFetched[type]) { fetchEmojiStatuses(type, true); - } else if (/*emojiStatusesHash[type] == 0 || */emojiStatusesFetchDate[type] == null || (System.currentTimeMillis() / 1000 - emojiStatusesFetchDate[type]) > 60 * 30) { + } else if (emojiStatuses[type] == null || emojiStatusesFetchDate[type] != null && (System.currentTimeMillis() / 1000 - emojiStatusesFetchDate[type]) > 60 * 30) { fetchEmojiStatuses(type, false); } return emojiStatuses[type]; @@ -7718,7 +7863,7 @@ public ArrayList getRecentEmojiStatuses() { final int type = 0; // recent if (!emojiStatusesFromCacheFetched[type]) { fetchEmojiStatuses(type, true); - } else if (/*emojiStatusesHash[type] == 0 || */emojiStatusesFetchDate[type] == null || (System.currentTimeMillis() / 1000 - emojiStatusesFetchDate[type]) > 60 * 30) { + } else if (emojiStatuses[type] == null || emojiStatusesFetchDate[type] != null && (System.currentTimeMillis() / 1000 - emojiStatusesFetchDate[type]) > 60 * 30) { fetchEmojiStatuses(type, false); } return emojiStatuses[type]; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessageLoaderLogger.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessageLoaderLogger.java new file mode 100644 index 0000000000..6e7421ae57 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessageLoaderLogger.java @@ -0,0 +1,54 @@ +package org.telegram.messenger; + +public class MessageLoaderLogger { + + final long dialogId; + final int count; + final int loadIndex; + final long startTime; + + long moveToStorageQueueTime; + long getFromDatabaseTime; + long moveToStageQueueTime; + long stageQueueProccessing; + + boolean reload; + + public MessageLoaderLogger(long dialogId, int loadIndex, int count) { + this.dialogId = dialogId; + this.count = count; + this.loadIndex = loadIndex; + startTime = System.currentTimeMillis(); + } + + public void logStorageQueuePost() { + moveToStorageQueueTime = System.currentTimeMillis() - startTime; + } + + public void logStorageProccessing() { + getFromDatabaseTime = System.currentTimeMillis() - startTime; + } + + public void logStageQueuePost() { + moveToStageQueueTime = System.currentTimeMillis() - startTime; + } + + public void reload() { + reload = true; + } + + public void logStageQueueProcessing() { + stageQueueProccessing = System.currentTimeMillis() - startTime; + } + + public void finish() { + long totalTime = System.currentTimeMillis() - startTime; + FileLog.d("MessageLoaderLogger dialogId=" + dialogId + " index=" + loadIndex + " count=" + count + " " + + " moveToStorageQueueTime=" + moveToStorageQueueTime + + " getFromDatabaseTime=" + getFromDatabaseTime + + " moveToStageQueueTime=" + moveToStageQueueTime + + " stageQueueProccessing=" + stageQueueProccessing + + " wasReload=" + reload + " totalTime=" + totalTime + ); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java index 4c7a735d96..7d431f0947 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java @@ -110,6 +110,9 @@ public class MessageObject { public static final int TYPE_EMOJIS = 19; public static final int TYPE_EXTENDED_MEDIA_PREVIEW = 20; public static final int TYPE_SUGGEST_PHOTO = 21; + public static final int TYPE_ACTION_WALLPAPER = 22; + public static final int TYPE_STORY = 23; + public static final int TYPE_STORY_MENTION = 24; public int localType; public String localName; @@ -121,6 +124,7 @@ public class MessageObject { public Boolean cachedIsSupergroup; public boolean localEdit; public TLRPC.Message messageOwner; + public TLRPC.StoryItem storyItem; public TLRPC.Document emojiAnimatedSticker; public Long emojiAnimatedStickerId; public boolean isTopicMainMessage; @@ -141,6 +145,7 @@ public class MessageObject { public String customName; public boolean reactionsChanged; public boolean isReactionPush; + public boolean isStoryPush, isStoryMentionPush, isStoryPushHidden; public boolean putInDownloadsStore; public boolean isDownloadingFile; public boolean forcePlayEffect; @@ -156,7 +161,8 @@ public class MessageObject { public float bufferedProgress; public float gifState; public int audioProgressSec; - public int audioPlayerDuration, attributeDuration; + public int audioPlayerDuration; + public double attributeDuration; public boolean isDateObject; public TLObject photoThumbsObject; public TLObject photoThumbsObject2; @@ -188,6 +194,7 @@ public class MessageObject { public boolean sponsoredShowPeerPhoto; public boolean sponsoredRecommended; public String sponsoredInfo, sponsoredAdditionalInfo; + public TLRPC.TL_sponsoredWebPage sponsoredWebPage; public TLRPC.TL_forumTopic replyToForumTopic; // used only for reply message in view all messages @@ -256,7 +263,7 @@ public class MessageObject { public CharSequence vCardData; public ArrayList highlightedWords; - public String messageTrimmedToHighlight; + public CharSequence messageTrimmedToHighlight; public int parentWidth; public ImageLocation mediaThumb; @@ -360,6 +367,49 @@ public static boolean canCreateStripedThubms() { return SharedConfig.getDevicePerformanceClass() == SharedConfig.PERFORMANCE_CLASS_HIGH; } + public static void normalizeFlags(TLRPC.Message message) { + if (message.from_id == null) { + message.flags &= ~256; + } + if (message.from_id == null) { + message.flags &= ~4; + } + if (message.reply_to == null) { + message.flags &= ~8; + } + if (message.media == null) { + message.flags &= ~512; + } + if (message.reply_markup == null) { + message.flags &= ~64; + } + if (message.replies == null) { + message.flags &= ~8388608; + } + if (message.reactions == null) { + message.flags &= ~1048576; + } + } + + public static double getDocumentDuration(TLRPC.Document document) { + if (document == null) { + return 0; + } + for (int a = 0, size = document.attributes.size(); a < size; a++) { + TLRPC.DocumentAttribute attribute = document.attributes.get(a); + if (attribute instanceof TLRPC.TL_documentAttributeVideo) { + return attribute.duration; + } else if (attribute instanceof TLRPC.TL_documentAttributeAudio) { + return attribute.duration; + } + } + return 0; + } + + public boolean isWallpaperAction() { + return type == TYPE_ACTION_WALLPAPER || (messageOwner != null && messageOwner.action instanceof TLRPC.TL_messageActionSetSameChatWallPaper); + } + public int getEmojiOnlyCount() { return emojiOnlyCount; } @@ -370,14 +420,7 @@ public boolean hasMediaSpoilers() { } public boolean shouldDrawReactionsInLayout() { - if (getDialogId() < 0 || UserConfig.getInstance(currentAccount).isPremium()) { - return true; - } - TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(getDialogId()); - if (user != null && user.premium) { - return true; - } - return false;//getDialogId() < 0 || UserConfig.getInstance(currentAccount).isPremium(); + return true; } public boolean isSenderChannel() { @@ -447,6 +490,10 @@ public void copyStableParams(MessageObject old) { } } isSpoilersRevealed = old.isSpoilersRevealed; + messageOwner.replyStory = old.messageOwner.replyStory; + if (messageOwner.media != null && old.messageOwner.media != null) { + messageOwner.media.storyItem = old.messageOwner.media.storyItem; + } if (isSpoilersRevealed && textLayoutBlocks != null) { for (TextLayoutBlock block : textLayoutBlocks) { block.spoilers.clear(); @@ -465,6 +512,14 @@ public ArrayList getChoosenReactions() return choosenReactions; } + public boolean isReplyToStory() { + return !(replyMessageObject != null && replyMessageObject.messageOwner instanceof TLRPC.TL_messageEmpty) && messageOwner.reply_to != null && messageOwner.reply_to.story_id != 0 && (messageOwner.flags & TLRPC.MESSAGE_FLAG_REPLY) != 0; + } + + public boolean isExpiredStory() { + return (type == MessageObject.TYPE_STORY || type == MessageObject.TYPE_STORY_MENTION) && messageOwner.media.storyItem instanceof TLRPC.TL_storyItemDeleted; + } + public static class SendAnimationData { public float x; public float y; @@ -1134,6 +1189,23 @@ public void reset() { public ArrayList textLayoutBlocks; + public MessageObject(int accountNum, TLRPC.StoryItem storyItem) { + currentAccount = accountNum; + this.storyItem = storyItem; + if (storyItem != null) { + messageOwner = new TLRPC.TL_message(); + messageOwner.id = storyItem.id; + messageOwner.date = storyItem.date; + messageOwner.dialog_id = storyItem.dialogId; + messageOwner.message = storyItem.caption; + messageOwner.entities = storyItem.entities; + messageOwner.media = storyItem.media; + messageOwner.attachPath = storyItem.attachPath; + } + photoThumbs = new ArrayList<>(); + photoThumbs2 = new ArrayList<>(); + } + public MessageObject(int accountNum, TLRPC.Message message, String formattedMessage, String name, String userName, boolean localMessage, boolean isChannel, boolean supergroup, boolean edit) { localType = localMessage ? 2 : 1; currentAccount = accountNum; @@ -1194,7 +1266,9 @@ public MessageObject(int accountNum, TLRPC.Message message, MessageObject replyT updateMessageText(users, chats, sUsers, sChats); setType(); - updateTranslation(false); + if (generateLayout) { + updateTranslation(false); + } measureInlineBotButtons(); Calendar rightNow = new GregorianCalendar(); @@ -1297,7 +1371,7 @@ private void createPathThumb() { } public void createStrippedThumb() { - if (photoThumbs == null || !canCreateStripedThubms() && !hasExtendedMediaPreview()) { + if (photoThumbs == null || !canCreateStripedThubms() && !hasExtendedMediaPreview() || strippedThumb != null) { return; } try { @@ -2144,7 +2218,11 @@ public MessageObject(int accountNum, TLRPC.TL_channelAdminLogEvent event, ArrayL } } else if (event.action instanceof TLRPC.TL_channelAdminLogEventActionParticipantJoinByInvite) { TLRPC.TL_channelAdminLogEventActionParticipantJoinByInvite action = (TLRPC.TL_channelAdminLogEventActionParticipantJoinByInvite) event.action; - messageText = replaceWithLink(LocaleController.getString("ActionInviteUser", R.string.ActionInviteUser), "un1", fromUser); + if (action.via_chatlist) { + messageText = replaceWithLink(LocaleController.getString("ActionInviteUserFolder", R.string.ActionInviteUserFolder), "un1", fromUser); + } else { + messageText = replaceWithLink(LocaleController.getString("ActionInviteUser", R.string.ActionInviteUser), "un1", fromUser); + } } else if (event.action instanceof TLRPC.TL_channelAdminLogEventActionToggleNoForwards) { TLRPC.TL_channelAdminLogEventActionToggleNoForwards action = (TLRPC.TL_channelAdminLogEventActionToggleNoForwards) event.action; boolean isChannel = ChatObject.isChannel(chat) && !chat.megagroup; @@ -2462,6 +2540,32 @@ public MessageObject(int accountNum, TLRPC.TL_channelAdminLogEvent event, ArrayL checkMediaExistance(); } + private boolean spoiledLoginCode = false; + private static Pattern loginCodePattern; + public void spoilLoginCode() { // spoil login code from +42777 + if (!spoiledLoginCode && messageText != null && messageOwner != null && messageOwner.entities != null && messageOwner.from_id instanceof TLRPC.TL_peerUser && messageOwner.from_id.user_id == 777000) { + if (loginCodePattern == null) { + loginCodePattern = Pattern.compile("[\\d\\-]{5,7}"); + } + try { + Matcher matcher = loginCodePattern.matcher(messageText); + if (matcher.find()) { + TLRPC.TL_messageEntitySpoiler spoiler = new TLRPC.TL_messageEntitySpoiler(); + spoiler.offset = matcher.start(); + spoiler.length = matcher.end() - spoiler.offset; + messageOwner.entities.add(spoiler); + } + } catch (Exception e) { + FileLog.e(e, false); + } + spoiledLoginCode = true; + } + } + + public boolean didSpoilLoginCode() { + return spoiledLoginCode; + } + private CharSequence getStringFrom(TLRPC.ChatReactions reactions) { if (reactions instanceof TLRPC.TL_chatReactionsAll) { return LocaleController.getString("AllReactions", R.string.AllReactions); @@ -2544,12 +2648,13 @@ public boolean updateTranslation() { public boolean translated = false; public boolean updateTranslation(boolean force) { boolean replyUpdated = replyMessageObject != null && replyMessageObject.updateTranslation(force); + TranslateController translateController = MessagesController.getInstance(currentAccount).getTranslateController(); if ( TranslateController.isTranslatable(this) && - MessagesController.getInstance(currentAccount).getTranslateController().isTranslatingDialog(getDialogId()) && + translateController.isTranslatingDialog(getDialogId()) && messageOwner != null && messageOwner.translatedText != null && - TextUtils.equals(MessagesController.getInstance(currentAccount).getTranslateController().getDialogTranslateTo(getDialogId()), messageOwner.translatedToLanguage) + TextUtils.equals(translateController.getDialogTranslateTo(getDialogId()), messageOwner.translatedToLanguage) ) { if (translated) { return replyUpdated || false; @@ -3061,6 +3166,10 @@ public void createMessageSendInfo() { } } + public boolean hasInlineBotButtons() { + return !isRestrictedMessage && messageOwner != null && messageOwner.reply_markup instanceof TLRPC.TL_replyInlineMarkup && !messageOwner.reply_markup.rows.isEmpty(); + } + public void measureInlineBotButtons() { if (isRestrictedMessage) { return; @@ -3075,7 +3184,7 @@ public void measureInlineBotButtons() { } } - if (messageOwner.reply_markup instanceof TLRPC.TL_replyInlineMarkup && !hasExtendedMedia()) { + if (messageOwner.reply_markup instanceof TLRPC.TL_replyInlineMarkup && !hasExtendedMedia() && messageOwner.reply_markup.rows != null) { for (int a = 0; a < messageOwner.reply_markup.rows.size(); a++) { TLRPC.TL_keyboardButtonRow row = messageOwner.reply_markup.rows.get(a); int maxButtonSize = 0; @@ -3155,7 +3264,41 @@ private void updateMessageText(AbstractMap users, AbstractMap< if (messageOwner instanceof TLRPC.TL_messageService) { if (messageOwner.action != null) { - if (messageOwner.action instanceof TLRPC.TL_messageActionGroupCallScheduled) { + if (messageOwner.action instanceof TLRPC.TL_messageActionSetSameChatWallPaper) { + contentType = 1; + type = TYPE_TEXT; + TLRPC.TL_messageActionSetSameChatWallPaper action = (TLRPC.TL_messageActionSetSameChatWallPaper) messageOwner.action; + TLRPC.User user = getUser(users, sUsers, isOutOwner() ? 0 : getDialogId()); + photoThumbs = new ArrayList<>(); + if (action.wallpaper.document != null) { + photoThumbs.addAll(action.wallpaper.document.thumbs); + photoThumbsObject = action.wallpaper.document; + } + if (user != null) { + if (user.id == UserConfig.getInstance(currentAccount).clientUserId) { + messageText = LocaleController.formatString("ActionSetSameWallpaperForThisChatSelf", R.string.ActionSetSameWallpaperForThisChatSelf); + } else { + messageText = LocaleController.formatString("ActionSetSameWallpaperForThisChat", R.string.ActionSetSameWallpaperForThisChat, user.first_name); + } + } + } else if (messageOwner.action instanceof TLRPC.TL_messageActionSetChatWallPaper) { + contentType = 1; + type = TYPE_ACTION_WALLPAPER; + TLRPC.TL_messageActionSetChatWallPaper wallPaper = (TLRPC.TL_messageActionSetChatWallPaper) messageOwner.action; + photoThumbs = new ArrayList<>(); + if (wallPaper.wallpaper.document != null) { + photoThumbs.addAll(wallPaper.wallpaper.document.thumbs); + photoThumbsObject = wallPaper.wallpaper.document; + } + TLRPC.User user = getUser(users, sUsers, isOutOwner() ? 0 : getDialogId()); + if (user != null) { + if (user.id == UserConfig.getInstance(currentAccount).clientUserId) { + messageText = LocaleController.formatString("ActionSetWallpaperForThisChatSelf", R.string.ActionSetWallpaperForThisChatSelf); + } else { + messageText = LocaleController.formatString("ActionSetWallpaperForThisChat", R.string.ActionSetWallpaperForThisChat, user.first_name); + } + } + } else if (messageOwner.action instanceof TLRPC.TL_messageActionGroupCallScheduled) { TLRPC.TL_messageActionGroupCallScheduled action = (TLRPC.TL_messageActionGroupCallScheduled) messageOwner.action; if (messageOwner.peer_id instanceof TLRPC.TL_peerChat || isSupergroup()) { messageText = LocaleController.formatString("ActionGroupCallScheduled", R.string.ActionGroupCallScheduled, LocaleController.formatStartsTime(action.schedule_date, 3, false)); @@ -3419,8 +3562,6 @@ private void updateMessageText(AbstractMap users, AbstractMap< messageText = LocaleController.formatString("MessageLifetimeRemoved", R.string.MessageLifetimeRemoved, UserObject.getFirstName(fromUser)); } } - } else if (messageOwner.action instanceof TLRPC.TL_messageActionAttachMenuBotAllowed || messageOwner.action instanceof TLRPC.TL_messageActionBotAllowed && ((TLRPC.TL_messageActionBotAllowed) messageOwner.action).attach_menu) { - messageText = LocaleController.getString(R.string.ActionAttachMenuBotAllowed); } else if (messageOwner.action instanceof TLRPC.TL_messageActionRequestedPeer) { TLRPC.Peer peer = ((TLRPC.TL_messageActionRequestedPeer) messageOwner.action).peer; TLObject peerObject = null; @@ -3700,13 +3841,35 @@ private void updateMessageText(AbstractMap users, AbstractMap< generatePaymentSentMessageText(user); } else if (messageOwner.action instanceof TLRPC.TL_messageActionBotAllowed) { String domain = ((TLRPC.TL_messageActionBotAllowed) messageOwner.action).domain; - String text = LocaleController.getString("ActionBotAllowed", R.string.ActionBotAllowed); - int start = text.indexOf("%1$s"); - SpannableString str = new SpannableString(String.format(text, domain)); - if (start >= 0) { - str.setSpan(new URLSpanNoUnderlineBold("http://" + domain), start, start + domain.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + TLRPC.BotApp botApp = ((TLRPC.TL_messageActionBotAllowed) messageOwner.action).app; + if (botApp != null) { + String botAppTitle = botApp.title; + String text = LocaleController.getString("ActionBotAllowedApp", R.string.ActionBotAllowedApp); + int start = text.indexOf("%1$s"); + SpannableString str = new SpannableString(String.format(text, botAppTitle)); + TLRPC.User bot = getUser(users, sUsers, getDialogId()); + if (start >= 0 && bot != null) { + final String username = UserObject.getPublicUsername(bot); + if (username != null) { + final String link = "https://" + MessagesController.getInstance(currentAccount).linkPrefix + "/" + username + "/" + botApp.short_name; + str.setSpan(new URLSpanNoUnderlineBold(link), start, start + botAppTitle.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + messageText = str; + } else { + if (domain == null) { + domain = ""; + } + String text = LocaleController.getString("ActionBotAllowed", R.string.ActionBotAllowed); + int start = text.indexOf("%1$s"); + SpannableString str = new SpannableString(String.format(text, domain)); + if (start >= 0 && !TextUtils.isEmpty(domain)) { + str.setSpan(new URLSpanNoUnderlineBold("http://" + domain), start, start + domain.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + messageText = str; } - messageText = str; + } else if (messageOwner.action instanceof TLRPC.TL_messageActionAttachMenuBotAllowed || messageOwner.action instanceof TLRPC.TL_messageActionBotAllowed && ((TLRPC.TL_messageActionBotAllowed) messageOwner.action).attach_menu) { + messageText = LocaleController.getString(R.string.ActionAttachMenuBotAllowed); } else if (messageOwner.action instanceof TLRPC.TL_messageActionSecureValuesSent) { TLRPC.TL_messageActionSecureValuesSent valuesSent = (TLRPC.TL_messageActionSecureValuesSent) messageOwner.action; StringBuilder str = new StringBuilder(); @@ -3782,7 +3945,23 @@ private void updateMessageText(AbstractMap users, AbstractMap< messageText = restrictionReason; isRestrictedMessage = true; } else if (!isMediaEmpty()) { - if (getMedia(messageOwner) instanceof TLRPC.TL_messageMediaDice) { + if (getMedia(messageOwner) instanceof TLRPC.TL_messageMediaStory) { + if (getMedia(messageOwner).via_mention) { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(getMedia(messageOwner).user_id); + String link = null, username; + if (user != null && (username = UserObject.getPublicUsername(user)) != null) { + link = MessagesController.getInstance(currentAccount).linkPrefix + "/" + username + "/s/" + getMedia(messageOwner).id; + } + if (link != null) { + messageText = new SpannableString(link); + ((SpannableString) messageText).setSpan(new URLSpanReplacement("https://" + link, new TextStyleSpan.TextStyleRun()), 0, messageText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } else { + messageText = ""; + } + } else { + messageText = LocaleController.getString("ForwardedStory", R.string.ForwardedStory); + } + } else if (getMedia(messageOwner) instanceof TLRPC.TL_messageMediaDice) { messageText = getDiceEmoji(); } else if (getMedia(messageOwner) instanceof TLRPC.TL_messageMediaPoll) { if (((TLRPC.TL_messageMediaPoll) getMedia(messageOwner)).poll.quiz) { @@ -3908,7 +4087,7 @@ public void setType() { } else { type = TYPE_ANIMATED_STICKER; } - } else if (isMediaEmpty() && !isDice() && emojiOnlyCount >= 1 && !hasUnwrappedEmoji) { + } else if (isMediaEmpty(false) && !isDice() && emojiOnlyCount >= 1 && !hasUnwrappedEmoji) { type = TYPE_EMOJIS; } else if (isMediaEmpty()) { type = TYPE_TEXT; @@ -3971,9 +4150,26 @@ public void setType() { type = TYPE_TEXT; } else if (getMedia(messageOwner) instanceof TLRPC.TL_messageMediaInvoice) { type = TYPE_TEXT; + } else if (getMedia(messageOwner) instanceof TLRPC.TL_messageMediaStory) { + type = getMedia(messageOwner).via_mention ? TYPE_STORY_MENTION : TYPE_STORY; + if (type == TYPE_STORY_MENTION) { + contentType = 1; + } } } else if (messageOwner instanceof TLRPC.TL_messageService) { - if (messageOwner.action instanceof TLRPC.TL_messageActionSuggestProfilePhoto) { + if (messageOwner.action instanceof TLRPC.TL_messageActionSetSameChatWallPaper) { + contentType = 1; + type = TYPE_TEXT; + } else if (messageOwner.action instanceof TLRPC.TL_messageActionSetChatWallPaper) { + contentType = 1; + type = TYPE_ACTION_WALLPAPER; + TLRPC.TL_messageActionSetChatWallPaper wallPaper = (TLRPC.TL_messageActionSetChatWallPaper) messageOwner.action; + photoThumbs = new ArrayList<>(); + if (wallPaper.wallpaper.document != null) { + photoThumbs.addAll(wallPaper.wallpaper.document.thumbs); + photoThumbsObject = wallPaper.wallpaper.document; + } + } else if (messageOwner.action instanceof TLRPC.TL_messageActionSuggestProfilePhoto) { contentType = 1; type = TYPE_SUGGEST_PHOTO; photoThumbs = new ArrayList<>(); @@ -4539,22 +4735,45 @@ public void generateLinkDescription() { if (linkDescription != null) { return; } + boolean allowUsernames = false; int hashtagsType = 0; - if (getMedia(messageOwner) instanceof TLRPC.TL_messageMediaWebPage && getMedia(messageOwner).webpage instanceof TLRPC.TL_webPage && getMedia(messageOwner).webpage.description != null) { - linkDescription = Spannable.Factory.getInstance().newSpannable(getMedia(messageOwner).webpage.description); - String siteName = getMedia(messageOwner).webpage.site_name; - if (siteName != null) { - siteName = siteName.toLowerCase(); - } - if ("instagram".equals(siteName)) { - hashtagsType = 1; - } else if ("twitter".equals(siteName)) { - hashtagsType = 2; - } - } else if (getMedia(messageOwner) instanceof TLRPC.TL_messageMediaGame && getMedia(messageOwner).game.description != null) { - linkDescription = Spannable.Factory.getInstance().newSpannable(getMedia(messageOwner).game.description); - } else if (getMedia(messageOwner) instanceof TLRPC.TL_messageMediaInvoice && getMedia(messageOwner).description != null) { - linkDescription = Spannable.Factory.getInstance().newSpannable(getMedia(messageOwner).description); + TLRPC.WebPage webpage = null; + if (storyMentionWebpage != null) { + webpage = storyMentionWebpage; + } else if (getMedia(messageOwner) instanceof TLRPC.TL_messageMediaWebPage) { + webpage = ((TLRPC.TL_messageMediaWebPage) getMedia(messageOwner)).webpage; + } + if (webpage != null) { + for (int i = 0; i < webpage.attributes.size(); ++i) { + TLRPC.WebPageAttribute attr = webpage.attributes.get(i); + if (attr instanceof TLRPC.TL_webPageAttributeStory) { + TLRPC.TL_webPageAttributeStory storyAttr = (TLRPC.TL_webPageAttributeStory) attr; + if (storyAttr.storyItem != null && storyAttr.storyItem.caption != null) { + linkDescription = new SpannableStringBuilder(storyAttr.storyItem.caption); + webPageDescriptionEntities = storyAttr.storyItem.entities; + allowUsernames = true; + break; + } + } + } + } + if (linkDescription == null) { + if (getMedia(messageOwner) instanceof TLRPC.TL_messageMediaWebPage && getMedia(messageOwner).webpage instanceof TLRPC.TL_webPage && getMedia(messageOwner).webpage.description != null) { + linkDescription = Spannable.Factory.getInstance().newSpannable(getMedia(messageOwner).webpage.description); + String siteName = getMedia(messageOwner).webpage.site_name; + if (siteName != null) { + siteName = siteName.toLowerCase(); + } + if ("instagram".equals(siteName)) { + hashtagsType = 1; + } else if ("twitter".equals(siteName)) { + hashtagsType = 2; + } + } else if (getMedia(messageOwner) instanceof TLRPC.TL_messageMediaGame && getMedia(messageOwner).game.description != null) { + linkDescription = Spannable.Factory.getInstance().newSpannable(getMedia(messageOwner).game.description); + } else if (getMedia(messageOwner) instanceof TLRPC.TL_messageMediaInvoice && getMedia(messageOwner).description != null) { + linkDescription = Spannable.Factory.getInstance().newSpannable(getMedia(messageOwner).description); + } } if (!TextUtils.isEmpty(linkDescription)) { if (containsUrls(linkDescription)) { @@ -4566,7 +4785,7 @@ public void generateLinkDescription() { } linkDescription = Emoji.replaceEmoji(linkDescription, Theme.chat_msgTextPaint.getFontMetricsInt(), AndroidUtilities.dp(20), false); if (webPageDescriptionEntities != null) { - addEntitiesToText(linkDescription, webPageDescriptionEntities, isOut(), false, false, true); + addEntitiesToText(linkDescription, webPageDescriptionEntities, isOut(), allowUsernames, false, !allowUsernames); replaceAnimatedEmoji(linkDescription, webPageDescriptionEntities, Theme.chat_msgTextPaint.getFontMetricsInt()); } if (hashtagsType != 0) { @@ -4638,7 +4857,15 @@ public void generateCaption() { } String text = messageOwner.message; ArrayList entities = messageOwner.entities; - if (hasExtendedMedia()) { + if (type == TYPE_STORY) { + if (messageOwner.media != null && messageOwner.media.storyItem != null) { + text = messageOwner.media.storyItem.caption; + entities = messageOwner.media.storyItem.entities; + } else { + text = ""; + entities = new ArrayList<>(); + } + } else if (hasExtendedMedia()) { text = messageOwner.message = messageOwner.media.description; } if (captionTranslated = translated) { @@ -4656,7 +4883,7 @@ public void generateCaption() { } if (!isMediaEmpty() && !(getMedia(messageOwner) instanceof TLRPC.TL_messageMediaGame) && !TextUtils.isEmpty(text)) { caption = Emoji.replaceEmoji(text, Theme.chat_msgTextPaint.getFontMetricsInt(), AndroidUtilities.dp(20), false); - caption = replaceAnimatedEmoji(caption, Theme.chat_msgTextPaint.getFontMetricsInt()); + caption = replaceAnimatedEmoji(caption, entities, Theme.chat_msgTextPaint.getFontMetricsInt(), false); boolean hasEntities; if (messageOwner.send_state != MESSAGE_SEND_STATE_SENT) { @@ -4696,9 +4923,9 @@ public void generateCaption() { addEntitiesToText(caption, useManualParse); if (isVideo()) { - addUrlsByPattern(isOutOwner(), caption, true, 3, getDuration(), false); + addUrlsByPattern(isOutOwner(), caption, true, 3, (int) getDuration(), false); } else if (isMusic() || isVoice()) { - addUrlsByPattern(isOutOwner(), caption, true, 4, getDuration(), false); + addUrlsByPattern(isOutOwner(), caption, true, 4, (int) getDuration(), false); } } } @@ -4725,6 +4952,9 @@ public static void addUrlsByPattern(boolean isOut, CharSequence charSequence, bo } matcher = urlPattern.matcher(charSequence); } + if (!(charSequence instanceof Spannable)) { + return; + } Spannable spannable = (Spannable) charSequence; while (matcher.find()) { int start = matcher.start(); @@ -4797,7 +5027,11 @@ public static void addUrlsByPattern(boolean isOut, CharSequence charSequence, bo url = new URLSpanBotCommand(charSequence.subSequence(start, end).toString(), isOut ? 1 : 0); } } else { - url = new URLSpanNoUnderline(charSequence.subSequence(start, end).toString()); + String uri = charSequence.subSequence(start, end).toString(); + if (uri != null) { + uri = uri.replaceAll("∕|⁄|%E2%81%84|%E2%88%95", "/"); + } + url = new URLSpanNoUnderline(uri); } } } @@ -4831,7 +5065,7 @@ public static int[] getWebDocumentWidthAndHeight(TLRPC.WebDocument document) { return null; } - public static int getWebDocumentDuration(TLRPC.WebDocument document) { + public static double getWebDocumentDuration(TLRPC.WebDocument document) { if (document == null) { return 0; } @@ -4858,9 +5092,9 @@ public static int[] getInlineResultWidthAndHeight(TLRPC.BotInlineResult inlineRe } public static int getInlineResultDuration(TLRPC.BotInlineResult inlineResult) { - int result = getWebDocumentDuration(inlineResult.content); + int result = (int) getWebDocumentDuration(inlineResult.content); if (result == 0) { - result = getWebDocumentDuration(inlineResult.thumb); + result = (int) getWebDocumentDuration(inlineResult.thumb); } return result; } @@ -5130,6 +5364,10 @@ public static boolean addEntitiesToText(CharSequence text, ArrayList(); - textWidth = 0; ArrayList entities = translated && messageOwner.translatedText != null ? messageOwner.translatedText.entities : messageOwner.entities; + spoilLoginCode(); boolean hasEntities; if (messageOwner.send_state != MESSAGE_SEND_STATE_SENT) { @@ -5390,17 +5646,26 @@ public void generateLayout(TLRPC.User fromUser) { } catch (Throwable e) { FileLog.e(e); } - if (isYouTubeVideo() || replyMessageObject != null && replyMessageObject.isYouTubeVideo()) { + if (isYouTubeVideo()) { addUrlsByPattern(isOutOwner(), messageText, false, 3, Integer.MAX_VALUE, false); - } else if (replyMessageObject != null) { - if (replyMessageObject.isVideo()) { - addUrlsByPattern(isOutOwner(), messageText, false, 3, replyMessageObject.getDuration(), false); - } else if (replyMessageObject.isMusic() || replyMessageObject.isVoice()) { - addUrlsByPattern(isOutOwner(), messageText, false, 4, replyMessageObject.getDuration(), false); - } + } else { + applyTimestampsHighlightForReplyMsg(); + } + + if (!(messageText instanceof Spannable)) { + messageText = new SpannableString(messageText); + } + return addEntitiesToText(messageText, useManualParse); + } + + public void generateLayout(TLRPC.User fromUser) { + if (type != TYPE_TEXT && type != TYPE_EMOJIS && type != TYPE_STORY_MENTION || messageOwner.peer_id == null || TextUtils.isEmpty(messageText)) { + return; } + boolean hasUrls = applyEntities(); - boolean hasUrls = addEntitiesToText(messageText, useManualParse); + textLayoutBlocks = new ArrayList<>(); + textWidth = 0; int maxWidth = getMaxMessageTextWidth(); @@ -5532,10 +5797,6 @@ public void generateLayout(TLRPC.User fromUser) { } } } - block.spoilers.clear(); - if (!isSpoilersRevealed) { - SpoilerEffect.addSpoilers(null, block.textLayout, null, block.spoilers); - } textLayoutBlocks.add(block); @@ -5641,6 +5902,11 @@ public void generateLayout(TLRPC.User fromUser) { } linesOffset += currentBlockLinesCount; + + block.spoilers.clear(); + if (!isSpoilersRevealed && !spoiledLoginCode) { + SpoilerEffect.addSpoilers(null, block.textLayout, -1, linesMaxWidthWithLeft, null, block.spoilers); + } } } @@ -5709,6 +5975,18 @@ public static long getFromChatId(TLRPC.Message message) { return getPeerId(message.from_id); } + public static long getObjectPeerId(TLObject peer) { + if (peer == null) { + return 0; + } + if (peer instanceof TLRPC.Chat) { + return -((TLRPC.Chat) peer).id; + } else if (peer instanceof TLRPC.User) { + return ((TLRPC.User) peer).id; + } + return 0; + } + public static long getPeerId(TLRPC.Peer peer) { if (peer == null) { return 0; @@ -6378,6 +6656,10 @@ public static String findAnimatedEmojiEmoticon(TLRPC.Document document) { } public static String findAnimatedEmojiEmoticon(TLRPC.Document document, String fallback) { + return findAnimatedEmojiEmoticon(document, fallback, null); + } + + public static String findAnimatedEmojiEmoticon(TLRPC.Document document, String fallback, Integer currentAccountForFull) { if (document == null) { return fallback; } @@ -6385,6 +6667,21 @@ public static String findAnimatedEmojiEmoticon(TLRPC.Document document, String f TLRPC.DocumentAttribute attribute = document.attributes.get(a); if (attribute instanceof TLRPC.TL_documentAttributeCustomEmoji || attribute instanceof TLRPC.TL_documentAttributeSticker) { + if (currentAccountForFull != null) { + TLRPC.TL_messages_stickerSet set = MediaDataController.getInstance(currentAccountForFull).getStickerSet(attribute.stickerset, true); + StringBuilder emoji = new StringBuilder(""); + if (set != null && set.packs != null) { + for (int p = 0; p < set.packs.size(); ++p) { + TLRPC.TL_stickerPack pack = set.packs.get(p); + if (pack.documents.contains(document.id)) { + emoji.append(pack.emoticon); + } + } + } + if (!TextUtils.isEmpty(emoji)) { + return emoji.toString(); + } + } return attribute.alt; } } @@ -6689,7 +6986,7 @@ public boolean isAnyKindOfSticker() { } public boolean shouldDrawWithoutBackground() { - return type == TYPE_STICKER || type == TYPE_ANIMATED_STICKER || type == TYPE_ROUND_VIDEO || type == TYPE_EMOJIS; + return type == TYPE_STICKER || type == TYPE_ANIMATED_STICKER || type == TYPE_ROUND_VIDEO || type == TYPE_EMOJIS || isExpiredStory(); } public boolean isAnimatedEmojiStickers() { @@ -6724,10 +7021,26 @@ public boolean isVideo() { return isVideoMessage(messageOwner); } + public boolean isVideoStory() { + TLRPC.MessageMedia media = MessageObject.getMedia(messageOwner); + if (media == null) { + return false; + } + TLRPC.StoryItem storyItem = media.storyItem; + if (storyItem == null || storyItem.media == null) { + return false; + } + return MessageObject.isVideoDocument(storyItem.media.document); + } + public boolean isPhoto() { return isPhoto(messageOwner); } + public boolean isStoryMedia() { + return messageOwner != null && messageOwner.media instanceof TLRPC.TL_messageMediaStory; + } + public boolean isLiveLocation() { return isLiveLocationMessage(messageOwner); } @@ -6795,7 +7108,11 @@ public boolean isNewGif() { public boolean isAndroidTheme() { if (getMedia(messageOwner) != null && getMedia(messageOwner).webpage != null && !getMedia(messageOwner).webpage.attributes.isEmpty()) { for (int b = 0, N2 = getMedia(messageOwner).webpage.attributes.size(); b < N2; b++) { - TLRPC.TL_webPageAttributeTheme attribute = getMedia(messageOwner).webpage.attributes.get(b); + TLRPC.WebPageAttribute attribute_ = getMedia(messageOwner).webpage.attributes.get(b); + if (!(attribute_ instanceof TLRPC.TL_webPageAttributeTheme)) { + continue; + } + TLRPC.TL_webPageAttributeTheme attribute = (TLRPC.TL_webPageAttributeTheme) attribute_; ArrayList documents = attribute.documents; for (int a = 0, N = documents.size(); a < N; a++) { TLRPC.Document document = documents.get(a); @@ -6849,11 +7166,17 @@ public String getMusicTitle(boolean unknown) { return LocaleController.getString("AudioUnknownTitle", R.string.AudioUnknownTitle); } - public int getDuration() { + public double getDuration() { if (attributeDuration > 0) { return attributeDuration; } TLRPC.Document document = getDocument(); + if (document == null && type == TYPE_STORY) { + TLRPC.StoryItem storyItem = getMedia(messageOwner).storyItem; + if (storyItem != null && storyItem.media != null) { + document = storyItem.media.document; + } + } if (document == null) { return 0; } @@ -6981,9 +7304,10 @@ public boolean isForwarded() { } public boolean needDrawForwarded() { - return (messageOwner.flags & TLRPC.MESSAGE_FLAG_FWD) != 0 && messageOwner.fwd_from != null && !messageOwner.fwd_from.imported && ( - messageOwner.fwd_from.saved_from_peer == null || !(messageOwner.fwd_from.from_id instanceof TLRPC.TL_peerChannel) || messageOwner.fwd_from.saved_from_peer.channel_id != messageOwner.fwd_from.from_id.channel_id - ) && UserConfig.getInstance(currentAccount).getClientUserId() != getDialogId() && !UserObject.isReplyUser(messageOwner.dialog_id); + if (type == MessageObject.TYPE_STORY && !isExpiredStory()) { + return true; + } + return (messageOwner.flags & TLRPC.MESSAGE_FLAG_FWD) != 0 && messageOwner.fwd_from != null && !messageOwner.fwd_from.imported && (messageOwner.fwd_from.saved_from_peer == null || !(messageOwner.fwd_from.from_id instanceof TLRPC.TL_peerChannel) || messageOwner.fwd_from.saved_from_peer.channel_id != messageOwner.fwd_from.from_id.channel_id) && UserConfig.getInstance(currentAccount).getClientUserId() != getDialogId() && !UserObject.isReplyUser(messageOwner.dialog_id); } public static boolean isForwardedMessage(TLRPC.Message message) { @@ -6998,12 +7322,20 @@ public boolean isMediaEmpty() { return isMediaEmpty(messageOwner); } + public boolean isMediaEmpty(boolean webpageIsEmpty) { + return isMediaEmpty(messageOwner, webpageIsEmpty); + } + public boolean isMediaEmptyWebpage() { return isMediaEmptyWebpage(messageOwner); } public static boolean isMediaEmpty(TLRPC.Message message) { - return message == null || getMedia(message) == null || getMedia(message) instanceof TLRPC.TL_messageMediaEmpty || getMedia(message) instanceof TLRPC.TL_messageMediaWebPage; + return isMediaEmpty(message, true); + } + + public static boolean isMediaEmpty(TLRPC.Message message, boolean allowWebpageIsEmpty) { + return message == null || getMedia(message) == null || getMedia(message) instanceof TLRPC.TL_messageMediaEmpty || allowWebpageIsEmpty && getMedia(message) instanceof TLRPC.TL_messageMediaWebPage; } public static boolean isMediaEmptyWebpage(TLRPC.Message message) { @@ -7150,7 +7482,10 @@ public static boolean canEditMessage(int currentAccount, TLRPC.Message message, } public boolean canDeleteMessage(boolean inScheduleMode, TLRPC.Chat chat) { - return eventId == 0 && sponsoredId == null && canDeleteMessage(currentAccount, inScheduleMode, messageOwner, chat); + return ( + isStory() && messageOwner != null && messageOwner.dialog_id == UserConfig.getInstance(currentAccount).getClientUserId() || + eventId == 0 && sponsoredId == null && canDeleteMessage(currentAccount, inScheduleMode, messageOwner, chat) + ); } public static boolean canDeleteMessage(int currentAccount, boolean inScheduleMode, TLRPC.Message message, TLRPC.Chat chat) { @@ -7163,7 +7498,7 @@ public static boolean canDeleteMessage(int currentAccount, boolean inScheduleMod if (message.id < 0) { return true; } - if (chat == null && message.peer_id.channel_id != 0) { + if (chat == null && message.peer_id != null && message.peer_id.channel_id != 0) { chat = MessagesController.getInstance(currentAccount).getChat(message.peer_id.channel_id); } if (ChatObject.isChannel(chat)) { @@ -7505,7 +7840,10 @@ private void handleFoundWords(ArrayList foundWords, String[] queryWord) } highlightedWords = foundWords; if (messageOwner.message != null) { - String str = messageOwner.message.replace('\n', ' ').replaceAll(" +", " ").trim(); +// String str = messageOwner.message.replace('\n', ' ').replaceAll(" +", " ").trim(); + applyEntities(); + CharSequence charSequence = AndroidUtilities.replaceCharSequence("\n", messageText, " "); + String str = charSequence.toString(); int lastIndex = str.length(); int startHighlightedIndex = str.toLowerCase().indexOf(foundWords.get(0)); int maxSymbols = 200; @@ -7514,15 +7852,31 @@ private void handleFoundWords(ArrayList foundWords, String[] queryWord) } if (lastIndex > maxSymbols) { int newStart = Math.max(0, startHighlightedIndex - maxSymbols / 2); - str = str.substring(newStart, Math.min(lastIndex, startHighlightedIndex - newStart + startHighlightedIndex + maxSymbols / 2)); + charSequence = charSequence.subSequence(newStart, Math.min(lastIndex, startHighlightedIndex - newStart + startHighlightedIndex + maxSymbols / 2)); } - messageTrimmedToHighlight = str; + messageTrimmedToHighlight = charSequence; } } } public void createMediaThumbs() { - if (isVideo()) { + if (isStoryMedia()) { + TLRPC.StoryItem storyItem = getMedia(messageOwner).storyItem; + if (storyItem != null && storyItem.media != null) { + TLRPC.Document document = storyItem.media.document; + if (document != null) { + TLRPC.PhotoSize thumb = FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 50); + TLRPC.PhotoSize qualityThumb = FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 320, false, null, true); + mediaThumb = ImageLocation.getForDocument(qualityThumb, document); + mediaSmallThumb = ImageLocation.getForDocument(thumb, document); + } else { + TLRPC.PhotoSize currentPhotoObjectThumb = FileLoader.getClosestPhotoSizeWithSize(photoThumbs, 50); + TLRPC.PhotoSize currentPhotoObject = FileLoader.getClosestPhotoSizeWithSize(photoThumbs, 320, false, currentPhotoObjectThumb, true); + mediaThumb = ImageLocation.getForObject(currentPhotoObject, photoThumbsObject); + mediaSmallThumb = ImageLocation.getForObject(currentPhotoObjectThumb, photoThumbsObject); + } + } + } else if (isVideo()) { TLRPC.Document document = getDocument(); TLRPC.PhotoSize thumb = FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 50); TLRPC.PhotoSize qualityThumb = FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 320); @@ -7541,11 +7895,14 @@ public boolean hasHighlightedWords() { } public boolean equals(MessageObject obj) { + if (obj == null) { + return false; + } return getId() == obj.getId() && getDialogId() == obj.getDialogId(); } public boolean isReactionsAvailable() { - return !isEditing() && !isSponsored() && isSent() && messageOwner.action == null; + return !isEditing() && !isSponsored() && isSent() && messageOwner.action == null && !isExpiredStory(); } public boolean selectReaction(ReactionsLayoutInBubble.VisibleReaction visibleReaction, boolean big, boolean fromDoubleTap) { @@ -7652,9 +8009,13 @@ public boolean selectReaction(ReactionsLayoutInBubble.VisibleReaction visibleRea if (messageOwner.reactions.can_see_list || (messageOwner.dialog_id > 0 && maxReactionsCount > 1)) { TLRPC.TL_messagePeerReaction action = new TLRPC.TL_messagePeerReaction(); + if (messageOwner.isThreadMessage && messageOwner.fwd_from != null) { + action.peer_id = MessagesController.getInstance(currentAccount).getSendAsSelectedPeer(getFromChatId()); + } else { + action.peer_id = MessagesController.getInstance(currentAccount).getSendAsSelectedPeer(getDialogId()); + } messageOwner.reactions.recent_reactions.add(0, action); - action.peer_id = new TLRPC.TL_peerUser(); - action.peer_id.user_id = UserConfig.getInstance(currentAccount).getClientUserId(); + if (visibleReaction.emojicon != null) { action.reaction = new TLRPC.TL_reactionEmoji(); @@ -7706,4 +8067,33 @@ public byte[] getWaveform() { } return null; } + + public boolean isStory() { + return storyItem != null; + } + + private TLRPC.WebPage storyMentionWebpage; + public TLRPC.WebPage getStoryMentionWebpage() { + if (!isStoryMention()) { + return null; + } + if (storyMentionWebpage != null) { + return storyMentionWebpage; + } + TLRPC.WebPage webpage = new TLRPC.TL_webPage(); + webpage.type = "telegram_story"; + TLRPC.TL_webPageAttributeStory attr = new TLRPC.TL_webPageAttributeStory(); + attr.id = messageOwner.media.id; + attr.user_id = messageOwner.media.user_id; + if (messageOwner.media.storyItem != null) { + attr.flags |= 1; + attr.storyItem = messageOwner.media.storyItem; + } + webpage.attributes.add(attr); + return (storyMentionWebpage = webpage); + } + + public boolean isStoryMention() { + return type == MessageObject.TYPE_STORY_MENTION && !isExpiredStory(); + } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java index b29c8f271e..4ef4ee2b67 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java @@ -27,6 +27,7 @@ import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Base64; +import android.util.Pair; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.SparseIntArray; @@ -70,6 +71,8 @@ import org.telegram.ui.LaunchActivity; import org.telegram.ui.PremiumPreviewFragment; import org.telegram.ui.ProfileActivity; +import org.telegram.ui.Stories.StoriesController; +import org.telegram.ui.Stories.recorder.DualCameraView; import org.telegram.ui.TopicsFragment; import java.io.File; @@ -143,6 +146,41 @@ public class MessagesController extends BaseController implements NotificationCe public ConcurrentHashMap onlinePrivacy = new ConcurrentHashMap<>(20, 1.0f, 2); private LongSparseIntArray pendingUnreadCounter = new LongSparseIntArray(); private int lastPrintingStringCount; + private SparseArray chatlistFoldersUpdates = new SparseArray<>(); + public int largeQueueMaxActiveOperations = 2; + public int smallQueueMaxActiveOperations = 5; + public StoriesController storiesController; + private boolean hasArchivedChats; + private boolean hasStories; + + public static TLRPC.Peer getPeerFromInputPeer(TLRPC.InputPeer peer) { + if (peer.chat_id != 0) { + TLRPC.TL_peerChat peerChat = new TLRPC.TL_peerChat(); + peerChat.chat_id = peer.chat_id; + return peerChat; + } else if (peer.channel_id != 0) { + TLRPC.TL_peerChannel peerChannel = new TLRPC.TL_peerChannel(); + peerChannel.channel_id = peer.channel_id; + return peerChannel; + } else { + TLRPC.TL_peerUser peerUser = new TLRPC.TL_peerUser(); + peerUser.user_id = peer.user_id; + return peerUser; + } + } + + class ChatlistUpdatesStat { + public ChatlistUpdatesStat() { + this.loading = true; + } + public ChatlistUpdatesStat(TLRPC.TL_chatlists_chatlistUpdates value) { + this.lastRequestTime = System.currentTimeMillis(); + this.lastValue = value; + } + boolean loading = false; + long lastRequestTime; + TLRPC.TL_chatlists_chatlistUpdates lastValue; + } private boolean dialogsInTransaction; @@ -174,6 +212,8 @@ public class MessagesController extends BaseController implements NotificationCe public SparseIntArray premiumFeaturesTypesToPosition = new SparseIntArray(); public ArrayList dialogFilters = new ArrayList<>(); + public ArrayList frozenDialogFilters = null; + public ArrayList hiddenUndoChats = new ArrayList<>(); public SparseArray dialogFiltersById = new SparseArray<>(); private boolean loadingSuggestedFilters; private boolean loadingRemoteFilters; @@ -298,25 +338,31 @@ public boolean isOnline() { private HashMap uploadingThemes = new HashMap<>(); - private String uploadingWallpaper; - private Theme.OverrideWallpaperInfo uploadingWallpaperInfo; + public String uploadingWallpaper; + public Theme.OverrideWallpaperInfo uploadingWallpaperInfo; private UserNameResolver userNameResolver; - private boolean loadingAppConfig; - private Fetcher appConfigFetcher = new Fetcher() { + public ArrayList getDialogFilters() { + if (frozenDialogFilters != null) { + return frozenDialogFilters; + } + return dialogFilters; + } + + private final CacheFetcher appConfigFetcher = new CacheFetcher() { @Override - protected void getRemote(int currentAccount, Integer arguments, long hash, Utilities.Callback3 onResult) { + protected void getRemote(int currentAccount, Integer arguments, long hash, Utilities.Callback4 onResult) { TLRPC.TL_help_getAppConfig req = new TLRPC.TL_help_getAppConfig(); req.hash = (int) hash; getConnectionsManager().sendRequest(req, (res, err) -> { if (res instanceof TLRPC.TL_help_appConfigNotModified) { - onResult.run(true, null, 0L); + onResult.run(true, null, 0L, true); } else if (res instanceof TLRPC.TL_help_appConfig) { - onResult.run(false, (TLRPC.TL_help_appConfig) res, (long) ((TLRPC.TL_help_appConfig) res).hash); + onResult.run(false, (TLRPC.TL_help_appConfig) res, (long) ((TLRPC.TL_help_appConfig) res).hash, true); } else { FileLog.e("getting appconfig error " + (err != null ? err.code + " " + err.text : "")); - onResult.run(false, null, 0L); + onResult.run(false, null, 0L, err == null || !(err.code == -2000 || err.code == -2001)); } }); } @@ -457,6 +503,7 @@ protected boolean useCache(Integer arguments) { public boolean remoteConfigLoaded; public int ringtoneDurationMax; public int ringtoneSizeMax; + public boolean storiesExportNopublicLink; public int channelsLimitDefault; public int channelsLimitPremium; @@ -476,6 +523,7 @@ protected boolean useCache(Integer arguments) { public int publicLinksLimitPremium; public int captionLengthLimitDefault; public int captionLengthLimitPremium; + public int storyCaptionLengthLimit; public int aboutLengthLimitDefault; public int aboutLengthLimitPremium; public int reactionsUserMaxDefault; @@ -486,6 +534,9 @@ protected boolean useCache(Integer arguments) { public long telegramAntispamUserId; public int telegramAntispamGroupSizeMin; public int hiddenMembersGroupSizeMin; + private int chatlistUpdatePeriod; + public int storyExpiringLimitDefault; + public int storyExpiringLimitPremium; public int uploadMaxFileParts; public int uploadMaxFilePartsPremium; @@ -515,6 +566,12 @@ protected boolean useCache(Integer arguments) { public boolean giftAttachMenuIcon; public boolean giftTextFieldIcon; + public int chatlistInvitesLimitDefault; + public int chatlistInvitesLimitPremium; + public int chatlistJoinedLimitDefault; + public int chatlistJoinedLimitPremium; + public String storiesPosting; + public int checkResetLangpack; public void getNextReactionMention(long dialogId, int topicId, int count, Consumer callback) { @@ -930,6 +987,9 @@ public static class PrintingUser { public static int DIALOG_FILTER_FLAG_ONLY_ARCHIVED = 0x00000100; public static int DIALOG_FILTER_FLAG_ALL_CHATS = DIALOG_FILTER_FLAG_CONTACTS | DIALOG_FILTER_FLAG_NON_CONTACTS | DIALOG_FILTER_FLAG_GROUPS | DIALOG_FILTER_FLAG_CHANNELS | DIALOG_FILTER_FLAG_BOTS; + public static int DIALOG_FILTER_FLAG_CHATLIST = 0x00000200; + public static int DIALOG_FILTER_FLAG_CHATLIST_ADMIN = 0x00000400; + public static class DialogFilter { public int id; public String name; @@ -941,8 +1001,13 @@ public static class DialogFilter { public ArrayList neverShow = new ArrayList<>(); public LongSparseIntArray pinnedDialogs = new LongSparseIntArray(); public ArrayList dialogs = new ArrayList<>(); + public String emoticon; + public ArrayList dialogsForward = new ArrayList<>(); + + public ArrayList invites = null; + private static int dialogFilterPointer = 10; public int localId = dialogFilterPointer++; public boolean locked; @@ -1030,13 +1095,21 @@ public boolean alwaysShow(int currentAccount, TLRPC.Dialog dialog) { public boolean isDefault() { return id == 0; } + + public boolean isChatlist() { + return (flags & DIALOG_FILTER_FLAG_CHATLIST) > 0; + } + + public boolean isMyChatlist() { + return isChatlist() && (flags & DIALOG_FILTER_FLAG_CHATLIST_ADMIN) > 0; + } } private DialogFilter sortingDialogFilter; - private Comparator dialogDateComparator = (dialog1, dialog2) -> { - int pinnedNum1 = sortingDialogFilter.pinnedDialogs.get(dialog1.id, Integer.MIN_VALUE); - int pinnedNum2 = sortingDialogFilter.pinnedDialogs.get(dialog2.id, Integer.MIN_VALUE); + private final Comparator dialogDateComparator = (dialog1, dialog2) -> { + int pinnedNum1 = sortingDialogFilter == null ? Integer.MIN_VALUE : sortingDialogFilter.pinnedDialogs.get(dialog1.id, Integer.MIN_VALUE); + int pinnedNum2 = sortingDialogFilter == null ? Integer.MIN_VALUE : sortingDialogFilter.pinnedDialogs.get(dialog2.id, Integer.MIN_VALUE); if (dialog1 instanceof TLRPC.TL_dialogFolder && !(dialog2 instanceof TLRPC.TL_dialogFolder)) { return -1; } else if (!(dialog1 instanceof TLRPC.TL_dialogFolder) && dialog2 instanceof TLRPC.TL_dialogFolder) { @@ -1065,6 +1138,13 @@ public boolean isDefault() { return 0; }; + public void sortDialogsList(ArrayList dialogs) { + if (dialogs == null) { + return; + } + Collections.sort(dialogs, dialogComparator); + } + private Comparator dialogComparator = (dialog1, dialog2) -> { if (dialog1 instanceof TLRPC.TL_dialogFolder && !(dialog2 instanceof TLRPC.TL_dialogFolder)) { return -1; @@ -1244,6 +1324,7 @@ public MessagesController(int num) { MessagesController messagesController = getMessagesController(); getNotificationCenter().addObserver(messagesController, NotificationCenter.fileUploaded); getNotificationCenter().addObserver(messagesController, NotificationCenter.fileUploadFailed); + getNotificationCenter().addObserver(messagesController, NotificationCenter.fileUploadProgressChanged); getNotificationCenter().addObserver(messagesController, NotificationCenter.fileLoaded); getNotificationCenter().addObserver(messagesController, NotificationCenter.fileLoadFailed); getNotificationCenter().addObserver(messagesController, NotificationCenter.messageReceivedByServer); @@ -1284,8 +1365,8 @@ public MessagesController(int num) { availableMapProviders = mainPreferences.getInt("availableMapProviders", 3); mapKey = mainPreferences.getString("pk", null); installReferer = mainPreferences.getString("installReferer", null); - revokeTimeLimit = mainPreferences.getInt("revokeTimeLimit", revokeTimeLimit); - revokeTimePmLimit = mainPreferences.getInt("revokeTimePmLimit", revokeTimePmLimit); + revokeTimeLimit = mainPreferences.getInt("revokeTimeLimit", 2147483647); + revokeTimePmLimit = mainPreferences.getInt("revokeTimePmLimit", 2147483647); canRevokePmInbox = mainPreferences.getBoolean("canRevokePmInbox", canRevokePmInbox); preloadFeaturedStickers = mainPreferences.getBoolean("preloadFeaturedStickers", false); youtubePipType = mainPreferences.getString("youtubePipType", "disabled"); @@ -1340,6 +1421,7 @@ public MessagesController(int num) { publicLinksLimitPremium = mainPreferences.getInt("publicLinksLimitPremium", 20); captionLengthLimitDefault = mainPreferences.getInt("captionLengthLimitDefault", 1024); captionLengthLimitPremium = mainPreferences.getInt("captionLengthLimitPremium", 4096); + storyCaptionLengthLimit = mainPreferences.getInt("storyCaptionLengthLimit", 1024); aboutLengthLimitDefault = mainPreferences.getInt("aboutLengthLimitDefault", 70); aboutLengthLimitPremium = mainPreferences.getInt("aboutLengthLimitPremium", 140); reactionsUserMaxDefault = mainPreferences.getInt("reactionsUserMaxDefault", 1); @@ -1358,20 +1440,32 @@ public MessagesController(int num) { telegramAntispamUserId = mainPreferences.getLong("telegramAntispamUserId", -1); telegramAntispamGroupSizeMin = mainPreferences.getInt("telegramAntispamGroupSizeMin", 100); hiddenMembersGroupSizeMin = mainPreferences.getInt("hiddenMembersGroupSizeMin", 100); + chatlistUpdatePeriod = mainPreferences.getInt("chatlistUpdatePeriod", 3600); uploadMarkupVideo = mainPreferences.getBoolean("uploadMarkupVideo", true); giftAttachMenuIcon = mainPreferences.getBoolean("giftAttachMenuIcon", false); giftTextFieldIcon = mainPreferences.getBoolean("giftTextFieldIcon", false); checkResetLangpack = mainPreferences.getInt("checkResetLangpack", 0); + smallQueueMaxActiveOperations = mainPreferences.getInt("smallQueueMaxActiveOperations", 5); + largeQueueMaxActiveOperations = mainPreferences.getInt("largeQueueMaxActiveOperations", 2); + boolean isTest = ConnectionsManager.native_isTestBackend(currentAccount) != 0; + chatlistInvitesLimitDefault = mainPreferences.getInt("chatlistInvitesLimitDefault", 3); + storyExpiringLimitDefault = mainPreferences.getInt("storyExpiringLimitDefault", 50); + storyExpiringLimitPremium = mainPreferences.getInt("storyExpiringLimitPremium", 100); + chatlistInvitesLimitPremium = mainPreferences.getInt("chatlistInvitesLimitPremium", isTest ? 5 : 20); + chatlistJoinedLimitDefault = mainPreferences.getInt("chatlistJoinedLimitDefault", 2); + chatlistJoinedLimitPremium = mainPreferences.getInt("chatlistJoinedLimitPremium", isTest ? 5 : 20); + storiesPosting = mainPreferences.getString("storiesPosting", "premium"); + storiesExportNopublicLink = mainPreferences.getBoolean("storiesExportNopublicLink", false); // BuildVars.GOOGLE_AUTH_CLIENT_ID = mainPreferences.getString("googleAuthClientId", BuildVars.GOOGLE_AUTH_CLIENT_ID); if (mainPreferences.contains("dcDomainName2")) { dcDomainName = mainPreferences.getString("dcDomainName2", "apv3.stel.com"); } else { - dcDomainName = ConnectionsManager.native_isTestBackend(currentAccount) != 0 ? "tapv3.stel.com" : "apv3.stel.com"; + dcDomainName = isTest ? "tapv3.stel.com" : "apv3.stel.com"; } if (mainPreferences.contains("webFileDatacenterId")) { webFileDatacenterId = mainPreferences.getInt("webFileDatacenterId", 4); } else { - webFileDatacenterId = ConnectionsManager.native_isTestBackend(currentAccount) != 0 ? 2 : 4; + webFileDatacenterId = isTest ? 2 : 4; } Set currencySet = mainPreferences.getStringSet("directPaymentsCurrency", null); @@ -1516,7 +1610,7 @@ public MessagesController(int num) { } - private void sendLoadPeersRequest(TLObject req, ArrayList requests, TLRPC.messages_Dialogs pinnedDialogs, TLRPC.messages_Dialogs pinnedRemoteDialogs, ArrayList users, ArrayList chats, ArrayList filtersToSave, SparseArray filtersToDelete, ArrayList filtersOrder, HashMap> filterDialogRemovals, HashSet filtersUnreadCounterReset) { + private void sendLoadPeersRequest(TLObject req, ArrayList requests, TLRPC.messages_Dialogs pinnedDialogs, TLRPC.messages_Dialogs pinnedRemoteDialogs, ArrayList users, ArrayList chats, ArrayList filtersToSave, SparseArray filtersToDelete, ArrayList filtersOrder, HashMap> filterDialogRemovals, HashSet filtersUnreadCounterReset, Runnable onDone) { getConnectionsManager().sendRequest(req, (response, error) -> { if (response instanceof TLRPC.TL_messages_chats) { TLRPC.TL_messages_chats res = (TLRPC.TL_messages_chats) response; @@ -1538,12 +1632,12 @@ private void sendLoadPeersRequest(TLObject req, ArrayList requests, TL } requests.remove(req); if (requests.isEmpty()) { - getMessagesStorage().processLoadedFilterPeers(pinnedDialogs, pinnedRemoteDialogs, users, chats, filtersToSave, filtersToDelete, filtersOrder, filterDialogRemovals, filtersUnreadCounterReset); + getMessagesStorage().processLoadedFilterPeers(pinnedDialogs, pinnedRemoteDialogs, users, chats, filtersToSave, filtersToDelete, filtersOrder, filterDialogRemovals, filtersUnreadCounterReset, onDone); } }); } - protected void loadFilterPeers(HashMap dialogsToLoadMap, HashMap usersToLoadMap, HashMap chatsToLoadMap, TLRPC.messages_Dialogs pinnedDialogs, TLRPC.messages_Dialogs pinnedRemoteDialogs, ArrayList users, ArrayList chats, ArrayList filtersToSave, SparseArray filtersToDelete, ArrayList filtersOrder, HashMap> filterDialogRemovals, HashSet filtersUnreadCounterReset) { + protected void loadFilterPeers(HashMap dialogsToLoadMap, HashMap usersToLoadMap, HashMap chatsToLoadMap, TLRPC.messages_Dialogs pinnedDialogs, TLRPC.messages_Dialogs pinnedRemoteDialogs, ArrayList users, ArrayList chats, ArrayList filtersToSave, SparseArray filtersToDelete, ArrayList filtersOrder, HashMap> filterDialogRemovals, HashSet filtersUnreadCounterReset, Runnable onDone) { Utilities.stageQueue.postRunnable(() -> { ArrayList requests = new ArrayList<>(); TLRPC.TL_users_getUsers req = null; @@ -1554,12 +1648,12 @@ protected void loadFilterPeers(HashMap dialogsToLoadMap, } req.id.add(getInputUser(entry.getValue())); if (req.id.size() == 100) { - sendLoadPeersRequest(req, requests, pinnedDialogs, pinnedRemoteDialogs, users, chats, filtersToSave, filtersToDelete, filtersOrder, filterDialogRemovals, filtersUnreadCounterReset); + sendLoadPeersRequest(req, requests, pinnedDialogs, pinnedRemoteDialogs, users, chats, filtersToSave, filtersToDelete, filtersOrder, filterDialogRemovals, filtersUnreadCounterReset, onDone); req = null; } } if (req != null) { - sendLoadPeersRequest(req, requests, pinnedDialogs, pinnedRemoteDialogs, users, chats, filtersToSave, filtersToDelete, filtersOrder, filterDialogRemovals, filtersUnreadCounterReset); + sendLoadPeersRequest(req, requests, pinnedDialogs, pinnedRemoteDialogs, users, chats, filtersToSave, filtersToDelete, filtersOrder, filterDialogRemovals, filtersUnreadCounterReset, onDone); } TLRPC.TL_messages_getChats req2 = null; TLRPC.TL_channels_getChannels req3 = null; @@ -1572,7 +1666,7 @@ protected void loadFilterPeers(HashMap dialogsToLoadMap, } req2.id.add(entry.getKey()); if (req2.id.size() == 100) { - sendLoadPeersRequest(req2, requests, pinnedDialogs, pinnedRemoteDialogs, users, chats, filtersToSave, filtersToDelete, filtersOrder, filterDialogRemovals, filtersUnreadCounterReset); + sendLoadPeersRequest(req2, requests, pinnedDialogs, pinnedRemoteDialogs, users, chats, filtersToSave, filtersToDelete, filtersOrder, filterDialogRemovals, filtersUnreadCounterReset, onDone); req2 = null; } } else if (inputPeer.channel_id != 0) { @@ -1582,16 +1676,16 @@ protected void loadFilterPeers(HashMap dialogsToLoadMap, } req3.id.add(getInputChannel(inputPeer)); if (req3.id.size() == 100) { - sendLoadPeersRequest(req3, requests, pinnedDialogs, pinnedRemoteDialogs, users, chats, filtersToSave, filtersToDelete, filtersOrder, filterDialogRemovals, filtersUnreadCounterReset); + sendLoadPeersRequest(req3, requests, pinnedDialogs, pinnedRemoteDialogs, users, chats, filtersToSave, filtersToDelete, filtersOrder, filterDialogRemovals, filtersUnreadCounterReset, onDone); req3 = null; } } } if (req2 != null) { - sendLoadPeersRequest(req2, requests, pinnedDialogs, pinnedRemoteDialogs, users, chats, filtersToSave, filtersToDelete, filtersOrder, filterDialogRemovals, filtersUnreadCounterReset); + sendLoadPeersRequest(req2, requests, pinnedDialogs, pinnedRemoteDialogs, users, chats, filtersToSave, filtersToDelete, filtersOrder, filterDialogRemovals, filtersUnreadCounterReset, onDone); } if (req3 != null) { - sendLoadPeersRequest(req3, requests, pinnedDialogs, pinnedRemoteDialogs, users, chats, filtersToSave, filtersToDelete, filtersOrder, filterDialogRemovals, filtersUnreadCounterReset); + sendLoadPeersRequest(req3, requests, pinnedDialogs, pinnedRemoteDialogs, users, chats, filtersToSave, filtersToDelete, filtersOrder, filterDialogRemovals, filtersUnreadCounterReset, onDone); } TLRPC.TL_messages_getPeerDialogs req4 = null; @@ -1604,17 +1698,17 @@ protected void loadFilterPeers(HashMap dialogsToLoadMap, inputDialogPeer.peer = entry.getValue(); req4.peers.add(inputDialogPeer); if (req4.peers.size() == 100) { - sendLoadPeersRequest(req4, requests, pinnedDialogs, pinnedRemoteDialogs, users, chats, filtersToSave, filtersToDelete, filtersOrder, filterDialogRemovals, filtersUnreadCounterReset); + sendLoadPeersRequest(req4, requests, pinnedDialogs, pinnedRemoteDialogs, users, chats, filtersToSave, filtersToDelete, filtersOrder, filterDialogRemovals, filtersUnreadCounterReset, onDone); req4 = null; } } if (req4 != null) { - sendLoadPeersRequest(req4, requests, pinnedDialogs, pinnedRemoteDialogs, users, chats, filtersToSave, filtersToDelete, filtersOrder, filterDialogRemovals, filtersUnreadCounterReset); + sendLoadPeersRequest(req4, requests, pinnedDialogs, pinnedRemoteDialogs, users, chats, filtersToSave, filtersToDelete, filtersOrder, filterDialogRemovals, filtersUnreadCounterReset, onDone); } }); } - protected void processLoadedDialogFilters(ArrayList filters, TLRPC.messages_Dialogs pinnedDialogs, TLRPC.messages_Dialogs pinnedRemoteDialogs, ArrayList users, ArrayList chats, ArrayList encryptedChats, int remote) { + protected void processLoadedDialogFilters(ArrayList filters, TLRPC.messages_Dialogs pinnedDialogs, TLRPC.messages_Dialogs pinnedRemoteDialogs, ArrayList users, ArrayList chats, ArrayList encryptedChats, int remote, Runnable onDone) { Utilities.stageQueue.postRunnable(() -> { LongSparseArray new_dialogs_dict = new LongSparseArray<>(); @@ -1666,7 +1760,7 @@ protected void processLoadedDialogFilters(ArrayList filters, TLRPC new_dialogMessage.put(dialogId, arrayList); } } - getFileLoader().checkMediaExistance(newMessages); + //getFileLoader().checkMediaExistance(newMessages); for (int a = 0; a < pinnedDialogs.dialogs.size(); a++) { TLRPC.Dialog d = pinnedDialogs.dialogs.get(a); @@ -1821,7 +1915,7 @@ protected void processLoadedDialogFilters(ArrayList filters, TLRPC } } } - getTranslateController().checkDialogMessages(key); + getTranslateController().checkDialogMessage(key); } else { currentDialog.pinned = newDialog.pinned; currentDialog.pinnedNum = newDialog.pinnedNum; @@ -1869,7 +1963,7 @@ protected void processLoadedDialogFilters(ArrayList filters, TLRPC } } } - getTranslateController().checkDialogMessages(key); + getTranslateController().checkDialogMessage(key); } } else { // if (newMsg == null || newMsg.messageOwner.date > oldMsg.messageOwner.date) { @@ -1907,7 +2001,7 @@ protected void processLoadedDialogFilters(ArrayList filters, TLRPC } } } - getTranslateController().checkDialogMessages(key); + getTranslateController().checkDialogMessage(key); } } } @@ -1931,6 +2025,10 @@ protected void processLoadedDialogFilters(ArrayList filters, TLRPC } lockFiltersInternal(); + + if (onDone != null) { + onDone.run(); + } }); }); } @@ -1961,7 +2059,16 @@ public void loadSuggestedFilters() { })); } + private Utilities.Callback onLoadedRemoteFilters; + public void loadRemoteFilters(boolean force) { + loadRemoteFilters(force, null); + } + + public void loadRemoteFilters(boolean force, Utilities.Callback whenDone) { + if (whenDone != null) { + onLoadedRemoteFilters = whenDone; + } if (loadingRemoteFilters || !getUserConfig().isClientActivated() || !force && getUserConfig().filtersLoaded) { return; } @@ -1972,46 +2079,54 @@ public void loadRemoteFilters(boolean force) { TLRPC.TL_messages_getDialogFilters req = new TLRPC.TL_messages_getDialogFilters(); getConnectionsManager().sendRequest(req, (response, error) -> { if (response instanceof TLRPC.Vector) { - getMessagesStorage().checkLoadedRemoteFilters((TLRPC.Vector) response); - + getMessagesStorage().checkLoadedRemoteFilters((TLRPC.Vector) response, () -> { + if (onLoadedRemoteFilters != null) { + onLoadedRemoteFilters.run(true); + onLoadedRemoteFilters = null; + } + }); } else { - AndroidUtilities.runOnUIThread(() -> loadingRemoteFilters = false); + AndroidUtilities.runOnUIThread(() -> { + loadingRemoteFilters = false; + if (onLoadedRemoteFilters != null) { + onLoadedRemoteFilters.run(false); + onLoadedRemoteFilters = null; + } + }); } }); } - private boolean loggedStorageDir; - public void logStorageDir() { - if (collectDeviceStats && !loggedStorageDir) { + private boolean loggedDeviceStats; + public void logDeviceStats() { + if (collectDeviceStats && !loggedDeviceStats) { ArrayList storageDirs = AndroidUtilities.getRootDirs(); - if (storageDirs.isEmpty()) { - return; - } - - String dir = storageDirs.get(0).getAbsolutePath(); - if (!TextUtils.isEmpty(SharedConfig.storageCacheDir)) { - for (int a = 0, N = storageDirs.size(); a < N; a++) { - String path = storageDirs.get(a).getAbsolutePath(); - if (path.startsWith(SharedConfig.storageCacheDir)) { - dir = path; - break; + if (!storageDirs.isEmpty()) { + String dir = storageDirs.get(0).getAbsolutePath(); + if (!TextUtils.isEmpty(SharedConfig.storageCacheDir)) { + for (int a = 0, N = storageDirs.size(); a < N; a++) { + String path = storageDirs.get(a).getAbsolutePath(); + if (path.startsWith(SharedConfig.storageCacheDir)) { + dir = path; + break; + } } } - } - final boolean value = dir.contains("/storage/emulated/"); + final boolean value = dir.contains("/storage/emulated/"); - TLRPC.TL_help_saveAppLog req = new TLRPC.TL_help_saveAppLog(); - TLRPC.TL_inputAppEvent event = new TLRPC.TL_inputAppEvent(); - event.time = getConnectionsManager().getCurrentTime(); - event.type = "android_sdcard_exists"; - TLRPC.TL_jsonBool bool = new TLRPC.TL_jsonBool(); - bool.value = value; - event.data = bool; - event.peer = value ? 1 : 0; - req.events.add(event); + TLRPC.TL_help_saveAppLog req = new TLRPC.TL_help_saveAppLog(); + TLRPC.TL_inputAppEvent event = new TLRPC.TL_inputAppEvent(); + event.time = getConnectionsManager().getCurrentTime(); + event.type = "android_sdcard_exists"; + TLRPC.TL_jsonBool bool = new TLRPC.TL_jsonBool(); + bool.value = value; + event.data = bool; + event.peer = value ? 1 : 0; + req.events.add(event); - getConnectionsManager().sendRequest(req, (response, error) -> {}); - loggedStorageDir = true; + getConnectionsManager().sendRequest(req, (response, error) -> {}); + } + loggedDeviceStats = true; } } @@ -2027,6 +2142,7 @@ public void selectDialogFilter(DialogFilter filter, int index) { if (selectedDialogFilter[index] == null) { if (prevFilter != null) { prevFilter.dialogs.clear(); + prevFilter.dialogsForward.clear(); } } else { sortDialogs(null); @@ -2050,7 +2166,7 @@ public void addFilter(DialogFilter filter, boolean atBegin) { order = Math.min(order, dialogFilters.get(a).order); } filter.order = order - 1; - if (dialogFilters.get(0).isDefault() && !getUserConfig().isPremium()) { + if (dialogFilters.get(0).isDefault()) { dialogFilters.add(1, filter); } else { dialogFilters.add(0, filter); @@ -2093,27 +2209,44 @@ public void removeFilter(DialogFilter filter) { } public void loadAppConfig() { - if (loadingAppConfig) { - return; + loadAppConfig(false); + } + + public void loadAppConfig(boolean force) { + if (force) { + appConfigFetcher.forceRequest(currentAccount, 0); } - loadingAppConfig = true; appConfigFetcher.fetch(currentAccount, 0, config -> AndroidUtilities.runOnUIThread(() -> { if (config != null && config.config instanceof TLRPC.TL_jsonObject) { applyAppConfig((TLRPC.TL_jsonObject) config.config); } - loadingAppConfig = false; })); } private void applyAppConfig(TLRPC.TL_jsonObject object) { SharedPreferences.Editor editor = mainPreferences.edit(); boolean changed = false; + boolean storiesChanged = false; boolean keelAliveChanged = false; resetAppConfig(); TLRPC.TL_jsonObject liteAppOptions = null; for (int a = 0, N = object.value.size(); a < N; a++) { TLRPC.TL_jsonObjectValue value = object.value.get(a); switch (value.key) { + case "large_queue_max_active_operations_count": { + if (value.value instanceof TLRPC.TL_jsonNumber) { + largeQueueMaxActiveOperations = (int) ((TLRPC.TL_jsonNumber) value.value).value; + editor.putInt("largeQueueMaxActiveOperations", largeQueueMaxActiveOperations); + } + break; + } + case "small_queue_max_active_operations_count": { + if (value.value instanceof TLRPC.TL_jsonNumber) { + smallQueueMaxActiveOperations = (int) ((TLRPC.TL_jsonNumber) value.value).value; + editor.putInt("smallQueueMaxActiveOperations", smallQueueMaxActiveOperations); + } + break; + } case "premium_gift_text_field_icon": { if (value.value instanceof TLRPC.TL_jsonBool) { if (giftTextFieldIcon != ((TLRPC.TL_jsonBool) value.value).value) { @@ -2373,17 +2506,6 @@ private void applyAppConfig(TLRPC.TL_jsonObject object) { } break; } - case "autologin_token": { - if (value.value instanceof TLRPC.TL_jsonString) { - TLRPC.TL_jsonString string = (TLRPC.TL_jsonString) value.value; - if (!string.value.equals(autologinToken)) { - autologinToken = string.value; - editor.putString("autologinToken", autologinToken); - changed = true; - } - } - break; - } case "emojies_send_dice": { HashSet newEmojies = new HashSet<>(); if (value.value instanceof TLRPC.TL_jsonArray) { @@ -2961,6 +3083,17 @@ private void applyAppConfig(TLRPC.TL_jsonObject object) { } break; } + case "story_caption_length_limit": { + if (value.value instanceof TLRPC.TL_jsonNumber) { + TLRPC.TL_jsonNumber number = (TLRPC.TL_jsonNumber) value.value; + if (number.value != storyCaptionLengthLimit) { + storyCaptionLengthLimit = (int) number.value; + editor.putInt("storyCaptionLengthLimit", storyCaptionLengthLimit); + changed = true; + } + } + break; + } case "about_length_limit_default": { if (value.value instanceof TLRPC.TL_jsonNumber) { TLRPC.TL_jsonNumber number = (TLRPC.TL_jsonNumber) value.value; @@ -3076,6 +3209,17 @@ private void applyAppConfig(TLRPC.TL_jsonObject object) { } break; } + case "chatlist_update_period": { + if (value.value instanceof TLRPC.TL_jsonNumber) { + TLRPC.TL_jsonNumber number = (TLRPC.TL_jsonNumber) value.value; + if (number.value != chatlistUpdatePeriod) { + chatlistUpdatePeriod = (int) number.value; + editor.putInt("chatlistUpdatePeriod", chatlistUpdatePeriod); + changed = true; + } + } + break; + } case "android_collect_device_stats": { if (value.value instanceof TLRPC.TL_jsonBool) { TLRPC.TL_jsonBool bool = (TLRPC.TL_jsonBool) value.value; @@ -3098,6 +3242,92 @@ private void applyAppConfig(TLRPC.TL_jsonObject object) { } break; } + case "chatlist_invites_limit_default": { + if (value.value instanceof TLRPC.TL_jsonNumber) { + TLRPC.TL_jsonNumber num = (TLRPC.TL_jsonNumber) value.value; + if (num.value != chatlistInvitesLimitDefault) { + chatlistInvitesLimitDefault = (int) num.value; + editor.putInt("chatlistInvitesLimitDefault", chatlistInvitesLimitDefault); + changed = true; + } + } + break; + } + case "story_expiring_limit_default": { + if (value.value instanceof TLRPC.TL_jsonNumber) { + TLRPC.TL_jsonNumber num = (TLRPC.TL_jsonNumber) value.value; + if (num.value != storyExpiringLimitDefault) { + storyExpiringLimitDefault = (int) num.value; + editor.putInt("storyExpiringLimitDefault", storyExpiringLimitDefault); + changed = true; + } + } + break; + } + case "story_expiring_limit_premium": { + if (value.value instanceof TLRPC.TL_jsonNumber) { + TLRPC.TL_jsonNumber num = (TLRPC.TL_jsonNumber) value.value; + if (num.value != storyExpiringLimitPremium) { + storyExpiringLimitPremium = (int) num.value; + editor.putInt("storyExpiringLimitPremium", storyExpiringLimitPremium); + changed = true; + } + } + break; + } + case "chatlist_invites_limit_premium": { + if (value.value instanceof TLRPC.TL_jsonNumber) { + TLRPC.TL_jsonNumber num = (TLRPC.TL_jsonNumber) value.value; + if (num.value != chatlistInvitesLimitPremium) { + chatlistInvitesLimitPremium = (int) num.value; + editor.putInt("chatlistInvitesLimitPremium", chatlistInvitesLimitPremium); + changed = true; + } + } + break; + } + case "chatlists_joined_limit_default": { + if (value.value instanceof TLRPC.TL_jsonNumber) { + TLRPC.TL_jsonNumber num = (TLRPC.TL_jsonNumber) value.value; + if (num.value != chatlistJoinedLimitDefault) { + chatlistJoinedLimitDefault = (int) num.value; + editor.putInt("chatlistJoinedLimitDefault", chatlistJoinedLimitDefault); + changed = true; + } + } + break; + } + case "chatlists_joined_limit_premium": { + if (value.value instanceof TLRPC.TL_jsonNumber) { + TLRPC.TL_jsonNumber num = (TLRPC.TL_jsonNumber) value.value; + if (num.value != chatlistJoinedLimitPremium) { + chatlistJoinedLimitPremium = (int) num.value; + editor.putInt("chatlistJoinedLimitPremium", chatlistJoinedLimitPremium); + changed = true; + } + } + break; + } + case "stories_posting": { + if (value.value instanceof TLRPC.TL_jsonString) { + TLRPC.TL_jsonString bool = (TLRPC.TL_jsonString) value.value; + if (!TextUtils.equals(bool.value, storiesPosting)) { + storiesPosting = bool.value; + editor.putString("storiesPosting", storiesPosting); + changed = storiesChanged = true; + } + } + } + case "stories_export_nopublic_link": { + if (value.value instanceof TLRPC.TL_jsonBool) { + TLRPC.TL_jsonBool bool = (TLRPC.TL_jsonBool) value.value; + if (storiesExportNopublicLink != bool.value) { + storiesExportNopublicLink = bool.value; + editor.putBoolean("storiesExportNopublicLink", storiesExportNopublicLink); + changed = true; + } + } + } } } if (changed) { @@ -3111,7 +3341,12 @@ private void applyAppConfig(TLRPC.TL_jsonObject object) { ConnectionsManager connectionsManager = getConnectionsManager(); connectionsManager.setPushConnectionEnabled(connectionsManager.isPushConnectionEnabled()); } - logStorageDir(); + if (storiesChanged) { + AndroidUtilities.runOnUIThread(() -> { + getNotificationCenter().postNotificationName(NotificationCenter.storiesEnabledUpdate); + }); + } + logDeviceStats(); } private void resetAppConfig() { @@ -3195,6 +3430,7 @@ public void updateConfig(final TLRPC.TL_config config) { getDownloadController().loadAutoDownloadConfig(false); loadAppConfig(); thisDc = config.this_dc; + remoteConfigLoaded = true; maxMegagroupCount = config.megagroup_size_max; maxGroupCount = config.chat_size_max; maxEditTime = config.edit_time_limit; @@ -3326,6 +3562,7 @@ public void updateConfig(final TLRPC.TL_config config) { editor.putInt("webFileDatacenterId", webFileDatacenterId); editor.putString("suggestedLangCode", suggestedLangCode); editor.putBoolean("forceTryIpV6", forceTryIpV6); + editor.putString("autologinToken", autologinToken = config.autologin_token); editor.commit(); getConnectionsManager().setForceTryIpV6(forceTryIpV6); @@ -3543,11 +3780,14 @@ public void didReceivedNotification(int id, int account, Object... args) { req.file = file; req.mime_type = "image/jpeg"; Theme.OverrideWallpaperInfo overrideWallpaperInfo = uploadingWallpaperInfo; + String uploadingWallpaperFinal = uploadingWallpaper; TLRPC.TL_wallPaperSettings settings = new TLRPC.TL_wallPaperSettings(); settings.blur = overrideWallpaperInfo.isBlurred; settings.motion = overrideWallpaperInfo.isMotion; req.settings = settings; - getConnectionsManager().sendRequest(req, (response, error) -> { + uploadingWallpaperInfo.uploadingProgress = 1f; + uploadingWallpaperInfo.requestIds = new ArrayList<>(); + uploadingWallpaperInfo.requestIds.add(getConnectionsManager().sendRequest(req, (response, error) -> { TLRPC.WallPaper wallPaper = (TLRPC.WallPaper) response; File path = new File(ApplicationLoader.getFilesDirFixed(), overrideWallpaperInfo.originalFileName); if (wallPaper != null) { @@ -3573,9 +3813,12 @@ public void didReceivedNotification(int id, int account, Object... args) { ImageLoader.getInstance().replaceImageInCache(oldKey, newKey, ImageLocation.getForDocument(image, wallPaper.document), false); } NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.wallpapersNeedReload, wallPaper.slug); + if (overrideWallpaperInfo.dialogId != 0) { + uploadingWallpaperInfo.requestIds.add(ChatThemeController.getInstance(currentAccount).setWallpaperToUser(overrideWallpaperInfo.dialogId, uploadingWallpaperFinal, overrideWallpaperInfo, null, null)); + } } }); - }); + })); } else { Object object = uploadingThemes.get(location); Theme.ThemeInfo themeInfo; @@ -3775,6 +4018,13 @@ public void didReceivedNotification(int id, int account, Object... args) { getNotificationCenter().postNotificationName(NotificationCenter.themeUploadError, accent.parentTheme, accent); } } + } if (id == NotificationCenter.fileUploadProgressChanged) { + String location = (String) args[0]; + if (uploadingWallpaper != null && uploadingWallpaper.equals(location)) { + Long loadedSize = (Long) args[1]; + Long totalSize = (Long) args[2]; + uploadingWallpaperInfo.uploadingProgress = loadedSize / (float) totalSize; + } } else if (id == NotificationCenter.messageReceivedByServer) { Boolean scheduled = (Boolean) args[6]; if (scheduled) { @@ -3835,6 +4085,9 @@ public void cleanup() { getMediaDataController().cleanup(); getColorPalette().cleanup(); getTranslateController().cleanup(); + if (storiesController != null) { + storiesController.cleanup(); + } showFiltersTooltip = false; @@ -3998,8 +4251,6 @@ public void cleanup() { dialogsEndReached.clear(); serverDialogsEndReached.clear(); - loadingAppConfig = false; - checkingTosUpdate = false; nextTosCheckTime = 0; nextPromoInfoCheckTime = 0; @@ -4905,6 +5156,7 @@ public void loadFullUser(final TLRPC.User user, int classGuid, boolean force) { putChats(res.chats, false); res.full_user.user = getUser(res.full_user.id); getMessagesStorage().updateUserInfo(userFull, false); + ChatThemeController.getInstance(currentAccount).saveChatWallpaper(res.full_user.id, res.full_user.wallpaper); AndroidUtilities.runOnUIThread(() -> { savePeerSettings(userFull.user.id, userFull.settings, false); @@ -5151,7 +5403,7 @@ private void savePeerSettings(long dialogId, TLRPC.TL_peerSettings settings, boo return; } SharedPreferences.Editor editor = notificationsPreferences.edit(); - boolean bar_hidden = !settings.report_spam && !settings.add_contact && !settings.block_contact && !settings.share_contact && !settings.report_geo && !settings.invite_members; + boolean bar_hidden = settings.flags == 0; if (BuildVars.LOGS_ENABLED) { FileLog.d("peer settings loaded for " + dialogId + " add = " + settings.add_contact + " block = " + settings.block_contact + " spam = " + settings.report_spam + " share = " + settings.share_contact + " geo = " + settings.report_geo + " hide = " + bar_hidden + " distance = " + settings.geo_distance + " invite = " + settings.invite_members); } @@ -5919,40 +6171,8 @@ public void saveWallpaperToServer(File path, Theme.OverrideWallpaperInfo info, b uploadingWallpaperInfo = info; getFileLoader().uploadFile(uploadingWallpaper, false, true, ConnectionsManager.FileTypePhoto); } else if (!info.isDefault() && !info.isColor() && info.wallpaperId > 0 && !info.isTheme()) { - TLRPC.InputWallPaper inputWallPaper; - if (info.wallpaperId > 0) { - TLRPC.TL_inputWallPaper inputWallPaperId = new TLRPC.TL_inputWallPaper(); - inputWallPaperId.id = info.wallpaperId; - inputWallPaperId.access_hash = info.accessHash; - inputWallPaper = inputWallPaperId; - } else { - TLRPC.TL_inputWallPaperSlug inputWallPaperSlug = new TLRPC.TL_inputWallPaperSlug(); - inputWallPaperSlug.slug = info.slug; - inputWallPaper = inputWallPaperSlug; - } - - TLRPC.TL_wallPaperSettings settings = new TLRPC.TL_wallPaperSettings(); - settings.blur = info.isBlurred; - settings.motion = info.isMotion; - if (info.color != 0) { - settings.background_color = info.color & 0x00ffffff; - settings.flags |= 1; - settings.intensity = (int) (info.intensity * 100); - settings.flags |= 8; - } - if (info.gradientColor1 != 0) { - settings.second_background_color = info.gradientColor1 & 0x00ffffff; - settings.rotation = AndroidUtilities.getWallpaperRotation(info.rotation, true); - settings.flags |= 16; - } - if (info.gradientColor2 != 0) { - settings.third_background_color = info.gradientColor2 & 0x00ffffff; - settings.flags |= 32; - } - if (info.gradientColor3 != 0) { - settings.fourth_background_color = info.gradientColor3 & 0x00ffffff; - settings.flags |= 64; - } + TLRPC.InputWallPaper inputWallPaper = getInputWallpaper(info); + TLRPC.TL_wallPaperSettings settings = getWallpaperSetting(info); TLObject req; if (install) { @@ -6041,6 +6261,50 @@ public void saveWallpaperToServer(File path, Theme.OverrideWallpaperInfo info, b } } + public static TLRPC.TL_wallPaperSettings getWallpaperSetting(Theme.OverrideWallpaperInfo info) { + TLRPC.TL_wallPaperSettings settings = new TLRPC.TL_wallPaperSettings(); + settings.blur = info.isBlurred; + settings.motion = info.isMotion; + if (info.color != 0) { + settings.background_color = info.color & 0x00ffffff; + settings.flags |= 1; + settings.intensity = (int) (info.intensity * 100); + settings.flags |= 8; + } else if (info.intensity > 0) { + settings.intensity = (int) (info.intensity * 100); + settings.flags |= 8; + } + if (info.gradientColor1 != 0) { + settings.second_background_color = info.gradientColor1 & 0x00ffffff; + settings.rotation = AndroidUtilities.getWallpaperRotation(info.rotation, true); + settings.flags |= 16; + } + if (info.gradientColor2 != 0) { + settings.third_background_color = info.gradientColor2 & 0x00ffffff; + settings.flags |= 32; + } + if (info.gradientColor3 != 0) { + settings.fourth_background_color = info.gradientColor3 & 0x00ffffff; + settings.flags |= 64; + } + return settings; + } + + public static TLRPC.InputWallPaper getInputWallpaper(Theme.OverrideWallpaperInfo info) { + TLRPC.InputWallPaper inputWallPaper; + if (info.wallpaperId > 0) { + TLRPC.TL_inputWallPaper inputWallPaperId = new TLRPC.TL_inputWallPaper(); + inputWallPaperId.id = info.wallpaperId; + inputWallPaperId.access_hash = info.accessHash; + inputWallPaper = inputWallPaperId; + } else { + TLRPC.TL_inputWallPaperSlug inputWallPaperSlug = new TLRPC.TL_inputWallPaperSlug(); + inputWallPaperSlug.slug = info.slug; + inputWallPaper = inputWallPaperSlug; + } + return inputWallPaper; + } + public void markDialogMessageAsDeleted(long dialogId, ArrayList messages) { ArrayList objs = dialogMessage.get(dialogId); if (objs != null) { @@ -6448,6 +6712,7 @@ private void removeDialog(TLRPC.Dialog dialog) { for (int a = 0; a < selectedDialogFilter.length; a++) { if (selectedDialogFilter[a] != null) { selectedDialogFilter[a].dialogs.remove(dialog); + selectedDialogFilter[a].dialogsForward.remove(dialog); } } dialogs_dict.remove(did); @@ -6821,7 +7086,11 @@ public void saveRecentSticker(Object parentObject, TLRPC.Document document, bool } public void loadChannelParticipants(Long chatId) { - if (loadingFullParticipants.contains(chatId) || loadedFullParticipants.contains(chatId)) { + loadChannelParticipants(chatId, null); + } + + public void loadChannelParticipants(Long chatId, Utilities.Callback whenDone) { + if (whenDone == null && (loadingFullParticipants.contains(chatId) || loadedFullParticipants.contains(chatId))) { return; } loadingFullParticipants.add(chatId); @@ -6841,6 +7110,9 @@ public void loadChannelParticipants(Long chatId) { loadedFullParticipants.add(chatId); } loadingFullParticipants.remove(chatId); + if (whenDone != null) { + whenDone.run(response instanceof TLRPC.TL_channels_channelParticipants ? (TLRPC.TL_channels_channelParticipants) response : null); + } })); } @@ -7514,28 +7786,29 @@ private void checkPromoInfoInternal(boolean reset) { } objects.add(messageObject); dialogMessage.put(did, objects); - if (promoDialog.last_message_date == 0) { - promoDialog.last_message_date = messageObject.messageOwner.date; - } - getTranslateController().checkDialogMessages(did);} - sortDialogs(null); - getNotificationCenter().postNotificationName(NotificationCenter.dialogsNeedReload, true); - }); - } else { - AndroidUtilities.runOnUIThread(() -> { - if (promoDialog != null) { - if (promoDialog.id < 0) { - TLRPC.Chat chat = getChat(-promoDialog.id); - if (ChatObject.isNotInChat(chat) || chat.restricted) { - removeDialog(promoDialog); - } - } else { + if (promoDialog.last_message_date == 0) { + promoDialog.last_message_date = messageObject.messageOwner.date; + } + getTranslateController().checkDialogMessage(did); + } + sortDialogs(null); + getNotificationCenter().postNotificationName(NotificationCenter.dialogsNeedReload, true); + }); + } else { + AndroidUtilities.runOnUIThread(() -> { + if (promoDialog != null) { + if (promoDialog.id < 0) { + TLRPC.Chat chat = getChat(-promoDialog.id); + if (ChatObject.isNotInChat(chat) || chat.restricted) { removeDialog(promoDialog); } - promoDialog = null; - sortDialogs(null); - getNotificationCenter().postNotificationName(NotificationCenter.dialogsNeedReload); + } else { + removeDialog(promoDialog); } + promoDialog = null; + sortDialogs(null); + getNotificationCenter().postNotificationName(NotificationCenter.dialogsNeedReload); + } }); } checkingPromoInfo = false; @@ -7932,21 +8205,24 @@ public void loadMessages(long dialogId, long mergeDialogId, boolean loadInfo, in } public void loadMessages(long dialogId, long mergeDialogId, boolean loadInfo, int count, int max_id, int offset_date, boolean fromCache, int midDate, int classGuid, int load_type, int last_message_id, int mode, int threadMessageId, int loadIndex, int first_unread, int unread_count, int last_date, boolean queryFromServer, int mentionsCount, boolean isTopic) { - loadMessagesInternal(dialogId, mergeDialogId, loadInfo, count, max_id, offset_date, fromCache, midDate, classGuid, load_type, last_message_id, mode, threadMessageId, loadIndex, first_unread, unread_count, last_date, queryFromServer, mentionsCount, true, true, isTopic); + loadMessagesInternal(dialogId, mergeDialogId, loadInfo, count, max_id, offset_date, fromCache, midDate, classGuid, load_type, last_message_id, mode, threadMessageId, loadIndex, first_unread, unread_count, last_date, queryFromServer, mentionsCount, true, true, isTopic, null); } - private void loadMessagesInternal(long dialogId, long mergeDialogId, boolean loadInfo, int count, int max_id, int offset_date, boolean fromCache, int minDate, int classGuid, int load_type, int last_message_id, int mode, int threadMessageId, int loadIndex, int first_unread, int unread_count, int last_date, boolean queryFromServer, int mentionsCount, boolean loadDialog, boolean processMessages, boolean isTopic) { + private void loadMessagesInternal(long dialogId, long mergeDialogId, boolean loadInfo, int count, int max_id, int offset_date, boolean fromCache, int minDate, int classGuid, int load_type, int last_message_id, int mode, int threadMessageId, int loadIndex, int first_unread, int unread_count, int last_date, boolean queryFromServer, int mentionsCount, boolean loadDialog, boolean processMessages, boolean isTopic, MessageLoaderLogger loaderLogger) { if (BuildVars.LOGS_ENABLED) { FileLog.d("load messages in chat " + dialogId + " topic_id " + threadMessageId + " count " + count + " max_id " + max_id + " cache " + fromCache + " mindate = " + minDate + " guid " + classGuid + " load_type " + load_type + " last_message_id " + last_message_id + " mode " + mode + " index " + loadIndex + " firstUnread " + first_unread + " unread_count " + unread_count + " last_date " + last_date + " queryFromServer " + queryFromServer + " isTopic " + isTopic); } + if (BuildVars.LOGS_ENABLED && loaderLogger == null && mode == 0) { + loaderLogger = new MessageLoaderLogger(dialogId, loadIndex, count); + } if ((threadMessageId == 0 || isTopic) && mode != 2 && (fromCache || DialogObject.isEncryptedDialog(dialogId))) { - getMessagesStorage().getMessages(dialogId, mergeDialogId, loadInfo, count, max_id, offset_date, minDate, classGuid, load_type, mode == 1, threadMessageId, loadIndex, processMessages, isTopic); + getMessagesStorage().getMessages(dialogId, mergeDialogId, loadInfo, count, max_id, offset_date, minDate, classGuid, load_type, mode == 1, threadMessageId, loadIndex, processMessages, isTopic, loaderLogger); } else { if (threadMessageId != 0) { if (loadDialog && isTopic && load_type == 2 && last_message_id == 0) { TLRPC.TL_forumTopic topic = topicsController.findTopic(-dialogId, threadMessageId); if (topic != null) { - loadMessagesInternal(dialogId, mergeDialogId, loadInfo, count, max_id, offset_date, false, minDate, classGuid, load_type, topic.top_message, 0, threadMessageId, loadIndex, first_unread, topic.unread_count, last_date, queryFromServer, topic.unread_mentions_count, false, processMessages, isTopic); + loadMessagesInternal(dialogId, mergeDialogId, loadInfo, count, max_id, offset_date, false, minDate, classGuid, load_type, topic.top_message, 0, threadMessageId, loadIndex, first_unread, topic.unread_count, last_date, queryFromServer, topic.unread_mentions_count, false, processMessages, isTopic, loaderLogger); return; } } @@ -8004,7 +8280,7 @@ private void loadMessagesInternal(long dialogId, long mergeDialogId, boolean loa } } } - processLoadedMessages(res, res.messages.size(), dialogId, mergeDialogId, count, mid, offset_date, false, classGuid, fnid, last_message_id, unread_count, last_date, load_type, false, 0, threadMessageId, loadIndex, queryFromServer, mentionsCount, processMessages, isTopic); + processLoadedMessages(res, res.messages.size(), dialogId, mergeDialogId, count, mid, offset_date, false, classGuid, fnid, last_message_id, unread_count, last_date, load_type, false, 0, threadMessageId, loadIndex, queryFromServer, mentionsCount, processMessages, isTopic, null); } else { AndroidUtilities.runOnUIThread(() -> getNotificationCenter().postNotificationName(NotificationCenter.loadingMessagesFailed, classGuid, req, error)); } @@ -8032,7 +8308,7 @@ private void loadMessagesInternal(long dialogId, long mergeDialogId, boolean loa } } } - processLoadedMessages(res, res.messages.size(), dialogId, mergeDialogId, count, mid, offset_date, false, classGuid, first_unread, last_message_id, unread_count, last_date, load_type, false, mode, threadMessageId, loadIndex, queryFromServer, mentionsCount, processMessages, isTopic); + processLoadedMessages(res, res.messages.size(), dialogId, mergeDialogId, count, mid, offset_date, false, classGuid, first_unread, last_message_id, unread_count, last_date, load_type, false, mode, threadMessageId, loadIndex, queryFromServer, mentionsCount, processMessages, isTopic, null); } }); getConnectionsManager().bindRequestToGuid(reqId, classGuid); @@ -8059,7 +8335,7 @@ private void loadMessagesInternal(long dialogId, long mergeDialogId, boolean loa getMessagesStorage().putDialogs(dialogs, 2); } - loadMessagesInternal(dialogId, mergeDialogId, loadInfo, count, max_id, offset_date, false, minDate, classGuid, load_type, dialog.top_message, 0, threadMessageId, loadIndex, first_unread, dialog.unread_count, last_date, queryFromServer, dialog.unread_mentions_count, false, processMessages, isTopic); + loadMessagesInternal(dialogId, mergeDialogId, loadInfo, count, max_id, offset_date, false, minDate, classGuid, load_type, dialog.top_message, 0, threadMessageId, loadIndex, first_unread, dialog.unread_count, last_date, queryFromServer, dialog.unread_mentions_count, false, processMessages, isTopic, null); } } else { AndroidUtilities.runOnUIThread(() -> getNotificationCenter().postNotificationName(NotificationCenter.loadingMessagesFailed, classGuid, req, error)); @@ -8107,7 +8383,7 @@ private void loadMessagesInternal(long dialogId, long mergeDialogId, boolean loa } } } - processLoadedMessages(res, res.messages.size(), dialogId, mergeDialogId, count, mid, offset_date, false, classGuid, first_unread, last_message_id, unread_count, last_date, load_type, false, 0, threadMessageId, loadIndex, queryFromServer, mentionsCount, processMessages, isTopic); + processLoadedMessages(res, res.messages.size(), dialogId, mergeDialogId, count, mid, offset_date, false, classGuid, first_unread, last_message_id, unread_count, last_date, load_type, false, 0, threadMessageId, loadIndex, queryFromServer, mentionsCount, processMessages, isTopic, null); } else { AndroidUtilities.runOnUIThread(() -> getNotificationCenter().postNotificationName(NotificationCenter.loadingMessagesFailed, classGuid, req, error)); } @@ -8166,7 +8442,7 @@ public void reloadWebPages(final long dialogId, HashMap 60 * 1000); } else { reload = resCount == 0 && (!isInitialLoading || (SystemClock.elapsedRealtime() - lastServerQueryTime.get(dialogId, 0L)) > 60 * 1000 || (isCache && isTopic)); - if (isCache && dialogId < 0) { - TLRPC.Chat chat = getChat(-dialogId); - if (chat == null) { - chat = chatsDict.get(-dialogId); - } - if (chat != null && mode == 0 && ChatObject.isNotInChat(chat) && (SystemClock.elapsedRealtime() - lastServerQueryTime.get(dialogId, 0L)) > 24 * 60 * 60 * 1000) { - messagesRes.messages.clear(); - reload = true; - } - } +// if (isCache && dialogId < 0) { +// TLRPC.Chat chat = getChat(-dialogId); +// if (chat == null) { +// chat = chatsDict.get(-dialogId); +// } +// if (chat != null && mode == 0 && ChatObject.isNotInChat(chat) && (SystemClock.elapsedRealtime() - lastServerQueryTime.get(dialogId, 0L)) > 24 * 60 * 60 * 1000) { +// messagesRes.messages.clear(); +// reload = true; +// } +// } } if (!DialogObject.isEncryptedDialog(dialogId) && isCache && reload) { int hash; @@ -8242,7 +8518,10 @@ public void processLoadedMessages(TLRPC.messages_Messages messagesRes, int resCo lastServerQueryTime.put(dialogId, SystemClock.elapsedRealtime()); hash = 0; } - AndroidUtilities.runOnUIThread(() -> loadMessagesInternal(dialogId, mergeDialogId, false, count, load_type == 2 && queryFromServer ? first_unread : max_id, offset_date, false, hash, classGuid, load_type, last_message_id, mode, threadMessageId, loadIndex, first_unread, unread_count, last_date, queryFromServer, mentionsCount, true, needProcess, isTopic)); + if (loaderLogger != null) { + loaderLogger.reload(); + } + AndroidUtilities.runOnUIThread(() -> loadMessagesInternal(dialogId, mergeDialogId, false, count, load_type == 2 && queryFromServer ? first_unread : max_id, offset_date, false, hash, classGuid, load_type, last_message_id, mode, threadMessageId, loadIndex, first_unread, unread_count, last_date, queryFromServer, mentionsCount, true, needProcess, isTopic, loaderLogger)); if (messagesRes.messages.isEmpty()) { return; } @@ -8335,10 +8614,16 @@ public void processLoadedMessages(TLRPC.messages_Messages messagesRes, int resCo if (BuildVars.LOGS_ENABLED) { FileLog.d("process time=" + (SystemClock.elapsedRealtime() - startProcessTime) + " count=" + objects.size() + " for dialog " + dialogId); } + if (loaderLogger != null) { + loaderLogger.logStageQueueProcessing(); + } AndroidUtilities.runOnUIThread(() -> { putUsers(messagesRes.users, isCache); putChats(messagesRes.chats, isCache); + if (loaderLogger != null) { + loaderLogger.finish(); + } if (messagesRes.animatedEmoji != null && needProcess) { AnimatedEmojiDrawable.getDocumentFetcher(currentAccount).processDocuments(messagesRes.animatedEmoji); } @@ -8409,7 +8694,7 @@ public void loadHintDialogs() { }); } - private TLRPC.TL_dialogFolder ensureFolderDialogExists(int folderId, boolean[] folderCreated) { + public TLRPC.TL_dialogFolder ensureFolderDialogExists(int folderId, boolean[] folderCreated) { if (folderId == 0) { return null; } @@ -8470,12 +8755,38 @@ private void removeFolder(int folderId) { protected void onFolderEmpty(int folderId) { long[] dialogsLoadOffset = getUserConfig().getDialogLoadOffsets(folderId); if (dialogsLoadOffset[UserConfig.i_dialogsLoadOffsetId] == Integer.MAX_VALUE) { - removeFolder(folderId); + if (folderId == 1) { + hasArchivedChats = false; + checkArchiveFolder(); + } else { + removeFolder(folderId); + } } else { - loadDialogs(folderId, 0, 10, false, () -> removeFolder(folderId)); + loadDialogs(folderId, 0, 10, false, () -> { + if (folderId == 1) { + hasArchivedChats = false; + checkArchiveFolder(); + } else { + removeFolder(folderId); + } + }); } } + public void checkArchiveFolder() { + if (!hasArchivedChats && !getStoriesController().hasHiddenStories()) { + removeFolder(1); + } else { + boolean[] created = new boolean[]{false}; + ensureFolderDialogExists(1, created); + if (created[0]) { + sortDialogs(null); + getNotificationCenter().postNotificationName(NotificationCenter.dialogsNeedReload); + } + } + getNotificationCenter().postNotificationName(NotificationCenter.updateInterfaces, 0); + } + public void checkIfFolderEmpty(int folderId) { if (folderId == 0) { return; @@ -8521,6 +8832,7 @@ public int addDialogToFolder(ArrayList dialogIds, int folderId, int pinned } if (folderCreated == null) { folderCreated = new boolean[1]; + hasArchivedChats = true; ensureFolderDialogExists(folderId, folderCreated); } if (DialogObject.isEncryptedDialog(dialogId)) { @@ -8736,6 +9048,12 @@ public void loadGlobalNotificationsSettings() { if ((notify_settings.flags & 1) != 0) { editor.putBoolean("EnablePreviewAll", notify_settings.show_previews); } + if ((notify_settings.flags & 64) != 0) { + editor.putBoolean("EnableAllStories", !notify_settings.stories_muted); + } + if ((notify_settings.flags & 128) != 0) { + editor.putBoolean("EnableHideStoriesSenders", notify_settings.stories_hide_sender); + } if ((notify_settings.flags & 2) != 0) { /*if (notify_settings.silent) { editor.putString("GlobalSoundPath", "NoSound"); @@ -8746,6 +9064,9 @@ public void loadGlobalNotificationsSettings() { if ((notify_settings.flags & 4) != 0) { editor.putInt("EnableAll2", notify_settings.mute_until); } + if ((notify_settings.flags & 64) != 0) { + editor.putBoolean("EnableAllStories", !notify_settings.stories_muted); + } } else { if ((notify_settings.flags & 1) != 0) { editor.putBoolean("EnablePreviewChannel", notify_settings.show_previews); @@ -9125,7 +9446,7 @@ protected void completeDialogsReset(final TLRPC.messages_Dialogs dialogsRes, int } } } - getTranslateController().checkDialogMessages(key); + getTranslateController().checkDialogMessage(key); } allDialogs.clear(); @@ -9432,6 +9753,9 @@ public void processLoadedDialogs(final TLRPC.messages_Dialogs dialogsRes, ArrayL if (NekoConfig.ignoreBlocked.Bool() && getMessagesController().blockePeers.indexOfKey(message.peer_id.user_id) >= 0) { continue; } + if (message.date == 0) { + continue; + } if (lastMessage == null || message.date < lastMessage.date) { lastMessage = message; } @@ -9456,8 +9780,6 @@ public void processLoadedDialogs(final TLRPC.messages_Dialogs dialogsRes, ArrayL arrayList.add(messageObject); new_dialogMessage.put(did, arrayList); } - getFileLoader().checkMediaExistance(newMessages); - if (!fromCache && !migrate && dialogsLoadOffset[UserConfig.i_dialogsLoadOffsetId] != -1 && loadType == 0) { int totalDialogsLoadCount = getUserConfig().getTotalDialogsCount(folderId); int dialogsLoadOffsetId; @@ -9546,6 +9868,7 @@ public void processLoadedDialogs(final TLRPC.messages_Dialogs dialogsRes, ArrayL if (maxDate > Integer.MIN_VALUE) { d.last_message_date = maxDate; } + } } boolean allowCheck = true; @@ -9657,6 +9980,7 @@ public void processLoadedDialogs(final TLRPC.messages_Dialogs dialogsRes, ArrayL int archivedDialogsCount = 0; int lastDialogDate = migrate && !allDialogs.isEmpty() ? allDialogs.get(allDialogs.size() - 1).last_message_date : 0; + final boolean translating = getTranslateController().isFeatureAvailable(); for (int a = 0; a < new_dialogs_dict.size(); a++) { long key = new_dialogs_dict.keyAt(a); TLRPC.Dialog value = new_dialogs_dict.valueAt(a); @@ -9689,7 +10013,9 @@ public void processLoadedDialogs(final TLRPC.messages_Dialogs dialogsRes, ArrayL } } } - getTranslateController().checkDialogMessages(key); + if (translating) { + getTranslateController().checkDialogMessageSure(key); + } } else { if (loadType != DIALOGS_LOAD_TYPE_CACHE) { currentDialog.notify_settings = value.notify_settings; @@ -9741,7 +10067,9 @@ public void processLoadedDialogs(final TLRPC.messages_Dialogs dialogsRes, ArrayL } } } - getTranslateController().checkDialogMessages(key); + if (translating) { + getTranslateController().checkDialogMessageSure(key); + } } } else { // if (newMsg == null && oldMs.getId() > 0 || newMsg != null && newMsg.messageOwner.date > oldMsg.messageOwner.date) @@ -9779,7 +10107,9 @@ public void processLoadedDialogs(final TLRPC.messages_Dialogs dialogsRes, ArrayL } } } - getTranslateController().checkDialogMessages(key); + if (translating) { + getTranslateController().checkDialogMessageSure(key); + } } } } @@ -10199,6 +10529,8 @@ public void processDialogsUpdate(final TLRPC.messages_Dialogs dialogsRes, ArrayL putUsers(dialogsRes.users, true); putChats(dialogsRes.chats, true); + final boolean translating = getTranslateController().isFeatureAvailable(); + for (int a = 0; a < new_dialogs_dict.size(); a++) { long key = new_dialogs_dict.keyAt(a); if (BuildVars.LOGS_ENABLED) { @@ -10237,7 +10569,9 @@ public void processDialogsUpdate(final TLRPC.messages_Dialogs dialogsRes, ArrayL FileLog.d("processDialogsUpdate new message not null"); } } - getTranslateController().checkDialogMessages(key); + if (translating) { + getTranslateController().checkDialogMessageSure(key); + } } else { if (BuildVars.LOGS_ENABLED) { FileLog.d("processDialogsUpdate dialog not null"); @@ -10297,7 +10631,9 @@ public void processDialogsUpdate(final TLRPC.messages_Dialogs dialogsRes, ArrayL } } } - getTranslateController().checkDialogMessages(key); + if (translating) { + getTranslateController().checkDialogMessageSure(key); + } } if (fromCache && newMsgs == null) { checkLastDialogMessage(value, null, 0); @@ -10344,7 +10680,9 @@ public void processDialogsUpdate(final TLRPC.messages_Dialogs dialogsRes, ArrayL } } } - getTranslateController().checkDialogMessages(key); + if (translating) { + getTranslateController().checkDialogMessageSure(key); + } } } } @@ -11260,9 +11598,9 @@ public void addUsersToChat(TLRPC.Chat currentChat, BaseFragment baseFragment, Ar final ArrayList userRestrictedPrivacy = new ArrayList<>(); processed[0] = 0; final Runnable showUserRestrictedPrivacyAlert = () -> { - AndroidUtilities.runOnUIThread(() ->{ + AndroidUtilities.runOnUIThread(() -> { BaseFragment lastFragment = LaunchActivity.getLastFragment(); - if (lastFragment != null && lastFragment.getParentActivity() != null) { + if (lastFragment != null && lastFragment.getParentActivity() != null && !lastFragment.getParentActivity().isFinishing()) { // if (ChatObject.canUserDoAdminAction(currentChat, ChatObject.ACTION_INVITE)) { LimitReachedBottomSheet restricterdUsersBottomSheet = new LimitReachedBottomSheet(lastFragment, lastFragment.getParentActivity(), LimitReachedBottomSheet.TYPE_ADD_MEMBERS_RESTRICTED, currentAccount); restricterdUsersBottomSheet.setRestrictedUsers(currentChat, userRestrictedPrivacy); @@ -12107,6 +12445,9 @@ protected void loadUnknownChannel(final TLRPC.Chat channel, long taskId) { } public void startShortPoll(TLRPC.Chat chat, int guid, boolean stop) { + startShortPoll(chat, guid, stop, null); + } + public void startShortPoll(TLRPC.Chat chat, int guid, boolean stop, Consumer needPollConsumer) { if (chat == null) { return; } @@ -12136,9 +12477,17 @@ public void startShortPoll(TLRPC.Chat chat, int guid, boolean stop) { if (!guids.contains(guid)) { guids.add(guid); } + boolean needGetDifference = false; if (shortPollChannels.indexOfKey(chat.id) < 0) { + needGetDifference = true; getChannelDifference(chat.id, 3, 0, null); } + boolean finalNeedGetDifference = needGetDifference; + if (needPollConsumer != null) { + AndroidUtilities.runOnUIThread(() -> { + needPollConsumer.accept(finalNeedGetDifference); + }); + } if (chat.megagroup) { if (onlineGuids == null) { onlineGuids = new ArrayList<>(); @@ -12183,6 +12532,9 @@ protected void getChannelDifference(long channelId, int newDialogType, long task if (newDialogType == 1) { channelPts = channelsPts.get(channelId); if (channelPts != 0) { + AndroidUtilities.runOnUIThread(() -> { + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.onReceivedChannelDifference, channelId); + }); return; } channelPts = 1; @@ -12195,10 +12547,16 @@ protected void getChannelDifference(long channelId, int newDialogType, long task channelsPts.put(channelId, channelPts); } if (channelPts == 0 && (newDialogType == 2 || newDialogType == 3)) { + AndroidUtilities.runOnUIThread(() -> { + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.onReceivedChannelDifference, channelId); + }); return; } } if (channelPts == 0) { + AndroidUtilities.runOnUIThread(() -> { + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.onReceivedChannelDifference, channelId); + }); return; } } @@ -12217,6 +12575,9 @@ protected void getChannelDifference(long channelId, int newDialogType, long task if (taskId != 0) { getMessagesStorage().removePendingTask(taskId); } + AndroidUtilities.runOnUIThread(() -> { + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.onReceivedChannelDifference, channelId); + }); return; } long newTaskId; @@ -12306,6 +12667,7 @@ protected void getChannelDifference(long channelId, int newDialogType, long task } Utilities.stageQueue.postRunnable(() -> { + boolean notifyAbountDifference = true; if (res instanceof TLRPC.TL_updates_channelDifference || res instanceof TLRPC.TL_updates_channelDifferenceEmpty) { if (!res.new_messages.isEmpty()) { LongSparseArray> messages = new LongSparseArray<>(); @@ -12388,7 +12750,10 @@ protected void getChannelDifference(long channelId, int newDialogType, long task message.dialog_id = -channelId; message.unread = !(message.action instanceof TLRPC.TL_messageActionChannelCreate || channelFinal != null && channelFinal.left || (message.out ? outboxValue : inboxValue) >= message.id); } - getMessagesStorage().overwriteChannel(channelId, (TLRPC.TL_updates_channelDifferenceTooLong) res, newDialogType); + getMessagesStorage().overwriteChannel(channelId, (TLRPC.TL_updates_channelDifferenceTooLong) res, newDialogType, () -> AndroidUtilities.runOnUIThread(() -> { + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.onReceivedChannelDifference, channelId); + })); + notifyAbountDifference = false; } gettingDifferenceChannels.delete(channelId); channelsPts.put(channelId, res.pts); @@ -12407,10 +12772,18 @@ protected void getChannelDifference(long channelId, int newDialogType, long task if (newTaskId != 0) { getMessagesStorage().removePendingTask(newTaskId); } + if (notifyAbountDifference) { + AndroidUtilities.runOnUIThread(() -> { + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.onReceivedChannelDifference, channelId); + }); + } }); }); } else if (error != null) { - AndroidUtilities.runOnUIThread(() -> checkChannelError(error.text, channelId)); + AndroidUtilities.runOnUIThread(() -> { + checkChannelError(error.text, channelId); + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.onReceivedChannelDifference, channelId); + }); gettingDifferenceChannels.delete(channelId); if (newTaskId != 0) { getMessagesStorage().removePendingTask(newTaskId); @@ -12475,6 +12848,7 @@ public void getDifference(int pts, int date, int qts, boolean slice) { loadedFullUsers.clear(); loadedFullChats.clear(); resetDialogs(true, getMessagesStorage().getLastSeqValue(), res.pts, date, qts); + getStoriesController().cleanup(); }); } else { if (res instanceof TLRPC.TL_updates_differenceSlice) { @@ -12962,7 +13336,7 @@ public void loadPinnedDialogs(final int folderId, long newDialogId, ArrayList folder dialog.pinned = false; dialog.pinnedNum = 0; dialog.folder_id = folderPeer.folder_id; - ensureFolderDialogExists(folderPeer.folder_id, null); + hasArchivedChats = true; + checkArchiveFolder(); } } updated = true; @@ -14427,6 +14802,8 @@ public boolean processUpdateArray(ArrayList updates, ArrayList(); } chatInfoToUpdate.add(update.participants); + } if (baseUpdate instanceof TLRPC.TL_updateStory) { + getStoriesController().processUpdate((TLRPC.TL_updateStory) baseUpdate); } else if (baseUpdate instanceof TLRPC.TL_updateUserStatus) { interfaceUpdateMask |= UPDATE_MASK_STATUS; if (updatesOnMainThread == null) { @@ -14467,6 +14844,9 @@ public boolean processUpdateArray(ArrayList updates, ArrayList(); } updatesOnMainThread.add(baseUpdate); + } else if (baseUpdate instanceof TLRPC.TL_updateReadStories) { + TLRPC.TL_updateReadStories updateReadStories = (TLRPC.TL_updateReadStories) baseUpdate; + getStoriesController().markStoriesAsReadFromServer(updateReadStories.user_id, updateReadStories.max_id); } else if (baseUpdate instanceof TLRPC.TL_updatePeerSettings) { TLRPC.TL_updatePeerSettings update = (TLRPC.TL_updatePeerSettings) baseUpdate; if (contactsIds == null) { @@ -15285,6 +15665,8 @@ public boolean processUpdateArray(ArrayList updates, ArrayList updates, ArrayList updates, ArrayList dialogs = filter.dialogs; + ArrayList dialogsForward = filter.dialogsForward; + dialogs.clear(); + dialogsForward.clear(); + sortingDialogFilter = filter; + try { + Collections.sort(allDialogs, dialogDateComparator); + } catch (Exception e) { + FileLog.e(e); + } + for (int a = 0, N = allDialogs.size(); a < N; a++) { + TLRPC.Dialog d = allDialogs.get(a); + if (d instanceof TLRPC.TL_dialog) { + long dialogId = d.id; + if (DialogObject.isEncryptedDialog(dialogId)) { + TLRPC.EncryptedChat encryptedChat = getEncryptedChat(DialogObject.getEncryptedChatId(dialogId)); + if (encryptedChat != null) { + dialogId = encryptedChat.user_id; + } + } + if (filter.includesDialog(getAccountInstance(), dialogId, d)) { + if (canAddToForward(d)) { + dialogsForward.add(d); + } + dialogs.add(d); + } + } + } + try { + Collections.sort(allDialogs, dialogComparator); + } catch (Exception e) {} + } + + public boolean canAddToForward(TLRPC.Dialog d) { + if (d == null) { + return false; + } + if (DialogObject.isEncryptedDialog(d.id)) { + return true; + } + boolean canAddToForward = true; + if (DialogObject.isChannel(d)) { + TLRPC.Chat chat = getChat(-d.id); + if (chat != null && chat.megagroup) { + canAddToForward = !chat.gigagroup || ChatObject.hasAdminRights(chat); + } else { + canAddToForward = ChatObject.hasAdminRights(chat) && ChatObject.canPost(chat); + } + } + return canAddToForward; +>>>>>>> off/master + } + public void sortDialogs(LongSparseArray chatsDict) { if (chatsDict == null && ApplicationLoader.mainInterfacePaused) { return; @@ -17075,11 +17546,6 @@ public void sortDialogs(LongSparseArray chatsDict) { dialogsMyChannels.clear(); dialogsChannelsOnly.clear(); dialogsGroupsOnly.clear(); - for (int a = 0; a < selectedDialogFilter.length; a++) { - if (selectedDialogFilter[a] != null) { - selectedDialogFilter[a].dialogs.clear(); - } - } dialogsUsersOnly.clear(); dialogsForBlock.clear(); dialogsForward.clear(); @@ -17098,13 +17564,15 @@ public void sortDialogs(LongSparseArray chatsDict) { if (sortingDialogFilter == null) { continue; } + ArrayList dialogs = sortingDialogFilter.dialogs; + ArrayList dialogsForward = sortingDialogFilter.dialogsForward; + dialogs.clear(); + dialogsForward.clear(); try { Collections.sort(allDialogs, dialogDateComparator); } catch (Exception e) { FileLog.e(e); } - ArrayList dialogsByFilter = sortingDialogFilter.dialogs; - for (int a = 0, N = allDialogs.size(); a < N; a++) { TLRPC.Dialog d = allDialogs.get(a); if (d instanceof TLRPC.TL_dialog) { @@ -17116,7 +17584,10 @@ public void sortDialogs(LongSparseArray chatsDict) { } } if (sortingDialogFilter.includesDialog(getAccountInstance(), dialogId, d)) { - dialogsByFilter.add(d); + if (canAddToForward(d)) { + dialogsForward.add(d); + } + dialogs.add(d); } } } @@ -17161,7 +17632,6 @@ public void sortDialogs(LongSparseArray chatsDict) { } } } - boolean canAddToForward = true; if (!DialogObject.isEncryptedDialog(d.id)) { dialogsServerOnly.add(d); if (DialogObject.isChannel(d)) { @@ -17179,10 +17649,8 @@ public void sortDialogs(LongSparseArray chatsDict) { } if (chat != null && chat.megagroup) { dialogsGroupsOnly.add(d); - canAddToForward = !chat.gigagroup || ChatObject.hasAdminRights(chat); } else { dialogsChannelsOnly.add(d); - canAddToForward = ChatObject.hasAdminRights(chat) && ChatObject.canPost(chat); } } else if (d.id < 0) { if (chatsDict != null) { @@ -17210,7 +17678,7 @@ public void sortDialogs(LongSparseArray chatsDict) { } } } - if (canAddToForward && d.folder_id == 0) { + if (canAddToForward(d) && d.folder_id == 0) { if (d.id == selfId) { dialogsForward.add(0, d); selfAdded = true; @@ -17252,6 +17720,7 @@ public void sortDialogs(LongSparseArray chatsDict) { dialogsByFolder.remove(folderId); } } + hasArchivedChats = dialogsByFolder.get(1, null) != null; } private void addDialogToItsFolder(int index, TLRPC.Dialog dialog) { @@ -17385,7 +17854,10 @@ public static void openChatOrProfileWith(TLRPC.User user, TLRPC.Chat chat, BaseF reason = getRestrictionReason(user.restriction_reason); if (type != 3 && user.bot) { type = 1; - closeLast = true; + BaseFragment lastFragment = LaunchActivity.getLastFragment(); + if (!(lastFragment.storyViewer != null && lastFragment.storyViewer.isShown())) { + closeLast = true; + } } } if (reason != null) { @@ -17548,9 +18020,9 @@ public void didReceivedNotification(int id, int account, Object... args) { int lastMessageId = (int) args[4]; if ((size < count / 2 && !isEnd) && isCache) { if (finalMessageId != 0) { - loadMessagesInternal(dialogId, 0, false, count, finalMessageId, 0, false, 0, classGuid, 3, lastMessageId, 0, 0, -1, 0, 0, 0, false, 0, true, false, false); + loadMessagesInternal(dialogId, 0, false, count, finalMessageId, 0, false, 0, classGuid, 3, lastMessageId, 0, 0, -1, 0, 0, 0, false, 0, true, false, false, null); } else { - loadMessagesInternal(dialogId, 0, false, count, finalMessageId, 0, false, 0, classGuid, 2, lastMessageId, 0, 0, -1, 0, 0, 0, false, 0, true, false, false); + loadMessagesInternal(dialogId, 0, false, count, finalMessageId, 0, false, 0, classGuid, 2, lastMessageId, 0, 0, -1, 0, 0, 0, false, 0, true, false, false, null); } } else { getNotificationCenter().removeObserver(this, NotificationCenter.messagesDidLoadWithoutProcess); @@ -17574,9 +18046,9 @@ public void didReceivedNotification(int id, int account, Object... args) { getNotificationCenter().addObserver(delegate, NotificationCenter.loadingMessagesFailed); if (messageId != 0) { - loadMessagesInternal(dialogId, 0, true, count, finalMessageId, 0, true, 0, classGuid, 3, 0, 0, 0, -1, 0, 0, 0, false, 0, true, false, false); + loadMessagesInternal(dialogId, 0, true, count, finalMessageId, 0, true, 0, classGuid, 3, 0, 0, 0, -1, 0, 0, 0, false, 0, true, false, false, null); } else { - loadMessagesInternal(dialogId, 0, true, count, finalMessageId, 0, true, 0, classGuid, 2, 0, 0, 0, -1, 0, 0, 0, false, 0, true, false, false); + loadMessagesInternal(dialogId, 0, true, count, finalMessageId, 0, true, 0, classGuid, 2, 0, 0, 0, -1, 0, 0, 0, false, 0, true, false, false, null); } } @@ -17916,4 +18388,158 @@ public void requestContactToken(long minDuration, Utilities.Callback 1000L * chatlistUpdatePeriod || invalidate)) { + if (stat == null) { + stat = new ChatlistUpdatesStat(); + chatlistFoldersUpdates.put(filterId, stat); + } + final ChatlistUpdatesStat finalStat = stat; + finalStat.loading = false; + TLRPC.TL_chatlists_getChatlistUpdates req = new TLRPC.TL_chatlists_getChatlistUpdates(); + req.chatlist = new TLRPC.TL_inputChatlistDialogFilter(); + req.chatlist.filter_id = filterId; + getConnectionsManager().sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + if (res instanceof TLRPC.TL_chatlists_chatlistUpdates) { + TLRPC.TL_chatlists_chatlistUpdates updates = (TLRPC.TL_chatlists_chatlistUpdates) res; + putChats(updates.chats, false); + putUsers(updates.users, false); + chatlistFoldersUpdates.put(filterId, new ChatlistUpdatesStat(updates)); + getNotificationCenter().postNotificationName(NotificationCenter.chatlistFolderUpdate, filterId); + } else { + finalStat.loading = false; + } + })); + } + } + + public TLRPC.TL_chatlists_chatlistUpdates getChatlistFolderUpdates(int filterId) { + ChatlistUpdatesStat stat = chatlistFoldersUpdates.get(filterId); + if (stat == null) { + return null; + } + return stat.lastValue; + } + + public Pair removeFolderTemporarily(final int filterId, final ArrayList chats) { + + frozenDialogFilters = new ArrayList<>(dialogFilters); + for (int i = 0; i < frozenDialogFilters.size(); ++i) { + DialogFilter f = frozenDialogFilters.get(i); + if (f.id == filterId) { + frozenDialogFilters.remove(i); + i--; + } + } + + hiddenUndoChats.clear(); + if (chats != null) { + hiddenUndoChats.addAll(chats); + } + final boolean hidChats = !hiddenUndoChats.isEmpty(); + + getNotificationCenter().postNotificationName(NotificationCenter.dialogFiltersUpdated); + if (hidChats) { + getNotificationCenter().postNotificationName(NotificationCenter.dialogsNeedReload); + } + + Runnable apply = () -> { + for (int i = 0; i < dialogFilters.size(); ++i) { + DialogFilter f = dialogFilters.get(i); + if (f.id == filterId) { + dialogFilters.remove(i); + i--; + } + } + frozenDialogFilters = null; + hiddenUndoChats.clear(); + getNotificationCenter().postNotificationName(NotificationCenter.dialogFiltersUpdated); + if (hidChats) { + getNotificationCenter().postNotificationName(NotificationCenter.dialogsNeedReload); + } + }; + Runnable undo = () -> { + frozenDialogFilters = null; + hiddenUndoChats.clear(); + getNotificationCenter().postNotificationName(NotificationCenter.dialogFiltersUpdated); + if (hidChats) { + getNotificationCenter().postNotificationName(NotificationCenter.dialogsNeedReload); + } + }; + + return new Pair<>(apply, undo); + } + + public boolean isHiddenByUndo(long did) { + return !hiddenUndoChats.isEmpty() && hiddenUndoChats.contains(did); + } + + public void cancelUploadWallpaper() { + if (uploadingWallpaperInfo != null) { + if (uploadingWallpaperInfo.requestIds != null) { + for (int i = 0; i < uploadingWallpaperInfo.requestIds.size(); i++) { + ConnectionsManager.getInstance(currentAccount).cancelRequest(uploadingWallpaperInfo.requestIds.get(i), true); + } + } + FileLoader.getInstance(currentAccount).cancelFileUpload(uploadingWallpaper, false); + if (uploadingWallpaperInfo.dialogId != 0) { + TLRPC.UserFull userFull = getUserFull(uploadingWallpaperInfo.dialogId); + if (userFull != null) { + userFull.wallpaper = uploadingWallpaperInfo.prevUserWallpaper; + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.userInfoDidLoad, uploadingWallpaperInfo.dialogId, userFull); + } + } + uploadingWallpaperInfo = null; + uploadingWallpaper = null; + } + } + + public StoriesController getStoriesController() { + if (storiesController != null) { + return storiesController; + } + synchronized (lockObjects[currentAccount]) { + if (storiesController != null) { + return storiesController; + } + storiesController = new StoriesController(currentAccount); + } + return storiesController; + } + + public boolean storiesEnabled() { + switch (storiesPosting) { + case "premium": + return getUserConfig().isPremium(); + case "enabled": + return true; + default: + case "disabled": + return false; + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java index 78a589c3b3..20709dd65e 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java @@ -96,7 +96,7 @@ public class MessagesStorage extends BaseController { private static SparseArray Instance = new SparseArray(); private static final Object lockObject = new Object(); - public final static int LAST_DB_VERSION = 116; + public final static int LAST_DB_VERSION = 127; private boolean databaseMigrationInProgress; public boolean showClearDatabaseAlert; private LongSparseIntArray dialogIsForum = new LongSparseIntArray(); @@ -513,7 +513,7 @@ public static void createTables(SQLiteDatabase database) throws SQLiteException database.executeFast("CREATE INDEX IF NOT EXISTS reply_to_idx_scheduled_messages_v2 ON scheduled_messages_v2(mid, reply_to_message_id);").stepThis().dispose(); database.executeFast("CREATE INDEX IF NOT EXISTS idx_to_reply_scheduled_messages_v2 ON scheduled_messages_v2(reply_to_message_id, mid);").stepThis().dispose(); - database.executeFast("CREATE TABLE messages_v2(mid INTEGER, uid INTEGER, read_state INTEGER, send_state INTEGER, date INTEGER, data BLOB, out INTEGER, ttl INTEGER, media INTEGER, replydata BLOB, imp INTEGER, mention INTEGER, forwards INTEGER, replies_data BLOB, thread_reply_id INTEGER, is_channel INTEGER, reply_to_message_id INTEGER, custom_params BLOB, group_id INTEGER, PRIMARY KEY(mid, uid))").stepThis().dispose(); + database.executeFast("CREATE TABLE messages_v2(mid INTEGER, uid INTEGER, read_state INTEGER, send_state INTEGER, date INTEGER, data BLOB, out INTEGER, ttl INTEGER, media INTEGER, replydata BLOB, imp INTEGER, mention INTEGER, forwards INTEGER, replies_data BLOB, thread_reply_id INTEGER, is_channel INTEGER, reply_to_message_id INTEGER, custom_params BLOB, group_id INTEGER, reply_to_story_id INTEGER, PRIMARY KEY(mid, uid))").stepThis().dispose(); database.executeFast("CREATE INDEX IF NOT EXISTS uid_mid_read_out_idx_messages_v2 ON messages_v2(uid, mid, read_state, out);").stepThis().dispose(); database.executeFast("CREATE INDEX IF NOT EXISTS uid_date_mid_idx_messages_v2 ON messages_v2(uid, date, mid);").stepThis().dispose(); database.executeFast("CREATE INDEX IF NOT EXISTS mid_out_idx_messages_v2 ON messages_v2(mid, out);").stepThis().dispose(); @@ -566,7 +566,7 @@ public static void createTables(SQLiteDatabase database) throws SQLiteException database.executeFast("CREATE TABLE bot_keyboard_topics(uid INTEGER, tid INTEGER, mid INTEGER, info BLOB, PRIMARY KEY(uid, tid))").stepThis().dispose(); database.executeFast("CREATE INDEX IF NOT EXISTS bot_keyboard_topics_idx_mid_v2 ON bot_keyboard_topics(mid, uid, tid);").stepThis().dispose(); - database.executeFast("CREATE TABLE chat_settings_v2(uid INTEGER PRIMARY KEY, info BLOB, pinned INTEGER, online INTEGER, inviter INTEGER, links INTEGER)").stepThis().dispose(); + database.executeFast("CREATE TABLE chat_settings_v2(uid INTEGER PRIMARY KEY, info BLOB, pinned INTEGER, online INTEGER, inviter INTEGER, links INTEGER, participants_count INTEGER)").stepThis().dispose(); database.executeFast("CREATE INDEX IF NOT EXISTS chat_settings_pinned_idx ON chat_settings_v2(uid, pinned) WHERE pinned != 0;").stepThis().dispose(); database.executeFast("CREATE TABLE user_settings(uid INTEGER PRIMARY KEY, info BLOB, pinned INTEGER)").stepThis().dispose(); @@ -638,7 +638,7 @@ public static void createTables(SQLiteDatabase database) throws SQLiteException database.executeFast("CREATE TABLE messages_holes_topics(uid INTEGER, topic_id INTEGER, start INTEGER, end INTEGER, PRIMARY KEY(uid, topic_id, start));").stepThis().dispose(); database.executeFast("CREATE INDEX IF NOT EXISTS uid_end_messages_holes ON messages_holes_topics(uid, topic_id, end);").stepThis().dispose(); - database.executeFast("CREATE TABLE messages_topics(mid INTEGER, uid INTEGER, topic_id INTEGER, read_state INTEGER, send_state INTEGER, date INTEGER, data BLOB, out INTEGER, ttl INTEGER, media INTEGER, replydata BLOB, imp INTEGER, mention INTEGER, forwards INTEGER, replies_data BLOB, thread_reply_id INTEGER, is_channel INTEGER, reply_to_message_id INTEGER, custom_params BLOB, PRIMARY KEY(mid, topic_id, uid))").stepThis().dispose(); + database.executeFast("CREATE TABLE messages_topics(mid INTEGER, uid INTEGER, topic_id INTEGER, read_state INTEGER, send_state INTEGER, date INTEGER, data BLOB, out INTEGER, ttl INTEGER, media INTEGER, replydata BLOB, imp INTEGER, mention INTEGER, forwards INTEGER, replies_data BLOB, thread_reply_id INTEGER, is_channel INTEGER, reply_to_message_id INTEGER, custom_params BLOB, reply_to_story_id INTEGER, PRIMARY KEY(mid, topic_id, uid))").stepThis().dispose(); database.executeFast("CREATE INDEX IF NOT EXISTS uid_date_mid_idx_messages_topics ON messages_topics(uid, date, mid);").stepThis().dispose(); database.executeFast("CREATE INDEX IF NOT EXISTS mid_out_idx_messages_topics ON messages_topics(mid, out);").stepThis().dispose(); database.executeFast("CREATE INDEX IF NOT EXISTS task_idx_messages_topics ON messages_topics(uid, out, read_state, ttl, date, send_state);").stepThis().dispose(); @@ -672,6 +672,16 @@ public static void createTables(SQLiteDatabase database) throws SQLiteException database.executeFast("CREATE TABLE emoji_groups(type INTEGER PRIMARY KEY, data BLOB)").stepThis().dispose(); database.executeFast("CREATE TABLE app_config(data BLOB)").stepThis().dispose(); + database.executeFast("CREATE TABLE stories (dialog_id INTEGER, story_id INTEGER, data BLOB, local_path TEXT, local_thumb_path TEXT, PRIMARY KEY (dialog_id, story_id));").stepThis().dispose(); + database.executeFast("CREATE TABLE stories_counter (dialog_id INTEGER PRIMARY KEY, count INTEGER, max_read INTEGER);").stepThis().dispose(); + + database.executeFast("CREATE TABLE profile_stories (dialog_id INTEGER, story_id INTEGER, data BLOB, PRIMARY KEY(dialog_id, story_id));").stepThis().dispose(); + database.executeFast("CREATE TABLE archived_stories (story_id INTEGER PRIMARY KEY, data BLOB);").stepThis().dispose(); + + database.executeFast("CREATE TABLE story_drafts (id INTEGER PRIMARY KEY, date INTEGER, data BLOB);").stepThis().dispose(); + + database.executeFast("CREATE TABLE story_pushes (uid INTEGER, sid INTEGER, date INTEGER, localName TEXT, flags INTEGER, expire_date INTEGER, PRIMARY KEY(uid, sid));").stepThis().dispose(); + database.executeFast("PRAGMA user_version = " + MessagesStorage.LAST_DB_VERSION).stepThis().dispose(); } @@ -1190,6 +1200,54 @@ public void setDialogFlags(long did, long flags) { }); } + public void putStoryPushMessage(NotificationsController.StoryNotification push) { + storageQueue.postRunnable(() -> { + try { + database.executeFast("DELETE FROM story_pushes WHERE uid = " + push.dialogId).stepThis().dispose(); + SQLitePreparedStatement state = database.executeFast("REPLACE INTO story_pushes VALUES(?, ?, ?, ?, ?, ?)"); + for (Map.Entry> e : push.dateByIds.entrySet()) { + int id = e.getKey(); + long date = e.getValue().first; + long expire_date = e.getValue().second; + state.requery(); + state.bindLong(1, push.dialogId); + state.bindInteger(2, id); + state.bindLong(3, date); + if (push.localName == null) { + push.localName = ""; + } + state.bindString(4, push.localName); + state.bindInteger(5, push.hidden ? 1 : 0); + state.bindLong(6, expire_date); + state.step(); + } + state.dispose(); + } catch (Exception e) { + checkSQLException(e); + } + }); + } + + public void deleteStoryPushMessage(long dialogId) { + storageQueue.postRunnable(() -> { + try { + database.executeFast("DELETE FROM story_pushes WHERE uid = " + dialogId).stepThis().dispose(); + } catch (Exception e) { + checkSQLException(e); + } + }); + } + + public void deleteAllStoryPushMessages() { + storageQueue.postRunnable(() -> { + try { + database.executeFast("DELETE FROM story_pushes").stepThis().dispose(); + } catch (Exception e) { + checkSQLException(e); + } + }); + } + public void putPushMessage(MessageObject message) { storageQueue.postRunnable(() -> { try { @@ -1263,6 +1321,9 @@ public void clearLocalDatabase() { database.executeFast("DELETE FROM media_counts_topics").stepThis().dispose(); database.executeFast("DELETE FROM chat_pinned_v2").stepThis().dispose(); database.executeFast("DELETE FROM chat_pinned_count").stepThis().dispose(); + database.executeFast("DELETE FROM profile_stories").stepThis().dispose(); + database.executeFast("DELETE FROM archived_stories").stepThis().dispose(); + database.executeFast("DELETE FROM story_pushes").stepThis().dispose(); cursor = database.queryFinalized("SELECT did FROM dialogs WHERE 1"); while (cursor.next()) { @@ -1819,7 +1880,7 @@ public void removeTopics(long dialogId, ArrayList topicIds) { try { String topics = TextUtils.join(", ", topicIds); database.executeFast(String.format(Locale.US, "DELETE FROM topics WHERE did = %d AND topic_id IN (%s)", dialogId, topics)).stepThis().dispose(); - database.executeFast(String.format(Locale.US, "DELETE FROM messages_topics WHERE uid = %d AND topic_id = IN (%s)", dialogId, topics)).stepThis().dispose(); + database.executeFast(String.format(Locale.US, "DELETE FROM messages_topics WHERE uid = %d AND topic_id IN (%s)", dialogId, topics)).stepThis().dispose(); } catch (SQLiteException e) { e.printStackTrace(); } @@ -1868,12 +1929,27 @@ public void reset() { getUserConfig().clearFilters(); getUserConfig().clearPinnedDialogsLoaded(); - NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.didClearDatabase); + getNotificationCenter().postNotificationName(NotificationCenter.didClearDatabase); getMediaDataController().loadAttachMenuBots(false, true); getNotificationCenter().postNotificationName(NotificationCenter.onDatabaseReset); + + getMessagesController().getStoriesController().cleanup(); }); } + public void fullReset() { + storageQueue.postRunnable(() -> { + cleanupInternal(true); + clearLoadingDialogsOffsets(); + openDatabase(1); + AndroidUtilities.runOnUIThread(() -> { + getNotificationCenter().postNotificationName(NotificationCenter.onDatabaseReset); + getNotificationCenter().postNotificationName(NotificationCenter.didClearDatabase); + }); + }); + + } + private static class ReadDialog { public int lastMid; public int date; @@ -2314,7 +2390,7 @@ private void loadDialogFilters() { getChatsInternal(TextUtils.join(",", chatsToLoad), chats); } - getMessagesController().processLoadedDialogFilters(new ArrayList<>(dialogFilters), dialogs, null, users, chats, encryptedChats, 0); + getMessagesController().processLoadedDialogFilters(new ArrayList<>(dialogFilters), dialogs, null, users, chats, encryptedChats, 0, null); } catch (Exception e) { checkSQLException(e); } finally { @@ -2739,7 +2815,11 @@ private void saveDialogFilterInternal(MessagesController.DialogFilter filter, bo try { if (!dialogFilters.contains(filter)) { if (atBegin) { - dialogFilters.add(0, filter); + if (dialogFilters.get(0).isDefault()) { + dialogFilters.add(1, filter); + } else { + dialogFilters.add(0, filter); + } } else { dialogFilters.add(filter); } @@ -2832,294 +2912,7 @@ private ArrayList toPeerIds(ArrayList inputPeers) { return array; } -// public void checkLoadedRemoteFilter(TLRPC.DialogFilter newFilter) { -// storageQueue.postRunnable(() -> { -// try { -// SparseArray filtersToDelete = new SparseArray<>(); -// for (int a = 0, N = dialogFilters.size(); a < N; a++) { -// MessagesController.DialogFilter filter = dialogFilters.get(a); -// filtersToDelete.put(filter.id, filter); -// } -// ArrayList filtersOrder = new ArrayList<>(); -// -// ArrayList usersToLoad = new ArrayList<>(); -// HashMap usersToLoadMap = new HashMap<>(); -// ArrayList chatsToLoad = new ArrayList<>(); -// HashMap chatsToLoadMap = new HashMap<>(); -// ArrayList dialogsToLoad = new ArrayList<>(); -// HashMap dialogsToLoadMap = new HashMap<>(); -// -// ArrayList filtersToSave = new ArrayList<>(); -// HashMap> filterDialogRemovals = new HashMap<>(); -// HashSet filtersUnreadCounterReset = new HashSet<>(); -// for (int a = 0, N = vector.objects.size(); a < N; a++) { -// TLRPC.DialogFilter newFilter = (TLRPC.DialogFilter) vector.objects.get(a); -// filtersOrder.add(newFilter.id); -// int newFlags = 0; -// if (newFilter.contacts) { -// newFlags |= MessagesController.DIALOG_FILTER_FLAG_CONTACTS; -// } -// if (newFilter.non_contacts) { -// newFlags |= MessagesController.DIALOG_FILTER_FLAG_NON_CONTACTS; -// } -// if (newFilter.groups) { -// newFlags |= MessagesController.DIALOG_FILTER_FLAG_GROUPS; -// } -// if (newFilter.broadcasts) { -// newFlags |= MessagesController.DIALOG_FILTER_FLAG_CHANNELS; -// } -// if (newFilter.bots) { -// newFlags |= MessagesController.DIALOG_FILTER_FLAG_BOTS; -// } -// if (newFilter.exclude_muted) { -// newFlags |= MessagesController.DIALOG_FILTER_FLAG_EXCLUDE_MUTED; -// } -// if (newFilter.exclude_read) { -// newFlags |= MessagesController.DIALOG_FILTER_FLAG_EXCLUDE_READ; -// } -// if (newFilter.exclude_archived) { -// newFlags |= MessagesController.DIALOG_FILTER_FLAG_EXCLUDE_ARCHIVED; -// } -// if (newFilter instanceof TLRPC.TL_dialogFilterCommunity) { -// newFlags |= MessagesController.DIALOG_FILTER_FLAG_COMMUNITY; -// if (newFilter.community_can_admin) { -// newFlags |= MessagesController.DIALOG_FILTER_FLAG_COMMUNITY_ADMIN; -// } -// } -// -// MessagesController.DialogFilter filter = dialogFiltersMap.get(newFilter.id); -// if (filter != null) { -// filtersToDelete.remove(newFilter.id); -// boolean changed = false; -// boolean unreadChanged = false; -// if (!TextUtils.equals(filter.name, newFilter.title)) { -// changed = true; -// filter.name = newFilter.title; -// } -// if (filter.flags != newFlags) { -// filter.flags = newFlags; -// changed = true; -// unreadChanged = true; -// } -// -// HashSet existingIds = new HashSet<>(filter.alwaysShow); -// existingIds.addAll(filter.neverShow); -// HashSet existingDialogsIds = new HashSet<>(); -// -// LinkedHashMap secretChatsMap = null; -// if (filter.pinnedDialogs.size() != 0) { -// ArrayList pinArray = new ArrayList<>(); -// boolean hasSecret = false; -// for (int c = 0, N2 = filter.pinnedDialogs.size(); c < N2; c++) { -// long did = filter.pinnedDialogs.keyAt(c); -// if (DialogObject.isEncryptedDialog(did)) { -// hasSecret = true; -// } -// pinArray.add(did); -// } -// if (hasSecret) { -// secretChatsMap = new LinkedHashMap<>(); -// LongSparseIntArray pinnedDialogs = filter.pinnedDialogs; -// Collections.sort(pinArray, (o1, o2) -> { -// int idx1 = pinnedDialogs.get(o1); -// int idx2 = pinnedDialogs.get(o2); -// if (idx1 > idx2) { -// return 1; -// } else if (idx1 < idx2) { -// return -1; -// } -// return 0; -// }); -// for (int c = 0, N2 = pinArray.size(); c < N2; c++) { -// long did = pinArray.get(c); -// if (!DialogObject.isEncryptedDialog(did)) { -// continue; -// } -// secretChatsMap.put(c, did); -// } -// } -// } -// for (int c = 0, N2 = filter.pinnedDialogs.size(); c < N2; c++) { -// long did = filter.pinnedDialogs.keyAt(c); -// if (DialogObject.isEncryptedDialog(did)) { -// continue; -// } -// existingDialogsIds.add(did); -// existingIds.remove(did); -// } -// -// filter.pinnedDialogs.clear(); -// for (int b = 0, N2 = newFilter.pinned_peers.size(); b < N2; b++) { -// TLRPC.InputPeer peer = newFilter.pinned_peers.get(b); -// Long id; -// if (peer.user_id != 0) { -// id = peer.user_id; -// } else { -// id = -(peer.chat_id != 0 ? peer.chat_id : peer.channel_id); -// } -// int index = filter.pinnedDialogs.size(); -// if (secretChatsMap != null) { -// Long did; -// while ((did = secretChatsMap.remove(index)) != null) { -// filter.pinnedDialogs.put(did, index); -// index++; -// } -// } -// filter.pinnedDialogs.put(id, index); -// existingIds.remove(id); -// if (!existingDialogsIds.remove(id)) { -// changed = true; -// if (!dialogsToLoadMap.containsKey(id)) { -// dialogsToLoad.add(id); -// dialogsToLoadMap.put(id, peer); -// } -// } -// } -// if (secretChatsMap != null) { -// for (LinkedHashMap.Entry entry : secretChatsMap.entrySet()) { -// filter.pinnedDialogs.put(entry.getValue(), filter.pinnedDialogs.size()); -// } -// } -// -// for (int c = 0; c < 2; c++) { -// ArrayList fromArray = toPeerIds(c == 0 ? newFilter.include_peers : newFilter.exclude_peers); -// ArrayList toArray = c == 0 ? filter.alwaysShow : filter.neverShow; -// -// if (c == 0) { -// // put pinned_peers into include_peers (alwaysShow) -// ArrayList pinnedArray = toPeerIds(newFilter.pinned_peers); -// for (int i = 0; i < pinnedArray.size(); ++i) { -// fromArray.remove(pinnedArray.get(i)); -// } -// fromArray.addAll(0, pinnedArray); -// } -// -// final int fromArrayCount = fromArray.size(); -// boolean isDifferent = fromArray.size() != toArray.size(); -// if (!isDifferent) { -// for (int i = 0; i < fromArrayCount; ++i) { -// if (!toArray.contains(fromArray.get(i))) { -// isDifferent = true; -// break; -// } -// } -// } -// -// if (isDifferent) { -// unreadChanged = true; -// changed = true; -// if (c == 0) { -// filter.alwaysShow = fromArray; -// } else { -// filter.neverShow = fromArray; -// } -// } -// } -// if (!existingDialogsIds.isEmpty()) { -// filterDialogRemovals.put(filter.id, existingDialogsIds); -// changed = true; -// } -// if (changed) { -// filtersToSave.add(filter); -// } -// if (unreadChanged) { -// filtersUnreadCounterReset.add(filter.id); -// } -// } else { -// filter = new MessagesController.DialogFilter(); -// filter.id = newFilter.id; -// filter.flags = newFlags; -// filter.name = newFilter.title; -// filter.pendingUnreadCount = -1; -// for (int c = 0; c < 2; c++) { -// if (c == 0) { -// for (int b = 0, N2 = newFilter.pinned_peers.size(); b < N2; b++) { -// TLRPC.InputPeer peer = newFilter.pinned_peers.get(b); -// Long id; -// if (peer.user_id != 0) { -// id = peer.user_id; -// } else { -// id = -(peer.chat_id != 0 ? peer.chat_id : peer.channel_id); -// } -// if (!filter.alwaysShow.contains(id)) { -// filter.alwaysShow.add(id); -// } -// filter.pinnedDialogs.put(id, filter.pinnedDialogs.size() + 1); -// if (!dialogsToLoadMap.containsKey(id)) { -// dialogsToLoad.add(id); -// dialogsToLoadMap.put(id, peer); -// } -// } -// } -// ArrayList fromArray = c == 0 ? newFilter.include_peers : newFilter.exclude_peers; -// ArrayList toArray = c == 0 ? filter.alwaysShow : filter.neverShow; -// for (int b = 0, N2 = fromArray.size(); b < N2; b++) { -// TLRPC.InputPeer peer = fromArray.get(b); -// if (peer.user_id != 0) { -// Long uid = peer.user_id; -// if (!toArray.contains(uid)) { -// toArray.add(uid); -// } -// if (!usersToLoadMap.containsKey(uid)) { -// usersToLoad.add(uid); -// usersToLoadMap.put(uid, peer); -// } -// } else { -// Long chatId = peer.chat_id != 0 ? peer.chat_id : peer.channel_id; -// Long dialogId = -chatId; -// if (!toArray.contains(dialogId)) { -// toArray.add(dialogId); -// } -// if (!chatsToLoadMap.containsKey(chatId)) { -// chatsToLoad.add(chatId); -// chatsToLoadMap.put(chatId, peer); -// } -// } -// } -// } -// filtersToSave.add(filter); -// } -// } -// -// TLRPC.messages_Dialogs dialogs; -// if (!dialogsToLoad.isEmpty()) { -// dialogs = loadDialogsByIds(TextUtils.join(",", dialogsToLoad), usersToLoad, chatsToLoad, new ArrayList<>()); -// for (int a = 0, N = dialogs.dialogs.size(); a < N; a++) { -// TLRPC.Dialog dialog = dialogs.dialogs.get(a); -// dialogsToLoadMap.remove(dialog.id); -// } -// } else { -// dialogs = new TLRPC.TL_messages_dialogs(); -// } -// ArrayList users = new ArrayList<>(); -// if (!usersToLoad.isEmpty()) { -// getUsersInternal(TextUtils.join(",", usersToLoad), users); -// for (int a = 0, N = users.size(); a < N; a++) { -// TLRPC.User user = users.get(a); -// usersToLoadMap.remove(user.id); -// } -// } -// ArrayList chats = new ArrayList<>(); -// if (!chatsToLoad.isEmpty()) { -// getChatsInternal(TextUtils.join(",", chatsToLoad), chats); -// for (int a = 0, N = chats.size(); a < N; a++) { -// TLRPC.Chat chat = chats.get(a); -// chatsToLoadMap.remove(chat.id); -// } -// } -// -// if (usersToLoadMap.isEmpty() && chatsToLoadMap.isEmpty() && dialogsToLoadMap.isEmpty()) { -// processLoadedFilterPeersInternal(dialogs, null, users, chats, filtersToSave, filtersToDelete, filtersOrder, filterDialogRemovals, filtersUnreadCounterReset); -// } else { -// getMessagesController().loadFilterPeers(dialogsToLoadMap, usersToLoadMap, chatsToLoadMap, dialogs, new TLRPC.TL_messages_dialogs(), users, chats, filtersToSave, filtersToDelete, filtersOrder, filterDialogRemovals, filtersUnreadCounterReset); -// } -// } catch (Exception e) { -// checkSQLException(e); -// } -// }); -// } - - public void checkLoadedRemoteFilters(TLRPC.Vector vector) { + public void checkLoadedRemoteFilters(TLRPC.Vector vector, Runnable onDone) { storageQueue.postRunnable(() -> { try { SparseArray filtersToDelete = new SparseArray<>(); @@ -3167,6 +2960,12 @@ public void checkLoadedRemoteFilters(TLRPC.Vector vector) { if (newFilter.exclude_archived) { newFlags |= MessagesController.DIALOG_FILTER_FLAG_EXCLUDE_ARCHIVED; } + if (newFilter instanceof TLRPC.TL_dialogFilterChatlist) { + newFlags |= MessagesController.DIALOG_FILTER_FLAG_CHATLIST; + if (newFilter.has_my_invites) { + newFlags |= MessagesController.DIALOG_FILTER_FLAG_CHATLIST_ADMIN; + } + } MessagesController.DialogFilter filter = dialogFiltersMap.get(newFilter.id); if (filter != null) { @@ -3395,9 +3194,9 @@ public void checkLoadedRemoteFilters(TLRPC.Vector vector) { } if (usersToLoadMap.isEmpty() && chatsToLoadMap.isEmpty() && dialogsToLoadMap.isEmpty()) { - processLoadedFilterPeersInternal(dialogs, null, users, chats, filtersToSave, filtersToDelete, filtersOrder, filterDialogRemovals, filtersUnreadCounterReset); + processLoadedFilterPeersInternal(dialogs, null, users, chats, filtersToSave, filtersToDelete, filtersOrder, filterDialogRemovals, filtersUnreadCounterReset, onDone); } else { - getMessagesController().loadFilterPeers(dialogsToLoadMap, usersToLoadMap, chatsToLoadMap, dialogs, new TLRPC.TL_messages_dialogs(), users, chats, filtersToSave, filtersToDelete, filtersOrder, filterDialogRemovals, filtersUnreadCounterReset); + getMessagesController().loadFilterPeers(dialogsToLoadMap, usersToLoadMap, chatsToLoadMap, dialogs, new TLRPC.TL_messages_dialogs(), users, chats, filtersToSave, filtersToDelete, filtersOrder, filterDialogRemovals, filtersUnreadCounterReset, onDone); } } catch (Exception e) { checkSQLException(e); @@ -3405,7 +3204,7 @@ public void checkLoadedRemoteFilters(TLRPC.Vector vector) { }); } - private void processLoadedFilterPeersInternal(TLRPC.messages_Dialogs pinnedDialogs, TLRPC.messages_Dialogs pinnedRemoteDialogs, ArrayList users, ArrayList chats, ArrayList filtersToSave, SparseArray filtersToDelete, ArrayList filtersOrder, HashMap> filterDialogRemovals, HashSet filtersUnreadCounterReset) { + private void processLoadedFilterPeersInternal(TLRPC.messages_Dialogs pinnedDialogs, TLRPC.messages_Dialogs pinnedRemoteDialogs, ArrayList users, ArrayList chats, ArrayList filtersToSave, SparseArray filtersToDelete, ArrayList filtersOrder, HashMap> filterDialogRemovals, HashSet filtersUnreadCounterReset, Runnable onDone) { boolean anythingChanged = false; putUsersAndChats(users, chats, true, false); for (int a = 0, N = filtersToDelete.size(); a < N; a++) { @@ -3457,11 +3256,11 @@ private void processLoadedFilterPeersInternal(TLRPC.messages_Dialogs pinnedDialo } int remote = anythingChanged ? 1 : 2; calcUnreadCounters(true); - getMessagesController().processLoadedDialogFilters(new ArrayList<>(dialogFilters), pinnedDialogs, pinnedRemoteDialogs, users, chats, null, remote); + getMessagesController().processLoadedDialogFilters(new ArrayList<>(dialogFilters), pinnedDialogs, pinnedRemoteDialogs, users, chats, null, remote, onDone); } - protected void processLoadedFilterPeers(TLRPC.messages_Dialogs pinnedDialogs, TLRPC.messages_Dialogs pinnedRemoteDialogs, ArrayList users, ArrayList chats, ArrayList filtersToSave, SparseArray filtersToDelete, ArrayList filtersOrder, HashMap> filterDialogRemovals, HashSet filtersUnreadCounterReset) { - storageQueue.postRunnable(() -> processLoadedFilterPeersInternal(pinnedDialogs, pinnedRemoteDialogs, users, chats, filtersToSave, filtersToDelete, filtersOrder, filterDialogRemovals, filtersUnreadCounterReset)); + protected void processLoadedFilterPeers(TLRPC.messages_Dialogs pinnedDialogs, TLRPC.messages_Dialogs pinnedRemoteDialogs, ArrayList users, ArrayList chats, ArrayList filtersToSave, SparseArray filtersToDelete, ArrayList filtersOrder, HashMap> filterDialogRemovals, HashSet filtersUnreadCounterReset, Runnable onDone) { + storageQueue.postRunnable(() -> processLoadedFilterPeersInternal(pinnedDialogs, pinnedRemoteDialogs, users, chats, filtersToSave, filtersToDelete, filtersOrder, filterDialogRemovals, filtersUnreadCounterReset, onDone)); } private void deleteDialogFilterInternal(MessagesController.DialogFilter filter) { @@ -3792,7 +3591,53 @@ public void loadUnreadMessages() { } } Collections.reverse(messages); - AndroidUtilities.runOnUIThread(() -> getNotificationsController().processLoadedUnreadMessages(pushDialogs, messages, pushMessages, users, chats, encryptedChats)); + + usersToLoad.clear(); + chatsToLoad.clear(); + cursor = database.queryFinalized("SELECT uid, sid, date, expire_date, localName, flags FROM story_pushes"); + HashMap storyPushes = new HashMap<>(); + while (cursor.next()) { + long dialogId = cursor.longValue(0); + if (dialogId >= 0) { + if (!usersToLoad.contains(dialogId)) { + usersToLoad.add(dialogId); + } + } else { + if (!chatsToLoad.contains(dialogId)) { + chatsToLoad.add(dialogId); + } + } + int id = cursor.intValue(1); + long date = cursor.longValue(2); + long expire_date = cursor.longValue(3); + String localName = cursor.stringValue(4); + int flags = cursor.intValue(5); + NotificationsController.StoryNotification notification = storyPushes.get(dialogId); + if (notification != null) { + notification.dateByIds.put(id, new Pair<>(date, expire_date)); + notification.date = notification.getLeastDate(); + notification.hidden |= (flags & 1) != 0; + if (!TextUtils.isEmpty(localName)) { + notification.localName = localName; + } + } else { + notification = new NotificationsController.StoryNotification(dialogId, localName, id, date, expire_date); + notification.hidden = (flags & 1) != 0; + storyPushes.put(dialogId, notification); + } + } + cursor.dispose(); + cursor = null; + + if (!usersToLoad.isEmpty()) { + getUsersInternal(TextUtils.join(",", usersToLoad), users); + } + + if (!chatsToLoad.isEmpty()) { + getChatsInternal(TextUtils.join(",", chatsToLoad), chats); + } + + AndroidUtilities.runOnUIThread(() -> getNotificationsController().processLoadedUnreadMessages(pushDialogs, messages, pushMessages, users, chats, encryptedChats, storyPushes.values())); } catch (Exception e) { checkSQLException(e); } finally { @@ -4450,12 +4295,12 @@ private void putDialogPhotosInternal(long did, TLRPC.photos_Photos photos, Array } state.requery(); int size = photo.getObjectSize(); - if (messages != null && messages.get(a) != null) { + if (messages != null && a < messages.size() && messages.get(a) != null) { size += messages.get(a).getObjectSize(); } NativeByteBuffer data = new NativeByteBuffer(size); photo.serializeToStream(data); - if (messages != null && messages.get(a) != null) { + if (messages != null && a < messages.size() && messages.get(a) != null) { messages.get(a).serializeToStream(data); } state.bindLong(1, did); @@ -4555,10 +4400,11 @@ public void emptyMessagesMedia(long dialogId, ArrayList mids) { cursor = null; deleteFromDownloadQueue(idsToDelete, true); if (!messages.isEmpty()) { - state = database.executeFast("REPLACE INTO messages_v2 VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, 0, ?, ?)"); + state = database.executeFast("REPLACE INTO messages_v2 VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?, ?)"); for (int a = 0; a < messages.size(); a++) { TLRPC.Message message = messages.get(a); + MessageObject.normalizeFlags(message); NativeByteBuffer data = new NativeByteBuffer(message.getObjectSize()); message.serializeToStream(data); @@ -4576,39 +4422,52 @@ public void emptyMessagesMedia(long dialogId, ArrayList mids) { } else { state.bindInteger(9, getMessageMediaType(message)); } + NativeByteBuffer storyData = null; + if (message.replyStory != null) { + storyData = new NativeByteBuffer(message.replyStory.getObjectSize()); + message.replyStory.serializeToStream(storyData); + state.bindByteBuffer(10, storyData); + } else { + state.bindNull(10); + } int flags = 0; if (message.stickerVerified == 0) { flags |= 1; } else if (message.stickerVerified == 2) { flags |= 2; } - state.bindInteger(10, flags); - state.bindInteger(11, message.mentioned ? 1 : 0); - state.bindInteger(12, message.forwards); + state.bindInteger(11, flags); + state.bindInteger(12, message.mentioned ? 1 : 0); + state.bindInteger(13, message.forwards); NativeByteBuffer repliesData = null; if (message.replies != null) { repliesData = new NativeByteBuffer(message.replies.getObjectSize()); message.replies.serializeToStream(repliesData); - state.bindByteBuffer(13, repliesData); + state.bindByteBuffer(14, repliesData); } else { - state.bindNull(13); + state.bindNull(14); } if (message.reply_to != null) { - state.bindInteger(14, message.reply_to.reply_to_top_id != 0 ? message.reply_to.reply_to_top_id : message.reply_to.reply_to_msg_id); + state.bindInteger(15, message.reply_to.reply_to_top_id != 0 ? message.reply_to.reply_to_top_id : message.reply_to.reply_to_msg_id); } else { - state.bindInteger(14, 0); + state.bindInteger(15, 0); } - state.bindLong(15, MessageObject.getChannelId(message)); + state.bindLong(16, MessageObject.getChannelId(message)); NativeByteBuffer customParams = MessageCustomParamsHelper.writeLocalParams(message); if (customParams != null) { state.bindByteBuffer(16, customParams); } else { - state.bindNull(16); + state.bindNull(17); } if ((message.flags & 131072) != 0) { - state.bindLong(17, message.grouped_id); + state.bindLong(18, message.grouped_id); } else { - state.bindNull(17); + state.bindNull(18); + } + if (message.reply_to != null) { + state.bindInteger(19, message.reply_to.story_id); + } else { + state.bindInteger(19, 0); } state.step(); data.reuse(); @@ -4618,6 +4477,9 @@ public void emptyMessagesMedia(long dialogId, ArrayList mids) { if (customParams != null) { customParams.reuse(); } + if (storyData != null) { + storyData.reuse(); + } } state.dispose(); state = null; @@ -4695,6 +4557,7 @@ public void updateMessagePollResults(long pollId, TLRPC.Poll poll, TLRPC.PollRes MessageObject.updatePollResults(media, results); } + MessageObject.normalizeFlags(message); data = new NativeByteBuffer(message.getObjectSize()); message.serializeToStream(data); currentState.requery(); @@ -4752,6 +4615,7 @@ public void updateMessageReactions(long dialogId, int msgId, TLRPC.TL_messageRea } else { state = database.executeFast("UPDATE messages_topics SET data = ? WHERE mid = ? AND uid = ?"); } + MessageObject.normalizeFlags(message); NativeByteBuffer data2 = new NativeByteBuffer(message.getObjectSize()); message.serializeToStream(data2); state.requery(); @@ -6172,7 +6036,7 @@ public void updateChatParticipants(TLRPC.ChatParticipants participants) { TLRPC.ChatFull finalInfo = info; AndroidUtilities.runOnUIThread(() -> getNotificationCenter().postNotificationName(NotificationCenter.chatInfoDidLoad, finalInfo, 0, false, false)); - SQLitePreparedStatement state = database.executeFast("REPLACE INTO chat_settings_v2 VALUES(?, ?, ?, ?, ?, ?)"); + SQLitePreparedStatement state = database.executeFast("REPLACE INTO chat_settings_v2 VALUES(?, ?, ?, ?, ?, ?, ?)"); NativeByteBuffer data = new NativeByteBuffer(info.getObjectSize()); info.serializeToStream(data); state.bindLong(1, info.id); @@ -6181,6 +6045,7 @@ public void updateChatParticipants(TLRPC.ChatParticipants participants) { state.bindInteger(4, info.online_count); state.bindLong(5, info.inviterId); state.bindInteger(6, info.invitesCount); + state.bindInteger(7, info.participants_count); state.step(); state.dispose(); data.reuse(); @@ -6466,11 +6331,12 @@ public void loadUserInfo(TLRPC.User user, boolean force, int classGuid, int from public void updateUserInfo(TLRPC.UserFull info, boolean ifExist) { storageQueue.postRunnable(() -> { + long id = info.user != null ? info.user.id : info.id; SQLiteCursor cursor = null; SQLitePreparedStatement state = null; try { if (ifExist) { - cursor = database.queryFinalized("SELECT uid FROM user_settings WHERE uid = " + info.user.id); + cursor = database.queryFinalized("SELECT uid FROM user_settings WHERE uid = " + id); boolean exist = cursor.next(); cursor.dispose(); cursor = null; @@ -6481,7 +6347,7 @@ public void updateUserInfo(TLRPC.UserFull info, boolean ifExist) { state = database.executeFast("REPLACE INTO user_settings VALUES(?, ?, ?)"); NativeByteBuffer data = new NativeByteBuffer(info.getObjectSize()); info.serializeToStream(data); - state.bindLong(1, info.user.id); + state.bindLong(1, id); state.bindByteBuffer(2, data); state.bindInteger(3, info.pinned_msg_id); state.step(); @@ -6491,7 +6357,7 @@ public void updateUserInfo(TLRPC.UserFull info, boolean ifExist) { if ((info.flags & 2048) != 0) { state = database.executeFast("UPDATE dialogs SET folder_id = ? WHERE did = ?"); state.bindInteger(1, info.folder_id); - state.bindLong(2, info.user.id); + state.bindLong(2, id); state.step(); state.dispose(); state = null; @@ -6500,7 +6366,7 @@ public void updateUserInfo(TLRPC.UserFull info, boolean ifExist) { if ((info.flags & 16384) != 0) { state = database.executeFast("UPDATE dialogs SET ttl_period = ? WHERE did = ?"); state.bindInteger(1, info.ttl_period); - state.bindLong(2, info.user.id); + state.bindLong(2, id); state.step(); state.dispose(); state = null; @@ -6587,7 +6453,7 @@ public void updateChatInfo(TLRPC.ChatFull info, boolean ifExist) { info.invitesCount = links; } - state = database.executeFast("REPLACE INTO chat_settings_v2 VALUES(?, ?, ?, ?, ?, ?)"); + state = database.executeFast("REPLACE INTO chat_settings_v2 VALUES(?, ?, ?, ?, ?, ?, ?)"); NativeByteBuffer data = new NativeByteBuffer(info.getObjectSize()); info.serializeToStream(data); state.bindLong(1, info.id); @@ -6596,6 +6462,7 @@ public void updateChatInfo(TLRPC.ChatFull info, boolean ifExist) { state.bindInteger(4, info.online_count); state.bindLong(5, info.inviterId); state.bindInteger(6, info.invitesCount); + state.bindInteger(7, info.participants_count); state.step(); state.dispose(); state = null; @@ -6886,7 +6753,7 @@ public void updateChatInfo(long chatId, long userId, int what, long invited_id, TLRPC.ChatFull finalInfo = info; AndroidUtilities.runOnUIThread(() -> getNotificationCenter().postNotificationName(NotificationCenter.chatInfoDidLoad, finalInfo, 0, false, false)); - SQLitePreparedStatement state = database.executeFast("REPLACE INTO chat_settings_v2 VALUES(?, ?, ?, ?, ?, ?)"); + SQLitePreparedStatement state = database.executeFast("REPLACE INTO chat_settings_v2 VALUES(?, ?, ?, ?, ?, ?, ?)"); NativeByteBuffer data = new NativeByteBuffer(info.getObjectSize()); info.serializeToStream(data); state.bindLong(1, chatId); @@ -6895,6 +6762,7 @@ public void updateChatInfo(long chatId, long userId, int what, long invited_id, state.bindInteger(4, info.online_count); state.bindLong(5, info.inviterId); state.bindInteger(6, info.invitesCount); + state.bindInteger(7, info.participants_count); state.step(); state.dispose(); data.reuse(); @@ -7018,6 +6886,38 @@ public boolean hasInviteMeMessage(long chatId) { return result[0]; } + public HashMap getSmallGroupsParticipantsCount() { + HashMap result = new HashMap<>(); + + SQLiteCursor cursor = null; + try { + cursor = database.queryFinalized("SELECT uid, info, participants_count FROM chat_settings_v2 WHERE participants_count > 1"); + while (cursor.next()) { + TLRPC.ChatFull info = null; + long id = cursor.longValue(0); + NativeByteBuffer data = cursor.byteBufferValue(1); + int participants_count = cursor.intValue(2); + if (data != null) { + info = TLRPC.ChatFull.TLdeserialize(data, data.readInt32(false), false); + data.reuse(); + // legacy groups already contain participants_count in TLRPC.Chat and not need to load chatfull + if (info instanceof TLRPC.TL_channelFull) { + result.put(id, participants_count); + } + } + } + cursor.dispose(); + cursor = null; + } catch (Exception e) { + checkSQLException(e); + } finally { + if (cursor != null) { + cursor.dispose(); + } + } + return result; + } + private TLRPC.ChatFull loadChatInfoInternal(long chatId, boolean isChannel, boolean force, boolean byChannelUsers, int fromMessageId) { TLRPC.ChatFull info = null; ArrayList loadedUsers = new ArrayList<>(); @@ -7176,6 +7076,11 @@ public TLRPC.ChatFull loadChatInfo(long chatId, boolean isChannel, CountDownLatc return result[0]; } + public TLRPC.ChatFull loadChatInfoInQueue(long chatId, boolean isChannel, boolean force, boolean byChannelUsers, int fromMessageId) { + return loadChatInfoInternal(chatId, isChannel, force, byChannelUsers, fromMessageId); + } + + public void processPendingRead(long dialogId, int maxPositiveId, int maxNegativeId, int scheduledCount) { int maxDate = lastSavedDate; storageQueue.postRunnable(() -> { @@ -7849,7 +7754,7 @@ public void getMessagesCount(long dialog_id, IntCallback callback) { }); } - public Runnable getMessagesInternal(long dialogId, long mergeDialogId, int count, int max_id, int offset_date, int minDate, int classGuid, int load_type, boolean scheduled, int threadMessageId, int loadIndex, boolean processMessages, boolean isTopic) { + public Runnable getMessagesInternal(long dialogId, long mergeDialogId, int count, int max_id, int offset_date, int minDate, int classGuid, int load_type, boolean scheduled, int threadMessageId, int loadIndex, boolean processMessages, boolean isTopic, MessageLoaderLogger loaderLogger) { TLRPC.TL_messages_messages res = new TLRPC.TL_messages_messages(); long currentUserId = getUserConfig().clientUserId; int count_unread = 0; @@ -7880,9 +7785,9 @@ public Runnable getMessagesInternal(long dialogId, long mergeDialogId, int count ArrayList replyMessageRandomIds = new ArrayList<>(); String messageSelect; if (threadMessageId != 0) { - messageSelect = "SELECT m.read_state, m.data, m.send_state, m.mid, m.date, r.random_id, m.replydata, m.media, m.ttl, m.mention, m.imp, m.forwards, m.replies_data, m.custom_params FROM messages_topics as m LEFT JOIN randoms_v2 as r ON r.mid = m.mid AND r.uid = m.uid"; + messageSelect = "SELECT m.read_state, m.data, m.send_state, m.mid, m.date, r.random_id, m.replydata, m.media, m.ttl, m.mention, m.imp, m.forwards, m.replies_data, m.custom_params, m.reply_to_story_id FROM messages_topics as m LEFT JOIN randoms_v2 as r ON r.mid = m.mid AND r.uid = m.uid"; } else { - messageSelect = "SELECT m.read_state, m.data, m.send_state, m.mid, m.date, r.random_id, m.replydata, m.media, m.ttl, m.mention, m.imp, m.forwards, m.replies_data, m.custom_params FROM messages_v2 as m LEFT JOIN randoms_v2 as r ON r.mid = m.mid AND r.uid = m.uid"; + messageSelect = "SELECT m.read_state, m.data, m.send_state, m.mid, m.date, r.random_id, m.replydata, m.media, m.ttl, m.mention, m.imp, m.forwards, m.replies_data, m.custom_params, m.reply_to_story_id FROM messages_v2 as m LEFT JOIN randoms_v2 as r ON r.mid = m.mid AND r.uid = m.uid"; } if (scheduled) { isEnd = true; @@ -8362,6 +8267,7 @@ public Runnable getMessagesInternal(long dialogId, long mergeDialogId, int count if (cursor.next()) { holeMessageId = cursor.intValue(0); } + cursor.dispose(); cursor = null; if (threadMessageId != 0) { @@ -8536,15 +8442,25 @@ public Runnable getMessagesInternal(long dialogId, long mergeDialogId, int count addUsersAndChatsFromMessage(message, usersToLoad, chatsToLoad, animatedEmojiToLoad); - if (message.reply_to != null && (message.reply_to.reply_to_msg_id != 0 || message.reply_to.reply_to_random_id != 0)) { - if (!cursor.isNull(6)) { - data = cursor.byteBufferValue(6); - if (data != null) { - message.replyMessage = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false); - message.replyMessage.readAttachPath(data, currentUserId); - data.reuse(); - if (message.replyMessage != null) { - addUsersAndChatsFromMessage(message.replyMessage, usersToLoad, chatsToLoad, animatedEmojiToLoad); + if (message.reply_to != null) { + if ((message.reply_to.reply_to_msg_id != 0 || message.reply_to.reply_to_random_id != 0)) { + if (!cursor.isNull(6)) { + data = cursor.byteBufferValue(6); + if (data != null) { + message.replyMessage = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false); + message.replyMessage.readAttachPath(data, currentUserId); + data.reuse(); + if (message.replyMessage != null) { + addUsersAndChatsFromMessage(message.replyMessage, usersToLoad, chatsToLoad, animatedEmojiToLoad); + } + } + } + } else if (message.reply_to.story_id != 0) { + if (!cursor.isNull(6)) { + data = cursor.byteBufferValue(6); + if (data != null) { + message.replyStory = TLRPC.StoryItem.TLdeserialize(data, data.readInt32(false), false); + data.reuse(); } } } @@ -8732,7 +8648,7 @@ public Runnable getMessagesInternal(long dialogId, long mergeDialogId, int count }; } else {*/ int finalMessagesCount = scheduled ? res.messages.size() : messagesCount; - return () -> getMessagesController().processLoadedMessages(res, finalMessagesCount, dialogId, mergeDialogId, countQueryFinal, maxIdOverrideFinal, offset_date, true, classGuid, minUnreadIdFinal, lastMessageIdFinal, countUnreadFinal, maxUnreadDateFinal, load_type, isEndFinal, scheduled ? 1 : 0, threadMessageId, loadIndex, queryFromServerFinal, mentionsUnreadFinal, processMessages, isTopic); + return () -> getMessagesController().processLoadedMessages(res, finalMessagesCount, dialogId, mergeDialogId, countQueryFinal, maxIdOverrideFinal, offset_date, true, classGuid, minUnreadIdFinal, lastMessageIdFinal, countUnreadFinal, maxUnreadDateFinal, load_type, isEndFinal, scheduled ? 1 : 0, threadMessageId, loadIndex, queryFromServerFinal, mentionsUnreadFinal, processMessages, isTopic, loaderLogger); //} } @@ -8763,10 +8679,19 @@ private void getAnimatedEmoji(String join, ArrayList documents) } } - public void getMessages(long dialogId, long mergeDialogId, boolean loadInfo, int count, int max_id, int offset_date, int minDate, int classGuid, int load_type, boolean scheduled, int replyMessageId, int loadIndex, boolean processMessages, boolean isTopic) { + public void getMessages(long dialogId, long mergeDialogId, boolean loadInfo, int count, int max_id, int offset_date, int minDate, int classGuid, int load_type, boolean scheduled, int replyMessageId, int loadIndex, boolean processMessages, boolean isTopic, MessageLoaderLogger loaderLogger) { storageQueue.postRunnable(() -> { - Runnable processMessagesRunnable = getMessagesInternal(dialogId, mergeDialogId, count, max_id, offset_date, minDate, classGuid, load_type, scheduled, replyMessageId, loadIndex, processMessages, isTopic); + if (loaderLogger != null) { + loaderLogger.logStorageQueuePost(); + } + Runnable processMessagesRunnable = getMessagesInternal(dialogId, mergeDialogId, count, max_id, offset_date, minDate, classGuid, load_type, scheduled, replyMessageId, loadIndex, processMessages, isTopic, loaderLogger); + if (loaderLogger != null) { + loaderLogger.logStorageProccessing(); + } Utilities.stageQueue.postRunnable(() -> { + if (loaderLogger != null) { + loaderLogger.logStageQueuePost(); + } processMessagesRunnable.run(); }); }); @@ -9462,7 +9387,7 @@ private String formatUserSearchName(TLRPC.User user) { return str.toString().toLowerCase(); } - private void putUsersInternal(ArrayList users) throws Exception { + private void putUsersInternal(List users) throws Exception { if (users == null || users.isEmpty()) { return; } @@ -9575,7 +9500,7 @@ public void updateChatDefaultBannedRights(long chatId, TLRPC.TL_chatBannedRights }); } - private void putChatsInternal(ArrayList chats) throws Exception { + private void putChatsInternal(List chats) throws Exception { if (chats == null || chats.isEmpty()) { return; } @@ -9617,6 +9542,9 @@ private void putChatsInternal(ArrayList chats) throws Exception { oldChat.username = null; oldChat.flags = oldChat.flags & ~64; } + if (chat.participants_count > 0) { + oldChat.participants_count = chat.participants_count; + } chat = oldChat; } } @@ -9742,7 +9670,7 @@ public void getEncryptedChatsInternal(String chatsToLoad, ArrayList users, ArrayList chats, boolean withTransaction) { + private void putUsersAndChatsInternal(List users, List chats, boolean withTransaction) { try { if (withTransaction) { database.beginTransaction(); @@ -9758,7 +9686,7 @@ private void putUsersAndChatsInternal(ArrayList users, ArrayList users, ArrayList chats, boolean withTransaction, boolean useQueue) { + public void putUsersAndChats(List users, List chats, boolean withTransaction, boolean useQueue) { if (users != null && users.isEmpty() && chats != null && chats.isEmpty()) { return; } @@ -9969,6 +9897,7 @@ public void putWebPages(LongSparseArray webPages) { state2 = database.executeFast("UPDATE media_v4 SET data = ? WHERE mid = ? AND uid = ?"); for (int a = 0; a < messages.size(); a++) { TLRPC.Message message = messages.get(a); + MessageObject.normalizeFlags(message); NativeByteBuffer data = new NativeByteBuffer(message.getObjectSize()); message.serializeToStream(data); @@ -10013,7 +9942,7 @@ public void putWebPages(LongSparseArray webPages) { }); } - public void overwriteChannel(long channelId, TLRPC.TL_updates_channelDifferenceTooLong difference, int newDialogType) { + public void overwriteChannel(long channelId, TLRPC.TL_updates_channelDifferenceTooLong difference, int newDialogType, Runnable onDone) { storageQueue.postRunnable(() -> { SQLiteCursor cursor = null; try { @@ -10083,6 +10012,9 @@ public void overwriteChannel(long channelId, TLRPC.TL_updates_channelDifferenceT cursor.dispose(); } } + if (onDone != null) { + onDone.run(); + } }); } @@ -10507,7 +10439,7 @@ private void putMessagesInternal(ArrayList messages, boolean with if (message.local_id != 0) { messageId = message.local_id; } - + MessageObject.normalizeFlags(message); NativeByteBuffer data = new NativeByteBuffer(message.getObjectSize()); message.serializeToStream(data); @@ -10595,8 +10527,8 @@ private void putMessagesInternal(ArrayList messages, boolean with HashMap> messagesMediaIdsMapTopics = new HashMap<>(); ArrayList createNewTopics = null; - state_messages = database.executeFast("REPLACE INTO messages_v2 VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, 0, ?, ?)"); - state_messages_topic = database.executeFast("REPLACE INTO messages_topics VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, 0, ?)"); + state_messages = database.executeFast("REPLACE INTO messages_v2 VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?, ?)"); + state_messages_topic = database.executeFast("REPLACE INTO messages_topics VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?)"); state_media = null; state_randoms = database.executeFast("REPLACE INTO randoms_v2 VALUES(?, ?, ?)"); state_download = database.executeFast("REPLACE INTO download_queue VALUES(?, ?, ?, ?, ?)"); @@ -11008,7 +10940,7 @@ private void putMessagesInternal(ArrayList messages, boolean with if (message.local_id != 0) { messageId = message.local_id; } - + MessageObject.normalizeFlags(message); NativeByteBuffer data = new NativeByteBuffer(message.getObjectSize()); message.serializeToStream(data); @@ -11080,6 +11012,14 @@ private void putMessagesInternal(ArrayList messages, boolean with } else { statement.bindInteger(pointer++, getMessageMediaType(message)); } + NativeByteBuffer storyData = null; + if (message.replyStory != null) { + storyData = new NativeByteBuffer(message.replyStory.getObjectSize()); + message.replyStory.serializeToStream(storyData); + statement.bindByteBuffer(pointer++, storyData); + } else { + statement.bindNull(pointer++); + } int flags = 0; if (message.stickerVerified == 0) { flags |= 1; @@ -11116,6 +11056,11 @@ private void putMessagesInternal(ArrayList messages, boolean with statement.bindNull(pointer++); } } + if (message.reply_to != null) { + statement.bindInteger(pointer++, message.reply_to.story_id); + } else { + statement.bindInteger(pointer++, 0); + } statement.step(); if (repliesData != null) { @@ -11124,6 +11069,9 @@ private void putMessagesInternal(ArrayList messages, boolean with if (customParams != null) { customParams.reuse(); } + if (storyData != null) { + storyData.reuse(); + } } if (message.random_id != 0) { @@ -13397,9 +13345,11 @@ private void closeHolesInTable(String table, long did, int minId, int maxId, int if (hole.end != minId) { try { if (thread_message_id != 0) { - database.executeFast(String.format(Locale.US, "UPDATE " + table + " SET end = %d WHERE uid = %d AND topic_id = %d AND start = %d AND end = %d", minId, did, thread_message_id, hole.start, hole.end)).stepThis().dispose(); + database.executeFast(String.format(Locale.US, "DELETE FROM " + table + " WHERE uid = %d AND topic_id = %d AND start = %d AND end = %d", did, thread_message_id, hole.start, hole.end)).stepThis().dispose(); + database.executeFast(String.format(Locale.US, "REPLACE INTO " + table + " VALUES(%d, %d, %d, %d)", did, thread_message_id, hole.start, minId)).stepThis().dispose(); } else { - database.executeFast(String.format(Locale.US, "UPDATE " + table + " SET end = %d WHERE uid = %d AND start = %d AND end = %d", minId, did, hole.start, hole.end)).stepThis().dispose(); + database.executeFast(String.format(Locale.US, "DELETE FROM " + table + " WHERE uid = %d AND start = %d AND end = %d", did, hole.start, hole.end)).stepThis().dispose(); + database.executeFast(String.format(Locale.US, "REPLACE INTO " + table + " VALUES(%d, %d, %d)", did, hole.start, minId)).stepThis().dispose(); } } catch (Exception e) { checkSQLException(e, false); @@ -13409,9 +13359,11 @@ private void closeHolesInTable(String table, long did, int minId, int maxId, int if (hole.start != maxId) { try { if (thread_message_id != 0) { - database.executeFast(String.format(Locale.US, "UPDATE " + table + " SET start = %d WHERE uid = %d AND topic_id = %d AND start = %d AND end = %d", maxId, did, thread_message_id, hole.start, hole.end)).stepThis().dispose(); + database.executeFast(String.format(Locale.US, "DELETE FROM " + table + " WHERE uid = %d AND topic_id = %d AND start = %d AND end = %d", did, thread_message_id, hole.start, hole.end)).stepThis().dispose(); + database.executeFast(String.format(Locale.US, "REPLACE INTO " + table + " VALUES(%d, %d, %d, %d)", did, thread_message_id, maxId, hole.end)).stepThis().dispose(); } else { - database.executeFast(String.format(Locale.US, "UPDATE " + table + " SET start = %d WHERE uid = %d AND start = %d AND end = %d", maxId, did, hole.start, hole.end)).stepThis().dispose(); + database.executeFast(String.format(Locale.US, "DELETE FROM " + table + " WHERE uid = %d AND start = %d AND end = %d", did, hole.start, hole.end)).stepThis().dispose(); + database.executeFast(String.format(Locale.US, "REPLACE INTO " + table + " VALUES(%d, %d, %d)", did, maxId, hole.end)).stepThis().dispose(); } } catch (Exception e) { checkSQLException(e, false); @@ -13425,6 +13377,7 @@ private void closeHolesInTable(String table, long did, int minId, int maxId, int database.executeFast(String.format(Locale.US, "DELETE FROM " + table + " WHERE uid = %d AND start = %d AND end = %d", did, hole.start, hole.end)).stepThis().dispose(); state = database.executeFast("REPLACE INTO " + table + " VALUES(?, ?, ?)"); } + int pointer = 1; state.requery(); state.bindLong(pointer++, did); @@ -13494,6 +13447,7 @@ public void replaceMessageIfExists(TLRPC.Message message, ArrayList } fixUnsupportedMedia(message); + MessageObject.normalizeFlags(message); NativeByteBuffer data = new NativeByteBuffer(message.getObjectSize()); message.serializeToStream(data); @@ -13504,9 +13458,9 @@ public void replaceMessageIfExists(TLRPC.Message message, ArrayList continue; } if (isTopic) { - state = database.executeFast("REPLACE INTO messages_topics VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, 0, ?)"); + state = database.executeFast("REPLACE INTO messages_topics VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?)"); } else { - state = database.executeFast("REPLACE INTO messages_v2 VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, 0, ?, ?)"); + state = database.executeFast("REPLACE INTO messages_v2 VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?, ?)"); } state.requery(); @@ -13527,6 +13481,14 @@ public void replaceMessageIfExists(TLRPC.Message message, ArrayList } else { state.bindInteger(pointer++, getMessageMediaType(message)); } + NativeByteBuffer storyData = null; + if (message.replyStory != null) { + storyData = new NativeByteBuffer(message.replyStory.getObjectSize()); + message.replyStory.serializeToStream(storyData); + state.bindByteBuffer(pointer++, storyData); + } else { + state.bindNull(pointer++); + } int flags = 0; if (message.stickerVerified == 0) { flags |= 1; @@ -13562,12 +13524,20 @@ public void replaceMessageIfExists(TLRPC.Message message, ArrayList state.bindNull(pointer++); } } + if (message.reply_to != null) { + state.bindInteger(pointer++, message.reply_to.story_id); + } else { + state.bindInteger(pointer++, 0); + } state.step(); state.dispose(); state = null; if (repliesData != null) { repliesData.reuse(); } + if (storyData != null) { + storyData.reuse(); + } } if (MediaDataController.canAddMessageToMedia(message)) { @@ -13660,6 +13630,7 @@ public void putMessages(TLRPC.messages_Messages messages, long dialogId, int loa } fixUnsupportedMedia(message); + MessageObject.normalizeFlags(message); state_messages.requery(); NativeByteBuffer data = new NativeByteBuffer(message.getObjectSize()); message.serializeToStream(data); @@ -13721,8 +13692,8 @@ public void putMessages(TLRPC.messages_Messages messages, long dialogId, int loa Integer lastMessageId = null; Long lastMessageGroupId = null; - state_messages_topics = database.executeFast("REPLACE INTO messages_topics VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, 0, ?)"); - state_messages = database.executeFast("REPLACE INTO messages_v2 VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, 0, ?, ?)"); + state_messages_topics = database.executeFast("REPLACE INTO messages_topics VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?)"); + state_messages = database.executeFast("REPLACE INTO messages_v2 VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?, ?)"); state_media = database.executeFast("REPLACE INTO media_v4 VALUES(?, ?, ?, ?, ?)"); state_media_topics = database.executeFast("REPLACE INTO media_topics VALUES(?, ?, ?, ?, ?, ?)"); state_polls = null; @@ -13860,6 +13831,7 @@ public void putMessages(TLRPC.messages_Messages messages, long dialogId, int loa } fixUnsupportedMedia(message); + MessageObject.normalizeFlags(message); NativeByteBuffer data = new NativeByteBuffer(message.getObjectSize()); message.serializeToStream(data); @@ -13893,6 +13865,14 @@ public void putMessages(TLRPC.messages_Messages messages, long dialogId, int loa } else { currentState.bindInteger(pointer++, getMessageMediaType(message)); } + NativeByteBuffer storyData = null; + if (message.replyStory != null) { + storyData = new NativeByteBuffer(message.replyStory.getObjectSize()); + message.replyStory.serializeToStream(storyData); + currentState.bindByteBuffer(pointer++, storyData); + } else { + currentState.bindNull(pointer++); + } int flags = 0; if (message.stickerVerified == 0) { flags |= 1; @@ -13929,6 +13909,11 @@ public void putMessages(TLRPC.messages_Messages messages, long dialogId, int loa currentState.bindNull(pointer++); } } + if (message.reply_to != null) { + currentState.bindInteger(pointer++, message.reply_to.story_id); + } else { + currentState.bindInteger(pointer++, 0); + } currentState.step(); if (repliesData != null) { @@ -13937,6 +13922,9 @@ public void putMessages(TLRPC.messages_Messages messages, long dialogId, int loa if (customParams != null) { customParams.reuse(); } + if (storyData != null) { + storyData.reuse(); + } } if (threadMessageId == 0 || load_type == -2) { @@ -14173,7 +14161,16 @@ public static void addUsersAndChatsFromMessage(TLRPC.Message message, ArrayList< if (message.media instanceof TLRPC.TL_messageMediaPoll) { TLRPC.TL_messageMediaPoll messageMediaPoll = (TLRPC.TL_messageMediaPoll) message.media; if (!messageMediaPoll.results.recent_voters.isEmpty()) { - usersToLoad.addAll(messageMediaPoll.results.recent_voters); + for (int i = 0; i < messageMediaPoll.results.recent_voters.size(); i++) { + TLRPC.Peer peer = messageMediaPoll.results.recent_voters.get(i); + if (peer.user_id != 0) { + usersToLoad.add(peer.user_id); + } else if (peer.chat_id != 0) { + chatsToLoad.add(-peer.chat_id); + } else if (peer.channel_id != 0) { + chatsToLoad.add(-peer.channel_id); + } + } } } } @@ -14669,7 +14666,7 @@ private void putDialogsInternal(TLRPC.messages_Dialogs dialogs, int check) { } if (!dialogs.dialogs.isEmpty()) { - state_messages = database.executeFast("REPLACE INTO messages_v2 VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, 0, NULL, ?)"); + state_messages = database.executeFast("REPLACE INTO messages_v2 VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, 0, NULL, ?, ?)"); state_dialogs = database.executeFast("REPLACE INTO dialogs VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); state_media = database.executeFast("REPLACE INTO media_v4 VALUES(?, ?, ?, ?, ?)"); state_settings = database.executeFast("REPLACE INTO dialog_settings VALUES(?, ?)"); @@ -14727,6 +14724,7 @@ private void putDialogsInternal(TLRPC.messages_Dialogs dialogs, int check) { } fixUnsupportedMedia(message); + MessageObject.normalizeFlags(message); NativeByteBuffer data = new NativeByteBuffer(message.getObjectSize()); message.serializeToStream(data); @@ -14768,6 +14766,11 @@ private void putDialogsInternal(TLRPC.messages_Dialogs dialogs, int check) { } else { state_messages.bindNull(16); } + if (message.reply_to != null) { + state_messages.bindInteger(17, message.reply_to.story_id); + } else { + state_messages.bindInteger(17, 0); + } state_messages.step(); if (MediaDataController.canAddMessageToMedia(message)) { @@ -14965,12 +14968,16 @@ public void setDialogsFolderId(ArrayList peers, ArrayList peers, ArrayList peers, ArrayList peers, ArrayList heavyOperationsCounter = new HashSet<>(); - private final HashMap allowedNotifications = new HashMap<>(); + private final SparseArray allowedNotifications = new SparseArray<>(); public interface NotificationCenterDelegate { void didReceivedNotification(int id, int account, Object... args); @@ -391,19 +403,19 @@ public int setAnimationInProgress(int oldIndex, int[] allowedNotifications, bool private void checkForExpiredNotifications() { checkForExpiredNotifications = null; - if (this.allowedNotifications.isEmpty()) { + if (this.allowedNotifications.size() == 0) { return; } long minTime = Long.MAX_VALUE; long currentTime = SystemClock.elapsedRealtime(); ArrayList expiredIndices = null; - for (HashMap.Entry entry : this.allowedNotifications.entrySet()) { - AllowedNotifications allowedNotification = entry.getValue(); + for (int i = 0; i < allowedNotifications.size(); i++) { + AllowedNotifications allowedNotification = allowedNotifications.valueAt(i); if (currentTime - allowedNotification.time > 1000) { if (expiredIndices == null) { expiredIndices = new ArrayList<>(); } - expiredIndices.add(entry.getKey()); + expiredIndices.add(allowedNotifications.keyAt(i)); } else { minTime = Math.min(allowedNotification.time, minTime); } @@ -427,7 +439,8 @@ public void updateAllowedNotifications(int transitionAnimationIndex, int[] allow } public void onAnimationFinish(int index) { - AllowedNotifications allowed = allowedNotifications.remove(index); + AllowedNotifications allowed = allowedNotifications.get(index); + allowedNotifications.delete(index); if (allowed != null) { animationInProgressCount--; if (!heavyOperationsCounter.isEmpty()) { @@ -440,7 +453,7 @@ public void onAnimationFinish(int index) { runDelayedNotifications(); } } - if (checkForExpiredNotifications != null && allowedNotifications.isEmpty()) { + if (checkForExpiredNotifications != null && allowedNotifications.size() == 0) { AndroidUtilities.cancelRunOnUIThread(checkForExpiredNotifications); checkForExpiredNotifications = null; } @@ -481,20 +494,24 @@ public ArrayList getObservers(int id) { return observers.get(id); } + public void postNotificationNameOnUIThread(final int id, final Object... args) { + AndroidUtilities.runOnUIThread(() -> postNotificationName(id, args)); + } + public void postNotificationName(int id, Object... args) { boolean allowDuringAnimation = id == startAllHeavyOperations || id == stopAllHeavyOperations || id == didReplacedPhotoInMemCache || id == closeChats || id == invalidateMotionBackground; ArrayList expiredIndices = null; - if (!allowDuringAnimation && !allowedNotifications.isEmpty()) { + if (!allowDuringAnimation && allowedNotifications.size() > 0) { int size = allowedNotifications.size(); int allowedCount = 0; long currentTime = SystemClock.elapsedRealtime(); - for (HashMap.Entry entry : allowedNotifications.entrySet()) { - AllowedNotifications allowedNotification = entry.getValue(); + for (int i = 0; i < allowedNotifications.size(); i++) { + AllowedNotifications allowedNotification = allowedNotifications.valueAt(i); if (currentTime - allowedNotification.time > EXPIRE_NOTIFICATIONS_TIME) { if (expiredIndices == null) { expiredIndices = new ArrayList<>(); } - expiredIndices.add(entry.getKey()); + expiredIndices.add(allowedNotifications.keyAt(i)); } int[] allowed = allowedNotification.allowedIds; if (allowed != null) { @@ -517,7 +534,11 @@ public void postNotificationName(int id, Object... args) { Integer flags = (Integer) args[0]; currentHeavyOperationFlags |= flags; } - postNotificationNameInternal(id, allowDuringAnimation, args); + if (shouldDebounce(id, args) && BuildVars.DEBUG_VERSION) { + postNotificationDebounced(id, args); + } else { + postNotificationNameInternal(id, allowDuringAnimation, args); + } if (expiredIndices != null) { for (int i = 0; i < expiredIndices.size(); i++) { @@ -526,6 +547,26 @@ public void postNotificationName(int id, Object... args) { } } + SparseArray alreadyPostedRannubles = new SparseArray<>(); + + private void postNotificationDebounced(int id, Object[] args) { + int hash = id + (Arrays.hashCode(args) << 16); + if (alreadyPostedRannubles.indexOfKey(hash) >= 0) { + //skip + } else { + Runnable runnable = () -> { + postNotificationNameInternal(id, false, args); + alreadyPostedRannubles.remove(hash); + }; + alreadyPostedRannubles.put(hash, runnable); + AndroidUtilities.runOnUIThread(runnable, 250); + } + } + + private boolean shouldDebounce(int id, Object[] args) { + return id == updateInterfaces; + } + @UiThread public void postNotificationNameInternal(int id, boolean allowDuringAnimation, Object... args) { if (BuildVars.DEBUG_VERSION) { @@ -717,6 +758,20 @@ public void onViewDetachedFromWindow(View view) { }); } + public void listenOnce(int id, Runnable callback) { + final NotificationCenterDelegate[] observer = new NotificationCenterDelegate[1]; + observer[0] = (nid, account, args) -> { + if (nid == id && observer[0] != null) { + if (callback != null) { + callback.run(); + } + removeObserver(observer[0], id); + observer[0] = null; + } + }; + addObserver(observer[0], id); + } + private class UniqArrayList extends ArrayList { HashSet set = new HashSet<>(); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationDismissReceiver.java b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationDismissReceiver.java index a01db8628c..c5d763be4e 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationDismissReceiver.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationDismissReceiver.java @@ -25,7 +25,9 @@ public void onReceive(Context context, Intent intent) { } long dialogId = intent.getLongExtra("dialogId", 0); int date = intent.getIntExtra("messageDate", 0); - if (dialogId == 0) { + if (intent.hasExtra("story") && intent.getBooleanExtra("story", false)) { + NotificationsController.getInstance(currentAccount).processIgnoreStories(); + } else if (dialogId == 0) { MessagesController.getNotificationsSettings(currentAccount).edit().putInt("dismissDate", date).commit(); } else { MessagesController.getNotificationsSettings(currentAccount).edit().putInt("dismissDate" + dialogId, date).commit(); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsController.java b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsController.java index e7572e25d8..c49bc32c80 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsController.java @@ -23,15 +23,21 @@ import android.content.SharedPreferences; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ImageDecoder; +import android.graphics.LinearGradient; +import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; +import android.graphics.Shader; +import android.graphics.Xfermode; import android.graphics.drawable.BitmapDrawable; import android.media.AudioAttributes; import android.media.AudioManager; @@ -41,13 +47,17 @@ import android.os.PowerManager; import android.os.SystemClock; import android.provider.Settings; +import android.text.TextPaint; import android.text.TextUtils; +import android.util.Pair; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.text.TextUtils; import android.util.SparseArray; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.collection.LongSparseArray; import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationManagerCompat; @@ -60,24 +70,31 @@ import androidx.core.graphics.ColorUtils; import androidx.core.graphics.drawable.IconCompat; -import com.google.android.exoplayer2.util.Log; - import org.telegram.messenger.support.LongSparseIntArray; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.BubbleActivity; +import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.spoilers.SpoilerEffect; import org.telegram.ui.LaunchActivity; import org.telegram.ui.PopupNotificationActivity; +import org.telegram.ui.Stories.recorder.StoryEntry; import java.io.File; import java.io.FileOutputStream; import java.util.ArrayList; import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.TreeSet; import java.util.concurrent.CountDownLatch; import java.util.function.Consumer; @@ -90,21 +107,23 @@ public class NotificationsController extends BaseController { public static final String EXTRA_VOICE_REPLY = "extra_voice_reply"; public static String OTHER_NOTIFICATIONS_CHANNEL = null; - private static DispatchQueue notificationsQueue = new DispatchQueue("notificationsQueue"); - private ArrayList pushMessages = new ArrayList<>(); - private ArrayList delayedPushMessages = new ArrayList<>(); - private LongSparseArray> pushMessagesDict = new LongSparseArray<>(); - private LongSparseArray fcmRandomMessagesDict = new LongSparseArray<>(); - private LongSparseArray smartNotificationsDialogs = new LongSparseArray<>(); + private static final DispatchQueue notificationsQueue = new DispatchQueue("notificationsQueue"); + private final ArrayList pushMessages = new ArrayList<>(); + private final ArrayList delayedPushMessages = new ArrayList<>(); + private final LongSparseArray> pushMessagesDict = new LongSparseArray<>(); + private final LongSparseArray fcmRandomMessagesDict = new LongSparseArray<>(); + private final LongSparseArray smartNotificationsDialogs = new LongSparseArray<>(); private static NotificationManagerCompat notificationManager = null; private static NotificationManager systemNotificationManager = null; - private LongSparseArray pushDialogs = new LongSparseArray<>(); - private LongSparseArray wearNotificationsIds = new LongSparseArray<>(); - private LongSparseArray lastWearNotifiedMessageId = new LongSparseArray<>(); - private LongSparseArray pushDialogsOverrideMention = new LongSparseArray<>(); - public ArrayList popupMessages = new ArrayList<>(); + private final LongSparseArray pushDialogs = new LongSparseArray<>(); + private final LongSparseArray wearNotificationsIds = new LongSparseArray<>(); + private final LongSparseArray lastWearNotifiedMessageId = new LongSparseArray<>(); + private final LongSparseArray pushDialogsOverrideMention = new LongSparseArray<>(); + public final ArrayList popupMessages = new ArrayList<>(); public ArrayList popupReplyMessages = new ArrayList<>(); - private HashSet openedInBubbleDialogs = new HashSet<>(); + private final HashSet openedInBubbleDialogs = new HashSet<>(); + private final ArrayList storyPushMessages = new ArrayList<>(); + private final LongSparseArray storyPushMessagesDict = new LongSparseArray<>(); private long openedDialogId = 0; private int openedTopicId = 0; private int lastButtonId = 5000; @@ -265,12 +284,22 @@ public static void checkOtherNotificationsChannel() { } } + private static final LongSparseArray sharedPrefCachedKeys = new LongSparseArray<>(); + public static String getSharedPrefKey(long dialog_id, int topicId) { - String str = Long.toString(dialog_id); + long hash = dialog_id + ((long) topicId << 12); + int index = sharedPrefCachedKeys.indexOfKey(hash); + if (index >= 0) { + return sharedPrefCachedKeys.valueAt(index); + } + String key; if (topicId != 0) { - str += "_" + topicId; + key = String.format(Locale.US, "%d_%d",dialog_id, topicId); + } else { + key = String.valueOf(dialog_id); } - return str; + sharedPrefCachedKeys.put(hash, key); + return key; } public void muteUntil(long did, int topicId, int selectedTimeInSeconds) { @@ -349,6 +378,7 @@ public void cleanup() { systemNotificationManager.deleteNotificationChannelGroup("channels" + currentAccount); systemNotificationManager.deleteNotificationChannelGroup("groups" + currentAccount); systemNotificationManager.deleteNotificationChannelGroup("private" + currentAccount); + systemNotificationManager.deleteNotificationChannelGroup("stories" + currentAccount); systemNotificationManager.deleteNotificationChannelGroup("other" + currentAccount); String keyStart = currentAccount + "channel"; @@ -674,6 +704,77 @@ public void removeDeletedHisoryFromNotifications(LongSparseIntArray deletedMessa }); } + public void processDeleteStory(long dialogId, int storyId) { + notificationsQueue.postRunnable(() -> { + boolean changed = false; + StoryNotification notification = storyPushMessagesDict.get(dialogId); + if (notification != null) { + notification.dateByIds.remove(storyId); + if (notification.dateByIds.isEmpty()) { + storyPushMessagesDict.remove(dialogId); + storyPushMessages.remove(notification); + changed = true; + getMessagesStorage().deleteStoryPushMessage(dialogId); + } else { + getMessagesStorage().putStoryPushMessage(notification); + } + } + if (changed) { + showOrUpdateNotification(false); + } + }); + } + + public void processReadStories(long dialogId, int maxId) { + notificationsQueue.postRunnable(() -> { + boolean changed = false; + StoryNotification notification = storyPushMessagesDict.get(dialogId); + if (notification != null) { +// if (notification.maxId <= maxId) { + storyPushMessagesDict.remove(dialogId); + storyPushMessages.remove(notification); + changed = true; + getMessagesStorage().deleteStoryPushMessage(dialogId); +// } else { +// StoryNotification newNotification = new StoryNotification(dialogId, notification.localName, Math.max(notification.minId, maxId), Math.max(notification.maxId, maxId), notification.date); +// storyPushMessagesDict.put(dialogId, newNotification); +// storyPushMessages.remove(notification); +// storyPushMessages.add(newNotification); +// changed = true; +// getMessagesStorage().putStoryPushMessage(newNotification); +// } + } + if (changed) { + showOrUpdateNotification(false); + updateStoryPushesRunnable(); + } + }); + } + + public void processIgnoreStories() { + notificationsQueue.postRunnable(() -> { + boolean changed = !storyPushMessages.isEmpty(); + storyPushMessages.clear(); + storyPushMessagesDict.clear(); + getMessagesStorage().deleteAllStoryPushMessages(); + if (changed) { + showOrUpdateNotification(false); + } + }); + } + + public void processIgnoreStories(long dialogId) { + notificationsQueue.postRunnable(() -> { + boolean changed = !storyPushMessages.isEmpty(); + storyPushMessages.clear(); + storyPushMessagesDict.clear(); + getMessagesStorage().deleteStoryPushMessage(dialogId); + if (changed) { + showOrUpdateNotification(false); + } + }); + } + public void processReadMessages(LongSparseIntArray inbox, long dialogId, int maxDate, int maxId, boolean isPopup) { ArrayList popupArrayRemove = new ArrayList<>(0); notificationsQueue.postRunnable(() -> { @@ -849,6 +950,7 @@ public void processNewMessages(ArrayList messageObjects, boolean notificationsQueue.postRunnable(() -> { boolean added = false; boolean edited = false; + boolean storiesUpdated = false; LongSparseArray settingsCache = new LongSparseArray<>(); SharedPreferences preferences = getAccountInstance().getNotificationsSettings(); @@ -868,6 +970,36 @@ public void processNewMessages(ArrayList messageObjects, boolean continue; } + if (messageObject.isStoryPush) { + long date = messageObject.messageOwner == null ? System.currentTimeMillis() : messageObject.messageOwner.date * 1000L; + long dialogId = messageObject.getDialogId(); + int id = messageObject.getId(); + StoryNotification oldNotification = storyPushMessagesDict.get(dialogId); + StoryNotification notification; + if (oldNotification != null) { + edited = true; + oldNotification.dateByIds.put(id, new Pair<>(date, date + 86400000L)); + if (oldNotification.hidden != messageObject.isStoryPushHidden) { + oldNotification.hidden = messageObject.isStoryPushHidden; + storiesUpdated = true; + } + oldNotification.date = oldNotification.getLeastDate(); + getMessagesStorage().putStoryPushMessage(oldNotification); + } else { + added = true; + storiesUpdated = true; + notification = new StoryNotification(dialogId, messageObject.localName, id, date); + notification.hidden = messageObject.isStoryPushHidden; + storyPushMessages.add(notification); + storyPushMessagesDict.put(dialogId, notification); + getMessagesStorage().putStoryPushMessage(notification); + } + TLRPC.TL_updateStory updateStory = new TLRPC.TL_updateStory(); + updateStory.story = new TLRPC.TL_storyItemSkipped(); + + Collections.sort(storyPushMessages, Comparator.comparingLong(n -> n.date)); + continue; + } int mid = messageObject.getId(); long randomId = messageObject.isFcmMessage() ? messageObject.messageOwner.random_id : 0; long dialogId = messageObject.getDialogId(); @@ -1042,6 +1174,7 @@ public void processNewMessages(ArrayList messageObjects, boolean newCount = override; } } + canAddValue = canAddValue && !messageObject.isStoryPush; if (canAddValue) { if (getMessagesController().isForum(dialog_id)) { @@ -1055,7 +1188,7 @@ public void processNewMessages(ArrayList messageObjects, boolean } pushDialogs.put(dialog_id, newCount); } - if (old_unread_count != total_unread_count) { + if (old_unread_count != total_unread_count || storiesUpdated) { delayedPushMessages.clear(); showOrUpdateNotification(notifyCheck); int pushDialogsCount = pushDialogs.size(); @@ -1070,6 +1203,9 @@ public void processNewMessages(ArrayList messageObjects, boolean } } } + if (storiesUpdated) { + updateStoryPushesRunnable(); + } if (countDownLatch != null) { countDownLatch.countDown(); } @@ -1078,7 +1214,11 @@ public void processNewMessages(ArrayList messageObjects, boolean private void appendMessage(MessageObject messageObject) { for (int i = 0; i < pushMessages.size(); i++) { - if (pushMessages.get(i).getId() == messageObject.getId() && pushMessages.get(i).getDialogId() == messageObject.getDialogId()) { + if ( + pushMessages.get(i).getId() == messageObject.getId() && + pushMessages.get(i).getDialogId() == messageObject.getDialogId() && + pushMessages.get(i).isStoryPush == messageObject.isStoryPush + ) { return; } } @@ -1214,7 +1354,7 @@ public void processDialogsUpdateRead(LongSparseIntArray dialogsToUpdate) { }); } - public void processLoadedUnreadMessages(LongSparseArray dialogs, ArrayList messages, ArrayList push, ArrayList users, ArrayList chats, ArrayList encryptedChats) { + public void processLoadedUnreadMessages(LongSparseArray dialogs, ArrayList messages, ArrayList push, ArrayList users, ArrayList chats, ArrayList encryptedChats, Collection storyPushes) { getMessagesController().putUsers(users, true); getMessagesController().putChats(chats, true); getMessagesController().putEncryptedChats(encryptedChats, true); @@ -1223,6 +1363,8 @@ public void processLoadedUnreadMessages(LongSparseArray dialogs, ArrayL pushDialogs.clear(); pushMessages.clear(); pushMessagesDict.clear(); + storyPushMessages.clear(); + storyPushMessagesDict.clear(); total_unread_count = 0; personalCount = 0; SharedPreferences preferences = getAccountInstance().getNotificationsSettings(); @@ -1388,6 +1530,20 @@ public void processLoadedUnreadMessages(LongSparseArray dialogs, ArrayL } } + if (storyPushes != null) { + for (StoryNotification notification : storyPushes) { + long dialogId = notification.dialogId; + StoryNotification oldNotification = storyPushMessagesDict.get(dialogId); + if (oldNotification != null) { + oldNotification.dateByIds.putAll(notification.dateByIds); + } else { + storyPushMessages.add(notification); + storyPushMessagesDict.put(dialogId, notification); + } + } + Collections.sort(storyPushMessages, Comparator.comparingLong(n -> n.date)); + } + int pushDialogsCount = pushDialogs.size(); AndroidUtilities.runOnUIThread(() -> { if (total_unread_count == 0) { @@ -1596,7 +1752,11 @@ private String getShortStringForMessage(MessageObject messageObject, String[] us if (dialogPreviewEnabled && (chat_id == 0 && fromId != 0 && preferences.getBoolean("EnablePreviewAll", true) || chat_id != 0 && (!isChannel && preferences.getBoolean("EnablePreviewGroup", true) || isChannel && preferences.getBoolean("EnablePreviewChannel", true)))) { if (messageObject.messageOwner instanceof TLRPC.TL_messageService) { userName[0] = null; - if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionGeoProximityReached) { + if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionSetSameChatWallPaper) { + return LocaleController.getString("WallpaperSameNotification", R.string.WallpaperSameNotification); + } else if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionSetChatWallPaper) { + return LocaleController.getString("WallpaperNotification", R.string.WallpaperNotification); + } else if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionGeoProximityReached) { return messageObject.messageText.toString(); } else if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionUserJoined || messageObject.messageOwner.action instanceof TLRPC.TL_messageActionContactSignUp) { return LocaleController.formatString("NotificationContactJoined", R.string.NotificationContactJoined, name); @@ -2024,6 +2184,13 @@ private String getShortStringForMessage(MessageObject messageObject, String[] us return LocaleController.getString("AttachDocument", R.string.AttachDocument); } } + } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaStory) { + TLRPC.TL_messageMediaStory storyMedia = (TLRPC.TL_messageMediaStory) messageObject.messageOwner.media; + if (storyMedia.via_mention) { + return LocaleController.formatString("StoryNotificationMention", R.string.StoryNotificationMention, userName[0] == null ? "" : userName[0]); + } else { + return LocaleController.getString("Story", R.string.Story); + } } else if (!TextUtils.isEmpty(messageObject.messageText)) { return replaceSpoilers(messageObject); } else { @@ -2045,8 +2212,11 @@ private String getShortStringForMessage(MessageObject messageObject, String[] us }; private String replaceSpoilers(MessageObject messageObject) { + if (messageObject == null || messageObject.messageOwner == null) { + return null; + } String text = messageObject.messageOwner.message; - if (text == null || messageObject == null || messageObject.messageOwner == null || messageObject.messageOwner.entities == null) { + if (text == null || messageObject.messageOwner.entities == null) { return null; } StringBuilder stringBuilder = new StringBuilder(text); @@ -2067,6 +2237,9 @@ private String getStringForMessage(MessageObject messageObject, boolean shortMes if (AndroidUtilities.needShowPasscode() || SharedConfig.isWaitingForPasscodeEnter) { return LocaleController.getString("YouHaveNewMessage", R.string.YouHaveNewMessage); } + if (messageObject.isStoryPush || messageObject.isStoryMentionPush) { + return "!" + messageObject.messageOwner.message; + } long dialogId = messageObject.messageOwner.dialog_id; long chatId = messageObject.messageOwner.peer_id.chat_id != 0 ? messageObject.messageOwner.peer_id.chat_id : messageObject.messageOwner.peer_id.channel_id; long fromId = messageObject.messageOwner.peer_id.user_id; @@ -2155,7 +2328,11 @@ private String getStringForMessage(MessageObject messageObject, boolean shortMes if (chatId == 0 && fromId != 0) { if (dialogPreviewEnabled && preferences.getBoolean("EnablePreviewAll", true)) { if (messageObject.messageOwner instanceof TLRPC.TL_messageService) { - if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionGeoProximityReached) { + if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionSetSameChatWallPaper) { + msg = LocaleController.getString("WallpaperSameNotification", R.string.WallpaperSameNotification); + } else if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionSetChatWallPaper) { + msg = LocaleController.getString("WallpaperNotification", R.string.WallpaperNotification); + } else if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionGeoProximityReached) { msg = messageObject.messageText.toString(); } else if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionUserJoined || messageObject.messageOwner.action instanceof TLRPC.TL_messageActionContactSignUp) { msg = LocaleController.formatString("NotificationContactJoined", R.string.NotificationContactJoined, name); @@ -2955,6 +3132,8 @@ public void deleteNotificationChannelGlobalInternal(int type, int what) { key = "channels"; } else if (type == TYPE_GROUP) { key = "groups"; + } else if (type == TYPE_STORIES) { + key = "stories"; } else { key = "private"; } @@ -2978,6 +3157,8 @@ public void deleteNotificationChannelGlobalInternal(int type, int what) { key = "channels_ia"; } else if (type == TYPE_GROUP) { key = "groups_ia"; + } else if (type == TYPE_STORIES) { + key = "stories_ia"; } else { key = "private_ia"; } @@ -2999,6 +3180,8 @@ public void deleteNotificationChannelGlobalInternal(int type, int what) { overwriteKey = "overwrite_channel"; } else if (type == TYPE_GROUP) { overwriteKey = "overwrite_group"; + } else if (type == TYPE_STORIES) { + overwriteKey = "overwrite_stories"; } else { overwriteKey = "overwrite_private"; } @@ -3050,7 +3233,7 @@ private boolean unsupportedNotificationShortcut() { } @SuppressLint("RestrictedApi") - private String createNotificationShortcut(NotificationCompat.Builder builder, long did, String name, TLRPC.User user, TLRPC.Chat chat, Person person) { + private String createNotificationShortcut(NotificationCompat.Builder builder, long did, String name, TLRPC.User user, TLRPC.Chat chat, Person person, boolean supportsBubble) { if (unsupportedNotificationShortcut() || ChatObject.isChannel(chat) && !chat.megagroup) { return null; } @@ -3101,15 +3284,17 @@ private String createNotificationShortcut(NotificationCompat.Builder builder, lo } else { icon = IconCompat.createWithResource(ApplicationLoader.applicationContext, R.drawable.book_group); } - if (!NekoConfig.disableNotificationBubbles.Bool()) { + if (supportsBubble && !NekoConfig.disableNotificationBubbles.Bool()) { NotificationCompat.BubbleMetadata.Builder bubbleBuilder = new NotificationCompat.BubbleMetadata.Builder( - PendingIntent.getActivity(ApplicationLoader.applicationContext, 0, intent, PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE), + PendingIntent.getActivity(ApplicationLoader.applicationContext, 0, intent, PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT), icon); bubbleBuilder.setSuppressNotification(openedDialogId == did); bubbleBuilder.setAutoExpandBubble(false); bubbleBuilder.setDesiredHeight(AndroidUtilities.dp(640)); builder.setBubbleMetadata(bubbleBuilder.build()); + } else { + builder.setBubbleMetadata(null); } return id; } catch (Exception e) { @@ -3181,6 +3366,7 @@ protected void ensureGroupsCreated() { String channelsId = "channels" + currentAccount; String groupsId = "groups" + currentAccount; String privateId = "private" + currentAccount; + String storiesId = "stories" + currentAccount; String otherId = "other" + currentAccount; for (int a = 0, N = list.size(); a < N; a++) { String id = list.get(a).getId(); @@ -3188,17 +3374,19 @@ protected void ensureGroupsCreated() { channelsId = null; } else if (groupsId != null && groupsId.equals(id)) { groupsId = null; + } else if (storiesId != null && storiesId.equals(id)) { + storiesId = null; } else if (privateId != null && privateId.equals(id)) { privateId = null; } else if (otherId != null && otherId.equals(id)) { otherId = null; } - if (channelsId == null && groupsId == null && privateId == null && otherId == null) { + if (channelsId == null && storiesId == null && groupsId == null && privateId == null && otherId == null) { break; } } - if (channelsId != null || groupsId != null || privateId != null || otherId != null) { + if (channelsId != null || groupsId != null || storiesId != null || privateId != null || otherId != null) { TLRPC.User user = getMessagesController().getUser(getUserConfig().getClientUserId()); if (user == null) { getUserConfig().getCurrentUser(); @@ -3217,6 +3405,9 @@ protected void ensureGroupsCreated() { if (groupsId != null) { channelGroups.add(new NotificationChannelGroup(groupsId, LocaleController.getString("NotificationsGroups", R.string.NotificationsGroups) + userName)); } + if (storiesId != null) { + channelGroups.add(new NotificationChannelGroup(storiesId, LocaleController.getString("NotificationsStories", R.string.NotificationsStories) + userName)); + } if (privateId != null) { channelGroups.add(new NotificationChannelGroup(privateId, LocaleController.getString("NotificationsPrivateChats", R.string.NotificationsPrivateChats) + userName)); } @@ -3250,6 +3441,9 @@ private String validateChannelId(long dialogId, int topicId, String name, long[] } else if (type == TYPE_GROUP) { groupId = "groups" + currentAccount; overwriteKey = "overwrite_group"; + } else if (type == TYPE_STORIES) { + groupId = "stories" + currentAccount; + overwriteKey = "overwrite_stories"; } else { groupId = "private" + currentAccount; overwriteKey = "overwrite_private"; @@ -3259,7 +3453,8 @@ private String validateChannelId(long dialogId, int topicId, String name, long[] boolean secretChat = !isDefault && DialogObject.isEncryptedDialog(dialogId); boolean shouldOverwrite = !isInApp && overwriteKey != null && preferences.getBoolean(overwriteKey, false); - String soundHash = Utilities.MD5(sound == null ? "NoSound" : sound.toString()); + int nosoundPatch = 2; // when changing code here about no-sound issues, make sure to increment this value + String soundHash = Utilities.MD5(sound == null ? "NoSound" + nosoundPatch : sound.toString()); if (soundHash != null && soundHash.length() > 5) { soundHash = soundHash.substring(0, 5); } @@ -3272,6 +3467,8 @@ private String validateChannelId(long dialogId, int topicId, String name, long[] key = isInApp ? "channels_ia" : "channels"; } else if (type == TYPE_GROUP) { key = isInApp ? "groups_ia" : "groups"; + } else if (type == TYPE_STORIES) { + key = isInApp ? "stories_ia" : "stories"; } else { key = isInApp ? "private_ia" : "private"; } @@ -3328,12 +3525,20 @@ private String validateChannelId(long dialogId, int topicId, String name, long[] editor = preferences.edit(); if (isDefault) { if (!isInApp) { - editor.putInt(getGlobalNotificationsKey(type), Integer.MAX_VALUE); + if (type == TYPE_STORIES) { + editor.putBoolean("EnableAllStories", false); + } else { + editor.putInt(getGlobalNotificationsKey(type), Integer.MAX_VALUE); + } updateServerNotificationsSettings(type); } } else { - editor.putInt("notify2_" + NotificationsController.getSharedPrefKey(dialogId, 0), 2); - updateServerNotificationsSettings(dialogId, 0,true); + if (type == TYPE_STORIES) { + editor.putBoolean("stories_" + NotificationsController.getSharedPrefKey(dialogId, 0), false); + } else { + editor.putInt("notify2_" + NotificationsController.getSharedPrefKey(dialogId, 0), 2); + } + updateServerNotificationsSettings(dialogId, 0, true); } edited = true; } else if (channelImportance != importance) { @@ -3350,18 +3555,28 @@ private String validateChannelId(long dialogId, int topicId, String name, long[] priority = 0; } if (isDefault) { - editor.putInt(getGlobalNotificationsKey(type), 0).commit(); + if (type == TYPE_STORIES) { + editor.putBoolean("EnableAllStories", true); + } else { + editor.putInt(getGlobalNotificationsKey(type), 0); + } if (type == TYPE_CHANNEL) { editor.putInt("priority_channel", priority); } else if (type == TYPE_GROUP) { editor.putInt("priority_group", priority); + } else if (type == TYPE_STORIES) { + editor.putInt("priority_stories", priority); } else { editor.putInt("priority_messages", priority); } } else { - editor.putInt("notify2_" + dialogId, 0); - editor.remove("notifyuntil_" + dialogId); - editor.putInt("priority_" + dialogId, priority); + if (type == TYPE_STORIES) { + editor.putBoolean("stories_" + dialogId, true); + } else { + editor.putInt("notify2_" + dialogId, 0); + editor.remove("notifyuntil_" + dialogId); + editor.putInt("priority_" + dialogId, priority); + } } } edited = true; @@ -3377,6 +3592,8 @@ private String validateChannelId(long dialogId, int topicId, String name, long[] editor.putInt("vibrate_channel", vibrate ? 0 : 2); } else if (type == TYPE_GROUP) { editor.putInt("vibrate_group", vibrate ? 0 : 2); + } else if (type == TYPE_STORIES) { + editor.putInt("vibrate_stories", vibrate ? 0 : 2); } else { editor.putInt("vibrate_messages", vibrate ? 0 : 2); } @@ -3397,6 +3614,8 @@ private String validateChannelId(long dialogId, int topicId, String name, long[] editor.putInt("ChannelLed", channelLedColor); } else if (type == TYPE_GROUP) { editor.putInt("GroupLed", channelLedColor); + } else if (type == TYPE_STORIES) { + editor.putInt("StoriesLed", channelLedColor); } else { editor.putInt("MessagesLed", channelLedColor); } @@ -3477,7 +3696,8 @@ private String validateChannelId(long dialogId, int topicId, String name, long[] if (sound != null) { notificationChannel.setSound(sound, builder.build()); } else { - // notificationChannel.setSound(null, null); + // todo: deal with vendor messed up crash here later + notificationChannel.setSound(null, builder.build()); } systemNotificationManager.createNotificationChannel(notificationChannel); if (BuildVars.LOGS_ENABLED) { @@ -3491,23 +3711,93 @@ private String validateChannelId(long dialogId, int topicId, String name, long[] } private void showOrUpdateNotification(boolean notifyAboutLast) { - if (!getUserConfig().isClientActivated() || pushMessages.isEmpty() || !SharedConfig.showNotificationsForAllAccounts && currentAccount != UserConfig.selectedAccount) { + if (!getUserConfig().isClientActivated() || pushMessages.isEmpty() && storyPushMessages.isEmpty() || !SharedConfig.showNotificationsForAllAccounts && currentAccount != UserConfig.selectedAccount) { dismissNotification(); return; } try { getConnectionsManager().resumeNetworkMaybe(); - MessageObject lastMessageObject = pushMessages.get(0); + Object lastNotification = null; + long maxDate = 0; + for (int i = 0; i < pushMessages.size(); ++i) { + MessageObject message = pushMessages.get(i); + if (maxDate < message.messageOwner.date) { + lastNotification = message; + maxDate = message.messageOwner.date; + } + } + for (int i = 0; i < storyPushMessages.size(); ++i) { + StoryNotification n = storyPushMessages.get(i); + if (maxDate < n.date / 1000L) { + lastNotification = n; + maxDate = n.date / 1000L; + } + } + if (lastNotification == null) { + return; + } + + Bitmap largeBitmap = null; + MessageObject lastMessageObject; + if (lastNotification instanceof StoryNotification) { + StoryNotification lastStoryNotification = (StoryNotification) lastNotification; + TLRPC.TL_message msg = new TLRPC.TL_message(); + msg.date = (int) (System.currentTimeMillis() / 1000L); + int storiesCount = 0; + boolean hidden = false; + for (int i = 0; i < storyPushMessages.size(); ++i) { + hidden |= storyPushMessages.get(i).hidden; + msg.date = Math.min(msg.date, (int) (storyPushMessages.get(i).date / 1000L)); + storiesCount += storyPushMessages.get(i).dateByIds.size(); + } + TLRPC.TL_peerUser peer = new TLRPC.TL_peerUser(); + msg.dialog_id = peer.user_id = lastStoryNotification.dialogId; + msg.peer_id = peer; + ArrayList names = new ArrayList<>(); + ArrayList avatars = new ArrayList<>(); + parseStoryPushes(names, avatars); + if (SharedConfig.getDevicePerformanceClass() >= SharedConfig.PERFORMANCE_CLASS_AVERAGE) { + largeBitmap = loadMultipleAvatars(avatars); + } + String name; + if (hidden || storyPushMessages.size() >= 2 || names.isEmpty()) { + name = LocaleController.formatPluralString("Stories", storiesCount); + } else { + name = names.get(0); + } + if (hidden) { + msg.message = LocaleController.formatPluralString("StoryNotificationHidden", storiesCount); + } else if (names.isEmpty()) { + msg.message = ""; + } else if (names.size() == 1) { + if (storiesCount == 1) { + msg.message = LocaleController.getString("StoryNotificationSingle"); + } else { + msg.message = LocaleController.formatPluralString("StoryNotification1", storiesCount, names.get(0)); + } + } else if (names.size() == 2) { + msg.message = LocaleController.formatString(R.string.StoryNotification2, names.get(0), names.get(1)); + } else if (names.size() == 3 && storyPushMessages.size() == 3) { + msg.message = LocaleController.formatString(R.string.StoryNotification3, cutLastName(names.get(0)), cutLastName(names.get(1)), cutLastName(names.get(2))); + } else { + msg.message = LocaleController.formatPluralString("StoryNotification4", storyPushMessages.size() - 2, cutLastName(names.get(0)), cutLastName(names.get(1))); + } + lastMessageObject = new MessageObject(currentAccount, msg, msg.message, name, name, false, false, false, false); + lastMessageObject.isStoryPush = true; + } else { + lastMessageObject = pushMessages.get(0); + } SharedPreferences preferences = getAccountInstance().getNotificationsSettings(); int dismissDate = preferences.getInt("dismissDate", 0); - if (lastMessageObject.messageOwner.date <= dismissDate) { + if (!lastMessageObject.isStoryPush && lastMessageObject.messageOwner.date <= dismissDate) { dismissNotification(); return; } long dialog_id = lastMessageObject.getDialogId(); int topicId = MessageObject.getTopicId(lastMessageObject.messageOwner, getMessagesController().isForum(lastMessageObject)); + boolean story = lastMessageObject.isStoryPush; boolean isChannel = false; long override_dialog_id = dialog_id; @@ -3597,11 +3887,10 @@ private void showOrUpdateNotification(boolean notifyAboutLast) { int silent = 2; String lastMessage = null; boolean hasNewMessages = false; - if (pushMessages.size() == 1) { - MessageObject messageObject = pushMessages.get(0); + if (pushMessages.size() <= 1) { boolean[] text = new boolean[1]; - String message = lastMessage = getStringForMessage(messageObject, false, text, null); - silent = isSilentMessage(messageObject) ? 1 : 0; + String message = lastMessage = getStringForMessage(lastMessageObject, false, text, null); + silent = isSilentMessage(lastMessageObject) ? 1 : 0; if (message == null) { return; } @@ -3627,7 +3916,7 @@ private void showOrUpdateNotification(boolean notifyAboutLast) { for (int i = 0; i < count; i++) { MessageObject messageObject = pushMessages.get(i); String message = getStringForMessage(messageObject, false, text, null); - if (message == null || messageObject.messageOwner.date <= dismissDate) { + if (message == null || !messageObject.isStoryPush && messageObject.messageOwner.date <= dismissDate) { continue; } if (silent == 2) { @@ -3756,17 +4045,17 @@ private void showOrUpdateNotification(boolean notifyAboutLast) { chatType = TYPE_GROUP; } } else if (userId != 0) { - long soundDocumentId = preferences.getLong("GlobalSoundDocId", 0); + long soundDocumentId = preferences.getLong(story ? "StoriesSoundDocId" : "GlobalSoundDocId", 0); if (soundDocumentId != 0) { isInternalSoundFile = true; soundPath = getMediaDataController().ringtoneDataStore.getSoundPath(soundDocumentId); } else { - soundPath = preferences.getString("GlobalSoundPath", defaultPath); + soundPath = preferences.getString(story ? "StoriesSoundPath" : "GlobalSoundPath", defaultPath); } vibrate = preferences.getInt("vibrate_messages", 0); importance = preferences.getInt("priority_messages", 1); ledColor = preferences.getInt("MessagesLed", 0xff0000ff); - chatType = TYPE_PRIVATE; + chatType = story ? TYPE_STORIES : TYPE_PRIVATE; } if (vibrate == 4) { vibrateOnlyIfSilent = true; @@ -3824,7 +4113,13 @@ private void showOrUpdateNotification(boolean notifyAboutLast) { intent.setAction("com.tmessages.openchat" + Math.random() + Integer.MAX_VALUE); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); //intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); - if (!DialogObject.isEncryptedDialog(dialog_id)) { + if (lastMessageObject.isStoryPush) { + long[] peerIds = new long[storyPushMessages.size()]; + for (int i = 0; i < storyPushMessages.size(); ++i) { + peerIds[i] = storyPushMessages.get(i).dialogId; + } + intent.putExtra("storyDialogIds", peerIds); + } else if (!DialogObject.isEncryptedDialog(dialog_id)) { if (pushDialogs.size() == 1) { if (chatId != 0) { intent.putExtra("chatId", chatId); @@ -3878,12 +4173,17 @@ private void showOrUpdateNotification(boolean notifyAboutLast) { Intent dismissIntent = new Intent(ApplicationLoader.applicationContext, NotificationDismissReceiver.class); dismissIntent.putExtra("messageDate", lastMessageObject.messageOwner.date); dismissIntent.putExtra("currentAccount", currentAccount); + if (lastMessageObject.isStoryPush) { + dismissIntent.putExtra("story", true); + } mBuilder.setDeleteIntent(PendingIntent.getBroadcast(ApplicationLoader.applicationContext, 1, dismissIntent, PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT)); } catch (Throwable e) { FileLog.e(e); } - if (photoPath != null) { + if (largeBitmap != null) { + mBuilder.setLargeIcon(largeBitmap); + } else if (photoPath != null) { BitmapDrawable img = ImageLoader.getInstance().getImageFromMemory(photoPath, null, "50_50"); if (img != null) { mBuilder.setLargeIcon(img.getBitmap()); @@ -3942,9 +4242,9 @@ private void showOrUpdateNotification(boolean notifyAboutLast) { } mBuilder.setTicker(lastMessage); } - if (soundPath != null && !soundPath.equals("NoSound")) { + if (soundPath != null && !soundPath.equalsIgnoreCase("NoSound")) { if (Build.VERSION.SDK_INT >= 26) { - if (soundPath.equals("Default") || soundPath.equals(defaultPath)) { + if (soundPath.equalsIgnoreCase("Default") || soundPath.equals(defaultPath)) { sound = Settings.System.DEFAULT_NOTIFICATION_URI; } else { if (isInternalSoundFile) { @@ -4055,15 +4355,19 @@ private void resetNotificationSound(NotificationCompat.Builder notificationBuild editor.putString("ChannelSound", ringtoneName); } else if (chatType == TYPE_GROUP) { editor.putString("GroupSound", ringtoneName); - } else { + } else if (chatType == TYPE_PRIVATE) { editor.putString("GlobalSound", ringtoneName); + } else if (chatType == TYPE_STORIES) { + editor.putString("StoriesSound", ringtoneName); } if (chatType == TYPE_CHANNEL) { editor.putString("ChannelSoundPath", newSound); } else if (chatType == TYPE_GROUP) { editor.putString("GroupSoundPath", newSound); - } else { + } else if (chatType == TYPE_PRIVATE) { editor.putString("GlobalSoundPath", newSound); + } else if (chatType == TYPE_STORIES) { + editor.putString("StoriesSoundPath", newSound); } getNotificationsController().deleteNotificationChannelGlobalInternal(chatType, -1); } else { @@ -4084,7 +4388,7 @@ private void showExtraNotifications(NotificationCompat.Builder notificationBuild notificationBuilder.setChannelId(validateChannelId(lastDialogId, lastTopicId, chatName, vibrationPattern, ledColor, sound, importance, isDefault, isInApp, isSilent, chatType)); } Notification mainNotification = notificationBuilder.build(); - if (Build.VERSION.SDK_INT < 18) { + if (Build.VERSION.SDK_INT <= 19) { notificationManager.notify(notificationId, mainNotification); if (BuildVars.LOGS_ENABLED) { FileLog.d("show summary notification by SDK check"); @@ -4095,13 +4399,16 @@ private void showExtraNotifications(NotificationCompat.Builder notificationBuild SharedPreferences preferences = getAccountInstance().getNotificationsSettings(); ArrayList sortedDialogs = new ArrayList<>(); + if (!storyPushMessages.isEmpty()) { + sortedDialogs.add(new DialogKey(0, 0, true)); + } LongSparseArray> messagesByDialogs = new LongSparseArray<>(); for (int a = 0; a < pushMessages.size(); a++) { MessageObject messageObject = pushMessages.get(a); long dialog_id = messageObject.getDialogId(); int topicId = MessageObject.getTopicId(messageObject.messageOwner, getMessagesController().isForum(messageObject)); int dismissDate = preferences.getInt("dismissDate" + dialog_id, 0); - if (messageObject.messageOwner.date <= dismissDate) { + if (!messageObject.isStoryPush && messageObject.messageOwner.date <= dismissDate) { continue; } @@ -4109,7 +4416,7 @@ private void showExtraNotifications(NotificationCompat.Builder notificationBuild if (arrayList == null) { arrayList = new ArrayList<>(); messagesByDialogs.put(dialog_id, arrayList); - sortedDialogs.add(new DialogKey(dialog_id, topicId)); + sortedDialogs.add(new DialogKey(dialog_id, topicId, false)); } arrayList.add(messageObject); } @@ -4124,18 +4431,20 @@ class NotificationHolder { int id; long dialogId; int topicId; + boolean story; String name; TLRPC.User user; TLRPC.Chat chat; NotificationCompat.Builder notification; - NotificationHolder(int i, long li, int topicId, String n, TLRPC.User u, TLRPC.Chat c, NotificationCompat.Builder builder) { + NotificationHolder(int i, long li, boolean story, int topicId, String n, TLRPC.User u, TLRPC.Chat c, NotificationCompat.Builder builder) { id = i; name = n; user = u; chat = c; notification = builder; dialogId = li; + this.story = story; this.topicId = topicId; } @@ -4154,7 +4463,7 @@ void call() { ArrayList holders = new ArrayList<>(); - boolean useSummaryNotification = Build.VERSION.SDK_INT <= Build.VERSION_CODES.O_MR1 || sortedDialogs.size() > 1; + boolean useSummaryNotification = Build.VERSION.SDK_INT <= Build.VERSION_CODES.O_MR1 || sortedDialogs.size() > (storyPushMessages.isEmpty() ? 1 : 2); if (useSummaryNotification && Build.VERSION.SDK_INT >= 26) { checkOtherNotificationsChannel(); } @@ -4170,19 +4479,38 @@ void call() { break; } DialogKey dialogKey = sortedDialogs.get(b); - long dialogId = dialogKey.dialogId; - int topicId = dialogKey.topicId; - ArrayList messageObjects = messagesByDialogs.get(dialogId); - int maxId = messageObjects.get(0).getId(); + long dialogId; + int topicId, maxId; + MessageObject lastMessageObject = null; + ArrayList messageObjects; + if (dialogKey.story) { + messageObjects = new ArrayList<>(); + if (storyPushMessages.isEmpty()) { + continue; + } + dialogId = storyPushMessages.get(0).dialogId; + topicId = 0; + maxId = 0; + for (int id : storyPushMessages.get(0).dateByIds.keySet()) { + maxId = Math.max(maxId, id); + } + } else { + dialogId = dialogKey.dialogId; + topicId = dialogKey.topicId; + messageObjects = messagesByDialogs.get(dialogId); + maxId = messageObjects.get(0).getId(); + lastMessageObject = messageObjects.get(0); + } Integer internalId = oldIdsWear.get(dialogId); - if (internalId == null) { + if (dialogKey.story) { + internalId = Integer.MAX_VALUE - 1; + } else if (internalId == null) { internalId = (int) dialogId + (int) (dialogId >> 32); } else { oldIdsWear.remove(dialogId); } - MessageObject lastMessageObject = messageObjects.get(0); int maxDate = 0; for (int i = 0; i < messageObjects.size(); i++) { if (maxDate < messageObjects.get(i).messageOwner.date) { @@ -4199,7 +4527,22 @@ void call() { File avatalFile = null; boolean canReply; - if (!DialogObject.isEncryptedDialog(dialogId)) { + if (dialogKey.story) { + canReply = false; + user = getMessagesController().getUser(dialogId); + if (storyPushMessages.size() == 1) { + if (user != null) { + name = UserObject.getFirstName(user); + } else { + name = storyPushMessages.get(0).localName; + } + } else { + name = LocaleController.formatPluralString("Stories", storyPushMessages.size()); + } + if (user != null && user.photo != null && user.photo.photo_small != null && user.photo.photo_small.volume_id != 0 && user.photo.photo_small.local_id != 0) { + photoPath = user.photo.photo_small; + } + } else if (!DialogObject.isEncryptedDialog(dialogId)) { canReply = dialogId != 777000; if (DialogObject.isUserDialog(dialogId)) { user = getMessagesController().getUser(dialogId); @@ -4346,7 +4689,12 @@ void call() { if (count == null) { count = 0; } - int n = Math.max(count, messageObjects.size()); + int n; + if (dialogKey.story) { + n = storyPushMessages.size(); + } else { + n = Math.max(count, messageObjects.size()); + } String conversationName; if (n <= 1 || Build.VERSION.SDK_INT >= 28) { conversationName = name; @@ -4373,7 +4721,7 @@ void call() { } } - boolean needAddPerson = !(lastMessageObject.messageOwner.action instanceof TLRPC.TL_messageActionChatJoinedByRequest); + boolean needAddPerson = lastMessageObject == null || !(lastMessageObject.messageOwner.action instanceof TLRPC.TL_messageActionChatJoinedByRequest); NotificationCompat.MessagingStyle messagingStyle; if (selfPerson != null && needAddPerson) { messagingStyle = new NotificationCompat.MessagingStyle(selfPerson); @@ -4390,207 +4738,244 @@ void call() { boolean[] preview = new boolean[1]; ArrayList rows = null; int rowsMid = 0; - for (int a = messageObjects.size() - 1; a >= 0; a--) { - MessageObject messageObject = messageObjects.get(a); - int messageTopicId = MessageObject.getTopicId(messageObject.messageOwner, getMessagesController().isForum(messageObject)); - if (topicId != messageTopicId) { - continue; - } - if (NekoConfig.ignoreBlocked.Bool() && getMessagesController().blockePeers.indexOfKey(messageObject.getSenderId()) >= 0) { + if (dialogKey.story) { + ArrayList names = new ArrayList<>(); + ArrayList avatars = new ArrayList<>(); + Pair pair = parseStoryPushes(names, avatars); + int storiesCount = pair.first; + boolean hidden = pair.second; + if (hidden) { + text.append(LocaleController.formatPluralString("StoryNotificationHidden", storiesCount)); + } else if (names.isEmpty()) { continue; - } - String message = getShortStringForMessage(messageObject, senderName, preview); - if (dialogId == selfUserId) { - senderName[0] = name; - } else if (DialogObject.isChatDialog(dialogId) && messageObject.messageOwner.from_scheduled) { - senderName[0] = LocaleController.getString("NotificationMessageScheduledName", R.string.NotificationMessageScheduledName); - } - if (message == null) { - if (BuildVars.LOGS_ENABLED) { - FileLog.w("message text is null for " + messageObject.getId() + " did = " + messageObject.getDialogId()); + } else if (names.size() == 1) { + if (storiesCount == 1) { + text.append(LocaleController.getString("StoryNotificationSingle")); + } else { + text.append(LocaleController.formatPluralString("StoryNotification1", storiesCount, names.get(0))); } - continue; + } else if (names.size() == 2) { + text.append(LocaleController.formatString(R.string.StoryNotification2, names.get(0), names.get(1))); + } else if (names.size() == 3 && storyPushMessages.size() == 3) { + text.append(LocaleController.formatString(R.string.StoryNotification3, cutLastName(names.get(0)), cutLastName(names.get(1)), cutLastName(names.get(2)))); + } else { + text.append(LocaleController.formatPluralString("StoryNotification4", storyPushMessages.size() - 2, cutLastName(names.get(0)), cutLastName(names.get(1)))); } - if (text.length() > 0) { - text.append("\n\n"); + long date = Long.MAX_VALUE; + for (int i = 0; i < storyPushMessages.size(); ++i) { + date = Math.min(storyPushMessages.get(i).date, date); } - if (dialogId != selfUserId && messageObject.messageOwner.from_scheduled && DialogObject.isUserDialog(dialogId)) { - message = String.format("%1$s: %2$s", LocaleController.getString("NotificationMessageScheduledName", R.string.NotificationMessageScheduledName), message); - text.append(message); + messagingStyle.setGroupConversation(false); + final String title = name = names.size() == 1 && !hidden ? names.get(0) : LocaleController.formatPluralString("Stories", storiesCount); + messagingStyle.addMessage(text, date, new Person.Builder().setName(title).build()); + if (!hidden) { + avatarBitmap = loadMultipleAvatars(avatars); } else { - if (senderName[0] != null) { - text.append(String.format("%1$s: %2$s", senderName[0], message)); - } else { + avatarBitmap = null; + } + } else { + for (int a = messageObjects.size() - 1; a >= 0; a--) { + MessageObject messageObject = messageObjects.get(a); + int messageTopicId = MessageObject.getTopicId(messageObject.messageOwner, getMessagesController().isForum(messageObject)); + if (topicId != messageTopicId) { + continue; + } + if (NekoConfig.ignoreBlocked.Bool() && getMessagesController().blockePeers.indexOfKey(messageObject.getSenderId()) >= 0) { + continue; + } + String message = getShortStringForMessage(messageObject, senderName, preview); + if (dialogId == selfUserId) { + senderName[0] = name; + } else if (DialogObject.isChatDialog(dialogId) && messageObject.messageOwner.from_scheduled) { + senderName[0] = LocaleController.getString("NotificationMessageScheduledName", R.string.NotificationMessageScheduledName); + } + if (message == null) { + if (BuildVars.LOGS_ENABLED) { + FileLog.w("message text is null for " + messageObject.getId() + " did = " + messageObject.getDialogId()); + } + continue; + } + if (text.length() > 0) { + text.append("\n\n"); + } + if (dialogId != selfUserId && messageObject.messageOwner.from_scheduled && DialogObject.isUserDialog(dialogId)) { + message = String.format("%1$s: %2$s", LocaleController.getString("NotificationMessageScheduledName", R.string.NotificationMessageScheduledName), message); text.append(message); + } else { + if (senderName[0] != null) { + text.append(String.format("%1$s: %2$s", senderName[0], message)); + } else { + text.append(message); + } } - } - long uid; - if (DialogObject.isUserDialog(dialogId)) { - uid = dialogId; - } else if (isChannel) { - uid = -dialogId; - } else if (DialogObject.isChatDialog(dialogId)) { - uid = messageObject.getSenderId(); - } else { - uid = dialogId; - } - Person person = personCache.get(uid + ((long) topicId << 16)); - CharSequence personName = ""; - if (senderName[0] == null) { - if (waitingForPasscode) { - if (DialogObject.isChatDialog(dialogId)) { - if (isChannel) { - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O_MR1) { - personName = LocaleController.getString("NotificationHiddenChatName", R.string.NotificationHiddenChatName); + long uid; + if (DialogObject.isUserDialog(dialogId)) { + uid = dialogId; + } else if (isChannel) { + uid = -dialogId; + } else if (DialogObject.isChatDialog(dialogId)) { + uid = messageObject.getSenderId(); + } else { + uid = dialogId; + } + Person person = personCache.get(uid + ((long) topicId << 16)); + CharSequence personName = ""; + if (senderName[0] == null) { + if (waitingForPasscode) { + if (DialogObject.isChatDialog(dialogId)) { + if (isChannel) { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O_MR1) { + personName = LocaleController.getString("NotificationHiddenChatName", R.string.NotificationHiddenChatName); + } + } else { + personName = LocaleController.getString("NotificationHiddenChatUserName", R.string.NotificationHiddenChatUserName); } - } else { - personName = LocaleController.getString("NotificationHiddenChatUserName", R.string.NotificationHiddenChatUserName); + } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O_MR1) { + personName = LocaleController.getString("NotificationHiddenName", R.string.NotificationHiddenName); } - } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O_MR1) { - personName = LocaleController.getString("NotificationHiddenName", R.string.NotificationHiddenName); } + } else { + personName = senderName[0]; } - } else { - personName = senderName[0]; - } - if (person == null || !TextUtils.equals(person.getName(), personName)) { - Person.Builder personBuilder = new Person.Builder().setName(personName); - if (preview[0] && !DialogObject.isEncryptedDialog(dialogId) && Build.VERSION.SDK_INT >= 28) { - File avatar = null; - if (DialogObject.isUserDialog(dialogId) || isChannel) { - avatar = avatalFile; - } else { - long fromId = messageObject.getSenderId(); - TLRPC.User sender = getMessagesController().getUser(fromId); - if (sender == null) { - sender = getMessagesStorage().getUserSync(fromId); - if (sender != null) { - getMessagesController().putUser(sender, true); + if (person == null || !TextUtils.equals(person.getName(), personName)) { + Person.Builder personBuilder = new Person.Builder().setName(personName); + if (preview[0] && !DialogObject.isEncryptedDialog(dialogId) && Build.VERSION.SDK_INT >= 28) { + File avatar = null; + if (DialogObject.isUserDialog(dialogId) || isChannel) { + avatar = avatalFile; + } else { + long fromId = messageObject.getSenderId(); + TLRPC.User sender = getMessagesController().getUser(fromId); + if (sender == null) { + sender = getMessagesStorage().getUserSync(fromId); + if (sender != null) { + getMessagesController().putUser(sender, true); + } + } + if (sender != null && sender.photo != null && sender.photo.photo_small != null && sender.photo.photo_small.volume_id != 0 && sender.photo.photo_small.local_id != 0) { + avatar = getFileLoader().getPathToAttach(sender.photo.photo_small, true); } } - if (sender != null && sender.photo != null && sender.photo.photo_small != null && sender.photo.photo_small.volume_id != 0 && sender.photo.photo_small.local_id != 0) { - avatar = getFileLoader().getPathToAttach(sender.photo.photo_small, true); - } + loadRoundAvatar(avatar, personBuilder); } - loadRoundAvatar(avatar, personBuilder); + person = personBuilder.build(); + personCache.put(uid, person); } - person = personBuilder.build(); - personCache.put(uid, person); - } - if (!DialogObject.isEncryptedDialog(dialogId)) { - boolean setPhoto = false; - if (preview[0] && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && !((ActivityManager) ApplicationLoader.applicationContext.getSystemService(Context.ACTIVITY_SERVICE)).isLowRamDevice()) { - if (!waitingForPasscode && !messageObject.isSecretMedia() && (messageObject.type == MessageObject.TYPE_PHOTO || messageObject.isSticker())) { - File attach = getFileLoader().getPathToMessage(messageObject.messageOwner); - File blurredAttach; - if (attach.exists() && messageObject.hasMediaSpoilers()) { - blurredAttach = new File(attach.getParentFile(), attach.getName() + ".blur.jpg"); - if (!blurredAttach.exists()) { - try { - Bitmap bitmap = BitmapFactory.decodeFile(attach.getAbsolutePath()); + if (!DialogObject.isEncryptedDialog(dialogId)) { + boolean setPhoto = false; + if (preview[0] && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && !((ActivityManager) ApplicationLoader.applicationContext.getSystemService(Context.ACTIVITY_SERVICE)).isLowRamDevice()) { + if (!waitingForPasscode && !messageObject.isSecretMedia() && (messageObject.type == MessageObject.TYPE_PHOTO || messageObject.isSticker())) { + File attach = getFileLoader().getPathToMessage(messageObject.messageOwner); + File blurredAttach; + if (attach.exists() && messageObject.hasMediaSpoilers()) { + blurredAttach = new File(attach.getParentFile(), attach.getName() + ".blur.jpg"); + if (!blurredAttach.exists()) { + try { + Bitmap bitmap = BitmapFactory.decodeFile(attach.getAbsolutePath()); - Bitmap blurBitmap = Utilities.stackBlurBitmapMax(bitmap); - bitmap.recycle(); + Bitmap blurBitmap = Utilities.stackBlurBitmapMax(bitmap); + bitmap.recycle(); - Bitmap scaledBitmap = Bitmap.createScaledBitmap(blurBitmap, bitmap.getWidth(), bitmap.getHeight(), true); - Utilities.stackBlurBitmap(scaledBitmap, 5); - blurBitmap.recycle(); + Bitmap scaledBitmap = Bitmap.createScaledBitmap(blurBitmap, bitmap.getWidth(), bitmap.getHeight(), true); + Utilities.stackBlurBitmap(scaledBitmap, 5); + blurBitmap.recycle(); - Canvas canvas = new Canvas(scaledBitmap); - int sColor = Color.WHITE; - mediaSpoilerEffect.setColor(ColorUtils.setAlphaComponent(sColor, (int) (Color.alpha(sColor) * 0.325f))); - mediaSpoilerEffect.setBounds(0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight()); - mediaSpoilerEffect.draw(canvas); + Canvas canvas = new Canvas(scaledBitmap); + int sColor = Color.WHITE; + mediaSpoilerEffect.setColor(ColorUtils.setAlphaComponent(sColor, (int) (Color.alpha(sColor) * 0.325f))); + mediaSpoilerEffect.setBounds(0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight()); + mediaSpoilerEffect.draw(canvas); - FileOutputStream fos = new FileOutputStream(blurredAttach); - scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos); - fos.close(); + FileOutputStream fos = new FileOutputStream(blurredAttach); + scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos); + fos.close(); - scaledBitmap.recycle(); + scaledBitmap.recycle(); - attach = blurredAttach; + attach = blurredAttach; + } catch (Exception e) { + FileLog.e(e); + } + } + } else { + blurredAttach = null; + } + NotificationCompat.MessagingStyle.Message msg = new NotificationCompat.MessagingStyle.Message(message, ((long) messageObject.messageOwner.date) * 1000L, person); + String mimeType = messageObject.isSticker() ? "image/webp" : "image/jpeg"; + Uri uri; + if (attach.exists()) { + try { + uri = FileProvider.getUriForFile(ApplicationLoader.applicationContext, ApplicationLoader.getApplicationId() + ".provider", attach); } catch (Exception e) { FileLog.e(e); + uri = null; } - } - } else { - blurredAttach = null; - } - NotificationCompat.MessagingStyle.Message msg = new NotificationCompat.MessagingStyle.Message(message, ((long) messageObject.messageOwner.date) * 1000L, person); - String mimeType = messageObject.isSticker() ? "image/webp" : "image/jpeg"; - Uri uri; - if (attach.exists()) { - try { - uri = FileProvider.getUriForFile(ApplicationLoader.applicationContext, ApplicationLoader.getApplicationId() + ".provider", attach); - } catch (Exception e) { - FileLog.e(e); + } else if (getFileLoader().isLoadingFile(attach.getName())) { + Uri.Builder _uri = new Uri.Builder() + .scheme("content") + .authority(NotificationImageProvider.getAuthority()) + .appendPath("msg_media_raw") + .appendPath(currentAccount + "") + .appendPath(attach.getName()) + .appendQueryParameter("final_path", attach.getAbsolutePath()); + uri = _uri.build(); + } else { uri = null; } - } else if (getFileLoader().isLoadingFile(attach.getName())) { - Uri.Builder _uri = new Uri.Builder() - .scheme("content") - .authority(NotificationImageProvider.getAuthority()) - .appendPath("msg_media_raw") - .appendPath(currentAccount + "") - .appendPath(attach.getName()) - .appendQueryParameter("final_path", attach.getAbsolutePath()); - uri = _uri.build(); - } else { - uri = null; - } - if (uri != null) { - msg.setData(mimeType, uri); - messagingStyle.addMessage(msg); - Uri uriFinal = uri; - ApplicationLoader.applicationContext.grantUriPermission("com.android.systemui", uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); - AndroidUtilities.runOnUIThread(() -> { - ApplicationLoader.applicationContext.revokeUriPermission(uriFinal, Intent.FLAG_GRANT_READ_URI_PERMISSION); - if (blurredAttach != null) { - blurredAttach.delete(); - } - }, 20000); + if (uri != null) { + msg.setData(mimeType, uri); + messagingStyle.addMessage(msg); + Uri uriFinal = uri; + ApplicationLoader.applicationContext.grantUriPermission("com.android.systemui", uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); + AndroidUtilities.runOnUIThread(() -> { + ApplicationLoader.applicationContext.revokeUriPermission(uriFinal, Intent.FLAG_GRANT_READ_URI_PERMISSION); + if (blurredAttach != null) { + blurredAttach.delete(); + } + }, 20000); - if (!TextUtils.isEmpty(messageObject.caption)) { - messagingStyle.addMessage(messageObject.caption, ((long) messageObject.messageOwner.date) * 1000, person); + if (!TextUtils.isEmpty(messageObject.caption)) { + messagingStyle.addMessage(messageObject.caption, ((long) messageObject.messageOwner.date) * 1000, person); + } + setPhoto = true; } - setPhoto = true; } } - } - if (!setPhoto) { - messagingStyle.addMessage(message, ((long) messageObject.messageOwner.date) * 1000, person); - } - if (preview[0] && !waitingForPasscode && messageObject.isVoice()) { - List messages = messagingStyle.getMessages(); - if (!messages.isEmpty()) { - File f = getFileLoader().getPathToMessage(messageObject.messageOwner); - Uri uri; - if (Build.VERSION.SDK_INT >= 24) { - try { - uri = FileProvider.getUriForFile(ApplicationLoader.applicationContext, ApplicationLoader.getApplicationId() + ".provider", f); - } catch (Exception ignore) { - uri = null; + if (!setPhoto) { + messagingStyle.addMessage(message, ((long) messageObject.messageOwner.date) * 1000, person); + } + if (preview[0] && !waitingForPasscode && messageObject.isVoice()) { + List messages = messagingStyle.getMessages(); + if (!messages.isEmpty()) { + File f = getFileLoader().getPathToMessage(messageObject.messageOwner); + Uri uri; + if (Build.VERSION.SDK_INT >= 24) { + try { + uri = FileProvider.getUriForFile(ApplicationLoader.applicationContext, ApplicationLoader.getApplicationId() + ".provider", f); + } catch (Exception ignore) { + uri = null; + } + } else { + uri = Uri.fromFile(f); + } + if (uri != null) { + NotificationCompat.MessagingStyle.Message addedMessage = messages.get(messages.size() - 1); + addedMessage.setData("audio/ogg", uri); } - } else { - uri = Uri.fromFile(f); - } - if (uri != null) { - NotificationCompat.MessagingStyle.Message addedMessage = messages.get(messages.size() - 1); - addedMessage.setData("audio/ogg", uri); } } + } else { + messagingStyle.addMessage(message, ((long) messageObject.messageOwner.date) * 1000, person); } - } else { - messagingStyle.addMessage(message, ((long) messageObject.messageOwner.date) * 1000, person); - } - if (dialogId == 777000 && messageObject.messageOwner.reply_markup != null) { - rows = messageObject.messageOwner.reply_markup.rows; - rowsMid = messageObject.getId(); + if (dialogId == 777000 && messageObject.messageOwner.reply_markup != null) { + rows = messageObject.messageOwner.reply_markup.rows; + rowsMid = messageObject.getId(); + } } } @@ -4598,7 +4983,13 @@ void call() { intent.setAction("com.tmessages.openchat" + Math.random() + Integer.MAX_VALUE); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.addCategory(Intent.CATEGORY_LAUNCHER); - if (DialogObject.isEncryptedDialog(dialogId)) { + if (dialogKey.story) { + long[] peerIds = new long[storyPushMessages.size()]; + for (int i = 0; i < storyPushMessages.size(); ++i) { + peerIds[i] = storyPushMessages.get(i).dialogId; + } + intent.putExtra("storyDialogIds", peerIds); + } else if (DialogObject.isEncryptedDialog(dialogId)) { intent.putExtra("encId", DialogObject.getEncryptedChatId(dialogId)); } else if (DialogObject.isUserDialog(dialogId)) { intent.putExtra("userId", dialogId); @@ -4649,14 +5040,22 @@ void call() { } wearableExtender.setBridgeTag("tgaccount" + selfUserId); - long date = ((long) messageObjects.get(0).messageOwner.date) * 1000; + long date; + if (dialogKey.story) { + date = Long.MAX_VALUE; + for (int i = 0; i < storyPushMessages.size(); ++i) { + date = Math.min(storyPushMessages.get(i).date, date); + } + } else { + date = ((long) messageObjects.get(0).messageOwner.date) * 1000; + } NotificationCompat.Builder builder = new NotificationCompat.Builder(ApplicationLoader.applicationContext) .setContentTitle(name) .setSmallIcon(getNotificationIconResId()) .setContentText(text.toString()) .setAutoCancel(true) - .setNumber(messageObjects.size()) + .setNumber(dialogKey.story ? storyPushMessages.size() : messageObjects.size()) .setColor(NekoXConfig.getNotificationColor()) .setGroupSummary(false) .setWhen(date) @@ -4672,6 +5071,9 @@ void call() { dismissIntent.putExtra("messageDate", maxDate); dismissIntent.putExtra("dialogId", dialogId); dismissIntent.putExtra("currentAccount", currentAccount); + if (dialogKey.story) { + dismissIntent.putExtra("story", true); + } builder.setDeleteIntent(PendingIntent.getBroadcast(ApplicationLoader.applicationContext, internalId, dismissIntent, PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT)); } catch (Exception e) { FileLog.e(e); @@ -4685,10 +5087,10 @@ void call() { if (wearReplyAction != null) { builder.addAction(wearReplyAction); } - if (!waitingForPasscode) { + if (!waitingForPasscode && !dialogKey.story) { builder.addAction(readAction); } - if (sortedDialogs.size() == 1 && !TextUtils.isEmpty(summary)) { + if (sortedDialogs.size() == 1 && !TextUtils.isEmpty(summary) && !dialogKey.story) { builder.setSubText(summary); } if (DialogObject.isEncryptedDialog(dialogId)) { @@ -4726,7 +5128,7 @@ void call() { if (Build.VERSION.SDK_INT >= 26) { setNotificationChannel(mainNotification, builder, useSummaryNotification); } - holders.add(new NotificationHolder(internalId, dialogId, topicId, name, user, chat, builder)); + holders.add(new NotificationHolder(internalId, dialogId, dialogKey.story, topicId, name, user, chat, builder)); wearNotificationsIds.put(dialogId, internalId); } @@ -4763,7 +5165,7 @@ void call() { NotificationHolder holder = holders.get(a); ids.clear(); if (Build.VERSION.SDK_INT >= 29 && !DialogObject.isEncryptedDialog(holder.dialogId)) { - String shortcutId = createNotificationShortcut(holder.notification, holder.dialogId, holder.name, holder.user, holder.chat, personCache.get(holder.dialogId)); + String shortcutId = createNotificationShortcut(holder.notification, holder.dialogId, holder.name, holder.user, holder.chat, personCache.get(holder.dialogId), !holder.story); if (shortcutId != null) { ids.add(shortcutId); } @@ -4775,29 +5177,169 @@ void call() { } } - @TargetApi(Build.VERSION_CODES.P) - private void loadRoundAvatar(File avatar, Person.Builder personBuilder) { - if (avatar != null) { + private String cutLastName(String name) { + if (name == null) { + return null; + } + int index; + if ((index = name.indexOf(' ')) >= 0) { + return name.substring(0, index) + (name.endsWith("…") ? "…" : ""); + } + return name; + } + + private Pair parseStoryPushes(ArrayList names, ArrayList avatars) { + int storiesCount = 0; + boolean hidden = false; + final int count = Math.min(3, storyPushMessages.size()); + for (int i = 0; i < count; ++i) { + StoryNotification notification = storyPushMessages.get(i); + storiesCount += notification.dateByIds.size(); + hidden |= notification.hidden; + TLRPC.User user1 = getMessagesController().getUser(notification.dialogId); + if (user1 == null) { + user1 = getMessagesStorage().getUserSync(notification.dialogId); + if (user1 != null) { + getMessagesController().putUser(user1, true); + } + } + String username; + File avatar = null; + if (user1 != null) { + username = UserObject.getUserName(user1); + if (user1 != null && user1.photo != null && user1.photo.photo_small != null && user1.photo.photo_small.volume_id != 0 && user1.photo.photo_small.local_id != 0) { + File file = getFileLoader().getPathToAttach(user1.photo.photo_small, true); + if (!file.exists()) { + file = null; + if (user1.photo.photo_big != null) { + file = getFileLoader().getPathToAttach(user1.photo.photo_big, true); + } + if (file != null && !file.exists()) { + file = null; + } + } + if (file != null) { + avatar = file; + } + } + } else if (notification.localName != null) { + username = notification.localName; + } else { + continue; + } + if (username.length() > 50) { + username = username.substring(0, 25) + "…"; + } + names.add(username); + if (avatar == null && user1 != null) { + avatars.add(user1); + } else if (avatar != null) { + avatars.add(avatar); + } + } + if (hidden) { + avatars.clear(); + } + return new Pair<>(storiesCount, hidden); + } + + public static Person.Builder loadRoundAvatar(File avatar, Person.Builder personBuilder) { + if (avatar != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { try { - Bitmap bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(avatar), (decoder, info, src) -> decoder.setPostProcessor((canvas) -> { - Path path = new Path(); - path.setFillType(Path.FillType.INVERSE_EVEN_ODD); - int width = canvas.getWidth(); - int height = canvas.getHeight(); - path.addRoundRect(0, 0, width, height, width / 2, width / 2, Path.Direction.CW); - Paint paint = new Paint(); - paint.setAntiAlias(true); - paint.setColor(Color.TRANSPARENT); - paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); - canvas.drawPath(path, paint); - return PixelFormat.TRANSLUCENT; - })); + Bitmap bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(avatar), (decoder, info, src) -> { + decoder.setPostProcessor((canvas) -> { + Path path = new Path(); + path.setFillType(Path.FillType.INVERSE_EVEN_ODD); + int width = canvas.getWidth(); + int height = canvas.getHeight(); + path.addRoundRect(0, 0, width, height, width / 2, width / 2, Path.Direction.CW); + Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setColor(Color.TRANSPARENT); + paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); + canvas.drawPath(path, paint); + return PixelFormat.TRANSLUCENT; + }); + }); IconCompat icon = IconCompat.createWithBitmap(bitmap); personBuilder.setIcon(icon); } catch (Throwable ignore) { } } + return personBuilder; + } + + public static Bitmap loadMultipleAvatars(ArrayList avatars) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P || avatars == null || avatars.size() == 0) { + return null; + } + final int sz = AndroidUtilities.dp(64); + // TODO: cache that bitmap + final Bitmap finalBitmap = Bitmap.createBitmap(sz, sz, Bitmap.Config.ARGB_8888); + final Canvas canvas = new Canvas(finalBitmap); + final Matrix matrix = new Matrix(); + final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + final Paint clearPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + final Rect rect = new Rect(); + TextPaint textPaint = null; + clearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + final float s = avatars.size() == 1 ? 1f : avatars.size() == 2 ? .65f : .5f; + for (int i = 0; i < avatars.size(); ++i) { + try { + final float x = sz * (1 - s) / avatars.size() * (avatars.size() - 1 - i); + final float y = sz * (1 - s) / avatars.size() * i; + + canvas.drawCircle(x + sz * s / 2, y + sz * s / 2, sz * s / 2 + AndroidUtilities.dp(2), clearPaint); + + Object obj = avatars.get(i); + + if (obj instanceof File) { + final String path = ((File) avatars.get(i)).getAbsolutePath(); + BitmapFactory.Options opts = new BitmapFactory.Options(); + opts.inJustDecodeBounds = true; + BitmapFactory.decodeFile(path, opts); + opts.inSampleSize = StoryEntry.calculateInSampleSize(opts, (int) (sz * s), (int) (sz * s)); + opts.inJustDecodeBounds = false; + opts.inDither = true; + Bitmap avatarBitmap = BitmapFactory.decodeFile(path, opts); + + BitmapShader shader = new BitmapShader(avatarBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); + matrix.reset(); + matrix.postScale((sz * s) / avatarBitmap.getWidth(), (sz * s) / avatarBitmap.getHeight()); + matrix.postTranslate(x, y); + shader.setLocalMatrix(matrix); + paint.setShader(shader); + canvas.drawCircle(x + sz * s / 2, y + sz * s / 2, sz * s / 2, paint); + + avatarBitmap.recycle(); + } else if (obj instanceof TLRPC.User) { + TLRPC.User user = (TLRPC.User) obj; + int[] colors = new int[] { + Theme.getColor(Theme.keys_avatar_background[AvatarDrawable.getColorIndex(user.id)]), + Theme.getColor(Theme.keys_avatar_background2[AvatarDrawable.getColorIndex(user.id)]) + }; + LinearGradient shader = new LinearGradient(x, y, x, y + sz * s, colors, new float[] {0, 1}, Shader.TileMode.CLAMP); + paint.setShader(shader); + canvas.drawCircle(x + sz * s / 2, y + sz * s / 2, sz * s / 2, paint); + + if (textPaint == null) { + textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + textPaint.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + textPaint.setTextSize(sz * .25f); + textPaint.setColor(0xFFFFFFFF); + } + StringBuilder string = new StringBuilder(); + AvatarDrawable.getAvatarSymbols(user.first_name, user.last_name, null, string); + String text = string.toString(); + + textPaint.getTextBounds(text, 0, text.length(), rect); + canvas.drawText(text, x + sz * s / 2 - rect.width() / 2f - rect.left, y + sz * s / 2 - rect.height() / 2f - rect.top, textPaint); + } + + } catch (Throwable ignore) {} + } + return finalBitmap; } public void playOutChatSound() { @@ -4929,11 +5471,18 @@ public void updateServerNotificationsSettings(long dialogId, int topicId, boolea TLRPC.TL_account_updateNotifySettings req = new TLRPC.TL_account_updateNotifySettings(); req.settings = new TLRPC.TL_inputPeerNotifySettings(); + final String key = NotificationsController.getSharedPrefKey(dialogId, topicId); + req.settings.flags |= 1; - req.settings.show_previews = preferences.getBoolean("content_preview_" + NotificationsController.getSharedPrefKey(dialogId, topicId), true); + req.settings.show_previews = preferences.getBoolean("content_preview_" + key, true); req.settings.flags |= 2; - req.settings.silent = preferences.getBoolean("silent_" + NotificationsController.getSharedPrefKey(dialogId, topicId), false); + req.settings.silent = preferences.getBoolean("silent_" + key, false); + + if (preferences.contains("stories_" + key)) { + req.settings.flags |= 64; + req.settings.stories_muted = !preferences.getBoolean("stories_" + key, true); + } int mute_type = preferences.getInt("notify2_" + NotificationsController.getSharedPrefKey(dialogId, topicId), -1); if (mute_type != -1) { @@ -4953,7 +5502,7 @@ public void updateServerNotificationsSettings(long dialogId, int topicId, boolea ringtoneSound.id = soundDocumentId; req.settings.sound = ringtoneSound; } else if (soundPath != null) { - if (soundPath.equals("NoSound")){ + if (soundPath.equalsIgnoreCase("NoSound")) { req.settings.sound = new TLRPC.TL_notificationSoundNone(); } else { TLRPC.TL_notificationSoundLocal localSound = new TLRPC.TL_notificationSoundLocal(); @@ -4982,61 +5531,68 @@ public void updateServerNotificationsSettings(long dialogId, int topicId, boolea public final static int TYPE_GROUP = 0; public final static int TYPE_PRIVATE = 1; public final static int TYPE_CHANNEL = 2; + public final static int TYPE_STORIES = 3; public void updateServerNotificationsSettings(int type) { SharedPreferences preferences = getAccountInstance().getNotificationsSettings(); TLRPC.TL_account_updateNotifySettings req = new TLRPC.TL_account_updateNotifySettings(); req.settings = new TLRPC.TL_inputPeerNotifySettings(); req.settings.flags = 5; - String soundDocumentIdPref; - String soundPathPref; - String soundNamePref; if (type == TYPE_GROUP) { req.peer = new TLRPC.TL_inputNotifyChats(); req.settings.mute_until = preferences.getInt("EnableGroup2", 0); req.settings.show_previews = preferences.getBoolean("EnablePreviewGroup", true); - soundNamePref = "GroupSound"; - soundDocumentIdPref = "GroupSoundDocId"; - soundPathPref = "GroupSoundPath"; - } else if (type == TYPE_PRIVATE) { + + req.settings.flags |= 8; + req.settings.sound = getInputSound(preferences, "GroupSound", "GroupSoundDocId", "GroupSoundPath"); + } else if (type == TYPE_PRIVATE || type == TYPE_STORIES) { req.peer = new TLRPC.TL_inputNotifyUsers(); req.settings.mute_until = preferences.getInt("EnableAll2", 0); req.settings.show_previews = preferences.getBoolean("EnablePreviewAll", true); - soundNamePref = "GlobalSound"; - soundDocumentIdPref = "GlobalSoundDocId"; - soundPathPref = "GlobalSoundPath"; + + req.settings.flags |= 128; + req.settings.stories_hide_sender = preferences.getBoolean("EnableHideStoriesSenders", false); + if (preferences.contains("EnableAllStories")) { + req.settings.flags |= 64; + req.settings.stories_muted = !preferences.getBoolean("EnableAllStories", true); + } + + req.settings.flags |= 8; + req.settings.sound = getInputSound(preferences, "GlobalSound", "GlobalSoundDocId", "GlobalSoundPath"); + + req.settings.flags |= 256; + req.settings.stories_sound = getInputSound(preferences, "StoriesSound", "StoriesSoundDocId", "StoriesSoundPath"); } else { req.peer = new TLRPC.TL_inputNotifyBroadcasts(); req.settings.mute_until = preferences.getInt("EnableChannel2", 0); req.settings.show_previews = preferences.getBoolean("EnablePreviewChannel", true); - soundNamePref = "ChannelSound"; - soundDocumentIdPref = "ChannelSoundDocId"; - soundPathPref = "ChannelSoundPath"; + + req.settings.flags |= 8; + req.settings.sound = getInputSound(preferences, "ChannelSound", "ChannelSoundDocId", "ChannelSoundPath"); } - req.settings.flags |= 8; - long soundDocumentId = preferences.getLong(soundDocumentIdPref, 0); - String soundPath = preferences.getString(soundPathPref, "NoSound"); + getConnectionsManager().sendRequest(req, (response, error) -> { }); + } + private TLRPC.NotificationSound getInputSound(SharedPreferences preferences, String namePref, String docPref, String pathPref) { + long soundDocumentId = preferences.getLong(docPref, 0); + String soundPath = preferences.getString(pathPref, "NoSound"); if (soundDocumentId != 0) { TLRPC.TL_notificationSoundRingtone ringtoneSound = new TLRPC.TL_notificationSoundRingtone(); ringtoneSound.id = soundDocumentId; - req.settings.sound = ringtoneSound; + return ringtoneSound; } else if (soundPath != null) { - if (soundPath.equals("NoSound")){ - req.settings.sound = new TLRPC.TL_notificationSoundNone(); + if (soundPath.equalsIgnoreCase("NoSound")) { + return new TLRPC.TL_notificationSoundNone(); } else { TLRPC.TL_notificationSoundLocal localSound = new TLRPC.TL_notificationSoundLocal(); - localSound.title = preferences.getString(soundNamePref, null); + localSound.title = preferences.getString(namePref, null); localSound.data = soundPath; - req.settings.sound = localSound; + return localSound; } } else { - req.settings.sound = new TLRPC.TL_notificationSoundDefault(); + return new TLRPC.TL_notificationSoundDefault(); } - getConnectionsManager().sendRequest(req, (response, error) -> { - - }); } public boolean isGlobalNotificationsEnabled(long dialogId) { @@ -5067,6 +5623,9 @@ public boolean isGlobalNotificationsEnabled(long dialogId, Boolean forceChannel) } public boolean isGlobalNotificationsEnabled(int type) { + if (type == TYPE_STORIES) { + return getAccountInstance().getNotificationsSettings().getBoolean("EnableAllStories", true); + } return getAccountInstance().getNotificationsSettings().getInt(getGlobalNotificationsKey(type), 0) < getConnectionsManager().getCurrentTime(); } @@ -5160,10 +5719,89 @@ public void loadTopicsNotificationsExceptions(long dialogId, Consumer> dateByIds = new HashMap<>(); + boolean hidden; + + public long date; + + public StoryNotification(long dialogId, String localName, int id, long date) { + this(dialogId, localName, id, date, date + 86400000); + } + + public StoryNotification(long dialogId, String localName, int id, long date, long expire_date) { + this.dialogId = dialogId; + this.localName = localName; + this.dateByIds.put(id, new Pair<>(date, expire_date)); + this.date = date; + } + + public long getLeastDate() { + long minDate = -1; + for (Pair date : dateByIds.values()) { + if (minDate == -1 || minDate > date.first) { + minDate = date.first; + } + } + return minDate; + } + } + + private void checkStoryPushes() { + boolean changed = false; + final long now = System.currentTimeMillis(); + for (int i = 0; i < storyPushMessages.size(); ++i) { + StoryNotification push = storyPushMessages.get(i); + Iterator>> it = push.dateByIds.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry> e = it.next(); + long expire_date = e.getValue().second; + if (now >= expire_date) { + it.remove(); + changed = true; + } + } + if (changed) { + if (push.dateByIds.isEmpty()) { + getMessagesStorage().deleteStoryPushMessage(push.dialogId); + storyPushMessages.remove(i); + i--; + } else { + getMessagesStorage().putStoryPushMessage(push); + } + } + } + if (changed) { + showOrUpdateNotification(false); + } + updateStoryPushesRunnable(); + } + + private Runnable checkStoryPushesRunnable = this::checkStoryPushes; + + private void updateStoryPushesRunnable() { + long minChangeTime = Long.MAX_VALUE; + for (int i = 0; i < storyPushMessages.size(); ++i) { + StoryNotification push = storyPushMessages.get(i); + for (Pair d : push.dateByIds.values()) { + minChangeTime = Math.min(minChangeTime, d.second); + } + } + AndroidUtilities.cancelRunOnUIThread(checkStoryPushesRunnable); + long delay = minChangeTime - System.currentTimeMillis(); + if (minChangeTime != Long.MAX_VALUE) { + AndroidUtilities.runOnUIThread(checkStoryPushesRunnable, Math.max(0, delay)); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsDisabledReceiver.java b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsDisabledReceiver.java index bd1590b139..760605e4ab 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsDisabledReceiver.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsDisabledReceiver.java @@ -58,7 +58,7 @@ public void onReceive(Context context, Intent intent) { return; } if (BuildVars.LOGS_ENABLED) { - FileLog.d("apply channel " + channelId + " state"); + FileLog.d("apply channel{channel} " + channelId + " state"); } preferences.edit().putInt(NotificationsController.getGlobalNotificationsKey(NotificationsController.TYPE_CHANNEL), state ? Integer.MAX_VALUE : 0).commit(); AccountInstance.getInstance(account).getNotificationsController().updateServerNotificationsSettings(NotificationsController.TYPE_CHANNEL); @@ -68,7 +68,7 @@ public void onReceive(Context context, Intent intent) { return; } if (BuildVars.LOGS_ENABLED) { - FileLog.d("apply channel " + channelId + " state"); + FileLog.d("apply channel{groups} " + channelId + " state"); } preferences.edit().putInt(NotificationsController.getGlobalNotificationsKey(NotificationsController.TYPE_GROUP), state ? Integer.MAX_VALUE : 0).commit(); AccountInstance.getInstance(account).getNotificationsController().updateServerNotificationsSettings(NotificationsController.TYPE_GROUP); @@ -78,10 +78,20 @@ public void onReceive(Context context, Intent intent) { return; } if (BuildVars.LOGS_ENABLED) { - FileLog.d("apply channel " + channelId + " state"); + FileLog.d("apply channel{private} " + channelId + " state"); } preferences.edit().putInt(NotificationsController.getGlobalNotificationsKey(NotificationsController.TYPE_PRIVATE), state ? Integer.MAX_VALUE : 0).commit(); AccountInstance.getInstance(account).getNotificationsController().updateServerNotificationsSettings(NotificationsController.TYPE_PRIVATE); + } else if (args[1].startsWith("stories")) { + String currentChannel = preferences.getString("stories", null); + if (!channelId.equals(currentChannel)) { + return; + } + if (BuildVars.LOGS_ENABLED) { + FileLog.d("apply channel{stories} " + channelId + " state"); + } + preferences.edit().putBoolean(NotificationsController.getGlobalNotificationsKey(NotificationsController.TYPE_STORIES), !state).commit(); + AccountInstance.getInstance(account).getNotificationsController().updateServerNotificationsSettings(NotificationsController.TYPE_PRIVATE); } else { long dialogId = Utilities.parseLong(args[1]); if (dialogId == 0) { @@ -94,7 +104,7 @@ public void onReceive(Context context, Intent intent) { return; } if (BuildVars.LOGS_ENABLED) { - FileLog.d("apply channel " + channelId + " state"); + FileLog.d("apply channel{else} " + channelId + " state"); } SharedPreferences.Editor editor = preferences.edit(); editor.putInt("notify2_" + key, state ? 2 : 0); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsSettingsFacade.java b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsSettingsFacade.java index a828a455c9..1a14b51ac0 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsSettingsFacade.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsSettingsFacade.java @@ -6,6 +6,7 @@ import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLRPC; +import org.telegram.ui.NotificationsSoundActivity; public class NotificationsSettingsFacade { @@ -14,6 +15,7 @@ public class NotificationsSettingsFacade { public final static String PROPERTY_NOTIFY_UNTIL = "notifyuntil_"; public final static String PROPERTY_CONTENT_PREVIEW = "content_preview_"; public final static String PROPERTY_SILENT = "silent_"; + public final static String PROPERTY_STORIES_NOTIFY = "stories_"; private final int currentAccount; @@ -35,6 +37,7 @@ public void clearPreference(long dialogId, int topicId) { .remove(PROPERTY_NOTIFY_UNTIL + key) .remove(PROPERTY_CONTENT_PREVIEW + key) .remove(PROPERTY_SILENT + key) + .remove(PROPERTY_STORIES_NOTIFY + key) .apply(); } @@ -106,6 +109,11 @@ public void applyDialogNotificationsSettings(long dialogId, int topicId, TLRPC.P } else { editor.remove(PROPERTY_SILENT + key); } + if ((notify_settings.flags & 64) != 0) { + editor.putBoolean(PROPERTY_STORIES_NOTIFY + key, !notify_settings.stories_muted); + } else { + editor.remove(PROPERTY_STORIES_NOTIFY + key); + } TLRPC.Dialog dialog = null; if (topicId == 0) { @@ -192,6 +200,10 @@ public void applySoundSettings(TLRPC.NotificationSound settings, SharedPreferenc soundPref = "GroupSound"; soundDocPref = "GroupSoundDocId"; soundPathPref = "GroupSoundPath"; + } else if (globalType == NotificationsController.TYPE_STORIES) { + soundPref = "StoriesSound"; + soundDocPref = "StoriesSoundDocId"; + soundPathPref = "StoriesSoundPath"; } else if (globalType == TYPE_PRIVATE) { soundPref = "GlobalSound"; soundDocPref = "GlobalSoundDocId"; @@ -203,6 +215,23 @@ public void applySoundSettings(TLRPC.NotificationSound settings, SharedPreferenc } } + if (settings instanceof TLRPC.TL_notificationSoundLocal) { + TLRPC.TL_notificationSoundLocal localSound = (TLRPC.TL_notificationSoundLocal) settings; + if ("Default".equalsIgnoreCase(localSound.data)) { + settings = new TLRPC.TL_notificationSoundDefault(); + } else if ("NoSound".equalsIgnoreCase(localSound.data)) { + settings = new TLRPC.TL_notificationSoundNone(); + } else { + String path = NotificationsSoundActivity.findRingtonePathByName(localSound.title); + if (path == null) { +// settings = new TLRPC.TL_notificationSoundDefault(); + return; + } else { + localSound.data = path; + } + } + } + if (settings instanceof TLRPC.TL_notificationSoundDefault) { editor.putString(soundPref, "Default"); editor.putString(soundPathPref, "Default"); @@ -219,7 +248,7 @@ public void applySoundSettings(TLRPC.NotificationSound settings, SharedPreferenc } else if (settings instanceof TLRPC.TL_notificationSoundRingtone) { TLRPC.TL_notificationSoundRingtone soundRingtone = (TLRPC.TL_notificationSoundRingtone) settings; editor.putLong(soundDocPref, soundRingtone.id); - MediaDataController.getInstance(currentAccount).checkRingtones(); + MediaDataController.getInstance(currentAccount).checkRingtones(true); if (serverUpdate && dialogId != 0) { editor.putBoolean("custom_" + dialogId, true); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/PushListenerController.java b/TMessagesProj/src/main/java/org/telegram/messenger/PushListenerController.java index 6ec918c31b..990934a911 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/PushListenerController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/PushListenerController.java @@ -285,7 +285,7 @@ public static void processRemoteMessage(@PushType int pushType, String data, lon if (custom.has("topic_id")) { topicId = custom.getInt("topic_id"); } - FileLog.d( "recived push notification chatId " + chat_id + " custom topicId " + topicId); + FileLog.d( "recived push notification {"+ loc_key+ "} chatId " + chat_id + " custom topicId " + topicId); if (custom.has("encryption_id")) { dialogId = DialogObject.makeEncryptedDialogId(custom.getInt("encryption_id")); } @@ -324,6 +324,12 @@ public static void processRemoteMessage(@PushType int pushType, String data, lon updates.add(update); } MessagesController.getInstance(accountFinal).processUpdateArray(updates, null, null, false, 0); + } else if ("READ_STORIES".equals(loc_key)) { + int maxId = custom.getInt("max_id"); + NotificationsController.getInstance(currentAccount).processReadStories(dialogId, maxId); + } else if ("STORY_DELETED".equals(loc_key)) { + int storyId = custom.getInt("story_id"); + NotificationsController.getInstance(currentAccount).processDeleteStory(dialogId, storyId); } else if ("MESSAGE_DELETED".equals(loc_key)) { String messages = custom.getString("messages"); String[] messagesArgs = messages.split(","); @@ -392,6 +398,14 @@ public static void processRemoteMessage(@PushType int pushType, String data, lon processNotification = true; } + int story_id = -1; + if (loc_key.equals("STORY_NOTEXT") || loc_key.equals("STORY_HIDDEN_AUTHOR")) { + if (custom.has("story_id")) { + story_id = custom.getInt("story_id"); + } + processNotification = story_id >= 0; + } + if (processNotification) { long chat_from_id = custom.optLong("chat_from_id", 0); long chat_from_broadcast_id = custom.optLong("chat_from_broadcast_id", 0); @@ -413,14 +427,14 @@ public static void processRemoteMessage(@PushType int pushType, String data, lon } String messageText = null; String message1 = null; - String name = args[0]; + String name = args == null || args.length <= 0 ? null : args[0]; String userName = null; boolean localMessage = false; boolean supergroup = false; boolean pinned = false; boolean channel = false; boolean edited = custom.has("edit_date"); - if (loc_key.startsWith("CHAT_")) { + if (loc_key.startsWith("CHAT_") && args != null && args.length > 0) { if (UserObject.isReplyUser(dialogId)) { name += " @ " + args[1]; } else { @@ -435,13 +449,33 @@ public static void processRemoteMessage(@PushType int pushType, String data, lon channel = true; } - if (BuildVars.LOGS_ENABLED) { - FileLog.d(tag + " received message notification " + loc_key + " for dialogId = " + dialogId + " mid = " + msg_id); - } + if (loc_key.startsWith("REACT_") || loc_key.startsWith("CHAT_REACT_")) { messageText = getReactedText(loc_key, args); } else { switch (loc_key) { + case "STORY_NOTEXT": { + messageText = LocaleController.getString("StoryNotificationSingle"); + message1 = null; + msg_id = story_id; + break; + } + case "STORY_HIDDEN_AUTHOR": { + messageText = LocaleController.formatPluralString("StoryNotificationHidden", 1); + message1 = null; + msg_id = story_id; + break; + } + case "MESSAGE_SAME_WALLPAPER": { + messageText = LocaleController.formatString("ActionSetSameWallpaperForThisChat", R.string.ActionSetSameWallpaperForThisChat, args[0]); + message1 = LocaleController.getString("WallpaperSameNotification", R.string.WallpaperSameNotification); + break; + } + case "MESSAGE_WALLPAPER": { + messageText = LocaleController.formatString("ActionSetWallpaperForThisChat", R.string.ActionSetWallpaperForThisChat, args[0]); + message1 = LocaleController.getString("WallpaperNotification", R.string.WallpaperNotification); + break; + } case "MESSAGE_RECURRING_PAY": { messageText = LocaleController.formatString("NotificationMessageRecurringPay", R.string.NotificationMessageRecurringPay, args[0], args[1]); message1 = LocaleController.getString("PaymentInvoice", R.string.PaymentInvoice); @@ -458,6 +492,16 @@ public static void processRemoteMessage(@PushType int pushType, String data, lon message1 = LocaleController.getString("Message", R.string.Message); break; } + case "MESSAGE_STORY": { + messageText = LocaleController.formatString("NotificationStory", R.string.NotificationStory, args[0]); + message1 = LocaleController.getString("Story", R.string.Story); + break; + } + case "MESSAGE_STORY_MENTION": { + messageText = LocaleController.getString("StoryNotificationMention", R.string.StoryNotificationMention); + message1 = null; + break; + } case "MESSAGE_PHOTO": { messageText = LocaleController.formatString("NotificationMessagePhoto", R.string.NotificationMessagePhoto, args[0]); message1 = LocaleController.getString("AttachPhoto", R.string.AttachPhoto); @@ -587,6 +631,11 @@ public static void processRemoteMessage(@PushType int pushType, String data, lon message1 = LocaleController.getString("Message", R.string.Message); break; } + case "CHANNEL_MESSAGE_STORY": { + messageText = LocaleController.formatString("NotificationChannelStory", R.string.NotificationChannelStory, args[0]); + message1 = LocaleController.getString("Story", R.string.Story); + break; + } case "CHANNEL_MESSAGE_PHOTO": { messageText = LocaleController.formatString("ChannelMessagePhoto", R.string.ChannelMessagePhoto, args[0]); message1 = LocaleController.getString("AttachPhoto", R.string.AttachPhoto); @@ -697,6 +746,11 @@ public static void processRemoteMessage(@PushType int pushType, String data, lon message1 = LocaleController.getString("Message", R.string.Message); break; } + case "CHAT_MESSAGE_STORY": { + messageText = LocaleController.formatString("NotificationChatStory", R.string.NotificationChatStory, args[0]); + message1 = LocaleController.getString("Story", R.string.Story); + break; + } case "CHAT_MESSAGE_PHOTO": { messageText = LocaleController.formatString("NotificationMessageGroupPhoto", R.string.NotificationMessageGroupPhoto, args[0], args[1]); message1 = LocaleController.getString("AttachPhoto", R.string.AttachPhoto); @@ -1109,6 +1163,10 @@ public static void processRemoteMessage(@PushType int pushType, String data, lon } } } + + if (BuildVars.LOGS_ENABLED) { + FileLog.d(tag + " received message notification " + loc_key + " for dialogId = " + dialogId + " mid = " + msg_id); + } if (messageText != null) { TLRPC.TL_message messageOwner = new TLRPC.TL_message(); messageOwner.id = msg_id; @@ -1156,6 +1214,9 @@ public static void processRemoteMessage(@PushType int pushType, String data, lon messageObject.messageOwner.reply_to.reply_to_top_id = topicId; } messageObject.isReactionPush = loc_key.startsWith("REACT_") || loc_key.startsWith("CHAT_REACT_"); + messageObject.isStoryPush = loc_key.equals("STORY_NOTEXT") || loc_key.equals("STORY_HIDDEN_AUTHOR"); + messageObject.isStoryMentionPush = loc_key.equals("MESSAGE_STORY_MENTION"); + messageObject.isStoryPushHidden = loc_key.equals("STORY_HIDDEN_AUTHOR"); ArrayList arrayList = new ArrayList<>(); arrayList.add(messageObject); canRelease = false; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/RuntimeClassNameTypeAdapterFactory.java b/TMessagesProj/src/main/java/org/telegram/messenger/RuntimeClassNameTypeAdapterFactory.java new file mode 100644 index 0000000000..9586f9dce9 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/RuntimeClassNameTypeAdapterFactory.java @@ -0,0 +1,282 @@ +package org.telegram.messenger; + +import com.google.gson.ExclusionStrategy; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.internal.Streams; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; + +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + *

+ * Disclaimer: taken from here https://stackoverflow.com/a/40133286/285091 with some modifications + *

+ * + * Adapts values whose runtime type may differ from their declaration type. This + * is necessary when a field's type is not the same type that GSON should create + * when deserializing that field. For example, consider these types: + *
   {@code
+ *   abstract class Shape {
+ *     int x;
+ *     int y;
+ *   }
+ *   class Circle extends Shape {
+ *     int radius;
+ *   }
+ *   class Rectangle extends Shape {
+ *     int width;
+ *     int height;
+ *   }
+ *   class Diamond extends Shape {
+ *     int width;
+ *     int height;
+ *   }
+ *   class Drawing {
+ *     Shape bottomShape;
+ *     Shape topShape;
+ *   }
+ * }
+ *

Without additional type information, the serialized JSON is ambiguous. Is + * the bottom shape in this drawing a rectangle or a diamond?

   {@code
+ *   {
+ *     "bottomShape": {
+ *       "width": 10,
+ *       "height": 5,
+ *       "x": 0,
+ *       "y": 0
+ *     },
+ *     "topShape": {
+ *       "radius": 2,
+ *       "x": 4,
+ *       "y": 1
+ *     }
+ *   }}
+ * This class addresses this problem by adding type information to the + * serialized JSON and honoring that type information when the JSON is + * deserialized:
   {@code
+ *   {
+ *     "bottomShape": {
+ *       "type": "Diamond",
+ *       "width": 10,
+ *       "height": 5,
+ *       "x": 0,
+ *       "y": 0
+ *     },
+ *     "topShape": {
+ *       "type": "Circle",
+ *       "radius": 2,
+ *       "x": 4,
+ *       "y": 1
+ *     }
+ *   }}
+ * Both the type field name ({@code "type"}) and the type labels ({@code + * "Rectangle"}) are configurable. + *

+ *

Registering Types

+ * Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field + * name to the {@link #of} factory method. If you don't supply an explicit type + * field name, {@code "type"} will be used.
   {@code
+ *   RuntimeTypeAdapterFactory shapeAdapterFactory
+ *       = RuntimeTypeAdapterFactory.of(Shape.class, "type");
+ * }
+ * Next register all of your subtypes. Every subtype must be explicitly + * registered. This protects your application from injection attacks. If you + * don't supply an explicit type label, the type's simple name will be used. + *
   {@code
+ *   shapeAdapter.registerSubtype(Rectangle.class, "Rectangle");
+ *   shapeAdapter.registerSubtype(Circle.class, "Circle");
+ *   shapeAdapter.registerSubtype(Diamond.class, "Diamond");
+ * }
+ * Finally, register the type adapter factory in your application's GSON builder: + *
   {@code
+ *   Gson gson = new GsonBuilder()
+ *       .registerTypeAdapterFactory(shapeAdapterFactory)
+ *       .create();
+ * }
+ * Like {@code GsonBuilder}, this API supports chaining:
   {@code
+ *   RuntimeTypeAdapterFactory shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class)
+ *       .registerSubtype(Rectangle.class)
+ *       .registerSubtype(Circle.class)
+ *       .registerSubtype(Diamond.class);
+ * }
+ */ +public final class RuntimeClassNameTypeAdapterFactory implements TypeAdapterFactory { + private final Class baseType; + private final String typeFieldName; + private final Map> labelToSubtype = new LinkedHashMap>(); + private final Map, String> subtypeToLabel = new LinkedHashMap, String>(); + private final ExclusionStrategy exclusionStrategy; + + private RuntimeClassNameTypeAdapterFactory(Class baseType, String typeFieldName, ExclusionStrategy exclusionStrategy) { + if (typeFieldName == null || baseType == null) { + throw new NullPointerException(); + } + this.baseType = baseType; + this.typeFieldName = typeFieldName; + this.exclusionStrategy = exclusionStrategy; + } + + /** + * Creates a new runtime type adapter using for {@code baseType} using {@code + * typeFieldName} as the type field name. Type field names are case sensitive. + */ + public static RuntimeClassNameTypeAdapterFactory of(Class baseType, String typeFieldName, ExclusionStrategy exclusionStrategy) { + return new RuntimeClassNameTypeAdapterFactory(baseType, typeFieldName, exclusionStrategy); + } + + /** + * Creates a new runtime type adapter for {@code baseType} using {@code "type"} as + * the type field name. + */ + public static RuntimeClassNameTypeAdapterFactory of(Class baseType) { + return new RuntimeClassNameTypeAdapterFactory(baseType, "class", null); + } + + /** + * Registers {@code type} identified by {@code label}. Labels are case + * sensitive. + * + * @throws IllegalArgumentException if either {@code type} or {@code label} + * have already been registered on this type adapter. + */ + public RuntimeClassNameTypeAdapterFactory registerSubtype(Class type, String label) { + if (type == null || label == null) { + throw new NullPointerException(); + } + if (subtypeToLabel.containsKey(type) || labelToSubtype.containsKey(label)) { + throw new IllegalArgumentException("types and labels must be unique"); + } + labelToSubtype.put(label, type); + subtypeToLabel.put(type, label); + return this; + } + + /** + * Registers {@code type} identified by its {@link Class#getSimpleName simple + * name}. Labels are case sensitive. + * + * @throws IllegalArgumentException if either {@code type} or its simple name + * have already been registered on this type adapter. + */ + public RuntimeClassNameTypeAdapterFactory registerSubtype(Class type) { + return registerSubtype(type, type.getSimpleName()); + } + + public TypeAdapter create(Gson gson, TypeToken type) { + + if (exclusionStrategy.shouldSkipClass(type.getRawType().getClass())) { + return null; + } + final Map> labelToDelegate + = new LinkedHashMap>(); + final Map, TypeAdapter> subtypeToDelegate + = new LinkedHashMap, TypeAdapter>(); + +// && !String.class.isAssignableFrom(type.getRawType()) + + if (Object.class.isAssignableFrom(type.getRawType())) { + TypeAdapter delegate = gson.getDelegateAdapter(this, type); + labelToDelegate.put(type.getRawType().getSimpleName(), delegate); + subtypeToDelegate.put(type.getRawType(), delegate); + } + +// for (Map.Entry> entry : labelToSubtype.entrySet()) { +// TypeAdapter delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue())); +// labelToDelegate.put(entry.getKey(), delegate); +// subtypeToDelegate.put(entry.getValue(), delegate); +// } + + return new TypeAdapter() { + @SuppressWarnings("unchecked") + @Override + public R read(JsonReader in) throws IOException { + JsonElement jsonElement = Streams.parse(in); + if (jsonElement.isJsonObject()) { + JsonElement labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName); + if (labelJsonElement == null) { + throw new JsonParseException("cannot deserialize " + baseType + + " because it does not define a field named " + typeFieldName); + } + String label = labelJsonElement.getAsString(); + TypeAdapter delegate = (TypeAdapter) labelToDelegate.get(label); + if (delegate == null) { + Class aClass; + try { + aClass = (Class) Class.forName(label); + } catch (ClassNotFoundException e) { + throw new JsonParseException("Cannot find class " + label, e); + } + + TypeToken subClass = TypeToken.get(aClass); + delegate = gson.getDelegateAdapter(RuntimeClassNameTypeAdapterFactory.this, subClass); + if (delegate == null) { + throw new JsonParseException("cannot deserialize " + baseType + " subtype named " + + label + "; did you forget to register a subtype?"); + } + } + return delegate.fromJsonTree(jsonElement); + } else if (jsonElement.isJsonNull()) { + return null; + } else { + TypeAdapter delegate = gson.getDelegateAdapter(RuntimeClassNameTypeAdapterFactory.this, type); + if (delegate == null) { + throw new JsonParseException("cannot deserialize " + baseType + "; did you forget to register a subtype?"); + } + return delegate.fromJsonTree(jsonElement); + } + } + + @Override + public void write(JsonWriter out, R value) throws IOException { + Class srcType = value.getClass(); + String label = srcType.getSimpleName(); + TypeAdapter delegate = getDelegate(srcType); + if (delegate == null) { + throw new JsonParseException("cannot serialize " + srcType.getSimpleName() + + "; did you forget to register a subtype?"); + } + JsonElement jsonTree = delegate.toJsonTree(value); + if (!jsonTree.isJsonObject()) { + Streams.write(jsonTree, out); + } else { + JsonObject jsonObject = jsonTree.getAsJsonObject(); + if (jsonObject.has(typeFieldName)) { + throw new JsonParseException("cannot serialize " + srcType.getSimpleName() + + " because it already defines a field named " + typeFieldName); + } + JsonObject clone = new JsonObject(); + clone.add(typeFieldName, new JsonPrimitive(label)); + for (Map.Entry e : jsonObject.entrySet()) { + clone.add(e.getKey(), e.getValue()); + } + Streams.write(clone, out); + } + } + + @SuppressWarnings("unchecked") + private TypeAdapter getDelegate(Class srcType) { + TypeAdapter typeAdapter = subtypeToDelegate.get(srcType); + if (typeAdapter != null) { + return (TypeAdapter) typeAdapter; + } + + for (Map.Entry, TypeAdapter> classTypeAdapterEntry : subtypeToDelegate.entrySet()) { + if (classTypeAdapterEntry.getKey().isAssignableFrom(srcType)) { + return (TypeAdapter) classTypeAdapterEntry.getValue(); + } + } + return null; + } + }.nullSafe(); + } +} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/SaveToGallerySettingsHelper.java b/TMessagesProj/src/main/java/org/telegram/messenger/SaveToGallerySettingsHelper.java index 80c7cfe973..aa38edde14 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/SaveToGallerySettingsHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/SaveToGallerySettingsHelper.java @@ -206,7 +206,7 @@ public CharSequence createDescription(int currentAccount) { } builder.append(LocaleController.getString("SaveToGalleryVideos", R.string.SaveToGalleryVideos)); if (limitVideo > 0 && limitVideo < 4L * 1000 * 1024 * 1024) { - builder.append(" (").append(AndroidUtilities.formatFileSize(limitVideo, true)).append(")"); + builder.append(" (").append(AndroidUtilities.formatFileSize(limitVideo, true, false)).append(")"); } } } else { @@ -244,7 +244,7 @@ public CharSequence createDescription(int currentAccount) { } if (limitVideo > 0 && limitVideo < 4L * 1000 * 1024 * 1024) { - builder.append(LocaleController.formatString("SaveToGalleryVideosUpTo", R.string.SaveToGalleryVideosUpTo, AndroidUtilities.formatFileSize(limitVideo, true))); + builder.append(LocaleController.formatString("SaveToGalleryVideosUpTo", R.string.SaveToGalleryVideosUpTo, AndroidUtilities.formatFileSize(limitVideo, true, false))); } else { builder.append(LocaleController.formatString("SaveToGalleryVideos", R.string.SaveToGalleryVideos)); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/SecretChatHelper.java b/TMessagesProj/src/main/java/org/telegram/messenger/SecretChatHelper.java index 2b6ba369e7..e2840bed32 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/SecretChatHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/SecretChatHelper.java @@ -1006,7 +1006,7 @@ public TLRPC.Message processDecryptedObject(TLRPC.EncryptedChat chat, TLRPC.Encr for (int a = 0, N = newMessage.media.document.attributes.size(); a < N; a++) { TLRPC.DocumentAttribute attribute = newMessage.media.document.attributes.get(a); if (attribute instanceof TLRPC.TL_documentAttributeAudio || attribute instanceof TLRPC.TL_documentAttributeVideo) { - newMessage.ttl = Math.max(attribute.duration + 1, newMessage.ttl); + newMessage.ttl = (int) Math.max(attribute.duration + 1, newMessage.ttl); break; } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java b/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java index f937a5cbf9..d499608d91 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java @@ -70,6 +70,7 @@ import org.telegram.ui.Components.Premium.LimitReachedBottomSheet; import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble; import org.telegram.ui.Components.Reactions.ReactionsUtils; +import org.telegram.ui.Stories.MessageMediaStoryFull; import org.telegram.ui.TwoStepVerificationActivity; import org.telegram.ui.TwoStepVerificationSetupActivity; @@ -92,6 +93,8 @@ public class SendMessagesHelper extends BaseController implements NotificationCenter.NotificationCenterDelegate { + public static final int MEDIA_TYPE_DICE = 11; + public static final int MEDIA_TYPE_STORY = 12; private HashMap> delayedMessages = new HashMap<>(); private SparseArray unsentMessages = new SparseArray<>(); private SparseArray sendingMessages = new SparseArray<>(); @@ -122,6 +125,28 @@ public static boolean checkUpdateStickersOrder(CharSequence text) { return false; } + public TLRPC.InputReplyTo creteReplyInput(TLRPC.StoryItem storyItem) { + TLRPC.TL_inputReplyToStory replyTo = new TLRPC.TL_inputReplyToStory(); + replyTo.story_id = storyItem.id; + replyTo.user_id = getMessagesController().getInputUser(storyItem.dialogId); + return replyTo; + } + + public static TLRPC.InputReplyTo creteReplyInput(int replyToMsgId) { + return creteReplyInput(replyToMsgId, 0); + } + + public static TLRPC.InputReplyTo creteReplyInput(int replyToMsgId, int topMessageId) { + TLRPC.TL_inputReplyToMessage replyTo = new TLRPC.TL_inputReplyToMessage(); + replyTo.reply_to_msg_id = replyToMsgId; + if (topMessageId != 0) { + replyTo.flags |= 1; + replyTo.top_msg_id = topMessageId; + } + return replyTo; + } + + public class ImportingHistory { public String historyPath; public ArrayList mediaPaths = new ArrayList<>(); @@ -1043,8 +1068,9 @@ public void didReceivedNotification(int id, int account, final Object... args) { String finalPath = (String) args[1]; long availableSize = (Long) args[2]; long finalSize = (Long) args[3]; + Float progress = (Float) args[4]; boolean isEncrypted = DialogObject.isEncryptedDialog(messageObject.getDialogId()); - getFileLoader().checkUploadNewDataAvailable(finalPath, isEncrypted, availableSize, finalSize); + getFileLoader().checkUploadNewDataAvailable(finalPath, isEncrypted, availableSize, finalSize, progress); if (finalSize != 0) { stopVideoService(messageObject.messageOwner.attachPath); ArrayList arr = delayedMessages.get(messageObject.messageOwner.attachPath); @@ -1433,7 +1459,7 @@ public boolean retrySendMessage(MessageObject messageObject, boolean unsent) { if (unsent) { unsentMessages.put(messageObject.getId(), messageObject); } - sendMessage(messageObject); + sendMessage(SendMessagesHelper.SendMessageParams.of(messageObject)); return true; } @@ -1456,18 +1482,18 @@ public void processForwardFromMyName(MessageObject messageObject, long did) { params.put("parentObject", "sent_" + messageObject.messageOwner.peer_id.channel_id + "_" + messageObject.getId() + "_" + messageObject.getDialogId() + "_" + messageObject.type + "_" + messageObject.getSize()); } if (messageObject.messageOwner.media.photo instanceof TLRPC.TL_photo) { - sendMessage((TLRPC.TL_photo) messageObject.messageOwner.media.photo, null, did, messageObject.replyMessageObject, null, messageObject.messageOwner.message, messageObject.messageOwner.entities, null, params, true, 0, messageObject.messageOwner.media.ttl_seconds, messageObject, false); + sendMessage(SendMessagesHelper.SendMessageParams.of((TLRPC.TL_photo) messageObject.messageOwner.media.photo, null, did, messageObject.replyMessageObject, null, messageObject.messageOwner.message, messageObject.messageOwner.entities, null, params, true, 0, messageObject.messageOwner.media.ttl_seconds, messageObject, false)); } else if (messageObject.messageOwner.media.document instanceof TLRPC.TL_document) { - sendMessage((TLRPC.TL_document) messageObject.messageOwner.media.document, null, messageObject.messageOwner.attachPath, did, messageObject.replyMessageObject, null, messageObject.messageOwner.message, messageObject.messageOwner.entities, null, params, true, 0, messageObject.messageOwner.media.ttl_seconds, messageObject, null, false); + sendMessage(SendMessagesHelper.SendMessageParams.of((TLRPC.TL_document) messageObject.messageOwner.media.document, null, messageObject.messageOwner.attachPath, did, messageObject.replyMessageObject, null, messageObject.messageOwner.message, messageObject.messageOwner.entities, null, params, true, 0, messageObject.messageOwner.media.ttl_seconds, messageObject, null, false)); } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaVenue || messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeo) { - sendMessage(messageObject.messageOwner.media, did, messageObject.replyMessageObject, null, null, null, true, 0); + sendMessage(SendMessagesHelper.SendMessageParams.of(messageObject.messageOwner.media, did, messageObject.replyMessageObject, null, null, null, true, 0)); } else if (messageObject.messageOwner.media.phone_number != null) { TLRPC.User user = new TLRPC.TL_userContact_old2(); user.phone = messageObject.messageOwner.media.phone_number; user.first_name = messageObject.messageOwner.media.first_name; user.last_name = messageObject.messageOwner.media.last_name; user.id = messageObject.messageOwner.media.user_id; - sendMessage(user, did, messageObject.replyMessageObject, null, null, null, true, 0); + sendMessage(SendMessagesHelper.SendMessageParams.of(user, did, messageObject.replyMessageObject, null, null, null, true, 0)); } else if (!DialogObject.isEncryptedDialog(did)) { ArrayList arrayList = new ArrayList<>(); arrayList.add(messageObject); @@ -1496,7 +1522,7 @@ public void processForwardFromMyName(MessageObject messageObject, long did) { } else { entities = null; } - sendMessage(messageObject.messageOwner.message, did, messageObject.replyMessageObject, null, webPage, true, entities, null, null, true, 0, null, false); + sendMessage(SendMessagesHelper.SendMessageParams.of(messageObject.messageOwner.message, did, messageObject.replyMessageObject, null, webPage, true, entities, null, null, true, 0, null, false)); } else if (DialogObject.isEncryptedDialog(did)) { ArrayList arrayList = new ArrayList<>(); arrayList.add(messageObject); @@ -1516,7 +1542,7 @@ public void sendScreenshotMessage(TLRPC.User user, int messageId, TLRPC.Message TLRPC.Message message; if (resendMessage != null) { message = resendMessage; - req.reply_to_msg_id = messageId; + req.reply_to = SendMessagesHelper.creteReplyInput(messageId); req.random_id = resendMessage.random_id; } else { message = new TLRPC.TL_messageService(); @@ -1553,11 +1579,11 @@ public void sendScreenshotMessage(TLRPC.User user, int messageId, TLRPC.Message performSendMessageRequest(req, newMsgObj, null, null, null, null, false); } - public void sendSticker(TLRPC.Document document, String query, long peer, MessageObject replyToMsg, MessageObject replyToTopMsg, Object parentObject, MessageObject.SendAnimationData sendAnimationData, boolean notify, int scheduleDate, boolean updateStickersOrder) { - sendSticker(document, query, peer, replyToMsg, replyToTopMsg, parentObject, sendAnimationData, notify, scheduleDate, updateStickersOrder, null, null); + public void sendSticker(TLRPC.Document document, String query, long peer, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.StoryItem storyItem, MessageObject.SendAnimationData sendAnimationData, boolean notify, int scheduleDate, boolean updateStickersOrder, Object parentObject) { + sendSticker(document, query, peer, replyToMsg, replyToTopMsg, storyItem, sendAnimationData, notify, scheduleDate, updateStickersOrder, parentObject, null, null); } - public void sendSticker(TLRPC.Document document, String query, long peer, MessageObject replyToMsg, MessageObject replyToTopMsg, Object parentObject, MessageObject.SendAnimationData sendAnimationData, boolean notify, int scheduleDate, boolean updateStickersOrder, String caption, ArrayList entities) { + public void sendSticker(TLRPC.Document document, String query, long peer, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.StoryItem storyItem, MessageObject.SendAnimationData sendAnimationData, boolean notify, int scheduleDate, boolean updateStickersOrder, Object parentObject, String caption, ArrayList entities) { if (document == null) { return; } @@ -1655,7 +1681,9 @@ public void sendSticker(TLRPC.Document document, String query, long peer, Messag if (bitmapFinal[0] != null && keyFinal[0] != null) { ImageLoader.getInstance().putImageToCache(new BitmapDrawable(bitmapFinal[0]), keyFinal[0], false); } - sendMessage((TLRPC.TL_document) finalDocument, null, null, peer, replyToMsg, replyToTopMsg, caption, entities, null, null, notify, scheduleDate, 0, parentObject, sendAnimationData, false); + SendMessageParams sendMessageParams = SendMessageParams.of((TLRPC.TL_document) finalDocument, null, null, peer, replyToMsg, replyToTopMsg, caption, entities, null, null, notify, scheduleDate, 0, parentObject, sendAnimationData, false); + sendMessageParams.replyToStoryItem = storyItem; + sendMessage(sendMessageParams); }); }); } else { @@ -1666,7 +1694,9 @@ public void sendSticker(TLRPC.Document document, String query, long peer, Messag } else { params = null; } - sendMessage((TLRPC.TL_document) finalDocument, null, null, peer, replyToMsg, replyToTopMsg, null, null, null, params, notify, scheduleDate, 0, parentObject, sendAnimationData, updateStickersOrder); + SendMessageParams sendMessageParams = SendMessageParams.of((TLRPC.TL_document) finalDocument, null, null, peer, replyToMsg, replyToTopMsg, null, null, null, params, notify, scheduleDate, 0, parentObject, sendAnimationData, updateStickersOrder); + sendMessageParams.replyToStoryItem = storyItem; + sendMessage(sendMessageParams); } } @@ -1749,7 +1779,7 @@ public int sendMessage(ArrayList messages, final long peer, boole if (msgObj.getId() <= 0 || msgObj.needDrawBluredPreview()) { if (msgObj.type == MessageObject.TYPE_TEXT && !TextUtils.isEmpty(msgObj.messageText)) { TLRPC.WebPage webPage = msgObj.messageOwner.media != null ? msgObj.messageOwner.media.webpage : null; - sendMessage(msgObj.messageText.toString(), peer, null, replyToTopMsg, webPage, webPage != null, msgObj.messageOwner.entities, null, null, notify, scheduleDate, null, false); + sendMessage(SendMessageParams.of(msgObj.messageText.toString(), peer, null, replyToTopMsg, webPage, webPage != null, msgObj.messageOwner.entities, null, null, notify, scheduleDate, null, false)); } continue; } @@ -2737,7 +2767,7 @@ private void sendLocation(Location location) { mediaGeo.geo._long = AndroidUtilities.fixLocationCoord(location.getLongitude()); for (HashMap.Entry entry : waitingForLocation.entrySet()) { MessageObject messageObject = entry.getValue(); - sendMessage(mediaGeo, messageObject.getDialogId(), messageObject, null, null, null, true, 0); + sendMessage(SendMessagesHelper.SendMessageParams.of(mediaGeo, messageObject.getDialogId(), messageObject, null, null, null, true, 0)); } } @@ -3281,63 +3311,41 @@ public void sendGame(TLRPC.InputPeer peer, TLRPC.TL_inputMediaGame game, long ra }); } - public void sendMessage(MessageObject retryMessageObject) { - sendMessage(null, null, null, null, null, null, null, null, null, null, retryMessageObject.getDialogId(), retryMessageObject.messageOwner.attachPath, null, null, null, true, retryMessageObject, null, retryMessageObject.messageOwner.reply_markup, retryMessageObject.messageOwner.params, !retryMessageObject.messageOwner.silent, retryMessageObject.scheduled ? retryMessageObject.messageOwner.date : 0, 0, null, null, false); - } - - public void sendMessage(TLRPC.User user, long peer, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.ReplyMarkup replyMarkup, HashMap params, boolean notify, int scheduleDate) { - sendMessage(null, null, null, null, null, user, null, null, null, null, peer, null, replyToMsg, replyToTopMsg, null, true, null, null, replyMarkup, params, notify, scheduleDate, 0, null, null, false); - } - - public void sendMessage(TLRPC.TL_messageMediaInvoice invoice, long peer, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.ReplyMarkup replyMarkup, HashMap params, boolean notify, int scheduleDate) { - sendMessage(null, null, null, null, null, null, null, null, null, invoice, peer, null, replyToMsg, replyToTopMsg, null, true, null, null, replyMarkup, params, notify, scheduleDate, 0, null, null, false); - } - - public void sendMessage(TLRPC.TL_document document, VideoEditedInfo videoEditedInfo, String path, long peer, MessageObject replyToMsg, MessageObject replyToTopMsg, String caption, ArrayList entities, TLRPC.ReplyMarkup replyMarkup, HashMap params, boolean notify, int scheduleDate, int ttl, Object parentObject, MessageObject.SendAnimationData sendAnimationData, boolean updateStickersOrder) { - sendMessage(null, caption, null, null, videoEditedInfo, null, document, null, null, null, peer, path, replyToMsg, replyToTopMsg, null, true, null, entities, replyMarkup, params, notify, scheduleDate, ttl, parentObject, sendAnimationData, updateStickersOrder); - } - - public void sendMessage(TLRPC.TL_document document, VideoEditedInfo videoEditedInfo, String path, long peer, MessageObject replyToMsg, MessageObject replyToTopMsg, String caption, ArrayList entities, TLRPC.ReplyMarkup replyMarkup, HashMap params, boolean notify, int scheduleDate, int ttl, Object parentObject, MessageObject.SendAnimationData sendAnimationData, boolean updateStickersOrder, boolean hasMediaSpoilers) { - sendMessage(null, caption, null, null, videoEditedInfo, null, document, null, null, null, peer, path, replyToMsg, replyToTopMsg, null, true, null, entities, replyMarkup, params, notify, scheduleDate, ttl, parentObject, sendAnimationData, updateStickersOrder, hasMediaSpoilers); - } - public void sendMessage(String message, long peer, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.WebPage webPage, boolean searchLinks, ArrayList entities, TLRPC.ReplyMarkup replyMarkup, HashMap params, boolean notify, int scheduleDate, MessageObject.SendAnimationData sendAnimationData, boolean updateStickersOrder) { - sendMessage(message, null, null, null, null, null, null, null, null, null, peer, null, replyToMsg, replyToTopMsg, webPage, searchLinks, null, entities, replyMarkup, params, notify, scheduleDate, 0, null, sendAnimationData, updateStickersOrder); - } - - public void sendMessage(String message, long peer, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.WebPage webPage, boolean searchLinks, ArrayList entities, TLRPC.ReplyMarkup replyMarkup, HashMap params, boolean notify, int scheduleDate, MessageObject.SendAnimationData sendAnimationData, boolean updateStickersOrder, boolean canSendGames) { - sendMessage(message, null, null, null, null, null, null, null, null, null, peer, null, replyToMsg, replyToTopMsg, webPage, searchLinks, null, entities, replyMarkup, params, notify, scheduleDate, 0, null, sendAnimationData, updateStickersOrder, canSendGames); - } - - public void sendMessage(TLRPC.MessageMedia location, long peer, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.ReplyMarkup replyMarkup, HashMap params, boolean notify, int scheduleDate) { - sendMessage(null, null, location, null, null, null, null, null, null, null, peer, null, replyToMsg, replyToTopMsg, null, true, null, null, replyMarkup, params, notify, scheduleDate, 0, null, null, false); - } - - public void sendMessage(TLRPC.TL_messageMediaPoll poll, long peer, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.ReplyMarkup replyMarkup, HashMap params, boolean notify, int scheduleDate) { - sendMessage(null, null, null, null, null, null, null, null, poll, null, peer, null, replyToMsg, replyToTopMsg, null, true, null, null, replyMarkup, params, notify, scheduleDate, 0, null, null, false); + sendMessage(SendMessageParams.of(message, null, null, null, null, null, null, null, null, null, peer, null, replyToMsg, replyToTopMsg, webPage, searchLinks, null, entities, replyMarkup, params, notify, scheduleDate, 0, null, sendAnimationData, updateStickersOrder, false)); } - public void sendMessage(TLRPC.TL_game game, long peer, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.ReplyMarkup replyMarkup, HashMap params, boolean notify, int scheduleDate) { - sendMessage(null, null, null, null, null, null, null, game, null, null, peer, null, replyToMsg, replyToTopMsg, null, true, null, null, replyMarkup, params, notify, scheduleDate, 0, null, null, false); - } - - public void sendMessage(TLRPC.TL_photo photo, String path, long peer, MessageObject replyToMsg, MessageObject replyToTopMsg, String caption, ArrayList entities, TLRPC.ReplyMarkup replyMarkup, HashMap params, boolean notify, int scheduleDate, int ttl, Object parentObject, boolean updateStickersOrder, boolean hasMediaSpoilers) { - sendMessage(null, caption, null, photo, null, null, null, null, null, null, peer, path, replyToMsg, replyToTopMsg, null, true, null, entities, replyMarkup, params, notify, scheduleDate, ttl, parentObject, null, updateStickersOrder, hasMediaSpoilers); - } - - public void sendMessage(TLRPC.TL_photo photo, String path, long peer, MessageObject replyToMsg, MessageObject replyToTopMsg, String caption, ArrayList entities, TLRPC.ReplyMarkup replyMarkup, HashMap params, boolean notify, int scheduleDate, int ttl, Object parentObject, boolean updateStickersOrder) { - sendMessage(null, caption, null, photo, null, null, null, null, null, null, peer, path, replyToMsg, replyToTopMsg, null, true, null, entities, replyMarkup, params, notify, scheduleDate, ttl, parentObject, null, updateStickersOrder); - } - - private void sendMessage(String message, String caption, TLRPC.MessageMedia location, TLRPC.TL_photo photo, VideoEditedInfo videoEditedInfo, TLRPC.User user, TLRPC.TL_document document, TLRPC.TL_game game, TLRPC.TL_messageMediaPoll poll, TLRPC.TL_messageMediaInvoice invoice, long peer, String path, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.WebPage webPage, boolean searchLinks, MessageObject retryMessageObject, ArrayList entities, TLRPC.ReplyMarkup replyMarkup, HashMap params, boolean notify, int scheduleDate, int ttl, Object parentObject, MessageObject.SendAnimationData sendAnimationData, boolean updateStickersOrder) { - sendMessage(message, caption, location, photo, videoEditedInfo, user, document, game, poll, invoice, peer, path, replyToMsg, replyToTopMsg, webPage, searchLinks, retryMessageObject, entities, replyMarkup, params, notify, scheduleDate, ttl, parentObject, sendAnimationData, updateStickersOrder, false); - } - - private void sendMessage(String message, String caption, TLRPC.MessageMedia location, TLRPC.TL_photo photo, VideoEditedInfo videoEditedInfo, TLRPC.User user, TLRPC.TL_document document, TLRPC.TL_game game, TLRPC.TL_messageMediaPoll poll, TLRPC.TL_messageMediaInvoice invoice, long peer, String path, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.WebPage webPage, boolean searchLinks, MessageObject retryMessageObject, ArrayList entities, TLRPC.ReplyMarkup replyMarkup, HashMap params, boolean notify, int scheduleDate, int ttl, Object parentObject, MessageObject.SendAnimationData sendAnimationData, boolean updateStickersOrder, boolean hasMediaSpoilers) { - sendMessage(message, caption, location, photo, videoEditedInfo, user, document, game, poll, invoice, peer, path, replyToMsg, replyToTopMsg, webPage, searchLinks, retryMessageObject, entities, replyMarkup, params, notify, scheduleDate, ttl, parentObject, sendAnimationData, updateStickersOrder, hasMediaSpoilers, true); - } - - private void sendMessage(String message, String caption, TLRPC.MessageMedia location, TLRPC.TL_photo photo, VideoEditedInfo videoEditedInfo, TLRPC.User user, TLRPC.TL_document document, TLRPC.TL_game game, TLRPC.TL_messageMediaPoll poll, TLRPC.TL_messageMediaInvoice invoice, long peer, String path, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.WebPage webPage, boolean searchLinks, MessageObject retryMessageObject, ArrayList entities, TLRPC.ReplyMarkup replyMarkup, HashMap params, boolean notify, int scheduleDate, int ttl, Object parentObject, MessageObject.SendAnimationData sendAnimationData, boolean updateStickersOrder, boolean hasMediaSpoilers, boolean canSendGames) { + public void sendMessage(SendMessageParams sendMessageParams) { + String message = sendMessageParams.message; + String caption = sendMessageParams.caption; + TLRPC.MessageMedia location = sendMessageParams.location; + TLRPC.TL_photo photo = sendMessageParams.photo; + VideoEditedInfo videoEditedInfo = sendMessageParams.videoEditedInfo; + TLRPC.User user = sendMessageParams.user; + TLRPC.TL_document document = sendMessageParams.document; + TLRPC.TL_game game = sendMessageParams.game; + TLRPC.TL_messageMediaPoll poll = sendMessageParams.poll; + TLRPC.TL_messageMediaInvoice invoice = sendMessageParams.invoice; + long peer = sendMessageParams.peer; + String path = sendMessageParams.path; + MessageObject replyToMsg = sendMessageParams.replyToMsg; + MessageObject replyToTopMsg = sendMessageParams.replyToTopMsg; + TLRPC.WebPage webPage = sendMessageParams.webPage; + boolean searchLinks = sendMessageParams.searchLinks; + MessageObject retryMessageObject = sendMessageParams.retryMessageObject; + ArrayList entities = sendMessageParams.entities; + TLRPC.ReplyMarkup replyMarkup = sendMessageParams.replyMarkup; + HashMap params = sendMessageParams.params; + boolean notify = sendMessageParams.notify; + int scheduleDate = sendMessageParams.scheduleDate; + int ttl = sendMessageParams.ttl; + Object parentObject = sendMessageParams.parentObject; + MessageObject.SendAnimationData sendAnimationData = sendMessageParams.sendAnimationData; + boolean updateStickersOrder = sendMessageParams.updateStickersOrder; + boolean hasMediaSpoilers = sendMessageParams.hasMediaSpoilers; + TLRPC.StoryItem replyToStoryItem = sendMessageParams.replyToStoryItem; + TLRPC.StoryItem sendingStory = sendMessageParams.sendingStory; + boolean canSendGames = sendMessageParams.canSendGames; if (user != null && user.phone == null) { return; } @@ -3396,7 +3404,7 @@ private void sendMessage(String message, String caption, TLRPC.MessageMedia loca type = 4; } else { if (retryMessageObject.isDice()) { - type = 11; + type = MEDIA_TYPE_DICE; message = retryMessageObject.getDiceEmoji(); caption = ""; } else if (retryMessageObject.type == MessageObject.TYPE_TEXT || retryMessageObject.isAnimatedEmoji()) { @@ -3492,7 +3500,7 @@ private void sendMessage(String message, String caption, TLRPC.MessageMedia loca mediaDice.emoticon = message; mediaDice.value = -1; newMsg.media = mediaDice; - type = 11; + type = MEDIA_TYPE_DICE; caption = ""; } else { if (webPage == null) { @@ -3677,6 +3685,18 @@ private void sendMessage(String message, String caption, TLRPC.MessageMedia loca } } } + } else if (sendingStory != null) { + if (encryptedChat != null) { + newMsg = new TLRPC.TL_message_secret(); + } else { + newMsg = new TLRPC.TL_message(); + } + TLRPC.TL_messageMediaStory mediaStory = new MessageMediaStoryFull(); + mediaStory.id = sendingStory.id; + mediaStory.user_id = sendingStory.dialogId; + mediaStory.storyItem = sendingStory; + newMsg.media = mediaStory; + type = MEDIA_TYPE_STORY; } if (entities != null && !entities.isEmpty()) { newMsg.entities = entities; @@ -3749,7 +3769,13 @@ private void sendMessage(String message, String caption, TLRPC.MessageMedia loca } newMsg.flags |= TLRPC.MESSAGE_FLAG_HAS_MEDIA; newMsg.dialog_id = peer; - if (replyToMsg != null && (replyToTopMsg == null || replyToMsg != replyToTopMsg || replyToTopMsg.getId() != 1)) { + if (replyToStoryItem != null) { + newMsg.reply_to = new TLRPC.TL_messageReplyStoryHeader(); + newMsg.reply_to.story_id = replyToStoryItem.id; + newMsg.reply_to.user_id = replyToStoryItem.dialogId; + newMsg.replyStory = replyToStoryItem; + newMsg.flags |= TLRPC.MESSAGE_FLAG_REPLY; + } else if (replyToMsg != null && (replyToTopMsg == null || replyToMsg != replyToTopMsg || replyToTopMsg.getId() != 1)) { newMsg.reply_to = new TLRPC.TL_messageReplyHeader(); if (encryptedChat != null && replyToMsg.messageOwner.random_id != 0) { newMsg.reply_to.reply_to_random_id = replyToMsg.messageOwner.random_id; @@ -3822,7 +3848,7 @@ private void sendMessage(String message, String caption, TLRPC.MessageMedia loca for (int a = 0; a < newMsg.media.document.attributes.size(); a++) { TLRPC.DocumentAttribute attribute = newMsg.media.document.attributes.get(a); if (attribute instanceof TLRPC.TL_documentAttributeAudio) { - duration = attribute.duration; + duration = (int) attribute.duration; break; } } @@ -3832,7 +3858,7 @@ private void sendMessage(String message, String caption, TLRPC.MessageMedia loca for (int a = 0; a < newMsg.media.document.attributes.size(); a++) { TLRPC.DocumentAttribute attribute = newMsg.media.document.attributes.get(a); if (attribute instanceof TLRPC.TL_documentAttributeVideo) { - duration = attribute.duration; + duration = (int) attribute.duration; break; } } @@ -3923,9 +3949,13 @@ private void sendMessage(String message, String caption, TLRPC.MessageMedia loca reqSend.silent = newMsg.silent; reqSend.peer = sendToPeer; reqSend.random_id = newMsg.random_id; - if (replyToTopMsg != null) { - reqSend.top_msg_id = replyToTopMsg.getId(); - reqSend.flags |= 512; + if (replyToStoryItem != null) { + reqSend.reply_to = creteReplyInput(replyToStoryItem); + reqSend.flags |= 1; + } else if (newMsg.reply_to != null) { + int topMsgId = replyToTopMsg == null ? 0 : replyToTopMsg.getId(); + reqSend.reply_to = SendMessagesHelper.creteReplyInput(newMsg.reply_to.reply_to_msg_id, topMsgId); + reqSend.flags |= 1; } if (updateStickersOrder && SharedConfig.updateStickersOrderOnSend) { reqSend.update_stickersets_order = true; @@ -3933,10 +3963,6 @@ private void sendMessage(String message, String caption, TLRPC.MessageMedia loca if (newMsg.from_id != null) { reqSend.send_as = getMessagesController().getInputPeer(newMsg.from_id); } - if (newMsg.reply_to != null && newMsg.reply_to.reply_to_msg_id != 0) { - reqSend.flags |= 1; - reqSend.reply_to_msg_id = newMsg.reply_to.reply_to_msg_id; - } if (!searchLinks) { reqSend.no_webpage = true; } @@ -3983,7 +4009,7 @@ private void sendMessage(String message, String caption, TLRPC.MessageMedia loca getMediaDataController().cleanDraft(peer, replyToTopMsg != null ? replyToTopMsg.getId() : 0, false); } } - } else if (type >= 1 && type <= 3 || type >= 5 && type <= 8 || type == 9 && encryptedChat != null || type == 10 || type == 11) { + } else if (type >= 1 && type <= 3 || type >= 5 && type <= 8 || type == 9 && encryptedChat != null || type == 10 || type == MEDIA_TYPE_DICE || type == MEDIA_TYPE_STORY) { if (encryptedChat == null) { TLRPC.InputMedia inputMedia = null; if (type == 1) { @@ -4247,10 +4273,15 @@ private void sendMessage(String message, String caption, TLRPC.MessageMedia loca inputMediaPoll.flags |= 2; } inputMedia = inputMediaPoll; - } else if (type == 11) { + } else if (type == MEDIA_TYPE_DICE) { TLRPC.TL_inputMediaDice inputMediaDice = new TLRPC.TL_inputMediaDice(); inputMediaDice.emoticon = message; inputMedia = inputMediaDice; + } else if (type == MEDIA_TYPE_STORY) { + TLRPC.TL_inputMediaStory inputMediaStory = new TLRPC.TL_inputMediaStory(); + inputMediaStory.id = sendingStory.id; + inputMediaStory.user_id = MessagesController.getInstance(currentAccount).getInputUser(sendingStory.dialogId); + inputMedia = inputMediaStory; } TLObject reqSend; @@ -4263,13 +4294,14 @@ private void sendMessage(String message, String caption, TLRPC.MessageMedia loca request = new TLRPC.TL_messages_sendMultiMedia(); request.peer = sendToPeer; request.silent = newMsg.silent; - if (replyToTopMsg != null) { - request.top_msg_id = replyToTopMsg.getId(); - request.flags |= 512; - } - if (newMsg.reply_to != null && newMsg.reply_to.reply_to_msg_id != 0) { + + if (newMsg.replyStory != null) { + request.flags |= 1; + request.reply_to = creteReplyInput(replyToStoryItem); + } else if (newMsg.reply_to != null && newMsg.reply_to.reply_to_msg_id != 0) { + int replyToTopMsgId = replyToTopMsg != null ? replyToTopMsg.getId() : 0; request.flags |= 1; - request.reply_to_msg_id = newMsg.reply_to.reply_to_msg_id; + request.reply_to = SendMessagesHelper.creteReplyInput(newMsg.reply_to.reply_to_msg_id, replyToTopMsgId); } if (scheduleDate != 0) { request.schedule_date = scheduleDate; @@ -4299,13 +4331,16 @@ private void sendMessage(String message, String caption, TLRPC.MessageMedia loca TLRPC.TL_messages_sendMedia request = new TLRPC.TL_messages_sendMedia(); request.peer = sendToPeer; request.silent = newMsg.silent; + int replyToTopMsgInt = 0; if (replyToTopMsg != null) { - request.top_msg_id = replyToTopMsg.getId(); - request.flags |= 512; + replyToTopMsgInt = replyToTopMsg.getId(); } - if (newMsg.reply_to != null && newMsg.reply_to.reply_to_msg_id != 0) { + if (replyToStoryItem != null) { + request.reply_to = creteReplyInput(replyToStoryItem); request.flags |= 1; - request.reply_to_msg_id = newMsg.reply_to.reply_to_msg_id; + } else if (newMsg.reply_to != null && newMsg.reply_to.reply_to_msg_id != 0) { + request.flags |= 1; + request.reply_to = SendMessagesHelper.creteReplyInput(newMsg.reply_to.reply_to_msg_id, replyToTopMsgInt); } request.random_id = newMsg.random_id; if (newMsg.from_id != null) { @@ -4360,7 +4395,7 @@ private void sendMessage(String message, String caption, TLRPC.MessageMedia loca } else { performSendMessageRequest(reqSend, newMsgObj, originalPath, delayedMessage, parentObject, params, scheduleDate != 0); } - } else if (type == 10 || type == 11) { + } else if (type == 10 || type == MEDIA_TYPE_DICE || type == MEDIA_TYPE_STORY) { performSendMessageRequest(reqSend, newMsgObj, originalPath, delayedMessage, parentObject, params, scheduleDate != 0); } } else { @@ -4480,7 +4515,7 @@ private void sendMessage(String message, String caption, TLRPC.MessageMedia loca if (attribute instanceof TLRPC.TL_documentAttributeVideo) { reqSend.media.w = attribute.w; reqSend.media.h = attribute.h; - reqSend.media.duration = attribute.duration; + reqSend.media.duration = (int) attribute.duration; break; } } @@ -4700,7 +4735,7 @@ private void sendMessage(String message, String caption, TLRPC.MessageMedia loca reqSend.peer = sendToPeer; reqSend.random_id = newMsg.random_id; if (replyToTopMsg != null) { - reqSend.top_msg_id = replyToTopMsg.getId(); + reqSend.reply_to = SendMessagesHelper.creteReplyInput(replyToTopMsg.getId()); reqSend.flags |= 512; } if (newMsg.from_id != null) { @@ -4709,7 +4744,7 @@ private void sendMessage(String message, String caption, TLRPC.MessageMedia loca reqSend.hide_via = !params.containsKey("bot"); if (newMsg.reply_to != null && newMsg.reply_to.reply_to_msg_id != 0) { reqSend.flags |= 1; - reqSend.reply_to_msg_id = newMsg.reply_to.reply_to_msg_id; + reqSend.reply_to = SendMessagesHelper.creteReplyInput(newMsg.reply_to.reply_to_msg_id); } reqSend.silent = newMsg.silent; if (scheduleDate != 0) { @@ -4808,7 +4843,9 @@ private void performSendDelayedMessage(final DelayedMessage message, int index) location = FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE) + "/" + document.id + ".mp4"; } putToDelayedMessages(location, message); - MediaController.getInstance().scheduleVideoConvert(message.obj); + if (!message.videoEditedInfo.alreadyScheduledConverting) { + MediaController.getInstance().scheduleVideoConvert(message.obj); + } putToUploadingMessages(message.obj); } else { if (message.videoEditedInfo != null) { @@ -4974,7 +5011,9 @@ private void performSendDelayedMessage(final DelayedMessage message, int index) if (message.photoSize != null && message.photoSize.location != null) { message.extraHashMap.put(location + "_t", message.photoSize); } - MediaController.getInstance().scheduleVideoConvert(messageObject); + if (!message.videoEditedInfo.alreadyScheduledConverting) { + MediaController.getInstance().scheduleVideoConvert(messageObject); + } message.obj = messageObject; putToUploadingMessages(messageObject); } else { @@ -5880,7 +5919,9 @@ private void updateMediaPaths(MessageObject newMsgObj, TLRPC.Message sentMessage if (newMsg.media != null) { TLRPC.PhotoSize strippedOld = null; TLObject photoObject = null; - if (newMsgObj.isLiveLocation() && sentMessage.media instanceof TLRPC.TL_messageMediaGeoLive) { + if (newMsg.media.storyItem != null) { + sentMessage.media = newMsg.media; + } else if (newMsgObj.isLiveLocation() && sentMessage.media instanceof TLRPC.TL_messageMediaGeoLive) { newMsg.media.period = sentMessage.media.period; } else if (newMsgObj.isDice()) { TLRPC.TL_messageMediaDice mediaDice = (TLRPC.TL_messageMediaDice) newMsg.media; @@ -6362,7 +6403,7 @@ public TLRPC.TL_photo generatePhotoSizes(TLRPC.TL_photo photo, String path, Uri private final static int ERROR_TYPE_UNSUPPORTED = 1; private final static int ERROR_TYPE_FILE_TOO_LARGE = 2; - private static int prepareSendingDocumentInternal(AccountInstance accountInstance, String path, String originalPath, Uri uri, String mime, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, CharSequence caption, final ArrayList entities, final MessageObject editingMessageObject, long[] groupId, boolean isGroupFinal, boolean forceDocument, boolean notify, int scheduleDate, Integer[] docType) { + private static int prepareSendingDocumentInternal(AccountInstance accountInstance, String path, String originalPath, Uri uri, String mime, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.StoryItem storyItem, final ArrayList entities, final MessageObject editingMessageObject, long[] groupId, boolean isGroupFinal, CharSequence caption, boolean notify, int scheduleDate, Integer[] docType, boolean forceDocument) { if ((path == null || path.length() == 0) && uri == null) { return ERROR_TYPE_UNSUPPORTED; } @@ -6635,7 +6676,9 @@ private static int prepareSendingDocumentInternal(AccountInstance accountInstanc if (editingMessageObject != null) { accountInstance.getSendMessagesHelper().editMessage(editingMessageObject, null, null, documentFinal, pathFinal, params, false, false, parentFinal); } else { - accountInstance.getSendMessagesHelper().sendMessage(documentFinal, null, pathFinal, dialogId, replyToMsg, replyToTopMsg, captionFinal, entities, null, params, notify, scheduleDate, 0, parentFinal, null, false); + SendMessageParams sendMessageParams = SendMessagesHelper.SendMessageParams.of(documentFinal, null, pathFinal, dialogId, replyToMsg, replyToTopMsg, captionFinal, entities, null, params, notify, scheduleDate, 0, parentFinal, null, false); + sendMessageParams.replyToStoryItem = storyItem; + accountInstance.getSendMessagesHelper().sendMessage(sendMessageParams); } }); return 0; @@ -6666,7 +6709,7 @@ private static boolean checkFileSize(AccountInstance accountInstance, Uri uri) { } @UiThread - public static void prepareSendingDocument(AccountInstance accountInstance, String path, String originalPath, Uri uri, String caption, String mine, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, InputContentInfoCompat inputContent, MessageObject editingMessageObject, boolean notify, int scheduleDate) { + public static void prepareSendingDocument(AccountInstance accountInstance, String path, String originalPath, Uri uri, String caption, String mine, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.StoryItem storyItem, MessageObject editingMessageObject, boolean notify, int scheduleDate, InputContentInfoCompat inputContent) { if ((path == null || originalPath == null) && uri == null) { return; } @@ -6681,11 +6724,11 @@ public static void prepareSendingDocument(AccountInstance accountInstance, Strin paths.add(path); originalPaths.add(originalPath); } - prepareSendingDocuments(accountInstance, paths, originalPaths, uris, caption, mine, dialogId, replyToMsg, replyToTopMsg, inputContent, editingMessageObject, notify, scheduleDate); + prepareSendingDocuments(accountInstance, paths, originalPaths, uris, caption, mine, dialogId, replyToMsg, replyToTopMsg, storyItem, editingMessageObject, notify, scheduleDate, inputContent); } @UiThread - public static void prepareSendingAudioDocuments(AccountInstance accountInstance, ArrayList messageObjects, CharSequence caption, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, MessageObject editingMessageObject, boolean notify, int scheduleDate) { + public static void prepareSendingAudioDocuments(AccountInstance accountInstance, ArrayList messageObjects, CharSequence caption, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.StoryItem storyItem, boolean notify, int scheduleDate, MessageObject editingMessageObject) { new Thread(() -> { int count = messageObjects.size(); long groupId = 0; @@ -6748,7 +6791,9 @@ public static void prepareSendingAudioDocuments(AccountInstance accountInstance, if (editingMessageObject != null) { accountInstance.getSendMessagesHelper().editMessage(editingMessageObject, null, null, documentFinal, messageObject.messageOwner.attachPath, params, false, false, parentFinal); } else { - accountInstance.getSendMessagesHelper().sendMessage(documentFinal, null, messageObject.messageOwner.attachPath, dialogId, replyToMsg, replyToTopMsg, captionFinal, entities, null, params, notify, scheduleDate, 0, parentFinal, null, false, false); + SendMessageParams sendMessageParams = SendMessageParams.of(documentFinal, null, messageObject.messageOwner.attachPath, dialogId, replyToMsg, replyToTopMsg, captionFinal, entities, null, params, notify, scheduleDate, 0, parentFinal, null, false, false); + sendMessageParams.replyToStoryItem = storyItem; + accountInstance.getSendMessagesHelper().sendMessage(sendMessageParams); } }); } @@ -6776,7 +6821,7 @@ private static void finishGroup(AccountInstance accountInstance, long groupId, i } @UiThread - public static void prepareSendingDocuments(AccountInstance accountInstance, ArrayList paths, ArrayList originalPaths, ArrayList uris, String caption, String mime, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, InputContentInfoCompat inputContent, MessageObject editingMessageObject, boolean notify, int scheduleDate) { + public static void prepareSendingDocuments(AccountInstance accountInstance, ArrayList paths, ArrayList originalPaths, ArrayList uris, String caption, String mime, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.StoryItem storyItem, MessageObject editingMessageObject, boolean notify, int scheduleDate, InputContentInfoCompat inputContent) { if (paths == null && originalPaths == null && uris == null || paths != null && originalPaths != null && paths.size() != originalPaths.size()) { return; } @@ -6801,7 +6846,7 @@ public static void prepareSendingDocuments(AccountInstance accountInstance, Arra } mediaCount++; long prevGroupId = groupId[0]; - error = prepareSendingDocumentInternal(accountInstance, paths.get(a), originalPaths.get(a), null, mime, dialogId, replyToMsg, replyToTopMsg, captionFinal, null, editingMessageObject, groupId, mediaCount == 10 || a == count - 1, inputContent == null, notify, scheduleDate, docType); + error = prepareSendingDocumentInternal(accountInstance, paths.get(a), originalPaths.get(a), null, mime, dialogId, replyToMsg, replyToTopMsg, storyItem, null, editingMessageObject, groupId, mediaCount == 10 || a == count - 1, captionFinal, notify, scheduleDate, docType, inputContent == null); if (prevGroupId != groupId[0] || groupId[0] == -1) { mediaCount = 1; } @@ -6822,7 +6867,7 @@ public static void prepareSendingDocuments(AccountInstance accountInstance, Arra } mediaCount++; long prevGroupId = groupId[0]; - error = prepareSendingDocumentInternal(accountInstance, null, null, uris.get(a), mime, dialogId, replyToMsg, replyToTopMsg, captionFinal, null, editingMessageObject, groupId, mediaCount == 10 || a == count - 1, inputContent == null, notify, scheduleDate, docType); + error = prepareSendingDocumentInternal(accountInstance, null, null, uris.get(a), mime, dialogId, replyToMsg, replyToTopMsg, storyItem, null, editingMessageObject, groupId, mediaCount == 10 || a == count - 1, captionFinal, notify, scheduleDate, docType, inputContent == null); if (prevGroupId != groupId[0] || groupId[0] == -1) { mediaCount = 1; } @@ -6871,17 +6916,17 @@ public static void prepareSendingLocation(AccountInstance accountInstance, final sendingMedia.geo = new TLRPC.TL_geoPoint(); sendingMedia.geo.lat = location.getLatitude(); sendingMedia.geo._long = location.getLongitude(); - accountInstance.getSendMessagesHelper().sendMessage(sendingMedia, dialog_id, null, null, null, null, true, 0); + accountInstance.getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of(sendingMedia, dialog_id, null, null, null, null, true, 0)); }))); } @UiThread public static void prepareSendingPhoto(AccountInstance accountInstance, String imageFilePath, Uri imageUri, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, CharSequence caption, ArrayList entities, ArrayList stickers, InputContentInfoCompat inputContent, int ttl, MessageObject editingMessageObject, boolean notify, int scheduleDate) { - prepareSendingPhoto(accountInstance, imageFilePath, null, imageUri, dialogId, replyToMsg, replyToTopMsg, caption, entities, stickers, inputContent, ttl, editingMessageObject, null, notify, scheduleDate, false); + prepareSendingPhoto(accountInstance, imageFilePath, null, imageUri, dialogId, replyToMsg, replyToTopMsg, null, entities, stickers, inputContent, ttl, editingMessageObject, null, notify, scheduleDate, false, caption); } @UiThread - public static void prepareSendingPhoto(AccountInstance accountInstance, String imageFilePath, String thumbFilePath, Uri imageUri, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, CharSequence caption, ArrayList entities, ArrayList stickers, InputContentInfoCompat inputContent, int ttl, MessageObject editingMessageObject, VideoEditedInfo videoEditedInfo, boolean notify, int scheduleDate, boolean forceDocument) { + public static void prepareSendingPhoto(AccountInstance accountInstance, String imageFilePath, String thumbFilePath, Uri imageUri, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.StoryItem storyItem, ArrayList entities, ArrayList stickers, InputContentInfoCompat inputContent, int ttl, MessageObject editingMessageObject, VideoEditedInfo videoEditedInfo, boolean notify, int scheduleDate, boolean forceDocument, CharSequence caption) { SendingMediaInfo info = new SendingMediaInfo(); info.path = imageFilePath; info.thumbPath = thumbFilePath; @@ -6897,11 +6942,11 @@ public static void prepareSendingPhoto(AccountInstance accountInstance, String i info.videoEditedInfo = videoEditedInfo; ArrayList infos = new ArrayList<>(); infos.add(info); - prepareSendingMedia(accountInstance, infos, dialogId, replyToMsg, replyToTopMsg, inputContent, forceDocument, false, editingMessageObject, notify, scheduleDate, false); + prepareSendingMedia(accountInstance, infos, dialogId, replyToMsg, replyToTopMsg, null, forceDocument, false, editingMessageObject, notify, scheduleDate, false, inputContent); } @UiThread - public static void prepareSendingBotContextResult(BaseFragment fragment, AccountInstance accountInstance, TLRPC.BotInlineResult result, HashMap params, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, boolean notify, int scheduleDate) { + public static void prepareSendingBotContextResult(BaseFragment fragment, AccountInstance accountInstance, TLRPC.BotInlineResult result, HashMap params, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.StoryItem storyItem, boolean notify, int scheduleDate) { if (result == null) { return; } @@ -7169,15 +7214,20 @@ public static void prepareSendingBotContextResult(BaseFragment fragment, Account return; } AndroidUtilities.runOnUIThread(() -> { + SendMessageParams params2 = null; if (finalDocument != null) { if (precahcedThumb[0] != null && precachedKey[0] != null) { ImageLoader.getInstance().putImageToCache(new BitmapDrawable(precahcedThumb[0]), precachedKey[0], false); } - accountInstance.getSendMessagesHelper().sendMessage(finalDocument, null, finalPathFinal, dialogId, replyToMsg, replyToTopMsg, result.send_message.message, result.send_message.entities, result.send_message.reply_markup, params, notify, scheduleDate, 0, result, null, false); + params2 = SendMessageParams.of(finalDocument, null, finalPathFinal, dialogId, replyToMsg, replyToTopMsg, result.send_message.message, result.send_message.entities, result.send_message.reply_markup, params, notify, scheduleDate, 0, result, null, false); } else if (finalPhoto != null) { - accountInstance.getSendMessagesHelper().sendMessage(finalPhoto, result.content != null ? result.content.url : null, dialogId, replyToMsg, replyToTopMsg, result.send_message.message, result.send_message.entities, result.send_message.reply_markup, params, notify, scheduleDate, 0, result, false); + params2 = SendMessageParams.of(finalPhoto, result.content != null ? result.content.url : null, dialogId, replyToMsg, replyToTopMsg, result.send_message.message, result.send_message.entities, result.send_message.reply_markup, params, notify, scheduleDate, 0, result, false); } else if (finalGame != null) { - accountInstance.getSendMessagesHelper().sendMessage(finalGame, dialogId, replyToMsg, replyToTopMsg, result.send_message.reply_markup, params, notify, scheduleDate); + params2 = SendMessageParams.of(finalGame, dialogId, replyToMsg, replyToTopMsg, result.send_message.reply_markup, params, notify, scheduleDate); + } + if (params2 != null) { + params2.replyToStoryItem = storyItem; + accountInstance.getSendMessagesHelper().sendMessage(params2); } }); }).run(); @@ -7193,7 +7243,7 @@ public static void prepareSendingBotContextResult(BaseFragment fragment, Account } } } - accountInstance.getSendMessagesHelper().sendMessage(result.send_message.message, dialogId, replyToMsg, replyToTopMsg, webPage, !result.send_message.no_webpage, result.send_message.entities, result.send_message.reply_markup, params, notify, scheduleDate, null, false); + accountInstance.getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of(result.send_message.message, dialogId, replyToMsg, replyToTopMsg, webPage, !result.send_message.no_webpage, result.send_message.entities, result.send_message.reply_markup, params, notify, scheduleDate, null, false)); } else if (result.send_message instanceof TLRPC.TL_botInlineMessageMediaVenue) { TLRPC.TL_messageMediaVenue venue = new TLRPC.TL_messageMediaVenue(); venue.geo = result.send_message.geo; @@ -7205,7 +7255,7 @@ public static void prepareSendingBotContextResult(BaseFragment fragment, Account if (venue.venue_type == null) { venue.venue_type = ""; } - accountInstance.getSendMessagesHelper().sendMessage(venue, dialogId, replyToMsg, replyToTopMsg, result.send_message.reply_markup, params, notify, scheduleDate); + accountInstance.getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of(venue, dialogId, replyToMsg, replyToTopMsg, result.send_message.reply_markup, params, notify, scheduleDate)); } else if (result.send_message instanceof TLRPC.TL_botInlineMessageMediaGeo) { if (result.send_message.period != 0 || result.send_message.proximity_notification_radius != 0) { TLRPC.TL_messageMediaGeoLive location = new TLRPC.TL_messageMediaGeoLive(); @@ -7213,12 +7263,12 @@ public static void prepareSendingBotContextResult(BaseFragment fragment, Account location.geo = result.send_message.geo; location.heading = result.send_message.heading; location.proximity_notification_radius = result.send_message.proximity_notification_radius; - accountInstance.getSendMessagesHelper().sendMessage(location, dialogId, replyToMsg, replyToTopMsg, result.send_message.reply_markup, params, notify, scheduleDate); + accountInstance.getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of(location, dialogId, replyToMsg, replyToTopMsg, result.send_message.reply_markup, params, notify, scheduleDate)); } else { TLRPC.TL_messageMediaGeo location = new TLRPC.TL_messageMediaGeo(); location.geo = result.send_message.geo; location.heading = result.send_message.heading; - accountInstance.getSendMessagesHelper().sendMessage(location, dialogId, replyToMsg, replyToTopMsg, result.send_message.reply_markup, params, notify, scheduleDate); + accountInstance.getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of(location, dialogId, replyToMsg, replyToTopMsg, result.send_message.reply_markup, params, notify, scheduleDate)); } } else if (result.send_message instanceof TLRPC.TL_botInlineMessageMediaContact) { TLRPC.User user = new TLRPC.TL_user(); @@ -7230,7 +7280,7 @@ public static void prepareSendingBotContextResult(BaseFragment fragment, Account reason.platform = ""; reason.reason = ""; user.restriction_reason.add(reason); - accountInstance.getSendMessagesHelper().sendMessage(user, dialogId, replyToMsg, replyToTopMsg, result.send_message.reply_markup, params, notify, scheduleDate); + accountInstance.getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of(user, dialogId, replyToMsg, replyToTopMsg, result.send_message.reply_markup, params, notify, scheduleDate)); } else if (result.send_message instanceof TLRPC.TL_botInlineMessageMediaInvoice) { if (DialogObject.isEncryptedDialog(dialogId)) { return; //doesn't work in secret chats for now @@ -7248,7 +7298,7 @@ public static void prepareSendingBotContextResult(BaseFragment fragment, Account messageMediaInvoice.currency = invoice.currency; messageMediaInvoice.total_amount = invoice.total_amount; messageMediaInvoice.start_param = ""; - accountInstance.getSendMessagesHelper().sendMessage(messageMediaInvoice, dialogId, replyToMsg, replyToTopMsg, result.send_message.reply_markup, params, notify, scheduleDate); + accountInstance.getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of(messageMediaInvoice, dialogId, replyToMsg, replyToTopMsg, result.send_message.reply_markup, params, notify, scheduleDate)); } } @@ -7287,7 +7337,7 @@ public static void prepareSendingText(AccountInstance accountInstance, String te } for (int a = 0; a < count; a++) { String mess = textFinal.substring(a * 4096, Math.min((a + 1) * 4096, textFinal.length())); - accountInstance.getSendMessagesHelper().sendMessage(mess, dialogId, replyToMsg, replyToMsg, null, true, null, null, null, notify, scheduleDate, null, false); + accountInstance.getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of(mess, dialogId, replyToMsg, replyToMsg, null, true, null, null, null, notify, scheduleDate, null, false)); } } }))); @@ -7420,7 +7470,7 @@ public static boolean shouldSendWebPAsSticker(String path, Uri uri) { } @UiThread - public static void prepareSendingMedia(AccountInstance accountInstance, ArrayList media, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, InputContentInfoCompat inputContent, boolean forceDocument, boolean groupMedia, MessageObject editingMessageObject, boolean notify, int scheduleDate, boolean updateStikcersOrder) { + public static void prepareSendingMedia(AccountInstance accountInstance, ArrayList media, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.StoryItem storyItem, boolean forceDocument, boolean groupMedia, MessageObject editingMessageObject, boolean notify, int scheduleDate, boolean updateStikcersOrder, InputContentInfoCompat inputContent) { if (media.isEmpty()) { return; } @@ -7624,7 +7674,9 @@ public static void prepareSendingMedia(AccountInstance accountInstance, ArrayLis if (editingMessageObject != null) { accountInstance.getSendMessagesHelper().editMessage(editingMessageObject, null, null, documentFinal, pathFinal, params, false, info.hasMediaSpoilers, parentFinal); } else { - accountInstance.getSendMessagesHelper().sendMessage(documentFinal, null, pathFinal, dialogId, replyToMsg, replyToTopMsg, info.caption, info.entities, null, params, notify, scheduleDate, 0, parentFinal, null, false, info.hasMediaSpoilers); + SendMessageParams sendMessageParams = SendMessageParams.of(documentFinal, null, pathFinal, dialogId, replyToMsg, replyToTopMsg, info.caption, info.entities, null, params, notify, scheduleDate, 0, parentFinal, null, false, info.hasMediaSpoilers); + sendMessageParams.replyToStoryItem = storyItem; + accountInstance.getSendMessagesHelper().sendMessage(sendMessageParams); } }); } else { @@ -7695,7 +7747,9 @@ public static void prepareSendingMedia(AccountInstance accountInstance, ArrayLis if (editingMessageObject != null) { accountInstance.getSendMessagesHelper().editMessage(editingMessageObject, photoFinal, null, null, needDownloadHttpFinal ? info.searchImage.imageUrl : null, params, false, info.hasMediaSpoilers, parentFinal); } else { - accountInstance.getSendMessagesHelper().sendMessage(photoFinal, needDownloadHttpFinal ? info.searchImage.imageUrl : null, dialogId, replyToMsg, replyToTopMsg, info.caption, info.entities, null, params, notify, scheduleDate, info.ttl, parentFinal, false, info.hasMediaSpoilers); + SendMessageParams sendMessageParams = SendMessageParams.of(photoFinal, needDownloadHttpFinal ? info.searchImage.imageUrl : null, dialogId, replyToMsg, replyToTopMsg, info.caption, info.entities, null, params, notify, scheduleDate, info.ttl, parentFinal, false, info.hasMediaSpoilers); + sendMessageParams.replyToStoryItem = storyItem; + accountInstance.getSendMessagesHelper().sendMessage(sendMessageParams); } }); } @@ -7758,21 +7812,15 @@ public static void prepareSendingMedia(AccountInstance accountInstance, ArrayLis } TLRPC.PhotoSize size = null; - String localPath = null; if (thumb != null) { int side = isEncrypted || info.ttl != 0 ? 90 : Math.max(thumb.getWidth(), thumb.getHeight()); size = ImageLoader.scaleAndSaveImage(thumb, side, side, side > 90 ? 80 : 55, isEncrypted); if (size != null && size.location != null) { thumbKey = getKeyForPhotoSize(accountInstance, size, null, true, false); - - String fileName = size.location.volume_id + "_" + size.location.local_id + ".jpg"; - File fileDir = FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE); - localPath = new File(fileDir, fileName).getAbsolutePath(); } } document = new TLRPC.TL_document(); document.file_reference = new byte[0]; - document.localPath = localPath; if (size != null) { document.thumbs.add(size); document.flags |= 1; @@ -7875,7 +7923,9 @@ public static void prepareSendingMedia(AccountInstance accountInstance, ArrayLis if (editingMessageObject != null) { accountInstance.getSendMessagesHelper().editMessage(editingMessageObject, null, videoEditedInfo, videoFinal, finalPath, params, false, info.hasMediaSpoilers, parentFinal); } else { - accountInstance.getSendMessagesHelper().sendMessage(videoFinal, videoEditedInfo, finalPath, dialogId, replyToMsg, replyToTopMsg, info.caption, info.entities, null, params, notify, scheduleDate, info.ttl, parentFinal, null, false, info.hasMediaSpoilers); + SendMessageParams sendMessageParams = SendMessageParams.of(videoFinal, videoEditedInfo, finalPath, dialogId, replyToMsg, replyToTopMsg, info.caption, info.entities, null, params, notify, scheduleDate, info.ttl, parentFinal, null, false, info.hasMediaSpoilers); + sendMessageParams.replyToStoryItem = storyItem; + accountInstance.getSendMessagesHelper().sendMessage(sendMessageParams); } }); } else { @@ -8072,7 +8122,9 @@ public static void prepareSendingMedia(AccountInstance accountInstance, ArrayLis if (editingMessageObject != null) { accountInstance.getSendMessagesHelper().editMessage(editingMessageObject, photoFinal, null, null, null, params, false, info.hasMediaSpoilers, parentFinal); } else { - accountInstance.getSendMessagesHelper().sendMessage(photoFinal, null, dialogId, replyToMsg, replyToTopMsg, info.caption, info.entities, null, params, notify, scheduleDate, info.ttl, parentFinal, updateStikcersOrder, info.hasMediaSpoilers); + SendMessageParams sendMessageParams = SendMessageParams.of(photoFinal, null, dialogId, replyToMsg, replyToTopMsg, info.caption, info.entities, null, params, notify, scheduleDate, info.ttl, parentFinal, updateStikcersOrder, info.hasMediaSpoilers); + sendMessageParams.replyToStoryItem = storyItem; + accountInstance.getSendMessagesHelper().sendMessage(sendMessageParams); } }); } else { @@ -8108,7 +8160,7 @@ public static void prepareSendingMedia(AccountInstance accountInstance, ArrayLis mediaCount = 0; } mediaCount++; - int error = prepareSendingDocumentInternal(accountInstance, sendAsDocuments.get(a), sendAsDocumentsOriginal.get(a), sendAsDocumentsUri.get(a), extension, dialogId, replyToMsg, replyToTopMsg, sendAsDocumentsCaptions.get(a), sendAsDocumentsEntities.get(a), editingMessageObject, groupId2, mediaCount == 10 || a == documentsCount - 1, forceDocument, notify, scheduleDate, null); + int error = prepareSendingDocumentInternal(accountInstance, sendAsDocuments.get(a), sendAsDocumentsOriginal.get(a), sendAsDocumentsUri.get(a), extension, dialogId, replyToMsg, replyToTopMsg, storyItem, sendAsDocumentsEntities.get(a), editingMessageObject, groupId2, mediaCount == 10 || a == documentsCount - 1, sendAsDocumentsCaptions.get(a), notify, scheduleDate, null, forceDocument); handleError(error, accountInstance); } } @@ -8118,7 +8170,7 @@ public static void prepareSendingMedia(AccountInstance accountInstance, ArrayLis }); } - private static void fillVideoAttribute(String videoPath, TLRPC.TL_documentAttributeVideo attributeVideo, VideoEditedInfo videoEditedInfo) { + public static void fillVideoAttribute(String videoPath, TLRPC.TL_documentAttributeVideo attributeVideo, VideoEditedInfo videoEditedInfo) { boolean infoObtained = false; MediaMetadataRetriever mediaMetadataRetriever = null; @@ -8135,7 +8187,7 @@ private static void fillVideoAttribute(String videoPath, TLRPC.TL_documentAttrib } String duration = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION); if (duration != null) { - attributeVideo.duration = (int) Math.ceil(Long.parseLong(duration) / 1000.0f); + attributeVideo.duration = Long.parseLong(duration) / 1000.0; } if (Build.VERSION.SDK_INT >= 17) { String rotation = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION); @@ -8166,7 +8218,7 @@ private static void fillVideoAttribute(String videoPath, TLRPC.TL_documentAttrib try { MediaPlayer mp = MediaPlayer.create(ApplicationLoader.applicationContext, Uri.fromFile(new File(videoPath))); if (mp != null) { - attributeVideo.duration = (int) Math.ceil(mp.getDuration() / 1000.0f); + attributeVideo.duration = mp.getDuration() / 1000.0; attributeVideo.w = mp.getVideoWidth(); attributeVideo.h = mp.getVideoHeight(); mp.release(); @@ -8248,6 +8300,7 @@ private static VideoEditedInfo createCompressionSettings(String videoPath) { return null; } + long originalSize = new File(videoPath).length(); int originalBitrate = MediaController.getVideoBitrate(videoPath); if (originalBitrate == -1) { originalBitrate = params[AnimatedFileDrawable.PARAM_NUM_BITRATE]; @@ -8357,11 +8410,13 @@ private static VideoEditedInfo createCompressionSettings(String videoPath) { videoEditedInfo.resultWidth = videoEditedInfo.originalWidth; videoEditedInfo.resultHeight = videoEditedInfo.originalHeight; videoEditedInfo.bitrate = bitrate; + videoEditedInfo.estimatedSize = originalSize; } else { videoEditedInfo.bitrate = bitrate; + int encoderBitrate = MediaController.extractRealEncoderBitrate(videoEditedInfo.resultWidth, videoEditedInfo.resultHeight, bitrate, false); + videoEditedInfo.estimatedSize = (long) (audioFramesSize + videoDuration / 1000.0f * encoderBitrate / 8); } - videoEditedInfo.estimatedSize = (long) (audioFramesSize + videoDuration / 1000.0f * bitrate / 8); if (videoEditedInfo.estimatedSize == 0) { videoEditedInfo.estimatedSize = 1; } @@ -8370,7 +8425,7 @@ private static VideoEditedInfo createCompressionSettings(String videoPath) { } @UiThread - public static void prepareSendingVideo(AccountInstance accountInstance, String videoPath, VideoEditedInfo info, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, CharSequence caption, ArrayList entities, int ttl, MessageObject editingMessageObject, boolean notify, int scheduleDate, boolean forceDocument, boolean hasMediaSpoilers) { + public static void prepareSendingVideo(AccountInstance accountInstance, String videoPath, VideoEditedInfo info, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.StoryItem storyItem, ArrayList entities, int ttl, MessageObject editingMessageObject, boolean notify, int scheduleDate, boolean forceDocument, boolean hasMediaSpoilers, CharSequence caption) { if (videoPath == null || videoPath.length() == 0) { return; } @@ -8517,12 +8572,131 @@ public static void prepareSendingVideo(AccountInstance accountInstance, String v if (editingMessageObject != null) { accountInstance.getSendMessagesHelper().editMessage(editingMessageObject, null, videoEditedInfo, videoFinal, finalPath, params, false, hasMediaSpoilers, parentFinal); } else { - accountInstance.getSendMessagesHelper().sendMessage(videoFinal, videoEditedInfo, finalPath, dialogId, replyToMsg, replyToTopMsg, captionFinal, entities, null, params, notify, scheduleDate, ttl, parentFinal, null, false, hasMediaSpoilers); + SendMessageParams sendMessageParams = SendMessageParams.of(videoFinal, videoEditedInfo, finalPath, dialogId, replyToMsg, replyToTopMsg, captionFinal, entities, null, params, notify, scheduleDate, ttl, parentFinal, null, false, hasMediaSpoilers); + sendMessageParams.replyToStoryItem = storyItem; + accountInstance.getSendMessagesHelper().sendMessage(sendMessageParams); } }); } else { - prepareSendingDocumentInternal(accountInstance, videoPath, videoPath, null, null, dialogId, replyToMsg, replyToTopMsg, caption, entities, editingMessageObject, null, false, forceDocument, notify, scheduleDate, null); + prepareSendingDocumentInternal(accountInstance, videoPath, videoPath, null, null, dialogId, replyToMsg, replyToTopMsg, storyItem, entities, editingMessageObject, null, false, caption, notify, scheduleDate, null, forceDocument); } }).start(); } + + public static class SendMessageParams { + public String message; + public String caption; + public TLRPC.MessageMedia location; + public TLRPC.TL_photo photo; + public VideoEditedInfo videoEditedInfo; + public TLRPC.User user; + public TLRPC.TL_document document; + public TLRPC.TL_game game; + public TLRPC.TL_messageMediaPoll poll; + public TLRPC.TL_messageMediaInvoice invoice; + public long peer; + public String path; + public MessageObject replyToMsg; + public MessageObject replyToTopMsg; + public TLRPC.WebPage webPage; + public boolean searchLinks = true; + public MessageObject retryMessageObject; + public ArrayList entities; + public TLRPC.ReplyMarkup replyMarkup; + public HashMap params; + public boolean notify; + public int scheduleDate; + public int ttl; + public Object parentObject; + public MessageObject.SendAnimationData sendAnimationData; + public boolean updateStickersOrder; + public boolean hasMediaSpoilers; + public TLRPC.StoryItem replyToStoryItem; + public TLRPC.StoryItem sendingStory; + public boolean canSendGames; + + public static SendMessageParams of(String string, long dialogId) { + return of(string, null, null, null, null, null, null, null, null, null, dialogId, null, null, null, null, true, null, null, null, null, false, 0, 0, null, null, false); + } + + public static SendMessageParams of(MessageObject retryMessageObject) { + return of(null, null, null, null, null, null, null, null, null, null, retryMessageObject.getDialogId(), retryMessageObject.messageOwner.attachPath, null, null, null, true, retryMessageObject, null, retryMessageObject.messageOwner.reply_markup, retryMessageObject.messageOwner.params, !retryMessageObject.messageOwner.silent, retryMessageObject.scheduled ? retryMessageObject.messageOwner.date : 0, 0, null, null, false); + } + + public static SendMessageParams of(TLRPC.User user, long peer, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.ReplyMarkup replyMarkup, HashMap params, boolean notify, int scheduleDate) { + return of(null, null, null, null, null, user, null, null, null, null, peer, null, replyToMsg, replyToTopMsg, null, true, null, null, replyMarkup, params, notify, scheduleDate, 0, null, null, false); + } + + public static SendMessageParams of(TLRPC.TL_messageMediaInvoice invoice, long peer, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.ReplyMarkup replyMarkup, HashMap params, boolean notify, int scheduleDate) { + return of(null, null, null, null, null, null, null, null, null, invoice, peer, null, replyToMsg, replyToTopMsg, null, true, null, null, replyMarkup, params, notify, scheduleDate, 0, null, null, false); + } + + public static SendMessageParams of(TLRPC.TL_document document, VideoEditedInfo videoEditedInfo, String path, long peer, MessageObject replyToMsg, MessageObject replyToTopMsg, String caption, ArrayList entities, TLRPC.ReplyMarkup replyMarkup, HashMap params, boolean notify, int scheduleDate, int ttl, Object parentObject, MessageObject.SendAnimationData sendAnimationData, boolean updateStickersOrder) { + return of(null, caption, null, null, videoEditedInfo, null, document, null, null, null, peer, path, replyToMsg, replyToTopMsg, null, true, null, entities, replyMarkup, params, notify, scheduleDate, ttl, parentObject, sendAnimationData, updateStickersOrder); + } + + public static SendMessageParams of(TLRPC.TL_document document, VideoEditedInfo videoEditedInfo, String path, long peer, MessageObject replyToMsg, MessageObject replyToTopMsg, String caption, ArrayList entities, TLRPC.ReplyMarkup replyMarkup, HashMap params, boolean notify, int scheduleDate, int ttl, Object parentObject, MessageObject.SendAnimationData sendAnimationData, boolean updateStickersOrder, boolean hasMediaSpoilers) { + return of(null, caption, null, null, videoEditedInfo, null, document, null, null, null, peer, path, replyToMsg, replyToTopMsg, null, true, null, entities, replyMarkup, params, notify, scheduleDate, ttl, parentObject, sendAnimationData, updateStickersOrder, hasMediaSpoilers); + } + + public static SendMessageParams of(String message, long peer, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.WebPage webPage, boolean searchLinks, ArrayList entities, TLRPC.ReplyMarkup replyMarkup, HashMap params, boolean notify, int scheduleDate, MessageObject.SendAnimationData sendAnimationData, boolean updateStickersOrder) { + return of(message, null, null, null, null, null, null, null, null, null, peer, null, replyToMsg, replyToTopMsg, webPage, searchLinks, null, entities, replyMarkup, params, notify, scheduleDate, 0, null, sendAnimationData, updateStickersOrder); + } + + public static SendMessageParams of(TLRPC.MessageMedia location, long peer, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.ReplyMarkup replyMarkup, HashMap params, boolean notify, int scheduleDate) { + return of(null, null, location, null, null, null, null, null, null, null, peer, null, replyToMsg, replyToTopMsg, null, true, null, null, replyMarkup, params, notify, scheduleDate, 0, null, null, false); + } + + public static SendMessageParams of(TLRPC.TL_messageMediaPoll poll, long peer, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.ReplyMarkup replyMarkup, HashMap params, boolean notify, int scheduleDate) { + return of(null, null, null, null, null, null, null, null, poll, null, peer, null, replyToMsg, replyToTopMsg, null, true, null, null, replyMarkup, params, notify, scheduleDate, 0, null, null, false); + } + + public static SendMessageParams of(TLRPC.TL_game game, long peer, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.ReplyMarkup replyMarkup, HashMap params, boolean notify, int scheduleDate) { + return of(null, null, null, null, null, null, null, game, null, null, peer, null, replyToMsg, replyToTopMsg, null, true, null, null, replyMarkup, params, notify, scheduleDate, 0, null, null, false); + } + + public static SendMessageParams of(TLRPC.TL_photo photo, String path, long peer, MessageObject replyToMsg, MessageObject replyToTopMsg, String caption, ArrayList entities, TLRPC.ReplyMarkup replyMarkup, HashMap params, boolean notify, int scheduleDate, int ttl, Object parentObject, boolean updateStickersOrder, boolean hasMediaSpoilers) { + return of(null, caption, null, photo, null, null, null, null, null, null, peer, path, replyToMsg, replyToTopMsg, null, true, null, entities, replyMarkup, params, notify, scheduleDate, ttl, parentObject, null, updateStickersOrder, hasMediaSpoilers); + } + + public static SendMessageParams of(TLRPC.TL_photo photo, String path, long peer, MessageObject replyToMsg, MessageObject replyToTopMsg, String caption, ArrayList entities, TLRPC.ReplyMarkup replyMarkup, HashMap params, boolean notify, int scheduleDate, int ttl, Object parentObject, boolean updateStickersOrder) { + return of(null, caption, null, photo, null, null, null, null, null, null, peer, path, replyToMsg, replyToTopMsg, null, true, null, entities, replyMarkup, params, notify, scheduleDate, ttl, parentObject, null, updateStickersOrder); + } + + private static SendMessageParams of(String message, String caption, TLRPC.MessageMedia location, TLRPC.TL_photo photo, VideoEditedInfo videoEditedInfo, TLRPC.User user, TLRPC.TL_document document, TLRPC.TL_game game, TLRPC.TL_messageMediaPoll poll, TLRPC.TL_messageMediaInvoice invoice, long peer, String path, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.WebPage webPage, boolean searchLinks, MessageObject retryMessageObject, ArrayList entities, TLRPC.ReplyMarkup replyMarkup, HashMap params, boolean notify, int scheduleDate, int ttl, Object parentObject, MessageObject.SendAnimationData sendAnimationData, boolean updateStickersOrder) { + return of(message, caption, location, photo, videoEditedInfo, user, document, game, poll, invoice, peer, path, replyToMsg, replyToTopMsg, webPage, searchLinks, retryMessageObject, entities, replyMarkup, params, notify, scheduleDate, ttl, parentObject, sendAnimationData, updateStickersOrder, false); + } + + public static SendMessageParams of(String message, String caption, TLRPC.MessageMedia location, TLRPC.TL_photo photo, VideoEditedInfo videoEditedInfo, TLRPC.User user, TLRPC.TL_document document, TLRPC.TL_game game, TLRPC.TL_messageMediaPoll poll, TLRPC.TL_messageMediaInvoice invoice, long peer, String path, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.WebPage webPage, boolean searchLinks, MessageObject retryMessageObject, ArrayList entities, TLRPC.ReplyMarkup replyMarkup, HashMap messageParams, boolean notify, int scheduleDate, int ttl, Object parentObject, MessageObject.SendAnimationData sendAnimationData, boolean updateStickersOrder, boolean hasMediaSpoilers) { + SendMessageParams params = new SendMessageParams(); + params.message = message; + params.caption = caption; + params.location = location; + params.photo = photo; + params.videoEditedInfo = videoEditedInfo; + params.user = user; + params.document = document; + params.game = game; + params.poll = poll; + params.invoice = invoice; + params.peer = peer; + params.path = path; + params.replyToMsg = replyToMsg; + params.replyToTopMsg = replyToTopMsg; + params.webPage = webPage; + params.searchLinks = searchLinks; + params.retryMessageObject = retryMessageObject; + params.entities = entities; + params.replyMarkup = replyMarkup; + params.params = messageParams; + params.notify = notify; + params.scheduleDate = scheduleDate; + params.ttl = ttl; + params.parentObject = parentObject; + params.sendAnimationData = sendAnimationData; + params.updateStickersOrder = updateStickersOrder; + params.hasMediaSpoilers = hasMediaSpoilers; + return params; + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java b/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java index cdb5305628..bb1f607285 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java @@ -15,6 +15,8 @@ import android.content.SharedPreferences; import android.net.Uri; import android.content.pm.PackageInfo; +import android.media.MediaCodecInfo; +import android.media.MediaCodecList; import android.os.Build; import android.os.SystemClock; import android.text.TextUtils; @@ -22,6 +24,8 @@ import android.webkit.WebView; import androidx.annotation.Nullable; +import androidx.annotation.IntDef; +import androidx.annotation.RequiresApi; import androidx.core.content.pm.ShortcutManagerCompat; import com.v2ray.ang.V2RayConfig; @@ -56,6 +60,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.concurrent.CopyOnWriteArraySet; @@ -133,6 +138,86 @@ public static void checkSdCard(File file) { } } + static Boolean allowPreparingHevcPlayers; + + public static boolean allowPreparingHevcPlayers() { + if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) { + return false; + } + if (allowPreparingHevcPlayers == null) { + int codecCount = MediaCodecList.getCodecCount(); + int maxInstances = 0; + int capabilities = 0; + + for (int i = 0; i < codecCount; i++) { + MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i); + if (codecInfo.isEncoder()) { + continue; + } + + boolean found = false; + for (int k = 0; k < codecInfo.getSupportedTypes().length; k++) { + if (codecInfo.getSupportedTypes()[k].contains("video/hevc")) { + found = true; + break; + } + } + if (!found) { + continue; + } + capabilities = codecInfo.getCapabilitiesForType("video/hevc").getMaxSupportedInstances(); + if (capabilities > maxInstances) { + maxInstances = capabilities; + } + } + allowPreparingHevcPlayers = maxInstances >= 8; + } + return allowPreparingHevcPlayers; + } + + public static void toggleSurfaceInStories() { + useSurfaceInStories = !useSurfaceInStories; + ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE) + .edit() + .putBoolean("useSurfaceInStories", useSurfaceInStories) + .apply(); + } + + private static String goodHevcEncoder; + private static HashSet hevcEncoderWhitelist = new HashSet<>(); + static { + hevcEncoderWhitelist.add("c2.exynos.hevc.encoder"); + hevcEncoderWhitelist.add("OMX.Exynos.HEVC.Encoder".toLowerCase()); + } + + @RequiresApi(api = Build.VERSION_CODES.Q) + public static String findGoodHevcEncoder() { + if (goodHevcEncoder == null) { + int codecCount = MediaCodecList.getCodecCount(); + for (int i = 0; i < codecCount; i++) { + MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i); + if (!codecInfo.isEncoder()) { + continue; + } + + for (int k = 0; k < codecInfo.getSupportedTypes().length; k++) { + if (codecInfo.getSupportedTypes()[k].contains("video/hevc") && codecInfo.isHardwareAccelerated() && isWhitelisted(codecInfo)) { + return goodHevcEncoder = codecInfo.getName(); + } + } + } + goodHevcEncoder = ""; + } + return TextUtils.isEmpty(goodHevcEncoder) ? null : goodHevcEncoder; + } + + private static boolean isWhitelisted(MediaCodecInfo codecInfo) { + if (BuildVars.DEBUG_PRIVATE_VERSION) { + return true; + } + return hevcEncoderWhitelist.contains(codecInfo.getName().toLowerCase()); + } + @Retention(RetentionPolicy.SOURCE) @IntDef({ PASSCODE_TYPE_PIN, @@ -184,10 +269,12 @@ public static void checkSdCard(File file) { public static boolean forwardingOptionsHintShown; public static boolean searchMessagesAsListUsed; public static boolean stickersReorderingHintUsed; + public static int dayNightWallpaperSwitchHint; public static boolean disableVoiceAudioEffects; public static boolean forceDisableTabletMode; public static boolean updateStickersOrderOnSend = true; public static boolean bigCameraForRound; + public static boolean useSurfaceInStories; private static int lastLocalId = -210000; public static String storageCacheDir; @@ -205,6 +292,7 @@ public static void checkSdCard(File file) { public static boolean chatBubbles = Build.VERSION.SDK_INT >= 30; public static boolean raiseToSpeak = false; public static boolean raiseToListen = true; + public static boolean nextMediaTap = true; public static boolean recordViaSco = false; public static boolean customTabs = true; public static boolean directShare = true; @@ -257,6 +345,7 @@ public static void checkSdCard(File file) { public static int distanceSystemType; public static int mediaColumnsCount = 3; + public static int storiesColumnsCount = 3; public static int fastScrollHintCount = 3; public static boolean dontAskManageStorage; @@ -282,35 +371,6 @@ public static void checkSdCard(File file) { -1853602818 // SDM439 }; - private static final int[] LOW_DEVICES = { - 1903542002, // XIAOMI NIKEL (Redmi Note 4) - 1904553494, // XIAOMI OLIVE (Redmi 8) - 1616144535, // OPPO CPH2273 (Oppo A54s) - -713271737, // OPPO OP4F2F (Oppo A54) - -1394191140, // SAMSUNG A12 (Galaxy A12) - -270252297, // SAMSUNG A12S (Galaxy A12) - -270251367, // SAMSUNG A21S (Galaxy A21s) - -270252359 // SAMSUNG A10S (Galaxy A10s) - }; - - private static final int[] AVERAGE_DEVICES = { - 812981419, // XIAOMI ANGELICA (Redmi 9C) - -993913431 // XIAOMI DANDELION (Redmi 9A) - }; - - private static final int[] HIGH_DEVICES = { - 1908570923, // XIAOMI SWEET (Redmi Note 10 Pro) - -980514379, // XIAOMI SECRET (Redmi Note 10S) - 577463889, // XIAOMI JOYEUSE (Redmi Note 9 Pro) - 1764745014, // XIAOMI BEGONIA (Redmi Note 8 Pro) - 1908524435, // XIAOMI SURYA (Poco X3 NFC) - -215787089, // XIAOMI KAMA (Poco X3) - -215458996, // XIAOMI VAYU (Poco X3 Pro) - -1394179578, // SAMSUNG M21 - 220599115, // SAMSUNG J6LTE - 1737652784 // SAMSUNG J6PRIMELTE - }; - static { loadConfig(); } @@ -1360,6 +1420,7 @@ public static void loadConfig() { mapPreviewType = preferences.getInt("mapPreviewType", 2); raiseToListen = preferences.getBoolean("raise_to_listen", true); raiseToSpeak = preferences.getBoolean("raise_to_speak", false); + nextMediaTap = preferences.getBoolean("next_media_on_tap", true); recordViaSco = preferences.getBoolean("record_via_sco", false); customTabs = preferences.getBoolean("custom_tabs", true); directShare = preferences.getBoolean("direct_share", true); @@ -1442,12 +1503,15 @@ public static void loadConfig() { preferences.edit().putBoolean("activeAccountsLoaded", true).apply(); } mediaColumnsCount = preferences.getInt("mediaColumnsCount", 3); + storiesColumnsCount = preferences.getInt("storiesColumnsCount", 3); fastScrollHintCount = preferences.getInt("fastScrollHintCount", 3); dontAskManageStorage = preferences.getBoolean("dontAskManageStorage", false); hasEmailLogin = preferences.getBoolean("hasEmailLogin", false); isFloatingDebugActive = preferences.getBoolean("floatingDebugActive", false); updateStickersOrderOnSend = preferences.getBoolean("updateStickersOrderOnSend", true); + dayNightWallpaperSwitchHint = preferences.getInt("dayNightWallpaperSwitchHint", 0); bigCameraForRound = preferences.getBoolean("bigCameraForRound", false); + useSurfaceInStories = preferences.getBoolean("useSurfaceInStories", Build.VERSION.SDK_INT >= 30); preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); showNotificationsForAllAccounts = preferences.getBoolean("AllAccounts", true); @@ -1615,6 +1679,7 @@ public static void clearConfig() { messageSeenHintCount = 3; emojiInteractionsHintCount = 3; dayNightThemeSwitchHintCount = 3; + dayNightWallpaperSwitchHint = 0; saveConfig(); } @@ -1623,7 +1688,7 @@ public static void setSuggestStickers(int type) { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putInt("suggestStickers", suggestStickers); - editor.commit(); + editor.apply(); } public static void setSearchMessagesAsListUsed(boolean value) { @@ -1631,7 +1696,7 @@ public static void setSearchMessagesAsListUsed(boolean value) { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("searchMessagesAsListUsed", searchMessagesAsListUsed); - editor.commit(); + editor.apply(); } public static void setStickersReorderingHintUsed(boolean value) { @@ -1639,28 +1704,35 @@ public static void setStickersReorderingHintUsed(boolean value) { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("stickersReorderingHintUsed", stickersReorderingHintUsed); - editor.commit(); + editor.apply(); } public static void increaseTextSelectionHintShowed() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putInt("textSelectionHintShows", ++textSelectionHintShows); - editor.commit(); + editor.apply(); + } + + public static void increaseDayNightWallpaperSiwtchHint() { + SharedPreferences preferences = MessagesController.getGlobalMainSettings(); + SharedPreferences.Editor editor = preferences.edit(); + editor.putInt("dayNightWallpaperSwitchHint", ++dayNightWallpaperSwitchHint); + editor.apply(); } public static void removeTextSelectionHint() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putInt("textSelectionHintShows", 3); - editor.commit(); + editor.apply(); } public static void increaseScheduledOrNoSuoundHintShowed() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putInt("scheduledOrNoSoundHintShows", ++scheduledOrNoSoundHintShows); - editor.commit(); + editor.apply(); } public static void forwardingOptionsHintHintShowed() { @@ -1668,35 +1740,35 @@ public static void forwardingOptionsHintHintShowed() { SharedPreferences.Editor editor = preferences.edit(); forwardingOptionsHintShown = true; editor.putBoolean("forwardingOptionsHintShown", forwardingOptionsHintShown); - editor.commit(); + editor.apply(); } public static void removeScheduledOrNoSoundHint() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putInt("scheduledOrNoSoundHintShows", 3); - editor.commit(); + editor.apply(); } public static void increaseLockRecordAudioVideoHintShowed() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putInt("lockRecordAudioVideoHint", ++lockRecordAudioVideoHint); - editor.commit(); + editor.apply(); } public static void removeLockRecordAudioVideoHint() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putInt("lockRecordAudioVideoHint", 3); - editor.commit(); + editor.apply(); } public static void increaseSearchAsListHintShows() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putInt("searchMessagesAsListHintShows", ++searchMessagesAsListHintShows); - editor.commit(); + editor.apply(); } public static void setKeepMedia(int value) { @@ -1704,14 +1776,14 @@ public static void setKeepMedia(int value) { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putInt("keep_media", keepMedia); - editor.commit(); + editor.apply(); } public static void toggleUpdateStickersOrderOnSend() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("updateStickersOrderOnSend", updateStickersOrderOnSend = !updateStickersOrderOnSend); - editor.commit(); + editor.apply(); } public static void checkLogsToDelete() { @@ -1737,7 +1809,7 @@ public static void checkLogsToDelete() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putInt("lastLogsCheckTime", lastLogsCheckTime); - editor.commit(); + editor.apply(); }); } @@ -1746,7 +1818,7 @@ public static void toggleDisableVoiceAudioEffects() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("disableVoiceAudioEffects", disableVoiceAudioEffects); - editor.commit(); + editor.apply(); } public static void toggleNoiseSupression() { @@ -1754,7 +1826,7 @@ public static void toggleNoiseSupression() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("noiseSupression", noiseSupression); - editor.commit(); + editor.apply(); } public static void toggleDebugWebView() { @@ -1777,7 +1849,7 @@ public static void toggleBigEmoji() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("allowBigEmoji", allowBigEmoji); - editor.commit(); + editor.apply(); } public static void toggleSuggestAnimatedEmoji() { @@ -1785,7 +1857,7 @@ public static void toggleSuggestAnimatedEmoji() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("suggestAnimatedEmoji", suggestAnimatedEmoji); - editor.commit(); + editor.apply(); } public static void setPlaybackOrderType(int type) { @@ -1804,7 +1876,7 @@ public static void setPlaybackOrderType(int type) { SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("shuffleMusic", shuffleMusic); editor.putBoolean("playOrderReversed", playOrderReversed); - editor.commit(); + editor.apply(); } public static void setRepeatMode(int mode) { @@ -1815,11 +1887,11 @@ public static void setRepeatMode(int mode) { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putInt("repeatMode", repeatMode); - editor.commit(); + editor.apply(); } public static void overrideDevicePerformanceClass(int performanceClass) { - MessagesController.getGlobalMainSettings().edit().putInt("overrideDevicePerformanceClass", overrideDevicePerformanceClass = performanceClass).remove("lite_mode").commit(); + MessagesController.getGlobalMainSettings().edit().putInt("overrideDevicePerformanceClass", overrideDevicePerformanceClass = performanceClass).remove("lite_mode").apply(); if (liteMode != null) { liteMode.loadPreference(); } @@ -1834,7 +1906,7 @@ public static void setUseThreeLinesLayout(boolean value) { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("useThreeLinesLayout", useThreeLinesLayout); - editor.commit(); + editor.apply(); NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.dialogsNeedReload, true); } @@ -1843,7 +1915,7 @@ public static void toggleArchiveHidden() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("archiveHidden", archiveHidden); - editor.commit(); + editor.apply(); } public static void toggleAutoplayVideo() { @@ -1860,7 +1932,7 @@ public static void setSecretMapPreviewType(int value) { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putInt("mapPreviewType", mapPreviewType); - editor.commit(); + editor.apply(); } public static void setNoSoundHintShowed(boolean value) { @@ -1871,7 +1943,7 @@ public static void setNoSoundHintShowed(boolean value) { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("noSoundHintShowed", noSoundHintShowed); - editor.commit(); + editor.apply(); } public static void toggleRaiseToSpeak() { @@ -1879,7 +1951,7 @@ public static void toggleRaiseToSpeak() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("raise_to_speak", raiseToSpeak); - editor.commit(); + editor.apply(); } public static void toggleRaiseToListen() { @@ -1887,7 +1959,15 @@ public static void toggleRaiseToListen() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("raise_to_listen", raiseToListen); - editor.commit(); + editor.apply(); + } + + public static void toggleNextMediaTap() { + nextMediaTap = !nextMediaTap; + SharedPreferences preferences = MessagesController.getGlobalMainSettings(); + SharedPreferences.Editor editor = preferences.edit(); + editor.putBoolean("next_media_on_tap", nextMediaTap); + editor.apply(); } public static boolean enabledRaiseTo(boolean speak) { @@ -1899,7 +1979,7 @@ public static void toggleCustomTabs() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("custom_tabs", customTabs); - editor.commit(); + editor.apply(); } public static void toggleDirectShare() { @@ -1907,7 +1987,7 @@ public static void toggleDirectShare() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("direct_share", directShare); - editor.commit(); + editor.apply(); ShortcutManagerCompat.removeAllDynamicShortcuts(ApplicationLoader.applicationContext); MediaDataController.getInstance(UserConfig.selectedAccount).buildShortcuts(); } @@ -1917,7 +1997,7 @@ public static void toggleStreamMedia() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("streamMedia", streamMedia); - editor.commit(); + editor.apply(); } public static void toggleSortContactsByName() { @@ -1925,7 +2005,7 @@ public static void toggleSortContactsByName() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("sortContactsByName", sortContactsByName); - editor.commit(); + editor.apply(); } public static void toggleSortFilesByName() { @@ -1933,7 +2013,7 @@ public static void toggleSortFilesByName() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("sortFilesByName", sortFilesByName); - editor.commit(); + editor.apply(); } public static void toggleStreamAllVideo() { @@ -1941,7 +2021,7 @@ public static void toggleStreamAllVideo() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("streamAllVideo", streamAllVideo); - editor.commit(); + editor.apply(); } public static void toggleStreamMkv() { @@ -1949,7 +2029,7 @@ public static void toggleStreamMkv() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("streamMkv", streamMkv); - editor.commit(); + editor.apply(); } public static void toggleSaveStreamMedia() { @@ -1957,7 +2037,7 @@ public static void toggleSaveStreamMedia() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("saveStreamMedia", saveStreamMedia); - editor.commit(); + editor.apply(); } public static void setSmoothKeyboard(boolean smoothKeyboard) { @@ -1972,7 +2052,7 @@ public static void togglePauseMusicOnRecord() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("pauseMusicOnRecord", pauseMusicOnRecord); - editor.commit(); + editor.apply(); } public static void togglePauseMusicOnMedia() { @@ -1980,7 +2060,7 @@ public static void togglePauseMusicOnMedia() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("pauseMusicOnMedia", pauseMusicOnMedia); - editor.commit(); + editor.apply(); } public static void toggleChatBlur() { @@ -1992,7 +2072,7 @@ public static void toggleForceDisableTabletMode() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("forceDisableTabletMode", forceDisableTabletMode); - editor.commit(); + editor.apply(); } public static void toggleInappCamera() { @@ -2000,7 +2080,7 @@ public static void toggleInappCamera() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("inappCamera", inappCamera); - editor.commit(); + editor.apply(); } public static void setInappCamera(boolean inappCamera) { @@ -2015,7 +2095,7 @@ public static void toggleRoundCamera16to9() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("roundCamera16to9", roundCamera16to9); - editor.commit(); + editor.apply(); } public static void setDistanceSystemType(int type) { @@ -2023,7 +2103,7 @@ public static void setDistanceSystemType(int type) { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putInt("distanceSystemType", distanceSystemType); - editor.commit(); + editor.apply(); LocaleController.resetImperialSystemType(); } @@ -2438,26 +2518,8 @@ public static int measureDevicePerformanceClass() { int cpuCount = ConnectionsManager.CPU_COUNT; int memoryClass = ((ActivityManager) ApplicationLoader.applicationContext.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass(); - if (Build.DEVICE != null && Build.MANUFACTURER != null) { - int hash = (Build.MANUFACTURER + " " + Build.DEVICE).toUpperCase().hashCode(); - for (int i = 0; i < LOW_DEVICES.length; ++i) { - if (LOW_DEVICES[i] == hash) { - return PERFORMANCE_CLASS_LOW; - } - } - for (int i = 0; i < AVERAGE_DEVICES.length; ++i) { - if (AVERAGE_DEVICES[i] == hash) { - return PERFORMANCE_CLASS_AVERAGE; - } - } - for (int i = 0; i < HIGH_DEVICES.length; ++i) { - if (HIGH_DEVICES[i] == hash) { - return PERFORMANCE_CLASS_HIGH; - } - } - } if (NaConfig.INSTANCE.getFakeHighPerformanceDevice().Bool()) { - devicePerformanceClass = PERFORMANCE_CLASS_HIGH; + return PERFORMANCE_CLASS_HIGH; } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && Build.SOC_MODEL != null) { @@ -2535,6 +2597,13 @@ public static void setMediaColumnsCount(int count) { } } + public static void setStoriesColumnsCount(int count) { + if (storiesColumnsCount != count) { + storiesColumnsCount = count; + ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE).edit().putInt("storiesColumnsCount", storiesColumnsCount).apply(); + } + } + public static void setFastScrollHintCount(int count) { if (fastScrollHintCount != count) { fastScrollHintCount = count; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/SvgHelper.java b/TMessagesProj/src/main/java/org/telegram/messenger/SvgHelper.java index 419bd74e4a..9b9bf24973 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/SvgHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/SvgHelper.java @@ -43,6 +43,8 @@ import androidx.core.graphics.ColorUtils; +import com.google.android.exoplayer2.util.Log; + import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Components.DrawingInBackgroundThreadDrawable; import org.xml.sax.Attributes; @@ -109,26 +111,31 @@ public static class SvgDrawable extends Drawable { private Paint backgroundPaint; protected int width; protected int height; - private static int[] parentPosition = new int[2]; + private static final int[] parentPosition = new int[2]; - private Bitmap[] backgroundBitmap = new Bitmap[1 + DrawingInBackgroundThreadDrawable.THREAD_COUNT]; - private Canvas[] backgroundCanvas = new Canvas[1 + DrawingInBackgroundThreadDrawable.THREAD_COUNT]; - private LinearGradient[] placeholderGradient = new LinearGradient[1 + DrawingInBackgroundThreadDrawable.THREAD_COUNT]; - private Matrix[] placeholderMatrix = new Matrix[1 + DrawingInBackgroundThreadDrawable.THREAD_COUNT]; + private final Bitmap[] backgroundBitmap = new Bitmap[1 + DrawingInBackgroundThreadDrawable.THREAD_COUNT]; + private final Canvas[] backgroundCanvas = new Canvas[1 + DrawingInBackgroundThreadDrawable.THREAD_COUNT]; + private final LinearGradient[] placeholderGradient = new LinearGradient[1 + DrawingInBackgroundThreadDrawable.THREAD_COUNT]; + private final Matrix[] placeholderMatrix = new Matrix[1 + DrawingInBackgroundThreadDrawable.THREAD_COUNT]; private static float totalTranslation; private static float gradientWidth; private static long lastUpdateTime; private static Runnable shiftRunnable; private static WeakReference shiftDrawable; private ImageReceiver parentImageReceiver; - private int[] currentColor = new int[2]; - private String currentColorKey; + private final int[] currentColor = new int[2]; + private int currentColorKey; private Integer overrideColor; private Theme.ResourcesProvider currentResourcesProvider; private float colorAlpha; private float crossfadeAlpha = 1.0f; SparseArray overridePaintByPosition = new SparseArray<>(); + private static boolean lite = LiteMode.isEnabled(LiteMode.FLAG_CHAT_BACKGROUND); + public static void updateLiteValues() { + lite = LiteMode.isEnabled(LiteMode.FLAG_CHAT_BACKGROUND); + } + private boolean aspectFill = true; private boolean aspectCenter = false; @@ -161,12 +168,12 @@ public void draw(Canvas canvas) { } public void drawInternal(Canvas canvas, boolean drawInBackground, int threadIndex, long time, float x, float y, float w, float h) { - if (currentColorKey != null) { + if (currentColorKey >= 0) { setupGradient(currentColorKey, currentResourcesProvider, colorAlpha, drawInBackground); } float scale = getScale((int) w, (int) h); - if (placeholderGradient[threadIndex] != null && gradientWidth > 0 && LiteMode.isEnabled(LiteMode.FLAG_CHAT_BACKGROUND)) { + if (placeholderGradient[threadIndex] != null && gradientWidth > 0 && lite) { if (drawInBackground) { long dt = time - lastUpdateTime; if (dt > 64) { @@ -313,11 +320,11 @@ public void setParent(ImageReceiver imageReceiver) { parentImageReceiver = imageReceiver; } - public void setupGradient(String colorKey, float alpha, boolean drawInBackground) { + public void setupGradient(int colorKey, float alpha, boolean drawInBackground) { setupGradient(colorKey, null, alpha, drawInBackground); } - public void setupGradient(String colorKey, Theme.ResourcesProvider resourcesProvider, float alpha, boolean drawInBackground) { + public void setupGradient(int colorKey, Theme.ResourcesProvider resourcesProvider, float alpha, boolean drawInBackground) { int color = overrideColor == null ? Theme.getColor(colorKey, resourcesProvider) : overrideColor; int index = drawInBackground ? 1 : 0; currentResourcesProvider = resourcesProvider; @@ -326,7 +333,7 @@ public void setupGradient(String colorKey, Theme.ResourcesProvider resourcesProv currentColorKey = colorKey; currentColor[index] = color; gradientWidth = AndroidUtilities.displaySize.x * 2; - if (!LiteMode.isEnabled(LiteMode.FLAG_CHAT_BACKGROUND)) { + if (!lite) { int color2 = ColorUtils.setAlphaComponent(currentColor[index], 70); if (drawInBackground) { if (backgroundPaint == null) { @@ -380,11 +387,11 @@ public void setupGradient(String colorKey, Theme.ResourcesProvider resourcesProv } } - public void setColorKey(String colorKey) { + public void setColorKey(int colorKey) { currentColorKey = colorKey; } - public void setColorKey(String colorKey, Theme.ResourcesProvider resourcesProvider) { + public void setColorKey(int colorKey, Theme.ResourcesProvider resourcesProvider) { currentColorKey = colorKey; currentResourcesProvider = resourcesProvider; } @@ -436,6 +443,7 @@ public static Bitmap getBitmap(int res, int width, int height, int color, float SAXParser sp = spf.newSAXParser(); XMLReader xr = sp.getXMLReader(); SVGHandler handler = new SVGHandler(width, height, color, false, scale); + ///handler.alphaOnly = true; xr.setContentHandler(handler); xr.parse(new InputSource(stream)); return handler.getBitmap(); @@ -451,6 +459,9 @@ public static Bitmap getBitmap(File file, int width, int height, boolean white) SAXParser sp = spf.newSAXParser(); XMLReader xr = sp.getXMLReader(); SVGHandler handler = new SVGHandler(width, height, white ? 0xffffffff : null, false, 1f); + if (!white) { + handler.alphaOnly = true; + } xr.setContentHandler(handler); xr.parse(new InputSource(stream)); return handler.getBitmap(); @@ -1111,6 +1122,7 @@ private static class SVGHandler extends DefaultHandler { boolean pushed = false; private HashMap globalStyles = new HashMap<>(); + private boolean alphaOnly; private SVGHandler(int dw, int dh, Integer color, boolean asDrawable, float scale) { globalScale = scale; @@ -1270,7 +1282,7 @@ public void startElement(String namespaceURI, String localName, String qName, At height *= scale; } if (drawable == null) { - bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + bitmap = Bitmap.createBitmap(width, height, alphaOnly ? Bitmap.Config.ALPHA_8 : Bitmap.Config.ARGB_8888); bitmap.eraseColor(0); canvas = new Canvas(bitmap); if (scale != 0) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/TranslateController.java b/TMessagesProj/src/main/java/org/telegram/messenger/TranslateController.java index 2b3897a704..5850dd4029 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/TranslateController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/TranslateController.java @@ -38,7 +38,7 @@ public class TranslateController extends BaseController { private static final int MAX_SYMBOLS_PER_REQUEST = 25000; private static final int MAX_MESSAGES_PER_REQUEST = 20; - private static final int GROUPING_TRANSLATIONS_TIMEOUT = 200; + private static final int GROUPING_TRANSLATIONS_TIMEOUT = 80; private final Set translatingDialogs = new HashSet<>(); private final Set translatableDialogs = new HashSet<>(); @@ -48,7 +48,7 @@ public class TranslateController extends BaseController { private final HashMap> keptReplyMessageObjects = new HashMap<>(); private final Set hideTranslateDialogs = new HashSet<>(); - class TranslatableDecision { + static class TranslatableDecision { Set certainlyTranslatable = new HashSet<>(); Set unknown = new HashSet<>(); Set certainlyNotTranslatable = new HashSet<>(); @@ -64,19 +64,32 @@ public TranslateController(MessagesController messagesController) { } public boolean isFeatureAvailable() { - return UserConfig.getInstance(currentAccount).isPremium() && isChatTranslateEnabled(); + return isChatTranslateEnabled() && UserConfig.getInstance(currentAccount).isPremium(); } + private Boolean chatTranslateEnabled; + private Boolean contextTranslateEnabled; + public boolean isChatTranslateEnabled() { - return MessagesController.getMainSettings(currentAccount).getBoolean("translate_chat_button", true); + if (chatTranslateEnabled == null) { + chatTranslateEnabled = messagesController.getMainSettings().getBoolean("translate_chat_button", true); + } + return chatTranslateEnabled; } public boolean isContextTranslateEnabled() { - return MessagesController.getMainSettings(currentAccount).getBoolean("translate_button", MessagesController.getGlobalMainSettings().getBoolean("translate_button", false)); + if (contextTranslateEnabled == null) { + contextTranslateEnabled = messagesController.getMainSettings().getBoolean("translate_button", MessagesController.getGlobalMainSettings().getBoolean("translate_button", false)); + } + return contextTranslateEnabled; } public void setContextTranslateEnabled(boolean enable) { - MessagesController.getMainSettings(currentAccount).edit().putBoolean("translate_button", enable).apply(); + messagesController.getMainSettings().edit().putBoolean("translate_button", contextTranslateEnabled = enable).apply(); + } + + public void setChatTranslateEnabled(boolean enable) { + messagesController.getMainSettings().edit().putBoolean("translate_chat_button", chatTranslateEnabled = enable).apply(); } public static boolean isTranslatable(MessageObject messageObject) { @@ -542,8 +555,14 @@ public void invalidateTranslation(MessageObject messageObject) { }); } - public void checkDialogMessages(long dialogId) { - if (!isFeatureAvailable()) { + public void checkDialogMessage(long dialogId) { + if (isFeatureAvailable()) { + checkDialogMessageSure(dialogId); + } + } + + public void checkDialogMessageSure(long dialogId) { + if (!translatingDialogs.contains(dialogId)) { return; } getMessagesStorage().getStorageQueue().postRunnable(() -> { @@ -661,6 +680,7 @@ private static class PendingTranslation { ArrayList> callbacks = new ArrayList<>(); String language; + int delay = GROUPING_TRANSLATIONS_TIMEOUT; int symbolsCount; int reqId = -1; @@ -705,6 +725,8 @@ private void pushToTranslate( if (pendingTranslation.symbolsCount + messageSymbolsCount >= MAX_SYMBOLS_PER_REQUEST || pendingTranslation.messageIds.size() + 1 >= MAX_MESSAGES_PER_REQUEST) { + AndroidUtilities.cancelRunOnUIThread(pendingTranslation.runnable); + AndroidUtilities.runOnUIThread(pendingTranslation.runnable); // without timeout dialogPendingTranslations.add(pendingTranslation = new PendingTranslation()); } @@ -774,7 +796,8 @@ private void pushToTranslate( pendingTranslation1.reqId = reqId; } }; - AndroidUtilities.runOnUIThread(pendingTranslation.runnable, GROUPING_TRANSLATIONS_TIMEOUT); + AndroidUtilities.runOnUIThread(pendingTranslation.runnable, pendingTranslation.delay); + pendingTranslation.delay /= 2; } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java b/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java index ca827373b3..ae8d07116e 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java @@ -15,6 +15,8 @@ import android.util.SparseArray; import android.util.LongSparseArray; +import com.google.android.exoplayer2.util.Log; + import org.telegram.tgnet.SerializedData; import org.telegram.tgnet.TLRPC; @@ -71,9 +73,6 @@ public class UserConfig extends BaseController { public boolean official; public boolean deviceInfo; - public List awaitBillingProductIds = new ArrayList<>(); - public TLRPC.InputStorePaymentPurpose billingPaymentPurpose; - public String premiumGiftsStickerPack; public String genericAnimationsStickerPack; public String defaultTopicIcons; @@ -176,15 +175,6 @@ public void saveConfig(boolean withFile) { editor.putBoolean("deviceInfo", deviceInfo); editor.putBoolean("filtersLoaded", filtersLoaded); - editor.putStringSet("awaitBillingProductIds", new HashSet<>(awaitBillingProductIds)); - if (billingPaymentPurpose != null) { - SerializedData data = new SerializedData(billingPaymentPurpose.getObjectSize()); - billingPaymentPurpose.serializeToStream(data); - editor.putString("billingPaymentPurpose", Base64.encodeToString(data.toByteArray(), Base64.DEFAULT)); - data.cleanup(); - } else { - editor.remove("billingPaymentPurpose"); - } editor.putString("premiumGiftsStickerPack", premiumGiftsStickerPack); editor.putLong("lastUpdatedPremiumGiftsStickerPack", lastUpdatedPremiumGiftsStickerPack); @@ -330,18 +320,6 @@ private void checkPremiumSelf(TLRPC.User oldUser, TLRPC.User newUser) { sharingMyLocationUntil = preferences.getInt("sharingMyLocationUntil", 0); lastMyLocationShareTime = preferences.getInt("lastMyLocationShareTime", 0); filtersLoaded = preferences.getBoolean("filtersLoaded", false); - awaitBillingProductIds = new ArrayList<>(preferences.getStringSet("awaitBillingProductIds", Collections.emptySet())); - if (preferences.contains("billingPaymentPurpose")) { - String purpose = preferences.getString("billingPaymentPurpose", null); - if (purpose != null) { - byte[] arr = Base64.decode(purpose, Base64.DEFAULT); - if (arr != null) { - SerializedData data = new SerializedData(); - billingPaymentPurpose = TLRPC.InputStorePaymentPurpose.TLdeserialize(data, data.readInt32(false), false); - data.cleanup(); - } - } - } premiumGiftsStickerPack = preferences.getString("premiumGiftsStickerPack", null); lastUpdatedPremiumGiftsStickerPack = preferences.getLong("lastUpdatedPremiumGiftsStickerPack", 0); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/UserObject.java b/TMessagesProj/src/main/java/org/telegram/messenger/UserObject.java index 8dc6f95c45..f037eccdf1 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/UserObject.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/UserObject.java @@ -135,4 +135,8 @@ public static Long getEmojiStatusDocumentId(TLRPC.EmojiStatus emojiStatus) { } return null; } + + public static boolean isService(long user_id) { + return user_id == 333000 || user_id == 777000 || user_id == 42777; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java b/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java index df3c8f3cb7..d7a3efa9f1 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java @@ -39,6 +39,7 @@ public class Utilities { public static volatile DispatchQueue phoneBookQueue = new DispatchQueue("phoneBookQueue"); public static volatile DispatchQueue themeQueue = new DispatchQueue("themeQueue"); public static volatile DispatchQueue externalNetworkQueue = new DispatchQueue("externalNetworkQueue"); + public static volatile DispatchQueue videoPlayerQueue; private final static String RANDOM_STRING_CHARS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; @@ -473,6 +474,10 @@ public static int clamp(int value, int maxValue, int minValue) { return Math.max(Math.min(value, maxValue), minValue); } + public static long clamp(long value, long maxValue, long minValue) { + return Math.max(Math.min(value, maxValue), minValue); + } + public static float clamp(float value, float maxValue, float minValue) { if (Float.isNaN(value)) { return minValue; @@ -524,6 +529,10 @@ public static interface Callback3 { public void run(T arg, T2 arg2, T3 arg3); } + public static interface Callback4 { + public void run(T arg, T2 arg2, T3 arg3, T4 arg4); + } + public static Value getOrDefault(HashMap map, Key key, Value defaultValue) { Value v = map.get(key); if (v == null) { @@ -561,4 +570,11 @@ public static void raceCallbacks(Runnable onFinish, Utilities.Callback actions[i].run(checkFinish); } } + + public static DispatchQueue getOrCreatePlayerQueue() { + if (videoPlayerQueue == null) { + videoPlayerQueue = new DispatchQueue("playerQueue"); + } + return videoPlayerQueue; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/VideoEditedInfo.java b/TMessagesProj/src/main/java/org/telegram/messenger/VideoEditedInfo.java index d219b56c05..8f7838a846 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/VideoEditedInfo.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/VideoEditedInfo.java @@ -20,6 +20,7 @@ import org.telegram.ui.Components.Paint.PaintTypeface; import org.telegram.ui.Components.PhotoFilterView; import org.telegram.ui.Components.Point; +import org.telegram.ui.Stories.recorder.StoryEntry; import java.util.ArrayList; import java.util.Locale; @@ -55,19 +56,32 @@ public class VideoEditedInfo { public ArrayList mediaEntities; public MediaController.CropState cropState; public boolean isPhoto; + public boolean isStory; + public StoryEntry.HDRInfo hdrInfo; + public ArrayList parts; + + public Integer gradientTopColor, gradientBottomColor; + public boolean forceFragmenting; + + public boolean alreadyScheduledConverting; public boolean canceled; public boolean videoConvertFirstWrite; public boolean needUpdateProgress = false; public boolean shouldLimitFps = true; + public boolean tryUseHevc = false; + public boolean fromCamera; public static class EmojiEntity extends TLRPC.TL_messageEntityCustomEmoji { public String documentAbsolutePath; + public MediaEntity entity; + public byte subType; @Override public void readParams(AbstractSerializedData stream, boolean exception) { super.readParams(stream, exception); + subType = stream.readByte(exception); boolean hasPath = stream.readBool(exception); if (hasPath) { documentAbsolutePath = stream.readString(exception); @@ -80,6 +94,7 @@ public void readParams(AbstractSerializedData stream, boolean exception) { @Override public void serializeToStream(AbstractSerializedData stream) { super.serializeToStream(stream); + stream.writeByte(subType); stream.writeBool(!TextUtils.isEmpty(documentAbsolutePath)); if (!TextUtils.isEmpty(documentAbsolutePath)) { stream.writeString(documentAbsolutePath); @@ -88,6 +103,11 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class MediaEntity { + + public static final int TYPE_STICKER = 0; + public static final int TYPE_TEXT = 1; + public static final int TYPE_PHOTO = 2; + public byte type; public byte subType; public float x; @@ -100,6 +120,7 @@ public static class MediaEntity { public int color; public int fontSize; public PaintTypeface textTypeface; + public String textTypefaceKey; public int textAlign; public int viewWidth; public int viewHeight; @@ -129,7 +150,7 @@ public MediaEntity() { } - private MediaEntity(SerializedData data) { + public MediaEntity(AbstractSerializedData data, boolean full) { type = data.readByte(false); subType = data.readByte(false); x = data.readFloat(false); @@ -150,10 +171,23 @@ private MediaEntity(SerializedData data) { viewWidth = data.readInt32(false); viewHeight = data.readInt32(false); textAlign = data.readInt32(false); - textTypeface = PaintTypeface.find(data.readString(false)); + textTypeface = PaintTypeface.find(textTypefaceKey = data.readString(false)); + scale = data.readFloat(false); + textViewWidth = data.readFloat(false); + textViewHeight = data.readFloat(false); + textViewX = data.readFloat(false); + textViewY = data.readFloat(false); + if (full) { + int magic = data.readInt32(false); + if (magic == TLRPC.TL_null.constructor) { + document = null; + } else { + document = TLRPC.Document.TLdeserialize(data, magic, false); + } + } } - private void serializeTo(SerializedData data) { + public void serializeTo(AbstractSerializedData data, boolean full) { data.writeByte(type); data.writeByte(subType); data.writeFloat(x); @@ -171,7 +205,19 @@ private void serializeTo(SerializedData data) { data.writeInt32(viewWidth); data.writeInt32(viewHeight); data.writeInt32(textAlign); - data.writeString(textTypeface == null ? "" : textTypeface.getKey()); + data.writeString(textTypeface == null ? (textTypefaceKey == null ? "" : textTypefaceKey) : textTypeface.getKey()); + data.writeFloat(scale); + data.writeFloat(textViewWidth); + data.writeFloat(textViewHeight); + data.writeFloat(textViewX); + data.writeFloat(textViewY); + if (full) { + if (document == null) { + data.writeInt32(TLRPC.TL_null.constructor); + } else { + document.serializeToStream(data); + } + } } public MediaEntity copy() { @@ -215,7 +261,7 @@ public String getString() { paintPathBytes = null; } SerializedData serializedData = new SerializedData(len); - serializedData.writeInt32(5); + serializedData.writeInt32(7); serializedData.writeInt64(avatarStartTime); serializedData.writeInt32(originalBitrate); if (filterState != null) { @@ -276,7 +322,7 @@ public String getString() { serializedData.writeByte(1); serializedData.writeInt32(mediaEntities.size()); for (int a = 0, N = mediaEntities.size(); a < N; a++) { - mediaEntities.get(a).serializeTo(serializedData); + mediaEntities.get(a).serializeTo(serializedData, false); } serializedData.writeByte(isPhoto ? 1 : 0); } else { @@ -297,6 +343,16 @@ public String getString() { } else { serializedData.writeByte(0); } + if (parts != null && !parts.isEmpty()) { + serializedData.writeInt32(parts.size()); + for (StoryEntry.Part part : parts) { + part.serializeToStream(serializedData); + } + } else { + serializedData.writeInt32(0); + } + serializedData.writeBool(isStory); + serializedData.writeBool(fromCamera); filters = Utilities.bytesToHex(serializedData.toByteArray()); serializedData.cleanup(); } else { @@ -388,7 +444,7 @@ public boolean parseString(String string) { int count = serializedData.readInt32(false); mediaEntities = new ArrayList<>(count); for (int a = 0; a < count; a++) { - mediaEntities.add(new MediaEntity(serializedData)); + mediaEntities.add(new MediaEntity(serializedData, false)); } isPhoto = serializedData.readByte(false) == 1; } @@ -410,6 +466,17 @@ public boolean parseString(String string) { } } } + if (version >= 6) { + int count = serializedData.readInt32(false); + for (int i = 0; i < count; ++i) { + StoryEntry.Part part = new StoryEntry.Part(); + part.readParams(serializedData, false); + } + } + if (version >= 7) { + isStory = serializedData.readBool(false); + fromCamera = serializedData.readBool(false); + } serializedData.cleanup(); } } else { @@ -432,6 +499,12 @@ public boolean parseString(String string) { } public boolean needConvert() { + if (isStory) { + if (!fromCamera) { + return true; + } + return mediaEntities != null || paintPath != null || filterState != null || (cropState != null && !cropState.isEmpty()) || startTime > 0 || endTime != -1 && endTime != estimatedDuration || originalHeight != resultHeight || originalWidth != resultWidth; + } return mediaEntities != null || paintPath != null || filterState != null || cropState != null || !roundVideo || startTime > 0 || endTime != -1 && endTime != estimatedDuration; } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/WearReplyReceiver.java b/TMessagesProj/src/main/java/org/telegram/messenger/WearReplyReceiver.java index 8e1b6b78f7..05eeb38f54 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/WearReplyReceiver.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/WearReplyReceiver.java @@ -79,7 +79,7 @@ private void sendMessage(AccountInstance accountInstance, CharSequence text, lon replyToMsgId = new MessageObject(accountInstance.getCurrentAccount(), topicStartMessage, false, false); } - accountInstance.getSendMessagesHelper().sendMessage(text.toString(), dialog_id, replyToMsgId, null, null, true, null, null, null, true, 0, null, false); + accountInstance.getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of(text.toString(), dialog_id, replyToMsgId, null, null, true, null, null, null, true, 0, null, false)); //TODO handle topics if (topicId == 0) { accountInstance.getMessagesController().markDialogAsRead(dialog_id, max_id, max_id, 0, false, topicId, 0, true, 0); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/browser/Browser.java b/TMessagesProj/src/main/java/org/telegram/messenger/browser/Browser.java index 4f79013d7d..ca4d95d6ca 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/browser/Browser.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/browser/Browser.java @@ -239,10 +239,14 @@ public void onCancel(Runnable onCancelListener) { } public static void openUrl(final Context context, Uri uri, final boolean allowCustom, boolean tryTelegraph) { - openUrl(context, uri, allowCustom, tryTelegraph, null); + openUrl(context, uri, allowCustom, tryTelegraph, false, null); } public static void openUrl(final Context context, Uri uri, final boolean allowCustom, boolean tryTelegraph, Progress inCaseLoading) { + openUrl(context, uri, allowCustom, tryTelegraph, false, inCaseLoading); + } + + public static void openUrl(final Context context, Uri uri, final boolean allowCustom, boolean tryTelegraph, boolean forceNotInternalForApps, Progress inCaseLoading) { if (context == null || uri == null) { return; } @@ -425,6 +429,7 @@ public static void openUrl(final Context context, Uri uri, final boolean allowCu intent.putExtra(android.provider.Browser.EXTRA_APPLICATION_ID, context.getPackageName()); intent.putExtra("internal", true); if (internalUri && context instanceof LaunchActivity) { + intent.putExtra(LaunchActivity.EXTRA_FORCE_NOT_INTERNAL_APPS, forceNotInternalForApps); ((LaunchActivity) context).onNewIntent(intent, inCaseLoading); } else { context.startActivity(intent); @@ -537,4 +542,32 @@ public static boolean isInternalUri(Uri uri, boolean all, boolean[] forceBrowser } return false; } + + // © ChatGPT. All puns reserved. 🤖📜 + public static String replaceHostname(Uri originalUri, String newHostname) { + String scheme = originalUri.getScheme(); + String userInfo = originalUri.getUserInfo(); + int port = originalUri.getPort(); + String path = originalUri.getPath(); + String query = originalUri.getQuery(); + String fragment = originalUri.getFragment(); + + StringBuilder modifiedUriBuilder = new StringBuilder(); + modifiedUriBuilder.append(scheme).append("://"); + if (userInfo != null) { + modifiedUriBuilder.append(userInfo).append("@"); + } + modifiedUriBuilder.append(newHostname); + if (port != -1) { + modifiedUriBuilder.append(":").append(port); + } + modifiedUriBuilder.append(path); + if (query != null) { + modifiedUriBuilder.append("?").append(query); + } + if (fragment != null) { + modifiedUriBuilder.append("#").append(fragment); + } + return modifiedUriBuilder.toString(); + } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/camera/CameraController.java b/TMessagesProj/src/main/java/org/telegram/messenger/camera/CameraController.java index 1e399dd7dc..b8f0d2c9af 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/camera/CameraController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/camera/CameraController.java @@ -21,6 +21,7 @@ import android.os.Build; import android.provider.MediaStore; import android.util.Base64; +import android.util.Log; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ApplicationLoader; @@ -36,6 +37,7 @@ import org.telegram.messenger.Utilities; import org.telegram.tgnet.SerializedData; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.util.ArrayList; @@ -54,8 +56,7 @@ public class CameraController implements MediaRecorder.OnInfoListener { private static final int MAX_POOL_SIZE = 1; private static final int KEEP_ALIVE_SECONDS = 60; - private ThreadPoolExecutor threadPool; - protected ArrayList availableFlashModes = new ArrayList<>(); + protected ThreadPoolExecutor threadPool; private MediaRecorder recorder; private String recordedFile; private boolean mirrorRecorderVideo; @@ -65,7 +66,12 @@ public class CameraController implements MediaRecorder.OnInfoListener { private boolean loadingCameras; private ArrayList onFinishCameraInitRunnables = new ArrayList<>(); - CameraView recordingCurrentCameraView; + ICameraView recordingCurrentCameraView; + + public interface ICameraView { + void stopRecording(); + boolean startRecording(File file, Runnable runnable); + } private static volatile CameraController Instance = null; @@ -257,6 +263,10 @@ public boolean isCameraInitied() { } public void close(final CameraSession session, final CountDownLatch countDownLatch, final Runnable beforeDestroyRunnable) { + close(session, countDownLatch, beforeDestroyRunnable, null); + } + + public void close(final CameraSession session, final CountDownLatch countDownLatch, final Runnable beforeDestroyRunnable, final Runnable afterDestroyRunnable) { session.destroy(); threadPool.execute(() -> { if (beforeDestroyRunnable != null) { @@ -279,6 +289,9 @@ public void close(final CameraSession session, final CountDownLatch countDownLat if (countDownLatch != null) { countDownLatch.countDown(); } + if (afterDestroyRunnable != null) { + AndroidUtilities.runOnUIThread(afterDestroyRunnable); + } }); if (countDownLatch != null) { try { @@ -295,7 +308,7 @@ public ArrayList getCameras() { private static int getOrientation(byte[] jpeg) { if (jpeg == null) { - return 0; + return -1; } int offset = 0; @@ -318,7 +331,7 @@ private static int getOrientation(byte[] jpeg) { length = pack(jpeg, offset, 2, false); if (length < 2 || offset + length > jpeg.length) { - return 0; + return -1; } // Break if the marker is EXIF in APP1. @@ -337,13 +350,13 @@ private static int getOrientation(byte[] jpeg) { if (length > 8) { int tag = pack(jpeg, offset, 4, false); if (tag != 0x49492A00 && tag != 0x4D4D002A) { - return 0; + return -1; } boolean littleEndian = (tag == 0x49492A00); int count = pack(jpeg, offset + 4, 4, littleEndian) + 2; if (count < 10 || count > length) { - return 0; + return -1; } offset += count; length -= count; @@ -363,13 +376,13 @@ private static int getOrientation(byte[] jpeg) { case 8: return 270; } - return 0; + return -1; } offset += 12; length -= 12; } } - return 0; + return -1; } private static int pack(byte[] bytes, int offset, int length, boolean littleEndian) { @@ -387,7 +400,7 @@ private static int pack(byte[] bytes, int offset, int length, boolean littleEndi return value; } - public boolean takePicture(final File path, final CameraSession session, final Runnable callback) { + public boolean takePicture(final File path, final boolean ignoreOrientation, final CameraSession session, final Utilities.Callback callback) { if (session == null) { return false; } @@ -397,6 +410,7 @@ public boolean takePicture(final File path, final CameraSession session, final R try { camera.takePicture(null, null, (data, camera1) -> { Bitmap bitmap = null; + int orientation = 0; int size = (int) (AndroidUtilities.getPhotoSize() / AndroidUtilities.density); String key = String.format(Locale.US, "%s@%d_%d", Utilities.MD5(path.getAbsolutePath()), size, size); try { @@ -415,10 +429,14 @@ public boolean takePicture(final File path, final CameraSession session, final R FileLog.e(e); } try { + orientation = getOrientation(data); if (info.frontCamera != 0 && flipFront) { try { Matrix matrix = new Matrix(); - matrix.setRotate(getOrientation(data)); + if (!ignoreOrientation && orientation != -1) { + matrix.setRotate(orientation); + } + orientation = 0; matrix.postScale(-1, 1); Bitmap scaled = Bitmaps.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); if (scaled != bitmap) { @@ -433,7 +451,7 @@ public boolean takePicture(final File path, final CameraSession session, final R ImageLoader.getInstance().putImageToCache(new BitmapDrawable(scaled), key, false); } if (callback != null) { - callback.run(); + callback.run(orientation); } return; } catch (Throwable e) { @@ -452,7 +470,7 @@ public boolean takePicture(final File path, final CameraSession session, final R FileLog.e(e); } if (callback != null) { - callback.run(); + callback.run(orientation); } }); return true; @@ -471,6 +489,7 @@ public void startPreview(final CameraSession session) { try { if (camera == null) { camera = session.cameraInfo.camera = Camera.open(session.cameraInfo.cameraId); + camera.setErrorCallback(getErrorListener(session)); } camera.startPreview(); } catch (Exception e) { @@ -492,6 +511,7 @@ public void stopPreview(final CameraSession session) { try { if (camera == null) { camera = session.cameraInfo.camera = Camera.open(session.cameraInfo.cameraId); + camera.setErrorCallback(getErrorListener(session)); } camera.stopPreview(); } catch (Exception e) { @@ -555,18 +575,19 @@ public void open(final CameraSession session, final SurfaceTexture texture, fina if (camera == null) { camera = session.cameraInfo.camera = Camera.open(session.cameraInfo.cameraId); } + camera.setErrorCallback(getErrorListener(session)); Camera.Parameters params = camera.getParameters(); List rawFlashModes = params.getSupportedFlashModes(); - availableFlashModes.clear(); + session.availableFlashModes.clear(); if (rawFlashModes != null) { for (int a = 0; a < rawFlashModes.size(); a++) { String rawFlashMode = rawFlashModes.get(a); if (rawFlashMode.equals(Camera.Parameters.FLASH_MODE_OFF) || rawFlashMode.equals(Camera.Parameters.FLASH_MODE_ON) || rawFlashMode.equals(Camera.Parameters.FLASH_MODE_AUTO)) { - availableFlashModes.add(rawFlashMode); + session.availableFlashModes.add(rawFlashMode); } } - session.checkFlashMode(availableFlashModes.get(0)); + session.checkFlashMode(session.availableFlashModes.get(0)); } if (prestartCallback != null) { @@ -589,7 +610,11 @@ public void open(final CameraSession session, final SurfaceTexture texture, fina }); } - public void recordVideo(final CameraSession session, final File path, boolean mirror, final VideoTakeCallback callback, final Runnable onVideoStartRecord, CameraView cameraView) { + public void recordVideo(final CameraSession session, final File path, boolean mirror, final VideoTakeCallback callback, final Runnable onVideoStartRecord, ICameraView cameraView) { + recordVideo(session, path, mirror, callback, onVideoStartRecord, cameraView, true); + } + + public void recordVideo(final CameraSession session, final File path, boolean mirror, final VideoTakeCallback callback, final Runnable onVideoStartRecord, ICameraView cameraView, boolean createThumbnail) { if (session == null) { return; } @@ -611,7 +636,7 @@ public void recordVideo(final CameraSession session, final File path, boolean mi FileLog.e(e); } AndroidUtilities.runOnUIThread(() -> { - cameraView.startRecording(path, this::finishRecordingVideo); + cameraView.startRecording(path, () -> finishRecordingVideo(createThumbnail)); if (onVideoStartRecord != null) { onVideoStartRecord.run(); @@ -652,7 +677,7 @@ public void recordVideo(final CameraSession session, final File path, boolean mi recorder.setMaxDuration(0); Size pictureSize; pictureSize = new Size(16, 9); - pictureSize = CameraController.chooseOptimalSize(info.getPictureSizes(), 720, 480, pictureSize); + pictureSize = CameraController.chooseOptimalSize(info.getPictureSizes(), 720, 480, pictureSize, false); int bitrate; if (Math.min(pictureSize.mHeight,pictureSize.mWidth) >= 720) { bitrate = 3500000; @@ -682,7 +707,7 @@ public void recordVideo(final CameraSession session, final File path, boolean mi }); } - private void finishRecordingVideo() { + private void finishRecordingVideo(boolean createThumbnail) { MediaMetadataRetriever mediaMetadataRetriever = null; long duration = 0; try { @@ -690,7 +715,7 @@ private void finishRecordingVideo() { mediaMetadataRetriever.setDataSource(recordedFile); String d = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION); if (d != null) { - duration = (int) Math.ceil(Long.parseLong(d) / 1000.0f); + duration = Long.parseLong(d); } } catch (Exception e) { FileLog.e(e); @@ -703,31 +728,47 @@ private void finishRecordingVideo() { FileLog.e(e); } } - Bitmap bitmap = SendMessagesHelper.createVideoThumbnail(recordedFile, MediaStore.Video.Thumbnails.MINI_KIND); - if (mirrorRecorderVideo) { - Bitmap b = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(b); - canvas.scale(-1, 1, b.getWidth() / 2, b.getHeight() / 2); - canvas.drawBitmap(bitmap, 0, 0, null); - bitmap.recycle(); - bitmap = b; - } - String fileName = Integer.MIN_VALUE + "_" + SharedConfig.getLastLocalId() + ".jpg"; - final File cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), fileName); - try { - FileOutputStream stream = new FileOutputStream(cacheFile); - bitmap.compress(Bitmap.CompressFormat.JPEG, 87, stream); - } catch (Throwable e) { - FileLog.e(e); + final File cacheFile; + Bitmap bitmap = null; + if (createThumbnail) { + bitmap = SendMessagesHelper.createVideoThumbnail(recordedFile, MediaStore.Video.Thumbnails.MINI_KIND); + if (mirrorRecorderVideo) { + Bitmap b = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(b); + canvas.scale(-1, 1, b.getWidth() / 2, b.getHeight() / 2); + canvas.drawBitmap(bitmap, 0, 0, null); + bitmap.recycle(); + bitmap = b; + } + String fileName = Integer.MIN_VALUE + "_" + SharedConfig.getLastLocalId() + ".jpg"; + cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), fileName); + FileOutputStream stream = null; + try { + stream = new FileOutputStream(cacheFile); + bitmap.compress(Bitmap.CompressFormat.JPEG, 87, stream); + } catch (Throwable e) { + FileLog.e(e); + } finally { + if (stream != null) { + try { + stream.close(); + } catch (Throwable ignore) {} + } + } + } else { + cacheFile = null; } SharedConfig.saveConfig(); final long durationFinal = duration; final Bitmap bitmapFinal = bitmap; AndroidUtilities.runOnUIThread(() -> { if (onVideoTakeCallback != null) { - String path = cacheFile.getAbsolutePath(); - if (bitmapFinal != null) { - ImageLoader.getInstance().putImageToCache(new BitmapDrawable(bitmapFinal), Utilities.MD5(path), false); + String path = null; + if (cacheFile != null) { + path = cacheFile.getAbsolutePath(); + if (bitmapFinal != null) { + ImageLoader.getInstance().putImageToCache(new BitmapDrawable(bitmapFinal), Utilities.MD5(path), false); + } } onVideoTakeCallback.onFinishVideoRecording(path, durationFinal); onVideoTakeCallback = null; @@ -745,12 +786,16 @@ public void onInfo(MediaRecorder mediaRecorder, int what, int extra) { tempRecorder.release(); } if (onVideoTakeCallback != null) { - finishRecordingVideo(); + finishRecordingVideo(true); } } } public void stopVideoRecording(final CameraSession session, final boolean abandon) { + stopVideoRecording(session, abandon, true); + } + + public void stopVideoRecording(final CameraSession session, final boolean abandon, final boolean createThumbnail) { if (recordingCurrentCameraView != null) { recordingCurrentCameraView.stopRecording(); recordingCurrentCameraView = null; @@ -802,7 +847,7 @@ public void stopVideoRecording(final CameraSession session, final boolean abando } }); if (!abandon && onVideoTakeCallback != null) { - finishRecordingVideo(); + finishRecordingVideo(createThumbnail); } else { onVideoTakeCallback = null; } @@ -811,13 +856,16 @@ public void stopVideoRecording(final CameraSession session, final boolean abando }); } - public static Size chooseOptimalSize(List choices, int width, int height, Size aspectRatio) { + public static Size chooseOptimalSize(List choices, int width, int height, Size aspectRatio, boolean notBigger) { List bigEnoughWithAspectRatio = new ArrayList<>(choices.size()); List bigEnough = new ArrayList<>(choices.size()); int w = aspectRatio.getWidth(); int h = aspectRatio.getHeight(); for (int a = 0; a < choices.size(); a++) { Size option = choices.get(a); + if (notBigger && (option.getHeight() > height || option.getWidth() > width)) { + continue; + } if (option.getHeight() == option.getWidth() * h / w && option.getWidth() >= width && option.getHeight() >= height) { bigEnoughWithAspectRatio.add(option); } else if (option.getHeight() * option.getWidth() <= width * height * 4) { @@ -839,4 +887,38 @@ public int compare(Size lhs, Size rhs) { return Long.signum((long) lhs.getWidth() * lhs.getHeight() - (long) rhs.getWidth() * rhs.getHeight()); } } + + public interface ErrorCallback { + public default void onError(int errorId, Camera camera, CameraSession cameraSession) { + + } + } + + private ArrayList errorCallbacks; + public void addOnErrorListener(ErrorCallback callback) { + if (errorCallbacks == null) { + errorCallbacks = new ArrayList<>(); + } + errorCallbacks.remove(callback); + errorCallbacks.add(callback); + } + + public void removeOnErrorListener(ErrorCallback callback) { + if (errorCallbacks != null) { + errorCallbacks.remove(callback); + } + } + + public Camera.ErrorCallback getErrorListener(CameraSession session) { + return (errorId, camera) -> { + if (errorCallbacks != null) { + for (int i = 0; i < errorCallbacks.size(); ++i) { + ErrorCallback callback = errorCallbacks.get(i); + if (callback != null) { + callback.onError(errorId, camera, session); + } + } + } + }; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/camera/CameraInfo.java b/TMessagesProj/src/main/java/org/telegram/messenger/camera/CameraInfo.java index 5729064975..ac65a7dbdc 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/camera/CameraInfo.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/camera/CameraInfo.java @@ -18,11 +18,11 @@ public class CameraInfo { - protected int cameraId; + public int cameraId; protected Camera camera; protected ArrayList pictureSizes = new ArrayList<>(); protected ArrayList previewSizes = new ArrayList<>(); - protected final int frontCamera; + public final int frontCamera; protected CameraDevice cameraDevice; CameraCharacteristics cameraCharacteristics; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/camera/CameraSession.java b/TMessagesProj/src/main/java/org/telegram/messenger/camera/CameraSession.java index bea54dcd7a..2232dfeac1 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/camera/CameraSession.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/camera/CameraSession.java @@ -16,6 +16,7 @@ import android.media.CamcorderProfile; import android.media.MediaRecorder; import android.os.Build; +import android.util.Log; import android.view.OrientationEventListener; import android.view.Surface; import android.view.WindowManager; @@ -29,7 +30,7 @@ public class CameraSession { - protected CameraInfo cameraInfo; + public CameraInfo cameraInfo; private String currentFlashMode; private OrientationEventListener orientationEventListener; private int lastOrientation = -1; @@ -52,6 +53,8 @@ public class CameraSession { private boolean isRound; private boolean destroyed; + protected ArrayList availableFlashModes = new ArrayList<>(); + private int infoCameraId = -1; Camera.CameraInfo info = new Camera.CameraInfo(); @@ -130,7 +133,7 @@ public void setOptimizeForBarcode(boolean value) { } public void checkFlashMode(String mode) { - ArrayList modes = CameraController.getInstance().availableFlashModes; + ArrayList modes = availableFlashModes; if (modes.contains(currentFlashMode)) { return; } @@ -161,7 +164,7 @@ public String getCurrentFlashMode() { } public String getNextFlashMode() { - ArrayList modes = CameraController.getInstance().availableFlashModes; + ArrayList modes = availableFlashModes; for (int a = 0; a < modes.size(); a++) { String mode = modes.get(a); if (mode.equals(currentFlashMode)) { @@ -296,10 +299,10 @@ public void updateRotation() { displayOrientation = getDisplayOrientation(info, true); int cameraDisplayOrientation; + int degrees = 0; if ("samsung".equals(Build.MANUFACTURER) && "sf2wifixx".equals(Build.PRODUCT)) { cameraDisplayOrientation = 0; } else { - int degrees = 0; int temp = displayOrientation; switch (temp) { case Surface.ROTATION_0: @@ -414,7 +417,7 @@ protected void configurePhotoCamera() { } } - protected void focusToRect(Rect focusRect, Rect meteringRect) { + public void focusToRect(Rect focusRect, Rect meteringRect) { try { Camera camera = cameraInfo.camera; if (camera != null) { @@ -497,7 +500,7 @@ protected void configureRecorder(int quality, MediaRecorder recorder) { isVideo = true; } - protected void stopVideoRecording() { + public void stopVideoRecording() { isVideo = false; useTorch = false; configurePhotoCamera(); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/camera/CameraView.java b/TMessagesProj/src/main/java/org/telegram/messenger/camera/CameraView.java index fce3b449e5..3ab1abd3bb 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/camera/CameraView.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/camera/CameraView.java @@ -24,6 +24,7 @@ import android.graphics.SurfaceTexture; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.hardware.Camera; import android.media.AudioFormat; import android.media.AudioRecord; import android.media.MediaCodec; @@ -41,6 +42,7 @@ import android.os.Message; import android.os.VibrationEffect; import android.os.Vibrator; +import android.text.TextUtils; import android.util.Log; import android.view.Gravity; import android.view.HapticFeedbackConstants; @@ -53,21 +55,31 @@ import androidx.annotation.NonNull; import androidx.core.graphics.ColorUtils; +import androidx.interpolator.view.animation.FastOutSlowInInterpolator; + +import com.google.zxing.common.detector.MathUtils; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.BuildVars; import org.telegram.messenger.DispatchQueue; import org.telegram.messenger.FileLog; +import org.telegram.messenger.ImageLoader; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.R; import org.telegram.messenger.SharedConfig; import org.telegram.messenger.Utilities; import org.telegram.messenger.video.MP4Builder; +import org.telegram.messenger.video.MediaCodecVideoConvertor; import org.telegram.messenger.video.Mp4Movie; -import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AnimatedFloat; import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.InstantCameraView; import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.RLottieDrawable; import java.io.File; +import java.io.IOException; import java.lang.ref.WeakReference; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -88,16 +100,19 @@ import tw.nekomimi.nekogram.utils.VibrateUtil; @SuppressLint("NewApi") -public class CameraView extends FrameLayout implements TextureView.SurfaceTextureListener { +public class CameraView extends FrameLayout implements TextureView.SurfaceTextureListener, CameraController.ICameraView { + + public boolean WRITE_TO_FILE_IN_BACKGROUND = true; - private Size previewSize; - private Size pictureSize; - CameraInfo info; + public boolean isStory; + private Size[] previewSize = new Size[2]; + private Size[] pictureSize = new Size[2]; + CameraInfo[] info = new CameraInfo[2]; private boolean mirror; private boolean lazy; private TextureView textureView; private ImageView blurredStubView; - private CameraSession cameraSession; + private CameraSession[] cameraSession = new CameraSession[2]; private boolean inited; private CameraViewDelegate delegate; private int clipTop; @@ -129,33 +144,17 @@ public class CameraView extends FrameLayout implements TextureView.SurfaceTextur private File cameraFile; boolean firstFrameRendered; + boolean firstFrame2Rendered; private final Object layoutLock = new Object(); - private float[] mMVPMatrix = new float[16]; - private float[] mSTMatrix = new float[16]; - private float[] moldSTMatrix = new float[16]; - private static final String VERTEX_SHADER = - "uniform mat4 uMVPMatrix;\n" + - "uniform mat4 uSTMatrix;\n" + - "attribute vec4 aPosition;\n" + - "attribute vec4 aTextureCoord;\n" + - "varying vec2 vTextureCoord;\n" + - "void main() {\n" + - " gl_Position = uMVPMatrix * aPosition;\n" + - " vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" + - "}\n"; - - private static final String FRAGMENT_SCREEN_SHADER = - "#extension GL_OES_EGL_image_external : require\n" + - "precision lowp float;\n" + - "varying vec2 vTextureCoord;\n" + - "uniform samplerExternalOES sTexture;\n" + - "void main() {\n" + - " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" + - "}\n"; + private float[][] mMVPMatrix = new float[2][16]; + private float[][] mSTMatrix = new float[2][16]; + private float[][] moldSTMatrix = new float[2][16]; private FloatBuffer vertexBuffer; private FloatBuffer textureBuffer; + private float[][] cameraMatrix = new float[2][16]; + private volatile float lastCrossfadeValue = 0; private final static int audioSampleRate = 44100; @@ -165,7 +164,10 @@ public void setRecordFile(File generateVideoPath) { Runnable onRecordingFinishRunnable; + private CameraSession cameraSessionRecording; + public boolean startRecording(File path, Runnable onFinished) { + cameraSessionRecording = cameraSession[0]; cameraThread.startRecording(path); onRecordingFinishRunnable = onFinished; return true; @@ -177,6 +179,7 @@ public void stopRecording() { ValueAnimator flipAnimator; boolean flipHalfReached; + boolean flipping = false; private int fpsLimit = -1; long nextFrameTimeNs; @@ -193,17 +196,12 @@ public void startSwitchingAnimation() { Drawable drawable = new BitmapDrawable(bitmap); blurredStubView.setBackground(drawable); } - blurredStubView.setAlpha(0f); - } else { - blurredStubView.setAlpha(1f); } + blurredStubView.setAlpha(1f); blurredStubView.setVisibility(View.VISIBLE); - synchronized (layoutLock) { - firstFrameRendered = false; - } - flipHalfReached = false; + flipping = true; flipAnimator = ValueAnimator.ofFloat(0, 1f); flipAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override @@ -222,7 +220,7 @@ public void onAnimationUpdate(ValueAnimator valueAnimator) { textureView.setRotationY(rotation); blurredStubView.setRotationY(rotation); if (halfReached && !flipHalfReached) { - blurredStubView.setAlpha(1f); +// blurredStubView.setAlpha(1f); flipHalfReached = true; } } @@ -241,18 +239,149 @@ public void onAnimationEnd(Animator animation) { blurredStubView.setRotationY(0); if (!flipHalfReached) { - blurredStubView.setAlpha(1f); +// blurredStubView.setAlpha(1f); flipHalfReached = true; } invalidate(); } }); - flipAnimator.setDuration(400); + flipAnimator.setDuration(500); flipAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); flipAnimator.start(); invalidate(); } + protected boolean dual; + private boolean dualCameraAppeared; + private Matrix dualMatrix = new Matrix(); + private long toggleDualUntil; + private boolean closingDualCamera; + private boolean initFirstCameraAfterSecond; + public boolean toggledDualAsSave; + + public boolean isDual() { + return dual; + } + + private void enableDualInternal() { + if (cameraSession[1] != null) { + if (closingDualCamera) { + return; + } + closingDualCamera = true; + CameraController.getInstance().close(cameraSession[1], null, null, () -> { + closingDualCamera = false; + enableDualInternal(); + }); + if (cameraSessionRecording == cameraSession[1]) { + cameraSessionRecording = null; + } + cameraSession[1] = null; + addToDualWait(400L); + return; + } + if (!isFrontface && "samsung".equalsIgnoreCase(Build.MANUFACTURER) && !toggledDualAsSave && cameraSession[0] != null) { + final Handler handler = cameraThread.getHandler(); + if (handler != null) { + cameraThread.sendMessage(handler.obtainMessage(cameraThread.BLUR_CAMERA1), 0); + } + CameraController.getInstance().close(cameraSession[0], null, null, () -> { +// inited = false; +// synchronized (layoutLock) { +// firstFrameRendered = false; +// } + initFirstCameraAfterSecond = true; + updateCameraInfoSize(1); + if (handler != null) { + cameraThread.sendMessage(handler.obtainMessage(cameraThread.DO_DUAL_START, info[1].cameraId, 0, dualMatrix), 0); + } + addToDualWait(1200L); + }); + cameraSession[0] = null; + return; + } + updateCameraInfoSize(1); + final Handler handler = cameraThread.getHandler(); + if (handler != null) { + cameraThread.sendMessage(handler.obtainMessage(cameraThread.DO_DUAL_START, info[1].cameraId, 0, dualMatrix), 0); + } + addToDualWait(800L); + } + + public void toggleDual() { + toggleDual(false); + } + + public void toggleDual(boolean force) { + if (!force && (flipping || closingDualCamera || (System.currentTimeMillis() < toggleDualUntil || dual != dualCameraAppeared) && !dual)) { + return; + } + addToDualWait(200L); + dual = !dual; + if (dual) { + if (cameraSession[0] != null) { + cameraSession[0].setCurrentFlashMode(Camera.Parameters.FLASH_MODE_OFF); + } + enableDualInternal(); + } else { + if (cameraSession[1] == null || !cameraSession[1].isInitied()) { + dual = !dual; + return; + } + if (cameraSession[1] != null) { + closingDualCamera = true; + if (cameraSessionRecording == cameraSession[1]) { + cameraSessionRecording = null; + } + CameraController.getInstance().close(cameraSession[1], null, null, () -> { + closingDualCamera = false; + dualCameraAppeared = false; + addToDualWait(400L); + final Handler handler = cameraThread.getHandler(); + if (handler != null) { + cameraThread.sendMessage(handler.obtainMessage(cameraThread.DO_DUAL_END), 0); + } + }); + cameraSession[1] = null; + previewSize[1] = null; + pictureSize[1] = null; + info[1] = null; + } else { + dualCameraAppeared = false; + } + if (!closingDualCamera) { + final Handler handler = cameraThread.getHandler(); + if (handler != null) { + cameraThread.sendMessage(handler.obtainMessage(cameraThread.DO_DUAL_END), 0); + } + } + } + toggledDualAsSave = false; + } + + private void addToDualWait(long add) { + final long now = System.currentTimeMillis(); + if (toggleDualUntil < now) { + toggleDualUntil = now + add; + } else { + toggleDualUntil += add; + } + } + + public Matrix getDualPosition() { + return dualMatrix; + } + + public void updateDualPosition() { + if (cameraThread == null) { + return; + } + final Handler handler = cameraThread.getHandler(); + if (handler != null) { + cameraThread.sendMessage(handler.obtainMessage(cameraThread.DO_DUAL_MOVE, dualMatrix), 0); + } + } + public interface CameraViewDelegate { void onCameraInit(); } @@ -294,8 +423,8 @@ public void initTexture() { public void setOptimizeForBarcode(boolean value) { optimizeForBarcode = value; - if (cameraSession != null) { - cameraSession.setOptimizeForBarcode(true); + if (cameraSession[0] != null) { + cameraSession[0].setOptimizeForBarcode(true); } } @@ -331,6 +460,12 @@ public void setThumbDrawable(Drawable drawable) { if (thumbDrawable != null) { thumbDrawable.setCallback(this); } + if (!firstFrameRendered) { + blurredStubView.animate().setListener(null).cancel(); + blurredStubView.setBackground(thumbDrawable); + blurredStubView.setAlpha(1f); + blurredStubView.setVisibility(View.VISIBLE); + } } private int measurementsCount = 0; @@ -345,18 +480,18 @@ protected void onAttachedToWindow() { protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec), height = MeasureSpec.getSize(heightMeasureSpec); - if (previewSize != null && cameraSession != null) { + if (previewSize[0] != null && cameraSession[0] != null) { int frameWidth, frameHeight; if ((lastWidth != width || lastHeight != height) && measurementsCount > 1) { - cameraSession.updateRotation(); + cameraSession[0].updateRotation(); } measurementsCount++; - if (cameraSession.getWorldAngle() == 90 || cameraSession.getWorldAngle() == 270) { - frameWidth = previewSize.getWidth(); - frameHeight = previewSize.getHeight(); + if (cameraSession[0].getWorldAngle() == 90 || cameraSession[0].getWorldAngle() == 270) { + frameWidth = previewSize[0].getWidth(); + frameHeight = previewSize[0].getHeight(); } else { - frameWidth = previewSize.getHeight(); - frameHeight = previewSize.getWidth(); + frameWidth = previewSize[0].getHeight(); + frameHeight = previewSize[0].getWidth(); } float s = Math.max(MeasureSpec.getSize(widthMeasureSpec) / (float) frameWidth , MeasureSpec.getSize(heightMeasureSpec) / (float) frameHeight); blurredStubView.getLayoutParams().width = textureView.getLayoutParams().width = (int) (s * frameWidth); @@ -366,20 +501,27 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { checkPreviewMatrix(); lastWidth = width; lastHeight = height; + + pixelW = getMeasuredWidth(); + pixelH = getMeasuredHeight(); + if (pixelDualW <= 0) { + pixelDualW = getMeasuredWidth(); + pixelDualH = getMeasuredHeight(); + } } public float getTextureHeight(float width, float height) { - if (previewSize == null || cameraSession == null) { + if (previewSize[0] == null || cameraSession[0] == null) { return height; } int frameWidth, frameHeight; - if (cameraSession.getWorldAngle() == 90 || cameraSession.getWorldAngle() == 270) { - frameWidth = previewSize.getWidth(); - frameHeight = previewSize.getHeight(); + if (cameraSession[0].getWorldAngle() == 90 || cameraSession[0].getWorldAngle() == 270) { + frameWidth = previewSize[0].getWidth(); + frameHeight = previewSize[0].getHeight(); } else { - frameWidth = previewSize.getHeight(); - frameHeight = previewSize.getWidth(); + frameWidth = previewSize[0].getHeight(); + frameHeight = previewSize[0].getWidth(); } float s = Math.max(width / (float) frameWidth , height / (float) frameHeight); return (int) (s * frameHeight); @@ -417,25 +559,114 @@ public boolean hasFrontFaceCamera() { return false; } + private Integer shape; + public void dualToggleShape() { + if (flipping || !dual) { + return; + } + Handler handler = cameraThread.getHandler(); + if (shape == null) { + shape = MessagesController.getGlobalMainSettings().getInt("dualshape", 0); + } + shape++; + MessagesController.getGlobalMainSettings().edit().putInt("dualshape", shape).apply(); + if (handler != null) { + handler.sendMessage(handler.obtainMessage(cameraThread.DO_DUAL_TOGGLE_SHAPE)); + } + } + + public int getDualShape() { + if (shape == null) { + shape = MessagesController.getGlobalMainSettings().getInt("dualshape", 0); + } + return shape; + } + + private long lastDualSwitchTime; + public void switchCamera() { - if (cameraSession != null) { - CameraController.getInstance().close(cameraSession, new CountDownLatch(1), null); - cameraSession = null; + if (flipping || System.currentTimeMillis() < toggleDualUntil && !dualCameraAppeared) { + return; + } + if (dual) { + if (!dualCameraAppeared || System.currentTimeMillis() - lastDualSwitchTime < 420) { + return; + } + lastDualSwitchTime = System.currentTimeMillis(); + CameraInfo info0 = info[0]; + info[0] = info[1]; + info[1] = info0; + + Size previewSize0 = previewSize[0]; + previewSize[0] = previewSize[1]; + previewSize[1] = previewSize0; + + Size pictureSize0 = pictureSize[0]; + pictureSize[0] = pictureSize[1]; + pictureSize[1] = pictureSize0; + + CameraSession cameraSession0 = cameraSession[0]; + cameraSession[0] = cameraSession[1]; + cameraSession[1] = cameraSession0; + + isFrontface = !isFrontface; + + Handler handler = cameraThread.getHandler(); + if (handler != null) { + handler.sendMessage(handler.obtainMessage(cameraThread.DO_DUAL_FLIP)); + } + return; + } + startSwitchingAnimation(); + if (cameraSession[0] != null) { + if (cameraSessionRecording == cameraSession[0]) { + cameraSessionRecording = null; + } + CameraController.getInstance().close(cameraSession[0], null, null, () -> { + inited = false; + synchronized (layoutLock) { + firstFrameRendered = false; + } + updateCameraInfoSize(0); + cameraThread.reinitForNewCamera(); + }); + cameraSession[0] = null; } - inited = false; isFrontface = !isFrontface; - updateCameraInfoSize(); - cameraThread.reinitForNewCamera(); + } + + public void resetCamera() { + if (cameraSession[0] != null) { + if (cameraSessionRecording == cameraSession[0]) { + cameraSessionRecording = null; + } + final Handler handler = cameraThread.getHandler(); + if (handler != null) { + cameraThread.sendMessage(handler.obtainMessage(cameraThread.BLUR_CAMERA1), 0); + } + CameraController.getInstance().close(cameraSession[0], null, null, () -> { + inited = false; + synchronized (layoutLock) { + firstFrameRendered = false; + } + updateCameraInfoSize(0); + cameraThread.reinitForNewCamera(); + }); + cameraSession[0] = null; + } } public Size getPreviewSize() { - return previewSize; + return previewSize[0]; } - CameraGLThread cameraThread; + protected CameraGLThread cameraThread; @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { - updateCameraInfoSize(); + updateCameraInfoSize(0); + if (dual) { + updateCameraInfoSize(1); + } surfaceHeight = height; surfaceWidth = width; @@ -449,19 +680,24 @@ public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int hei } } - private void updateCameraInfoSize() { + private void updateCameraInfoSize(int i) { ArrayList cameraInfos = CameraController.getInstance().getCameras(); if (cameraInfos == null) { return; } for (int a = 0; a < cameraInfos.size(); a++) { CameraInfo cameraInfo = cameraInfos.get(a); - if (isFrontface && cameraInfo.frontCamera != 0 || !isFrontface && cameraInfo.frontCamera == 0) { - info = cameraInfo; + boolean cameraInfoIsFrontface = cameraInfo.frontCamera != 0; + boolean shouldBeFrontface = isFrontface; + if (i == 1) { + shouldBeFrontface = !shouldBeFrontface; + } + if (cameraInfoIsFrontface == shouldBeFrontface) { + info[i] = cameraInfo; break; } } - if (info == null) { + if (info[i] == null) { return; } float size4to3 = 4.0f / 3.0f; @@ -499,15 +735,18 @@ private void updateCameraInfoSize() { photoMaxWidth = 1280; photoMaxHeight = 960; } else { - photoMaxWidth = 1920; - photoMaxHeight = 1080; + photoMaxWidth = isStory ? 1280 : 1920; + photoMaxHeight = isStory ? 720 : 1080; } } } - previewSize = CameraController.chooseOptimalSize(info.getPreviewSizes(), wantedWidth, wantedHeight, aspectRatio); - pictureSize = CameraController.chooseOptimalSize(info.getPictureSizes(), photoMaxWidth, photoMaxHeight, aspectRatio); + previewSize[i] = CameraController.chooseOptimalSize(info[i].getPreviewSizes(), wantedWidth, wantedHeight, aspectRatio, isStory); + pictureSize[i] = CameraController.chooseOptimalSize(info[i].getPictureSizes(), photoMaxWidth, photoMaxHeight, aspectRatio, false); + if (BuildVars.LOGS_ENABLED) { + FileLog.d("camera preview " + previewSize[0]); + } requestLayout(); } @@ -524,15 +763,18 @@ public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) { cameraThread.shutdown(0); cameraThread.postRunnable(() -> this.cameraThread = null); } - if (cameraSession != null) { - CameraController.getInstance().close(cameraSession, null, null); + if (cameraSession[0] != null) { + CameraController.getInstance().close(cameraSession[0], null, null); + } + if (cameraSession[1] != null) { + CameraController.getInstance().close(cameraSession[1], null, null); } return false; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { - if (!inited && cameraSession != null && cameraSession.isInitied()) { + if (!inited && cameraSession[0] != null && cameraSession[0].isInitied()) { if (delegate != null) { delegate.onCameraInit(); } @@ -583,17 +825,21 @@ public void setClipBottom(int value) { private final Runnable updateRotationMatrix = () -> { final CameraGLThread cameraThread = this.cameraThread; - if (cameraThread != null && cameraThread.currentSession != null) { - int rotationAngle = cameraThread.currentSession.getWorldAngle(); - android.opengl.Matrix.setIdentityM(mMVPMatrix, 0); - if (rotationAngle != 0) { - android.opengl.Matrix.rotateM(mMVPMatrix, 0, rotationAngle, 0, 0, 1); + if (cameraThread != null) { + for (int i = 0; i < 2; ++i) { + if (cameraThread.currentSession[i] != null) { + int rotationAngle = cameraThread.currentSession[i].getWorldAngle(); + android.opengl.Matrix.setIdentityM(mMVPMatrix[i], 0); + if (rotationAngle != 0) { + android.opengl.Matrix.rotateM(mMVPMatrix[i], 0, rotationAngle, 0, 0, 1); + } + } } } }; private void checkPreviewMatrix() { - if (previewSize == null || textureView == null) { + if (previewSize[0] == null || textureView == null) { return; } @@ -601,8 +847,8 @@ private void checkPreviewMatrix() { int viewHeight = textureView.getHeight(); Matrix matrix = new Matrix(); - if (cameraSession != null) { - matrix.postRotate(cameraSession.getDisplayOrientation()); + if (cameraSession[0] != null) { + matrix.postRotate(cameraSession[0].getDisplayOrientation()); } matrix.postScale(viewWidth / 2000f, viewHeight / 2000f); matrix.postTranslate(viewWidth / 2f, viewHeight / 2f); @@ -640,19 +886,22 @@ private int clamp(int x, int min, int max) { } public void focusToPoint(int x, int y, boolean visible) { + focusToPoint(0, x, y, x, y, visible); + } + + public void focusToPoint(int i, int x, int y, int vx, int vy, boolean visible) { Rect focusRect = calculateTapArea(x, y, 1f); Rect meteringRect = calculateTapArea(x, y, 1.5f); - if (cameraSession != null) { - cameraSession.focusToRect(focusRect, meteringRect); + if (cameraSession[i] != null) { + cameraSession[i].focusToRect(focusRect, meteringRect); } - if (visible) { focusProgress = 0.0f; innerAlpha = 1.0f; outerAlpha = 1.0f; - cx = x; - cy = y; + cx = vx; + cy = vy; lastDrawTime = System.currentTimeMillis(); invalidate(); } @@ -663,8 +912,8 @@ public void focusToPoint(int x, int y) { } public void setZoom(float value) { - if (cameraSession != null) { - cameraSession.setZoom(value); + if (cameraSession[0] != null) { + cameraSession[0].setZoom(value); } } @@ -677,13 +926,23 @@ public boolean isInited() { } public CameraSession getCameraSession() { - return cameraSession; + return getCameraSession(0); + } + + public CameraSession getCameraSession(int i) { + return cameraSession[i]; + } + + public CameraSession getCameraSessionRecording() { + return cameraSessionRecording; } public void destroy(boolean async, final Runnable beforeDestroyRunnable) { - if (cameraSession != null) { - cameraSession.destroy(); - CameraController.getInstance().close(cameraSession, !async ? new CountDownLatch(1) : null, beforeDestroyRunnable); + for (int i = 0; i < 2; ++i) { + if (cameraSession[i] != null) { + cameraSession[i].destroy(); + CameraController.getInstance().close(cameraSession[i], !async ? new CountDownLatch(1) : null, beforeDestroyRunnable); + } } } @@ -770,12 +1029,26 @@ protected void dispatchDraw(Canvas canvas) { } } + private int videoWidth; + private int videoHeight; + + public int getVideoWidth() { + return videoWidth; + } + public int getVideoHeight() { + return videoHeight; + } private int[] position = new int[2]; - private int[] cameraTexture = new int[1]; + private int[][] cameraTexture = new int[2][1]; private int[] oldCameraTexture = new int[1]; private VideoRecorder videoEncoder; + private volatile float pixelW, pixelH; + private volatile float pixelDualW, pixelDualH; + private volatile float lastShapeTo; + private volatile float shapeValue; + public class CameraGLThread extends DispatchQueue { private final static int EGL_CONTEXT_CLIENT_VERSION = 0x3098; @@ -788,9 +1061,9 @@ public class CameraGLThread extends DispatchQueue { private EGLConfig eglConfig; private boolean initied; - private CameraSession currentSession; + private CameraSession currentSession[] = new CameraSession[2]; - private SurfaceTexture cameraSurface; + private final SurfaceTexture[] cameraSurface = new SurfaceTexture[2]; private final int DO_RENDER_MESSAGE = 0; private final int DO_SHUTDOWN_MESSAGE = 1; @@ -799,22 +1072,53 @@ public class CameraGLThread extends DispatchQueue { private final int DO_START_RECORDING = 4; private final int DO_STOP_RECORDING = 5; + private final int DO_DUAL_START = 6; + private final int DO_DUAL_MOVE = 7; + private final int DO_DUAL_FLIP = 8; + private final int DO_DUAL_TOGGLE_SHAPE = 9; + private final int DO_DUAL_END = 10; + private final int BLUR_CAMERA1 = 11; + private int drawProgram; private int vertexMatrixHandle; private int textureMatrixHandle; + private int cameraMatrixHandle; + private int oppositeCameraMatrixHandle; private int positionHandle; private int textureHandle; - + private int roundRadiusHandle; + private int pixelHandle; + private int dualHandle; + private int scaleHandle; + private int blurHandle; + private int alphaHandle; + private int crossfadeHandle; + private int shapeFromHandle; + private int shapeToHandle; + private int shapeHandle; + + private boolean initDual, initDualReverse; + private Matrix initDualMatrix; private boolean recording; private boolean needRecord; - private Integer cameraId = 0; + private int cameraId[] = new int[] { -1, -1 }; + + private final float[] verticesData = { + -1.0f, -1.0f, 0, + 1.0f, -1.0f, 0, + -1.0f, 1.0f, 0, + 1.0f, 1.0f, 0 + }; //private InstantCameraView.VideoRecorder videoEncoder; public CameraGLThread(SurfaceTexture surface) { super("CameraGLThread"); surfaceTexture = surface; + initDual = dual; + initDualReverse = !isFrontface; + initDualMatrix = dualMatrix; } private boolean initGL() { @@ -903,10 +1207,10 @@ private boolean initGL() { } GL gl = eglContext.getGL(); - android.opengl.Matrix.setIdentityM(mSTMatrix, 0); + android.opengl.Matrix.setIdentityM(mSTMatrix[0], 0); - int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER); - int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SCREEN_SHADER); + int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, RLottieDrawable.readRes(null, R.raw.camera_vert)); + int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, RLottieDrawable.readRes(null, R.raw.camera_frag)); if (vertexShader != 0 && fragmentShader != 0) { drawProgram = GLES20.glCreateProgram(); GLES20.glAttachShader(drawProgram, vertexShader); @@ -925,6 +1229,19 @@ private boolean initGL() { textureHandle = GLES20.glGetAttribLocation(drawProgram, "aTextureCoord"); vertexMatrixHandle = GLES20.glGetUniformLocation(drawProgram, "uMVPMatrix"); textureMatrixHandle = GLES20.glGetUniformLocation(drawProgram, "uSTMatrix"); + cameraMatrixHandle = GLES20.glGetUniformLocation(drawProgram, "cameraMatrix"); + oppositeCameraMatrixHandle = GLES20.glGetUniformLocation(drawProgram, "oppositeCameraMatrix"); + + roundRadiusHandle = GLES20.glGetUniformLocation(drawProgram, "roundRadius"); + pixelHandle = GLES20.glGetUniformLocation(drawProgram, "pixelWH"); + dualHandle = GLES20.glGetUniformLocation(drawProgram, "dual"); + scaleHandle = GLES20.glGetUniformLocation(drawProgram, "scale"); + blurHandle = GLES20.glGetUniformLocation(drawProgram, "blur"); + alphaHandle = GLES20.glGetUniformLocation(drawProgram, "alpha"); + crossfadeHandle = GLES20.glGetUniformLocation(drawProgram, "crossfade"); + shapeFromHandle = GLES20.glGetUniformLocation(drawProgram, "shapeFrom"); + shapeToHandle = GLES20.glGetUniformLocation(drawProgram, "shapeTo"); + shapeHandle = GLES20.glGetUniformLocation(drawProgram, "shapeT"); } } else { if (BuildVars.LOGS_ENABLED) { @@ -934,28 +1251,24 @@ private boolean initGL() { return false; } - GLES20.glGenTextures(1, cameraTexture, 0); - GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, cameraTexture[0]); + GLES20.glGenTextures(1, cameraTexture[0], 0); + GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, cameraTexture[0][0]); GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); - android.opengl.Matrix.setIdentityM(mMVPMatrix, 0); + GLES20.glEnable(GLES20.GL_BLEND); + GLES20.glBlendFuncSeparate(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA, GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA); + + android.opengl.Matrix.setIdentityM(mMVPMatrix[0], 0); if (BuildVars.LOGS_ENABLED) { FileLog.e("gl initied"); } - float tX = 1.0f / 2.0f; float tY = 1.0f / 2.0f; - float[] verticesData = { - -1.0f, -1.0f, 0, - 1.0f, -1.0f, 0, - -1.0f, 1.0f, 0, - 1.0f, 1.0f, 0 - }; float[] texData = { 0.5f - tX, 0.5f - tY, 0.5f + tX, 0.5f - tY, @@ -969,21 +1282,83 @@ private boolean initGL() { textureBuffer = ByteBuffer.allocateDirect(texData.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer(); textureBuffer.put(texData).position(0); - cameraSurface = new SurfaceTexture(cameraTexture[0]); - cameraSurface.setOnFrameAvailableListener(surfaceTexture -> requestRender()); - createCamera(cameraSurface); + cameraSurface[0] = new SurfaceTexture(cameraTexture[0][0]); + cameraSurface[0].setOnFrameAvailableListener(this::updTex); + + if (initDual) { + GLES20.glGenTextures(1, cameraTexture[1], 0); + GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, cameraTexture[1][0]); + GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); + GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); + GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); + + cameraSurface[1] = new SurfaceTexture(cameraTexture[1][0]); + cameraSurface[1].setOnFrameAvailableListener(this::updTex); + + } + + if (initDual) { + if (initDualReverse) { + createCamera(cameraSurface[1], 1); + createCamera(cameraSurface[0], 0); + } else { + createCamera(cameraSurface[0], 0); + createCamera(cameraSurface[1], 1); + } + } else { + createCamera(cameraSurface[0], 0); + } + + Matrix simpleMatrix = new Matrix(); + simpleMatrix.reset(); + getValues(simpleMatrix, cameraMatrix[0]); + if (initDualMatrix != null) { + getValues(initDualMatrix, cameraMatrix[1]); + } else { + getValues(simpleMatrix, cameraMatrix[1]); + } + + lastShapeTo = shapeTo; return true; } + private void updTex(SurfaceTexture surfaceTexture) { + if (surfaceTexture == cameraSurface[0]) { + if (!ignoreCamera1Upd && System.currentTimeMillis() > camera1AppearedUntil) { + camera1Appeared = true; + } + requestRender(true, false); + } else if (surfaceTexture == cameraSurface[1]) { + if (!dualAppeared) { + synchronized (layoutLock) { + dualCameraAppeared = true; + addToDualWait(1200L); + } + } + dualAppeared = true; + requestRender(false, true); + } + } + public void reinitForNewCamera() { Handler handler = getHandler(); if (handler != null) { - sendMessage(handler.obtainMessage(DO_REINIT_MESSAGE, info.cameraId), 0); + sendMessage(handler.obtainMessage(DO_REINIT_MESSAGE, info[0].cameraId), 0); } } public void finish() { + if (cameraSurface != null) { + for (int i = 0; i < cameraSurface.length; ++i) { + if (cameraSurface[i] != null) { + cameraSurface[i].setOnFrameAvailableListener(null); + cameraSurface[i].release(); + cameraSurface[i] = null; + } + } + } if (eglSurface != null) { egl10.eglMakeCurrent(eglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); egl10.eglDestroySurface(eglDisplay, eglSurface); @@ -999,16 +1374,25 @@ public void finish() { } } - public void setCurrentSession(CameraSession session) { + public void setCurrentSession(CameraSession session, int i) { Handler handler = getHandler(); if (handler != null) { - sendMessage(handler.obtainMessage(DO_SETSESSION_MESSAGE, session), 0); + sendMessage(handler.obtainMessage(DO_SETSESSION_MESSAGE, i, 0, session), 0); } } + private boolean crossfading; + private final AnimatedFloat crossfade = new AnimatedFloat(() -> this.requestRender(false, false), 560, CubicBezierInterpolator.EASE_OUT_QUINT); + private final AnimatedFloat camera1Appear = new AnimatedFloat(1f, () -> this.requestRender(false, false), 0, 420, CubicBezierInterpolator.EASE_OUT_QUINT); + private final AnimatedFloat dualAppear = new AnimatedFloat(() -> this.requestRender(false, false), 340, CubicBezierInterpolator.EASE_OUT_QUINT); + private final AnimatedFloat shape = new AnimatedFloat(() -> this.requestRender(false, false), 340, CubicBezierInterpolator.EASE_OUT_QUINT); + private boolean dualAppeared, camera1Appeared, ignoreCamera1Upd; + private long camera1AppearedUntil; + private float shapeTo = MessagesController.getGlobalMainSettings().getInt("dualshape", 0); + final int array[] = new int[1]; - private void onDraw(Integer cameraId, boolean updateTexImage) { + private void onDraw(int cameraId1, int cameraId2, boolean updateTexImage1, boolean updateTexImage2) { if (!initied) { return; } @@ -1021,9 +1405,32 @@ private void onDraw(Integer cameraId, boolean updateTexImage) { return; } } - if (updateTexImage) { + + final boolean waitingForCamera1; + final boolean dual; + synchronized (layoutLock) { + dual = CameraView.this.dual; + waitingForCamera1 = !camera1Appeared; + } + + if ((updateTexImage1 || updateTexImage2) && !waitingForCamera1) { + updateTexImage1 = updateTexImage2 = true; + } + + if (updateTexImage1) { try { - cameraSurface.updateTexImage(); + if (cameraSurface[0] != null && cameraId1 >= 0) { + cameraSurface[0].updateTexImage(); + } + } catch (Throwable e) { + FileLog.e(e); + } + } + if (updateTexImage2) { + try { + if (cameraSurface[1] != null && cameraId2 >= 0) { + cameraSurface[1].updateTexImage(); + } } catch (Throwable e) { FileLog.e(e); } @@ -1046,54 +1453,139 @@ private void onDraw(Integer cameraId, boolean updateTexImage) { } } - if (currentSession == null || currentSession.cameraInfo.cameraId != cameraId) { + if (currentSession[0] == null || currentSession[0].cameraInfo.cameraId != cameraId1) { return; } - if (recording && videoEncoder != null) { - videoEncoder.frameAvailable(cameraSurface, cameraId, System.nanoTime()); + if (recording && videoEncoder != null && (updateTexImage1 || updateTexImage2)) { + videoEncoder.frameAvailable(cameraSurface[0], cameraId1, System.nanoTime()); } if (!shouldRenderFrame) { return; } - cameraSurface.getTransformMatrix(mSTMatrix); - egl10.eglQuerySurface(eglDisplay, eglSurface, EGL10.EGL_WIDTH, array); int drawnWidth = array[0]; egl10.eglQuerySurface(eglDisplay, eglSurface, EGL10.EGL_HEIGHT, array); int drawnHeight = array[0]; GLES20.glViewport(0, 0, drawnWidth, drawnHeight); + if (dual) { + GLES20.glClearColor(0.f, 0.f, 0.f, 1.f); + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); + } + shapeValue = shape.set(shapeTo); + final float crossfade = lastCrossfadeValue = this.crossfade.set(0f); + final float dualScale = dualAppear.set(dualAppeared ? 1f : 0f); + final float camera1Blur = 1f - camera1Appear.set(camera1Appeared); + if (crossfade <= 0) { + crossfading = false; + } + for (int a = -1; a < 2; ++a) { + if (a == -1 && !crossfading) { + continue; + } + final int i = a < 0 ? 1 : a; + if (cameraSurface[i] == null) { + continue; + } + if (i != 0 && (currentSession[i] == null || !currentSession[i].isInitied()) || i == 0 && cameraId1 < 0 && !dual || i == 1 && cameraId2 < 0) { + continue; + } - GLES20.glUseProgram(drawProgram); - GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, cameraTexture[0]); + if (i == 0 && updateTexImage1 || i == 1 && updateTexImage2) { + cameraSurface[i].getTransformMatrix(mSTMatrix[i]); + } - GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 12, vertexBuffer); - GLES20.glEnableVertexAttribArray(positionHandle); + GLES20.glUseProgram(drawProgram); + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, cameraTexture[i][0]); - GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 8, textureBuffer); - GLES20.glEnableVertexAttribArray(textureHandle); + GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 12, vertexBuffer); + GLES20.glEnableVertexAttribArray(positionHandle); - GLES20.glUniformMatrix4fv(textureMatrixHandle, 1, false, mSTMatrix, 0); - GLES20.glUniformMatrix4fv(vertexMatrixHandle, 1, false, mMVPMatrix, 0); + GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 8, textureBuffer); + GLES20.glEnableVertexAttribArray(textureHandle); - GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + GLES20.glUniformMatrix4fv(cameraMatrixHandle, 1, false, cameraMatrix[i], 0); + GLES20.glUniformMatrix4fv(oppositeCameraMatrixHandle, 1, false, cameraMatrix[1 - i], 0); - GLES20.glDisableVertexAttribArray(positionHandle); - GLES20.glDisableVertexAttribArray(textureHandle); - GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0); - GLES20.glUseProgram(0); + GLES20.glUniformMatrix4fv(textureMatrixHandle, 1, false, mSTMatrix[i], 0); + GLES20.glUniformMatrix4fv(vertexMatrixHandle, 1, false, mMVPMatrix[i], 0); + if (i == 0) { + GLES20.glUniform2f(pixelHandle, pixelW, pixelH); + GLES20.glUniform1f(dualHandle, dual ? 1 : 0); + } else { + GLES20.glUniform2f(pixelHandle, pixelDualW, pixelDualH); + GLES20.glUniform1f(dualHandle, 1f); + } + GLES20.glUniform1f(blurHandle, i == 0 ? camera1Blur : 0f); + if (i == 1) { + GLES20.glUniform1f(alphaHandle, 1); + if (a < 0) { + GLES20.glUniform1f(roundRadiusHandle, 0); + GLES20.glUniform1f(scaleHandle, 1); + GLES20.glUniform1f(shapeFromHandle, 2); + GLES20.glUniform1f(shapeToHandle, 2); + GLES20.glUniform1f(shapeHandle, 0); + GLES20.glUniform1f(crossfadeHandle, 1); + } else if (!crossfading) { + GLES20.glUniform1f(roundRadiusHandle, AndroidUtilities.dp(16)); + GLES20.glUniform1f(scaleHandle, dualScale); + GLES20.glUniform1f(shapeFromHandle, (float) Math.floor(shapeValue)); + GLES20.glUniform1f(shapeToHandle, (float) Math.ceil(shapeValue)); + GLES20.glUniform1f(shapeHandle, shapeValue - (float) Math.floor(shapeValue)); + GLES20.glUniform1f(crossfadeHandle, 0); + } else { + GLES20.glUniform1f(roundRadiusHandle, AndroidUtilities.dp(16)); + GLES20.glUniform1f(scaleHandle, 1f - crossfade); + GLES20.glUniform1f(shapeFromHandle, (float) Math.floor(shapeValue)); + GLES20.glUniform1f(shapeToHandle, (float) Math.ceil(shapeValue)); + GLES20.glUniform1f(shapeHandle, shapeValue - (float) Math.floor(shapeValue)); + GLES20.glUniform1f(shapeHandle, crossfade); + GLES20.glUniform1f(crossfadeHandle, 0); + } + } else { + GLES20.glUniform1f(alphaHandle, 1f); + if (crossfading) { + GLES20.glUniform1f(roundRadiusHandle, AndroidUtilities.lerp(AndroidUtilities.dp(12), AndroidUtilities.dp(16), crossfade)); + GLES20.glUniform1f(scaleHandle, 1f); + GLES20.glUniform1f(shapeFromHandle, shapeTo); + GLES20.glUniform1f(shapeToHandle, 2); + GLES20.glUniform1f(shapeHandle, Utilities.clamp((1f - crossfade), 1, 0)); + GLES20.glUniform1f(crossfadeHandle, crossfade); + } else { + GLES20.glUniform1f(roundRadiusHandle, 0); + GLES20.glUniform1f(scaleHandle, 1f); + GLES20.glUniform1f(shapeFromHandle, 2f); + GLES20.glUniform1f(shapeToHandle, 2f); + GLES20.glUniform1f(shapeHandle, 0f); + GLES20.glUniform1f(crossfadeHandle, 0f); + } + } + + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + + GLES20.glDisableVertexAttribArray(positionHandle); + GLES20.glDisableVertexAttribArray(textureHandle); + GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0); + GLES20.glUseProgram(0); + } egl10.eglSwapBuffers(eglDisplay, eglSurface); synchronized (layoutLock) { - if (!firstFrameRendered) { + if (!firstFrameRendered && !waitingForCamera1) { firstFrameRendered = true; AndroidUtilities.runOnUIThread(() -> { - onFirstFrameRendered(); + onFirstFrameRendered(0); + }); + } + if (!firstFrame2Rendered && dualAppeared) { + firstFrame2Rendered = true; + AndroidUtilities.runOnUIThread(() -> { + onFirstFrameRendered(1); }); } } @@ -1111,7 +1603,7 @@ public void handleMessage(Message inputMessage) { switch (what) { case DO_RENDER_MESSAGE: - onDraw((Integer) inputMessage.obj, true); + onDraw(inputMessage.arg1, inputMessage.arg2, inputMessage.obj == updateTexBoth || inputMessage.obj == updateTex1, inputMessage.obj == updateTexBoth || inputMessage.obj == updateTex2); break; case DO_SHUTDOWN_MESSAGE: finish(); @@ -1123,7 +1615,10 @@ public void handleMessage(Message inputMessage) { looper.quit(); } break; + case DO_DUAL_START: case DO_REINIT_MESSAGE: { + final int i = what == DO_REINIT_MESSAGE ? 0 : 1; + if (!egl10.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) { if (BuildVars.LOGS_ENABLED) { FileLog.d("CameraView " + "eglMakeCurrent failed " + GLUtils.getEGLErrorString(egl10.eglGetError())); @@ -1131,39 +1626,66 @@ public void handleMessage(Message inputMessage) { return; } - if (cameraSurface != null) { - cameraSurface.getTransformMatrix(moldSTMatrix); - cameraSurface.setOnFrameAvailableListener(null); - cameraSurface.release(); + if (cameraSurface[i] != null) { + cameraSurface[i].getTransformMatrix(moldSTMatrix[i]); + cameraSurface[i].setOnFrameAvailableListener(null); + cameraSurface[i].release(); + cameraSurface[i] = null; + } + + if (cameraTexture[i][0] == 0) { + GLES20.glGenTextures(1, cameraTexture[i], 0); } - cameraId = (Integer) inputMessage.obj; + cameraId[i] = inputMessage.arg1; - GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, cameraTexture[0]); + GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, cameraTexture[i][0]); GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); - cameraSurface = new SurfaceTexture(cameraTexture[0]); - cameraSurface.setOnFrameAvailableListener(surfaceTexture -> requestRender()); - createCamera(cameraSurface); + if (i == 1) { + applyDualMatrix((Matrix) inputMessage.obj); + } + + cameraSurface[i] = new SurfaceTexture(cameraTexture[i][0]); + cameraSurface[i].setOnFrameAvailableListener(this::updTex); + if (ignoreCamera1Upd) { + camera1Appeared = false; + camera1AppearedUntil = System.currentTimeMillis() + 60L; + ignoreCamera1Upd = false; + } + createCamera(cameraSurface[i], i); + + if (i == 1) { + dualAppeared = false; + synchronized (layoutLock) { + dualCameraAppeared = false; + firstFrame2Rendered = false; + } + dualAppear.set(0f, true); + } break; } case DO_SETSESSION_MESSAGE: { - if (BuildVars.LOGS_ENABLED) { - FileLog.d("CameraView " + "set gl renderer session"); - } + final int i = inputMessage.arg1; CameraSession newSession = (CameraSession) inputMessage.obj; - if (currentSession != newSession) { - currentSession = newSession; - cameraId = newSession.cameraInfo.cameraId; + if (newSession == null) { + return; + } + if (currentSession[i] != newSession) { + currentSession[i] = newSession; + cameraId[i] = newSession.cameraInfo.cameraId; } - currentSession.updateRotation(); - int rotationAngle = currentSession.getWorldAngle(); - android.opengl.Matrix.setIdentityM(mMVPMatrix, 0); +// currentSession[i].updateRotation(); + int rotationAngle = currentSession[i].getWorldAngle(); + if (BuildVars.LOGS_ENABLED) { + FileLog.d("CameraView " + "set gl renderer session " + i + " angle=" + rotationAngle); + } + android.opengl.Matrix.setIdentityM(mMVPMatrix[i], 0); if (rotationAngle != 0) { - android.opengl.Matrix.rotateM(mMVPMatrix, 0, rotationAngle, 0, 0, 1); + android.opengl.Matrix.rotateM(mMVPMatrix[i], 0, rotationAngle, 0, 0, 1); } break; } @@ -1185,9 +1707,127 @@ public void handleMessage(Message inputMessage) { recording = false; break; } + case DO_DUAL_END: { + if (!egl10.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) { + if (BuildVars.LOGS_ENABLED) { + FileLog.d("CameraView " + "eglMakeCurrent failed " + GLUtils.getEGLErrorString(egl10.eglGetError())); + } + return; + } + if (cameraSurface[1] != null) { + cameraSurface[1].getTransformMatrix(moldSTMatrix[1]); + cameraSurface[1].setOnFrameAvailableListener(null); + cameraSurface[1].release(); + cameraSurface[1] = null; + } + if (cameraTexture[1][0] != 0) { + GLES20.glDeleteTextures(1, cameraTexture[1], 0); + cameraTexture[1][0] = 0; + } + currentSession[1] = null; + cameraId[1] = -1; + requestRender(false, false); + break; + } + case DO_DUAL_MOVE: { + applyDualMatrix((Matrix) inputMessage.obj); + requestRender(false, false); + break; + } + case DO_DUAL_TOGGLE_SHAPE: { + shapeTo++; + lastShapeTo = shapeTo; + requestRender(false, false); + break; + } + case DO_DUAL_FLIP: { + int cameraId0 = cameraId[0]; + cameraId[0] = cameraId[1]; + cameraId[1] = cameraId0; + + CameraSession cameraSession0 = currentSession[0]; + currentSession[0] = currentSession[1]; + currentSession[1] = cameraSession0; + + int[] cameraTexture0 = cameraTexture[0]; + cameraTexture[0] = cameraTexture[1]; + cameraTexture[1] = cameraTexture0; + + SurfaceTexture cameraSurface0 = cameraSurface[0]; + cameraSurface[0] = cameraSurface[1]; + cameraSurface[1] = cameraSurface0; + + float[] mMVPMatrix0 = mMVPMatrix[0]; + mMVPMatrix[0] = mMVPMatrix[1]; + mMVPMatrix[1] = mMVPMatrix0; + + float[] mSTMatrix0 = mSTMatrix[0]; + mSTMatrix[0] = mSTMatrix[1]; + mSTMatrix[1] = mSTMatrix0; + + float[] moldSTMatrix0 = moldSTMatrix[0]; + moldSTMatrix[0] = moldSTMatrix[1]; + moldSTMatrix[1] = moldSTMatrix0; + + crossfading = true; + lastCrossfadeValue = 1f; + crossfade.set(1f, true); + + requestRender(true, true); + break; + } + case BLUR_CAMERA1: { + camera1Appeared = false; + ignoreCamera1Upd = true; + camera1AppearedUntil = System.currentTimeMillis() + 60L; + requestRender(false, false); + break; + } } } +// private final float[] tempVertices = new float[6]; + private void applyDualMatrix(Matrix matrix) { +// tempVertices[0] = tempVertices[1] = 0; +// tempVertices[2] = pixelW; +// tempVertices[3] = 0; +// tempVertices[4] = 0; +// tempVertices[5] = pixelH; +// matrix.mapPoints(tempVertices); +// pixelDualW = MathUtils.distance(tempVertices[0], tempVertices[1], tempVertices[2], tempVertices[3]); +// pixelDualH = MathUtils.distance(tempVertices[0], tempVertices[1], tempVertices[4], tempVertices[5]); + getValues(matrix, cameraMatrix[1]); + } + + private float[] m3x3; + private void getValues(Matrix matrix3x3, float[] m4x4) { + if (m3x3 == null) { + m3x3 = new float[9]; + } + matrix3x3.getValues(m3x3); + + m4x4[0] = m3x3[0]; + m4x4[1] = m3x3[3]; + m4x4[2] = 0; + m4x4[3] = m3x3[6]; + + m4x4[4] = m3x3[1]; + m4x4[5] = m3x3[4]; + m4x4[6] = 0; + m4x4[7] = m3x3[7]; + + m4x4[8] = 0; + m4x4[9] = 0; + m4x4[10] = 1; + m4x4[11] = 0; + + m4x4[12] = m3x3[2]; + m4x4[13] = m3x3[5]; + m4x4[14] = 0; + m4x4[15] = m3x3[8]; + } + + public void shutdown(int send) { Handler handler = getHandler(); if (handler != null) { @@ -1195,10 +1835,35 @@ public void shutdown(int send) { } } - public void requestRender() { + private long pausedTime; + public void pause(long duration) { + pausedTime = System.currentTimeMillis() + duration; + } + + private final Object updateTex1 = new Object(); + private final Object updateTex2 = new Object(); + private final Object updateTexBoth = new Object(); + public void requestRender(boolean updateTexImage1, boolean updateTexImage2) { + if (pausedTime > 0 && System.currentTimeMillis() < pausedTime) { + return; + } + if (!updateTexImage1 && !updateTexImage2 && recording) { + // todo: currently video timestamps are messed up in that case + return; + } Handler handler = getHandler(); if (handler != null) { - sendMessage(handler.obtainMessage(DO_RENDER_MESSAGE, cameraId), 0); + if ((updateTexImage1 || updateTexImage2) && handler.hasMessages(DO_RENDER_MESSAGE, updateTexBoth)) { + return; + } + if (!updateTexImage1 && handler.hasMessages(DO_RENDER_MESSAGE, updateTex1)) { + updateTexImage1 = true; + } + if (!updateTexImage2 && handler.hasMessages(DO_RENDER_MESSAGE, updateTex2)) { + updateTexImage2 = true; + } + handler.removeMessages(DO_RENDER_MESSAGE); + sendMessage(handler.obtainMessage(DO_RENDER_MESSAGE, cameraId[0], cameraId[1], updateTexImage1 && updateTexImage2 ? updateTexBoth : (updateTexImage1 ? updateTex1 : updateTex2)), 0); } } @@ -1219,18 +1884,27 @@ public void stopRecording() { } } - private void onFirstFrameRendered() { - if (blurredStubView.getVisibility() == View.VISIBLE) { - blurredStubView.animate().alpha(0).setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); - blurredStubView.setVisibility(View.GONE); - } - }).start(); + private void onFirstFrameRendered(int i) { + if (i == 0) { + flipping = false; + if (blurredStubView.getVisibility() == View.VISIBLE) { + blurredStubView.animate().alpha(0).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + blurredStubView.setVisibility(View.GONE); + } + }).setDuration(120).start(); + } + } else { + onDualCameraSuccess(); } } + protected void onDualCameraSuccess() { + + } + private int loadShader(int type, String shaderCode) { int shader = GLES20.glCreateShader(type); GLES20.glShaderSource(shader, shaderCode); @@ -1247,50 +1921,60 @@ private int loadShader(int type, String shaderCode) { return shader; } - private void createCamera(final SurfaceTexture surfaceTexture) { + private void createCamera(final SurfaceTexture surfaceTexture, int i) { AndroidUtilities.runOnUIThread(() -> { CameraGLThread cameraThread = this.cameraThread; if (cameraThread == null) { return; } if (BuildVars.LOGS_ENABLED) { - FileLog.d("CameraView " + "create camera session"); + FileLog.d("CameraView " + "create camera session " + i); } - if (previewSize == null) { - updateCameraInfoSize(); + if (previewSize[i] == null) { + updateCameraInfoSize(i); } - if (previewSize == null) { + if (previewSize[i] == null) { return; } - surfaceTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight()); + surfaceTexture.setDefaultBufferSize(previewSize[i].getWidth(), previewSize[i].getHeight()); - cameraSession = new CameraSession(info, previewSize, pictureSize, ImageFormat.JPEG, false); - cameraThread.setCurrentSession(cameraSession); + cameraSession[i] = new CameraSession(info[i], previewSize[i], pictureSize[i], ImageFormat.JPEG, false); + cameraSession[i].setCurrentFlashMode(Camera.Parameters.FLASH_MODE_OFF); + cameraThread.setCurrentSession(cameraSession[i], i); requestLayout(); - CameraController.getInstance().open(cameraSession, surfaceTexture, () -> { - if (cameraSession != null) { + CameraController.getInstance().open(cameraSession[i], surfaceTexture, () -> { + if (cameraSession[i] != null) { if (BuildVars.LOGS_ENABLED) { - FileLog.d("CameraView " + "camera initied"); + FileLog.d("CameraView " + "camera initied " + i); } - cameraSession.setInitied(); + cameraSession[i].setInitied(); requestLayout(); } - }, () -> cameraThread.setCurrentSession(cameraSession)); + + if (dual && i == 1 && initFirstCameraAfterSecond) { + initFirstCameraAfterSecond = false; + AndroidUtilities.runOnUIThread(() -> { + updateCameraInfoSize(0); + cameraThread.reinitForNewCamera(); + addToDualWait(350L); + }); + } + }, () -> cameraThread.setCurrentSession(cameraSession[i], i)); }); } private class VideoRecorder implements Runnable { - private static final String VIDEO_MIME_TYPE = "video/avc"; + private static final String VIDEO_MIME_TYPE = "video/hevc"; private static final String AUDIO_MIME_TYPE = "audio/mp4a-latm"; private static final int FRAME_RATE = 30; private static final int IFRAME_INTERVAL = 1; private File videoFile; - private int videoWidth; - private int videoHeight; + private File fileToWrite; + private boolean writingToDifferentFile; private int videoBitrate; private boolean videoConvertFirstWrite = true; private boolean blendEnabled; @@ -1339,8 +2023,18 @@ private class VideoRecorder implements Runnable { private int drawProgram; private int vertexMatrixHandle; private int textureMatrixHandle; + private int cameraMatrixHandle; + private int oppositeCameraMatrixHandle; private int positionHandle; private int textureHandle; + private int roundRadiusHandle; + private int pixelHandle; + private int dualHandle; + private int crossfadeHandle; + private int shapeFromHandle, shapeToHandle, shapeHandle; + private int alphaHandle; + private int scaleHandle; + private int blurHandle; private int zeroTimeStamps; private Integer lastCameraId = 0; @@ -1349,8 +2043,8 @@ private class VideoRecorder implements Runnable { private ArrayBlockingQueue buffers = new ArrayBlockingQueue<>(10); private ArrayList keyframeThumbs = new ArrayList<>(); - private DispatchQueue generateKeyframeThumbsQueue; - private int frameCount; + + DispatchQueue fileWriteQueue; private Runnable recorderRunnable = new Runnable() { @@ -1424,6 +2118,7 @@ public void run() { handler.sendMessage(handler.obtainMessage(MSG_STOP_RECORDING, sendWhenDone, 0)); } }; + private String outputMimeType; public void startRecording(File outputFile, android.opengl.EGLContext sharedContext) { String model = Build.DEVICE; @@ -1433,7 +2128,7 @@ public void startRecording(File outputFile, android.opengl.EGLContext sharedCont Size pictureSize; int bitrate; - pictureSize = previewSize; + pictureSize = previewSize[0]; if (Math.min(pictureSize.mHeight, pictureSize.mWidth) >= 720) { bitrate = 3500000; } else { @@ -1442,7 +2137,7 @@ public void startRecording(File outputFile, android.opengl.EGLContext sharedCont videoFile = outputFile; - if (cameraSession.getWorldAngle() == 90 || cameraSession.getWorldAngle() == 270) { + if (cameraSession[0].getWorldAngle() == 90 || cameraSession[0].getWorldAngle() == 270) { videoWidth = pictureSize.getWidth(); videoHeight = pictureSize.getHeight(); } else { @@ -1451,7 +2146,6 @@ public void startRecording(File outputFile, android.opengl.EGLContext sharedCont } videoBitrate = bitrate; sharedEglContext = sharedContext; - synchronized (sync) { if (running) { return; @@ -1468,13 +2162,10 @@ public void startRecording(File outputFile, android.opengl.EGLContext sharedCont } } } + fileWriteQueue = new DispatchQueue("VR_FileWriteQueue"); + fileWriteQueue.setPriority(Thread.MAX_PRIORITY); + keyframeThumbs.clear(); - frameCount = 0; - if (generateKeyframeThumbsQueue != null) { - generateKeyframeThumbsQueue.cleanupQueue(); - generateKeyframeThumbsQueue.recycle(); - } - generateKeyframeThumbsQueue = new DispatchQueue("keyframes_thumb_queque"); handler.sendMessage(handler.obtainMessage(MSG_START_RECORDING)); } @@ -1649,6 +2340,7 @@ private void handleVideoFrameAvailable(long timestampNanos, Integer cameraId) { FileLog.e(e); } long dt; + long currentTime = System.currentTimeMillis(); if (!lastCameraId.equals(cameraId)) { lastTimestamp = -1; lastCameraId = cameraId; @@ -1656,7 +2348,7 @@ private void handleVideoFrameAvailable(long timestampNanos, Integer cameraId) { if (lastTimestamp == -1) { lastTimestamp = timestampNanos; if (currentTimestamp != 0) { - dt = (System.currentTimeMillis() - lastCommitedFrameTime) * 1000000; + dt = (currentTime - lastCommitedFrameTime) * 1000000; } else { dt = 0; } @@ -1664,7 +2356,7 @@ private void handleVideoFrameAvailable(long timestampNanos, Integer cameraId) { dt = (timestampNanos - lastTimestamp); lastTimestamp = timestampNanos; } - lastCommitedFrameTime = System.currentTimeMillis(); + lastCommitedFrameTime = currentTime; if (!skippedFirst) { skippedTime += dt; if (skippedTime < 200000000) { @@ -1681,32 +2373,98 @@ private void handleVideoFrameAvailable(long timestampNanos, Integer cameraId) { } videoLast = timestampNanos; + if (cameraTexture[1][0] != 0 && !blendEnabled) { + GLES20.glEnable(GLES20.GL_BLEND); + blendEnabled = true; + } + final boolean isDual = dual; + if (isDual) { + GLES20.glClearColor(0.f, 0.f, 0.f, 1.f); + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); + } + final float crossfade = lastCrossfadeValue; + final boolean crossfading = crossfade > 0; + for (int a = -1; a < 2; ++a) { + if (a == -1 && !crossfading) { + continue; + } + final int i = a < 0 ? 1 : a; + if (cameraTexture[i][0] == 0) { + continue; + } + + GLES20.glUseProgram(drawProgram); + GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 12, vertexBuffer); + GLES20.glEnableVertexAttribArray(positionHandle); + GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 8, textureBuffer); + GLES20.glEnableVertexAttribArray(textureHandle); + GLES20.glUniformMatrix4fv(vertexMatrixHandle, 1, false, mMVPMatrix[i], 0); + + GLES20.glUniformMatrix4fv(cameraMatrixHandle, 1, false, cameraMatrix[i], 0); + GLES20.glUniformMatrix4fv(oppositeCameraMatrixHandle, 1, false, cameraMatrix[1 - i], 0); - GLES20.glUseProgram(drawProgram); - GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 12, vertexBuffer); - GLES20.glEnableVertexAttribArray(positionHandle); - GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 8, textureBuffer); - GLES20.glEnableVertexAttribArray(textureHandle); - GLES20.glUniformMatrix4fv(vertexMatrixHandle, 1, false, mMVPMatrix, 0); + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glUniformMatrix4fv(textureMatrixHandle, 1, false, mSTMatrix[i], 0); - GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - if (oldCameraTexture[0] != 0) { - if (!blendEnabled) { - GLES20.glEnable(GLES20.GL_BLEND); - blendEnabled = true; + GLES20.glUniform1f(blurHandle, 0); + if (i == 0) { + GLES20.glUniform2f(pixelHandle, pixelW, pixelH); + GLES20.glUniform1f(dualHandle, isDual ? 1f : 0f); + } else { + GLES20.glUniform2f(pixelHandle, pixelDualW, pixelDualH); + GLES20.glUniform1f(dualHandle, 1f); } - GLES20.glUniformMatrix4fv(textureMatrixHandle, 1, false, moldSTMatrix, 0); - GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, oldCameraTexture[0]); + if (i == 1) { + GLES20.glUniform1f(alphaHandle, 1); + if (a < 0) { + GLES20.glUniform1f(roundRadiusHandle, 0); + GLES20.glUniform1f(scaleHandle, 1); + GLES20.glUniform1f(shapeFromHandle, 2); + GLES20.glUniform1f(shapeToHandle, 2); + GLES20.glUniform1f(shapeHandle, 0); + GLES20.glUniform1f(crossfadeHandle, 1); + } else if (!crossfading) { + GLES20.glUniform1f(roundRadiusHandle, AndroidUtilities.dp(16)); + GLES20.glUniform1f(scaleHandle, 1f); + GLES20.glUniform1f(shapeFromHandle, (float) Math.floor(shapeValue)); + GLES20.glUniform1f(shapeToHandle, (float) Math.ceil(shapeValue)); + GLES20.glUniform1f(shapeHandle, shapeValue - (float) Math.floor(shapeValue)); + GLES20.glUniform1f(crossfadeHandle, 0); + } else { + GLES20.glUniform1f(roundRadiusHandle, AndroidUtilities.dp(16)); + GLES20.glUniform1f(scaleHandle, 1f - crossfade); + GLES20.glUniform1f(shapeFromHandle, (float) Math.floor(shapeValue)); + GLES20.glUniform1f(shapeToHandle, (float) Math.ceil(shapeValue)); + GLES20.glUniform1f(shapeHandle, shapeValue - (float) Math.floor(shapeValue)); + GLES20.glUniform1f(shapeHandle, crossfade); + GLES20.glUniform1f(crossfadeHandle, 0); + } + } else { + GLES20.glUniform1f(alphaHandle, 1f); + if (crossfading) { + GLES20.glUniform1f(roundRadiusHandle, AndroidUtilities.lerp(AndroidUtilities.dp(12), AndroidUtilities.dp(16), crossfade)); + GLES20.glUniform1f(scaleHandle, 1f); + GLES20.glUniform1f(shapeFromHandle, lastShapeTo); + GLES20.glUniform1f(shapeToHandle, 2); + GLES20.glUniform1f(shapeHandle, Utilities.clamp((1f - crossfade), 1, 0)); + GLES20.glUniform1f(crossfadeHandle, crossfade); + } else { + GLES20.glUniform1f(roundRadiusHandle, 0); + GLES20.glUniform1f(scaleHandle, 1f); + GLES20.glUniform1f(shapeFromHandle, 2); + GLES20.glUniform1f(shapeToHandle, 2); + GLES20.glUniform1f(shapeHandle, 0); + GLES20.glUniform1f(crossfadeHandle, 0f); + } + } + GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, cameraTexture[i][0]); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); - } - GLES20.glUniformMatrix4fv(textureMatrixHandle, 1, false, mSTMatrix, 0); - GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, cameraTexture[0]); - GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); - GLES20.glDisableVertexAttribArray(positionHandle); - GLES20.glDisableVertexAttribArray(textureHandle); - GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0); - GLES20.glUseProgram(0); + GLES20.glDisableVertexAttribArray(positionHandle); + GLES20.glDisableVertexAttribArray(textureHandle); + GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0); + GLES20.glUseProgram(0); + } EGLExt.eglPresentationTimeANDROID(eglDisplay, eglSurface, currentTimestamp); EGL14.eglSwapBuffers(eglDisplay, eglSurface); @@ -1741,11 +2499,31 @@ private void handleStopRecording(final int send) { FileLog.e(e); } } - if (mediaMuxer != null) { + CountDownLatch countDownLatch = new CountDownLatch(1); + fileWriteQueue.postRunnable(() -> { try { mediaMuxer.finishMovie(); } catch (Exception e) { - FileLog.e(e); + e.printStackTrace(); + } + countDownLatch.countDown(); + }); + try { + countDownLatch.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + if (writingToDifferentFile) { + if (!fileToWrite.renameTo(videoFile)) { + FileLog.e("unable to rename file, try move file"); + try { + AndroidUtilities.copyFile(fileToWrite, videoFile); + fileToWrite.delete(); + } catch (IOException e) { + FileLog.e(e); + FileLog.e("unable to move file"); + } } } @@ -1767,7 +2545,12 @@ private void handleStopRecording(final int send) { handler.exit(); AndroidUtilities.runOnUIThread(() -> { - cameraSession.stopVideoRecording(); + if (cameraSession[0] != null) { + cameraSession[0].stopVideoRecording(); + } + if (cameraSession[1] != null) { + cameraSession[1].stopVideoRecording(); + } onRecordingFinishRunnable.run(); }); } @@ -1808,10 +2591,34 @@ private void prepareEncoder() { audioEncoder.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); audioEncoder.start(); - videoEncoder = MediaCodec.createEncoderByType(VIDEO_MIME_TYPE); + boolean shouldUseHevc = isStory; + outputMimeType = shouldUseHevc ? "video/hevc" : "video/avc"; + try { + if (shouldUseHevc) { + String encoderName = SharedConfig.findGoodHevcEncoder(); + if (encoderName != null) { + videoEncoder = MediaCodec.createByCodecName(encoderName); + } + } else { + outputMimeType = "video/avc"; + videoEncoder = MediaCodec.createEncoderByType(outputMimeType); + } + if (outputMimeType.equals("video/hevc") && videoEncoder != null && !videoEncoder.getCodecInfo().isHardwareAccelerated()) { + FileLog.e("hevc encoder isn't hardware accelerated"); + videoEncoder.release(); + videoEncoder = null; + } + } catch (Throwable e) { + FileLog.e("can't get hevc encoder"); + FileLog.e(e); + } + if (videoEncoder == null && outputMimeType.equals("video/hevc")) { + outputMimeType = "video/avc"; + videoEncoder = MediaCodec.createEncoderByType(outputMimeType); + } firstEncode = true; - MediaFormat format = MediaFormat.createVideoFormat(VIDEO_MIME_TYPE, videoWidth, videoHeight); + MediaFormat format = MediaFormat.createVideoFormat(outputMimeType, videoWidth, videoHeight); format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); format.setInteger(MediaFormat.KEY_BIT_RATE, videoBitrate); @@ -1822,11 +2629,27 @@ private void prepareEncoder() { surface = videoEncoder.createInputSurface(); videoEncoder.start(); + boolean isSdCard = ImageLoader.isSdCardPath(videoFile); + fileToWrite = videoFile; + if (isSdCard) { + try { + fileToWrite = new File(ApplicationLoader.getFilesDirFixed(), "camera_tmp.mp4"); + if (fileToWrite.exists()) { + fileToWrite.delete(); + } + writingToDifferentFile = true; + } catch (Throwable e) { + FileLog.e(e); + fileToWrite = videoFile; + writingToDifferentFile = false; + } + } + Mp4Movie movie = new Mp4Movie(); - movie.setCacheFile(videoFile); + movie.setCacheFile(fileToWrite); movie.setRotation(0); movie.setSize(videoWidth, videoHeight); - mediaMuxer = new MP4Builder().createMovie(movie, false); + mediaMuxer = new MP4Builder().createMovie(movie, false, false); } catch (Exception ioe) { throw new RuntimeException(ioe); @@ -1908,8 +2731,8 @@ private void prepareEncoder() { textureBuffer.put(texData).position(0); - int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER); - int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SCREEN_SHADER); + int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, RLottieDrawable.readRes(null, R.raw.camera_vert)); + int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, RLottieDrawable.readRes(null, R.raw.camera_frag)); if (vertexShader != 0 && fragmentShader != 0) { drawProgram = GLES20.glCreateProgram(); GLES20.glAttachShader(drawProgram, vertexShader); @@ -1925,6 +2748,19 @@ private void prepareEncoder() { textureHandle = GLES20.glGetAttribLocation(drawProgram, "aTextureCoord"); vertexMatrixHandle = GLES20.glGetUniformLocation(drawProgram, "uMVPMatrix"); textureMatrixHandle = GLES20.glGetUniformLocation(drawProgram, "uSTMatrix"); + cameraMatrixHandle = GLES20.glGetUniformLocation(drawProgram, "cameraMatrix"); + oppositeCameraMatrixHandle = GLES20.glGetUniformLocation(drawProgram, "oppositeCameraMatrix"); + + roundRadiusHandle = GLES20.glGetUniformLocation(drawProgram, "roundRadius"); + pixelHandle = GLES20.glGetUniformLocation(drawProgram, "pixelWH"); + dualHandle = GLES20.glGetUniformLocation(drawProgram, "dual"); + scaleHandle = GLES20.glGetUniformLocation(drawProgram, "scale"); + blurHandle = GLES20.glGetUniformLocation(drawProgram, "blur"); + alphaHandle = GLES20.glGetUniformLocation(drawProgram, "alpha"); + crossfadeHandle = GLES20.glGetUniformLocation(drawProgram, "crossfade"); + shapeFromHandle = GLES20.glGetUniformLocation(drawProgram, "shapeFrom"); + shapeToHandle = GLES20.glGetUniformLocation(drawProgram, "shapeTo"); + shapeHandle = GLES20.glGetUniformLocation(drawProgram, "shapeT"); } } } @@ -1959,7 +2795,7 @@ public void drainEncoder(boolean endOfStream) throws Exception { if (newFormat.containsKey(MediaFormat.KEY_PREPEND_HEADER_TO_SYNC_FRAMES) && newFormat.getInteger(MediaFormat.KEY_PREPEND_HEADER_TO_SYNC_FRAMES) == 1) { ByteBuffer spsBuff = newFormat.getByteBuffer("csd-0"); ByteBuffer ppsBuff = newFormat.getByteBuffer("csd-1"); - prependHeaderSize = spsBuff.limit() + ppsBuff.limit(); + prependHeaderSize = (spsBuff == null ? 0 : spsBuff.limit()) + (ppsBuff == null ? 0 : ppsBuff.limit()); } } } else if (encoderStatus >= 0) { @@ -1979,26 +2815,26 @@ public void drainEncoder(boolean endOfStream) throws Exception { videoBufferInfo.size -= prependHeaderSize; } if (firstEncode && (videoBufferInfo.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0) { - if (videoBufferInfo.size > 100) { - encodedData.position(videoBufferInfo.offset); - byte[] temp = new byte[100]; - encodedData.get(temp); - int nalCount = 0; - for (int a = 0; a < temp.length - 4; a++) { - if (temp[a] == 0 && temp[a + 1] == 0 && temp[a + 2] == 0 && temp[a + 3] == 1) { - nalCount++; - if (nalCount > 1) { - videoBufferInfo.offset += a; - videoBufferInfo.size -= a; - break; - } - } - } - } + MediaCodecVideoConvertor.cutOfNalData(outputMimeType, encodedData, videoBufferInfo); firstEncode = false; } - long availableSize = mediaMuxer.writeSampleData(videoTrackIndex, encodedData, videoBufferInfo, true); + MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); + bufferInfo.size = videoBufferInfo.size; + bufferInfo.offset = videoBufferInfo.offset; + bufferInfo.flags = videoBufferInfo.flags; + bufferInfo.presentationTimeUs = videoBufferInfo.presentationTimeUs; + ByteBuffer byteBuffer = AndroidUtilities.cloneByteBuffer(encodedData); + fileWriteQueue.postRunnable(() -> { + try { + mediaMuxer.writeSampleData(videoTrackIndex, byteBuffer, bufferInfo, true); + } catch (Exception e) { + FileLog.e(e); + } + }); } else if (videoTrackIndex == -5) { + if (outputMimeType.equals("video/hevc")) { + throw new RuntimeException("need fix parsing csd data"); + } byte[] csd = new byte[videoBufferInfo.size]; encodedData.limit(videoBufferInfo.offset + videoBufferInfo.size); encodedData.position(videoBufferInfo.offset); @@ -2067,7 +2903,19 @@ public void drainEncoder(boolean endOfStream) throws Exception { audioBufferInfo.size = 0; } if (audioBufferInfo.size != 0) { - mediaMuxer.writeSampleData(audioTrackIndex, encodedData, audioBufferInfo, false); + MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); + bufferInfo.size = audioBufferInfo.size; + bufferInfo.offset = audioBufferInfo.offset; + bufferInfo.flags = audioBufferInfo.flags; + bufferInfo.presentationTimeUs = audioBufferInfo.presentationTimeUs; + ByteBuffer byteBuffer = AndroidUtilities.cloneByteBuffer(encodedData); + fileWriteQueue.postRunnable(() -> { + try { + mediaMuxer.writeSampleData(audioTrackIndex, byteBuffer, bufferInfo, false); + } catch (Exception e) { + FileLog.e(e); + } + }); } audioEncoder.releaseOutputBuffer(encoderStatus, false); if ((audioBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { @@ -2079,6 +2927,10 @@ public void drainEncoder(boolean endOfStream) throws Exception { @Override protected void finalize() throws Throwable { + if (fileWriteQueue != null) { + fileWriteQueue.recycle(); + fileWriteQueue = null; + } try { if (eglDisplay != EGL14.EGL_NO_DISPLAY) { EGL14.eglMakeCurrent(eglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT); @@ -2159,4 +3011,10 @@ public void exit() { public void setFpsLimit(int fpsLimit) { this.fpsLimit = fpsLimit; } + + public void pauseAsTakingPicture() { + if (cameraThread != null) { + cameraThread.pause(600); + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ringtone/RingtoneDataStore.java b/TMessagesProj/src/main/java/org/telegram/messenger/ringtone/RingtoneDataStore.java index 60c195abc9..ce8fb81e5b 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ringtone/RingtoneDataStore.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ringtone/RingtoneDataStore.java @@ -50,12 +50,12 @@ public RingtoneDataStore(int currentAccount) { FileLog.e(e); } AndroidUtilities.runOnUIThread(() -> { - loadUserRingtones(); + loadUserRingtones(false); }); } - public void loadUserRingtones() { - boolean needReload = System.currentTimeMillis() - lastReloadTimeMs > reloadTimeoutMs; + public void loadUserRingtones(boolean force) { + boolean needReload = force || System.currentTimeMillis() - lastReloadTimeMs > reloadTimeoutMs; TLRPC.TL_account_getSavedRingtones req = new TLRPC.TL_account_getSavedRingtones(); req.hash = queryHash; if (needReload) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/utils/BillingUtilities.java b/TMessagesProj/src/main/java/org/telegram/messenger/utils/BillingUtilities.java new file mode 100644 index 0000000000..7e07c9a5a1 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/utils/BillingUtilities.java @@ -0,0 +1,108 @@ +package org.telegram.messenger.utils; + +import android.content.Context; +import android.util.Base64; + +import androidx.core.util.Pair; + +import com.android.billingclient.api.AccountIdentifiers; +import com.android.billingclient.api.Purchase; +import com.google.android.exoplayer2.util.Util; +import com.google.common.base.Charsets; + +import org.json.JSONObject; +import org.telegram.messenger.AccountInstance; +import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.UserConfig; +import org.telegram.tgnet.SerializedData; +import org.telegram.tgnet.TLRPC; + +import java.io.InputStream; +import java.util.Iterator; +import java.util.Map; + +public class BillingUtilities { + private static final String CURRENCY_FILE = "currencies.json"; + private static final String CURRENCY_EXP = "exp"; + + @SuppressWarnings("ConstantConditions") + public static void extractCurrencyExp(Map currencyExpMap) { + if (!currencyExpMap.isEmpty()) { + return; + } + try { + Context ctx = ApplicationLoader.applicationContext; + InputStream in = ctx.getAssets().open(CURRENCY_FILE); + JSONObject obj = new JSONObject(new String(Util.toByteArray(in), Charsets.UTF_8)); + Iterator it = obj.keys(); + while (it.hasNext()) { + String key = it.next(); + JSONObject currency = obj.optJSONObject(key); + currencyExpMap.put(key, currency.optInt(CURRENCY_EXP)); + } + in.close(); + } catch (Exception e) { + FileLog.e(e); + } + } + + public static Pair createDeveloperPayload(TLRPC.InputStorePaymentPurpose paymentPurpose, AccountInstance accountInstance) { + long currentAccountId = accountInstance.getUserConfig().getClientUserId(); + byte[] currentAccountIdBytes = String.valueOf(currentAccountId).getBytes(Charsets.UTF_8); + String obfuscatedAccountId = Base64.encodeToString(currentAccountIdBytes, Base64.DEFAULT); + + SerializedData serializedData = new SerializedData(paymentPurpose.getObjectSize()); + paymentPurpose.serializeToStream(serializedData); + String obfuscatedData = Base64.encodeToString(serializedData.toByteArray(), Base64.DEFAULT); + serializedData.cleanup(); + + return Pair.create(obfuscatedAccountId, obfuscatedData); + } + + private static AccountInstance findAccountById(long accountId) { + AccountInstance result = null; + for (int i = 0; i < UserConfig.MAX_ACCOUNT_COUNT; i++) { + AccountInstance acc = AccountInstance.getInstance(i); + if (acc.getUserConfig().getClientUserId() == accountId) { + result = acc; + break; + } + } + return result; + } + + public static Pair extractDeveloperPayload(Purchase purchase) { + AccountIdentifiers identifiers = purchase.getAccountIdentifiers(); + if (identifiers == null) { + FileLog.d("Billing: Extract payload. No AccountIdentifiers"); + return null; + } + String obfuscatedAccountId = identifiers.getObfuscatedAccountId(); + String obfuscatedData = identifiers.getObfuscatedProfileId(); + if (obfuscatedAccountId == null || obfuscatedAccountId.isEmpty() || obfuscatedData == null || obfuscatedData.isEmpty()) { + FileLog.d("Billing: Extract payload. Empty AccountIdentifiers"); + return null; + } + + try { + byte[] obfuscatedDataBytes = Base64.decode(obfuscatedData, Base64.DEFAULT); + SerializedData data = new SerializedData(obfuscatedDataBytes); + TLRPC.InputStorePaymentPurpose purpose = TLRPC.InputStorePaymentPurpose.TLdeserialize(data, data.readInt32(true), true); + data.cleanup(); + + byte[] obfuscatedAccountIdBytes = Base64.decode(obfuscatedAccountId, Base64.DEFAULT); + long accountId = Long.parseLong(new String(obfuscatedAccountIdBytes, Charsets.UTF_8)); + + AccountInstance acc = findAccountById(accountId); + if (acc == null) { + FileLog.d("Billing: Extract payload. AccountInstance not found"); + return null; + } + return Pair.create(acc, purpose); + } catch (Exception e) { + FileLog.e("Billing: Extract Payload", e); + return null; + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/utils/PhotoUtilities.java b/TMessagesProj/src/main/java/org/telegram/messenger/utils/PhotoUtilities.java index 82e3c54730..08ba9828e8 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/utils/PhotoUtilities.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/utils/PhotoUtilities.java @@ -103,7 +103,7 @@ public static void setImageAsAvatar(MediaController.PhotoEntry entry, BaseFragme args.putLong("user_id", UserConfig.getInstance(currentAccount).clientUserId); layout.getLastFragment().presentFragment(new ProfileActivity(args)); }); - BulletinFactory.of(layout.getLastFragment()).createUsersBulletin(Collections.singletonList(user), title, subtitle).show(); + BulletinFactory.of(layout.getLastFragment()).createUsersBulletin(Collections.singletonList(user), title, subtitle, null).show(); } } } @@ -224,7 +224,7 @@ public static void showAvatartConstructorForUpdateUserPhoto(ChatActivity chatAct args.putLong("user_id", userId); chatActivity.presentFragment(new ProfileActivity(args)); }); - BulletinFactory.of(chatActivity).createUsersBulletin(Collections.singletonList(user), title, subtitle).show(); + BulletinFactory.of(chatActivity).createUsersBulletin(Collections.singletonList(user), title, subtitle, null).show(); } })); } else { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/HevcConfigurationBox.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/HevcConfigurationBox.java new file mode 100644 index 0000000000..3e7a50d9a3 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/HevcConfigurationBox.java @@ -0,0 +1,134 @@ +package org.telegram.messenger.video; + +import com.googlecode.mp4parser.AbstractBox; + + +import java.nio.ByteBuffer; +import java.util.List; + +public class HevcConfigurationBox extends AbstractBox { + public static final String TYPE = "hvcC"; + + + private HevcDecoderConfigurationRecord hevcDecoderConfigurationRecord; + + public HevcConfigurationBox() { + super(TYPE); + hevcDecoderConfigurationRecord = new HevcDecoderConfigurationRecord(); + } + + @Override + protected long getContentSize() { + return hevcDecoderConfigurationRecord.getSize(); + } + + @Override + protected void getContent(ByteBuffer byteBuffer) { + hevcDecoderConfigurationRecord.write(byteBuffer); + } + + @Override + protected void _parseDetails(ByteBuffer content) { + hevcDecoderConfigurationRecord.parse(content); + } + + public HevcDecoderConfigurationRecord getHevcDecoderConfigurationRecord() { + return hevcDecoderConfigurationRecord; + } + + public void setHevcDecoderConfigurationRecord(HevcDecoderConfigurationRecord hevcDecoderConfigurationRecord) { + this.hevcDecoderConfigurationRecord = hevcDecoderConfigurationRecord; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + HevcConfigurationBox that = (HevcConfigurationBox) o; + + if (hevcDecoderConfigurationRecord != null ? !hevcDecoderConfigurationRecord.equals(that.hevcDecoderConfigurationRecord) : that.hevcDecoderConfigurationRecord != null) + return false; + + return true; + } + + @Override + public int hashCode() { + return hevcDecoderConfigurationRecord != null ? hevcDecoderConfigurationRecord.hashCode() : 0; + } + + + public int getConfigurationVersion() { + return hevcDecoderConfigurationRecord.configurationVersion; + } + + public int getGeneral_profile_space() { + return hevcDecoderConfigurationRecord.general_profile_space; + } + + public boolean isGeneral_tier_flag() { + return hevcDecoderConfigurationRecord.general_tier_flag; + } + + + public int getGeneral_profile_idc() { + return hevcDecoderConfigurationRecord.general_profile_idc; + } + + public long getGeneral_profile_compatibility_flags() { + return hevcDecoderConfigurationRecord.general_profile_compatibility_flags; + } + + public long getGeneral_constraint_indicator_flags() { + return hevcDecoderConfigurationRecord.general_constraint_indicator_flags; + } + + public int getGeneral_level_idc() { + return hevcDecoderConfigurationRecord.general_level_idc; + } + + public int getMin_spatial_segmentation_idc() { + return hevcDecoderConfigurationRecord.min_spatial_segmentation_idc; + } + + public int getParallelismType() { + return hevcDecoderConfigurationRecord.parallelismType; + } + + public int getChromaFormat() { + return hevcDecoderConfigurationRecord.chromaFormat; + } + + public int getBitDepthLumaMinus8() { + return hevcDecoderConfigurationRecord.bitDepthLumaMinus8; + } + + public int getBitDepthChromaMinus8() { + return hevcDecoderConfigurationRecord.bitDepthChromaMinus8; + } + + public int getAvgFrameRate() { + return hevcDecoderConfigurationRecord.avgFrameRate; + } + + public int getNumTemporalLayers() { + return hevcDecoderConfigurationRecord.numTemporalLayers; + } + + public int getLengthSizeMinusOne() { + return hevcDecoderConfigurationRecord.lengthSizeMinusOne; + } + + public boolean isTemporalIdNested() { + return hevcDecoderConfigurationRecord.temporalIdNested; + } + + public int getConstantFrameRate() { + return hevcDecoderConfigurationRecord.constantFrameRate; + } + + public List getArrays() { + return hevcDecoderConfigurationRecord.arrays; + } +} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/HevcDecoderConfigurationRecord.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/HevcDecoderConfigurationRecord.java new file mode 100644 index 0000000000..4ad61a2d5f --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/HevcDecoderConfigurationRecord.java @@ -0,0 +1,663 @@ +package org.telegram.messenger.video; + + +import static com.googlecode.mp4parser.authoring.tracks.h265.NalUnitTypes.NAL_TYPE_PPS_NUT; +import static com.googlecode.mp4parser.authoring.tracks.h265.NalUnitTypes.NAL_TYPE_PREFIX_SEI_NUT; +import static com.googlecode.mp4parser.authoring.tracks.h265.NalUnitTypes.NAL_TYPE_SPS_NUT; +import static com.googlecode.mp4parser.authoring.tracks.h265.NalUnitTypes.NAL_TYPE_VPS_NUT; + +import androidx.annotation.NonNull; + +import com.coremedia.iso.IsoTypeReader; +import com.coremedia.iso.IsoTypeWriter; +import com.coremedia.iso.boxes.SampleDescriptionBox; +import com.coremedia.iso.boxes.sampleentry.VisualSampleEntry; +import com.google.android.exoplayer2.util.Log; +import com.googlecode.mp4parser.authoring.tracks.CleanInputStream; +import com.googlecode.mp4parser.authoring.tracks.h265.H265TrackImpl; +import com.googlecode.mp4parser.authoring.tracks.h265.NalUnitHeader; +import com.googlecode.mp4parser.util.ByteBufferByteChannel; + +import org.telegram.messenger.Utilities; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.Channels; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.ListIterator; + +public class HevcDecoderConfigurationRecord { + int configurationVersion; + + int general_profile_space; + boolean general_tier_flag; + int general_profile_idc; + + long general_profile_compatibility_flags; + long general_constraint_indicator_flags; + + int general_level_idc; + + int reserved1 = 0xF; + int min_spatial_segmentation_idc; + + int reserved2 = 0x3F; + int parallelismType; + + int reserved3 = 0x3F; + int chromaFormat; + + int reserved4 = 0x1F; + int bitDepthLumaMinus8; + + int reserved5 = 0x1F; + int bitDepthChromaMinus8; + + int avgFrameRate; + + int constantFrameRate; + int numTemporalLayers; + boolean temporalIdNested; + int lengthSizeMinusOne; + + List arrays = new ArrayList(); + + boolean frame_only_constraint_flag; + boolean non_packed_constraint_flag; + boolean interlaced_source_flag; + boolean progressive_source_flag; + + + public HevcDecoderConfigurationRecord() { + } + + + public void parse(ByteBuffer content) { + configurationVersion = IsoTypeReader.readUInt8(content); + + /* + * unsigned int(2) general_profile_space; + * unsigned int(1) general_tier_flag; + * unsigned int(5) general_profile_idc; + */ + int a = IsoTypeReader.readUInt8(content); + general_profile_space = (a & 0xC0) >> 6; + general_tier_flag = (a & 0x20) > 0; + general_profile_idc = (a & 0x1F); + + /* unsigned int(32) general_profile_compatibility_flags; */ + general_profile_compatibility_flags = IsoTypeReader.readUInt32(content); + + + /* unsigned int(48) general_constraint_indicator_flags; */ + general_constraint_indicator_flags = IsoTypeReader.readUInt48(content); + + frame_only_constraint_flag = ((general_constraint_indicator_flags >> 44) & 0x08) > 0; + non_packed_constraint_flag = ((general_constraint_indicator_flags >> 44) & 0x04) > 0; + interlaced_source_flag = ((general_constraint_indicator_flags >> 44) & 0x02) > 0; + progressive_source_flag = ((general_constraint_indicator_flags >> 44) & 0x01) > 0; + + general_constraint_indicator_flags &= 0x7fffffffffffL; + + /* unsigned int(8) general_level_idc; */ + general_level_idc = IsoTypeReader.readUInt8(content); + + /* + * bit(4) reserved = ‘1111’b; + * unsigned int(12) min_spatial_segmentation_idc; + */ + a = IsoTypeReader.readUInt16(content); + reserved1 = (a & 0xF000) >> 12; + min_spatial_segmentation_idc = a & 0xFFF; + + a = IsoTypeReader.readUInt8(content); + reserved2 = (a & 0xFC) >> 2; + parallelismType = a & 0x3; + + a = IsoTypeReader.readUInt8(content); + reserved3 = (a & 0xFC) >> 2; + chromaFormat = a & 0x3; + + a = IsoTypeReader.readUInt8(content); + reserved4 = (a & 0xF8) >> 3; + bitDepthLumaMinus8 = a & 0x7; + + a = IsoTypeReader.readUInt8(content); + reserved5 = (a & 0xF8) >> 3; + bitDepthChromaMinus8 = a & 0x7; + + avgFrameRate = IsoTypeReader.readUInt16(content); + + a = IsoTypeReader.readUInt8(content); + constantFrameRate = (a & 0xC0) >> 6; + numTemporalLayers = (a & 0x38) >> 3; + temporalIdNested = (a & 0x4) > 0; + lengthSizeMinusOne = a & 0x3; + + + int numOfArrays = IsoTypeReader.readUInt8(content); + this.arrays = new ArrayList(); + for (int i = 0; i < numOfArrays; i++) { + Array array = new Array(); + + a = IsoTypeReader.readUInt8(content); + array.array_completeness = (a & 0x80) > 0; + array.reserved = (a & 0x40) > 0; + array.nal_unit_type = a & 0x3F; + + int numNalus = IsoTypeReader.readUInt16(content); + array.nalUnits = new ArrayList(); + for (int j = 0; j < numNalus; j++) { + int nalUnitLength = IsoTypeReader.readUInt16(content); + byte[] nal = new byte[nalUnitLength]; + content.get(nal); + array.nalUnits.add(nal); + } + arrays.add(array); + } + } + + public void write(ByteBuffer byteBuffer) { + IsoTypeWriter.writeUInt8(byteBuffer, configurationVersion); + + + IsoTypeWriter.writeUInt8(byteBuffer, (general_profile_space << 6) + (general_tier_flag ? 0x20 : 0) + general_profile_idc); + + IsoTypeWriter.writeUInt32(byteBuffer, general_profile_compatibility_flags); + long _general_constraint_indicator_flags = general_constraint_indicator_flags; + if (frame_only_constraint_flag) { + _general_constraint_indicator_flags |= 1l << 47; + } + if (non_packed_constraint_flag) { + _general_constraint_indicator_flags |= 1l << 46; + } + if (interlaced_source_flag) { + _general_constraint_indicator_flags |= 1l << 45; + } + if (progressive_source_flag) { + _general_constraint_indicator_flags |= 1l << 44; + } + + IsoTypeWriter.writeUInt48(byteBuffer, _general_constraint_indicator_flags); + + + IsoTypeWriter.writeUInt8(byteBuffer, general_level_idc); + + IsoTypeWriter.writeUInt16(byteBuffer, (reserved1 << 12) + min_spatial_segmentation_idc); + + IsoTypeWriter.writeUInt8(byteBuffer, (reserved2 << 2) + parallelismType); + + IsoTypeWriter.writeUInt8(byteBuffer, (reserved3 << 2) + chromaFormat); + + IsoTypeWriter.writeUInt8(byteBuffer, (reserved4 << 3) + bitDepthLumaMinus8); + + IsoTypeWriter.writeUInt8(byteBuffer, (reserved5 << 3) + bitDepthChromaMinus8); + + IsoTypeWriter.writeUInt16(byteBuffer, avgFrameRate); + + IsoTypeWriter.writeUInt8(byteBuffer, (constantFrameRate << 6) + (numTemporalLayers << 3) + (temporalIdNested ? 0x4 : 0) + lengthSizeMinusOne); + + IsoTypeWriter.writeUInt8(byteBuffer, arrays.size()); + + for (Array array : arrays) { + IsoTypeWriter.writeUInt8(byteBuffer, (array.array_completeness ? 0x80 : 0) + (array.reserved ? 0x40 : 0) + array.nal_unit_type); + + IsoTypeWriter.writeUInt16(byteBuffer, array.nalUnits.size()); + for (byte[] nalUnit : array.nalUnits) { + IsoTypeWriter.writeUInt16(byteBuffer, nalUnit.length); + byteBuffer.put(nalUnit); + } + } + } + + public int getSize() { + int size = 23; + for (Array array : arrays) { + size += 3; + for (byte[] nalUnit : array.nalUnits) { + size += 2; + size += nalUnit.length; + } + } + return size; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + HevcDecoderConfigurationRecord that = (HevcDecoderConfigurationRecord) o; + + if (avgFrameRate != that.avgFrameRate) return false; + if (bitDepthChromaMinus8 != that.bitDepthChromaMinus8) return false; + if (bitDepthLumaMinus8 != that.bitDepthLumaMinus8) return false; + if (chromaFormat != that.chromaFormat) return false; + if (configurationVersion != that.configurationVersion) return false; + if (constantFrameRate != that.constantFrameRate) return false; + if (general_constraint_indicator_flags != that.general_constraint_indicator_flags) + return false; + if (general_level_idc != that.general_level_idc) return false; + if (general_profile_compatibility_flags != that.general_profile_compatibility_flags) + return false; + if (general_profile_idc != that.general_profile_idc) return false; + if (general_profile_space != that.general_profile_space) return false; + if (general_tier_flag != that.general_tier_flag) return false; + if (lengthSizeMinusOne != that.lengthSizeMinusOne) return false; + if (min_spatial_segmentation_idc != that.min_spatial_segmentation_idc) return false; + if (numTemporalLayers != that.numTemporalLayers) return false; + if (parallelismType != that.parallelismType) return false; + if (reserved1 != that.reserved1) return false; + if (reserved2 != that.reserved2) return false; + if (reserved3 != that.reserved3) return false; + if (reserved4 != that.reserved4) return false; + if (reserved5 != that.reserved5) return false; + if (temporalIdNested != that.temporalIdNested) return false; + if (arrays != null ? !arrays.equals(that.arrays) : that.arrays != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = configurationVersion; + result = 31 * result + general_profile_space; + result = 31 * result + (general_tier_flag ? 1 : 0); + result = 31 * result + general_profile_idc; + result = 31 * result + (int) (general_profile_compatibility_flags ^ (general_profile_compatibility_flags >>> 32)); + result = 31 * result + (int) (general_constraint_indicator_flags ^ (general_constraint_indicator_flags >>> 32)); + result = 31 * result + general_level_idc; + result = 31 * result + reserved1; + result = 31 * result + min_spatial_segmentation_idc; + result = 31 * result + reserved2; + result = 31 * result + parallelismType; + result = 31 * result + reserved3; + result = 31 * result + chromaFormat; + result = 31 * result + reserved4; + result = 31 * result + bitDepthLumaMinus8; + result = 31 * result + reserved5; + result = 31 * result + bitDepthChromaMinus8; + result = 31 * result + avgFrameRate; + result = 31 * result + constantFrameRate; + result = 31 * result + numTemporalLayers; + result = 31 * result + (temporalIdNested ? 1 : 0); + result = 31 * result + lengthSizeMinusOne; + result = 31 * result + (arrays != null ? arrays.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "HEVCDecoderConfigurationRecord{" + + "configurationVersion=" + configurationVersion + + ", general_profile_space=" + general_profile_space + + ", general_tier_flag=" + general_tier_flag + + ", general_profile_idc=" + general_profile_idc + + ", general_profile_compatibility_flags=" + general_profile_compatibility_flags + + ", general_constraint_indicator_flags=" + general_constraint_indicator_flags + + ", general_level_idc=" + general_level_idc + + (reserved1 != 0xf ? (", reserved1=" + reserved1) : "") + + ", min_spatial_segmentation_idc=" + min_spatial_segmentation_idc + + (reserved2 != 0x3F ? (", reserved2=" + reserved2) : "") + + ", parallelismType=" + parallelismType + + (reserved3 != 0x3F ? (", reserved3=" + reserved3) : "") + + ", chromaFormat=" + chromaFormat + + (reserved4 != 0x1F ? (", reserved4=" + reserved4) : "") + + ", bitDepthLumaMinus8=" + bitDepthLumaMinus8 + + (reserved5 != 0x1F ? (", reserved5=" + reserved5) : "") + + ", bitDepthChromaMinus8=" + bitDepthChromaMinus8 + + ", avgFrameRate=" + avgFrameRate + + ", constantFrameRate=" + constantFrameRate + + ", numTemporalLayers=" + numTemporalLayers + + ", temporalIdNested=" + temporalIdNested + + ", lengthSizeMinusOne=" + lengthSizeMinusOne + + ", arrays=" + arrays + + '}'; + } + + public int getConfigurationVersion() { + return configurationVersion; + } + + public void setConfigurationVersion(int configurationVersion) { + this.configurationVersion = configurationVersion; + } + + public int getGeneral_profile_space() { + return general_profile_space; + } + + public void setGeneral_profile_space(int general_profile_space) { + this.general_profile_space = general_profile_space; + } + + public boolean isGeneral_tier_flag() { + return general_tier_flag; + } + + public void setGeneral_tier_flag(boolean general_tier_flag) { + this.general_tier_flag = general_tier_flag; + } + + public int getGeneral_profile_idc() { + return general_profile_idc; + } + + public void setGeneral_profile_idc(int general_profile_idc) { + this.general_profile_idc = general_profile_idc; + } + + public long getGeneral_profile_compatibility_flags() { + return general_profile_compatibility_flags; + } + + public void setGeneral_profile_compatibility_flags(long general_profile_compatibility_flags) { + this.general_profile_compatibility_flags = general_profile_compatibility_flags; + } + + public long getGeneral_constraint_indicator_flags() { + return general_constraint_indicator_flags; + } + + public void setGeneral_constraint_indicator_flags(long general_constraint_indicator_flags) { + this.general_constraint_indicator_flags = general_constraint_indicator_flags; + } + + public int getGeneral_level_idc() { + return general_level_idc; + } + + public void setGeneral_level_idc(int general_level_idc) { + this.general_level_idc = general_level_idc; + } + + public int getMin_spatial_segmentation_idc() { + return min_spatial_segmentation_idc; + } + + public void setMin_spatial_segmentation_idc(int min_spatial_segmentation_idc) { + this.min_spatial_segmentation_idc = min_spatial_segmentation_idc; + } + + public int getParallelismType() { + return parallelismType; + } + + public void setParallelismType(int parallelismType) { + this.parallelismType = parallelismType; + } + + public int getChromaFormat() { + return chromaFormat; + } + + public void setChromaFormat(int chromaFormat) { + this.chromaFormat = chromaFormat; + } + + public int getBitDepthLumaMinus8() { + return bitDepthLumaMinus8; + } + + public void setBitDepthLumaMinus8(int bitDepthLumaMinus8) { + this.bitDepthLumaMinus8 = bitDepthLumaMinus8; + } + + public int getBitDepthChromaMinus8() { + return bitDepthChromaMinus8; + } + + public void setBitDepthChromaMinus8(int bitDepthChromaMinus8) { + this.bitDepthChromaMinus8 = bitDepthChromaMinus8; + } + + public int getAvgFrameRate() { + return avgFrameRate; + } + + public void setAvgFrameRate(int avgFrameRate) { + this.avgFrameRate = avgFrameRate; + } + + public int getNumTemporalLayers() { + return numTemporalLayers; + } + + public void setNumTemporalLayers(int numTemporalLayers) { + this.numTemporalLayers = numTemporalLayers; + } + + public int getLengthSizeMinusOne() { + return lengthSizeMinusOne; + } + + public void setLengthSizeMinusOne(int lengthSizeMinusOne) { + this.lengthSizeMinusOne = lengthSizeMinusOne; + } + + public boolean isTemporalIdNested() { + return temporalIdNested; + } + + public void setTemporalIdNested(boolean temporalIdNested) { + this.temporalIdNested = temporalIdNested; + } + + public int getConstantFrameRate() { + return constantFrameRate; + } + + public void setConstantFrameRate(int constantFrameRate) { + this.constantFrameRate = constantFrameRate; + } + + public List getArrays() { + return arrays; + } + + public void setArrays(List arrays) { + this.arrays = arrays; + } + + public boolean isFrame_only_constraint_flag() { + return frame_only_constraint_flag; + } + + public void setFrame_only_constraint_flag(boolean frame_only_constraint_flag) { + this.frame_only_constraint_flag = frame_only_constraint_flag; + } + + public boolean isNon_packed_constraint_flag() { + return non_packed_constraint_flag; + } + + public void setNon_packed_constraint_flag(boolean non_packed_constraint_flag) { + this.non_packed_constraint_flag = non_packed_constraint_flag; + } + + public boolean isInterlaced_source_flag() { + return interlaced_source_flag; + } + + public void setInterlaced_source_flag(boolean interlaced_source_flag) { + this.interlaced_source_flag = interlaced_source_flag; + } + + public boolean isProgressive_source_flag() { + return progressive_source_flag; + } + + public void setProgressive_source_flag(boolean progressive_source_flag) { + this.progressive_source_flag = progressive_source_flag; + } + + public static class Array { + + public boolean array_completeness; + public boolean reserved; + public int nal_unit_type; + public List nalUnits; + + + public Array() { + + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Array array = (Array) o; + + if (array_completeness != array.array_completeness) return false; + if (nal_unit_type != array.nal_unit_type) return false; + if (reserved != array.reserved) return false; + ListIterator e1 = nalUnits.listIterator(); + ListIterator e2 = (array.nalUnits).listIterator(); + while (e1.hasNext() && e2.hasNext()) { + byte[] o1 = e1.next(); + byte[] o2 = e2.next(); + + if (!(o1 == null ? o2 == null : Arrays.equals(o1, o2))) + return false; + } + return !(e1.hasNext() || e2.hasNext()); + } + + @Override + public int hashCode() { + int result = (array_completeness ? 1 : 0); + result = 31 * result + (reserved ? 1 : 0); + result = 31 * result + nal_unit_type; + result = 31 * result + (nalUnits != null ? nalUnits.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "Array{" + + "nal_unit_type=" + nal_unit_type + + ", reserved=" + reserved + + ", array_completeness=" + array_completeness + + ", num_nals=" + nalUnits.size() + + '}'; + } + } + + + public static VisualSampleEntry parseFromCsd(List csd) throws IOException { + final ArrayList sps = new ArrayList<>(); + final ArrayList pps = new ArrayList<>(); + final ArrayList vps = new ArrayList<>(); + SequenceParameterSetRbsp spsStruct = null; + + for (ByteBuffer nal : csd) { + final H265NalUnitHeader unitHeader = getNalUnitHeader(nal); + nal.position(0); + // collect sps/vps/pps + switch (unitHeader.nalUnitType) { + case NAL_TYPE_PPS_NUT: + pps.add(nal.duplicate()); + break; + case NAL_TYPE_VPS_NUT: + vps.add(nal.duplicate()); + break; + case NAL_TYPE_SPS_NUT: + sps.add(nal.duplicate()); + nal.position(2); + spsStruct = new SequenceParameterSetRbsp(new CleanInputStream(Channels.newInputStream(new ByteBufferByteChannel(nal.slice())))); + break; + case NAL_TYPE_PREFIX_SEI_NUT: + //new SEIMessage(new BitReaderBuffer(nal.slice())); + break; + } + } + return createSampleEntry(sps, pps, vps, spsStruct); + } + + private static VisualSampleEntry createSampleEntry(ArrayList sps, ArrayList pps, ArrayList vps, SequenceParameterSetRbsp spsStruct) { + final VisualSampleEntry visualSampleEntry = new VisualSampleEntry("hvc1"); + visualSampleEntry.setDataReferenceIndex(1); + visualSampleEntry.setDepth(24); + visualSampleEntry.setFrameCount(1); + visualSampleEntry.setHorizresolution(72); + visualSampleEntry.setVertresolution(72); + visualSampleEntry.setCompressorname("HEVC Coding"); + + final HevcConfigurationBox hevcConfigurationBox = new HevcConfigurationBox(); + hevcConfigurationBox.getHevcDecoderConfigurationRecord().setConfigurationVersion(1); + + if (spsStruct != null) { + visualSampleEntry.setWidth(spsStruct.pic_width_in_luma_samples); + visualSampleEntry.setHeight(spsStruct.pic_height_in_luma_samples); + + final HevcDecoderConfigurationRecord hevcDecoderConfigurationRecord = hevcConfigurationBox.getHevcDecoderConfigurationRecord(); + hevcDecoderConfigurationRecord.setChromaFormat(spsStruct.chroma_format_idc); + hevcDecoderConfigurationRecord.setGeneral_profile_idc(spsStruct.general_profile_idc); + hevcDecoderConfigurationRecord.setGeneral_profile_compatibility_flags(spsStruct.general_profile_compatibility_flags); + hevcDecoderConfigurationRecord.setGeneral_constraint_indicator_flags(spsStruct.general_constraint_indicator_flags); + hevcDecoderConfigurationRecord.setGeneral_level_idc(spsStruct.general_level_idc); + hevcDecoderConfigurationRecord.setGeneral_tier_flag(spsStruct.general_tier_flag); + hevcDecoderConfigurationRecord.setGeneral_profile_space(spsStruct.general_profile_space); + hevcDecoderConfigurationRecord.setBitDepthChromaMinus8(spsStruct.bit_depth_chroma_minus8); + hevcDecoderConfigurationRecord.setBitDepthLumaMinus8(spsStruct.bit_depth_luma_minus8); + hevcDecoderConfigurationRecord.setTemporalIdNested(spsStruct.sps_temporal_id_nesting_flag); + } + + hevcConfigurationBox.getHevcDecoderConfigurationRecord().setLengthSizeMinusOne(3); + + final HevcDecoderConfigurationRecord.Array vpsArray = new HevcDecoderConfigurationRecord.Array(); + vpsArray.array_completeness = true; + vpsArray.nal_unit_type = NAL_TYPE_VPS_NUT; + vpsArray.nalUnits = new ArrayList<>(); + for (ByteBuffer vp : vps) { + vpsArray.nalUnits.add(vp.array()); + } + + final HevcDecoderConfigurationRecord.Array spsArray = new HevcDecoderConfigurationRecord.Array(); + spsArray.array_completeness = true; + spsArray.nal_unit_type = NAL_TYPE_SPS_NUT; + spsArray.nalUnits = new ArrayList<>(); + for (ByteBuffer sp : sps) { + spsArray.nalUnits.add(sp.array()); + } + + final HevcDecoderConfigurationRecord.Array ppsArray = new HevcDecoderConfigurationRecord.Array(); + ppsArray.array_completeness = true; + ppsArray.nal_unit_type = NAL_TYPE_PPS_NUT; + ppsArray.nalUnits = new ArrayList<>(); + for (ByteBuffer pp : pps) { + ppsArray.nalUnits.add(pp.array()); + } + + hevcConfigurationBox.getArrays().addAll(Arrays.asList(vpsArray, spsArray, ppsArray)); + + visualSampleEntry.addBox(hevcConfigurationBox); + return visualSampleEntry; + } + + private boolean isVcl(final @NonNull H265NalUnitHeader nalUnitHeader) { + return nalUnitHeader.nalUnitType >= 0 && nalUnitHeader.nalUnitType <= 31; + } + + private static H265NalUnitHeader getNalUnitHeader(final ByteBuffer nal) { + nal.position(0); + final int nalUnitHeaderValue = IsoTypeReader.readUInt16(nal); + final H265NalUnitHeader nalUnitHeader = new H265NalUnitHeader(); + nalUnitHeader.forbiddenZeroFlag = (nalUnitHeaderValue & 0x8000) >> 15; + nalUnitHeader.nalUnitType = (nalUnitHeaderValue & 0x7E00) >> 9; + nalUnitHeader.nuhLayerId = (nalUnitHeaderValue & 0x1F8) >> 3; + nalUnitHeader.nuhTemporalIdPlusOne = (nalUnitHeaderValue & 0x7); + return nalUnitHeader; + } + + public static class H265NalUnitHeader { + public int forbiddenZeroFlag; + public int nalUnitType; + public int nuhLayerId; + public int nuhTemporalIdPlusOne; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/MP4Builder.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/MP4Builder.java index b31595cda4..633d081bb6 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/video/MP4Builder.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/MP4Builder.java @@ -63,13 +63,13 @@ public class MP4Builder { private boolean splitMdat; private boolean wasFirstVideoFrame; - public MP4Builder createMovie(Mp4Movie mp4Movie, boolean split) throws Exception { + public MP4Builder createMovie(Mp4Movie mp4Movie, boolean split, boolean hevc) throws Exception { currentMp4Movie = mp4Movie; fos = new FileOutputStream(mp4Movie.getCacheFile()); fc = fos.getChannel(); - FileTypeBox fileTypeBox = createFileTypeBox(); + FileTypeBox fileTypeBox = createFileTypeBox(hevc); fileTypeBox.getBox(fc); dataOffset += fileTypeBox.getSize(); wroteSinceLastMdat += dataOffset; @@ -200,11 +200,11 @@ public void finishMovie() throws Exception { fos.close(); } - protected FileTypeBox createFileTypeBox() { + protected FileTypeBox createFileTypeBox(boolean hevc) { LinkedList minorBrands = new LinkedList<>(); minorBrands.add("isom"); minorBrands.add("iso2"); - minorBrands.add("avc1"); + minorBrands.add(hevc ? "hvc1" : "avc1"); minorBrands.add("mp41"); return new FileTypeBox("isom", 512, minorBrands); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/MediaCodecVideoConvertor.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/MediaCodecVideoConvertor.java index 21f2ee03fa..2bff56b50a 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/video/MediaCodecVideoConvertor.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/MediaCodecVideoConvertor.java @@ -8,17 +8,23 @@ import android.media.MediaFormat; import android.os.Build; +import androidx.annotation.NonNull; + import com.google.android.exoplayer2.util.Log; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.BuildVars; import org.telegram.messenger.FileLog; import org.telegram.messenger.MediaController; +import org.telegram.messenger.R; import org.telegram.messenger.SharedConfig; import org.telegram.messenger.Utilities; import org.telegram.messenger.VideoEditedInfo; +import org.telegram.ui.Components.RLottieDrawable; +import org.telegram.ui.Stories.recorder.StoryEntry; import java.io.File; +import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; @@ -40,6 +46,7 @@ public class MediaCodecVideoConvertor { private static final int MEDIACODEC_TIMEOUT_DEFAULT = 2500; private static final int MEDIACODEC_TIMEOUT_INCREASED = 22000; + private String outputMimeType; public boolean convertVideo(String videoPath, File cacheFile, int rotationValue, boolean isSecret, @@ -54,10 +61,13 @@ public boolean convertVideo(String videoPath, File cacheFile, boolean isPhoto, MediaController.CropState cropState, boolean isRound, - MediaController.VideoConvertorListener callback) { + MediaController.VideoConvertorListener callback, + Integer gradientTopColor, Integer gradientBottomColor, + boolean muted, boolean isStory, StoryEntry.HDRInfo hdrInfo, + ArrayList parts) { 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, isRound); + resultWidth, resultHeight, framerate, bitrate, originalBitrate, startTime, endTime, avatarStartTime, duration, needCompress, false, savedFilterState, paintPath, mediaEntities, isPhoto, cropState, isRound, gradientTopColor, gradientBottomColor, muted, isStory, hdrInfo, parts, 0); } public long getLastFrameTimestamp() { @@ -78,8 +88,13 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, ArrayList mediaEntities, boolean isPhoto, MediaController.CropState cropState, - boolean isRound) { + boolean isRound, + Integer gradientTopColor, Integer gradientBottomColor, boolean muted, boolean isStory, + StoryEntry.HDRInfo hdrInfo, + ArrayList parts, + int triesCount) { + FileLog.d("convertVideoInternal original=" + originalWidth + "x" + originalHeight + " result=" + resultWidth + "x" + resultHeight + " " + avatarStartTime); long time = System.currentTimeMillis(); boolean error = false; boolean repeatWithIncreasedTimeout = false; @@ -87,19 +102,24 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, int videoTrackIndex = -5; String selectedEncoderName = null; + boolean shouldUseHevc = isStory; + outputMimeType = shouldUseHevc ? "video/hevc" : "video/avc"; + + boolean canBeBrokenEncoder = false; + MediaCodec encoder = null; + MediaCodec decoder = null; + InputSurface inputSurface = null; + OutputSurface outputSurface = null; try { MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); Mp4Movie movie = new Mp4Movie(); movie.setCacheFile(cacheFile); movie.setRotation(0); movie.setSize(resultWidth, resultHeight); - mediaMuxer = new MP4Builder().createMovie(movie, isSecret); + long currentPts = 0; float durationS = duration / 1000f; - MediaCodec encoder = null; - InputSurface inputSurface = null; - OutputSurface outputSurface = null; int prependHeaderSize = 0; endPresentationTime = duration * 1000; checkConversionCanceled(); @@ -122,32 +142,37 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, bitrate = 921600; } - if (resultWidth % 16 != 0) { - if (BuildVars.LOGS_ENABLED) { - FileLog.d("changing width from " + resultWidth + " to " + Math.round(resultWidth / 16.0f) * 16); + if (cropState == null || cropState.useMatrix == null) { + if (resultWidth % 16 != 0) { + if (BuildVars.LOGS_ENABLED) { + FileLog.d("changing width from " + resultWidth + " to " + Math.round(resultWidth / 16.0f) * 16); + } + resultWidth = Math.round(resultWidth / 16.0f) * 16; } - resultWidth = Math.round(resultWidth / 16.0f) * 16; - } - if (resultHeight % 16 != 0) { - if (BuildVars.LOGS_ENABLED) { - FileLog.d("changing height from " + resultHeight + " to " + Math.round(resultHeight / 16.0f) * 16); + if (resultHeight % 16 != 0) { + if (BuildVars.LOGS_ENABLED) { + FileLog.d("changing height from " + resultHeight + " to " + Math.round(resultHeight / 16.0f) * 16); + } + resultHeight = Math.round(resultHeight / 16.0f) * 16; } - resultHeight = Math.round(resultHeight / 16.0f) * 16; } if (BuildVars.LOGS_ENABLED) { FileLog.d("create photo encoder " + resultWidth + " " + resultHeight + " duration = " + duration); } - MediaFormat outputFormat = MediaFormat.createVideoFormat(MediaController.VIDEO_MIME_TYPE, resultWidth, resultHeight); + if (encoder == null) { + encoder = createEncoderForMimeType(); + } + + MediaFormat outputFormat = MediaFormat.createVideoFormat(outputMimeType, resultWidth, resultHeight); outputFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); outputFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate); outputFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 30); outputFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1); - encoder = MediaCodec.createEncoderByType(MediaController.VIDEO_MIME_TYPE); - selectedEncoderName = encoder.getName(); + canBeBrokenEncoder = "c2.qti.avc.encoder".equalsIgnoreCase(selectedEncoderName); FileLog.d("selected encoder " + selectedEncoderName); encoder.configure(outputFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); @@ -155,7 +180,7 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, inputSurface.makeCurrent(); encoder.start(); - outputSurface = new OutputSurface(savedFilterState, videoPath, paintPath, mediaEntities, null, resultWidth, resultHeight, originalWidth, originalHeight, rotationValue, framerate, true); + outputSurface = new OutputSurface(savedFilterState, videoPath, paintPath, mediaEntities, cropState != null && cropState.useMatrix != null ? cropState : null, resultWidth, resultHeight, originalWidth, originalHeight, rotationValue, framerate, true, gradientTopColor, gradientBottomColor, null, parts); ByteBuffer[] encoderOutputBuffers = null; ByteBuffer[] encoderInputBuffers = null; @@ -167,6 +192,7 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, checkConversionCanceled(); + mediaMuxer = new MP4Builder().createMovie(movie, isSecret, outputMimeType.equals("video/hevc")); while (!outputDone) { checkConversionCanceled(); @@ -191,7 +217,7 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, if (newFormat.containsKey(MediaFormat.KEY_PREPEND_HEADER_TO_SYNC_FRAMES) && newFormat.getInteger(MediaFormat.KEY_PREPEND_HEADER_TO_SYNC_FRAMES) == 1) { ByteBuffer spsBuff = newFormat.getByteBuffer("csd-0"); ByteBuffer ppsBuff = newFormat.getByteBuffer("csd-1"); - prependHeaderSize = spsBuff.limit() + ppsBuff.limit(); + prependHeaderSize = (spsBuff == null ? 0 : spsBuff.limit()) + (ppsBuff == null ? 0 : ppsBuff.limit()); } } } else if (encoderStatus < 0) { @@ -213,31 +239,22 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, info.size -= prependHeaderSize; } if (firstEncode && (info.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0) { - if (info.size > 100) { - encodedData.position(info.offset); - byte[] temp = new byte[100]; - encodedData.get(temp); - int nalCount = 0; - for (int a = 0; a < temp.length - 4; a++) { - if (temp[a] == 0 && temp[a + 1] == 0 && temp[a + 2] == 0 && temp[a + 3] == 1) { - nalCount++; - if (nalCount > 1) { - info.offset += a; - info.size -= a; - break; - } - } - } - } + cutOfNalData(outputMimeType, encodedData, info); firstEncode = false; } long availableSize = mediaMuxer.writeSampleData(videoTrackIndex, encodedData, info, true); if (availableSize != 0) { if (callback != null) { - callback.didWriteData(availableSize, (currentPts / 1000f) / durationS); + if (info.presentationTimeUs > currentPts) { + currentPts = info.presentationTimeUs; + } + callback.didWriteData(availableSize, (currentPts / 1000f / 1000f) / durationS); } } } else if (videoTrackIndex == -5) { + if (outputMimeType.equals("video/hevc")) { + throw new RuntimeException("unsupported!!"); + } byte[] csd = new byte[info.size]; encodedData.limit(info.offset + info.size); encodedData.position(info.offset); @@ -258,7 +275,7 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, } } - MediaFormat newFormat = MediaFormat.createVideoFormat(MediaController.VIDEO_MIME_TYPE, resultWidth, resultHeight); + MediaFormat newFormat = MediaFormat.createVideoFormat(outputMimeType, resultWidth, resultHeight); if (sps != null && pps != null) { newFormat.setByteBuffer("csd-0", sps); newFormat.setByteBuffer("csd-1", pps); @@ -302,13 +319,16 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, if (outputSurface != null) { outputSurface.release(); + outputSurface = null; } if (inputSurface != null) { inputSurface.release(); + inputSurface = null; } if (encoder != null) { encoder.stop(); encoder.release(); + encoder = null; } checkConversionCanceled(); } else { @@ -316,7 +336,7 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, extractor.setDataSource(videoPath); int videoIndex = MediaController.findTrack(extractor, false); - int audioIndex = bitrate != -1 ? MediaController.findTrack(extractor, true) : -1; + int audioIndex = bitrate != -1 && !muted ? MediaController.findTrack(extractor, true) : -1; boolean needConvertVideo = false; if (videoIndex >= 0 && !extractor.getTrackFormat(videoIndex).getString(MediaFormat.KEY_MIME).equals(MediaController.VIDEO_MIME_TYPE)) { needConvertVideo = true; @@ -329,7 +349,6 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, long lastFramePts = -1; if (videoIndex >= 0) { - MediaCodec decoder = null; try { long videoTime = -1; @@ -386,7 +405,7 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, int w; int h; - if (cropState != null) { + if (cropState != null && cropState.useMatrix == null) { if (rotationValue == 90 || rotationValue == 270) { w = cropState.transformHeight; h = cropState.transformWidth; @@ -406,14 +425,15 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, } } + if (encoder == null) { - encoder = MediaCodec.createEncoderByType(MediaController.VIDEO_MIME_TYPE); + encoder = createEncoderForMimeType(); } if (BuildVars.LOGS_ENABLED) { FileLog.d("create encoder with w = " + w + " h = " + h + " bitrate = " + bitrate); } - MediaFormat outputFormat = MediaFormat.createVideoFormat(MediaController.VIDEO_MIME_TYPE, w, h); + MediaFormat outputFormat = MediaFormat.createVideoFormat(outputMimeType, w, h); outputFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); outputFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate); if (isAvatar && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { @@ -424,6 +444,28 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, outputFormat.setInteger(MediaFormat.KEY_FRAME_RATE, framerate); outputFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1); +// boolean hasHDR = false; +// int hdrType = 0; +// int colorTransfer = 0, colorStandard = 0, colorRange = 0; +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { +// if (videoFormat.containsKey(MediaFormat.KEY_COLOR_TRANSFER)) { +// colorTransfer = videoFormat.getInteger(MediaFormat.KEY_COLOR_TRANSFER); +// } +// if (videoFormat.containsKey(MediaFormat.KEY_COLOR_STANDARD)) { +// colorStandard = videoFormat.getInteger(MediaFormat.KEY_COLOR_STANDARD); +// } +// if (videoFormat.containsKey(MediaFormat.KEY_COLOR_RANGE)) { +// colorRange = videoFormat.getInteger(MediaFormat.KEY_COLOR_RANGE); +// } +// if (videoFormat.containsKey(MediaFormat.KEY_HDR_STATIC_INFO)) { +// ByteBuffer bytes = videoFormat.getByteBuffer(MediaFormat.KEY_HDR_STATIC_INFO); +// } +// if ((colorTransfer == MediaFormat.COLOR_TRANSFER_ST2084 || colorTransfer == MediaFormat.COLOR_TRANSFER_HLG) && colorStandard == MediaFormat.COLOR_STANDARD_BT2020) { +// hasHDR = true; +// hdrType = colorTransfer == MediaFormat.COLOR_TRANSFER_HLG ? 1 : 2; +// } +// } + if (Build.VERSION.SDK_INT < 23 && Math.min(h, w) <= 480 && !isAvatar) { if (bitrate > 921600) { bitrate = 921600; @@ -432,17 +474,28 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, } selectedEncoderName = encoder.getName(); + canBeBrokenEncoder = "c2.qti.avc.encoder".equalsIgnoreCase(selectedEncoderName); FileLog.d("selected encoder " + selectedEncoderName); encoder.configure(outputFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); inputSurface = new InputSurface(encoder.createInputSurface()); inputSurface.makeCurrent(); encoder.start(); - decoder = MediaCodec.createDecoderByType(videoFormat.getString(MediaFormat.KEY_MIME)); - outputSurface = new OutputSurface(savedFilterState, null, paintPath, mediaEntities, cropState, resultWidth, resultHeight, originalWidth, originalHeight, rotationValue, framerate, 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)); + outputSurface = new OutputSurface(savedFilterState, null, paintPath, mediaEntities, cropState, resultWidth, resultHeight, originalWidth, originalHeight, rotationValue, framerate, false, gradientTopColor, gradientBottomColor, hdrInfo, parts); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && hdrInfo != null && hdrInfo.getHDRType() != 0 && outputSurface.supportsEXTYUV()) { + outputSurface.changeFragmentShader( + hdrFragmentShader(originalWidth, originalHeight, resultWidth, resultHeight, true, hdrInfo), + hdrFragmentShader(originalWidth, originalHeight, resultWidth, resultHeight, false, hdrInfo), + true + ); + } else if (!isRound && Math.max(resultHeight, resultHeight) / (float) Math.max(originalHeight, originalWidth) < 0.9f) { + outputSurface.changeFragmentShader( + createFragmentShader(originalWidth, originalHeight, resultWidth, resultHeight, true, isStory ? 0 : 3), + createFragmentShader(originalWidth, originalHeight, resultWidth, resultHeight, false, isStory ? 0 : 3), + false + ); } + decoder = getDecoderByFormat(videoFormat); decoder.configure(videoFormat, outputSurface.getSurface(), null, 0); decoder.start(); @@ -455,6 +508,7 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, } int maxBufferSize = 0; + mediaMuxer = new MP4Builder().createMovie(movie, isSecret, outputMimeType.equals("video/hevc")); if (audioIndex >= 0) { MediaFormat audioFormat = extractor.getTrackFormat(audioIndex); copyAudioBuffer = audioFormat.getString(MediaFormat.KEY_MIME).equals(MediaController.AUIDO_MIME_TYPE) || audioFormat.getString(MediaFormat.KEY_MIME).equals("audio/mpeg"); @@ -507,6 +561,7 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, checkConversionCanceled(); + while (!outputDone || (!copyAudioBuffer && !audioEncoderDone)) { checkConversionCanceled(); @@ -598,7 +653,7 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, if (newFormat.containsKey(MediaFormat.KEY_PREPEND_HEADER_TO_SYNC_FRAMES) && newFormat.getInteger(MediaFormat.KEY_PREPEND_HEADER_TO_SYNC_FRAMES) == 1) { ByteBuffer spsBuff = newFormat.getByteBuffer("csd-0"); ByteBuffer ppsBuff = newFormat.getByteBuffer("csd-1"); - prependHeaderSize = spsBuff.limit() + ppsBuff.limit(); + prependHeaderSize = (spsBuff == null ? 0 : spsBuff.limit()) + (ppsBuff == null ? 0 : ppsBuff.limit()); } } } else if (encoderStatus < 0) { @@ -620,22 +675,7 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, info.size -= prependHeaderSize; } if (firstEncode && (info.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0) { - if (info.size > 100) { - encodedData.position(info.offset); - byte[] temp = new byte[100]; - encodedData.get(temp); - int nalCount = 0; - for (int a = 0; a < temp.length - 4; a++) { - if (temp[a] == 0 && temp[a + 1] == 0 && temp[a + 2] == 0 && temp[a + 3] == 1) { - nalCount++; - if (nalCount > 1) { - info.offset += a; - info.size -= a; - break; - } - } - } - } + cutOfNalData(outputMimeType, encodedData, info); firstEncode = false; } long availableSize = mediaMuxer.writeSampleData(videoTrackIndex, encodedData, info, true); @@ -668,7 +708,7 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, } } - MediaFormat newFormat = MediaFormat.createVideoFormat(MediaController.VIDEO_MIME_TYPE, w, h); + MediaFormat newFormat = MediaFormat.createVideoFormat(outputMimeType, w, h); if (sps != null && pps != null) { newFormat.setByteBuffer("csd-0", sps); newFormat.setByteBuffer("csd-1", pps); @@ -694,6 +734,7 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, if (BuildVars.LOGS_ENABLED) { FileLog.d("newFormat = " + newFormat); } + // TODO: apply hdr static info here } else if (decoderStatus < 0) { throw new RuntimeException("unexpected result from decoder.dequeueOutputBuffer: " + decoderStatus); } else { @@ -796,20 +837,24 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, } if (outputSurface != null) { outputSurface.release(); + outputSurface = null; } if (inputSurface != null) { inputSurface.release(); + inputSurface = null; } if (encoder != null) { encoder.stop(); encoder.release(); + encoder = null; } if (audioRecoder != null) { audioRecoder.release(); } checkConversionCanceled(); } else { - readAndWriteTracks(extractor, mediaMuxer, info, startTime, endTime, duration, cacheFile, bitrate != -1); + mediaMuxer = new MP4Builder().createMovie(movie, isSecret, false); + readAndWriteTracks(extractor, mediaMuxer, info, startTime, endTime, duration, cacheFile, bitrate != -1 && !muted); } } } catch (Throwable e) { @@ -828,6 +873,30 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, FileLog.e(e); } } + if (encoder != null) { + try { + encoder.release(); + } catch (Exception ignore) {} + encoder = null; + } + if (decoder != null) { + try { + decoder.release(); + } catch (Exception ignore) {} + decoder = null; + } + if (outputSurface != null) { + try { + outputSurface.release(); + } catch (Exception ignore) {} + outputSurface = null; + } + if (inputSurface != null) { + try { + inputSurface.release(); + } catch (Exception ignore) {} + inputSurface = null; + } } if (repeatWithIncreasedTimeout) { @@ -835,7 +904,15 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, originalWidth, originalHeight, resultWidth, resultHeight, framerate, bitrate, originalBitrate, startTime, endTime, avatarStartTime, duration, needCompress, true, savedFilterState, paintPath, mediaEntities, - isPhoto, cropState, isRound); + isPhoto, cropState, isRound, gradientTopColor, gradientBottomColor, muted, isStory, hdrInfo, parts, triesCount + 1); + } + + if (error && canBeBrokenEncoder && triesCount < 3) { + return convertVideoInternal(videoPath, cacheFile, rotationValue, isSecret, + originalWidth, originalHeight, + resultWidth, resultHeight, framerate, bitrate, originalBitrate, startTime, endTime, avatarStartTime, duration, + needCompress, increaseTimeout, savedFilterState, paintPath, mediaEntities, + isPhoto, cropState, isRound, gradientTopColor, gradientBottomColor, muted, isStory, hdrInfo, parts, triesCount + 1); } long timeLeft = System.currentTimeMillis() - time; @@ -846,6 +923,69 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, return error; } + private MediaCodec createEncoderForMimeType() throws IOException { + MediaCodec encoder = null;//MediaCodec.createEncoderByType(outputMimeType); + if (outputMimeType.equals("video/hevc") && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { + String encoderName = SharedConfig.findGoodHevcEncoder(); + if (encoderName != null) { + encoder = MediaCodec.createByCodecName(encoderName); + } + } else { + outputMimeType = "video/avc"; + encoder = MediaCodec.createEncoderByType(outputMimeType); + } +// if (encoder != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && "c2.qti.avc.encoder".equals(encoder.getName())) { +// FileLog.d("searching another encoder to replace c2.qti.avc.encoder"); +// MediaCodecInfo[] infos = new MediaCodecList(MediaCodecList.ALL_CODECS).getCodecInfos(); +// for (MediaCodecInfo codecInfo : infos) { +// if (codecInfo != null && codecInfo.isEncoder() && !"c2.qti.avc.encoder".equals(codecInfo.getName())) { +// String[] types = codecInfo.getSupportedTypes(); +// boolean found = false; +// for (int i = 0; i < types.length; ++i) { +// if (types[i] != null && types[i].startsWith(outputMimeType)) { +// found = true; +// break; +// } +// } +// if (found) { +// FileLog.d("blacklisting c2.qti.avc.encoder, replacing it with " + codecInfo.getName()); +// encoder.release(); +// encoder = MediaCodec.createByCodecName(codecInfo.getName()); +// break; +// } +// } +// } +// } + if (encoder == null && outputMimeType.equals("video/hevc")) { + outputMimeType = "video/avc"; + encoder = MediaCodec.createEncoderByType(outputMimeType); + } + return encoder; + } + + public static void cutOfNalData(String outputMimeType, ByteBuffer encodedData, MediaCodec.BufferInfo info) { + int maxNalCount = 1; + if (outputMimeType.equals("video/hevc")) { + maxNalCount = 3; + } + if (info.size > 100) { + encodedData.position(info.offset); + byte[] temp = new byte[100]; + encodedData.get(temp); + int nalCount = 0; + for (int a = 0; a < temp.length - 4; a++) { + if (temp[a] == 0 && temp[a + 1] == 0 && temp[a + 2] == 0 && temp[a + 3] == 1) { + nalCount++; + if (nalCount > maxNalCount) { + info.offset += a; + info.size -= a; + break; + } + } + } + } + } + private boolean isMediatekAvcEncoder(MediaCodec encoder) { return encoder.getName().equals("c2.mtk.avc.encoder"); } @@ -1007,19 +1147,54 @@ private void checkConversionCanceled() { throw new ConversionCanceledException(); } + private static String hdrFragmentShader( + final int srcWidth, + final int srcHeight, + final int dstWidth, + final int dstHeight, + boolean external, + StoryEntry.HDRInfo hdrInfo + ) { + if (external) { + String shaderCode; + if (hdrInfo.getHDRType() == 1) { + shaderCode = RLottieDrawable.readRes(null, R.raw.yuv_hlg2rgb); + } else { + shaderCode = RLottieDrawable.readRes(null, R.raw.yuv_pq2rgb); + } + shaderCode = shaderCode.replace("$dstWidth", dstWidth + ".0"); + shaderCode = shaderCode.replace("$dstHeight", dstHeight + ".0"); + return shaderCode + "\n" + + "in vec2 vTextureCoord;\n" + + "out vec4 fragColor;\n" + + "void main() {\n" + + " fragColor = TEX(vTextureCoord);\n" + + "}"; + } else { + return "#version 320 es\n" + + "precision mediump float;\n" + + "varying vec2 vTextureCoord;\n" + + "uniform sampler2D sTexture;\n" + + "out vec4 fragColor;\n" + + "void main() {\n" + + "fragColor = texture(sTexture, vTextureCoord);\n" + + "}\n"; + } + } + private static String createFragmentShader( final int srcWidth, final int srcHeight, final int dstWidth, - final int dstHeight, boolean external) { + final int dstHeight, boolean external, int maxKernelRadius) { final float kernelSize = Utilities.clamp((float) (Math.max(srcWidth, srcHeight) / (float) Math.max(dstHeight, dstWidth)) * 0.8f, 2f, 1f); int kernelRadius = (int) kernelSize; if (kernelRadius > 1 && SharedConfig.deviceIsAverage()) { kernelRadius = 1; } + kernelRadius = Math.min(maxKernelRadius, kernelRadius); FileLog.d("source size " + srcWidth + "x" + srcHeight + " dest size " + dstWidth + dstHeight + " kernelRadius " + kernelRadius); - if (external) { return "#extension GL_OES_EGL_image_external : require\n" + "precision mediump float;\n" + @@ -1067,4 +1242,31 @@ public ConversionCanceledException() { } } + @NonNull + private MediaCodec getDecoderByFormat(MediaFormat format) { + if (format == null) { + throw new RuntimeException("getDecoderByFormat: format is null"); + } + ArrayList types = new ArrayList<>(); + String mainType = format.getString(MediaFormat.KEY_MIME); + types.add(mainType); + if ("video/dolby-vision".equals(mainType)) { + types.add("video/hevc"); + types.add("video/avc"); + } + Exception exception = null; + while (!types.isEmpty()) { + try { + String mime = types.remove(0); + format.setString(MediaFormat.KEY_MIME, mime); + return MediaCodec.createDecoderByType(mime); + } catch (Exception e) { + if (exception == null) { + exception = e; + } + } + } + throw new RuntimeException(exception); + } + } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/OutputSurface.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/OutputSurface.java index d70be6893d..6c65b9b9ad 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/video/OutputSurface.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/OutputSurface.java @@ -9,10 +9,13 @@ package org.telegram.messenger.video; import android.graphics.SurfaceTexture; +import android.opengl.GLES20; import android.view.Surface; +import org.telegram.messenger.FileLog; import org.telegram.messenger.MediaController; import org.telegram.messenger.VideoEditedInfo; +import org.telegram.ui.Stories.recorder.StoryEntry; import java.util.ArrayList; @@ -36,8 +39,8 @@ public class OutputSurface implements SurfaceTexture.OnFrameAvailableListener { private boolean mFrameAvailable; private TextureRenderer mTextureRender; - public OutputSurface(MediaController.SavedFilterState savedFilterState, String imagePath, String paintPath, ArrayList mediaEntities, MediaController.CropState cropState, int w, int h, int originalW, int originalH, int rotation, float fps, boolean photo) { - mTextureRender = new TextureRenderer(savedFilterState, imagePath, paintPath, mediaEntities, cropState, w, h, originalW, originalH, rotation, fps, photo); + public OutputSurface(MediaController.SavedFilterState savedFilterState, String imagePath, String paintPath, ArrayList mediaEntities, MediaController.CropState cropState, int w, int h, int originalW, int originalH, int rotation, float fps, boolean photo, Integer gradientTopColor, Integer gradientBottomColor, StoryEntry.HDRInfo hdrInfo, ArrayList parts) { + mTextureRender = new TextureRenderer(savedFilterState, imagePath, paintPath, mediaEntities, cropState, w, h, originalW, originalH, rotation, fps, photo, gradientTopColor, gradientBottomColor, hdrInfo, parts); mTextureRender.surfaceCreated(); mSurfaceTexture = new SurfaceTexture(mTextureRender.getTextureId()); mSurfaceTexture.setOnFrameAvailableListener(this); @@ -166,7 +169,17 @@ private void checkEglError(String msg) { } } - public void changeFragmentShader(String fragmentExternalShader, String fragmentShader) { - mTextureRender.changeFragmentShader(fragmentExternalShader, fragmentShader); + public void changeFragmentShader(String fragmentExternalShader, String fragmentShader, boolean is300) { + mTextureRender.changeFragmentShader(fragmentExternalShader, fragmentShader, is300); + } + + public boolean supportsEXTYUV() { + try { + String extensions = GLES20.glGetString(GLES20.GL_EXTENSIONS); + return extensions.contains("GL_EXT_YUV_target"); + } catch (Exception e) { + FileLog.e(e); + } + return false; } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/SequenceParameterSetRbsp.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/SequenceParameterSetRbsp.java new file mode 100644 index 0000000000..54495de3bb --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/SequenceParameterSetRbsp.java @@ -0,0 +1,222 @@ +package org.telegram.messenger.video; + +import com.googlecode.mp4parser.h264.read.CAVLCReader; + +import java.io.IOException; +import java.io.InputStream; + + +public class SequenceParameterSetRbsp { + public int pic_width_in_luma_samples; + public int pic_height_in_luma_samples; + public int general_profile_space; + public boolean general_tier_flag; + public int general_profile_idc; + public long general_profile_compatibility_flags; + public long general_constraint_indicator_flags; + public byte general_level_idc; + public int chroma_format_idc; + public int bit_depth_luma_minus8; + public int bit_depth_chroma_minus8; + public int sps_max_sub_layers_minus1; + public boolean sps_temporal_id_nesting_flag; + + public SequenceParameterSetRbsp(InputStream is) throws IOException { + CAVLCReader bsr = new CAVLCReader(is); + + int sps_video_parameter_set_id = (int) bsr.readNBit(4, "sps_video_parameter_set_id"); + sps_max_sub_layers_minus1 = (int) bsr.readNBit(3, "sps_max_sub_layers_minus1"); + boolean sps_temporal_id_nesting_flag = bsr.readBool("sps_temporal_id_nesting_flag"); + profile_tier_level(sps_max_sub_layers_minus1, bsr); + int sps_seq_parameter_set_id = bsr.readUE("sps_seq_parameter_set_id"); + chroma_format_idc = bsr.readUE("chroma_format_idc"); + if (chroma_format_idc == 3) { + int separate_colour_plane_flag = bsr.read1Bit(); + } + pic_width_in_luma_samples = bsr.readUE("pic_width_in_luma_samples"); + pic_height_in_luma_samples = bsr.readUE("pic_width_in_luma_samples"); + boolean conformance_window_flag = bsr.readBool("conformance_window_flag"); + if (conformance_window_flag) { + int conf_win_left_offset = bsr.readUE("conf_win_left_offset"); + int conf_win_right_offset = bsr.readUE("conf_win_right_offset"); + int conf_win_top_offset = bsr.readUE("conf_win_top_offset"); + int conf_win_bottom_offset = bsr.readUE("conf_win_bottom_offset"); + } + + bit_depth_luma_minus8 = bsr.readUE("bit_depth_luma_minus8"); + bit_depth_chroma_minus8 = bsr.readUE("bit_depth_chroma_minus8"); + int log2_max_pic_order_cnt_lsb_minus4 = bsr.readUE("log2_max_pic_order_cnt_lsb_minus4"); + boolean sps_sub_layer_ordering_info_present_flag = bsr.readBool("sps_sub_layer_ordering_info_present_flag"); + + int j = sps_max_sub_layers_minus1 - (sps_sub_layer_ordering_info_present_flag ? 0 : sps_max_sub_layers_minus1) + 1; + int sps_max_dec_pic_buffering_minus1[] = new int[j]; + int sps_max_num_reorder_pics[] = new int[j]; + int sps_max_latency_increase_plus1[] = new int[j]; + + for (int i = (sps_sub_layer_ordering_info_present_flag ? 0 : sps_max_sub_layers_minus1); i <= sps_max_sub_layers_minus1; i++) { + sps_max_dec_pic_buffering_minus1[i] = bsr.readUE("sps_max_dec_pic_buffering_minus1[" + i + "]"); + sps_max_num_reorder_pics[i] = bsr.readUE("sps_max_num_reorder_pics[" + i + "]"); + sps_max_latency_increase_plus1[i] = bsr.readUE("sps_max_latency_increase_plus1[" + i + "]"); + } + + int log2_min_luma_coding_block_size_minus3 = bsr.readUE("log2_min_luma_coding_block_size_minus3"); + int log2_diff_max_min_luma_coding_block_size = bsr.readUE("log2_diff_max_min_luma_coding_block_size"); + int log2_min_transform_block_size_minus2 = bsr.readUE("log2_min_transform_block_size_minus2"); + int log2_diff_max_min_transform_block_size = bsr.readUE("log2_diff_max_min_transform_block_size"); + int max_transform_hierarchy_depth_inter = bsr.readUE("max_transform_hierarchy_depth_inter"); + int max_transform_hierarchy_depth_intra = bsr.readUE("max_transform_hierarchy_depth_intra"); + + boolean scaling_list_enabled_flag = bsr.readBool("scaling_list_enabled_flag"); + if (scaling_list_enabled_flag) { + boolean sps_scaling_list_data_present_flag = bsr.readBool("sps_scaling_list_data_present_flag"); + if (sps_scaling_list_data_present_flag) { + skip_scaling_list_data(bsr); + } + } + boolean amp_enabled_flag = bsr.readBool("amp_enabled_flag"); + boolean sample_adaptive_offset_enabled_flag = bsr.readBool("sample_adaptive_offset_enabled_flag"); + boolean pcm_enabled_flag = bsr.readBool("pcm_enabled_flag"); + + if (pcm_enabled_flag) { + int pcm_sample_bit_depth_luma_minus1 = (int) bsr.readNBit(4, "pcm_sample_bit_depth_luma_minus1"); + int pcm_sample_bit_depth_chroma_minus1 = (int) bsr.readNBit(4, "pcm_sample_bit_depth_chroma_minus1"); + int log2_min_pcm_luma_coding_block_size_minus3 = bsr.readUE("log2_min_pcm_luma_coding_block_size_minus3"); + int log2_diff_max_min_pcm_luma_coding_block_size = bsr.readUE("log2_diff_max_min_pcm_luma_coding_block_size"); + boolean pcm_loop_filter_disabled_flag = bsr.readBool("pcm_loop_filter_disabled_flag"); + } + int num_short_term_ref_pic_sets = bsr.readUE("num_short_term_ref_pic_sets"); + + parse_short_term_ref_pic_sets(num_short_term_ref_pic_sets, bsr); + + boolean long_term_ref_pics_present_flag = bsr.readBool("long_term_ref_pics_present_flag"); + if (long_term_ref_pics_present_flag) { + int num_long_term_ref_pics_sps = bsr.readUE("num_long_term_ref_pics_sps"); + int lt_ref_pic_poc_lsb_sps[] = new int[num_long_term_ref_pics_sps]; + boolean used_by_curr_pic_lt_sps_flag[] = new boolean[num_long_term_ref_pics_sps]; + for (int i = 0; i < num_long_term_ref_pics_sps; i++) { + lt_ref_pic_poc_lsb_sps[i] = bsr.readU(log2_max_pic_order_cnt_lsb_minus4 + 4, "lt_ref_pic_poc_lsb_sps[" + i + "]"); + used_by_curr_pic_lt_sps_flag[i] = bsr.readBool("used_by_curr_pic_lt_sps_flag[" + i + "]"); + } + } + boolean sps_temporal_mvp_enabled_flag = bsr.readBool("sps_temporal_mvp_enabled_flag"); + boolean strong_intra_smoothing_enabled_flag = bsr.readBool("strong_intra_smoothing_enabled_flag"); +// boolean vui_parameters_present_flag = bsr.readBool("vui_parameters_present_flag"); +// if (vui_parameters_present_flag) { +// vuiParameters = new VuiParameters(sps_max_sub_layers_minus1, bsr); +// } + } + + private void parse_short_term_ref_pic_sets(int num_short_term_ref_pic_sets, CAVLCReader bsr) throws IOException + { + // Based on FFMPEG implementation -- see hevc.c "parse_rps" + long[] num_delta_pocs = new long[num_short_term_ref_pic_sets]; + for (int rpsIdx = 0; rpsIdx < num_short_term_ref_pic_sets; rpsIdx++) { + if (rpsIdx != 0 && bsr.readBool()) { + bsr.readBool("delta_rps_sign"); + bsr.readUE("abs_delta_rps_minus1"); + num_delta_pocs[rpsIdx] = 0; + for (int i = 0; i <= num_delta_pocs[rpsIdx - 1]; i++) { + boolean use_delta_flag = false; + boolean used_by_curr_pic_flag = bsr.readBool(); + if (!used_by_curr_pic_flag) { + use_delta_flag = bsr.readBool(); + } + if (used_by_curr_pic_flag || use_delta_flag) { + num_delta_pocs[rpsIdx]++; + } + } + } + else { + long delta_pocs = bsr.readUE("num_negative_pics") + bsr.readUE("num_positive_pics"); + num_delta_pocs[rpsIdx] = delta_pocs; + for (long i = 0; i < delta_pocs; ++i) { + bsr.readUE("delta_poc_s0/1_minus1"); + bsr.readBool("used_by_curr_pic_s0/1_flag"); + } + } + } + } + + private static void skip_scaling_list_data(CAVLCReader bsr) throws IOException + { + // Based on FFMPEG implementation see hevc.c "skip_scaling_list_data" + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < (i == 3 ? 2 : 6); j++) + { + if (bsr.readBool()) + { + bsr.readUE("scaling_list_pred_matrix_id_delta"); + } + else + { + int coef_num = Math.min(64, (1 << (4 + (i << 1)))); + if (i > 1) + { + bsr.readUE("scaling_list_dc_coef_minus8"); + } + for (int k = 0; k < coef_num; k++) + { + bsr.readUE("scaling_list_delta_coef"); + } + } + } + } + } + + + private void profile_tier_level(int maxNumSubLayersMinus1, CAVLCReader bsr) throws IOException { + general_profile_space = bsr.readU(2, "general_profile_space"); + general_tier_flag = bsr.readBool("general_tier_flag"); + general_profile_idc = bsr.readU(5, "general_profile_idc"); + general_profile_compatibility_flags = bsr.readNBit(32); + general_constraint_indicator_flags = bsr.readNBit(48); + general_level_idc = (byte) bsr.readByte(); + boolean[] sub_layer_profile_present_flag = new boolean[maxNumSubLayersMinus1]; + boolean[] sub_layer_level_present_flag = new boolean[maxNumSubLayersMinus1]; + for (int i = 0; i < maxNumSubLayersMinus1; i++) { + sub_layer_profile_present_flag[i] = bsr.readBool("sub_layer_profile_present_flag[" + i + "]"); + sub_layer_level_present_flag[i] = bsr.readBool("sub_layer_level_present_flag[" + i + "]"); + } + + if (maxNumSubLayersMinus1 > 0) { + int[] reserved_zero_2bits = new int[8]; + + for (int i = maxNumSubLayersMinus1; i < 8; i++) { + reserved_zero_2bits[i] = bsr.readU(2, "reserved_zero_2bits[" + i + "]"); + } + } + int[] sub_layer_profile_space = new int[maxNumSubLayersMinus1]; + boolean[] sub_layer_tier_flag = new boolean[maxNumSubLayersMinus1]; + int[] sub_layer_profile_idc = new int[maxNumSubLayersMinus1]; + boolean[][] sub_layer_profile_compatibility_flag = new boolean[maxNumSubLayersMinus1][32]; + boolean[] sub_layer_progressive_source_flag = new boolean[maxNumSubLayersMinus1]; + boolean[] sub_layer_interlaced_source_flag = new boolean[maxNumSubLayersMinus1]; + boolean[] sub_layer_non_packed_constraint_flag = new boolean[maxNumSubLayersMinus1]; + boolean[] sub_layer_frame_only_constraint_flag = new boolean[maxNumSubLayersMinus1]; + long[] sub_layer_reserved_zero_44bits = new long[maxNumSubLayersMinus1]; + int[] sub_layer_level_idc = new int[maxNumSubLayersMinus1]; + + + for (int i = 0; i < maxNumSubLayersMinus1; i++) { + if (sub_layer_profile_present_flag[i]) { + sub_layer_profile_space[i] = bsr.readU(2, "sub_layer_profile_space[" + i + "]"); + sub_layer_tier_flag[i] = bsr.readBool("sub_layer_tier_flag[" + i + "]"); + sub_layer_profile_idc[i] = bsr.readU(5, "sub_layer_profile_idc[" + i + "]"); + for (int j = 0; j < 32; j++) { + sub_layer_profile_compatibility_flag[i][j] = bsr.readBool("sub_layer_profile_compatibility_flag[" + i + "][" + j + "]"); + } + sub_layer_progressive_source_flag[i] = bsr.readBool("sub_layer_progressive_source_flag[" + i + "]"); + sub_layer_interlaced_source_flag[i] = bsr.readBool("sub_layer_interlaced_source_flag[" + i + "]"); + sub_layer_non_packed_constraint_flag[i] = bsr.readBool("sub_layer_non_packed_constraint_flag[" + i + "]"); + sub_layer_frame_only_constraint_flag[i] = bsr.readBool("sub_layer_frame_only_constraint_flag[" + i + "]"); + sub_layer_reserved_zero_44bits[i] = bsr.readNBit(44); + } + if (sub_layer_level_present_flag[i]) { + sub_layer_level_idc[i] = bsr.readU(8, "sub_layer_level_idc[" + i + "]"); + } + } + } + + +} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/TextureRenderer.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/TextureRenderer.java index 4ad7c343c4..224baaac42 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/video/TextureRenderer.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/TextureRenderer.java @@ -11,6 +11,7 @@ import android.annotation.SuppressLint; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.graphics.BlendMode; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; @@ -22,21 +23,28 @@ import android.graphics.Typeface; import android.opengl.GLES11Ext; import android.opengl.GLES20; +import android.opengl.GLES30; import android.opengl.GLUtils; import android.opengl.Matrix; import android.os.Build; import android.text.Layout; import android.text.SpannableString; import android.text.Spanned; +import android.text.style.ReplacementSpan; import android.util.Log; +import android.util.Pair; import android.util.TypedValue; import android.view.Gravity; import android.view.View; import android.view.inputmethod.EditorInfo; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.exifinterface.media.ExifInterface; +import com.google.zxing.common.detector.MathUtils; + import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.Bitmaps; @@ -45,6 +53,7 @@ import org.telegram.messenger.FileLog; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MediaController; +import org.telegram.messenger.MessageObject; import org.telegram.messenger.UserConfig; import org.telegram.messenger.Utilities; import org.telegram.messenger.VideoEditedInfo; @@ -57,6 +66,7 @@ import org.telegram.ui.Components.Paint.Views.PaintTextOptionsView; import org.telegram.ui.Components.RLottieDrawable; import org.telegram.ui.Components.Rect; +import org.telegram.ui.Stories.recorder.StoryEntry; import java.io.File; import java.io.RandomAccessFile; @@ -71,10 +81,19 @@ public class TextureRenderer { private FloatBuffer verticesBuffer; + private FloatBuffer gradientVerticesBuffer; + private FloatBuffer gradientTextureBuffer; private FloatBuffer textureBuffer; private FloatBuffer renderTextureBuffer; private FloatBuffer bitmapVerticesBuffer; + private FloatBuffer partsVerticesBuffer[]; + private FloatBuffer partsTextureBuffer; + private ArrayList parts; + private int[] partsTexture; + + private boolean useMatrixForImagePath; + float[] bitmapData = { -1.0f, 1.0f, 1.0f, 1.0f, @@ -103,13 +122,25 @@ public class TextureRenderer { " vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" + "}\n"; + private static final String VERTEX_SHADER_300 = + "#version 320 es\n" + + "uniform mat4 uMVPMatrix;\n" + + "uniform mat4 uSTMatrix;\n" + + "in vec4 aPosition;\n" + + "in vec4 aTextureCoord;\n" + + "out vec2 vTextureCoord;\n" + + "void main() {\n" + + " gl_Position = uMVPMatrix * aPosition;\n" + + " vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" + + "}\n"; + private static final String FRAGMENT_EXTERNAL_SHADER = "#extension GL_OES_EGL_image_external : require\n" + "precision highp float;\n" + "varying vec2 vTextureCoord;\n" + "uniform samplerExternalOES sTexture;\n" + "void main() {\n" + - " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" + + " gl_FragColor = texture2D(sTexture, vTextureCoord);" + "}\n"; private static final String FRAGMENT_SHADER = @@ -120,6 +151,22 @@ public class TextureRenderer { " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" + "}\n"; + private static final String GRADIENT_FRAGMENT_SHADER = + "precision highp float;\n" + + "varying vec2 vTextureCoord;\n" + + "uniform vec4 gradientTopColor;\n" + + "uniform vec4 gradientBottomColor;\n" + + "float interleavedGradientNoise(vec2 n) {\n" + + " return fract(52.9829189 * fract(.06711056 * n.x + .00583715 * n.y));\n" + + "}\n" + + "void main() {\n" + + " gl_FragColor = mix(gradientTopColor, gradientBottomColor, vTextureCoord.y + (.2 * interleavedGradientNoise(gl_FragCoord.xy) - .1));\n" + + "}\n"; + + private int NUM_FILTER_SHADER = -1; + private int NUM_EXTERNAL_SHADER = -1; + private int NUM_GRADIENT_SHADER = -1; + private float[] mMVPMatrix = new float[16]; private float[] mSTMatrix = new float[16]; private float[] mSTMatrixIdentity = new float[16]; @@ -129,6 +176,9 @@ public class TextureRenderer { private int[] muSTMatrixHandle; private int[] maPositionHandle; private int[] maTextureHandle; + private int gradientTopColorHandle, gradientBottomColorHandle; + private int texSizeHandle; + // todo: HDR handles private int simpleShaderProgram; private int simplePositionHandle; @@ -150,9 +200,28 @@ public class TextureRenderer { private boolean firstFrame = true; Path path; Paint xRefPaint; - - public TextureRenderer(MediaController.SavedFilterState savedFilterState, String image, String paint, ArrayList entities, MediaController.CropState cropState, int w, int h, int originalWidth, int originalHeight, int rotation, float fps, boolean photo) { + Paint textColorPaint; + + private int gradientTopColor, gradientBottomColor; + + public TextureRenderer( + MediaController.SavedFilterState savedFilterState, + String image, + String paint, + ArrayList entities, + MediaController.CropState cropState, + int w, int h, + int originalWidth, int originalHeight, + int rotation, + float fps, + boolean photo, + Integer gradientTopColor, + Integer gradientBottomColor, + StoryEntry.HDRInfo hdrInfo, + ArrayList parts + ) { isPhoto = photo; + this.parts = parts; float[] texData = { 0.f, 0.f, @@ -181,8 +250,10 @@ public TextureRenderer(MediaController.SavedFilterState savedFilterState, String Matrix.setIdentityM(mSTMatrixIdentity, 0); if (savedFilterState != null) { - filterShaders = new FilterShaders(true); - filterShaders.setDelegate(FilterShaders.getFilterShadersDelegate(savedFilterState)); + filterShaders = new FilterShaders(true, hdrInfo); + if (savedFilterState != null) { + filterShaders.setDelegate(FilterShaders.getFilterShadersDelegate(savedFilterState)); + } } transformedWidth = w; transformedHeight = h; @@ -193,11 +264,13 @@ public TextureRenderer(MediaController.SavedFilterState savedFilterState, String mediaEntities = entities; videoFps = fps == 0 ? 30 : fps; - int count; + int count = 0; + NUM_EXTERNAL_SHADER = count++; + if (gradientBottomColor != null && gradientTopColor != null) { + NUM_GRADIENT_SHADER = count++; + } if (filterShaders != null) { - count = 2; - } else { - count = 1; + NUM_FILTER_SHADER = count++; } mProgram = new int[count]; muMVPMatrixHandle = new int[count]; @@ -207,34 +280,66 @@ public TextureRenderer(MediaController.SavedFilterState savedFilterState, String Matrix.setIdentityM(mMVPMatrix, 0); int textureRotation = 0; + if (gradientBottomColor != null && gradientTopColor != null) { + final float[] verticesData = { + -1.0f, -1.0f, + 1.0f, -1.0f, + -1.0f, 1.0f, + 1.0f, 1.0f + }; + gradientVerticesBuffer = ByteBuffer.allocateDirect(verticesData.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer(); + gradientVerticesBuffer.put(verticesData).position(0); + final float[] textureData = { + 0, isPhoto ? 1 : 0, + 1, isPhoto ? 1 : 0, + 0, isPhoto ? 0 : 1, + 1, isPhoto ? 0 : 1 + }; + gradientTextureBuffer = ByteBuffer.allocateDirect(textureData.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer(); + gradientTextureBuffer.put(textureData).position(0); + this.gradientTopColor = gradientTopColor; + this.gradientBottomColor = gradientBottomColor; + } if (cropState != null) { - float[] verticesData = { + if (cropState.useMatrix != null) { + useMatrixForImagePath = true; + float[] verticesData = { 0, 0, - w, 0, - 0, h, - w, h, - }; - textureRotation = cropState.transformRotation; - if (textureRotation == 90 || textureRotation == 270) { - int temp = originalWidth; - originalWidth = originalHeight; - originalHeight = temp; - } - - transformedWidth *= cropState.cropPw; - transformedHeight *= cropState.cropPh; - - float angle = (float) (-cropState.cropRotate * (Math.PI / 180.0f)); - for (int a = 0; a < 4; a++) { - float x1 = verticesData[a * 2] - w / 2; - float y1 = verticesData[a * 2 + 1] - h / 2; - float x2 = (float) (x1 * Math.cos(angle) - y1 * Math.sin(angle) + cropState.cropPx * w) * cropState.cropScale; - float y2 = (float) (x1 * Math.sin(angle) + y1 * Math.cos(angle) - cropState.cropPy * h) * cropState.cropScale; - verticesData[a * 2] = x2 / transformedWidth * 2; - verticesData[a * 2 + 1] = y2 / transformedHeight * 2; + originalWidth, 0, + 0, originalHeight, + originalWidth, originalHeight + }; + cropState.useMatrix.mapPoints(verticesData); + for (int a = 0; a < 4; a++) { + verticesData[a * 2] = verticesData[a * 2] / w * 2f - 1f; + verticesData[a * 2 + 1] = 1f - verticesData[a * 2 + 1] / h * 2f; + } + verticesBuffer = ByteBuffer.allocateDirect(verticesData.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer(); + verticesBuffer.put(verticesData).position(0); + } else { + float[] verticesData = { + 0, 0, + w, 0, + 0, h, + w, h, + }; + textureRotation = cropState.transformRotation; + + transformedWidth *= cropState.cropPw; + transformedHeight *= cropState.cropPh; + + float angle = (float) (-cropState.cropRotate * (Math.PI / 180.0f)); + for (int a = 0; a < 4; a++) { + float x1 = verticesData[a * 2] - w / 2; + float y1 = verticesData[a * 2 + 1] - h / 2; + float x2 = (float) (x1 * Math.cos(angle) - y1 * Math.sin(angle) + cropState.cropPx * w) * cropState.cropScale; + float y2 = (float) (x1 * Math.sin(angle) + y1 * Math.cos(angle) - cropState.cropPy * h) * cropState.cropScale; + verticesData[a * 2] = x2 / transformedWidth * 2; + verticesData[a * 2 + 1] = y2 / transformedHeight * 2; + } + verticesBuffer = ByteBuffer.allocateDirect(verticesData.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer(); + verticesBuffer.put(verticesData).position(0); } - verticesBuffer = ByteBuffer.allocateDirect(verticesData.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer(); - verticesBuffer.put(verticesData).position(0); } else { float[] verticesData = { -1.0f, -1.0f, @@ -307,6 +412,12 @@ public TextureRenderer(MediaController.SavedFilterState savedFilterState, String }; } } + if (!isPhoto && useMatrixForImagePath) { + textureData[1] = 1f - textureData[1]; + textureData[3] = 1f - textureData[3]; + textureData[5] = 1f - textureData[5]; + textureData[7] = 1f - textureData[7]; + } if (cropState != null && cropState.mirrored) { for (int a = 0; a < 4; a++) { if (textureData[a * 2] > 0.5f) { @@ -324,15 +435,28 @@ public int getTextureId() { return mTextureID; } + private void drawGradient() { + if (NUM_GRADIENT_SHADER < 0) { + return; + } + GLES20.glUseProgram(mProgram[NUM_GRADIENT_SHADER]); + + GLES20.glVertexAttribPointer(maPositionHandle[NUM_GRADIENT_SHADER], 2, GLES20.GL_FLOAT, false, 8, gradientVerticesBuffer); + GLES20.glEnableVertexAttribArray(maPositionHandle[NUM_GRADIENT_SHADER]); + GLES20.glVertexAttribPointer(maTextureHandle[NUM_GRADIENT_SHADER], 2, GLES20.GL_FLOAT, false, 8, gradientTextureBuffer); + GLES20.glEnableVertexAttribArray(maTextureHandle[NUM_GRADIENT_SHADER]); + + GLES20.glUniformMatrix4fv(muSTMatrixHandle[NUM_GRADIENT_SHADER], 1, false, mSTMatrix, 0); + GLES20.glUniformMatrix4fv(muMVPMatrixHandle[NUM_GRADIENT_SHADER], 1, false, mMVPMatrix, 0); + + GLES20.glUniform4f(gradientTopColorHandle, Color.red(gradientTopColor) / 255f, Color.green(gradientTopColor) / 255f, Color.blue(gradientTopColor) / 255f, Color.alpha(gradientTopColor) / 255f); + GLES20.glUniform4f(gradientBottomColorHandle, Color.red(gradientBottomColor) / 255f, Color.green(gradientBottomColor) / 255f, Color.blue(gradientBottomColor) / 255f, Color.alpha(gradientBottomColor) / 255f); + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + } + public void drawFrame(SurfaceTexture st) { if (isPhoto) { - GLES20.glUseProgram(simpleShaderProgram); - GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - - GLES20.glUniform1i(simpleSourceImageHandle, 0); - GLES20.glEnableVertexAttribArray(simpleInputTexCoordHandle); - GLES20.glVertexAttribPointer(simpleInputTexCoordHandle, 2, GLES20.GL_FLOAT, false, 8, textureBuffer); - GLES20.glEnableVertexAttribArray(simplePositionHandle); + drawGradient(); } else { st.getTransformMatrix(mSTMatrix); if (BuildVars.LOGS_ENABLED && firstFrame) { @@ -369,16 +493,18 @@ public void drawFrame(SurfaceTexture st) { } texture = filterShaders.getRenderTexture(blurred ? 0 : 1); - index = 1; + index = NUM_FILTER_SHADER; target = GLES20.GL_TEXTURE_2D; stMatrix = mSTMatrixIdentity; } else { texture = mTextureID; - index = 0; + index = NUM_EXTERNAL_SHADER; target = GLES11Ext.GL_TEXTURE_EXTERNAL_OES; stMatrix = mSTMatrix; } + drawGradient(); + GLES20.glUseProgram(mProgram[index]); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(target, texture); @@ -388,11 +514,15 @@ public void drawFrame(SurfaceTexture st) { GLES20.glVertexAttribPointer(maTextureHandle[index], 2, GLES20.GL_FLOAT, false, 8, renderTextureBuffer); GLES20.glEnableVertexAttribArray(maTextureHandle[index]); + if (texSizeHandle != 0) { + GLES20.glUniform2f(texSizeHandle, transformedWidth, transformedHeight); + } + GLES20.glUniformMatrix4fv(muSTMatrixHandle[index], 1, false, stMatrix, 0); GLES20.glUniformMatrix4fv(muMVPMatrixHandle[index], 1, false, mMVPMatrix, 0); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); } - if (paintTexture != null || stickerTexture != null) { + if (isPhoto || paintTexture != null || stickerTexture != null || partsTexture != null) { GLES20.glUseProgram(simpleShaderProgram); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); @@ -401,94 +531,118 @@ public void drawFrame(SurfaceTexture st) { GLES20.glVertexAttribPointer(simpleInputTexCoordHandle, 2, GLES20.GL_FLOAT, false, 8, textureBuffer); GLES20.glEnableVertexAttribArray(simplePositionHandle); } + if (paintTexture != null && imagePath != null) { + for (int a = 0; a < 1; a++) { + drawTexture(true, paintTexture[a], -10000, -10000, -10000, -10000, 0, false, useMatrixForImagePath && isPhoto && a == 0, -1); + } + } + if (partsTexture != null) { + for (int a = 0; a < partsTexture.length; a++) { + drawTexture(true, partsTexture[a], -10000, -10000, -10000, -10000, 0, false, false, a); + } + } if (paintTexture != null) { - for (int a = 0; a < paintTexture.length; a++) { - drawTexture(true, paintTexture[a]); + for (int a = (imagePath != null ? 1 : 0); a < paintTexture.length; a++) { + drawTexture(true, paintTexture[a], -10000, -10000, -10000, -10000, 0, false, useMatrixForImagePath && isPhoto && a == 0, -1); } } if (stickerTexture != null) { for (int a = 0, N = mediaEntities.size(); a < N; a++) { - VideoEditedInfo.MediaEntity entity = mediaEntities.get(a); - if (entity.ptr != 0) { - RLottieDrawable.getFrame(entity.ptr, (int) entity.currentFrame, stickerBitmap, 512, 512, stickerBitmap.getRowBytes(), true); - applyRoundRadius(entity, stickerBitmap); + drawEntity(mediaEntities.get(a), mediaEntities.get(a).color); + } + } + GLES20.glFinish(); + } + + private void drawEntity(VideoEditedInfo.MediaEntity entity, int textColor) { + if (entity.ptr != 0) { + RLottieDrawable.getFrame(entity.ptr, (int) entity.currentFrame, stickerBitmap, 512, 512, stickerBitmap.getRowBytes(), true); + applyRoundRadius(entity, stickerBitmap, (entity.subType & 8) != 0 ? textColor : 0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, stickerTexture[0]); + GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, stickerBitmap, 0); + entity.currentFrame += entity.framesPerDraw; + if (entity.currentFrame >= entity.metadata[0]) { + entity.currentFrame = 0; + } + drawTexture(false, stickerTexture[0], entity.x, entity.y, entity.width, entity.height, entity.rotation, (entity.subType & 2) != 0); + } else if (entity.animatedFileDrawable != null) { + int lastFrame = (int) entity.currentFrame; + entity.currentFrame += entity.framesPerDraw; + int currentFrame = (int) entity.currentFrame; + while (lastFrame != currentFrame) { + entity.animatedFileDrawable.getNextFrame(); + currentFrame--; + } + Bitmap frameBitmap = entity.animatedFileDrawable.getBackgroundBitmap(); + if (frameBitmap != null) { + if (stickerCanvas == null && stickerBitmap != null) { + stickerCanvas = new Canvas(stickerBitmap); + if (stickerBitmap.getHeight() != frameBitmap.getHeight() || stickerBitmap.getWidth() != frameBitmap.getWidth()) { + stickerCanvas.scale(stickerBitmap.getWidth() / (float) frameBitmap.getWidth(), stickerBitmap.getHeight() / (float) frameBitmap.getHeight()); + } + } + if (stickerBitmap != null) { + stickerBitmap.eraseColor(Color.TRANSPARENT); + stickerCanvas.drawBitmap(frameBitmap, 0, 0, null); + applyRoundRadius(entity, stickerBitmap, (entity.subType & 8) != 0 ? textColor : 0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, stickerTexture[0]); GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, stickerBitmap, 0); - entity.currentFrame += entity.framesPerDraw; - if (entity.currentFrame >= entity.metadata[0]) { - entity.currentFrame = 0; - } drawTexture(false, stickerTexture[0], entity.x, entity.y, entity.width, entity.height, entity.rotation, (entity.subType & 2) != 0); - } else if (entity.animatedFileDrawable != null) { - int lastFrame = (int) entity.currentFrame; - entity.currentFrame += entity.framesPerDraw; - int currentFrame = (int) entity.currentFrame; - while (lastFrame != currentFrame) { - entity.animatedFileDrawable.getNextFrame(); - currentFrame--; - } - Bitmap frameBitmap = entity.animatedFileDrawable.getBackgroundBitmap(); - if (frameBitmap != null) { - if (stickerCanvas == null && stickerBitmap != null) { - stickerCanvas = new Canvas(stickerBitmap); - if (stickerBitmap.getHeight() != frameBitmap.getHeight() || stickerBitmap.getWidth() != frameBitmap.getWidth()) { - stickerCanvas.scale(stickerBitmap.getWidth() / (float) frameBitmap.getWidth(), stickerBitmap.getHeight() / (float) frameBitmap.getHeight()); - } - } - if (stickerBitmap != null) { - stickerBitmap.eraseColor(Color.TRANSPARENT); - stickerCanvas.drawBitmap(frameBitmap, 0, 0, null); - applyRoundRadius(entity, stickerBitmap); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, stickerTexture[0]); - GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, stickerBitmap, 0); - drawTexture(false, stickerTexture[0], entity.x, entity.y, entity.width, entity.height, entity.rotation, (entity.subType & 2) != 0); - } + } + } + } else { + if (entity.bitmap != null) { + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, stickerTexture[0]); + GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, entity.bitmap, 0); + drawTexture(false, stickerTexture[0], entity.x, entity.y, entity.width, entity.height, entity.rotation, entity.type == VideoEditedInfo.MediaEntity.TYPE_PHOTO && (entity.subType & 2) != 0); + } + if (entity.entities != null && !entity.entities.isEmpty()) { + for (int i = 0; i < entity.entities.size(); ++i) { + VideoEditedInfo.EmojiEntity e = entity.entities.get(i); + if (e == null) { + continue; } - } else if (entity.view != null && entity.canvas != null && entity.bitmap != null) { - entity.bitmap.eraseColor(Color.TRANSPARENT); - int lastFrame = (int) entity.currentFrame; - entity.currentFrame += entity.framesPerDraw; - int currentFrame = (int) entity.currentFrame; - EditTextEffects editTextEffects = (EditTextEffects) entity.view; - editTextEffects.incrementFrames(currentFrame - lastFrame); - entity.view.draw(entity.canvas); - applyRoundRadius(entity, entity.bitmap); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, stickerTexture[0]); - GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, entity.bitmap, 0); - drawTexture(false, stickerTexture[0], entity.x, entity.y, entity.width, entity.height, entity.rotation, (entity.subType & 2) != 0); - } else { - if (entity.bitmap != null) { - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, stickerTexture[0]); - GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, entity.bitmap, 0); - drawTexture(false, stickerTexture[0], entity.x, entity.y, entity.width, entity.height, entity.rotation, (entity.subType & 2) != 0); + VideoEditedInfo.MediaEntity entity1 = e.entity; + if (entity1 == null) { + continue; } + drawEntity(entity1, entity.color); } } } - GLES20.glFinish(); } - private void applyRoundRadius(VideoEditedInfo.MediaEntity entity, Bitmap stickerBitmap) { - if (stickerBitmap == null || entity == null || entity.roundRadius == 0) { + private void applyRoundRadius(VideoEditedInfo.MediaEntity entity, Bitmap stickerBitmap, int color) { + if (stickerBitmap == null || entity == null || entity.roundRadius == 0 && color == 0) { return; } if (entity.roundRadiusCanvas == null) { entity.roundRadiusCanvas = new Canvas(stickerBitmap); } - if (path == null) { - path = new Path(); + if (entity.roundRadius != 0) { + if (path == null) { + path = new Path(); + } + if (xRefPaint == null) { + xRefPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + xRefPaint.setColor(0xff000000); + xRefPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + } + float rad = Math.min(stickerBitmap.getWidth(), stickerBitmap.getHeight()) * entity.roundRadius; + path.rewind(); + RectF rect = new RectF(0, 0, stickerBitmap.getWidth(), stickerBitmap.getHeight()); + path.addRoundRect(rect, rad, rad, Path.Direction.CCW); + path.toggleInverseFillType(); + entity.roundRadiusCanvas.drawPath(path, xRefPaint); } - if (xRefPaint == null) { - xRefPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - xRefPaint.setColor(0xff000000); - xRefPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + if (color != 0) { + if (textColorPaint == null) { + textColorPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + textColorPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); + } + textColorPaint.setColor(color); + entity.roundRadiusCanvas.drawRect(0, 0, stickerBitmap.getWidth(), stickerBitmap.getHeight(), textColorPaint); } - float rad = Math.min(stickerBitmap.getWidth(), stickerBitmap.getHeight()) * entity.roundRadius; - path.rewind(); - RectF rect = new RectF(0, 0, stickerBitmap.getWidth(), stickerBitmap.getHeight()); - path.addRoundRect(rect, rad, rad, Path.Direction.CCW); - path.toggleInverseFillType(); - entity.roundRadiusCanvas.drawPath(path, xRefPaint); } private void drawTexture(boolean bind, int texture) { @@ -496,6 +650,10 @@ private void drawTexture(boolean bind, int texture) { } private void drawTexture(boolean bind, int texture, float x, float y, float w, float h, float rotation, boolean mirror) { + drawTexture(bind, texture, x, y, w, h, rotation, mirror, false, -1); + } + + private void drawTexture(boolean bind, int texture, float x, float y, float w, float h, float rotation, boolean mirror, boolean useCropMatrix, int matrixIndex) { if (!blendEnabled) { GLES20.glEnable(GLES20.GL_BLEND); GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA); @@ -552,8 +710,9 @@ private void drawTexture(boolean bind, int texture, float x, float y, float w, f } } bitmapVerticesBuffer.put(bitmapData).position(0); - GLES20.glVertexAttribPointer(simplePositionHandle, 2, GLES20.GL_FLOAT, false, 8, bitmapVerticesBuffer); - + GLES20.glVertexAttribPointer(simplePositionHandle, 2, GLES20.GL_FLOAT, false, 8, matrixIndex >= 0 ? partsVerticesBuffer[matrixIndex] : (useCropMatrix ? verticesBuffer : bitmapVerticesBuffer)); + GLES20.glEnableVertexAttribArray(simpleInputTexCoordHandle); + GLES20.glVertexAttribPointer(simpleInputTexCoordHandle, 2, GLES20.GL_FLOAT, false, 8, matrixIndex >= 0 ? partsTextureBuffer : (useCropMatrix ? renderTextureBuffer : textureBuffer)); if (bind) { GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture); } @@ -568,11 +727,26 @@ public void setBreakStrategy(EditTextOutline editText) { @SuppressLint("WrongConstant") public void surfaceCreated() { for (int a = 0; a < mProgram.length; a++) { - mProgram[a] = createProgram(VERTEX_SHADER, a == 0 ? FRAGMENT_EXTERNAL_SHADER : FRAGMENT_SHADER); + String shader = null; + if (a == NUM_EXTERNAL_SHADER) { + shader = FRAGMENT_EXTERNAL_SHADER; + } else if (a == NUM_FILTER_SHADER) { + shader = FRAGMENT_SHADER; + } else if (a == NUM_GRADIENT_SHADER) { + shader = GRADIENT_FRAGMENT_SHADER; + } + if (shader == null) { + continue; + } + mProgram[a] = createProgram(VERTEX_SHADER, shader, false); maPositionHandle[a] = GLES20.glGetAttribLocation(mProgram[a], "aPosition"); maTextureHandle[a] = GLES20.glGetAttribLocation(mProgram[a], "aTextureCoord"); muMVPMatrixHandle[a] = GLES20.glGetUniformLocation(mProgram[a], "uMVPMatrix"); muSTMatrixHandle[a] = GLES20.glGetUniformLocation(mProgram[a], "uSTMatrix"); + if (a == NUM_GRADIENT_SHADER) { + gradientTopColorHandle = GLES20.glGetUniformLocation(mProgram[a], "gradientTopColor"); + gradientBottomColorHandle = GLES20.glGetUniformLocation(mProgram[a], "gradientBottomColor"); + } } int[] textures = new int[1]; GLES20.glGenTextures(1, textures, 0); @@ -583,7 +757,7 @@ public void surfaceCreated() { GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); - if (filterShaders != null || imagePath != null || paintPath != null || mediaEntities != null) { + if (filterShaders != null || imagePath != null || paintPath != null || mediaEntities != null || parts != null) { int vertexShader = FilterShaders.loadShader(GLES20.GL_VERTEX_SHADER, FilterShaders.simpleVertexShaderCode); int fragmentShader = FilterShaders.loadShader(GLES20.GL_FRAGMENT_SHADER, FilterShaders.simpleFragmentShaderCode); if (vertexShader != 0 && fragmentShader != 0) { @@ -617,32 +791,18 @@ public void surfaceCreated() { try { for (int a = 0; a < paintTexture.length; a++) { String path; - int angle = 0; + int angle = 0, invert = 0; if (a == 0 && imagePath != null) { path = imagePath; - try { - ExifInterface exif = new ExifInterface(path); - int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1); - switch (orientation) { - case ExifInterface.ORIENTATION_ROTATE_90: - angle = 90; - break; - case ExifInterface.ORIENTATION_ROTATE_180: - angle = 180; - break; - case ExifInterface.ORIENTATION_ROTATE_270: - angle = 270; - break; - } - } catch (Throwable ignore) { - - } + Pair orientation = AndroidUtilities.getImageOrientation(path); + angle = orientation.first; + invert = orientation.second; } else { path = paintPath; } Bitmap bitmap = BitmapFactory.decodeFile(path); if (bitmap != null) { - if (a == 0 && imagePath != null) { + if (a == 0 && imagePath != null && !useMatrixForImagePath) { Bitmap newBitmap = Bitmap.createBitmap(transformedWidth, transformedHeight, Bitmap.Config.ARGB_8888); newBitmap.eraseColor(0xff000000); Canvas canvas = new Canvas(newBitmap); @@ -655,7 +815,7 @@ public void surfaceCreated() { android.graphics.Matrix matrix = new android.graphics.Matrix(); matrix.postTranslate(-bitmap.getWidth() / 2, -bitmap.getHeight() / 2); - matrix.postScale(1.0f / scale, 1.0f / scale); + matrix.postScale((invert == 1 ? -1.0f : 1.0f) / scale, (invert == 2 ? -1.0f : 1.0f) / scale); matrix.postRotate(angle); matrix.postTranslate(newBitmap.getWidth() / 2, newBitmap.getHeight() / 2); canvas.drawBitmap(bitmap, matrix, new Paint(Paint.FILTER_BITMAP_FLAG)); @@ -674,6 +834,55 @@ public void surfaceCreated() { FileLog.e(e); } } + if (parts != null && !parts.isEmpty()) { + partsTexture = new int[parts.size()]; + partsVerticesBuffer = new FloatBuffer[parts.size()]; + GLES20.glGenTextures(partsTexture.length, partsTexture, 0); + try { + for (int a = 0; a < partsTexture.length; a++) { + StoryEntry.Part part = parts.get(a); + String path = part.file.getAbsolutePath(); + + BitmapFactory.Options opts = new BitmapFactory.Options(); + opts.inJustDecodeBounds = true; + BitmapFactory.decodeFile(path, opts); + opts.inJustDecodeBounds = false; + opts.inSampleSize = StoryEntry.calculateInSampleSize(opts, transformedWidth, transformedHeight); + Bitmap bitmap = BitmapFactory.decodeFile(path, opts); + GLES20.glBindTexture(GL10.GL_TEXTURE_2D, partsTexture[a]); + GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR); + GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); + GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE); + GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE); + GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); + + final float[] verticesData = { + 0, 0, + part.width, 0, + 0, part.height, + part.width, part.height + }; + part.matrix.mapPoints(verticesData); + for (int i = 0; i < 4; i++) { + verticesData[i * 2] = verticesData[i * 2] / transformedWidth * 2f - 1f; + verticesData[i * 2 + 1] = 1f - verticesData[i * 2 + 1] / transformedHeight * 2f; + } + partsVerticesBuffer[a] = ByteBuffer.allocateDirect(verticesData.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer(); + partsVerticesBuffer[a].put(verticesData).position(0); + } + } catch (Throwable e2) { + FileLog.e(e2); + } + + final float[] textureData = { + 0, 0, + 1f, 0, + 0, 1f, + 1f, 1f + }; + partsTextureBuffer = ByteBuffer.allocateDirect(textureData.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer(); + partsTextureBuffer.put(textureData).position(0); + } if (mediaEntities != null) { try { stickerBitmap = Bitmap.createBitmap(512, 512, Bitmap.Config.ARGB_8888); @@ -686,50 +895,12 @@ public void surfaceCreated() { GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE); for (int a = 0, N = mediaEntities.size(); a < N; a++) { VideoEditedInfo.MediaEntity entity = mediaEntities.get(a); - if (entity.type == 0) { - if ((entity.subType & 1) != 0) { - entity.metadata = new int[3]; - entity.ptr = RLottieDrawable.create(entity.text, null, 512, 512, entity.metadata, false, null, false, 0); - entity.framesPerDraw = entity.metadata[1] / videoFps; - } else if ((entity.subType & 4) != 0) { - entity.animatedFileDrawable = new AnimatedFileDrawable(new File(entity.text), true, 0, 0, null, null, null, 0, UserConfig.selectedAccount, true, 512, 512, null); - entity.framesPerDraw = videoFps / 30f; - entity.currentFrame = 0; - } else { - if (Build.VERSION.SDK_INT >= 19) { - entity.bitmap = BitmapFactory.decodeFile(entity.text); - } else { - File path = new File(entity.text); - RandomAccessFile file = new RandomAccessFile(path, "r"); - ByteBuffer buffer = file.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, path.length()); - BitmapFactory.Options bmOptions = new BitmapFactory.Options(); - bmOptions.inJustDecodeBounds = true; - Utilities.loadWebpImage(null, buffer, buffer.limit(), bmOptions, true); - entity.bitmap = Bitmaps.createBitmap(bmOptions.outWidth, bmOptions.outHeight, Bitmap.Config.ARGB_8888); - Utilities.loadWebpImage(entity.bitmap, buffer, buffer.limit(), null, true); - file.close(); - } - if (entity.bitmap != null) { - float aspect = entity.bitmap.getWidth() / (float) entity.bitmap.getHeight(); - if (aspect > 1) { - float h = entity.height / aspect; - entity.y += (entity.height - h) / 2; - entity.height = h; - } else if (aspect < 1) { - float w = entity.width * aspect; - entity.x += (entity.width - w) / 2; - entity.width = w; - } - } - } - } else if (entity.type == 1) { - EditTextOutline editText = new EditTextOutline(ApplicationLoader.applicationContext) { - { - animatedEmojiOffsetX = AndroidUtilities.dp(8); - animatedEmojiRawDraw = true; - animatedEmojiRawDrawFps = (int) videoFps; - } - }; + if (entity.type == VideoEditedInfo.MediaEntity.TYPE_STICKER || entity.type == VideoEditedInfo.MediaEntity.TYPE_PHOTO) { + initStickerEntity(entity); + } else if (entity.type == VideoEditedInfo.MediaEntity.TYPE_TEXT) { + EditTextOutline editText = new EditTextOutline(ApplicationLoader.applicationContext); + editText.betterFraming = useMatrixForImagePath; + editText.drawAnimatedEmojiDrawables = false; editText.setBackgroundColor(Color.TRANSPARENT); editText.setPadding(AndroidUtilities.dp(7), AndroidUtilities.dp(7), AndroidUtilities.dp(7), AndroidUtilities.dp(7)); Typeface typeface; @@ -738,21 +909,51 @@ public void surfaceCreated() { } editText.setTextSize(TypedValue.COMPLEX_UNIT_PX, entity.fontSize); SpannableString text = new SpannableString(entity.text); - boolean containsAnimated = false; for (VideoEditedInfo.EmojiEntity e : entity.entities) { - containsAnimated = true; - AnimatedEmojiSpan span; - if (e.document != null) { - span = new AnimatedEmojiSpan(e.document, editText.getPaint().getFontMetricsInt()); - } else { - span = new AnimatedEmojiSpan(e.document_id, editText.getPaint().getFontMetricsInt()); + if (e.documentAbsolutePath == null) { + continue; } - span.cacheType = AnimatedEmojiDrawable.CACHE_TYPE_RENDERING_VIDEO; - span.documentAbsolutePath = e.documentAbsolutePath; + e.entity = new VideoEditedInfo.MediaEntity(); + e.entity.text = e.documentAbsolutePath; + e.entity.subType = e.subType; + initStickerEntity(e.entity); + AnimatedEmojiSpan span = new AnimatedEmojiSpan(0L, 1f, editText.getPaint().getFontMetricsInt()) { + @Override + public void draw(@NonNull Canvas canvas, CharSequence charSequence, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) { + super.draw(canvas, charSequence, start, end, x, top, y, bottom, paint); + + float tcx = entity.x + (editText.getPaddingLeft() + x + measuredSize / 2f) / entity.viewWidth * entity.width; + float tcy = entity.y + ((editText.betterFraming ? editText.getPaddingTop() : 0) + top + (bottom - top) / 2f) / entity.viewHeight * entity.height; + + if (entity.rotation != 0) { + float mx = entity.x + entity.width / 2f; + float my = entity.y + entity.height / 2f; + float ratio = transformedWidth / (float) transformedHeight; + float x1 = tcx - mx; + float y1 = (tcy - my) / ratio; + tcx = (float) (x1 * Math.cos(-entity.rotation) - y1 * Math.sin(-entity.rotation)) + mx; + tcy = (float) (x1 * Math.sin(-entity.rotation) + y1 * Math.cos(-entity.rotation)) * ratio + my; + } + + e.entity.width = (float) measuredSize / entity.viewWidth * entity.width; + e.entity.height = (float) measuredSize / entity.viewHeight * entity.height; + e.entity.x = tcx - e.entity.width / 2f; + e.entity.y = tcy - e.entity.height / 2f; + e.entity.rotation = entity.rotation; + } + }; text.setSpan(span, e.offset, e.offset + e.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } editText.setText(Emoji.replaceEmoji(text, editText.getPaint().getFontMetricsInt(), (int) (editText.getTextSize() * .8f), false)); editText.setTextColor(entity.color); + CharSequence text2 = editText.getText(); + if (text2 instanceof Spanned) { + Emoji.EmojiSpan[] spans = ((Spanned) text2).getSpans(0, text2.length(), Emoji.EmojiSpan.class); + for (int i = 0; i < spans.length; ++i) { + spans[i].scale = .85f; + } + } + int gravity; switch (entity.textAlign) { @@ -793,21 +994,18 @@ public void surfaceCreated() { if (Build.VERSION.SDK_INT >= 23) { setBreakStrategy(editText); } - if ((entity.subType & 1) != 0) { - editText.setTextColor(0xffffffff); - editText.setStrokeColor(entity.color); - editText.setFrameColor(0); - editText.setShadowLayer(0, 0, 0, 0); - } else if ((entity.subType & 4) != 0) { - editText.setTextColor(0xff000000); - editText.setStrokeColor(0); + if (entity.subType == 0) { editText.setFrameColor(entity.color); - editText.setShadowLayer(0, 0, 0, 0); - } else { + editText.setTextColor(AndroidUtilities.computePerceivedBrightness(entity.color) >= .721f ? Color.BLACK : Color.WHITE); + } else if (entity.subType == 1) { + editText.setFrameColor(AndroidUtilities.computePerceivedBrightness(entity.color) >= .25f ? 0x99000000 : 0x99ffffff); + editText.setTextColor(entity.color); + } else if (entity.subType == 2) { + editText.setFrameColor(AndroidUtilities.computePerceivedBrightness(entity.color) >= .25f ? Color.BLACK : Color.WHITE); editText.setTextColor(entity.color); - editText.setStrokeColor(0); + } else if (entity.subType == 3) { editText.setFrameColor(0); - editText.setShadowLayer(5, 0, 1, 0x66000000); + editText.setTextColor(entity.color); } editText.measure(View.MeasureSpec.makeMeasureSpec(entity.viewWidth, View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(entity.viewHeight, View.MeasureSpec.EXACTLY)); @@ -815,12 +1013,6 @@ public void surfaceCreated() { entity.bitmap = Bitmap.createBitmap(entity.viewWidth, entity.viewHeight, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(entity.bitmap); editText.draw(canvas); - if (containsAnimated) { - entity.view = editText; - entity.canvas = canvas; - entity.framesPerDraw = videoFps / 30f; - entity.currentFrame = 0; - } } } } catch (Throwable e) { @@ -829,29 +1021,119 @@ public void surfaceCreated() { } } - private int createProgram(String vertexSource, String fragmentSource) { - int vertexShader = FilterShaders.loadShader(GLES20.GL_VERTEX_SHADER, vertexSource); - if (vertexShader == 0) { - return 0; - } - int pixelShader = FilterShaders.loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource); - if (pixelShader == 0) { - return 0; - } - int program = GLES20.glCreateProgram(); - if (program == 0) { - return 0; - } - GLES20.glAttachShader(program, vertexShader); - GLES20.glAttachShader(program, pixelShader); - GLES20.glLinkProgram(program); - int[] linkStatus = new int[1]; - GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0); - if (linkStatus[0] != GLES20.GL_TRUE) { - GLES20.glDeleteProgram(program); - program = 0; - } - return program; + private void initStickerEntity(VideoEditedInfo.MediaEntity entity) { + if ((entity.subType & 1) != 0) { + entity.metadata = new int[3]; + entity.ptr = RLottieDrawable.create(entity.text, null, 512, 512, entity.metadata, false, null, false, 0); + entity.framesPerDraw = entity.metadata[1] / videoFps; + } else if ((entity.subType & 4) != 0) { + entity.animatedFileDrawable = new AnimatedFileDrawable(new File(entity.text), true, 0, 0, null, null, null, 0, UserConfig.selectedAccount, true, 512, 512, null); + entity.framesPerDraw = entity.animatedFileDrawable.getFps() / videoFps; + entity.currentFrame = 0; + entity.animatedFileDrawable.getNextFrame(); + } else { + if (Build.VERSION.SDK_INT >= 19) { + BitmapFactory.Options opts = new BitmapFactory.Options(); + if (entity.type == VideoEditedInfo.MediaEntity.TYPE_PHOTO) { + opts.inMutable = true; + } + entity.bitmap = BitmapFactory.decodeFile(entity.text, opts); + } else { + try { + File path = new File(entity.text); + RandomAccessFile file = new RandomAccessFile(path, "r"); + ByteBuffer buffer = file.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, path.length()); + BitmapFactory.Options bmOptions = new BitmapFactory.Options(); + bmOptions.inJustDecodeBounds = true; + Utilities.loadWebpImage(null, buffer, buffer.limit(), bmOptions, true); + if (entity.type == VideoEditedInfo.MediaEntity.TYPE_PHOTO) { + bmOptions.inMutable = true; + } + entity.bitmap = Bitmaps.createBitmap(bmOptions.outWidth, bmOptions.outHeight, Bitmap.Config.ARGB_8888); + Utilities.loadWebpImage(entity.bitmap, buffer, buffer.limit(), null, true); + file.close(); + } catch (Throwable e) { + FileLog.e(e); + } + } + if (entity.type == VideoEditedInfo.MediaEntity.TYPE_PHOTO && entity.bitmap != null) { + entity.roundRadius = AndroidUtilities.dp(12) / (float) Math.min(entity.viewWidth, entity.viewHeight); + Pair orientation = AndroidUtilities.getImageOrientation(entity.text); + entity.rotation -= Math.toRadians(orientation.first); + if ((orientation.first / 90 % 2) == 1) { + float cx = entity.x + entity.width / 2f, cy = entity.y + entity.height / 2f; + + float w = entity.width * transformedWidth / transformedHeight; + entity.width = entity.height * transformedHeight / transformedWidth; + entity.height = w; + + entity.x = cx - entity.width / 2f; + entity.y = cy - entity.height / 2f; + } + applyRoundRadius(entity, entity.bitmap, 0); + } else if (entity.bitmap != null) { + float aspect = entity.bitmap.getWidth() / (float) entity.bitmap.getHeight(); + if (aspect > 1) { + float h = entity.height / aspect; + entity.y += (entity.height - h) / 2; + entity.height = h; + } else if (aspect < 1) { + float w = entity.width * aspect; + entity.x += (entity.width - w) / 2; + entity.width = w; + } + } + } + } + + private int createProgram(String vertexSource, String fragmentSource, boolean is300) { + if (is300) { + int vertexShader = FilterShaders.loadShader(GLES30.GL_VERTEX_SHADER, vertexSource); + if (vertexShader == 0) { + return 0; + } + int pixelShader = FilterShaders.loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentSource); + if (pixelShader == 0) { + return 0; + } + int program = GLES30.glCreateProgram(); + if (program == 0) { + return 0; + } + GLES30.glAttachShader(program, vertexShader); + GLES30.glAttachShader(program, pixelShader); + GLES30.glLinkProgram(program); + int[] linkStatus = new int[1]; + GLES30.glGetProgramiv(program, GLES30.GL_LINK_STATUS, linkStatus, 0); + if (linkStatus[0] != GLES30.GL_TRUE) { + GLES30.glDeleteProgram(program); + program = 0; + } + return program; + } else { + int vertexShader = FilterShaders.loadShader(GLES20.GL_VERTEX_SHADER, vertexSource); + if (vertexShader == 0) { + return 0; + } + int pixelShader = FilterShaders.loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource); + if (pixelShader == 0) { + return 0; + } + int program = GLES20.glCreateProgram(); + if (program == 0) { + return 0; + } + GLES20.glAttachShader(program, vertexShader); + GLES20.glAttachShader(program, pixelShader); + GLES20.glLinkProgram(program); + int[] linkStatus = new int[1]; + GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0); + if (linkStatus[0] != GLES20.GL_TRUE) { + GLES20.glDeleteProgram(program); + program = 0; + } + return program; + } } public void release() { @@ -871,12 +1153,22 @@ public void release() { } } - public void changeFragmentShader(String fragmentExternalShader, String fragmentShader) { - GLES20.glDeleteProgram(mProgram[0]); - mProgram[0] = createProgram(VERTEX_SHADER, fragmentExternalShader); - if (mProgram.length > 1) { - mProgram[1] = createProgram(VERTEX_SHADER, fragmentShader); - } + public void changeFragmentShader(String fragmentExternalShader, String fragmentShader, boolean is300) { + if (NUM_EXTERNAL_SHADER >= 0 && NUM_EXTERNAL_SHADER < mProgram.length) { + int newProgram = createProgram(is300 ? VERTEX_SHADER_300 : VERTEX_SHADER, fragmentExternalShader, is300); + if (newProgram != 0) { + GLES20.glDeleteProgram(mProgram[NUM_EXTERNAL_SHADER]); + mProgram[NUM_EXTERNAL_SHADER] = newProgram; + texSizeHandle = GLES20.glGetUniformLocation(newProgram, "texSize"); + } + } + if (NUM_FILTER_SHADER >= 0 && NUM_FILTER_SHADER < mProgram.length) { + int newProgram = createProgram(is300 ? VERTEX_SHADER_300 : VERTEX_SHADER, fragmentShader, is300); + if (newProgram != 0) { + GLES20.glDeleteProgram(mProgram[NUM_FILTER_SHADER]); + mProgram[NUM_FILTER_SHADER] = newProgram; + } + } } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/Track.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/Track.java index e90f46e1e4..6979f6ddbc 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/video/Track.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/Track.java @@ -25,8 +25,10 @@ import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.ESDescriptor; import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.SLConfigDescriptor; +import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; @@ -203,6 +205,58 @@ public Track(int id, MediaFormat format, boolean audio) { visualSampleEntry.setHeight(height); sampleDescriptionBox.addBox(visualSampleEntry); + } else if (mime.equals("video/hevc")) { + if (format.getByteBuffer("csd-0") != null) { + ByteBuffer byteBuffer = format.getByteBuffer("csd-0"); + byte bytes[] = byteBuffer.array(); + int vpsPosition = -1; + int spsPosition = -1; + int ppsPosition = -1; + int countBufferInititation = 0; + for (int i = 0; i < bytes.length; i++) { + if (countBufferInititation == 3 && bytes[i] == 1) { + if (vpsPosition == -1) { + vpsPosition = i - 3; + } else if (spsPosition == -1) { + spsPosition = i - 3; + } else if (ppsPosition == -1) { + ppsPosition = i - 3; + } + } + if (bytes[i] == 0) { + countBufferInititation++; + } else { + countBufferInititation = 0; + } + } + byte[] vps = new byte[spsPosition - 4]; + byte[] sps = new byte[ppsPosition - spsPosition - 4]; + byte[] pps = new byte[bytes.length - ppsPosition - 4]; + for (int i = 0; i < bytes.length; i++) { + if (i < spsPosition) { + if (i - 4 >= 0) { + vps[i - 4] = bytes[i]; + } + } else if (i < ppsPosition) { + if (i - spsPosition - 4 >= 0) { + sps[i - spsPosition - 4] = bytes[i]; + } + } else { + if (i - ppsPosition - 4 >= 0) { + pps[i - ppsPosition - 4] = bytes[i]; + } + } + } + + try { + VisualSampleEntry visualSampleEntry = HevcDecoderConfigurationRecord.parseFromCsd(Arrays.asList(ByteBuffer.wrap(vps),ByteBuffer.wrap(pps), ByteBuffer.wrap(sps))); + visualSampleEntry.setWidth(width); + visualSampleEntry.setHeight(height); + sampleDescriptionBox.addBox(visualSampleEntry); + } catch (IOException e) { + e.printStackTrace(); + } + } } } else { volume = 1; @@ -255,7 +309,7 @@ public Track(int id, MediaFormat format, boolean audio) { descriptor.setDecoderConfigDescriptor(decoderConfigDescriptor); ByteBuffer data = descriptor.serialize(); - esds.setEsDescriptor(descriptor); + //esds.setEsDescriptor(descriptor); esds.setData(data); audioSampleEntry.addBox(esds); sampleDescriptionBox.addBox(audioSampleEntry); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/VUIParameters.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/VUIParameters.java new file mode 100644 index 0000000000..1b5d48a397 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/VUIParameters.java @@ -0,0 +1,89 @@ +package org.telegram.messenger.video; + +import com.googlecode.mp4parser.h264.model.AspectRatio; +import com.googlecode.mp4parser.h264.model.HRDParameters; + +public class VUIParameters { + + public boolean aspect_ratio_info_present_flag; + public int sar_width; + public int sar_height; + public boolean overscan_info_present_flag; + public boolean overscan_appropriate_flag; + public boolean video_signal_type_present_flag; + public int video_format; + public boolean video_full_range_flag; + public boolean colour_description_present_flag; + public int colour_primaries; + public int transfer_characteristics; + public int matrix_coefficients; + public boolean chroma_loc_info_present_flag; + public int chroma_sample_loc_type_top_field; + public int chroma_sample_loc_type_bottom_field; + public boolean timing_info_present_flag; + public int num_units_in_tick; + public int time_scale; + public boolean fixed_frame_rate_flag; + public boolean low_delay_hrd_flag; + public boolean pic_struct_present_flag; + public HRDParameters nalHRDParams; + public HRDParameters vclHRDParams; + public BitstreamRestriction bitstreamRestriction; + public AspectRatio aspect_ratio; + + @Override + public String toString() { + return "VUIParameters{" + "\n" + + "aspect_ratio_info_present_flag=" + aspect_ratio_info_present_flag + "\n" + + ", sar_width=" + sar_width + "\n" + + ", sar_height=" + sar_height + "\n" + + ", overscan_info_present_flag=" + overscan_info_present_flag + "\n" + + ", overscan_appropriate_flag=" + overscan_appropriate_flag + "\n" + + ", video_signal_type_present_flag=" + video_signal_type_present_flag + "\n" + + ", video_format=" + video_format + "\n" + + ", video_full_range_flag=" + video_full_range_flag + "\n" + + ", colour_description_present_flag=" + colour_description_present_flag + "\n" + + ", colour_primaries=" + colour_primaries + "\n" + + ", transfer_characteristics=" + transfer_characteristics + "\n" + + ", matrix_coefficients=" + matrix_coefficients + "\n" + + ", chroma_loc_info_present_flag=" + chroma_loc_info_present_flag + "\n" + + ", chroma_sample_loc_type_top_field=" + chroma_sample_loc_type_top_field + "\n" + + ", chroma_sample_loc_type_bottom_field=" + chroma_sample_loc_type_bottom_field + "\n" + + ", timing_info_present_flag=" + timing_info_present_flag + "\n" + + ", num_units_in_tick=" + num_units_in_tick + "\n" + + ", time_scale=" + time_scale + "\n" + + ", fixed_frame_rate_flag=" + fixed_frame_rate_flag + "\n" + + ", low_delay_hrd_flag=" + low_delay_hrd_flag + "\n" + + ", pic_struct_present_flag=" + pic_struct_present_flag + "\n" + + ", nalHRDParams=" + nalHRDParams + "\n" + + ", vclHRDParams=" + vclHRDParams + "\n" + + ", bitstreamRestriction=" + bitstreamRestriction + "\n" + + ", aspect_ratio=" + aspect_ratio + "\n" + + '}'; + } + + public static class BitstreamRestriction { + + public boolean motion_vectors_over_pic_boundaries_flag; + public int max_bytes_per_pic_denom; + public int max_bits_per_mb_denom; + public int log2_max_mv_length_horizontal; + public int log2_max_mv_length_vertical; + public int num_reorder_frames; + public int max_dec_frame_buffering; + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("BitstreamRestriction{"); + sb.append("motion_vectors_over_pic_boundaries_flag=").append(motion_vectors_over_pic_boundaries_flag); + sb.append(", max_bytes_per_pic_denom=").append(max_bytes_per_pic_denom); + sb.append(", max_bits_per_mb_denom=").append(max_bits_per_mb_denom); + sb.append(", log2_max_mv_length_horizontal=").append(log2_max_mv_length_horizontal); + sb.append(", log2_max_mv_length_vertical=").append(log2_max_mv_length_vertical); + sb.append(", num_reorder_frames=").append(num_reorder_frames); + sb.append(", max_dec_frame_buffering=").append(max_dec_frame_buffering); + sb.append('}'); + return sb.toString(); + } + } +} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPService.java b/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPService.java index fdc6bc45e6..6c5557c86a 100755 --- a/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPService.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPService.java @@ -17,6 +17,7 @@ import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; +import android.app.Person; import android.app.Service; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; @@ -65,6 +66,7 @@ import android.os.PowerManager; import android.os.SystemClock; import android.os.Vibrator; +import android.provider.Settings; import android.telecom.CallAudioState; import android.telecom.Connection; import android.telecom.DisconnectCause; @@ -75,7 +77,6 @@ import android.text.SpannableString; import android.text.TextUtils; import android.text.style.ForegroundColorSpan; -import android.util.Log; import android.util.LruCache; import android.view.KeyEvent; import android.view.View; @@ -138,11 +139,12 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import kotlin.Unit; import tw.nekomimi.nekogram.NekoConfig; @@ -319,6 +321,7 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa private boolean startedRinging; private int classGuid; + private volatile CountDownLatch groupCallBottomSheetLatch; private HashMap currentStreamRequestTimestamp = new HashMap<>(); public boolean micSwitching; @@ -330,6 +333,7 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa public void run() { AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE); + VoipAudioManager vam = VoipAudioManager.get(); am.abandonAudioFocus(VoIPService.this); am.unregisterMediaButtonEventReceiver(new ComponentName(VoIPService.this, VoIPMediaButtonReceiver.class)); if (audioDeviceCallback != null) { @@ -342,7 +346,7 @@ public void run() { bluetoothScoActive = false; bluetoothScoConnecting = false; } - am.setSpeakerphoneOn(false); + vam.setSpeakerphoneOn(false); } Utilities.globalQueue.postRunnable(() -> soundPool.release()); @@ -404,7 +408,8 @@ public void onReceive(Context context, Intent intent) { } if (isHeadsetPlugged) { AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE); - if (am.isSpeakerphoneOn()) { + VoipAudioManager vam = VoipAudioManager.get(); + if (vam.isSpeakerphoneOn()) { previousAudioOutput = 0; } else if (am.isBluetoothScoOn()) { previousAudioOutput = 2; @@ -445,7 +450,8 @@ public void onReceive(Context context, Intent intent) { if (needSwitchToBluetoothAfterScoActivates) { needSwitchToBluetoothAfterScoActivates = false; AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE); - am.setSpeakerphoneOn(false); + VoipAudioManager vam = VoipAudioManager.get(); + vam.setSpeakerphoneOn(false); am.setBluetoothScoOn(true); } } @@ -469,6 +475,10 @@ public void onReceive(Context context, Intent intent) { } }; + public CountDownLatch getGroupCallBottomSheetLatch() { + return groupCallBottomSheetLatch; + } + public boolean isFrontFaceCamera() { return isFrontFaceCamera; } @@ -1717,8 +1727,14 @@ private void startGroupCall(int ssrc, String json, boolean create) { req.schedule_date = scheduleDate; req.flags |= 2; } + groupCallBottomSheetLatch = new CountDownLatch(1); ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> { if (response != null) { + try { + groupCallBottomSheetLatch.await(800, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + FileLog.e(e); + } TLRPC.Updates updates = (TLRPC.Updates) response; for (int a = 0; a < updates.updates.size(); a++) { TLRPC.Update update = updates.updates.get(a); @@ -2737,8 +2753,9 @@ public void toggleSpeakerphoneOrShowRouteSheet(Context context, boolean fromOver } } else if (audioConfigured && !USE_CONNECTION_SERVICE) { AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE); + VoipAudioManager vam = VoipAudioManager.get(); if (hasEarpiece()) { - am.setSpeakerphoneOn(!am.isSpeakerphoneOn()); + vam.setSpeakerphoneOn(!vam.isSpeakerphoneOn()); } else { am.setBluetoothScoOn(!am.isBluetoothScoOn()); } @@ -2756,6 +2773,7 @@ public void setAudioOutput(int which) { FileLog.d("setAudioOutput " + which); } AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE); + VoipAudioManager vam = VoipAudioManager.get(); if (USE_CONNECTION_SERVICE && systemCallConnection != null) { switch (which) { case 2: @@ -2780,7 +2798,7 @@ public void setAudioOutput(int which) { } } else { am.setBluetoothScoOn(true); - am.setSpeakerphoneOn(false); + vam.setSpeakerphoneOn(false); } audioRouteToSet = AUDIO_ROUTE_BLUETOOTH; break; @@ -2791,7 +2809,7 @@ public void setAudioOutput(int which) { bluetoothScoActive = false; bluetoothScoConnecting = false; } - am.setSpeakerphoneOn(false); + vam.setSpeakerphoneOn(false); am.setBluetoothScoOn(false); audioRouteToSet = AUDIO_ROUTE_EARPIECE; break; @@ -2803,7 +2821,7 @@ public void setAudioOutput(int which) { bluetoothScoConnecting = false; } am.setBluetoothScoOn(false); - am.setSpeakerphoneOn(true); + vam.setSpeakerphoneOn(true); audioRouteToSet = AUDIO_ROUTE_SPEAKER; break; } @@ -2835,7 +2853,8 @@ public boolean isSpeakerphoneOn() { return hasEarpiece() ? route == CallAudioState.ROUTE_SPEAKER : route == CallAudioState.ROUTE_BLUETOOTH; } else if (audioConfigured && !USE_CONNECTION_SERVICE) { AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE); - return hasEarpiece() ? am.isSpeakerphoneOn() : am.isBluetoothScoOn(); + VoipAudioManager vam = VoipAudioManager.get(); + return hasEarpiece() ? vam.isSpeakerphoneOn() : am.isBluetoothScoOn(); } return speakerphoneStateToSet; } @@ -2857,9 +2876,10 @@ public int getCurrentAudioRoute() { } if (audioConfigured) { AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE); + VoipAudioManager vam = VoipAudioManager.get(); if (am.isBluetoothScoOn()) { return AUDIO_ROUTE_BLUETOOTH; - } else if (am.isSpeakerphoneOn()) { + } else if (vam.isSpeakerphoneOn()) { return AUDIO_ROUTE_SPEAKER; } else { return AUDIO_ROUTE_EARPIECE; @@ -2880,10 +2900,12 @@ public long getCallDuration() { } public void stopRinging() { - if (ringtonePlayer != null) { - ringtonePlayer.stop(); - ringtonePlayer.release(); - ringtonePlayer = null; + synchronized (sync) { + if (ringtonePlayer != null) { + ringtonePlayer.stop(); + ringtonePlayer.release(); + ringtonePlayer = null; + } } if (vibrator != null) { vibrator.cancel(); @@ -2942,53 +2964,77 @@ private void startRingtoneAndVibration(long chatID) { AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE); boolean needRing = am.getRingerMode() != AudioManager.RINGER_MODE_SILENT; if (needRing) { - ringtonePlayer = new MediaPlayer(); - ringtonePlayer.setOnPreparedListener(mediaPlayer -> { + if (ringtonePlayer != null) { + return; + } + synchronized (sync) { + if (ringtonePlayer != null) { + return; + } + ringtonePlayer = new MediaPlayer(); + ringtonePlayer.setOnPreparedListener(mediaPlayer -> { + try { + ringtonePlayer.start(); + } catch (Throwable e) { + FileLog.e(e); + } + }); + ringtonePlayer.setLooping(true); + if (isHeadsetPlugged) { + ringtonePlayer.setAudioStreamType(AudioManager.STREAM_VOICE_CALL); + } else { + ringtonePlayer.setAudioStreamType(AudioManager.STREAM_RING); + if (!USE_CONNECTION_SERVICE) { + am.requestAudioFocus(this, AudioManager.STREAM_RING, AudioManager.AUDIOFOCUS_GAIN); + } + } try { - ringtonePlayer.start(); - } catch (Throwable e) { + String notificationUri; + if (prefs.getBoolean("custom_" + chatID, false)) { + notificationUri = prefs.getString("ringtone_path_" + chatID, null); + } else { + notificationUri = prefs.getString("CallsRingtonePath", null); + } + Uri ringtoneUri; + boolean isDafaultUri = false; + if (notificationUri == null) { + ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE); + isDafaultUri = true; + } else { + Uri defaultUri = Settings.System.DEFAULT_RINGTONE_URI; + if (defaultUri != null && notificationUri.equalsIgnoreCase(defaultUri.getPath())) { + ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE); + isDafaultUri = true; + } else { + ringtoneUri = Uri.parse(notificationUri); + } + } + FileLog.d("start ringtone with " + isDafaultUri + " " + ringtoneUri); + ringtonePlayer.setDataSource(this, ringtoneUri); + ringtonePlayer.prepareAsync(); + } catch (Exception e) { FileLog.e(e); + if (ringtonePlayer != null) { + ringtonePlayer.release(); + ringtonePlayer = null; + } } - }); - ringtonePlayer.setLooping(true); - if (isHeadsetPlugged) { - ringtonePlayer.setAudioStreamType(AudioManager.STREAM_VOICE_CALL); - } else { - ringtonePlayer.setAudioStreamType(AudioManager.STREAM_RING); - if (!USE_CONNECTION_SERVICE) { - am.requestAudioFocus(this, AudioManager.STREAM_RING, AudioManager.AUDIOFOCUS_GAIN); - } - } - try { - String notificationUri; + int vibrate; if (prefs.getBoolean("custom_" + chatID, false)) { - notificationUri = prefs.getString("ringtone_path_" + chatID, RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE).toString()); + vibrate = prefs.getInt("calls_vibrate_" + chatID, 0); } else { - notificationUri = prefs.getString("CallsRingtonePath", RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE).toString()); - } - ringtonePlayer.setDataSource(this, Uri.parse(notificationUri)); - ringtonePlayer.prepareAsync(); - } catch (Exception e) { - FileLog.e(e); - if (ringtonePlayer != null) { - ringtonePlayer.release(); - ringtonePlayer = null; - } - } - int vibrate; - if (prefs.getBoolean("custom_" + chatID, false)) { - vibrate = prefs.getInt("calls_vibrate_" + chatID, 0); - } else { - vibrate = prefs.getInt("vibrate_calls", 0); - } - if ((vibrate != 2 && vibrate != 4 && (am.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE || am.getRingerMode() == AudioManager.RINGER_MODE_NORMAL)) || (vibrate == 4 && am.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE)) { - long duration = 700; - if (vibrate == 1) { - duration /= 2; - } else if (vibrate == 3) { - duration *= 2; + vibrate = prefs.getInt("vibrate_calls", 0); + } + if ((vibrate != 2 && vibrate != 4 && (am.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE || am.getRingerMode() == AudioManager.RINGER_MODE_NORMAL)) || (vibrate == 4 && am.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE)) { + vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE); + long duration = 700; + if (vibrate == 1) { + duration /= 2; + } else if (vibrate == 3) { + duration *= 2; + } + VibrateUtil.vibrate(new long[]{0, duration, 500}, 0); } - VibrateUtil.vibrate(new long[]{0, duration, 500}, 0); } } } @@ -3068,11 +3114,12 @@ public void onDestroy() { cpuWakelock.release(); if (!playingSound) { AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE); + VoipAudioManager vam = VoipAudioManager.get(); if (!USE_CONNECTION_SERVICE) { if (isBtHeadsetConnected || bluetoothScoActive || bluetoothScoConnecting) { am.stopBluetoothSco(); am.setBluetoothScoOn(false); - am.setSpeakerphoneOn(false); + vam.setSpeakerphoneOn(false); bluetoothScoActive = false; bluetoothScoConnecting = false; } @@ -3352,7 +3399,7 @@ private void startRinging() { } dispatchStateChanged(STATE_WAITING_INCOMING); if (!notificationsDisabled && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - showIncomingNotification(ContactsController.formatName(user.first_name, user.last_name), null, user, privateCall.video, 0); + showIncomingNotification(ContactsController.formatName(user.first_name, user.last_name), user, privateCall.video, 0); if (BuildVars.LOGS_ENABLED) { FileLog.d("Showing incoming call notification"); } @@ -3681,6 +3728,7 @@ private void configureDeviceForCall() { } AndroidUtilities.runOnUIThread(() -> { am.requestAudioFocus(VoIPService.this, currentStreamType, AudioManager.AUDIOFOCUS_GAIN); + final VoipAudioManager vam = VoipAudioManager.get(); if (isBluetoothHeadsetConnected() && hasEarpiece()) { switch (audioRouteToSet) { case AUDIO_ROUTE_BLUETOOTH: @@ -3693,22 +3741,22 @@ private void configureDeviceForCall() { } } else { am.setBluetoothScoOn(true); - am.setSpeakerphoneOn(false); + vam.setSpeakerphoneOn(false); } break; case AUDIO_ROUTE_EARPIECE: am.setBluetoothScoOn(false); - am.setSpeakerphoneOn(false); + vam.setSpeakerphoneOn(false); break; case AUDIO_ROUTE_SPEAKER: am.setBluetoothScoOn(false); - am.setSpeakerphoneOn(true); + vam.setSpeakerphoneOn(true); break; } } else if (isBluetoothHeadsetConnected()) { am.setBluetoothScoOn(speakerphoneStateToSet); } else { - am.setSpeakerphoneOn(speakerphoneStateToSet); + vam.setSpeakerphoneOn(speakerphoneStateToSet); if (speakerphoneStateToSet) { audioRouteToSet = AUDIO_ROUTE_SPEAKER; } else { @@ -3756,7 +3804,8 @@ public void onSensorChanged(SensorEvent event) { } if (event.sensor.getType() == Sensor.TYPE_PROXIMITY) { AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE); - if (audioRouteToSet != AUDIO_ROUTE_EARPIECE || isHeadsetPlugged || am.isSpeakerphoneOn() || (isBluetoothHeadsetConnected() && am.isBluetoothScoOn())) { + VoipAudioManager vam = VoipAudioManager.get(); + if (audioRouteToSet != AUDIO_ROUTE_EARPIECE || isHeadsetPlugged || vam.isSpeakerphoneOn() || (isBluetoothHeadsetConnected() && am.isBluetoothScoOn())) { return; } boolean newIsNear = event.values[0] < Math.min(event.sensor.getMaximumRange(), 3); @@ -3991,14 +4040,13 @@ private Bitmap getRoundAvatarBitmap(TLObject userOrChat) { return bitmap; } - private void showIncomingNotification(String name, CharSequence subText, TLObject userOrChat, boolean video, int additionalMemberCount) { + private void showIncomingNotification(String name, TLObject userOrChat, boolean video, int additionalMemberCount) { Intent intent = new Intent(this, LaunchActivity.class); intent.setAction("voip"); + Notification.Builder builder = new Notification.Builder(this) .setContentTitle(video ? LocaleController.getString("VoipInVideoCallBranding", R.string.VoipInVideoCallBranding) : LocaleController.getString("VoipInCallBranding", R.string.VoipInCallBranding)) - .setContentText(name) .setSmallIcon(R.drawable.notification) - .setSubText(subText) .setContentIntent(PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_MUTABLE)); Uri soundProviderUri = Uri.parse("content://" + ApplicationLoader.getApplicationId() + ".call_sound_provider/start_ringing"); if (Build.VERSION.SDK_INT >= 26) { @@ -4050,22 +4098,20 @@ private void showIncomingNotification(String name, CharSequence subText, TLObjec endIntent.setAction(getPackageName() + ".DECLINE_CALL"); endIntent.putExtra("call_id", getCallID()); CharSequence endTitle = LocaleController.getString("VoipDeclineCall", R.string.VoipDeclineCall); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { endTitle = new SpannableString(endTitle); ((SpannableString) endTitle).setSpan(new ForegroundColorSpan(0xFFF44336), 0, endTitle.length(), 0); } PendingIntent endPendingIntent = PendingIntent.getBroadcast(this, 0, endIntent, PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_CANCEL_CURRENT); - builder.addAction(R.drawable.ic_call_end_white_24dp, endTitle, endPendingIntent); Intent answerIntent = new Intent(this, VoIPActionsReceiver.class); answerIntent.setAction(getPackageName() + ".ANSWER_CALL"); answerIntent.putExtra("call_id", getCallID()); CharSequence answerTitle = LocaleController.getString("VoipAnswerCall", R.string.VoipAnswerCall); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { answerTitle = new SpannableString(answerTitle); ((SpannableString) answerTitle).setSpan(new ForegroundColorSpan(0xFF00AA00), 0, answerTitle.length(), 0); } PendingIntent answerPendingIntent = PendingIntent.getBroadcast(this, 0, answerIntent, PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_CANCEL_CURRENT); - builder.addAction(R.drawable.ic_call, answerTitle, answerPendingIntent); builder.setPriority(Notification.PRIORITY_MAX); builder.setShowWhen(false); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { @@ -4080,27 +4126,34 @@ private void showIncomingNotification(String name, CharSequence subText, TLObjec } } } - Notification incomingNotification = builder.getNotification(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + Notification incomingNotification; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + Bitmap avatar = getRoundAvatarBitmap(userOrChat); + String presonName = ContactsController.formatName(userOrChat); + if (TextUtils.isEmpty(presonName)) { + //java.lang.IllegalArgumentException: person must have a non-empty a name + presonName = "___"; + } + Person person = new Person.Builder() + .setName(presonName) + .setIcon(Icon.createWithAdaptiveBitmap(avatar)).build(); + Notification.CallStyle notificationStyle = Notification.CallStyle.forIncomingCall(person, endPendingIntent, answerPendingIntent); + + builder.setStyle(notificationStyle); + incomingNotification = builder.build(); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ) { + builder.addAction(R.drawable.ic_call_end_white_24dp, endTitle, endPendingIntent); + builder.addAction(R.drawable.ic_call, answerTitle, answerPendingIntent); + builder.setContentText(name); + RemoteViews customView = new RemoteViews(getPackageName(), LocaleController.isRTL ? R.layout.call_notification_rtl : R.layout.call_notification); customView.setTextViewText(R.id.name, name); - boolean subtitleVisible = true; - if (TextUtils.isEmpty(subText)) { - customView.setViewVisibility(R.id.subtitle, View.GONE); - if (UserConfig.getActivatedAccountsCount() > 1) { - TLRPC.User self = UserConfig.getInstance(currentAccount).getCurrentUser(); - customView.setTextViewText(R.id.title, video ? LocaleController.formatString("VoipInVideoCallBrandingWithName", R.string.VoipInVideoCallBrandingWithName, ContactsController.formatName(self.first_name, self.last_name)) : LocaleController.formatString("VoipInCallBrandingWithName", R.string.VoipInCallBrandingWithName, ContactsController.formatName(self.first_name, self.last_name))); - } else { - customView.setTextViewText(R.id.title, video ? LocaleController.getString("VoipInVideoCallBranding", R.string.VoipInVideoCallBranding) : LocaleController.getString("VoipInCallBranding", R.string.VoipInCallBranding)); - } + customView.setViewVisibility(R.id.subtitle, View.GONE); + if (UserConfig.getActivatedAccountsCount() > 1) { + TLRPC.User self = UserConfig.getInstance(currentAccount).getCurrentUser(); + customView.setTextViewText(R.id.title, video ? LocaleController.formatString("VoipInVideoCallBrandingWithName", R.string.VoipInVideoCallBrandingWithName, ContactsController.formatName(self.first_name, self.last_name)) : LocaleController.formatString("VoipInCallBrandingWithName", R.string.VoipInCallBrandingWithName, ContactsController.formatName(self.first_name, self.last_name))); } else { - if (UserConfig.getActivatedAccountsCount() > 1) { - TLRPC.User self = UserConfig.getInstance(currentAccount).getCurrentUser(); - customView.setTextViewText(R.id.subtitle, LocaleController.formatString("VoipAnsweringAsAccount", R.string.VoipAnsweringAsAccount, ContactsController.formatName(self.first_name, self.last_name))); - } else { - customView.setViewVisibility(R.id.subtitle, View.GONE); - } - customView.setTextViewText(R.id.title, subText); + customView.setTextViewText(R.id.title, video ? LocaleController.getString("VoipInVideoCallBranding", R.string.VoipInVideoCallBranding) : LocaleController.getString("VoipInCallBranding", R.string.VoipInCallBranding)); } Bitmap avatar = getRoundAvatarBitmap(userOrChat); customView.setTextViewText(R.id.answer_text, LocaleController.getString("VoipAnswerCall", R.string.VoipAnswerCall)); @@ -4110,13 +4163,13 @@ private void showIncomingNotification(String name, CharSequence subText, TLObjec customView.setOnClickPendingIntent(R.id.decline_btn, endPendingIntent); builder.setLargeIcon(avatar); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - builder.setColor(0xFF282e31); - builder.setColorized(true); - builder.setCustomBigContentView(customView); - } else { - incomingNotification.headsUpContentView = incomingNotification.bigContentView = customView; - } + incomingNotification = builder.getNotification(); + incomingNotification.headsUpContentView = incomingNotification.bigContentView = customView; + } else { + builder.setContentText(name); + builder.addAction(R.drawable.ic_call_end_white_24dp, endTitle, endPendingIntent); + builder.addAction(R.drawable.ic_call, answerTitle, answerPendingIntent); + incomingNotification = builder.getNotification(); } startForeground(ID_INCOMING_CALL_NOTIFICATION, incomingNotification); startRingtoneAndVibration(); @@ -4380,8 +4433,9 @@ public void updateOutputGainControlState() { if (tgVoip[CAPTURE_DEVICE_CAMERA] != null) { if (!USE_CONNECTION_SERVICE) { final AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE); - tgVoip[CAPTURE_DEVICE_CAMERA].setAudioOutputGainControlEnabled(hasEarpiece() && !am.isSpeakerphoneOn() && !am.isBluetoothScoOn() && !isHeadsetPlugged); - tgVoip[CAPTURE_DEVICE_CAMERA].setEchoCancellationStrength(isHeadsetPlugged || (hasEarpiece() && !am.isSpeakerphoneOn() && !am.isBluetoothScoOn() && !isHeadsetPlugged) ? 0 : 1); + boolean isSpeakerPhoneOn = VoipAudioManager.get().isSpeakerphoneOn(); + tgVoip[CAPTURE_DEVICE_CAMERA].setAudioOutputGainControlEnabled(hasEarpiece() && !isSpeakerPhoneOn && !am.isBluetoothScoOn() && !isHeadsetPlugged); + tgVoip[CAPTURE_DEVICE_CAMERA].setEchoCancellationStrength(isHeadsetPlugged || (hasEarpiece() && !isSpeakerPhoneOn && !am.isBluetoothScoOn() && !isHeadsetPlugged) ? 0 : 1); } else { final boolean isEarpiece = systemCallConnection.getCallAudioState().getRoute() == CallAudioState.ROUTE_EARPIECE; tgVoip[CAPTURE_DEVICE_CAMERA].setAudioOutputGainControlEnabled(isEarpiece); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoipAudioManager.java b/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoipAudioManager.java new file mode 100644 index 0000000000..e96edb73ed --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoipAudioManager.java @@ -0,0 +1,53 @@ +package org.telegram.messenger.voip; + +import static android.content.Context.AUDIO_SERVICE; + +import android.media.AudioManager; + +import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.Utilities; + +public class VoipAudioManager { + + private Boolean isSpeakerphoneOn; + + private VoipAudioManager() { + + } + + private static final class InstanceHolder { + static final VoipAudioManager instance = new VoipAudioManager(); + } + + public static VoipAudioManager get() { + return InstanceHolder.instance; + } + + /** + * Sets the speakerphone on or off asynchronously. + * On Samsung devices {@link AudioManager#setSpeakerphoneOn} and {@link AudioManager#isSpeakerphoneOn} take too much time. + */ + public void setSpeakerphoneOn(boolean on) { + isSpeakerphoneOn = on; + final AudioManager audioManager = getAudioManager(); + Utilities.globalQueue.postRunnable(() -> { + audioManager.setSpeakerphoneOn(on); + }); + } + + /** + * Checks whether the speakerphone is on or off. + * {@link AudioManager#isSpeakerphoneOn} is fast if {@link AudioManager#setSpeakerphoneOn} has not been called before. + */ + public boolean isSpeakerphoneOn() { + if (isSpeakerphoneOn == null) { + AudioManager audioManager = getAudioManager(); + return audioManager.isSpeakerphoneOn(); + } + return isSpeakerphoneOn; + } + + private AudioManager getAudioManager() { + return (AudioManager) ApplicationLoader.applicationContext.getSystemService(AUDIO_SERVICE); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/tgnet/AbstractSerializedData.java b/TMessagesProj/src/main/java/org/telegram/tgnet/AbstractSerializedData.java index 5ab77520e4..ff252fda7f 100644 --- a/TMessagesProj/src/main/java/org/telegram/tgnet/AbstractSerializedData.java +++ b/TMessagesProj/src/main/java/org/telegram/tgnet/AbstractSerializedData.java @@ -26,12 +26,16 @@ public abstract class AbstractSerializedData { public abstract void writeByteBuffer(NativeByteBuffer buffer); + public abstract void writeFloat(float f); + public abstract int readInt32(boolean exception); public abstract boolean readBool(boolean exception); public abstract long readInt64(boolean exception); + public abstract byte readByte(boolean exception); + public abstract void readBytes(byte[] b, boolean exception); public abstract byte[] readData(int count, boolean exception); @@ -40,6 +44,8 @@ public abstract class AbstractSerializedData { public abstract byte[] readByteArray(boolean exception); + public abstract float readFloat(boolean exception); + public abstract NativeByteBuffer readByteBuffer(boolean exception); public abstract double readDouble(boolean exception); diff --git a/TMessagesProj/src/main/java/org/telegram/tgnet/ConnectionsManager.java b/TMessagesProj/src/main/java/org/telegram/tgnet/ConnectionsManager.java index 1d824044d8..83b22ef66d 100644 --- a/TMessagesProj/src/main/java/org/telegram/tgnet/ConnectionsManager.java +++ b/TMessagesProj/src/main/java/org/telegram/tgnet/ConnectionsManager.java @@ -140,6 +140,18 @@ public void setForceTryIpV6(boolean forceTryIpV6) { } } + public void discardConnection(int dcId, int connectionType) { + Utilities.stageQueue.postRunnable(() -> { + native_discardConnection(currentAccount, dcId, connectionType); + }); + } + + public void failNotRunningRequest(int requestToken) { + Utilities.stageQueue.postRunnable(() -> { + native_failNotRunningRequest(currentAccount, requestToken); + }); + } + private static class ResolvedDomain { public InetAddress[] addresses; @@ -359,7 +371,7 @@ private void sendRequestInternal(TLObject object, RequestDelegate onComplete, Re error = new TLRPC.TL_error(); error.code = errorCode; error.text = errorText; - if (BuildVars.LOGS_ENABLED) { + if (BuildVars.LOGS_ENABLED && error.code != -2000) { FileLog.e(object + " got error " + error.code + " " + error.text); } } @@ -867,6 +879,9 @@ public static void setProxySettings(boolean enabled, String address, int port, S public static native void native_onHostNameResolved(String host, long address, String ip, boolean ipv6); + public static native void native_discardConnection(int currentAccount, int datacenterId, int connectionType); + public static native void native_failNotRunningRequest(int currentAccount, int token); + public static int generateClassGuid() { return lastClassGuid++; } diff --git a/TMessagesProj/src/main/java/org/telegram/tgnet/NativeByteBuffer.java b/TMessagesProj/src/main/java/org/telegram/tgnet/NativeByteBuffer.java index e5152723a2..bc885e7b9d 100644 --- a/TMessagesProj/src/main/java/org/telegram/tgnet/NativeByteBuffer.java +++ b/TMessagesProj/src/main/java/org/telegram/tgnet/NativeByteBuffer.java @@ -136,6 +136,21 @@ public void writeInt64(long x) { } } + public void writeFloat(float f) { + try { + if (!justCalc) { + buffer.putInt(Float.floatToIntBits(f)); + } else { + len += 4; + } + } catch (Exception e) { + if (BuildVars.LOGS_ENABLED) { + FileLog.e("write float error"); + FileLog.e(e); + } + } + } + public void writeBool(boolean value) { if (!justCalc) { if (value) { @@ -380,6 +395,22 @@ public int getPosition() { return buffer.position(); } + public byte readByte(boolean exception) { + try { + return buffer.get(); + } catch (Exception e) { + if (exception) { + throw new RuntimeException("read byte error", e); + } else { + if (BuildVars.LOGS_ENABLED) { + FileLog.e("read byte error"); + FileLog.e(e); + } + } + } + return 0; + } + public int readInt32(boolean exception) { try { return buffer.getInt(); @@ -396,6 +427,22 @@ public int readInt32(boolean exception) { return 0; } + public float readFloat(boolean exception) { + try { + return Float.intBitsToFloat(buffer.getInt()); + } catch (Exception e) { + if (exception) { + throw new RuntimeException("read float error", e); + } else { + if (BuildVars.LOGS_ENABLED) { + FileLog.e("read float error"); + FileLog.e(e); + } + } + } + return 0; + } + public boolean readBool(boolean exception) { int consructor = readInt32(exception); if (consructor == 0x997275b5) { diff --git a/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java b/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java index a5bac370b0..aaa7c1be70 100644 --- a/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java +++ b/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java @@ -8,6 +8,7 @@ package org.telegram.tgnet; +import android.graphics.Bitmap; import android.graphics.Path; import android.graphics.drawable.BitmapDrawable; import android.os.Build; @@ -23,6 +24,8 @@ import org.telegram.messenger.MessageObject; import org.telegram.messenger.SvgHelper; import org.telegram.messenger.Utilities; +import org.telegram.ui.Stories.MessageMediaStoryFull; +import org.telegram.ui.Stories.MessageMediaStoryFull_old; import java.util.ArrayList; import java.util.HashMap; @@ -76,7 +79,7 @@ public class TLRPC { public static final int MESSAGE_FLAG_HAS_BOT_ID = 0x00000800; public static final int MESSAGE_FLAG_EDITED = 0x00008000; - public static final int LAYER = 157; + public static final int LAYER = 160; public static class TL_stats_megagroupStats extends TLObject { public static int constructor = 0xef7ff916; @@ -1634,6 +1637,425 @@ public void serializeToStream(AbstractSerializedData stream) { } } + public static class TL_inputChatlistDialogFilter extends TLObject { + public static int constructor = 0xf3e0da33; + + public int filter_id; + + public static TL_inputChatlistDialogFilter TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + if (TL_inputChatlistDialogFilter.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_inputChatlistDialogFilter", constructor)); + } else { + return null; + } + } + TL_inputChatlistDialogFilter result = new TL_inputChatlistDialogFilter(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbstractSerializedData stream, boolean exception) { + filter_id = stream.readInt32(exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(filter_id); + } + } + + public static class TL_chatlists_exportedChatlistInvite extends TLObject { + public static int constructor = 0x10e6e3a6; + + public DialogFilter filter; + public TL_exportedChatlistInvite invite; + + public static TL_chatlists_exportedChatlistInvite TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + if (TL_chatlists_exportedChatlistInvite.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_chatlists_exportedChatlistInvite", constructor)); + } else { + return null; + } + } + TL_chatlists_exportedChatlistInvite result = new TL_chatlists_exportedChatlistInvite(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbstractSerializedData stream, boolean exception) { + filter = DialogFilter.TLdeserialize(stream, stream.readInt32(exception), exception); + invite = TL_exportedChatlistInvite.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + filter.serializeToStream(stream); + invite.serializeToStream(stream); + } + } + + public static class TL_exportedChatlistInvite extends TLObject { + public static int constructor = 0xc5181ac; + + public int flags; + public boolean revoked; + public String title; + public String url; + public ArrayList peers = new ArrayList<>(); + + public static TL_exportedChatlistInvite TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + if (TL_exportedChatlistInvite.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_exportedChatlistInvite", constructor)); + } else { + return null; + } + } + TL_exportedChatlistInvite result = new TL_exportedChatlistInvite(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + revoked = (flags & 1) != 0; + title = stream.readString(exception); + url = stream.readString(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Peer object = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + peers.add(object); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = revoked ? (flags | 1) : (flags & ~1); + stream.writeInt32(flags); + stream.writeString(title); + stream.writeString(url); + stream.writeInt32(0x1cb5c415); + int count = peers.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + peers.get(a).serializeToStream(stream); + } + } + } + + public static class TL_chatlists_exportedInvites extends TLObject { + public static int constructor = 0x10ab6dc7; + + public ArrayList invites = new ArrayList<>(); + public ArrayList chats = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + + public static TL_chatlists_exportedInvites TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + if (TL_chatlists_exportedInvites.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_chatlists_exportedInvites", constructor)); + } else { + return null; + } + } + TL_chatlists_exportedInvites result = new TL_chatlists_exportedInvites(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbstractSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_exportedChatlistInvite object = TL_exportedChatlistInvite.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + invites.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Chat object = Chat.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + chats.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = invites.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + invites.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = chats.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + chats.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); + } + } + } + + public static abstract class chatlist_ChatlistInvite extends TLObject { + + public static chatlist_ChatlistInvite TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + chatlist_ChatlistInvite result = null; + switch (constructor) { + case 0xfa87f659: + result = new TL_chatlists_chatlistInviteAlready(); + break; + case 0x1dcd839d: + result = new TL_chatlists_chatlistInvite(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in chatlist_ChatlistInvite", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_chatlists_chatlistInviteAlready extends chatlist_ChatlistInvite { + public static int constructor = 0xfa87f659; + + public int filter_id; + public ArrayList missing_peers = new ArrayList<>(); + public ArrayList already_peers = new ArrayList<>(); + public ArrayList chats = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + + public void readParams(AbstractSerializedData stream, boolean exception) { + filter_id = stream.readInt32(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Peer object = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + missing_peers.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Peer object = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + already_peers.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Chat object = Chat.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + chats.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(filter_id); + stream.writeInt32(0x1cb5c415); + int count = missing_peers.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + missing_peers.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = chats.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + chats.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); + } + } + } + + public static class TL_chatlists_chatlistInvite extends chatlist_ChatlistInvite { + public static int constructor = 0x1dcd839d; + + public int flags; + public String title; + public String emoticon; + public ArrayList peers = new ArrayList<>(); + public ArrayList chats = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + title = stream.readString(exception); + if ((flags & 1) > 0) { + emoticon = stream.readString(exception); + } + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Peer object = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + peers.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Chat object = Chat.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + chats.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(flags); + stream.writeString(title); + if ((flags & 1) > 0) { + stream.writeString(emoticon); + } + stream.writeInt32(0x1cb5c415); + int count = peers.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + peers.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = chats.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + chats.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); + } + } + } + public static class TL_account_passwordSettings extends TLObject { public static int constructor = 0x9a5c33e5; @@ -1679,7 +2101,7 @@ public void serializeToStream(AbstractSerializedData stream) { public static abstract class DocumentAttribute extends TLObject { public String alt; public InputStickerSet stickerset; - public int duration; + public double duration; public int flags; public TL_maskCoords mask_coords; public boolean round_message; @@ -1692,6 +2114,8 @@ public static abstract class DocumentAttribute extends TLObject { public String performer; public boolean voice; public byte[] waveform; + public int preload_prefix_size; + public boolean nosound; public static DocumentAttribute TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { DocumentAttribute result = null; @@ -1699,6 +2123,9 @@ public static DocumentAttribute TLdeserialize(AbstractSerializedData stream, int case 0x3a556302: result = new TL_documentAttributeSticker_layer55(); break; + case 0xef02ce6: + result = new TL_documentAttributeVideo_layer159(); + break; case 0x51448e5: result = new TL_documentAttributeAudio_old(); break; @@ -1711,7 +2138,7 @@ public static DocumentAttribute TLdeserialize(AbstractSerializedData stream, int case 0x15590068: result = new TL_documentAttributeFilename(); break; - case 0xef02ce6: + case 0xd38ff1c2: result = new TL_documentAttributeVideo(); break; case 0x5910cccb: @@ -1775,7 +2202,7 @@ public void readParams(AbstractSerializedData stream, boolean exception) { public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt32(duration); + stream.writeInt32((int) duration); } } @@ -1829,6 +2256,37 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_documentAttributeVideo extends DocumentAttribute { + public static int constructor = 0xd38ff1c2; + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + round_message = (flags & 1) != 0; + supports_streaming = (flags & 2) != 0; + nosound = (flags & 8) != 0; + duration = stream.readDouble(exception); + w = stream.readInt32(exception); + h = stream.readInt32(exception); + if ((flags & 4) != 0) { + preload_prefix_size = stream.readInt32(exception); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = round_message ? (flags | 1) : (flags &~ 1); + flags = supports_streaming ? (flags | 2) : (flags &~ 2); + flags = nosound ? (flags | 8) : (flags &~ 8); + stream.writeInt32(flags); + stream.writeDouble(duration); + stream.writeInt32(w); + stream.writeInt32(h); + if ((flags & 4) != 0) { + stream.writeInt32(preload_prefix_size); + } + } + } + + public static class TL_documentAttributeVideo_layer159 extends TL_documentAttributeVideo { public static int constructor = 0xef02ce6; public void readParams(AbstractSerializedData stream, boolean exception) { @@ -1845,7 +2303,7 @@ public void serializeToStream(AbstractSerializedData stream) { flags = round_message ? (flags | 1) : (flags &~ 1); flags = supports_streaming ? (flags | 2) : (flags &~ 2); stream.writeInt32(flags); - stream.writeInt32(duration); + stream.writeInt32((int) duration); stream.writeInt32(w); stream.writeInt32(h); } @@ -1863,7 +2321,7 @@ public void readParams(AbstractSerializedData stream, boolean exception) { public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt32(duration); + stream.writeInt32((int) duration); stream.writeInt32(w); stream.writeInt32(h); } @@ -1881,7 +2339,7 @@ public void readParams(AbstractSerializedData stream, boolean exception) { public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt32(duration); + stream.writeInt32((int) duration); stream.writeString(title); stream.writeString(performer); } @@ -1961,7 +2419,7 @@ public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); flags = voice ? (flags | 1024) : (flags &~ 1024); stream.writeInt32(flags); - stream.writeInt32(duration); + stream.writeInt32((int) duration); if ((flags & 1) != 0) { stream.writeString(title); } @@ -4473,7 +4931,7 @@ public static abstract class PollResults extends TLObject { public boolean min; public ArrayList results = new ArrayList<>(); public int total_voters; - public ArrayList recent_voters = new ArrayList<>(); + public ArrayList recent_voters = new ArrayList<>(); public String solution; public ArrayList solution_entities = new ArrayList<>(); @@ -4490,6 +4948,9 @@ public static PollResults TLdeserialize(AbstractSerializedData stream, int const result = new TL_pollResults_layer131(); break; case 0xdcb82ea3: + result = new TL_pollResults_layer158(); + break; + case 0x7adf2420: result = new TL_pollResults(); break; } @@ -4587,7 +5048,9 @@ public void readParams(AbstractSerializedData stream, boolean exception) { } int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - recent_voters.add((long) stream.readInt32(exception)); + TL_peerUser user = new TL_peerUser(); + user.user_id = (long) stream.readInt32(exception); + recent_voters.add(user); } } } @@ -4612,7 +5075,7 @@ public void serializeToStream(AbstractSerializedData stream) { int count = recent_voters.size(); stream.writeInt32(count); for (int a = 0; a < count; a++) { - stream.writeInt32((int) (long) recent_voters.get(a)); + stream.writeInt32((int) (long) recent_voters.get(a).user_id); } } } @@ -4655,7 +5118,9 @@ public void readParams(AbstractSerializedData stream, boolean exception) { } int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - recent_voters.add((long) stream.readInt32(exception)); + TL_peerUser user = new TL_peerUser(); + user.user_id = (long) stream.readInt32(exception); + recent_voters.add(user); } } if ((flags & 16) != 0) { @@ -4700,7 +5165,7 @@ public void serializeToStream(AbstractSerializedData stream) { int count = recent_voters.size(); stream.writeInt32(count); for (int a = 0; a < count; a++) { - stream.writeInt32((int) (long) recent_voters.get(a)); + stream.writeInt32((int) (long) recent_voters.get(a).user_id); } } if ((flags & 16) != 0) { @@ -4718,6 +5183,108 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_pollResults extends PollResults { + public static int constructor = 0x7adf2420; + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + min = (flags & 1) != 0; + if ((flags & 2) != 0) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_pollAnswerVoters object = TL_pollAnswerVoters.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + results.add(object); + } + } + if ((flags & 4) != 0) { + total_voters = stream.readInt32(exception); + } + if ((flags & 8) != 0) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Peer object = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + recent_voters.add(object); + } + } + if ((flags & 16) != 0) { + solution = stream.readString(exception); + } + if ((flags & 16) != 0) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + MessageEntity object = MessageEntity.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + solution_entities.add(object); + } + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = min ? (flags | 1) : (flags &~ 1); + stream.writeInt32(flags); + if ((flags & 2) != 0) { + stream.writeInt32(0x1cb5c415); + int count = results.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + results.get(a).serializeToStream(stream); + } + } + if ((flags & 4) != 0) { + stream.writeInt32(total_voters); + } + if ((flags & 8) != 0) { + stream.writeInt32(0x1cb5c415); + int count = recent_voters.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + recent_voters.get(a).serializeToStream(stream); + } + } + if ((flags & 16) != 0) { + stream.writeString(solution); + } + if ((flags & 16) != 0) { + stream.writeInt32(0x1cb5c415); + int count = solution_entities.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + solution_entities.get(a).serializeToStream(stream); + } + } + } + } + + public static class TL_pollResults_layer158 extends PollResults { public static int constructor = 0xdcb82ea3; @@ -4754,7 +5321,9 @@ public void readParams(AbstractSerializedData stream, boolean exception) { } int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - recent_voters.add(stream.readInt64(exception)); + TL_peerUser user = new TL_peerUser(); + user.user_id = stream.readInt64(exception); + recent_voters.add(user); } } if ((flags & 16) != 0) { @@ -4799,7 +5368,7 @@ public void serializeToStream(AbstractSerializedData stream) { int count = recent_voters.size(); stream.writeInt32(count); for (int a = 0; a < count; a++) { - stream.writeInt64(recent_voters.get(a)); + stream.writeInt64(recent_voters.get(a).user_id); } } if ((flags & 16) != 0) { @@ -5639,6 +6208,9 @@ public static PrivacyKey TLdeserialize(AbstractSerializedData stream, int constr case 0x697f414: result = new TL_privacyKeyVoiceMessages(); break; + case 0xa486b761: + result = new TL_privacyKeyAbout(); + break; case 0x3d662b7b: result = new TL_privacyKeyPhoneCall(); break; @@ -5707,6 +6279,15 @@ public void serializeToStream(AbstractSerializedData stream) { } } + public static class TL_privacyKeyAbout extends PrivacyKey { + public static int constructor = 0xa486b761; + + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + } + } + public static class TL_privacyKeyVoiceMessages extends PrivacyKey { public static int constructor = 0x697f414; @@ -6460,6 +7041,7 @@ public static class TL_chatInviteImporter extends TLObject { public int date; public String about; public long approved_by; + public boolean via_chatlist; public static TL_chatInviteImporter TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { if (TL_chatInviteImporter.constructor != constructor) { @@ -6477,6 +7059,7 @@ public static TL_chatInviteImporter TLdeserialize(AbstractSerializedData stream, public void readParams(AbstractSerializedData stream, boolean exception) { flags = stream.readInt32(exception); requested = (flags & 1) != 0; + via_chatlist = (flags & 8) != 0; user_id = stream.readInt64(exception); date = stream.readInt32(exception); if ((flags & 4) != 0) { @@ -6490,6 +7073,7 @@ public void readParams(AbstractSerializedData stream, boolean exception) { public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); flags = requested ? (flags | 1) : (flags &~ 1); + flags = via_chatlist ? (flags | 8) : (flags &~ 8); stream.writeInt32(flags); stream.writeInt64(user_id); stream.writeInt32(date); @@ -7756,6 +8340,9 @@ public static PrivacyRule TLdeserialize(AbstractSerializedData stream, int const case 0xfffe1bac: result = new TL_privacyValueAllowContacts(); break; + case 0xf7e8d89b: + result = new TL_privacyValueAllowCloseFriends(); + break; } if (result == null && exception) { throw new RuntimeException(String.format("can't parse magic %x in PrivacyRule", constructor)); @@ -7923,6 +8510,15 @@ public void serializeToStream(AbstractSerializedData stream) { } } + public static class TL_privacyValueAllowCloseFriends extends PrivacyRule { + public static int constructor = 0xf7e8d89b; + + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + } + } + public static class TL_pageTableCell extends TLObject { public static int constructor = 0x34566b6a; @@ -8277,6 +8873,42 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_messageMediaDocument extends MessageMedia { + public static int constructor = 0x4cf4d72d; + + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + nopremium = (flags & 8) != 0; + spoiler = (flags & 16) != 0; + if ((flags & 1) != 0) { + document = Document.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 32) != 0) { + alt_document = Document.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 4) != 0) { + ttl_seconds = stream.readInt32(exception); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = nopremium ? (flags | 8) : (flags &~ 8); + flags = spoiler ? (flags | 16) : (flags &~ 16); + stream.writeInt32(flags); + if ((flags & 1) != 0) { + document.serializeToStream(stream); + } + if ((flags & 32) != 0) { + alt_document.serializeToStream(stream); + } + if ((flags & 4) != 0) { + stream.writeInt32(ttl_seconds); + } + } + } + + public static class TL_messageMediaDocument_layer159 extends TL_messageMediaDocument { public static int constructor = 0x9cb070d7; @@ -9120,13 +9752,21 @@ public static abstract class PeerNotifySettings extends TLObject { public NotificationSound ios_sound; public NotificationSound android_sound; public NotificationSound other_sound; + public boolean stories_muted; + public boolean stories_hide_sender; + public NotificationSound stories_ios_sound; + public NotificationSound stories_android_sound; + public NotificationSound stories_other_sound; public static PeerNotifySettings TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { PeerNotifySettings result = null; switch (constructor) { - case 0xa83b0426: + case 0x99622c0c: result = new TL_peerNotifySettings(); break; + case 0xa83b0426: + result = new TL_peerNotifySettings_layer156(); + break; case 0x9acda4c0: result = new TL_peerNotifySettings_layer77(); break; @@ -9172,6 +9812,85 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_peerNotifySettings extends PeerNotifySettings { + public static int constructor = 0x99622c0c; + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + if ((flags & 1) != 0) { + show_previews = stream.readBool(exception); + } + if ((flags & 2) != 0) { + silent = stream.readBool(exception); + } + if ((flags & 4) != 0) { + mute_until = stream.readInt32(exception); + } + if ((flags & 8) != 0) { + ios_sound = NotificationSound.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 16) != 0) { + android_sound = NotificationSound.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 32) != 0) { + other_sound = NotificationSound.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 64) != 0) { + stories_muted = stream.readBool(exception); + } + if ((flags & 128) != 0) { + stories_hide_sender = stream.readBool(exception); + } + if ((flags & 256) != 0) { + stories_ios_sound = NotificationSound.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 512) != 0) { + stories_android_sound = NotificationSound.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 1024) != 0) { + stories_other_sound = NotificationSound.TLdeserialize(stream, stream.readInt32(exception), exception); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(flags); + if ((flags & 1) != 0) { + stream.writeBool(show_previews); + } + if ((flags & 2) != 0) { + stream.writeBool(silent); + } + if ((flags & 4) != 0) { + stream.writeInt32(mute_until); + } + if ((flags & 8) != 0) { + ios_sound.serializeToStream(stream); + } + if ((flags & 16) != 0) { + android_sound.serializeToStream(stream); + } + if ((flags & 32) != 0) { + other_sound.serializeToStream(stream); + } + if ((flags & 64) != 0) { + stream.writeBool(stories_muted); + } + if ((flags & 128) != 0) { + stream.writeBool(stories_hide_sender); + } + if ((flags & 256) != 0) { + stories_ios_sound.serializeToStream(stream); + } + if ((flags & 512) != 0) { + stories_android_sound.serializeToStream(stream); + } + if ((flags & 1024) != 0) { + stories_other_sound.serializeToStream(stream); + } + } + } + + public static class TL_peerNotifySettings_layer156 extends TL_peerNotifySettings { public static int constructor = 0xa83b0426; public void readParams(AbstractSerializedData stream, boolean exception) { @@ -9691,10 +10410,12 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_globalPrivacySettings extends TLObject { - public static int constructor = 0xbea2f424; + public static int constructor = 0x734c4ccb; public int flags; public boolean archive_and_mute_new_noncontact_peers; + public boolean keep_archived_unmuted; + public boolean keep_archived_folders; public static TL_globalPrivacySettings TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { if (TL_globalPrivacySettings.constructor != constructor) { @@ -9711,17 +10432,17 @@ public static TL_globalPrivacySettings TLdeserialize(AbstractSerializedData stre public void readParams(AbstractSerializedData stream, boolean exception) { flags = stream.readInt32(exception); - if ((flags & 1) != 0) { - archive_and_mute_new_noncontact_peers = stream.readBool(exception); - } + archive_and_mute_new_noncontact_peers = (flags & 1) != 0; + keep_archived_unmuted = (flags & 2) != 0; + keep_archived_folders = (flags & 4) != 0; } public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); + flags = archive_and_mute_new_noncontact_peers ? (flags | 1) : (flags &~ 1); + flags = keep_archived_unmuted ? (flags | 2) : (flags &~ 2); + flags = keep_archived_folders ? (flags | 4) : (flags &~ 4); stream.writeInt32(flags); - if ((flags & 1) != 0) { - stream.writeBool(archive_and_mute_new_noncontact_peers); - } } } @@ -10738,6 +11459,101 @@ public void serializeToStream(AbstractSerializedData stream) { } } + public static class TL_bots_setBotInfo extends TLObject { + public static int constructor = 0x10cf3123; + + public int flags; + public InputUser bot; + public String lang_code; + public String name; + public String about; + public String description; + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(flags); + if ((flags & 4) != 0) { + bot.serializeToStream(stream); + } + stream.writeString(lang_code); + if ((flags & 8) != 0) { + stream.writeString(name); + } + if ((flags & 1) != 0) { + stream.writeString(about); + } + if ((flags & 2) != 0) { + stream.writeString(description); + } + } + } + + public static class TL_bots_getBotInfo extends TLObject { + public static int constructor = 0xdcd914fd; + + public int flags; + public InputUser bot; + public String lang_code; + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return BotInfo.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(flags); + if ((flags & 1) != 0) { + bot.serializeToStream(stream); + } + stream.writeString(lang_code); + } + } + + public static class TL_bots_reorderUsernames extends TLObject { + public static int constructor = 0x9709b1c2; + + public InputUser bot; + public ArrayList order = new ArrayList<>(); + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + bot.serializeToStream(stream); + stream.writeInt32(0x1cb5c415); + int count = order.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeString(order.get(a)); + } + } + } + + public static class TL_bots_toggleUsername extends TLObject { + public static int constructor = 0x53ca973; + + public InputUser bot; + public String username; + public boolean active; + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + bot.serializeToStream(stream); + stream.writeString(username); + stream.writeBool(active); + } + } + public static abstract class BotInfo extends TLObject { public long user_id; public String description; @@ -11693,26 +12509,67 @@ public void serializeToStream(AbstractSerializedData stream) { } } } - - public static class TL_webPageAttributeTheme extends TLObject { - public static int constructor = 0x54b56617; - + + public static class WebPageAttribute extends TLObject { public int flags; - public ArrayList documents = new ArrayList<>(); - public ThemeSettings settings; - public static TL_webPageAttributeTheme TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { - if (TL_webPageAttributeTheme.constructor != constructor) { - if (exception) { - throw new RuntimeException(String.format("can't parse magic %x in TL_webPageAttributeTheme", constructor)); - } else { - return null; - } + public static WebPageAttribute TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + WebPageAttribute result = null; + switch (constructor) { + case TL_webPageAttributeTheme.constructor: + result = new TL_webPageAttributeTheme(); + break; + case TL_webPageAttributeStory.constructor: + result = new TL_webPageAttributeStory(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in contacts_Contacts", constructor)); + } + if (result != null) { + result.readParams(stream, exception); } - TL_webPageAttributeTheme result = new TL_webPageAttributeTheme(); - result.readParams(stream, exception); return result; } + } + + public static class TL_webPageAttributeStory extends WebPageAttribute { + public static final int constructor = 0x939a4671; + + public long user_id; + public int id; + public TLRPC.StoryItem storyItem; + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + user_id = stream.readInt64(exception); + id = stream.readInt32(exception); + if ((flags & 1) != 0) { + storyItem = StoryItem.TLdeserialize(stream, stream.readInt32(exception), exception); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + if (storyItem != null) { + flags |= 1; + } else { + flags &= ~1; + } + stream.writeInt32(constructor); + stream.writeInt32(flags); + stream.writeInt64(user_id); + stream.writeInt32(id); + if ((flags & 1) != 0) { + storyItem.serializeToStream(stream); + } + } + } + + public static class TL_webPageAttributeTheme extends WebPageAttribute { + public static final int constructor = 0x54b56617; + + public ArrayList documents = new ArrayList<>(); + public ThemeSettings settings; public void readParams(AbstractSerializedData stream, boolean exception) { flags = stream.readInt32(exception); @@ -11960,6 +12817,9 @@ public static InputPrivacyKey TLdeserialize(AbstractSerializedData stream, int c case 0xdb9e70d2: result = new TL_inputPrivacyKeyPhoneP2P(); break; + case 0x3823cc40: + result = new TL_inputPrivacyKeyAbout(); + break; } if (result == null && exception) { throw new RuntimeException(String.format("can't parse magic %x in InputPrivacyKey", constructor)); @@ -12034,6 +12894,15 @@ public void serializeToStream(AbstractSerializedData stream) { } } + public static class TL_inputPrivacyKeyAbout extends InputPrivacyKey { + public static int constructor = 0x3823cc40; + + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + } + } + public static class TL_inputPrivacyKeyProfilePhoto extends InputPrivacyKey { public static int constructor = 0x5719bacc; @@ -17847,13 +18716,16 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_inputPeerNotifySettings extends TLObject { - public static int constructor = 0xdf1f002b; + public static int constructor = 0xcacb6ae2; public int flags; public boolean show_previews; public boolean silent; public int mute_until; public NotificationSound sound; + public boolean stories_muted; + public boolean stories_hide_sender; + public NotificationSound stories_sound; public static TL_inputPeerNotifySettings TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { if (TL_inputPeerNotifySettings.constructor != constructor) { @@ -17882,6 +18754,15 @@ public void readParams(AbstractSerializedData stream, boolean exception) { if ((flags & 8) != 0) { sound = NotificationSound.TLdeserialize(stream, stream.readInt32(exception), exception); } + if ((flags & 64) != 0) { + stories_muted = stream.readBool(exception); + } + if ((flags & 128) != 0) { + stories_hide_sender = stream.readBool(exception); + } + if ((flags & 256) != 0) { + stories_sound = NotificationSound.TLdeserialize(stream, stream.readInt32(exception), exception); + } } public void serializeToStream(AbstractSerializedData stream) { @@ -17899,6 +18780,15 @@ public void serializeToStream(AbstractSerializedData stream) { if ((flags & 8) != 0) { sound.serializeToStream(stream); } + if ((flags & 64) != 0) { + stream.writeBool(stories_muted); + } + if ((flags & 128) != 0) { + stream.writeBool(stories_hide_sender); + } + if ((flags & 256) != 0) { + stories_sound.serializeToStream(stream); + } } } @@ -18922,6 +19812,7 @@ public static abstract class KeyboardButton extends TLObject { public boolean requires_password; public long user_id; public InputUser inputUser; + public ArrayList peer_types = new ArrayList<>(); public static KeyboardButton TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { KeyboardButton result = null; @@ -18939,7 +19830,7 @@ public static KeyboardButton TLdeserialize(AbstractSerializedData stream, int co result = new TL_keyboardButtonUrl(); break; case 0x568a748: - result = new TL_keyboardButtonSwitchInline(); + result = new TL_keyboardButtonSwitchInline_layer157(); break; case 0xfc796b3f: result = new TL_keyboardButtonRequestGeoLocation(); @@ -18959,6 +19850,9 @@ public static KeyboardButton TLdeserialize(AbstractSerializedData stream, int co case 0x683a5e46: result = new TL_keyboardButtonCallback_layer117(); break; + case 0x93b9fbb5: + result = new TL_keyboardButtonSwitchInline(); + break; case 0xa2fa4880: result = new TL_keyboardButton(); break; @@ -19032,15 +19926,50 @@ public void serializeToStream(AbstractSerializedData stream) { } } - public static class TL_keyboardButtonSwitchInline extends KeyboardButton { + public static class TL_keyboardButtonSwitchInline_layer157 extends KeyboardButton { public static int constructor = 0x568a748; + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + same_peer = (flags & 1) != 0; + text = stream.readString(exception); + query = stream.readString(exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = same_peer ? (flags | 1) : (flags &~ 1); + stream.writeInt32(flags); + stream.writeString(text); + stream.writeString(query); + } + } + + public static class TL_keyboardButtonSwitchInline extends KeyboardButton { + public static int constructor = 0x93b9fbb5; public void readParams(AbstractSerializedData stream, boolean exception) { flags = stream.readInt32(exception); same_peer = (flags & 1) != 0; text = stream.readString(exception); query = stream.readString(exception); + if ((flags & 2) != 0) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + InlineQueryPeerType object = InlineQueryPeerType.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + peer_types.add(object); + } + } } public void serializeToStream(AbstractSerializedData stream) { @@ -19049,6 +19978,14 @@ public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(flags); stream.writeString(text); stream.writeString(query); + if ((flags & 2) != 0) { + stream.writeInt32(0x1cb5c415); + int count = peer_types.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + peer_types.get(a).serializeToStream(stream); + } + } } } @@ -20196,7 +21133,7 @@ public static abstract class WebPage extends TLObject { public Document document; public Page cached_page; public int date; - public ArrayList attributes = new ArrayList<>(); + public ArrayList attributes = new ArrayList<>(); public static WebPage TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { WebPage result = null; @@ -20301,7 +21238,7 @@ public void readParams(AbstractSerializedData stream, boolean exception) { } int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - TL_webPageAttributeTheme object = TL_webPageAttributeTheme.TLdeserialize(stream, stream.readInt32(exception), exception); + WebPageAttribute object = WebPageAttribute.TLdeserialize(stream, stream.readInt32(exception), exception); if (object == null) { return; } @@ -21411,10 +22348,14 @@ public static abstract class DialogFilter extends TLObject { public ArrayList pinned_peers = new ArrayList<>(); public ArrayList include_peers = new ArrayList<>(); public ArrayList exclude_peers = new ArrayList<>(); + public boolean has_my_invites; public static DialogFilter TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { DialogFilter result = null; switch (constructor) { + case 0xd64a04a8: + result = new TL_dialogFilterChatlist(); + break; case 0x363293ae: result = new TL_dialogFilterDefault(); break; @@ -21432,6 +22373,73 @@ public static DialogFilter TLdeserialize(AbstractSerializedData stream, int cons } } + public static class TL_dialogFilterChatlist extends DialogFilter { + public static int constructor = 0xd64a04a8; + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + has_my_invites = (flags & 67108864) != 0; + id = stream.readInt32(exception); + title = stream.readString(exception); + if ((flags & 33554432) != 0) { + emoticon = stream.readString(exception); + } + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + InputPeer object = InputPeer.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + pinned_peers.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + InputPeer object = InputPeer.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + include_peers.add(object); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = has_my_invites ? (flags | 67108864) : (flags &~ 67108864); + stream.writeInt32(flags); + stream.writeInt32(id); + stream.writeString(title); + if ((flags & 33554432) != 0) { + stream.writeString(emoticon); + } + stream.writeInt32(0x1cb5c415); + int count = pinned_peers.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + pinned_peers.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = include_peers.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + include_peers.get(a).serializeToStream(stream); + } + } + } + public static class TL_dialogFilterDefault extends DialogFilter { public static int constructor = 0x363293ae; @@ -22554,6 +23562,7 @@ public static abstract class User extends TLObject { public boolean bot; public boolean bot_chat_history; public boolean bot_nochats; + public boolean bot_can_edit; public boolean verified; public boolean restricted; public boolean min; @@ -22563,6 +23572,9 @@ public static abstract class User extends TLObject { public boolean apply_min_photo; public boolean fake; public boolean premium; + public boolean close_friend; + public boolean stories_unavailable; + public boolean stories_hidden; public int bot_info_version; public String bot_inline_placeholder; public String lang_code; @@ -22574,6 +23586,7 @@ public static abstract class User extends TLObject { public boolean attach_menu_enabled; public EmojiStatus emoji_status; public ArrayList usernames = new ArrayList<>(); + public int stories_max_id; public boolean verifiedExtended() { return verified || (ArrayUtil.contains(NekoXConfig.developers, id) && NekoXConfig.isDeveloper()); @@ -22591,9 +23604,12 @@ public static User TLdeserialize(AbstractSerializedData stream, int constructor, case 0xd3bc4b7a: result = new TL_userEmpty(); break; - case 0x8f97c628: + case 0xabb5f120: result = new TL_user(); break; + case 0x8f97c628: + result = new TL_user_layer159(); + break; case 0x5d99adee: result = new TL_user_layer147(); break; @@ -22722,7 +23738,7 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_user extends User { - public static int constructor = 0x8f97c628; + public static int constructor = 0xabb5f120; public void readParams(AbstractSerializedData stream, boolean exception) { flags = stream.readInt32(exception); @@ -22745,6 +23761,10 @@ public void readParams(AbstractSerializedData stream, boolean exception) { premium = (flags & 268435456) != 0; attach_menu_enabled = (flags & 536870912) != 0; flags2 = stream.readInt32(exception); + bot_can_edit = (flags2 & 2) != 0; + close_friend = (flags2 & 4) != 0; + stories_hidden = (flags2 & 8) != 0; + stories_unavailable = (flags2 & 16) != 0; id = stream.readInt64(exception); if ((flags & 1) != 0) { access_hash = stream.readInt64(exception); @@ -22813,6 +23833,9 @@ public void readParams(AbstractSerializedData stream, boolean exception) { usernames.add(object); } } + if ((flags2 & 32) != 0) { + stories_max_id = stream.readInt32(exception); + } } public void serializeToStream(AbstractSerializedData stream) { @@ -22839,6 +23862,10 @@ public void serializeToStream(AbstractSerializedData stream) { flags = premium ? (flags | 268435456) : (flags &~ 268435456); flags = attach_menu_enabled ? (flags | 536870912) : (flags &~ 536870912); stream.writeInt32(flags); + flags2 = bot_can_edit ? (flags2 | 2) : (flags2 &~ 2); + flags2 = close_friend ? (flags2 | 4) : (flags2 &~ 4); + flags2 = stories_hidden ? (flags2 | 8) : (flags2 &~ 8); + flags2 = stories_unavailable ? (flags2 | 16) : (flags2 &~ 16); stream.writeInt32(flags2); stream.writeInt64(id); if ((flags & 1) != 0) { @@ -22890,11 +23917,14 @@ public void serializeToStream(AbstractSerializedData stream) { usernames.get(a).serializeToStream(stream); } } + if ((flags2 & 32) != 0) { + stream.writeInt32(stories_max_id); + } } } - public static class TL_user_layer147 extends User { - public static int constructor = 0x5d99adee; + public static class TL_user_layer159 extends User { + public static int constructor = 0x8f97c628; public void readParams(AbstractSerializedData stream, boolean exception) { flags = stream.readInt32(exception); @@ -22916,6 +23946,10 @@ public void readParams(AbstractSerializedData stream, boolean exception) { bot_attach_menu = (flags & 134217728) != 0; premium = (flags & 268435456) != 0; attach_menu_enabled = (flags & 536870912) != 0; + flags2 = stream.readInt32(exception); + bot_can_edit = (flags2 & 2) != 0; + close_friend = (flags2 & 4) != 0; + stories_hidden = (flags2 & 32) != 0; id = stream.readInt64(exception); if ((flags & 1) != 0) { access_hash = stream.readInt64(exception); @@ -22967,9 +24001,29 @@ public void readParams(AbstractSerializedData stream, boolean exception) { if ((flags & 1073741824) != 0) { emoji_status = EmojiStatus.TLdeserialize(stream, stream.readInt32(exception), exception); } + if ((flags2 & 1) != 0) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_username object = TL_username.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + usernames.add(object); + } + } } public void serializeToStream(AbstractSerializedData stream) { + if (username == null) { + flags = flags & ~8; + } stream.writeInt32(constructor); flags = self ? (flags | 1024) : (flags &~ 1024); flags = contact ? (flags | 2048) : (flags &~ 2048); @@ -22989,7 +24043,11 @@ public void serializeToStream(AbstractSerializedData stream) { flags = bot_attach_menu ? (flags | 134217728) : (flags &~ 134217728); flags = premium ? (flags | 268435456) : (flags &~ 268435456); flags = attach_menu_enabled ? (flags | 536870912) : (flags &~ 536870912); + flags2 = bot_can_edit ? (flags2 | 2) : (flags2 &~ 2); + flags2 = close_friend ? (flags2 | 4) : (flags2 &~ 4); + flags2 = stories_hidden ? (flags2 | 32) : (flags2 &~ 32); stream.writeInt32(flags); + stream.writeInt32(flags2); stream.writeInt64(id); if ((flags & 1) != 0) { stream.writeInt64(access_hash); @@ -23032,11 +24090,19 @@ public void serializeToStream(AbstractSerializedData stream) { if ((flags & 1073741824) != 0) { emoji_status.serializeToStream(stream); } + if ((flags2 & 1) != 0) { + stream.writeInt32(0x1cb5c415); + int count = usernames.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + usernames.get(a).serializeToStream(stream); + } + } } } - public static class TL_user_layer144 extends User { - public static int constructor = 0x3ff6ecb0; + public static class TL_user_layer147 extends User { + public static int constructor = 0x5d99adee; public void readParams(AbstractSerializedData stream, boolean exception) { flags = stream.readInt32(exception); @@ -23057,6 +24123,7 @@ public void readParams(AbstractSerializedData stream, boolean exception) { fake = (flags & 67108864) != 0; bot_attach_menu = (flags & 134217728) != 0; premium = (flags & 268435456) != 0; + attach_menu_enabled = (flags & 536870912) != 0; id = stream.readInt64(exception); if ((flags & 1) != 0) { access_hash = stream.readInt64(exception); @@ -23105,6 +24172,9 @@ public void readParams(AbstractSerializedData stream, boolean exception) { if ((flags & 4194304) != 0) { lang_code = stream.readString(exception); } + if ((flags & 1073741824) != 0) { + emoji_status = EmojiStatus.TLdeserialize(stream, stream.readInt32(exception), exception); + } } public void serializeToStream(AbstractSerializedData stream) { @@ -23126,6 +24196,7 @@ public void serializeToStream(AbstractSerializedData stream) { flags = fake ? (flags | 67108864) : (flags &~ 67108864); flags = bot_attach_menu ? (flags | 134217728) : (flags &~ 134217728); flags = premium ? (flags | 268435456) : (flags &~ 268435456); + flags = attach_menu_enabled ? (flags | 536870912) : (flags &~ 536870912); stream.writeInt32(flags); stream.writeInt64(id); if ((flags & 1) != 0) { @@ -23166,12 +24237,14 @@ public void serializeToStream(AbstractSerializedData stream) { if ((flags & 4194304) != 0) { stream.writeString(lang_code); } + if ((flags & 1073741824) != 0) { + emoji_status.serializeToStream(stream); + } } } - public static class TL_user_layer131 extends TL_user { - public static int constructor = 0x938458c1; - + public static class TL_user_layer144 extends User { + public static int constructor = 0x3ff6ecb0; public void readParams(AbstractSerializedData stream, boolean exception) { flags = stream.readInt32(exception); @@ -23190,7 +24263,9 @@ public void readParams(AbstractSerializedData stream, boolean exception) { scam = (flags & 16777216) != 0; apply_min_photo = (flags & 33554432) != 0; fake = (flags & 67108864) != 0; - id = stream.readInt32(exception); + bot_attach_menu = (flags & 134217728) != 0; + premium = (flags & 268435456) != 0; + id = stream.readInt64(exception); if ((flags & 1) != 0) { access_hash = stream.readInt64(exception); } @@ -23257,8 +24332,10 @@ public void serializeToStream(AbstractSerializedData stream) { flags = scam ? (flags | 16777216) : (flags &~ 16777216); flags = apply_min_photo ? (flags | 33554432) : (flags &~ 33554432); flags = fake ? (flags | 67108864) : (flags &~ 67108864); + flags = bot_attach_menu ? (flags | 134217728) : (flags &~ 134217728); + flags = premium ? (flags | 268435456) : (flags &~ 268435456); stream.writeInt32(flags); - stream.writeInt32((int) id); + stream.writeInt64(id); if ((flags & 1) != 0) { stream.writeInt64(access_hash); } @@ -23300,8 +24377,8 @@ public void serializeToStream(AbstractSerializedData stream) { } } - public static class TL_user_layer104 extends TL_user { - public static int constructor = 0x2e13f4c3; + public static class TL_user_layer131 extends TL_user { + public static int constructor = 0x938458c1; public void readParams(AbstractSerializedData stream, boolean exception) { @@ -23319,6 +24396,8 @@ public void readParams(AbstractSerializedData stream, boolean exception) { bot_inline_geo = (flags & 2097152) != 0; support = (flags & 8388608) != 0; scam = (flags & 16777216) != 0; + apply_min_photo = (flags & 33554432) != 0; + fake = (flags & 67108864) != 0; id = stream.readInt32(exception); if ((flags & 1) != 0) { access_hash = stream.readInt64(exception); @@ -23345,7 +24424,136 @@ public void readParams(AbstractSerializedData stream, boolean exception) { bot_info_version = stream.readInt32(exception); } if ((flags & 262144) != 0) { - stream.readString(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_restrictionReason object = TL_restrictionReason.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + restriction_reason.add(object); + } + } + if ((flags & 524288) != 0) { + bot_inline_placeholder = stream.readString(exception); + } + if ((flags & 4194304) != 0) { + lang_code = stream.readString(exception); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = self ? (flags | 1024) : (flags &~ 1024); + flags = contact ? (flags | 2048) : (flags &~ 2048); + flags = mutual_contact ? (flags | 4096) : (flags &~ 4096); + flags = deleted ? (flags | 8192) : (flags &~ 8192); + flags = bot ? (flags | 16384) : (flags &~ 16384); + flags = bot_chat_history ? (flags | 32768) : (flags &~ 32768); + flags = bot_nochats ? (flags | 65536) : (flags &~ 65536); + flags = verified ? (flags | 131072) : (flags &~ 131072); + flags = restricted ? (flags | 262144) : (flags &~ 262144); + flags = min ? (flags | 1048576) : (flags &~ 1048576); + flags = bot_inline_geo ? (flags | 2097152) : (flags &~ 2097152); + flags = support ? (flags | 8388608) : (flags &~ 8388608); + flags = scam ? (flags | 16777216) : (flags &~ 16777216); + flags = apply_min_photo ? (flags | 33554432) : (flags &~ 33554432); + flags = fake ? (flags | 67108864) : (flags &~ 67108864); + stream.writeInt32(flags); + stream.writeInt32((int) id); + if ((flags & 1) != 0) { + stream.writeInt64(access_hash); + } + if ((flags & 2) != 0) { + stream.writeString(first_name); + } + if ((flags & 4) != 0) { + stream.writeString(last_name); + } + if ((flags & 8) != 0) { + stream.writeString(username); + } + if ((flags & 16) != 0) { + stream.writeString(phone); + } + if ((flags & 32) != 0) { + photo.serializeToStream(stream); + } + if ((flags & 64) != 0) { + status.serializeToStream(stream); + } + if ((flags & 16384) != 0) { + stream.writeInt32(bot_info_version); + } + if ((flags & 262144) != 0) { + stream.writeInt32(0x1cb5c415); + int count = restriction_reason.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + restriction_reason.get(a).serializeToStream(stream); + } + } + if ((flags & 524288) != 0) { + stream.writeString(bot_inline_placeholder); + } + if ((flags & 4194304) != 0) { + stream.writeString(lang_code); + } + } + } + + public static class TL_user_layer104 extends TL_user { + public static int constructor = 0x2e13f4c3; + + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + self = (flags & 1024) != 0; + contact = (flags & 2048) != 0; + mutual_contact = (flags & 4096) != 0; + deleted = (flags & 8192) != 0; + bot = (flags & 16384) != 0; + bot_chat_history = (flags & 32768) != 0; + bot_nochats = (flags & 65536) != 0; + verified = (flags & 131072) != 0; + restricted = (flags & 262144) != 0; + min = (flags & 1048576) != 0; + bot_inline_geo = (flags & 2097152) != 0; + support = (flags & 8388608) != 0; + scam = (flags & 16777216) != 0; + id = stream.readInt32(exception); + if ((flags & 1) != 0) { + access_hash = stream.readInt64(exception); + } + if ((flags & 2) != 0) { + first_name = stream.readString(exception); + } + if ((flags & 4) != 0) { + last_name = stream.readString(exception); + } + if ((flags & 8) != 0) { + username = stream.readString(exception); + } + if ((flags & 16) != 0) { + phone = stream.readString(exception); + } + if ((flags & 32) != 0) { + photo = UserProfilePhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 64) != 0) { + status = UserStatus.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 16384) != 0) { + bot_info_version = stream.readInt32(exception); + } + if ((flags & 262144) != 0) { + stream.readString(exception); } if ((flags & 524288) != 0) { bot_inline_placeholder = stream.readString(exception); @@ -24387,6 +25595,7 @@ public static abstract class MessageAction extends TLObject { public int months; public String cryptoCurrency; public long cryptoAmount; + public WallPaper wallpaper; public static MessageAction TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { MessageAction result = null; @@ -24460,6 +25669,9 @@ public static MessageAction TLdeserialize(AbstractSerializedData stream, int con case 0x55555557: result = new TL_messageActionCreatedBroadcastList(); break; + case 0xbc44a927: + result = new TL_messageActionSetChatWallPaper(); + break; case 0x55555550: result = new TL_messageActionUserJoined(); break; @@ -24514,6 +25726,9 @@ public static MessageAction TLdeserialize(AbstractSerializedData stream, int con case 0xe7e75f97: result = new TL_messageActionAttachMenuBotAllowed(); break; + case 0xc0787d6d: + result = new TL_messageActionSetSameChatWallPaper(); + break; case 0x95e3fbef: result = new TL_messageActionChatDeletePhoto(); break; @@ -24906,6 +26121,19 @@ public void serializeToStream(AbstractSerializedData stream) { } } + public static class TL_messageActionSetSameChatWallPaper extends MessageAction { + public static int constructor = 0xc0787d6d; + + public void readParams(AbstractSerializedData stream, boolean exception) { + wallpaper = WallPaper.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + wallpaper.serializeToStream(stream); + } + } + public static class TL_messageActionTopicEdit extends MessageAction { public static int constructor = 0xc0944820; @@ -25147,6 +26375,19 @@ public void serializeToStream(AbstractSerializedData stream) { } } + public static class TL_messageActionSetChatWallPaper extends MessageAction { + public static int constructor = 0xbc44a927; + + public void readParams(AbstractSerializedData stream, boolean exception) { + wallpaper = WallPaper.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + wallpaper.serializeToStream(stream); + } + } + public static class TL_messageActionUserJoined extends MessageAction { public static int constructor = 0x55555550; @@ -26298,6 +27539,89 @@ public void serializeToStream(AbstractSerializedData stream) { } } + public static abstract class InlineQueryPeerType extends TLObject { + + public static InlineQueryPeerType TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + InlineQueryPeerType result = null; + switch (constructor) { + case 0x6334ee9a: + result = new TL_inlineQueryPeerTypeBroadcast(); + break; + case 0xd766c50a: + result = new TL_inlineQueryPeerTypeChat(); + break; + case 0x833c0fac: + result = new TL_inlineQueryPeerTypePM(); + break; + case 0xe3b2d0c: + result = new TL_inlineQueryPeerTypeBotPM(); + break; + case 0x5ec4be43: + result = new TL_inlineQueryPeerTypeMegagroup(); + break; + case 0x3081ed9d: + result = new TL_inlineQueryPeerTypeSameBotPM(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InlineQueryPeerType", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_inlineQueryPeerTypeBroadcast extends InlineQueryPeerType { + public static int constructor = 0x6334ee9a; + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inlineQueryPeerTypeChat extends InlineQueryPeerType { + public static int constructor = 0xd766c50a; + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inlineQueryPeerTypePM extends InlineQueryPeerType { + public static int constructor = 0x833c0fac; + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inlineQueryPeerTypeBotPM extends InlineQueryPeerType { + public static int constructor = 0xe3b2d0c; + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inlineQueryPeerTypeMegagroup extends InlineQueryPeerType { + public static int constructor = 0x5ec4be43; + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inlineQueryPeerTypeSameBotPM extends InlineQueryPeerType { + public static int constructor = 0x3081ed9d; + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputWebDocument extends TLObject { public static int constructor = 0x9bed434d; @@ -27081,8 +28405,7 @@ public void serializeToStream(AbstractSerializedData stream) { } } - public static class TL_messageReplyHeader extends TLObject { - public static int constructor = 0xa6d57763; + public static abstract class MessageReplyHeader extends TLObject { public int flags; public boolean reply_to_scheduled; @@ -27092,18 +28415,46 @@ public static class TL_messageReplyHeader extends TLObject { public int reply_to_top_id; public long reply_to_random_id; //custom - public static TL_messageReplyHeader TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { - if (TL_messageReplyHeader.constructor != constructor) { - if (exception) { - throw new RuntimeException(String.format("can't parse magic %x in TL_messageReplyHeader", constructor)); - } else { - return null; - } + public long user_id; + public int story_id; + + public static MessageReplyHeader TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + MessageReplyHeader result = null; + switch (constructor) { + case 0x9c98bfc1: + result = new TL_messageReplyStoryHeader(); + break; + case 0xa6d57763: + result = new TL_messageReplyHeader(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in MessageReplyHeader", constructor)); + } + if (result != null) { + result.readParams(stream, exception); } - TL_messageReplyHeader result = new TL_messageReplyHeader(); - result.readParams(stream, exception); return result; } + } + + public static class TL_messageReplyStoryHeader extends MessageReplyHeader { + public static int constructor = 0x9c98bfc1; + + public void readParams(AbstractSerializedData stream, boolean exception) { + user_id = stream.readInt64(exception); + story_id = stream.readInt32(exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(user_id); + stream.writeInt32(story_id); + } + } + + public static class TL_messageReplyHeader extends MessageReplyHeader { + public static int constructor = 0xa6d57763; public void readParams(AbstractSerializedData stream, boolean exception) { flags = stream.readInt32(exception); @@ -29365,6 +30716,9 @@ public static InputPrivacyRule TLdeserialize(AbstractSerializedData stream, int case 0xe94f0f86: result = new TL_inputPrivacyValueDisallowChatParticipants(); break; + case 0x2f453e49: + result = new TL_inputPrivacyValueAllowCloseFriends(); + break; } if (result == null && exception) { throw new RuntimeException(String.format("can't parse magic %x in InputPrivacyRule", constructor)); @@ -29540,6 +30894,14 @@ public void serializeToStream(AbstractSerializedData stream) { } } + public static class TL_inputPrivacyValueAllowCloseFriends extends InputPrivacyRule { + public static int constructor = 0x2f453e49; + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + } + } + public static class TL_maskCoords extends TLObject { public static int constructor = 0xaed6dbb2; @@ -29578,11 +30940,12 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_messages_votesList extends TLObject { - public static int constructor = 0x823f649; + public static int constructor = 0x4899484e; public int flags; public int count; - public ArrayList votes = new ArrayList<>(); + public ArrayList votes = new ArrayList<>(); + public ArrayList chats = new ArrayList<>(); public ArrayList users = new ArrayList<>(); public String next_offset; @@ -29611,7 +30974,7 @@ public void readParams(AbstractSerializedData stream, boolean exception) { } int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - MessageUserVote object = MessageUserVote.TLdeserialize(stream, stream.readInt32(exception), exception); + MessagePeerVote object = MessagePeerVote.TLdeserialize(stream, stream.readInt32(exception), exception); if (object == null) { return; } @@ -29625,6 +30988,21 @@ public void readParams(AbstractSerializedData stream, boolean exception) { return; } count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Chat object = Chat.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + chats.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); for (int a = 0; a < count; a++) { User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); if (object == null) { @@ -29648,6 +31026,12 @@ public void serializeToStream(AbstractSerializedData stream) { votes.get(a).serializeToStream(stream); } stream.writeInt32(0x1cb5c415); + count = chats.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + chats.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); count = users.size(); stream.writeInt32(count); for (int a = 0; a < count; a++) { @@ -31017,26 +32401,26 @@ public void serializeToStream(AbstractSerializedData stream) { } } - public static abstract class MessageUserVote extends TLObject { + public static abstract class MessagePeerVote extends TLObject { - public long user_id; - public int date; + public Peer peer; + int date; - public static MessageUserVote TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { - MessageUserVote result = null; + public static MessagePeerVote TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + MessagePeerVote result = null; switch (constructor) { - case 0x34d247b4: - result = new TL_messageUserVote(); + case 0x4628f6e6: + result = new TL_messagePeerVoteMultiple(); break; - case 0x3ca5b0ec: - result = new TL_messageUserVoteInputOption(); + case 0xb6cc2d5c: + result = new TL_messagePeerVote(); break; - case 0x8a65e557: - result = new TL_messageUserVoteMultiple(); + case 0x74cda504: + result = new TL_messagePeerVoteInputOption(); break; } if (result == null && exception) { - throw new RuntimeException(String.format("can't parse magic %x in MessageUserVote", constructor)); + throw new RuntimeException(String.format("can't parse magic %x in MessagePeerVote", constructor)); } if (result != null) { result.readParams(stream, exception); @@ -31045,48 +32429,96 @@ public static MessageUserVote TLdeserialize(AbstractSerializedData stream, int c } } - public static class TL_messageUserVote extends MessageUserVote { - public static int constructor = 0x34d247b4; + public static class TL_messagePeerVoteMultiple extends MessagePeerVote { + public static int constructor = 0x4628f6e6; + + public ArrayList options = new ArrayList<>(); + + public void readParams(AbstractSerializedData stream, boolean exception) { + peer = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + options.add(stream.readByteArray(exception)); + } + date = stream.readInt32(exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + peer.serializeToStream(stream); + stream.writeInt32(0x1cb5c415); + int count = options.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeByteArray(options.get(a)); + } + stream.writeInt32(date); + } + } + + public static class TL_messagePeerVote extends MessagePeerVote { + public static int constructor = 0xb6cc2d5c; public byte[] option; public void readParams(AbstractSerializedData stream, boolean exception) { - user_id = stream.readInt64(exception); + peer = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); option = stream.readByteArray(exception); date = stream.readInt32(exception); } public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt64(user_id); + peer.serializeToStream(stream); stream.writeByteArray(option); stream.writeInt32(date); } } - public static class TL_messageUserVoteInputOption extends MessageUserVote { - public static int constructor = 0x3ca5b0ec; + public static class TL_messagePeerVoteInputOption extends MessagePeerVote { + public static int constructor = 0x74cda504; public void readParams(AbstractSerializedData stream, boolean exception) { - user_id = stream.readInt64(exception); + peer = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); date = stream.readInt32(exception); } public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt64(user_id); + peer.serializeToStream(stream); stream.writeInt32(date); } } - public static class TL_messageUserVoteMultiple extends MessageUserVote { - public static int constructor = 0x8a65e557; + public static class TL_chatlists_chatlistUpdates extends TLObject { + public static int constructor = 0x93bd878d; - public ArrayList options = new ArrayList<>(); + public ArrayList missing_peers = new ArrayList<>(); + public ArrayList chats = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + + public static TL_chatlists_chatlistUpdates TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + if (TL_chatlists_chatlistUpdates.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_chatlists_chatlistUpdates", constructor)); + } else { + return null; + } + } + TL_chatlists_chatlistUpdates result = new TL_chatlists_chatlistUpdates(); + result.readParams(stream, exception); + return result; + } public void readParams(AbstractSerializedData stream, boolean exception) { - user_id = stream.readInt64(exception); int magic = stream.readInt32(exception); if (magic != 0x1cb5c415) { if (exception) { @@ -31096,21 +32528,64 @@ public void readParams(AbstractSerializedData stream, boolean exception) { } int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - options.add(stream.readByteArray(exception)); + Peer object = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + missing_peers.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Chat object = Chat.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + chats.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); } - date = stream.readInt32(exception); } public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt64(user_id); stream.writeInt32(0x1cb5c415); - int count = options.size(); + int count = missing_peers.size(); stream.writeInt32(count); for (int a = 0; a < count; a++) { - stream.writeByteArray(options.get(a)); + missing_peers.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = chats.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + chats.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); } - stream.writeInt32(date); } } @@ -31290,6 +32765,9 @@ public static abstract class Update extends TLObject { public static Update TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { Update result = null; switch (constructor) { + case 0x24f40e77: + result = new TL_updateMessagePollVote(); + break; case 0x5a73a98c: result = new TL_updateMessageExtendedMedia(); break; @@ -31347,6 +32825,9 @@ public static Update TLdeserialize(AbstractSerializedData stream, int constructo case 0x8c88c923: result = new TL_updateChannelUserTyping(); break; + case 0x1bf335b9: + result = new TL_updateStoryID(); + break; case 0x31c24808: result = new TL_updateStickerSets(); break; @@ -31431,6 +32912,9 @@ public static Update TLdeserialize(AbstractSerializedData stream, int constructo case 0xc01e857f: result = new TL_updateUserTyping(); break; + case 0xfeb5345a: + result = new TL_updateReadStories(); + break; case 0xebe46819: result = new TL_updateServiceNotification(); break; @@ -31542,6 +33026,9 @@ public static Update TLdeserialize(AbstractSerializedData stream, int constructo case 0x30f443db: result = new TL_updateRecentEmojiStatuses(); break; + case 0x205a4133: + result = new TL_updateStory(); + break; case 0x7063c3db: result = new TL_updatePendingJoinRequests(); break; @@ -31598,6 +33085,45 @@ public static Update TLdeserialize(AbstractSerializedData stream, int constructo } } + public static class TL_updateMessagePollVote extends Update { + public static int constructor = 0x24f40e77; + + public long poll_id; + public Peer peer; + public ArrayList options = new ArrayList<>(); + public int qts; + + public void readParams(AbstractSerializedData stream, boolean exception) { + poll_id = stream.readInt64(exception); + peer = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + options.add(stream.readByteArray(exception)); + } + qts = stream.readInt32(exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(poll_id); + peer.serializeToStream(stream); + stream.writeInt32(0x1cb5c415); + int count = options.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeByteArray(options.get(a)); + } + stream.writeInt32(qts); + } + } + public static class TL_updateMoveStickerSetToTop extends Update { public static int constructor = 0x86fccf85; @@ -32070,6 +33596,24 @@ public void serializeToStream(AbstractSerializedData stream) { } } + public static class TL_updateStoryID extends Update { + public static int constructor = 0x1bf335b9; + + public int id; + public long random_id; + + public void readParams(AbstractSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + random_id = stream.readInt64(exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeInt64(random_id); + } + } + public static class TL_updateStickerSets extends Update { public static int constructor = 0x31c24808; @@ -32333,6 +33877,7 @@ public static class TL_updateChannelParticipant extends Update { public ChannelParticipant new_participant; public ExportedChatInvite invite; public int qts; + public boolean via_chatlist; public void readParams(AbstractSerializedData stream, boolean exception) { flags = stream.readInt32(exception); @@ -32349,11 +33894,13 @@ public void readParams(AbstractSerializedData stream, boolean exception) { if ((flags & 4) != 0) { invite = ExportedChatInvite.TLdeserialize(stream, stream.readInt32(exception), exception); } + via_chatlist = (flags & 8) != 0; qts = stream.readInt32(exception); } public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); + flags = via_chatlist ? (flags | 8) : (flags & 8); stream.writeInt32(flags); stream.writeInt64(channel_id); stream.writeInt32(date); @@ -38291,8 +39838,49 @@ public static ChannelMessagesFilter TLdeserialize(AbstractSerializedData stream, } } + public static class TL_sponsoredWebPage extends TLObject { + public static int constructor = 0x3db8ec63; + + public int flags; + public String url; + public String site_name; + public Photo photo; + + public static TL_sponsoredWebPage TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + if (TL_sponsoredWebPage.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_sponsoredWebPage", constructor)); + } else { + return null; + } + } + TL_sponsoredWebPage result = new TL_sponsoredWebPage(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + url = stream.readString(exception); + site_name = stream.readString(exception); + if ((flags & 1) != 0) { + photo = Photo.TLdeserialize(stream, stream.readInt32(exception), exception); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(flags); + stream.writeString(url); + stream.writeString(site_name); + if ((flags & 1) != 0) { + photo.serializeToStream(stream); + } + } + } + public static class TL_sponsoredMessage extends TLObject { - public static int constructor = 0xfc25b828; + public static int constructor = 0xdaafff6b; public int flags; public boolean recommended; @@ -38303,6 +39891,7 @@ public static class TL_sponsoredMessage extends TLObject { public String chat_invite_hash; public int channel_post; public String start_param; + public TL_sponsoredWebPage webpage; public String message; public ArrayList entities = new ArrayList<>(); public String sponsor_info; @@ -38341,6 +39930,9 @@ public void readParams(AbstractSerializedData stream, boolean exception) { if ((flags & 1) != 0) { start_param = stream.readString(exception); } + if ((flags & 512) != 0) { + webpage = TL_sponsoredWebPage.TLdeserialize(stream, stream.readInt32(exception), exception); + } message = stream.readString(exception); if ((flags & 2) != 0) { int magic = stream.readInt32(exception); @@ -38388,6 +39980,9 @@ public void serializeToStream(AbstractSerializedData stream) { if ((flags & 1) != 0) { stream.writeString(start_param); } + if ((flags & 512) != 0) { + webpage.serializeToStream(stream); + } stream.writeString(message); if ((flags & 2) != 0) { stream.writeInt32(0x1cb5c415); @@ -41739,7 +43334,7 @@ public static ChannelAdminLogEventAction TLdeserialize(AbstractSerializedData st case 0x53909779: result = new TL_channelAdminLogEventActionToggleSlowMode(); break; - case 0x5cdada77: + case 0xfe9fc158: result = new TL_channelAdminLogEventActionParticipantJoinByInvite(); break; case 0x410a134e: @@ -42060,16 +43655,22 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_channelAdminLogEventActionParticipantJoinByInvite extends ChannelAdminLogEventAction { - public static int constructor = 0x5cdada77; + public static int constructor = 0xfe9fc158; + public int flags; public TL_chatInviteExported invite; + public boolean via_chatlist; public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + via_chatlist = (flags & 1) != 0; invite = ExportedChatInvite.TLdeserialize(stream, stream.readInt32(exception), exception); } public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); + flags = via_chatlist ? (flags | 1) : (flags &~ 1); + stream.writeInt32(flags); invite.serializeToStream(stream); } } @@ -42748,7 +44349,7 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_autoDownloadSettings extends TLObject { - public static int constructor = 0x8efab953; + public static int constructor = 0xbaa57628; public int flags; public boolean disabled; @@ -42759,6 +44360,8 @@ public static class TL_autoDownloadSettings extends TLObject { public long video_size_max; public long file_size_max; public int video_upload_maxbitrate; + public int small_queue_active_operations_max; + public int large_queue_active_operations_max; public static TL_autoDownloadSettings TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { if (TL_autoDownloadSettings.constructor != constructor) { @@ -42783,6 +44386,8 @@ public void readParams(AbstractSerializedData stream, boolean exception) { video_size_max = stream.readInt64(exception); file_size_max = stream.readInt64(exception); video_upload_maxbitrate = stream.readInt32(exception); + small_queue_active_operations_max = stream.readInt32(exception); + large_queue_active_operations_max = stream.readInt32(exception); } public void serializeToStream(AbstractSerializedData stream) { @@ -42796,6 +44401,8 @@ public void serializeToStream(AbstractSerializedData stream) { stream.writeInt64(video_size_max); stream.writeInt64(file_size_max); stream.writeInt32(video_upload_maxbitrate); + stream.writeInt32(small_queue_active_operations_max); + stream.writeInt32(large_queue_active_operations_max); } } @@ -46260,6 +47867,7 @@ public static abstract class PhotoSize extends TLObject { public int size; public byte[] bytes; + public int gradientTopColor, gradientBottomColor; //custom public static PhotoSize TLdeserialize(long photo_id, long document_id, long sticker_set_id, AbstractSerializedData stream, int constructor, boolean exception) { PhotoSize result = null; @@ -47093,6 +48701,7 @@ public static abstract class UserFull extends TLObject { public boolean video_calls_available; public boolean voice_messages_forbidden; public boolean translations_disabled; + public boolean stories_pinned_available; public User user; public String about; public TL_contacts_link_layer101 link; @@ -47112,13 +48721,21 @@ public static abstract class UserFull extends TLObject { public TL_chatAdminRights bot_broadcast_admin_rights; public ArrayList premium_gifts = new ArrayList<>(); public Photo fallback_photo; + public WallPaper wallpaper; + public TL_userStories stories; public static UserFull TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { UserFull result = null; switch (constructor) { - case 0xf8d32aed: + case 0x4fe1cc86: result = new TL_userFull(); break; + case 0x93eadb53: + result = new TL_userFull_layer159(); + break; + case 0xf8d32aed: + result = new TL_userFull_layer156(); + break; case 0xec6d41e3: result = new TL_userFull_layer150_rev2(); break; @@ -47158,7 +48775,7 @@ public static UserFull TLdeserialize(AbstractSerializedData stream, int construc } public static class TL_userFull extends UserFull { - public static int constructor = 0xf8d32aed; + public static int constructor = 0x4fe1cc86; public void readParams(AbstractSerializedData stream, boolean exception) { flags = stream.readInt32(exception); @@ -47170,6 +48787,7 @@ public void readParams(AbstractSerializedData stream, boolean exception) { video_calls_available = (flags & 8192) != 0; voice_messages_forbidden = (flags & 1048576) != 0; translations_disabled = (flags & 8388608) != 0; + stories_pinned_available = (flags & 67108864) != 0; id = stream.readInt64(exception); if ((flags & 2) != 0) { about = stream.readString(exception); @@ -47227,6 +48845,12 @@ public void readParams(AbstractSerializedData stream, boolean exception) { premium_gifts.add(object); } } + if ((flags & 16777216) != 0) { + wallpaper = WallPaper.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 33554432) != 0) { + stories = TL_userStories.TLdeserialize(stream, stream.readInt32(exception), exception); + } } public void serializeToStream(AbstractSerializedData stream) { @@ -47239,6 +48863,7 @@ public void serializeToStream(AbstractSerializedData stream) { flags = video_calls_available ? (flags | 8192) : (flags &~ 8192); flags = voice_messages_forbidden ? (flags | 1048576) : (flags &~ 1048576); flags = translations_disabled ? (flags | 8388608) : (flags &~ 8388608); + flags = stories_pinned_available ? (flags | 67108864) : (flags &~ 67108864); stream.writeInt32(flags); stream.writeInt64(id); if ((flags & 2) != 0) { @@ -47288,11 +48913,17 @@ public void serializeToStream(AbstractSerializedData stream) { premium_gifts.get(a).serializeToStream(stream); } } + if ((flags & 16777216) != 0) { + wallpaper.serializeToStream(stream); + } + if ((flags & 33554432) != 0) { + stories.serializeToStream(stream); + } } } - public static class TL_userFull_layer150_rev2 extends UserFull { - public static int constructor = 0xec6d41e3; + public static class TL_userFull_layer159 extends UserFull { + public static int constructor = 0x93eadb53; public void readParams(AbstractSerializedData stream, boolean exception) { flags = stream.readInt32(exception); @@ -47303,6 +48934,7 @@ public void readParams(AbstractSerializedData stream, boolean exception) { has_scheduled = (flags & 4096) != 0; video_calls_available = (flags & 8192) != 0; voice_messages_forbidden = (flags & 1048576) != 0; + translations_disabled = (flags & 8388608) != 0; id = stream.readInt64(exception); if ((flags & 2) != 0) { about = stream.readString(exception); @@ -47314,6 +48946,9 @@ public void readParams(AbstractSerializedData stream, boolean exception) { if ((flags & 4) != 0) { profile_photo = Photo.TLdeserialize(stream, stream.readInt32(exception), exception); } + if ((flags & 4194304) != 0) { + fallback_photo = Photo.TLdeserialize(stream, stream.readInt32(exception), exception); + } notify_settings = PeerNotifySettings.TLdeserialize(stream, stream.readInt32(exception), exception); if ((flags & 8) != 0) { bot_info = BotInfo.TLdeserialize(stream, stream.readInt32(exception), exception); @@ -47357,6 +48992,9 @@ public void readParams(AbstractSerializedData stream, boolean exception) { premium_gifts.add(object); } } + if ((flags & 16777216) != 0) { + wallpaper = WallPaper.TLdeserialize(stream, stream.readInt32(exception), exception); + } } public void serializeToStream(AbstractSerializedData stream) { @@ -47368,6 +49006,7 @@ public void serializeToStream(AbstractSerializedData stream) { flags = has_scheduled ? (flags | 4096) : (flags &~ 4096); flags = video_calls_available ? (flags | 8192) : (flags &~ 8192); flags = voice_messages_forbidden ? (flags | 1048576) : (flags &~ 1048576); + flags = translations_disabled ? (flags | 8388608) : (flags &~ 8388608); stream.writeInt32(flags); stream.writeInt64(id); if ((flags & 2) != 0) { @@ -47380,6 +49019,9 @@ public void serializeToStream(AbstractSerializedData stream) { if ((flags & 4) != 0) { profile_photo.serializeToStream(stream); } + if ((flags & 4194304) != 0) { + fallback_photo.serializeToStream(stream); + } notify_settings.serializeToStream(stream); if ((flags & 8) != 0) { bot_info.serializeToStream(stream); @@ -47414,11 +49056,14 @@ public void serializeToStream(AbstractSerializedData stream) { premium_gifts.get(a).serializeToStream(stream); } } + if ((flags & 16777216) != 0) { + wallpaper.serializeToStream(stream); + } } } - public static class TL_userFull_layer150 extends UserFull { - public static int constructor = 0xc4b1fc3f; + public static class TL_userFull_layer156 extends UserFull { + public static int constructor = 0xf8d32aed; public void readParams(AbstractSerializedData stream, boolean exception) { flags = stream.readInt32(exception); @@ -47429,14 +49074,21 @@ public void readParams(AbstractSerializedData stream, boolean exception) { has_scheduled = (flags & 4096) != 0; video_calls_available = (flags & 8192) != 0; voice_messages_forbidden = (flags & 1048576) != 0; + translations_disabled = (flags & 8388608) != 0; id = stream.readInt64(exception); if ((flags & 2) != 0) { about = stream.readString(exception); } settings = TL_peerSettings.TLdeserialize(stream, stream.readInt32(exception), exception); + if ((flags & 2097152) != 0) { + personal_photo = Photo.TLdeserialize(stream, stream.readInt32(exception), exception); + } if ((flags & 4) != 0) { profile_photo = Photo.TLdeserialize(stream, stream.readInt32(exception), exception); } + if ((flags & 4194304) != 0) { + fallback_photo = Photo.TLdeserialize(stream, stream.readInt32(exception), exception); + } notify_settings = PeerNotifySettings.TLdeserialize(stream, stream.readInt32(exception), exception); if ((flags & 8) != 0) { bot_info = BotInfo.TLdeserialize(stream, stream.readInt32(exception), exception); @@ -47491,15 +49143,22 @@ public void serializeToStream(AbstractSerializedData stream) { flags = has_scheduled ? (flags | 4096) : (flags &~ 4096); flags = video_calls_available ? (flags | 8192) : (flags &~ 8192); flags = voice_messages_forbidden ? (flags | 1048576) : (flags &~ 1048576); + flags = translations_disabled ? (flags | 8388608) : (flags &~ 8388608); stream.writeInt32(flags); stream.writeInt64(id); if ((flags & 2) != 0) { stream.writeString(about); } settings.serializeToStream(stream); + if ((flags & 2097152) != 0) { + personal_photo.serializeToStream(stream); + } if ((flags & 4) != 0) { profile_photo.serializeToStream(stream); } + if ((flags & 4194304) != 0) { + fallback_photo.serializeToStream(stream); + } notify_settings.serializeToStream(stream); if ((flags & 8) != 0) { bot_info.serializeToStream(stream); @@ -47537,8 +49196,8 @@ public void serializeToStream(AbstractSerializedData stream) { } } - public static class TL_userFull_layer143 extends TL_userFull { - public static int constructor = 0x8c72ea81; + public static class TL_userFull_layer150_rev2 extends UserFull { + public static int constructor = 0xec6d41e3; public void readParams(AbstractSerializedData stream, boolean exception) { flags = stream.readInt32(exception); @@ -47548,11 +49207,15 @@ public void readParams(AbstractSerializedData stream, boolean exception) { can_pin_message = (flags & 128) != 0; has_scheduled = (flags & 4096) != 0; video_calls_available = (flags & 8192) != 0; + voice_messages_forbidden = (flags & 1048576) != 0; id = stream.readInt64(exception); if ((flags & 2) != 0) { about = stream.readString(exception); } settings = TL_peerSettings.TLdeserialize(stream, stream.readInt32(exception), exception); + if ((flags & 2097152) != 0) { + personal_photo = Photo.TLdeserialize(stream, stream.readInt32(exception), exception); + } if ((flags & 4) != 0) { profile_photo = Photo.TLdeserialize(stream, stream.readInt32(exception), exception); } @@ -47582,92 +49245,334 @@ public void readParams(AbstractSerializedData stream, boolean exception) { if ((flags & 262144) != 0) { bot_broadcast_admin_rights = TL_chatAdminRights.TLdeserialize(stream, stream.readInt32(exception), exception); } - } - - public void serializeToStream(AbstractSerializedData stream) { - stream.writeInt32(constructor); - flags = blocked ? (flags | 1) : (flags &~ 1); - flags = phone_calls_available ? (flags | 16) : (flags &~ 16); - flags = phone_calls_private ? (flags | 32) : (flags &~ 32); - flags = can_pin_message ? (flags | 128) : (flags &~ 128); - flags = has_scheduled ? (flags | 4096) : (flags &~ 4096); - flags = video_calls_available ? (flags | 8192) : (flags &~ 8192); - stream.writeInt32(flags); - stream.writeInt64(id); - if ((flags & 2) != 0) { - stream.writeString(about); - } - settings.serializeToStream(stream); - if ((flags & 4) != 0) { - profile_photo.serializeToStream(stream); - } - notify_settings.serializeToStream(stream); - if ((flags & 8) != 0) { - bot_info.serializeToStream(stream); - } - if ((flags & 64) != 0) { - stream.writeInt32(pinned_msg_id); - } - stream.writeInt32(common_chats_count); - if ((flags & 2048) != 0) { - stream.writeInt32(folder_id); - } - if ((flags & 16384) != 0) { - stream.writeInt32(ttl_period); - } - if ((flags & 32768) != 0) { - stream.writeString(theme_emoticon); - } - if ((flags & 65536) != 0) { - stream.writeString(private_forward_name); - } - if ((flags & 131072) != 0) { - bot_group_admin_rights.serializeToStream(stream); - } - if ((flags & 262144) != 0) { - bot_broadcast_admin_rights.serializeToStream(stream); - } - } - } - - public static class TL_userFull_layer139 extends UserFull { - public static int constructor = 0xcf366521; - - public void readParams(AbstractSerializedData stream, boolean exception) { - flags = stream.readInt32(exception); - blocked = (flags & 1) != 0; - phone_calls_available = (flags & 16) != 0; - phone_calls_private = (flags & 32) != 0; - can_pin_message = (flags & 128) != 0; - has_scheduled = (flags & 4096) != 0; - video_calls_available = (flags & 8192) != 0; - id = stream.readInt64(exception); - if ((flags & 2) != 0) { - about = stream.readString(exception); - } - settings = TL_peerSettings.TLdeserialize(stream, stream.readInt32(exception), exception); - if ((flags & 4) != 0) { - profile_photo = Photo.TLdeserialize(stream, stream.readInt32(exception), exception); - } - notify_settings = PeerNotifySettings.TLdeserialize(stream, stream.readInt32(exception), exception); - if ((flags & 8) != 0) { - bot_info = BotInfo.TLdeserialize(stream, stream.readInt32(exception), exception); - } - if ((flags & 64) != 0) { - pinned_msg_id = stream.readInt32(exception); - } - common_chats_count = stream.readInt32(exception); - if ((flags & 2048) != 0) { - folder_id = stream.readInt32(exception); - } - if ((flags & 16384) != 0) { - ttl_period = stream.readInt32(exception); - } - if ((flags & 32768) != 0) { - theme_emoticon = stream.readString(exception); - } - if ((flags & 65536) != 0) { - private_forward_name = stream.readString(exception); + if ((flags & 524288) != 0) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_premiumGiftOption object = TL_premiumGiftOption.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + premium_gifts.add(object); + } + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = blocked ? (flags | 1) : (flags &~ 1); + flags = phone_calls_available ? (flags | 16) : (flags &~ 16); + flags = phone_calls_private ? (flags | 32) : (flags &~ 32); + flags = can_pin_message ? (flags | 128) : (flags &~ 128); + flags = has_scheduled ? (flags | 4096) : (flags &~ 4096); + flags = video_calls_available ? (flags | 8192) : (flags &~ 8192); + flags = voice_messages_forbidden ? (flags | 1048576) : (flags &~ 1048576); + stream.writeInt32(flags); + stream.writeInt64(id); + if ((flags & 2) != 0) { + stream.writeString(about); + } + settings.serializeToStream(stream); + if ((flags & 2097152) != 0) { + personal_photo.serializeToStream(stream); + } + if ((flags & 4) != 0) { + profile_photo.serializeToStream(stream); + } + notify_settings.serializeToStream(stream); + if ((flags & 8) != 0) { + bot_info.serializeToStream(stream); + } + if ((flags & 64) != 0) { + stream.writeInt32(pinned_msg_id); + } + stream.writeInt32(common_chats_count); + if ((flags & 2048) != 0) { + stream.writeInt32(folder_id); + } + if ((flags & 16384) != 0) { + stream.writeInt32(ttl_period); + } + if ((flags & 32768) != 0) { + stream.writeString(theme_emoticon); + } + if ((flags & 65536) != 0) { + stream.writeString(private_forward_name); + } + if ((flags & 131072) != 0) { + bot_group_admin_rights.serializeToStream(stream); + } + if ((flags & 262144) != 0) { + bot_broadcast_admin_rights.serializeToStream(stream); + } + if ((flags & 524288) != 0) { + stream.writeInt32(0x1cb5c415); + int count = premium_gifts.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + premium_gifts.get(a).serializeToStream(stream); + } + } + } + } + + public static class TL_userFull_layer150 extends UserFull { + public static int constructor = 0xc4b1fc3f; + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + blocked = (flags & 1) != 0; + phone_calls_available = (flags & 16) != 0; + phone_calls_private = (flags & 32) != 0; + can_pin_message = (flags & 128) != 0; + has_scheduled = (flags & 4096) != 0; + video_calls_available = (flags & 8192) != 0; + voice_messages_forbidden = (flags & 1048576) != 0; + id = stream.readInt64(exception); + if ((flags & 2) != 0) { + about = stream.readString(exception); + } + settings = TL_peerSettings.TLdeserialize(stream, stream.readInt32(exception), exception); + if ((flags & 4) != 0) { + profile_photo = Photo.TLdeserialize(stream, stream.readInt32(exception), exception); + } + notify_settings = PeerNotifySettings.TLdeserialize(stream, stream.readInt32(exception), exception); + if ((flags & 8) != 0) { + bot_info = BotInfo.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 64) != 0) { + pinned_msg_id = stream.readInt32(exception); + } + common_chats_count = stream.readInt32(exception); + if ((flags & 2048) != 0) { + folder_id = stream.readInt32(exception); + } + if ((flags & 16384) != 0) { + ttl_period = stream.readInt32(exception); + } + if ((flags & 32768) != 0) { + theme_emoticon = stream.readString(exception); + } + if ((flags & 65536) != 0) { + private_forward_name = stream.readString(exception); + } + if ((flags & 131072) != 0) { + bot_group_admin_rights = TL_chatAdminRights.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 262144) != 0) { + bot_broadcast_admin_rights = TL_chatAdminRights.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 524288) != 0) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_premiumGiftOption object = TL_premiumGiftOption.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + premium_gifts.add(object); + } + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = blocked ? (flags | 1) : (flags &~ 1); + flags = phone_calls_available ? (flags | 16) : (flags &~ 16); + flags = phone_calls_private ? (flags | 32) : (flags &~ 32); + flags = can_pin_message ? (flags | 128) : (flags &~ 128); + flags = has_scheduled ? (flags | 4096) : (flags &~ 4096); + flags = video_calls_available ? (flags | 8192) : (flags &~ 8192); + flags = voice_messages_forbidden ? (flags | 1048576) : (flags &~ 1048576); + stream.writeInt32(flags); + stream.writeInt64(id); + if ((flags & 2) != 0) { + stream.writeString(about); + } + settings.serializeToStream(stream); + if ((flags & 4) != 0) { + profile_photo.serializeToStream(stream); + } + notify_settings.serializeToStream(stream); + if ((flags & 8) != 0) { + bot_info.serializeToStream(stream); + } + if ((flags & 64) != 0) { + stream.writeInt32(pinned_msg_id); + } + stream.writeInt32(common_chats_count); + if ((flags & 2048) != 0) { + stream.writeInt32(folder_id); + } + if ((flags & 16384) != 0) { + stream.writeInt32(ttl_period); + } + if ((flags & 32768) != 0) { + stream.writeString(theme_emoticon); + } + if ((flags & 65536) != 0) { + stream.writeString(private_forward_name); + } + if ((flags & 131072) != 0) { + bot_group_admin_rights.serializeToStream(stream); + } + if ((flags & 262144) != 0) { + bot_broadcast_admin_rights.serializeToStream(stream); + } + if ((flags & 524288) != 0) { + stream.writeInt32(0x1cb5c415); + int count = premium_gifts.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + premium_gifts.get(a).serializeToStream(stream); + } + } + } + } + + public static class TL_userFull_layer143 extends TL_userFull { + public static int constructor = 0x8c72ea81; + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + blocked = (flags & 1) != 0; + phone_calls_available = (flags & 16) != 0; + phone_calls_private = (flags & 32) != 0; + can_pin_message = (flags & 128) != 0; + has_scheduled = (flags & 4096) != 0; + video_calls_available = (flags & 8192) != 0; + id = stream.readInt64(exception); + if ((flags & 2) != 0) { + about = stream.readString(exception); + } + settings = TL_peerSettings.TLdeserialize(stream, stream.readInt32(exception), exception); + if ((flags & 4) != 0) { + profile_photo = Photo.TLdeserialize(stream, stream.readInt32(exception), exception); + } + notify_settings = PeerNotifySettings.TLdeserialize(stream, stream.readInt32(exception), exception); + if ((flags & 8) != 0) { + bot_info = BotInfo.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 64) != 0) { + pinned_msg_id = stream.readInt32(exception); + } + common_chats_count = stream.readInt32(exception); + if ((flags & 2048) != 0) { + folder_id = stream.readInt32(exception); + } + if ((flags & 16384) != 0) { + ttl_period = stream.readInt32(exception); + } + if ((flags & 32768) != 0) { + theme_emoticon = stream.readString(exception); + } + if ((flags & 65536) != 0) { + private_forward_name = stream.readString(exception); + } + if ((flags & 131072) != 0) { + bot_group_admin_rights = TL_chatAdminRights.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 262144) != 0) { + bot_broadcast_admin_rights = TL_chatAdminRights.TLdeserialize(stream, stream.readInt32(exception), exception); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = blocked ? (flags | 1) : (flags &~ 1); + flags = phone_calls_available ? (flags | 16) : (flags &~ 16); + flags = phone_calls_private ? (flags | 32) : (flags &~ 32); + flags = can_pin_message ? (flags | 128) : (flags &~ 128); + flags = has_scheduled ? (flags | 4096) : (flags &~ 4096); + flags = video_calls_available ? (flags | 8192) : (flags &~ 8192); + stream.writeInt32(flags); + stream.writeInt64(id); + if ((flags & 2) != 0) { + stream.writeString(about); + } + settings.serializeToStream(stream); + if ((flags & 4) != 0) { + profile_photo.serializeToStream(stream); + } + notify_settings.serializeToStream(stream); + if ((flags & 8) != 0) { + bot_info.serializeToStream(stream); + } + if ((flags & 64) != 0) { + stream.writeInt32(pinned_msg_id); + } + stream.writeInt32(common_chats_count); + if ((flags & 2048) != 0) { + stream.writeInt32(folder_id); + } + if ((flags & 16384) != 0) { + stream.writeInt32(ttl_period); + } + if ((flags & 32768) != 0) { + stream.writeString(theme_emoticon); + } + if ((flags & 65536) != 0) { + stream.writeString(private_forward_name); + } + if ((flags & 131072) != 0) { + bot_group_admin_rights.serializeToStream(stream); + } + if ((flags & 262144) != 0) { + bot_broadcast_admin_rights.serializeToStream(stream); + } + } + } + + public static class TL_userFull_layer139 extends UserFull { + public static int constructor = 0xcf366521; + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + blocked = (flags & 1) != 0; + phone_calls_available = (flags & 16) != 0; + phone_calls_private = (flags & 32) != 0; + can_pin_message = (flags & 128) != 0; + has_scheduled = (flags & 4096) != 0; + video_calls_available = (flags & 8192) != 0; + id = stream.readInt64(exception); + if ((flags & 2) != 0) { + about = stream.readString(exception); + } + settings = TL_peerSettings.TLdeserialize(stream, stream.readInt32(exception), exception); + if ((flags & 4) != 0) { + profile_photo = Photo.TLdeserialize(stream, stream.readInt32(exception), exception); + } + notify_settings = PeerNotifySettings.TLdeserialize(stream, stream.readInt32(exception), exception); + if ((flags & 8) != 0) { + bot_info = BotInfo.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 64) != 0) { + pinned_msg_id = stream.readInt32(exception); + } + common_chats_count = stream.readInt32(exception); + if ((flags & 2048) != 0) { + folder_id = stream.readInt32(exception); + } + if ((flags & 16384) != 0) { + ttl_period = stream.readInt32(exception); + } + if ((flags & 32768) != 0) { + theme_emoticon = stream.readString(exception); + } + if ((flags & 65536) != 0) { + private_forward_name = stream.readString(exception); } } @@ -48052,7 +49957,7 @@ public static abstract class Updates extends TLObject { public int pts_count; public MessageFwdHeader fwd_from; public long via_bot_id; - public TL_messageReplyHeader reply_to; + public MessageReplyHeader reply_to; public ArrayList entities = new ArrayList<>(); public MessageMedia media; public Update update; @@ -48174,7 +50079,7 @@ public void readParams(AbstractSerializedData stream, boolean exception) { via_bot_id = stream.readInt64(exception); } if ((flags & 8) != 0) { - reply_to = TL_messageReplyHeader.TLdeserialize(stream, stream.readInt32(exception), exception); + reply_to = MessageReplyHeader.TLdeserialize(stream, stream.readInt32(exception), exception); } if ((flags & 128) != 0) { int magic = stream.readInt32(exception); @@ -48270,7 +50175,7 @@ public void readParams(AbstractSerializedData stream, boolean exception) { via_bot_id = stream.readInt64(exception); } if ((flags & 8) != 0) { - reply_to = TL_messageReplyHeader.TLdeserialize(stream, stream.readInt32(exception), exception); + reply_to = MessageReplyHeader.TLdeserialize(stream, stream.readInt32(exception), exception); } if ((flags & 128) != 0) { int magic = stream.readInt32(exception); @@ -48367,6 +50272,8 @@ public static abstract class WallPaper extends TLObject { public String slug; public Document document; public WallPaperSettings settings; + public String uploadingImage;//custom + public Bitmap stripedThumb;//custom public static WallPaper TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { WallPaper result = null; @@ -52321,7 +54228,7 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_messages_sendMessage extends TLObject { - public static int constructor = 0x1cc20387; + public static int constructor = 0x280d096f; public int flags; public boolean no_webpage; @@ -52331,8 +54238,7 @@ public static class TL_messages_sendMessage extends TLObject { public boolean noforwards; public boolean update_stickersets_order; public InputPeer peer; - public int reply_to_msg_id; - public int top_msg_id; + public InputReplyTo reply_to; public String message; public long random_id; public ReplyMarkup reply_markup; @@ -52355,10 +54261,7 @@ public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(flags); peer.serializeToStream(stream); if ((flags & 1) != 0) { - stream.writeInt32(reply_to_msg_id); - } - if ((flags & 512) != 0) { - stream.writeInt32(top_msg_id); + reply_to.serializeToStream(stream); } stream.writeString(message); stream.writeInt64(random_id); @@ -52383,7 +54286,7 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_messages_sendMedia extends TLObject { - public static int constructor = 0x7547c966; + public static int constructor = 0x72ccc23d; public int flags; public boolean silent; @@ -52392,8 +54295,7 @@ public static class TL_messages_sendMedia extends TLObject { public boolean noforwards; public boolean update_stickersets_order; public InputPeer peer; - public int reply_to_msg_id; - public int top_msg_id; + public InputReplyTo reply_to; public InputMedia media; public String message; public long random_id; @@ -52416,10 +54318,7 @@ public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(flags); peer.serializeToStream(stream); if ((flags & 1) != 0) { - stream.writeInt32(reply_to_msg_id); - } - if ((flags & 512) != 0) { - stream.writeInt32(top_msg_id); + reply_to.serializeToStream(stream); } media.serializeToStream(stream); stream.writeString(message); @@ -52779,8 +54678,11 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_photos_updateProfilePhoto extends TLObject { - public static int constructor = 0x72d4742c; + public static int constructor = 0x9e82039; + public int flags; + public boolean fallback; + public InputUser bot; public InputPhoto id; public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { @@ -52789,15 +54691,21 @@ public TLObject deserializeResponse(AbstractSerializedData stream, int construct public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); + flags = fallback ? (flags | 1) : (flags &~ 1); + stream.writeInt32(flags); + if ((flags & 2) != 0) { + bot.serializeToStream(stream); + } id.serializeToStream(stream); } } public static class TL_photos_uploadProfilePhoto extends TLObject { - public static int constructor = 0x93c9a51; + public static int constructor = 0x388a3b5; public int flags; public boolean fallback; + public InputUser bot; public InputFile file; public InputFile video; public double video_start_ts; @@ -52811,6 +54719,9 @@ public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); flags = fallback ? (flags | 8) : (flags &~ 8); stream.writeInt32(flags); + if ((flags & 32) != 0) { + bot.serializeToStream(stream); + } if ((flags & 1) != 0) { file.serializeToStream(stream); } @@ -54633,7 +56544,7 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_messages_sendInlineBotResult extends TLObject { - public static int constructor = 0xd3fbdccb; + public static int constructor = 0xf7bc68ba; public int flags; public boolean silent; @@ -54641,8 +56552,7 @@ public static class TL_messages_sendInlineBotResult extends TLObject { public boolean clear_draft; public boolean hide_via; public InputPeer peer; - public int reply_to_msg_id; - public int top_msg_id; + public InputReplyTo reply_to; public long random_id; public long query_id; public String id; @@ -54662,10 +56572,7 @@ public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(flags); peer.serializeToStream(stream); if ((flags & 1) != 0) { - stream.writeInt32(reply_to_msg_id); - } - if ((flags & 512) != 0) { - stream.writeInt32(top_msg_id); + reply_to.serializeToStream(stream); } stream.writeInt64(random_id); stream.writeInt64(query_id); @@ -55247,10 +57154,10 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_messages_sendScreenshotNotification extends TLObject { - public static int constructor = 0xc97df020; + public static int constructor = 0xa1405817; public InputPeer peer; - public int reply_to_msg_id; + public InputReplyTo reply_to; public long random_id; public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { @@ -55260,7 +57167,7 @@ public TLObject deserializeResponse(AbstractSerializedData stream, int construct public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); - stream.writeInt32(reply_to_msg_id); + reply_to.serializeToStream(stream); stream.writeInt64(random_id); } } @@ -55369,7 +57276,7 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_messages_sendMultiMedia extends TLObject { - public static int constructor = 0xb6f11a1c; + public static int constructor = 0x456e8987; public int flags; public boolean silent; @@ -55378,8 +57285,7 @@ public static class TL_messages_sendMultiMedia extends TLObject { public boolean noforwards; public boolean update_stickersets_order; public InputPeer peer; - public int reply_to_msg_id; - public int top_msg_id; + public InputReplyTo reply_to; public ArrayList multi_media = new ArrayList<>(); public int schedule_date; public InputPeer send_as; @@ -55398,10 +57304,7 @@ public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(flags); peer.serializeToStream(stream); if ((flags & 1) != 0) { - stream.writeInt32(reply_to_msg_id); - } - if ((flags & 512) != 0) { - stream.writeInt32(top_msg_id); + reply_to.serializeToStream(stream); } stream.writeInt32(0x1cb5c415); int count = multi_media.size(); @@ -59418,6 +61321,7 @@ public static abstract class MessageMedia extends TLObject { public String venue_id; public Video video_unused; public Document document; + public Document alt_document; public String captionLegacy; public TL_game game; public String phone_number; @@ -59434,6 +61338,9 @@ public static abstract class MessageMedia extends TLObject { public boolean nopremium; public MessageExtendedMedia extended_media; public boolean spoiler; + public int id; + public StoryItem storyItem; + public boolean via_mention; public static MessageMedia TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { MessageMedia result = null; @@ -59459,6 +61366,15 @@ public static MessageMedia TLdeserialize(AbstractSerializedData stream, int cons case 0x3ded6320: result = new TL_messageMediaEmpty(); break; + case 0xcbb20d88: + result = new TL_messageMediaStory(); + break; + case 0xc79aee1d: + result = new MessageMediaStoryFull(); //custom + break; + case 0xc79aee1f: + result = new MessageMediaStoryFull_old(); //custom + break; case 0x7912b71f: result = new TL_messageMediaVenue_layer71(); break; @@ -59516,9 +61432,12 @@ public static MessageMedia TLdeserialize(AbstractSerializedData stream, int cons case 0xa32dd600: result = new TL_messageMediaWebPage(); break; - case 0x9cb070d7: + case 0x4cf4d72d: result = new TL_messageMediaDocument(); break; + case 0x9cb070d7: + result = new TL_messageMediaDocument_layer159(); + break; case 0xcbf24940: result = new TL_messageMediaContact_layer131(); break; @@ -59960,7 +61879,7 @@ public static class Message extends TLObject { public boolean pinned; public MessageFwdHeader fwd_from; public long via_bot_id; - public TL_messageReplyHeader reply_to; + public MessageReplyHeader reply_to; public String post_author; public long grouped_id; public TL_messageReactions reactions; @@ -59996,6 +61915,7 @@ public static class Message extends TLObject { public String originalLanguage; //custom public String translatedToLanguage; //custom public TL_textWithEntities translatedText; // custom + public StoryItem replyStory; //custom // NekoX Customs public String translatedMessage; //custom @@ -60812,7 +62732,7 @@ public void readParams(AbstractSerializedData stream, boolean exception) { via_bot_id = stream.readInt64(exception); } if ((flags & 8) != 0) { - reply_to = TL_messageReplyHeader.TLdeserialize(stream, stream.readInt32(exception), exception); + reply_to = MessageReplyHeader.TLdeserialize(stream, stream.readInt32(exception), exception); } date = stream.readInt32(exception); message = stream.readString(exception); @@ -60996,7 +62916,7 @@ public void readParams(AbstractSerializedData stream, boolean exception) { via_bot_id = stream.readInt64(exception); } if ((flags & 8) != 0) { - reply_to = TL_messageReplyHeader.TLdeserialize(stream, stream.readInt32(exception), exception); + reply_to = MessageReplyHeader.TLdeserialize(stream, stream.readInt32(exception), exception); } date = stream.readInt32(exception); message = stream.readString(exception); @@ -61172,7 +63092,7 @@ public void readParams(AbstractSerializedData stream, boolean exception) { via_bot_id = stream.readInt32(exception); } if ((flags & 8) != 0) { - reply_to = TL_messageReplyHeader.TLdeserialize(stream, stream.readInt32(exception), exception); + reply_to = MessageReplyHeader.TLdeserialize(stream, stream.readInt32(exception), exception); } date = stream.readInt32(exception); message = stream.readString(exception); @@ -61347,7 +63267,7 @@ public void readParams(AbstractSerializedData stream, boolean exception) { via_bot_id = stream.readInt32(exception); } if ((flags & 8) != 0) { - reply_to = TL_messageReplyHeader.TLdeserialize(stream, stream.readInt32(exception), exception); + reply_to = MessageReplyHeader.TLdeserialize(stream, stream.readInt32(exception), exception); } date = stream.readInt32(exception); message = stream.readString(exception); @@ -62943,7 +64863,7 @@ public void readParams(AbstractSerializedData stream, boolean exception) { } peer_id = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); if ((flags & 8) != 0) { - reply_to = TL_messageReplyHeader.TLdeserialize(stream, stream.readInt32(exception), exception); + reply_to = MessageReplyHeader.TLdeserialize(stream, stream.readInt32(exception), exception); } date = stream.readInt32(exception); action = MessageAction.TLdeserialize(stream, stream.readInt32(exception), exception); @@ -62990,7 +64910,7 @@ public void readParams(AbstractSerializedData stream, boolean exception) { } peer_id = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); if ((flags & 8) != 0) { - reply_to = TL_messageReplyHeader.TLdeserialize(stream, stream.readInt32(exception), exception); + reply_to = MessageReplyHeader.TLdeserialize(stream, stream.readInt32(exception), exception); } date = stream.readInt32(exception); action = MessageAction.TLdeserialize(stream, stream.readInt32(exception), exception); @@ -64505,7 +66425,7 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_messages_requestWebView extends TLObject { - public static int constructor = 0xfc87a53c; + public static int constructor = 0x269dc2c1; public int flags; public boolean from_bot_menu; @@ -64516,7 +66436,7 @@ public static class TL_messages_requestWebView extends TLObject { public String start_param; public TL_dataJSON theme_params; public String platform; - public int reply_to_msg_id; + public InputReplyTo reply_to; public InputPeer send_as; public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { @@ -64541,7 +66461,7 @@ public void serializeToStream(AbstractSerializedData stream) { } stream.writeString(platform); if ((flags & 1) != 0) { - stream.writeInt32(reply_to_msg_id); + reply_to.serializeToStream(stream); } if ((flags & 8192) != 0) { send_as.serializeToStream(stream); @@ -64550,14 +66470,14 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_messages_prolongWebView extends TLObject { - public static int constructor = 0xea5fbcce; + public static int constructor = 0xb0d81a83; public int flags; public boolean silent; public InputPeer peer; public InputUser bot; public long query_id; - public int reply_to_msg_id; + public InputReplyTo reply_to; public InputPeer send_as; public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { @@ -64572,7 +66492,7 @@ public void serializeToStream(AbstractSerializedData stream) { bot.serializeToStream(stream); stream.writeInt64(query_id); if ((flags & 1) != 0) { - stream.writeInt32(reply_to_msg_id); + reply_to.serializeToStream(stream); } if ((flags & 8192) != 0) { send_as.serializeToStream(stream); @@ -66773,6 +68693,23 @@ public void serializeToStream(AbstractSerializedData stream) { stream.writeBool(enabled); } } + + public static class TL_channels_clickSponsoredMessage extends TLObject { + public static int constructor = 0x18afbc93; + + public InputChannel channel; + public byte[] random_id; + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + channel.serializeToStream(stream); + stream.writeByteArray(random_id); + } + } public static class TL_messages_setDefaultHistoryTTL extends TLObject { public static int constructor = 0x9eb51445; @@ -67068,6 +69005,1553 @@ public void serializeToStream(AbstractSerializedData stream) { } } + public static class TL_chatlists_exportChatlistInvite extends TLObject { + public static int constructor = 0x8472478e; + + public TL_inputChatlistDialogFilter chatlist; + public String title; + public ArrayList peers = new ArrayList<>(); + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return TL_chatlists_exportedChatlistInvite.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + chatlist.serializeToStream(stream); + stream.writeString(title); + stream.writeInt32(0x1cb5c415); + int count = peers.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + peers.get(a).serializeToStream(stream); + } + } + } + + public static class TL_chatlists_deleteExportedInvite extends TLObject { + public static int constructor = 0x719c5c5e; + + public TL_inputChatlistDialogFilter chatlist; + public String slug; + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + chatlist.serializeToStream(stream); + stream.writeString(slug); + } + } + + public static class TL_chatlists_editExportedInvite extends TLObject { + public static int constructor = 0x653db63d; + + public int flags; + public boolean revoked; + public TL_inputChatlistDialogFilter chatlist; + public String slug; + public String title; + public ArrayList peers = new ArrayList<>(); + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return TL_exportedChatlistInvite.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = revoked ? (flags | 1) : (flags &~ 1); + stream.writeInt32(flags); + chatlist.serializeToStream(stream); + stream.writeString(slug); + if ((flags & 2) != 0) { + stream.writeString(title); + } + if ((flags & 4) != 0) { + stream.writeInt32(0x1cb5c415); + int count = peers.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + peers.get(a).serializeToStream(stream); + } + } + } + } + + public static class TL_chatlists_getExportedInvites extends TLObject { + public static int constructor = 0xce03da83; + + public TL_inputChatlistDialogFilter chatlist; + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return TL_chatlists_exportedInvites.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + chatlist.serializeToStream(stream); + } + } + + public static class TL_chatlists_checkChatlistInvite extends TLObject { + public static int constructor = 0x41c10fff; + + public String slug; + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return chatlist_ChatlistInvite.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(slug); + } + } + + public static class TL_chatlists_joinChatlistInvite extends TLObject { + public static int constructor = 0xa6b1e39a; + + public String slug; + public ArrayList peers = new ArrayList<>(); + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return Updates.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(slug); + stream.writeInt32(0x1cb5c415); + int count = peers.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + peers.get(a).serializeToStream(stream); + } + } + } + + public static class TL_chatlists_getChatlistUpdates extends TLObject { + public static int constructor = 0x89419521; + + public TL_inputChatlistDialogFilter chatlist; + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return TL_chatlists_chatlistUpdates.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + chatlist.serializeToStream(stream); + } + } + + public static class TL_chatlists_joinChatlistUpdates extends TLObject { + public static int constructor = 0xe089f8f5; + + public TL_inputChatlistDialogFilter chatlist; + public ArrayList peers = new ArrayList<>(); + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return Updates.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + chatlist.serializeToStream(stream); + stream.writeInt32(0x1cb5c415); + int count = peers.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + peers.get(a).serializeToStream(stream); + } + } + } + + public static class TL_chatlists_hideChatlistUpdates extends TLObject { + public static int constructor = 0x66e486fb; + + public TL_inputChatlistDialogFilter chatlist; + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + chatlist.serializeToStream(stream); + } + } + + public static class TL_chatlists_getLeaveChatlistSuggestions extends TLObject { + public static int constructor = 0xfdbcd714; + + public TL_inputChatlistDialogFilter chatlist; + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + Vector vector = new Vector(); + int size = stream.readInt32(exception); + for (int a = 0; a < size; a++) { + Peer object = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return vector; + } + vector.objects.add(object); + } + return vector; + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + chatlist.serializeToStream(stream); + } + } + + public static class TL_chatlists_leaveChatlist extends TLObject { + public static int constructor = 0x74fae13a; + + public TL_inputChatlistDialogFilter chatlist; + public ArrayList peers = new ArrayList<>(); + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return Updates.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + chatlist.serializeToStream(stream); + stream.writeInt32(0x1cb5c415); + int count = peers.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + peers.get(a).serializeToStream(stream); + } + } + } + + public static class TL_messages_setChatWallPaper extends TLObject { + public static int constructor = 0x8ffacae1; + + public int flags; + public InputPeer peer; + public InputWallPaper wallpaper; + public WallPaperSettings settings; + public int id; + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return Updates.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(flags); + peer.serializeToStream(stream); + if ((flags & 1) != 0) { + wallpaper.serializeToStream(stream); + } + if ((flags & 4) != 0) { + settings.serializeToStream(stream); + } + if ((flags & 2) != 0) { + stream.writeInt32(id); + } + } + } + + public static abstract class StoryItem extends TLObject { + + public int flags; + public boolean pinned; + public boolean isPublic; + public boolean close_friends; + public boolean contacts; + public boolean selected_contacts; + public boolean noforwards; + public boolean min; + public int id; + public int date; + public int expire_date; + public String caption; + public boolean edited; + public ArrayList entities = new ArrayList<>(); + public MessageMedia media; + public ArrayList privacy = new ArrayList<>(); + public TL_storyViews views; + public long lastUpdateTime; //custom + public String attachPath; //custom + public String firstFramePath; //custom + public long dialogId;// custom + public boolean justUploaded;// custom + public int messageId;//custom + public int messageType;//custom + public int fileReference; + + public static StoryItem TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + StoryItem result = null; + switch (constructor) { + case 0x562aa637: + result = new TL_storyItem(); + break; + case 0x51e6ee4f: + result = new TL_storyItemDeleted(); + break; + case 0xffadc913: + result = new TL_storyItemSkipped(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in StoryItem", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_storyViews extends TLObject { + public static int constructor = 0xd36760cf; + + public int flags; + public int views_count; + public ArrayList recent_viewers = new ArrayList<>(); + + public static TL_storyViews TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + if (TL_storyViews.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_storyViews", constructor)); + } else { + return null; + } + } + TL_storyViews result = new TL_storyViews(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + views_count = stream.readInt32(exception); + if ((flags & 1) != 0) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + recent_viewers.add(stream.readInt64(exception)); + } + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(flags); + stream.writeInt32(views_count); + if ((flags & 1) != 0) { + stream.writeInt32(0x1cb5c415); + int count = recent_viewers.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt64(recent_viewers.get(a)); + } + } + } + } + + public static class TL_storyItem extends StoryItem { + public static int constructor = 0x562aa637; + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + pinned = (flags & 32) != 0; + isPublic = (flags & 128) != 0; + close_friends = (flags & 256) != 0; + min = (flags & 512) != 0; + noforwards = (flags & 1024) != 0; + edited = (flags & 2048) != 0; + contacts = (flags & 4096) != 0; + selected_contacts = (flags & 8192) != 0; + id = stream.readInt32(exception); + date = stream.readInt32(exception); + expire_date = stream.readInt32(exception); + if ((flags & 1) != 0) { + caption = stream.readString(exception); + } + if ((flags & 2) != 0) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + MessageEntity object = MessageEntity.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + entities.add(object); + } + } + media = MessageMedia.TLdeserialize(stream, stream.readInt32(exception), exception); + if ((flags & 4) != 0) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + PrivacyRule object = PrivacyRule.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + privacy.add(object); + } + } + if ((flags & 8) != 0) { + views = TL_storyViews.TLdeserialize(stream, stream.readInt32(exception), exception); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = pinned ? (flags | 32) : (flags &~ 32); + flags = isPublic ? (flags | 128) : (flags &~ 128); + flags = close_friends ? (flags | 256) : (flags &~ 256); + flags = min ? (flags | 512) : (flags &~ 512); + flags = noforwards ? (flags | 1024) : (flags &~ 1024); + flags = edited ? (flags | 2048) : (flags &~ 2048); + flags = contacts ? (flags | 4096) : (flags &~ 4096); + flags = selected_contacts ? (flags | 8192) : (flags &~ 8192); + stream.writeInt32(flags); + stream.writeInt32(id); + stream.writeInt32(date); + stream.writeInt32(expire_date); + if ((flags & 1) != 0) { + stream.writeString(caption); + } + if ((flags & 2) != 0) { + stream.writeInt32(0x1cb5c415); + int count = entities.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + entities.get(a).serializeToStream(stream); + } + } + media.serializeToStream(stream); + if ((flags & 4) != 0) { + stream.writeInt32(0x1cb5c415); + int count = privacy.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + privacy.get(a).serializeToStream(stream); + } + } + if ((flags & 8) != 0) { + views.serializeToStream(stream); + } + } + } + + public static class TL_storyItemDeleted extends StoryItem { + public static int constructor = 0x51e6ee4f; + + public void readParams(AbstractSerializedData stream, boolean exception) { + id = stream.readInt32(exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + } + } + + public static class TL_storyItemSkipped extends StoryItem { + public static int constructor = 0xffadc913; + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + close_friends = (flags & 256) != 0; + id = stream.readInt32(exception); + date = stream.readInt32(exception); + expire_date = stream.readInt32(exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = close_friends ? (flags | 256) : (flags &~ 256); + stream.writeInt32(flags); + stream.writeInt32(id); + stream.writeInt32(date); + stream.writeInt32(expire_date); + } + } + + public static class TL_stories_storyViews extends TLObject { + public static int constructor = 0xde9eed1d; + + public ArrayList views = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + + public static TL_stories_storyViews TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + if (TL_stories_storyViews.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_stories_storyViews", constructor)); + } else { + return null; + } + } + TL_stories_storyViews result = new TL_stories_storyViews(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbstractSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_storyViews object = TL_storyViews.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + views.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = views.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + views.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); + } + } + } + + public static class TL_storyView extends TLObject { + public static int constructor = 0xa71aacc2; + + public long user_id; + public int date; + + public static TL_storyView TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + if (TL_storyView.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_storyView", constructor)); + } else { + return null; + } + } + TL_storyView result = new TL_storyView(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbstractSerializedData stream, boolean exception) { + user_id = stream.readInt64(exception); + date = stream.readInt32(exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(user_id); + stream.writeInt32(date); + } + } + + + public static class TL_userStories extends TLObject { + public static int constructor = 0x8611a200; + + public int flags; + public long user_id; + public int max_read_id; + public ArrayList stories = new ArrayList<>(); + + public static TL_userStories TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + if (TL_userStories.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_userStories", constructor)); + } else { + return null; + } + } + TL_userStories result = new TL_userStories(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + user_id = stream.readInt64(exception); + if ((flags & 1) != 0) { + max_read_id = stream.readInt32(exception); + } + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + StoryItem object = StoryItem.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + stories.add(object); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(flags); + stream.writeInt64(user_id); + if ((flags & 1) != 0) { + stream.writeInt32(max_read_id); + } + stream.writeInt32(0x1cb5c415); + int count = stories.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stories.get(a).serializeToStream(stream); + } + } + } + + public static class TL_stories_userStories extends TLObject { + public static int constructor = 0x37a6ff5f; + + public TL_userStories stories; + public ArrayList users = new ArrayList<>(); + + public static TL_stories_userStories TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + if (TL_stories_userStories.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_stories_userStories", constructor)); + } else { + return null; + } + } + TL_stories_userStories result = new TL_stories_userStories(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbstractSerializedData stream, boolean exception) { + stories = TL_userStories.TLdeserialize(stream, stream.readInt32(exception), exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stories.serializeToStream(stream); + stream.writeInt32(0x1cb5c415); + int count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); + } + } + } + + public static abstract class stories_AllStories extends TLObject { + + public static stories_AllStories TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + stories_AllStories result = null; + switch (constructor) { + case 0x47e0a07e: + result = new TL_stories_allStoriesNotModified(); + break; + case 0x839e0428: + result = new TL_stories_allStories(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in stories_AllStories", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_stories_allStoriesNotModified extends stories_AllStories { + public static int constructor = 0x47e0a07e; + + public String state; + + public void readParams(AbstractSerializedData stream, boolean exception) { + state = stream.readString(exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(state); + } + } + + public static class TL_stories_allStories extends stories_AllStories { + public static int constructor = 0x839e0428; + + public int flags; + public boolean has_more; + public int count; + public String state; + public ArrayList user_stories = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + has_more = (flags & 1) != 0; + count = stream.readInt32(exception); + state = stream.readString(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_userStories object = TL_userStories.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + user_stories.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = has_more ? (flags | 1) : (flags &~ 1); + stream.writeInt32(flags); + stream.writeInt32(count); + stream.writeString(state); + stream.writeInt32(0x1cb5c415); + int count = user_stories.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + user_stories.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); + } + } + } + + public static class TL_stories_sendStory extends TLObject { + public static int constructor = 0x424cd47a; + + public int flags; + public boolean pinned; + public boolean noforwards; + public InputMedia media; + public String caption; + public ArrayList entities = new ArrayList<>(); + public ArrayList privacy_rules = new ArrayList<>(); + public long random_id; + public int period; + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return Updates.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = pinned ? (flags | 4) : (flags &~ 4); + flags = noforwards ? (flags | 16) : (flags &~ 16); + stream.writeInt32(flags); + media.serializeToStream(stream); + if ((flags & 1) != 0) { + stream.writeString(caption); + } + if ((flags & 2) != 0) { + stream.writeInt32(0x1cb5c415); + int count = entities.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + entities.get(a).serializeToStream(stream); + } + } + stream.writeInt32(0x1cb5c415); + int count = privacy_rules.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + privacy_rules.get(a).serializeToStream(stream); + } + stream.writeInt64(random_id); + if ((flags & 8) != 0) { + stream.writeInt32(period); + } + } + } + + public static class TL_stories_deleteStories extends TLObject { + public static int constructor = 0xb5d501d7; + + public ArrayList id = new ArrayList<>(); + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + Vector vector = new Vector(); + int size = stream.readInt32(exception); + for (int a = 0; a < size; a++) { + vector.objects.add(stream.readInt32(exception)); + } + return vector; + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = id.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt32(id.get(a)); + } + } + } + + public static class TL_stories_togglePinned extends TLObject { + public static int constructor = 0x51602944; + + public ArrayList id = new ArrayList<>(); + public boolean pinned; + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + Vector vector = new Vector(); + int size = stream.readInt32(exception); + for (int a = 0; a < size; a++) { + vector.objects.add(stream.readInt32(exception)); + } + return vector; + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = id.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt32(id.get(a)); + } + stream.writeBool(pinned); + } + } + + public static class TL_stories_editStory extends TLObject { + public static int constructor = 0x2aae7a41; + + public int flags; + public int id; + public InputMedia media; + public String caption; + public ArrayList entities = new ArrayList<>(); + public ArrayList privacy_rules = new ArrayList<>(); + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return Updates.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(flags); + stream.writeInt32(id); + if ((flags & 1) != 0) { + media.serializeToStream(stream); + } + if ((flags & 2) != 0) { + stream.writeString(caption); + } + if ((flags & 2) != 0) { + stream.writeInt32(0x1cb5c415); + int count = entities.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + entities.get(a).serializeToStream(stream); + } + } + if ((flags & 4) != 0) { + stream.writeInt32(0x1cb5c415); + int count = privacy_rules.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + privacy_rules.get(a).serializeToStream(stream); + } + } + } + } + + public static class TL_stories_getAllStories extends TLObject { + public static int constructor = 0xeeb0d625; + + public int flags; + public boolean include_hidden; + public boolean next; + public String state; + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return stories_AllStories.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = next ? (flags | 2) : (flags &~ 2); + flags = include_hidden ? (flags | 4) : (flags &~ 4); + stream.writeInt32(flags); + if ((flags & 1) != 0) { + stream.writeString(state); + } + } + } + + public static class TL_contacts_toggleStoriesHidden extends TLObject { + public static int constructor = 0x753fb865; + + public InputUser id; + public boolean hidden; + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + id.serializeToStream(stream); + stream.writeBool(hidden); + } + } + + public static class TL_stories_stories extends TLObject { + public static int constructor = 0x4fe57df1; + + public int count; + public ArrayList stories = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + + public static TL_stories_stories TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + if (TL_stories_stories.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_stories_stories", constructor)); + } else { + return null; + } + } + TL_stories_stories result = new TL_stories_stories(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbstractSerializedData stream, boolean exception) { + count = stream.readInt32(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + StoryItem object = StoryItem.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + stories.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(count); + stream.writeInt32(0x1cb5c415); + int count = stories.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stories.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); + } + } + } + + public static class TL_stories_getUserStories extends TLObject { + public static int constructor = 0x96d528e0; + + public InputUser user_id; + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return TL_stories_userStories.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + user_id.serializeToStream(stream); + } + } + + public static class TL_stories_getPinnedStories extends TLObject { + public static int constructor = 0xb471137; + + public InputUser user_id; + public int offset_id; + public int limit; + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return TL_stories_stories.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + user_id.serializeToStream(stream); + stream.writeInt32(offset_id); + stream.writeInt32(limit); + } + } + + public static class TL_updateStory extends Update { + public static int constructor = 0x205a4133; + + public long user_id; + public StoryItem story; + + public void readParams(AbstractSerializedData stream, boolean exception) { + user_id = stream.readInt64(exception); + story = StoryItem.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(user_id); + story.serializeToStream(stream); + } + } + + public static class TL_stories_getStoriesArchive extends TLObject { + public static int constructor = 0x1f5bc5d2; + + public int offset_id; + public int limit; + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return TL_stories_stories.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(offset_id); + stream.writeInt32(limit); + } + } + + public static class TL_updateReadStories extends Update { + public static int constructor = 0xfeb5345a; + + public long user_id; + public int max_id; + + public void readParams(AbstractSerializedData stream, boolean exception) { + user_id = stream.readInt64(exception); + max_id = stream.readInt32(exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(user_id); + stream.writeInt32(max_id); + } + } + + public static class TL_stories_storyViewsList extends TLObject { + public static int constructor = 0xfb3f77ac; + + public int count; + public ArrayList views = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + + public static TL_stories_storyViewsList TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + if (TL_stories_storyViewsList.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_stories_storyViewsList", constructor)); + } else { + return null; + } + } + TL_stories_storyViewsList result = new TL_stories_storyViewsList(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbstractSerializedData stream, boolean exception) { + count = stream.readInt32(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_storyView object = TL_storyView.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + views.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(count); + stream.writeInt32(0x1cb5c415); + int count = views.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + views.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); + } + } + } + + public static class TL_stories_readStories extends TLObject { + public static int constructor = 0xedc5105b; + + public InputUser user_id; + public int max_id; + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + Vector vector = new Vector(); + int size = stream.readInt32(exception); + for (int a = 0; a < size; a++) { + vector.objects.add(stream.readInt32(exception)); + } + return vector; + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + user_id.serializeToStream(stream); + stream.writeInt32(max_id); + } + } + + public static class TL_stories_getStoryViewsList extends TLObject { + public static int constructor = 0x4b3b5e97; + + public int id; + public int offset_date; + public long offset_id; + public int limit; + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return TL_stories_storyViewsList.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(id); + stream.writeInt32(offset_date); + stream.writeInt64(offset_id); + stream.writeInt32(limit); + } + } + + public static class TL_editCloseFriends extends TLObject { + public static int constructor = 0xba6705f0; + + public ArrayList id = new ArrayList<>(); + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = id.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt64(id.get(a)); + } + } + } + + public static class TL_stories_getStoriesByID extends TLObject { + public static int constructor = 0x6a15cf46; + + public InputUser user_id; + public ArrayList id = new ArrayList<>(); + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return TL_stories_stories.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + user_id.serializeToStream(stream); + stream.writeInt32(0x1cb5c415); + int count = id.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt32(id.get(a)); + } + } + } + + public static class TL_stories_getStoriesViews extends TLObject { + public static int constructor = 0x9a75d6a6; + + public ArrayList id = new ArrayList<>(); + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return TL_stories_storyViews.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = id.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt32(id.get(a)); + } + } + } + + public static abstract class InputReplyTo extends TLObject { + + public static InputReplyTo TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + InputReplyTo result = null; + switch (constructor) { + case 0x9c5386e4: + result = new TL_inputReplyToMessage(); + break; + case 0x15b0f283: + result = new TL_inputReplyToStory(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in InputReplyTo", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_inputReplyToMessage extends InputReplyTo { + public static int constructor = 0x9c5386e4; + + public int flags; + public int reply_to_msg_id; + public int top_msg_id; + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + reply_to_msg_id = stream.readInt32(exception); + if ((flags & 1) != 0) { + top_msg_id = stream.readInt32(exception); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(flags); + stream.writeInt32(reply_to_msg_id); + if ((flags & 1) != 0) { + stream.writeInt32(top_msg_id); + } + } + } + + public static class TL_inputReplyToStory extends InputReplyTo { + public static int constructor = 0x15b0f283; + + public InputUser user_id; + public int story_id; + + public void readParams(AbstractSerializedData stream, boolean exception) { + user_id = InputUser.TLdeserialize(stream, stream.readInt32(exception), exception); + story_id = stream.readInt32(exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + user_id.serializeToStream(stream); + stream.writeInt32(story_id); + } + } + + public static class TL_exportedStoryLink extends TLObject { + public static int constructor = 0x3fc9053b; + + public String link; + + public static TL_exportedStoryLink TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + if (TL_exportedStoryLink.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_exportedStoryLink", constructor)); + } else { + return null; + } + } + TL_exportedStoryLink result = new TL_exportedStoryLink(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbstractSerializedData stream, boolean exception) { + link = stream.readString(exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(link); + } + } + + public static class TL_stories_exportStoryLink extends TLObject { + public static int constructor = 0x16e443ce; + + public InputUser user_id; + public int id; + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return TL_exportedStoryLink.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + user_id.serializeToStream(stream); + stream.writeInt32(id); + } + } + + public static class TL_inputMediaStory extends InputMedia { + public static int constructor = 0x9a86b58f; + + public InputUser user_id; + public int id; + + public void readParams(AbstractSerializedData stream, boolean exception) { + user_id = InputUser.TLdeserialize(stream, stream.readInt32(exception), exception); + id = stream.readInt32(exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + user_id.serializeToStream(stream); + stream.writeInt32(id); + } + } + + public static class TL_messageMediaStory extends MessageMedia { + public static int constructor = 0xcbb20d88; + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + via_mention = (flags & 2) != 0; + user_id = stream.readInt64(exception); + id = stream.readInt32(exception); + if ((flags & 1) != 0) { + storyItem = TLRPC.StoryItem.TLdeserialize(stream, stream.readInt32(exception), exception); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + if (storyItem != null) { + flags |= 1; + } else { + flags &= ~1; + } + stream.writeInt32(constructor); + flags = via_mention ? (flags | 2) : (flags &~ 2); + stream.writeInt32(flags); + stream.writeInt64(user_id); + stream.writeInt32(id); + if ((flags & 1) != 0) { + storyItem.serializeToStream(stream); + } + } + } + + public static class TL_stories_report extends TLObject { + public static int constructor = 0xc95be06a; + + public InputUser user_id; + public ArrayList id = new ArrayList<>(); + public ReportReason reason; + public String message; + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + user_id.serializeToStream(stream); + stream.writeInt32(0x1cb5c415); + int count = id.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt32(id.get(a)); + } + reason.serializeToStream(stream); + stream.writeString(message); + } + } + + public static class TL_stories_getAllReadUserStories extends TLObject { + public static int constructor = 0x729c562c; + + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return Updates.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_users_getStoriesMaxIDs extends TLObject { + public static int constructor = 0xca1cb9ab; + + public ArrayList id = new ArrayList<>(); + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + Vector vector = new Vector(); + int size = stream.readInt32(exception); + for (int a = 0; a < size; a++) { + vector.objects.add(stream.readInt32(exception)); + } + return vector; + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = id.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + id.get(a).serializeToStream(stream); + } + } + } + //functions public static class Vector extends TLObject { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBar.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBar.java index 422d3baf7e..4010f44194 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBar.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBar.java @@ -92,7 +92,7 @@ public boolean canOpenMenu() { private ActionBarMenu actionMode; private String actionModeTag; private boolean ignoreLayoutRequest; - private boolean occupyStatusBar = Build.VERSION.SDK_INT >= 21; + protected boolean occupyStatusBar = Build.VERSION.SDK_INT >= 21; private boolean actionModeVisible; private boolean addToContainer = true; private boolean clipContent; @@ -142,6 +142,10 @@ public boolean canOpenMenu() { private CharSequence subtitle; private boolean drawBackButton; private boolean attached; + private boolean resumed; + private boolean attachState; + private FrameLayout titlesContainer; + private boolean useContainerForTitles; private View.OnTouchListener interceptTouchEventListener; private final Theme.ResourcesProvider resourcesProvider; @@ -285,7 +289,7 @@ public boolean onInterceptTouchEvent(MotionEvent ev) { } protected boolean shouldClipChild(View child) { - return clipContent && (child == titleTextView[0] || child == titleTextView[1] || child == subtitleTextView || child == menu || child == backButtonImageView || child == additionalSubtitleTextView); + return clipContent && (child == titleTextView[0] || child == titleTextView[1] || child == subtitleTextView || child == menu || child == backButtonImageView || child == additionalSubtitleTextView || child == titlesContainer); } @Override @@ -452,7 +456,11 @@ public void setAlpha(float alpha) { titleTextView[i].setDrawablePadding(AndroidUtilities.dp(4)); titleTextView[i].setPadding(0, AndroidUtilities.dp(8), 0, AndroidUtilities.dp(8)); titleTextView[i].setRightDrawableTopPadding(-AndroidUtilities.dp(1)); - addView(titleTextView[i], 0, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP)); + if (useContainerForTitles) { + titlesContainer.addView(titleTextView[i], 0, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP)); + } else { + addView(titleTextView[i], 0, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP)); + } } public void setTitleRightMargin(int value) { @@ -1439,7 +1447,14 @@ public void onMenuButtonPressed() { } } + public void onResume() { + resumed = true; + updateAttachState(); + } + protected void onPause() { + resumed = false; + updateAttachState(); if (menu != null) { menu.hideAllPopupMenus(); } @@ -1724,7 +1739,7 @@ public boolean hasOverlappingRendering() { protected void onAttachedToWindow() { super.onAttachedToWindow(); attached = true; - ellipsizeSpanAnimator.onAttachedToWindow(); + updateAttachState(); if (SharedConfig.noStatusBar && actionModeVisible) { if (ColorUtils.calculateLuminance(actionModeColor) < 0.7f) { AndroidUtilities.setLightStatusBar(((Activity) getContext()).getWindow(), false); @@ -1741,7 +1756,7 @@ protected void onAttachedToWindow() { protected void onDetachedFromWindow() { super.onDetachedFromWindow(); attached = false; - ellipsizeSpanAnimator.onDetachedFromWindow(); + updateAttachState(); if (SharedConfig.noStatusBar && actionModeVisible) { if (actionBarColor == 0) { NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.needCheckSystemBarColors); @@ -1758,6 +1773,18 @@ protected void onDetachedFromWindow() { } } + private void updateAttachState() { + boolean attachState = attached && resumed; + if (this.attachState != attachState) { + this.attachState = attachState; + if (attachState) { + ellipsizeSpanAnimator.onAttachedToWindow(); + } else { + ellipsizeSpanAnimator.onDetachedFromWindow(); + } + } + } + public ActionBarMenu getActionMode() { return actionMode; } @@ -1767,7 +1794,7 @@ public void setOverlayTitleAnimation(boolean ovelayTitleAnimation) { } public void beginDelayedTransition() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && !LocaleController.isRTL) { TransitionSet transitionSet = new TransitionSet(); transitionSet.setOrdering(TransitionSet.ORDERING_TOGETHER); transitionSet.addTransition(new Fade()); @@ -1832,12 +1859,8 @@ public void onAnimationEnd(Animator animation) { } } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - if (color == null) { - color = parentFragment != null ? parentFragment.getThemedColor(key) : null; - } - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } public void setDrawBlurBackground(SizeNotifierFrameLayout contentView) { @@ -1868,7 +1891,6 @@ public void setDrawBackButton(boolean b) { } } - // NekoX Changes private StaticLayout countLayout; @@ -1918,4 +1940,26 @@ public void unreadBadgeSetCount(int count) { backButtonImageView.setUnread(count); } } + + public void setUseContainerForTitles() { + this.useContainerForTitles = true; + if (titlesContainer == null) { + titlesContainer = new FrameLayout(getContext()) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec)); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + + } + }; + addView(titlesContainer); + } + } + + public FrameLayout getTitlesContainer() { + return titlesContainer; + } } \ No newline at end of file diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.java index 6769b63e97..af8b64453e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.java @@ -46,6 +46,7 @@ import androidx.core.math.MathUtils; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.BuildVars; import org.telegram.messenger.FileLog; import org.telegram.messenger.ImageLoader; @@ -59,6 +60,7 @@ import org.telegram.ui.Components.FloatingDebug.FloatingDebugProvider; import org.telegram.ui.Components.GroupCallPip; import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Stories.StoryViewer; import java.util.ArrayList; import java.util.HashMap; @@ -78,6 +80,14 @@ public void setHighlightActionButtons(boolean highlightActionButtons) { this.highlightActionButtons = highlightActionButtons; } + public boolean storyViewerAttached() { + BaseFragment lastFragment = null; + if (!fragmentsStack.isEmpty()) { + lastFragment = fragmentsStack.get(fragmentsStack.size() - 1); + } + return lastFragment != null && lastFragment.storyViewer != null && lastFragment.storyViewer.attachedToParent(); + } + public class LayoutContainer extends FrameLayout { private Rect rect = new Rect(); @@ -96,6 +106,13 @@ public LayoutContainer(Context context) { @Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + BaseFragment lastFragment = null; + if (!fragmentsStack.isEmpty()) { + lastFragment = fragmentsStack.get(fragmentsStack.size() - 1); + } + if (storyViewerAttached() && lastFragment.storyViewer.isFullyVisible() && !lastFragment.isStoryViewer(child)) { + return true; + } if (child instanceof ActionBar) { return super.drawChild(canvas, child, drawingTime); } else { @@ -357,6 +374,7 @@ public void processMenuButtonsTouch(MotionEvent event) { private ArrayList presentingFragmentDescriptions; private ArrayList themeAnimatorDelegate = new ArrayList<>(); private AnimatorSet themeAnimatorSet; + AnimationNotificationsLocker notificationsLocker = new AnimationNotificationsLocker(); private float themeAnimationValue; private boolean animateThemeAfterAnimation; private Theme.ThemeInfo animateSetThemeAfterAnimation; @@ -445,6 +463,17 @@ public void onConfigurationChanged(android.content.res.Configuration newConfig) private int[] measureSpec = new int[2]; @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + BaseFragment lastFragment = null; + if (!fragmentsStack.isEmpty()) { + lastFragment = fragmentsStack.get(fragmentsStack.size() - 1); + } + if (lastFragment != null && storyViewerAttached()) { + //remeasure only storyViewer if keyboard visibility changed + int keyboardHeight = measureKeyboardHeight(); + lastFragment.setKeyboardHeightFromParent(keyboardHeight); + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec) + keyboardHeight, MeasureSpec.EXACTLY)); + return; + } if (delegate != null) { measureSpec[0] = widthMeasureSpec; measureSpec[1] = heightMeasureSpec; @@ -737,6 +766,7 @@ private void onSlideAnimationEnd(final boolean backAnimation) { parent.removeViewInLayout(lastFragment.actionBar); } } + lastFragment.detachStoryViewer(); } layoutToIgnore = null; } @@ -776,16 +806,14 @@ private void prepareForMoving(MotionEvent ev) { layoutParams.topMargin = layoutParams.bottomMargin = layoutParams.rightMargin = layoutParams.leftMargin = 0; fragmentView.setLayoutParams(layoutParams); if (lastFragment.actionBar != null && lastFragment.actionBar.shouldAddToContainer()) { - parent = (ViewGroup) lastFragment.actionBar.getParent(); - if (parent != null) { - parent.removeView(lastFragment.actionBar); - } + AndroidUtilities.removeFromParent(lastFragment.actionBar); if (removeActionBarExtraHeight) { lastFragment.actionBar.setOccupyStatusBar(false); } containerViewBack.addView(lastFragment.actionBar); lastFragment.actionBar.setTitleOverlayText(titleOverlayText, titleOverlayTextId, overlayAction); } + lastFragment.attachStoryViewer(containerViewBack); if (!lastFragment.hasOwnBackground && fragmentView.getBackground() == null) { fragmentView.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); } @@ -1057,6 +1085,7 @@ private void presentFragmentInternalRemoveOld(boolean removeLast, final BaseFrag parent.removeViewInLayout(fragment.actionBar); } } + fragment.detachStoryViewer(); } containerViewBack.setVisibility(View.INVISIBLE); } @@ -1098,7 +1127,7 @@ public void run() { Integer oldNavigationBarColor = oldFragment != null ? oldFragment.getNavigationBarColor() : null; Integer newNavigationBarColor = newFragment != null ? newFragment.getNavigationBarColor() : null; if (newFragment != null && oldNavigationBarColor != null) { - float ratio = MathUtils.clamp(2f * animationProgress - (open ? 1f : 0f), 0f, 1f); + float ratio = MathUtils.clamp(4f * animationProgress, 0f, 1f); newFragment.setNavigationBarColor(ColorUtils.blendARGB(oldNavigationBarColor, newNavigationBarColor, ratio)); } float interpolated; @@ -1191,6 +1220,10 @@ public boolean presentFragment(NavigationParams params) { if (fragment == null || checkTransitionAnimation() || delegate != null && check && !delegate.needPresentFragment(this, params) || !fragment.onFragmentCreate()) { return false; } + if (BuildVars.LOGS_ENABLED) { + FileLog.d("present fragment " + fragment.getClass().getSimpleName()); + } + StoryViewer.closeGlobalInstances(); if (inPreviewMode && transitionAnimationPreviewMode) { if (delayedOpenAnimationRunnable != null) { AndroidUtilities.cancelRunOnUIThread(delayedOpenAnimationRunnable); @@ -1268,13 +1301,11 @@ public boolean presentFragment(NavigationParams params) { if (removeActionBarExtraHeight) { fragment.actionBar.setOccupyStatusBar(false); } - ViewGroup parent = (ViewGroup) fragment.actionBar.getParent(); - if (parent != null) { - parent.removeView(fragment.actionBar); - } + AndroidUtilities.removeFromParent(fragment.actionBar); containerViewBack.addView(fragment.actionBar); fragment.actionBar.setTitleOverlayText(titleOverlayText, titleOverlayTextId, overlayAction); } + fragment.attachStoryViewer(containerViewBack); fragmentsStack.add(fragment); onFragmentStackChanged("presentFragment"); @@ -1533,6 +1564,7 @@ public boolean addFragmentToStack(BaseFragment fragment, int position) { parent.removeView(previousFragment.fragmentView); } } + previousFragment.detachStoryViewer(); } fragmentsStack.add(fragment); if (position != INavigationLayout.FORCE_NOT_ATTACH_VIEW) { @@ -1576,6 +1608,7 @@ private void attachView(BaseFragment fragment) { containerView.addView(fragment.actionBar); fragment.actionBar.setTitleOverlayText(titleOverlayText, titleOverlayTextId, overlayAction); } + fragment.attachStoryViewer(containerView); } private void closeLastFragmentInternalRemoveOld(BaseFragment fragment) { @@ -1721,13 +1754,11 @@ public void closeLastFragment(boolean animated, boolean forceNoAnimation) { if (removeActionBarExtraHeight) { previousFragment.actionBar.setOccupyStatusBar(false); } - parent = (ViewGroup) previousFragment.actionBar.getParent(); - if (parent != null) { - parent.removeView(previousFragment.actionBar); - } + AndroidUtilities.removeFromParent(previousFragment.actionBar); containerView.addView(previousFragment.actionBar); previousFragment.actionBar.setTitleOverlayText(titleOverlayText, titleOverlayTextId, overlayAction); } + previousFragment.attachStoryViewer(containerView); } newFragment = previousFragment; @@ -1855,6 +1886,7 @@ public void onAnimationEnd(Animator animation) { } } } + currentFragment.onFragmentClosed(); } @Override @@ -1899,13 +1931,11 @@ public void bringToFront(int i) { if (removeActionBarExtraHeight) { previousFragment.actionBar.setOccupyStatusBar(false); } - ViewGroup parent = (ViewGroup) previousFragment.actionBar.getParent(); - if (parent != null) { - parent.removeView(previousFragment.actionBar); - } + AndroidUtilities.removeFromParent(previousFragment.actionBar); containerView.addView(previousFragment.actionBar); previousFragment.actionBar.setTitleOverlayText(titleOverlayText, titleOverlayTextId, overlayAction); } + previousFragment.attachStoryViewer(containerView); previousFragment.onResume(); currentActionBar = previousFragment.actionBar; if (!previousFragment.hasOwnBackground && fragmentView.getBackground() == null) { @@ -2002,7 +2032,7 @@ public void setThemeAnimationValue(float value) { if (presentingFragmentDescriptions != null) { for (int i = 0, N = presentingFragmentDescriptions.size(); i < N; i++) { ThemeDescription description = presentingFragmentDescriptions.get(i); - String key = description.getCurrentKey(); + int key = description.getCurrentKey(); description.setColor(Theme.getColor(key), false, false); } } @@ -2126,6 +2156,7 @@ public void animateThemedValues(ThemeAnimationSettings settings, Runnable onDone animateEndColors.clear(); themeAnimatorDelegate.clear(); presentingFragmentDescriptions = null; + animationProgressListener = null; if (settings.afterAnimationRunnable != null) { settings.afterAnimationRunnable.run(); } @@ -2135,6 +2166,7 @@ public void animateThemedValues(ThemeAnimationSettings settings, Runnable onDone return; } Theme.setAnimatingColor(true); + setThemeAnimationValue(0f); if (settings.beforeAnimationRunnable != null) { settings.beforeAnimationRunnable.run(); } @@ -2142,10 +2174,12 @@ public void animateThemedValues(ThemeAnimationSettings settings, Runnable onDone if (animationProgressListener != null) { animationProgressListener.setProgress(0); } + notificationsLocker.lock(); themeAnimatorSet = new AnimatorSet(); themeAnimatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { + notificationsLocker.unlock(); if (animation.equals(themeAnimatorSet)) { themeAnimatorDescriptions.clear(); animateStartColors.clear(); @@ -2153,6 +2187,7 @@ public void onAnimationEnd(Animator animation) { themeAnimatorDelegate.clear(); Theme.setAnimatingColor(false); presentingFragmentDescriptions = null; + animationProgressListener = null; themeAnimatorSet = null; if (settings.afterAnimationRunnable != null) { settings.afterAnimationRunnable.run(); @@ -2169,6 +2204,7 @@ public void onAnimationCancel(Animator animation) { themeAnimatorDelegate.clear(); Theme.setAnimatingColor(false); presentingFragmentDescriptions = null; + animationProgressListener = null; themeAnimatorSet = null; if (settings.afterAnimationRunnable != null) { settings.afterAnimationRunnable.run(); @@ -2496,4 +2532,25 @@ protected void onDetachedFromWindow() { super.onDetachedFromWindow(); attached = false; } + + public int measureKeyboardHeight() { + View rootView = getRootView(); + getWindowVisibleDisplayFrame(rect); + if (rect.bottom == 0 && rect.top == 0) { + return 0; + } + int usableViewHeight = rootView.getHeight() - (rect.top != 0 ? AndroidUtilities.statusBarHeight : 0) - AndroidUtilities.getViewInset(rootView); + return Math.max(0, usableViewHeight - (rect.bottom - rect.top)); + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + if (getLastFragment() != null && getLastFragment().overlayStoryViewer != null && getLastFragment().overlayStoryViewer.attachedToParent()) { + return getLastFragment().overlayStoryViewer.windowView.dispatchTouchEvent(ev); + } + if (getLastFragment() != null && getLastFragment().storyViewer != null && getLastFragment().storyViewer.attachedToParent()) { + return getLastFragment().storyViewer.windowView.dispatchTouchEvent(ev); + } + return super.dispatchTouchEvent(ev); + } } \ No newline at end of file diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenu.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenu.java index 013c763290..21fcfb6430 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenu.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenu.java @@ -10,6 +10,7 @@ import android.content.Context; import android.graphics.drawable.Drawable; +import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; @@ -564,4 +565,17 @@ public void translateXItems(float offset) { public void clearSearchFilters() { } + + private Runnable onLayoutListener; + public void setOnLayoutListener(Runnable listener) { + this.onLayoutListener = listener; + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + if (onLayoutListener != null) { + onLayoutListener.run(); + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuItem.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuItem.java index fbe08f95bf..ce0bbc33ce 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuItem.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuItem.java @@ -57,6 +57,7 @@ import androidx.core.graphics.ColorUtils; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.LocaleController; import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; @@ -186,8 +187,9 @@ public interface ActionBarMenuItemDelegate { private boolean showSubmenuByMove = true; private ArrayList currentSearchFilters = new ArrayList<>(); private int selectedFilterIndex = -1; - private int notificationIndex = -1; + private final AnimationNotificationsLocker notificationsLocker = new AnimationNotificationsLocker(); private float dimMenu; + public int searchRightMargin; private float transitionOffset; private View showSubMenuFrom; @@ -524,8 +526,23 @@ public View addGap(int id) { return cell; } + public static View addGap(int id, ActionBarPopupWindow.ActionBarPopupWindowLayout popupLayout) { + View cell = new View(popupLayout.getContext()); + cell.setTag(id); + cell.setTag(R.id.object_tag, 1); + popupLayout.addView(cell); + LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) cell.getLayoutParams(); + if (LocaleController.isRTL) { + layoutParams.gravity = Gravity.RIGHT; + } + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = AndroidUtilities.dp(6); + cell.setLayoutParams(layoutParams); + return cell; + } + public ActionBarMenuSubItem addSubItem(int id, int icon, Drawable iconDrawable, CharSequence text, boolean dismiss, boolean needCheck) { - return addSubItem(id, icon, iconDrawable, text, dismiss, needCheck, null); + return addSubItem(id, icon, iconDrawable, text, dismiss, needCheck, resourcesProvider); } public ActionBarMenuSubItem addSubItem(int id, int icon, Drawable iconDrawable, CharSequence text, boolean dismiss, boolean needCheck, Theme.ResourcesProvider resourcesProvider) { @@ -834,7 +851,7 @@ protected void onDismiss() { } public boolean isSearchFieldVisible() { - return searchContainer.getVisibility() == VISIBLE; + return searchContainer != null && searchContainer.getVisibility() == VISIBLE; } @@ -966,21 +983,20 @@ public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues sta }.setDuration(150)).addTransition(changeBounds); transition.setOrdering(TransitionSet.ORDERING_TOGETHER); transition.setInterpolator(CubicBezierInterpolator.EASE_OUT); - int selectedAccount = UserConfig.selectedAccount; transition.addListener(new Transition.TransitionListener() { @Override public void onTransitionStart(Transition transition) { - notificationIndex = NotificationCenter.getInstance(selectedAccount).setAnimationInProgress(notificationIndex, null); + notificationsLocker.lock(); } @Override public void onTransitionEnd(Transition transition) { - NotificationCenter.getInstance(selectedAccount).onAnimationFinish(notificationIndex); + notificationsLocker.unlock(); } @Override public void onTransitionCancel(Transition transition) { - NotificationCenter.getInstance(selectedAccount).onAnimationFinish(notificationIndex); + notificationsLocker.unlock(); } @Override @@ -1313,7 +1329,7 @@ protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolea wrappedSearchFrameLayout.addView(horizontalScrollView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, 0, 0, 0, 48, 0)); parentMenu.addView(wrappedSearchFrameLayout, 0, LayoutHelper.createLinear(0, LayoutHelper.MATCH_PARENT, 1.0f, searchItemPaddingStart, 0, 0, 0)); } else { - parentMenu.addView(searchContainer, 0, LayoutHelper.createLinear(0, LayoutHelper.MATCH_PARENT, 1.0f, searchItemPaddingStart + 6, 0, 0, 0)); + parentMenu.addView(searchContainer, 0, LayoutHelper.createLinear(0, LayoutHelper.MATCH_PARENT, 1.0f, searchItemPaddingStart + 6, 0, searchRightMargin, 0)); } searchContainer.setVisibility(GONE); @@ -2001,9 +2017,8 @@ public void setTransitionOffset(float offset) { setTranslationX(0); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } private static class SearchFilterView extends FrameLayout { @@ -2075,7 +2090,7 @@ private void updateColors() { public void setData(FiltersView.MediaFilterData data) { this.data = data; - titleView.setText(data.title); + titleView.setText(data.getTitle()); thumbDrawable = Theme.createCircleDrawableWithIcon(AndroidUtilities.dp(32), data.iconResFilled); Theme.setCombinedDrawableColor(thumbDrawable, getThemedColor(Theme.key_avatar_backgroundBlue), false); Theme.setCombinedDrawableColor(thumbDrawable, getThemedColor(Theme.key_avatar_actionBarIconBlue), true); @@ -2149,9 +2164,8 @@ public FiltersView.MediaFilterData getFilter() { return data; } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + protected int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuSubItem.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuSubItem.java index 8b43d04181..07affcddfa 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuSubItem.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuSubItem.java @@ -85,7 +85,7 @@ public ActionBarMenuSubItem(Context context, int needCheck, boolean top, boolean if (needCheck > 0) { checkView = new CheckBox2(context, 26, resourcesProvider); checkView.setDrawUnchecked(false); - checkView.setColor(null, null, Theme.key_radioBackgroundChecked); + checkView.setColor(-1, -1, Theme.key_radioBackgroundChecked); checkView.setDrawBackgroundAsArc(-1); if (needCheck == 1) { addView(checkView, LayoutHelper.createFrame(26, LayoutHelper.MATCH_PARENT, Gravity.CENTER_VERTICAL | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT))); @@ -99,6 +99,9 @@ public ActionBarMenuSubItem(Context context, int needCheck, boolean top, boolean @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, View.MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(itemHeight), View.MeasureSpec.EXACTLY)); + if (expandIfMultiline && textView.getLayout().getLineCount() > 1) { + super.onMeasure(widthMeasureSpec, View.MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(itemHeight + 8), View.MeasureSpec.EXACTLY)); + } } public void setItemHeight(int itemHeight) { @@ -123,8 +126,8 @@ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { } } - public void setCheckColor(String colorKey) { - checkView.setColor(null, null, colorKey); + public void setCheckColor(int colorKey) { + checkView.setColor(-1, -1, colorKey); } public void setRightIcon(int icon) { @@ -145,9 +148,19 @@ public void setTextAndIcon(CharSequence text, int icon) { setTextAndIcon(text, icon, null); } + boolean expandIfMultiline; + public void setMultiline() { + setMultiline(true); + } + + public void setMultiline(boolean changeSize) { textView.setLines(2); - textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + if (changeSize) { + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + } else { + expandIfMultiline = true; + } textView.setSingleLine(false); textView.setGravity(Gravity.CENTER_VERTICAL); } @@ -264,9 +277,8 @@ void updateBackground() { setBackground(Theme.createRadSelectorDrawable(selectorColor, top ? 6 : 0, bottom ? 6 : 0)); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } public CheckBox2 getCheckView() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarPopupWindow.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarPopupWindow.java index 8a6408fc76..908a4dbb4c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarPopupWindow.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarPopupWindow.java @@ -16,6 +16,7 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.Path; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; @@ -40,8 +41,8 @@ import androidx.annotation.Nullable; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.FileLog; -import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; import org.telegram.messenger.UserConfig; import org.telegram.ui.Components.LayoutHelper; @@ -51,7 +52,6 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; -import java.util.Locale; public class ActionBarPopupWindow extends PopupWindow { @@ -85,7 +85,7 @@ public class ActionBarPopupWindow extends PopupWindow { private ViewTreeObserver.OnScrollChangedListener mSuperScrollListener; private ViewTreeObserver mViewTreeObserver; - private int popupAnimationIndex = -1; + private AnimationNotificationsLocker notificationsLocker = new AnimationNotificationsLocker(); public void setScaleOut(boolean b) { scaleOut = b; @@ -170,7 +170,13 @@ public ActionBarPopupWindowLayout(Context context, int resId, Theme.ResourcesPro try { scrollView = new ScrollView(context); -// scrollView.setVerticalScrollBarEnabled(verticalScrollBarEnabled); + scrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() { + @Override + public void onScrollChanged() { + invalidate(); + } + }); + scrollView.setVerticalScrollBarEnabled(false); if (swipeBackLayout != null) { swipeBackLayout.addView(scrollView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, shownFromBottom ? Gravity.BOTTOM : Gravity.TOP)); } else { @@ -420,6 +426,8 @@ public boolean dispatchKeyEvent(KeyEvent event) { return super.dispatchKeyEvent(event); } + Path path; + @Override protected void dispatchDraw(Canvas canvas) { if (swipeBackGravityRight) { @@ -491,9 +499,15 @@ protected void dispatchDraw(Canvas canvas) { backgroundDrawable.draw(canvas); if (hasGap) { canvas.save(); - AndroidUtilities.rectTmp2.set(backgroundDrawable.getBounds()); - AndroidUtilities.rectTmp2.inset(AndroidUtilities.dp(8), AndroidUtilities.dp(8)); - canvas.clipRect(AndroidUtilities.rectTmp2); + AndroidUtilities.rectTmp.set(backgroundDrawable.getBounds()); + AndroidUtilities.rectTmp.inset(AndroidUtilities.dp(8), AndroidUtilities.dp(8)); + if (path == null) { + path = new Path(); + } else { + path.rewind(); + } + path.addRoundRect(AndroidUtilities.rectTmp, AndroidUtilities.dp(8), AndroidUtilities.dp(8), Path.Direction.CW); + canvas.clipPath(path); for (int i = 0; i < linearLayout.getChildCount(); i++) { if (linearLayout.getChildAt(i) instanceof GapView && linearLayout.getChildAt(i).getVisibility() == View.VISIBLE) { canvas.save(); @@ -508,7 +522,7 @@ protected void dispatchDraw(Canvas canvas) { break; } } - canvas.translate(x, y * scrollView.getScaleY()); + canvas.translate(x, y * scrollView.getScaleY() - scrollView.getScrollY()); child.draw(canvas); canvas.restore(); } @@ -590,9 +604,8 @@ public void updateRadialSelectors() { } } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + protected int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } public void setOnSizeChangedListener(ActionBarPopupWindow.onSizeChangedListener onSizeChangedListener) { @@ -1024,12 +1037,12 @@ public void onAnimationEnd(Animator animation) { } unregisterListener(); if (pauseNotifications) { - NotificationCenter.getInstance(currentAccount).onAnimationFinish(popupAnimationIndex); + notificationsLocker.unlock(); } } }); if (pauseNotifications) { - popupAnimationIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(popupAnimationIndex, null); + notificationsLocker.lock(); } windowAnimatorSet.start(); } else { @@ -1064,7 +1077,7 @@ public GapView(Context context, int color, int shadowColor) { setBackgroundColor(color); } - public GapView(Context context, Theme.ResourcesProvider resourcesProvider, String colorKey) { + public GapView(Context context, Theme.ResourcesProvider resourcesProvider, int colorKey) { this(context, Theme.getColor(colorKey, resourcesProvider), Theme.getColor(Theme.key_windowBackgroundGrayShadow, resourcesProvider)); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/AdjustPanLayoutHelper.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/AdjustPanLayoutHelper.java index 0152ce2a5a..2024b3e97a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/AdjustPanLayoutHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/AdjustPanLayoutHelper.java @@ -24,8 +24,7 @@ import androidx.recyclerview.widget.ChatListItemAnimator; import org.telegram.messenger.AndroidUtilities; -import org.telegram.messenger.NotificationCenter; -import org.telegram.messenger.SharedConfig; +import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.UserConfig; import java.util.ArrayList; @@ -71,7 +70,7 @@ public View getAdjustingContentView() { View parentForListener; ValueAnimator animator; - int notificationsIndex; + AnimationNotificationsLocker notificationsLocker = new AnimationNotificationsLocker(); ArrayList viewsToHeightSet = new ArrayList<>(); protected float keyboardSize; @@ -147,7 +146,7 @@ public void onAnimationEnd(Animator animation) { animator.setDuration(keyboardDuration); animator.setInterpolator(keyboardInterpolator); - notificationsIndex = NotificationCenter.getInstance(selectedAccount).setAnimationInProgress(notificationsIndex, null); + notificationsLocker.lock(); if (needDelay) { needDelay = false; startAfter = SystemClock.elapsedRealtime() + 100; @@ -214,7 +213,7 @@ public void stopTransition() { } animationInProgress = false; usingInsetAnimator = false; - NotificationCenter.getInstance(UserConfig.selectedAccount).onAnimationFinish(notificationsIndex); + notificationsLocker.unlock(); animator = null; setViewHeight(ViewGroup.LayoutParams.MATCH_PARENT); viewsToHeightSet.clear(); @@ -228,7 +227,7 @@ public void stopTransition(float t, boolean isKeyboardVisible) { animator.cancel(); } animationInProgress = false; - NotificationCenter.getInstance(UserConfig.selectedAccount).onAnimationFinish(notificationsIndex); + notificationsLocker.unlock(); animator = null; setViewHeight(ViewGroup.LayoutParams.MATCH_PARENT); viewsToHeightSet.clear(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/AlertDialog.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/AlertDialog.java index 0bc7d31e57..ed299f7a02 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/AlertDialog.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/AlertDialog.java @@ -61,8 +61,10 @@ import org.telegram.messenger.R; import org.telegram.messenger.SharedConfig; import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.EffectsTextView; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.LineProgressView; +import org.telegram.ui.Components.LinkSpanDrawable; import org.telegram.ui.Components.RLottieDrawable; import org.telegram.ui.Components.RLottieImageView; import org.telegram.ui.Components.RadialProgressView; @@ -95,7 +97,7 @@ public class AlertDialog extends Dialog implements Drawable.Callback, Notificati private AnimatorSet[] shadowAnimation = new AnimatorSet[2]; private int customViewOffset = 12; - private String dialogButtonColorKey = Theme.key_dialogButton; + private int dialogButtonColorKey = Theme.key_dialogButton; private OnCancelListener onCancelListener; @@ -199,7 +201,7 @@ private boolean supportsNativeBlur() { public void redPositive() { TextView button = (TextView) getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(getThemedColor(Theme.key_dialogTextRed)); + button.setTextColor(getThemedColor(Theme.key_text_RedBold)); } } @@ -256,9 +258,8 @@ public void setTextAndIcon(CharSequence text, int icon) { } } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + protected int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } @@ -286,6 +287,8 @@ public AlertDialog(Context context, int progressStyle, Theme.ResourcesProvider r progressViewStyle = progressStyle; } + private long shownAt; + @Override public void show() { super.show(); @@ -298,6 +301,7 @@ public void show() { .setDuration(190) .start(); } + shownAt = System.currentTimeMillis(); } @Override @@ -718,13 +722,7 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { contentScrollView.addView(scrollContainer, new ScrollView.LayoutParams(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); } - messageTextView = new SpoilersTextView(getContext(), false) { - @Override - public void setText(CharSequence text, BufferType type) { - text = Emoji.replaceEmoji(text, getPaint().getFontMetricsInt(), AndroidUtilities.dp(14), false); - super.setText(text, type); - } - }; + messageTextView = new EffectsTextView(getContext()); NotificationCenter.listenEmojiLoading(messageTextView); messageTextView.setTextColor(getThemedColor(topAnimationIsNew ? Theme.key_windowBackgroundWhiteGrayText : Theme.key_dialogTextBlack)); messageTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); @@ -1162,7 +1160,7 @@ private void showCancelAlert() { if (!canCacnel || cancelDialog != null) { return; } - Builder builder = new Builder(getContext()); + Builder builder = new Builder(getContext(), resourcesProvider); builder.setTitle(LocaleController.getString("StopLoadingTitle", R.string.StopLoadingTitle)); builder.setMessage(LocaleController.getString("StopLoading", R.string.StopLoading)); builder.setPositiveButton(LocaleController.getString("WaitMore", R.string.WaitMore), null); @@ -1263,6 +1261,15 @@ public void didReceivedNotification(int id, int account, Object... args) { } } + public void dismissUnless(long minDuration) { + long currentShowDuration = System.currentTimeMillis() - shownAt; + if (currentShowDuration < minDuration) { + AndroidUtilities.runOnUIThread(this::dismiss, currentShowDuration - minDuration); + } else { + dismiss(); + } + } + @Override public void dismiss() { try { @@ -1426,9 +1433,8 @@ public void setPositiveButtonListener(final OnClickListener listener) { positiveButtonListener = listener; } - protected int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + protected int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } public void showDelayed(long delay) { @@ -1527,7 +1533,7 @@ public Builder setTopView(View view) { return this; } - public Builder setDialogButtonColorKey(String key) { + public Builder setDialogButtonColorKey(int key) { alertDialog.dialogButtonColorKey = key; return this; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BaseFragment.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BaseFragment.java index 41367615f2..6fa6d492a7 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BaseFragment.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BaseFragment.java @@ -17,6 +17,7 @@ import android.content.SharedPreferences; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.Paint; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; @@ -52,6 +53,8 @@ import org.telegram.messenger.UserConfig; import org.telegram.tgnet.ConnectionsManager; import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.LaunchActivity; +import org.telegram.ui.Stories.StoryViewer; import java.util.ArrayList; @@ -81,6 +84,9 @@ public abstract class BaseFragment { protected boolean fragmentBeginToShow; private boolean removingFromStack; private PreviewDelegate previewDelegate; + private Theme.ResourcesProvider resourceProvider; + public StoryViewer storyViewer; + public StoryViewer overlayStoryViewer; public BaseFragment() { classGuid = ConnectionsManager.generateClassGuid(); @@ -212,6 +218,14 @@ public void clearViews() { } actionBar = null; } + if (storyViewer != null) { + storyViewer.release(); + storyViewer = null; + } + if (overlayStoryViewer != null) { + overlayStoryViewer.release(); + overlayStoryViewer = null; + } parentLayout = null; } @@ -243,6 +257,14 @@ public void setParentLayout(INavigationLayout layout) { } if (parentLayout != null && parentLayout.getView().getContext() != fragmentView.getContext()) { fragmentView = null; + if (storyViewer != null) { + storyViewer.release(); + storyViewer = null; + } + if (overlayStoryViewer != null) { + overlayStoryViewer.release(); + overlayStoryViewer = null; + } } } if (actionBar != null) { @@ -374,6 +396,15 @@ public void onUserLeaveHint() {} @CallSuper public void onResume() { isPaused = false; + if (actionBar != null) { + actionBar.onResume(); + } + if (storyViewer != null) { + storyViewer.updatePlayingMode(); + } + if (overlayStoryViewer != null) { + overlayStoryViewer.updatePlayingMode(); + } } @CallSuper @@ -390,6 +421,12 @@ public void onPause() { } catch (Exception e) { FileLog.e(e); } + if (storyViewer != null) { + storyViewer.updatePlayingMode(); + } + if (overlayStoryViewer != null) { + overlayStoryViewer.updatePlayingMode(); + } } public void setPaused(boolean paused) { @@ -404,6 +441,10 @@ public void setPaused(boolean paused) { } } + public boolean isPaused() { + return isPaused; + } + public BaseFragment getFragmentForAlert(int offset) { if (parentLayout == null || parentLayout.getFragmentStack().size() <= 1 + offset) { return this; @@ -416,9 +457,22 @@ public void onConfigurationChanged(android.content.res.Configuration newConfig) } public boolean onBackPressed() { + if (closeStoryViewer()) { + return false; + } return true; } + public boolean closeStoryViewer() { + if (overlayStoryViewer != null && overlayStoryViewer.isShown()) { + return overlayStoryViewer.onBackPressed(); + } + if (storyViewer != null && storyViewer.isShown()) { + return storyViewer.onBackPressed(); + } + return false; + } + public void onActivityResultFragment(int requestCode, int resultCode, Intent data) { } @@ -599,6 +653,14 @@ public Dialog showDialog(Dialog dialog, boolean allowInTransition, final Dialog. if (dialog == null || parentLayout == null || parentLayout.isTransitionAnimationInProgress() || parentLayout.isSwipeInProgress() || !allowInTransition && parentLayout.checkTransitionAnimation()) { return null; } + if (overlayStoryViewer != null && overlayStoryViewer.isShown()) { + overlayStoryViewer.showDialog(dialog); + return dialog; + } + if (storyViewer != null && storyViewer.isShown()) { + storyViewer.showDialog(dialog); + return dialog; + } try { if (visibleDialog != null) { visibleDialog.dismiss(); @@ -754,20 +816,37 @@ public void setProgressToDrawerOpened(float v) { } public INavigationLayout[] showAsSheet(BaseFragment fragment) { + return showAsSheet(fragment, null); + } + + public INavigationLayout[] showAsSheet(BaseFragment fragment, BottomSheetParams params) { if (getParentActivity() == null) { return null; } - INavigationLayout[] actionBarLayout = new INavigationLayout[]{INavigationLayout.newLayout(getParentActivity())}; - BottomSheet bottomSheet = new BottomSheet(getParentActivity(), true) { + BottomSheet[] bottomSheet = new BottomSheet[1]; + INavigationLayout[] actionBarLayout = new INavigationLayout[]{INavigationLayout.newLayout(getParentActivity(), () -> bottomSheet[0])}; + bottomSheet[0] = new BottomSheet(getParentActivity(), true, fragment.getResourceProvider()) { { + drawNavigationBar = true; actionBarLayout[0].setFragmentStack(new ArrayList<>()); actionBarLayout[0].addFragmentToStack(fragment); actionBarLayout[0].showLastFragment(); actionBarLayout[0].getView().setPadding(backgroundPaddingLeft, 0, backgroundPaddingLeft, 0); containerView = actionBarLayout[0].getView(); setApplyBottomPadding(false); - setApplyBottomPadding(false); - setOnDismissListener(dialog -> fragment.onFragmentDestroy()); + setOnDismissListener(dialog -> { + fragment.onPause(); + fragment.onFragmentDestroy(); + if (params != null && params.onDismiss != null) { + params.onDismiss.run(); + } + }); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + fixNavigationBar(Theme.getColor(Theme.key_dialogBackgroundGray, fragment.getResourceProvider())); } @Override @@ -786,19 +865,41 @@ public void onBackPressed() { @Override public void dismiss() { + if (!isDismissed()) { + if (params != null && params.onPreFinished != null) { + params.onPreFinished.run(); + } + } super.dismiss(); actionBarLayout[0] = null; } + + @Override + public void onOpenAnimationEnd() { + if (params != null && params.onOpenAnimationFinished != null) { + params.onOpenAnimationFinished.run(); + } + } }; - fragment.setParentDialog(bottomSheet); - bottomSheet.show(); + if (params != null) { + bottomSheet[0].setAllowNestedScroll(params.allowNestedScroll); + bottomSheet[0].transitionFromRight(params.transitionFromLeft); + } + fragment.setParentDialog(bottomSheet[0]); + bottomSheet[0].show(); + return actionBarLayout; } - public int getThemedColor(String key) { + public int getThemedColor(int key) { return Theme.getColor(key, getResourceProvider()); } + public Paint getThemedPaint(String paintKey) { + Paint paint = getResourceProvider() != null ? getResourceProvider().getPaint(paintKey) : null; + return paint != null ? paint : Theme.getThemePaint(paintKey); + } + public Drawable getThemedDrawable(String key) { return Theme.getThemeDrawable(key); } @@ -811,17 +912,26 @@ public boolean hasForceLightStatusBar() { } public int getNavigationBarColor() { - return Theme.getColor(Theme.key_windowBackgroundGray); + int color = Theme.getColor(Theme.key_windowBackgroundGray, resourceProvider); + if (storyViewer != null && storyViewer.attachedToParent()) { + return storyViewer.getNavigationBarColor(color); + } + return color; } public void setNavigationBarColor(int color) { Activity activity = getParentActivity(); - if (activity != null) { - Window window = activity.getWindow(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && window != null && window.getNavigationBarColor() != color) { - window.setNavigationBarColor(color); - final float brightness = AndroidUtilities.computePerceivedBrightness(color); - AndroidUtilities.setLightNavigationBar(window, brightness >= 0.721f); + if (activity instanceof LaunchActivity) { + LaunchActivity launchActivity = (LaunchActivity) activity; + launchActivity.setNavigationBarColor(color, true); + } else { + if (activity != null) { + Window window = activity.getWindow(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && window != null && window.getNavigationBarColor() != color) { + window.setNavigationBarColor(color); + final float brightness = AndroidUtilities.computePerceivedBrightness(color); + AndroidUtilities.setLightNavigationBar(window, brightness >= 0.721f); + } } } } @@ -835,7 +945,7 @@ private void setParentDialog(Dialog dialog) { } public Theme.ResourcesProvider getResourceProvider() { - return null; + return resourceProvider; } protected boolean allowPresentFragment() { @@ -856,7 +966,7 @@ public boolean isLightStatusBar() { } Theme.ResourcesProvider resourcesProvider = getResourceProvider(); int color; - String key = Theme.key_actionBarDefault; + int key = Theme.key_actionBarDefault; if (actionBar != null && actionBar.isActionModeShowed()) { key = Theme.key_actionBarActionModeDefault; } @@ -896,9 +1006,77 @@ public void resetFragment() { } } + public void setResourceProvider(Theme.ResourcesProvider resourceProvider) { + this.resourceProvider = resourceProvider; + } + + public void onFragmentClosed() { + + } + + public void attachStoryViewer(ActionBarLayout.LayoutContainer parentLayout) { + if (storyViewer != null && storyViewer.attachedToParent()) { + AndroidUtilities.removeFromParent(storyViewer.windowView); + parentLayout.addView(storyViewer.windowView); + } + if (overlayStoryViewer != null && overlayStoryViewer.attachedToParent()) { + AndroidUtilities.removeFromParent(overlayStoryViewer.windowView); + parentLayout.addView(overlayStoryViewer.windowView); + } + } + + public void detachStoryViewer() { + if (storyViewer != null && storyViewer.attachedToParent()) { + AndroidUtilities.removeFromParent(storyViewer.windowView); + } + if (overlayStoryViewer != null && overlayStoryViewer.attachedToParent()) { + AndroidUtilities.removeFromParent(overlayStoryViewer.windowView); + } + } + + public boolean isStoryViewer(View child) { + if (storyViewer != null && child == storyViewer.windowView) { + return true; + } + if (overlayStoryViewer != null && child == overlayStoryViewer.windowView) { + return true; + } + return false; + } + + public void setKeyboardHeightFromParent(int keyboardHeight) { + if (storyViewer != null) { + storyViewer.setKeyboardHeightFromParent(keyboardHeight); + } + if (overlayStoryViewer != null) { + overlayStoryViewer.setKeyboardHeightFromParent(keyboardHeight); + } + } public interface PreviewDelegate { void finishFragment(); } + public StoryViewer getOrCreateStoryViewer() { + if (storyViewer == null) { + storyViewer = new StoryViewer(this); + } + return storyViewer; + } + + public StoryViewer getOrCreateOverlayStoryViewer() { + if (overlayStoryViewer == null) { + overlayStoryViewer = new StoryViewer(this); + } + return overlayStoryViewer; + } + + public static class BottomSheetParams { + public boolean transitionFromLeft; + public boolean allowNestedScroll; + public Runnable onDismiss; + public Runnable onOpenAnimationFinished; + public Runnable onPreFinished; + } + } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheet.java index a01f78669e..e0db9d7b81 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheet.java @@ -54,6 +54,7 @@ import androidx.core.view.ViewCompat; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.FileLog; import org.telegram.messenger.LocaleController; import org.telegram.messenger.NotificationCenter; @@ -78,10 +79,12 @@ public class BottomSheet extends Dialog { protected ViewGroup containerView; protected ContainerView container; protected boolean keyboardVisible; + protected int keyboardHeight; private WindowInsets lastInsets; public boolean drawNavigationBar; public boolean drawDoubleNavigationBar; public boolean scrollNavBar; + public boolean occupyNavigationBar; protected boolean waitingKeyboard; protected boolean useSmoothKeyboard; @@ -121,13 +124,14 @@ public void setAlpha(int alpha) { protected boolean useLightStatusBar = true; protected boolean useLightNavBar; - protected String behindKeyboardColorKey = Theme.key_dialogBackground; + protected int behindKeyboardColorKey = Theme.key_dialogBackground; protected int behindKeyboardColor; private boolean canDismissWithSwipe = false; private boolean allowCustomAnimation = true; private boolean showWithoutAnimation; + boolean showing; private int statusBarHeight = AndroidUtilities.statusBarHeight; @@ -168,13 +172,16 @@ public void setAlpha(int alpha) { private boolean disableScroll; private float currentPanTranslationY; - protected String navBarColorKey = Theme.key_windowBackgroundGray; + protected int navBarColorKey = Theme.key_windowBackgroundGray; protected int navBarColor; private OnDismissListener onHideListener; protected Theme.ResourcesProvider resourcesProvider; protected boolean isPortrait; public boolean pauseAllHeavyOperations = true; + private AnimationNotificationsLocker notificationsLocker = new AnimationNotificationsLocker(); + protected int playingImagesLayerNum; + protected int openedLayerNum; public void setDisableScroll(boolean b) { disableScroll = b; @@ -187,6 +194,15 @@ public void setDisableScroll(boolean b) { private float hideSystemVerticalInsetsProgress; public boolean useBackgroundTopPadding = true; protected int customViewGravity = Gravity.LEFT | Gravity.TOP; + private boolean transitionFromRight; + + public void transitionFromRight(boolean transitionFromRight) { + this.transitionFromRight = transitionFromRight; + } + + public void onOpenAnimationEnd() { + + } public class ContainerView extends FrameLayout implements NestedScrollingParent { @@ -199,7 +215,6 @@ public class ContainerView extends FrameLayout implements NestedScrollingParent private AnimatorSet currentAnimation = null; private NestedScrollingParentHelper nestedScrollingParentHelper; private Rect rect = new Rect(); - private int keyboardHeight; private Paint backgroundPaint = new Paint(); private boolean keyboardChanged; @@ -430,12 +445,12 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (keyboardVisible && rect.bottom != 0 && rect.top != 0) { bottomInset -= keyboardHeight; } - if (!drawNavigationBar) { + if (!drawNavigationBar && !occupyNavigationBar) { containerHeight -= getBottomInset(); } } setMeasuredDimension(width, containerHeight); - if (lastInsets != null && Build.VERSION.SDK_INT >= 21) { + if (lastInsets != null && Build.VERSION.SDK_INT >= 21 && !occupyNavigationBar) { int inset = (int) (lastInsets.getSystemWindowInsetBottom() * (1f - hideSystemVerticalInsetsProgress)); if (Build.VERSION.SDK_INT >= 29) { inset += getAdditionalMandatoryOffsets(); @@ -606,7 +621,7 @@ public boolean hasOverlappingRendering() { @Override protected void dispatchDraw(Canvas canvas) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - if (navBarColorKey != null) { + if (navBarColorKey >= 0) { backgroundPaint.setColor(getThemedColor(navBarColorKey)); } else { backgroundPaint.setColor(navBarColor); @@ -643,7 +658,7 @@ protected void dispatchDraw(Canvas canvas) { } if (containerView.getTranslationY() < 0) { - backgroundPaint.setColor(behindKeyboardColorKey != null ? getThemedColor(behindKeyboardColorKey) : behindKeyboardColor); + backgroundPaint.setColor(behindKeyboardColorKey >= 0 ? getThemedColor(behindKeyboardColorKey) : behindKeyboardColor); canvas.drawRect(containerView.getLeft() + backgroundPaddingLeft, containerView.getY() + containerView.getMeasuredHeight(), containerView.getRight() - backgroundPaddingLeft, getMeasuredHeight(), backgroundPaint); } } @@ -675,7 +690,7 @@ protected void onDraw(Canvas canvas) { } super.onDraw(canvas); if (lastInsets != null && keyboardHeight != 0) { - backgroundPaint.setColor(behindKeyboardColorKey != null ? getThemedColor(behindKeyboardColorKey) : behindKeyboardColor); + backgroundPaint.setColor(behindKeyboardColorKey >= 0 ? getThemedColor(behindKeyboardColorKey) : behindKeyboardColor); canvas.drawRect(containerView.getLeft() + backgroundPaddingLeft, getMeasuredHeight() - keyboardHeight - (drawNavigationBar ? getBottomInset() : 0), containerView.getRight() - backgroundPaddingLeft, getMeasuredHeight() - (drawNavigationBar ? getBottomInset() : 0), backgroundPaint); } onContainerDraw(canvas); @@ -686,7 +701,7 @@ protected void onDraw(Canvas canvas) { public void drawNavigationBar(Canvas canvas, float alpha) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - if (navBarColorKey != null) { + if (navBarColorKey >= 0) { backgroundPaint.setColor(getThemedColor(navBarColorKey)); } else { backgroundPaint.setColor(navBarColor); @@ -694,6 +709,9 @@ public void drawNavigationBar(Canvas canvas, float alpha) { } else { backgroundPaint.setColor(0xff000000); } + if (transitionFromRight && containerView.getVisibility() != View.VISIBLE) { + return; + } if ((drawNavigationBar && bottomInset != 0) || currentPanTranslationY != 0) { float translation = 0; int navBarHeight = drawNavigationBar ? getBottomInset() : 0; @@ -706,10 +724,14 @@ public void drawNavigationBar(Canvas canvas, float alpha) { } } int wasAlpha = backgroundPaint.getAlpha(); + if (transitionFromRight) { + alpha *= containerView.getAlpha(); + } + int left = transitionFromRight ? (int) containerView.getX() : containerView.getLeft(); if (alpha < 1f) { backgroundPaint.setAlpha((int) (wasAlpha * alpha)); } - canvas.drawRect(containerView.getLeft() + backgroundPaddingLeft, getMeasuredHeight() - navBarHeight + translation - currentPanTranslationY, containerView.getRight() - backgroundPaddingLeft, getMeasuredHeight() + translation, backgroundPaint); + canvas.drawRect(left + backgroundPaddingLeft, getMeasuredHeight() - navBarHeight + translation - currentPanTranslationY, containerView.getRight() - backgroundPaddingLeft, getMeasuredHeight() + translation, backgroundPaint); backgroundPaint.setAlpha(wasAlpha); if (overlayDrawNavBarColor != 0) { @@ -719,7 +741,7 @@ public void drawNavigationBar(Canvas canvas, float alpha) { backgroundPaint.setAlpha((int) (wasAlpha * alpha)); translation = 0; } - canvas.drawRect(containerView.getLeft() + backgroundPaddingLeft, getMeasuredHeight() - navBarHeight + translation - currentPanTranslationY, containerView.getRight() - backgroundPaddingLeft, getMeasuredHeight() + translation, backgroundPaint); + canvas.drawRect(left + backgroundPaddingLeft, getMeasuredHeight() - navBarHeight + translation - currentPanTranslationY, containerView.getRight() - backgroundPaddingLeft, getMeasuredHeight() + translation, backgroundPaint); backgroundPaint.setAlpha(wasAlpha); } } @@ -800,7 +822,7 @@ public BottomSheetCell(Context context, int type, Theme.ResourcesProvider resour this.resourcesProvider = resourcesProvider; currentType = type; - setBackgroundDrawable(Theme.getSelectorDrawable(false)); + setBackgroundDrawable(Theme.getSelectorDrawable(false, resourcesProvider)); //setPadding(AndroidUtilities.dp(16), 0, AndroidUtilities.dp(16), 0); imageView = new ImageView(context); @@ -892,9 +914,8 @@ public ImageView getImageView() { return imageView; } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + protected int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } public boolean isSelected = false; @@ -981,7 +1002,7 @@ protected void onConfigurationChanged(Configuration newConfig) { } }); if (Build.VERSION.SDK_INT >= 30) { - container.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); + container.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); } else { container.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); } @@ -995,14 +1016,14 @@ protected void mainContainerDispatchDraw(Canvas canvas) { } public void fixNavigationBar() { - fixNavigationBar(getThemedColor(Theme.key_dialogBackground)); + fixNavigationBar(getThemedColor(Theme.key_windowBackgroundGray)); } public void fixNavigationBar(int bgColor) { drawNavigationBar = true; drawDoubleNavigationBar = true; scrollNavBar = true; - navBarColorKey = null; + navBarColorKey = -1; setOverlayNavBarColor(navBarColor = bgColor); } @@ -1209,6 +1230,7 @@ public void setBackgroundColor(int color) { @Override public void show() { super.show(); + setShowing(true); if (focusable) { getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); } @@ -1221,26 +1243,22 @@ public void show() { return; } backDrawable.setAlpha(0); - if (Build.VERSION.SDK_INT >= 18) { - layoutCount = 2; - containerView.setTranslationY((Build.VERSION.SDK_INT >= 21 ? AndroidUtilities.statusBarHeight * (1f - hideSystemVerticalInsetsProgress) : 0) + containerView.getMeasuredHeight() + (scrollNavBar ? getBottomInset() : 0)); - long delay = openNoDelay ? 0 : 150; - if (waitingKeyboard) { - delay = 500; - } - AndroidUtilities.runOnUIThread(startAnimationRunnable = new Runnable() { - @Override - public void run() { - if (startAnimationRunnable != this || dismissed) { - return; - } - startAnimationRunnable = null; - startOpenAnimation(); - } - }, delay); - } else { - startOpenAnimation(); + layoutCount = 2; + containerView.setTranslationY((Build.VERSION.SDK_INT >= 21 ? AndroidUtilities.statusBarHeight * (1f - hideSystemVerticalInsetsProgress) : 0) + containerView.getMeasuredHeight() + (scrollNavBar ? getBottomInset() : 0)); + long delay = openNoDelay ? 0 : 150; + if (waitingKeyboard) { + delay = 500; } + AndroidUtilities.runOnUIThread(startAnimationRunnable = new Runnable() { + @Override + public void run() { + if (startAnimationRunnable != this || dismissed) { + return; + } + startAnimationRunnable = null; + startOpenAnimation(); + } + }, delay); } public ColorDrawable getBackDrawable() { @@ -1349,7 +1367,13 @@ private void startOpenAnimation() { if (Build.VERSION.SDK_INT >= 20 && useHardwareLayer) { container.setLayerType(View.LAYER_TYPE_HARDWARE, null); } - containerView.setTranslationY(getContainerViewHeight() + container.keyboardHeight + AndroidUtilities.dp(10) + (scrollNavBar ? getBottomInset() : 0)); + if (transitionFromRight) { + containerView.setTranslationX(AndroidUtilities.dp(48)); + containerView.setAlpha(0); + containerView.setTranslationY(0); + } else { + containerView.setTranslationY(getContainerViewHeight() + keyboardHeight + AndroidUtilities.dp(10) + (scrollNavBar ? getBottomInset() : 0)); + } currentSheetAnimationType = 1; if (navigationBarAnimation != null) { navigationBarAnimation.cancel(); @@ -1363,19 +1387,30 @@ private void startOpenAnimation() { }); currentSheetAnimation = new AnimatorSet(); currentSheetAnimation.playTogether( + ObjectAnimator.ofFloat(containerView, View.TRANSLATION_X, 0), + ObjectAnimator.ofFloat(containerView, View.ALPHA, 1f), ObjectAnimator.ofFloat(containerView, View.TRANSLATION_Y, 0), ObjectAnimator.ofInt(backDrawable, AnimationProperties.COLOR_DRAWABLE_ALPHA, dimBehind ? dimBehindAlpha : 0), navigationBarAnimation ); - currentSheetAnimation.setDuration(400); + if (transitionFromRight) { + currentSheetAnimation.setDuration(250); + currentSheetAnimation.setInterpolator(CubicBezierInterpolator.DEFAULT); + } else { + currentSheetAnimation.setDuration(400); + currentSheetAnimation.setInterpolator(openInterpolator); + } currentSheetAnimation.setStartDelay(waitingKeyboard ? 0 : 20); currentSheetAnimation.setInterpolator(openInterpolator); + int finalAccount = currentAccount; + notificationsLocker.lock(); currentSheetAnimation.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (currentSheetAnimation != null && currentSheetAnimation.equals(animation)) { currentSheetAnimation = null; currentSheetAnimationType = 0; + onOpenAnimationEnd(); if (delegate != null) { delegate.onOpenAnimationEnd(); } @@ -1392,6 +1427,7 @@ public void onAnimationEnd(Animator animation) { if (pauseAllHeavyOperations) { NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.startAllHeavyOperations, 512); } + notificationsLocker.unlock(); } @Override @@ -1480,7 +1516,7 @@ public void dismissWithButtonClick(final int item) { currentSheetAnimationType = 2; currentSheetAnimation = new AnimatorSet(); currentSheetAnimation.playTogether( - ObjectAnimator.ofFloat(containerView, View.TRANSLATION_Y, getContainerViewHeight() + container.keyboardHeight + AndroidUtilities.dp(10) + (scrollNavBar ? getBottomInset() : 0)), + ObjectAnimator.ofFloat(containerView, View.TRANSLATION_Y, getContainerViewHeight() + keyboardHeight + AndroidUtilities.dp(10) + (scrollNavBar ? getBottomInset() : 0)), ObjectAnimator.ofInt(backDrawable, AnimationProperties.COLOR_DRAWABLE_ALPHA, 0) ); currentSheetAnimation.setDuration(180); @@ -1567,20 +1603,24 @@ public void dismiss() { currentSheetAnimation = new AnimatorSet(); ArrayList animators = new ArrayList<>(); if (containerView != null) { - animators.add(ObjectAnimator.ofFloat(containerView, View.TRANSLATION_Y, getContainerViewHeight() + container.keyboardHeight + AndroidUtilities.dp(10) + (scrollNavBar ? getBottomInset() : 0))); + if (transitionFromRight) { + animators.add(ObjectAnimator.ofFloat(containerView, View.TRANSLATION_X, AndroidUtilities.dp(48))); + animators.add(ObjectAnimator.ofFloat(containerView, View.ALPHA, 0)); + } else { + animators.add(ObjectAnimator.ofFloat(containerView, View.TRANSLATION_Y, getContainerViewHeight() + keyboardHeight + AndroidUtilities.dp(10) + (scrollNavBar ? getBottomInset() : 0))); + } } animators.add(ObjectAnimator.ofInt(backDrawable, AnimationProperties.COLOR_DRAWABLE_ALPHA, 0)); animators.add(navigationBarAnimation); currentSheetAnimation.playTogether(animators); -// if (useFastDismiss) { -// int height = containerView.getMeasuredHeight(); -// duration = Math.max(60, (int) (250 * (height - containerView.getTranslationY()) / (float) height)); -// currentSheetAnimation.setDuration(duration); -// useFastDismiss = false; -// } else { + + if (transitionFromRight) { + currentSheetAnimation.setDuration(200); + currentSheetAnimation.setInterpolator(CubicBezierInterpolator.DEFAULT); + } else { currentSheetAnimation.setDuration(duration = 250); -// } - currentSheetAnimation.setInterpolator(CubicBezierInterpolator.EASE_OUT); + currentSheetAnimation.setInterpolator(CubicBezierInterpolator.EASE_OUT); + } currentSheetAnimation.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -1611,13 +1651,14 @@ public void onAnimationCancel(Animator animation) { } Bulletin bulletin = Bulletin.getVisibleBulletin(); - if (bulletin != null && bulletin.isShowing()) { + if (bulletin != null && bulletin.isShowing() && bulletin.hideAfterBottomSheet) { if (duration > 0) { bulletin.hide((long) (duration * 0.6f)); } else { bulletin.hide(); } } + setShowing(false); } public int getSheetAnimationType() { @@ -1690,6 +1731,12 @@ public Builder setCustomView(View view) { return this; } + public Builder setCustomView(View view, int gravity) { + bottomSheet.customView = view; + bottomSheet.customViewGravity = gravity; + return this; + } + public View getCustomView() { return bottomSheet.customView; } @@ -1971,9 +2018,8 @@ public int getCurrentAccount() { return currentAccount; } - protected int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + protected int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } public void setOpenNoDelay(boolean openNoDelay) { @@ -1983,4 +2029,23 @@ public void setOpenNoDelay(boolean openNoDelay) { public int getBackgroundPaddingLeft() { return this.backgroundPaddingLeft; } + + private void setShowing(boolean showing) { + if (this.showing == showing) { + return; + } + this.showing = showing; + if (openedLayerNum > 0) { + if (showing) { + NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.stopAllHeavyOperations, openedLayerNum); + } else { + NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.startAllHeavyOperations, openedLayerNum); + } + } + } + + public void setImageReceiverNumLevel(int playingImages, int onShowing) { + this.playingImagesLayerNum = playingImages; + this.openedLayerNum = onShowing; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/DarkAlertDialog.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/DarkAlertDialog.java index f58ccb8506..d90759c400 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/DarkAlertDialog.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/DarkAlertDialog.java @@ -9,14 +9,11 @@ public DarkAlertDialog(Context context, int progressStyle) { } @Override - protected int getThemedColor(String key) { - switch (key) { - case Theme.key_dialogBackground: - return 0xFF262626; - case Theme.key_dialogTextBlack: - case Theme.key_dialogButton: - case Theme.key_dialogScrollGlow: - return 0xFFFFFFFF; + protected int getThemedColor(int key) { + if (key == Theme.key_dialogBackground) { + return 0xFF262626; + } else if (key == Theme.key_dialogTextBlack || key == Theme.key_dialogButton || key == Theme.key_dialogScrollGlow) { + return 0xFFFFFFFF; } return super.getThemedColor(key); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/DrawerLayoutContainer.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/DrawerLayoutContainer.java index 4657eacad2..16d898761f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/DrawerLayoutContainer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/DrawerLayoutContainer.java @@ -40,8 +40,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -//import com.google.android.gms.vision.Frame; - import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.BuildVars; import org.telegram.messenger.FileLog; @@ -53,6 +51,9 @@ public class DrawerLayoutContainer extends FrameLayout { private static final int MIN_DRAWER_MARGIN = 64; private FrameLayout drawerLayout; + private View navigationBar; + private Paint navigationBarPaint = new Paint(); + private View drawerListView; private INavigationLayout parentActionBarLayout; private boolean maybeStartTracking; @@ -176,13 +177,18 @@ private int getTopInset(Object insets) { return 0; } - public void setDrawerLayout(FrameLayout layout) { + public void setDrawerLayout(FrameLayout layout, View drawerListView) { drawerLayout = layout; + this.drawerListView = drawerListView; addView(drawerLayout); drawerLayout.setVisibility(INVISIBLE); + drawerListView.setVisibility(GONE); if (Build.VERSION.SDK_INT >= 21) { drawerLayout.setFitsSystemWindows(true); } + AndroidUtilities.runOnUIThread(() -> { + drawerListView.setVisibility(View.VISIBLE); + }, 2500); } public void moveDrawerByX(float dx) { @@ -201,6 +207,9 @@ public void setDrawerPosition(float value) { drawerPosition = 0; } drawerLayout.setTranslationX(drawerPosition); + if (drawerPosition > 0 && drawerListView != null && drawerListView.getVisibility() != View.VISIBLE) { + drawerListView.setVisibility(View.VISIBLE); + } final int newVisibility = drawerPosition > 0 ? VISIBLE : INVISIBLE; if (drawerLayout.getVisibility() != newVisibility) { @@ -438,7 +447,7 @@ public boolean onTouchEvent(MotionEvent ev) { return true; } - if ((allowOpenDrawerBySwipe || drawerOpened) && allowOpenDrawer && parentActionBarLayout.getFragmentStack().size() == 1) { + if ((allowOpenDrawerBySwipe || drawerOpened) && allowOpenDrawer && parentActionBarLayout.getFragmentStack().size() == 1 && (parentActionBarLayout.getLastFragment().storyViewer == null || !parentActionBarLayout.getLastFragment().storyViewer.attachedToParent())) { if (ev != null && (ev.getAction() == MotionEvent.ACTION_DOWN || ev.getAction() == MotionEvent.ACTION_MOVE) && !startedTracking && !maybeStartTracking) { View scrollingChild = findScrollingChild(this, ev.getX(),ev.getY()); if (scrollingChild != null) { @@ -606,12 +615,12 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (Build.VERSION.SDK_INT < 21) { inLayout = true; if (heightSize == AndroidUtilities.displaySize.y + AndroidUtilities.statusBarHeight) { - if (getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { + if (getLayoutParams() instanceof MarginLayoutParams) { setPadding(0, AndroidUtilities.statusBarHeight, 0, 0); } heightSize = AndroidUtilities.displaySize.y; } else { - if (getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { + if (getLayoutParams() instanceof MarginLayoutParams) { setPadding(0, 0, 0, 0); } } @@ -651,6 +660,13 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } else { contentHeightSpec = MeasureSpec.makeMeasureSpec(heightSize - lp.topMargin - lp.bottomMargin, MeasureSpec.EXACTLY); } + if (child instanceof ActionBarLayout) { + ActionBarLayout actionBarLayout = (ActionBarLayout) child; + //fix keyboard measuring + if (actionBarLayout.storyViewerAttached()) { + child.forceLayout(); + } + } child.measure(contentWidthSpec, contentHeightSpec); } else { child.setPadding(0, 0, 0, 0); @@ -659,6 +675,17 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { child.measure(drawerWidthSpec, drawerHeightSpec); } } + + if (navigationBar != null) { + if (navigationBar.getParent() == null) { + ((FrameLayout) AndroidUtilities.findActivity(getContext()).getWindow().getDecorView()).addView(navigationBar); + } + if (navigationBar.getLayoutParams().height != AndroidUtilities.navigationBarHeight || ((LayoutParams)navigationBar.getLayoutParams()).topMargin != MeasureSpec.getSize(heightMeasureSpec)) { + navigationBar.getLayoutParams().height = AndroidUtilities.navigationBarHeight; + ((LayoutParams)navigationBar.getLayoutParams()).topMargin = MeasureSpec.getSize(heightMeasureSpec); + navigationBar.requestLayout(); + } + } } public void setBehindKeyboardColor(int color) { @@ -766,6 +793,28 @@ public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent ev return super.onRequestSendAccessibilityEvent(child, event); } + public void setNavigationBarColor(int color) { + navigationBarPaint.setColor(color); + if (navigationBar != null) { + navigationBar.invalidate(); + } + } + + public int getNavigationBarColor() { + return navigationBarPaint.getColor(); + } + + public View createNavigationBar() { + navigationBar = new View(getContext()) { + @Override + protected void onDraw(Canvas canvas) { + canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), navigationBarPaint); + } + }; + navigationBarPaint.setColor(0xff000000); + return navigationBar; + } + private static class PreviewForegroundDrawable extends Drawable { private final GradientDrawable topDrawable; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/EmojiThemes.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/EmojiThemes.java index fe692f2566..d3bd4acc34 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/EmojiThemes.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/EmojiThemes.java @@ -6,6 +6,7 @@ import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; import android.util.Pair; +import android.util.SparseIntArray; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ApplicationLoader; @@ -22,17 +23,16 @@ import java.io.File; import java.io.FileOutputStream; import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; public class EmojiThemes { public boolean showAsDefaultStub; public String emoji; + public TLRPC.WallPaper wallpaper; int currentIndex = 0; public ArrayList items = new ArrayList<>(); - private static final String[] previewColorKeys = new String[]{ + private static final int[] previewColorKeys = new int[]{ Theme.key_chat_inBubble, Theme.key_chat_outBubble, Theme.key_featuredStickers_addButton, @@ -43,7 +43,7 @@ public class EmojiThemes { Theme.key_chat_wallpaper_gradient_rotation }; - private EmojiThemes() { + public EmojiThemes() { } public EmojiThemes(TLRPC.TL_theme chatThemeObject, boolean isDefault) { @@ -234,8 +234,8 @@ public int getSettingsIndex(int index) { return items.get(index).settingsIndex; } - public HashMap getPreviewColors(int currentAccount, int index) { - HashMap currentColors = items.get(index).currentPreviewColors; + public SparseIntArray getPreviewColors(int currentAccount, int index) { + SparseIntArray currentColors = items.get(index).currentPreviewColors; if (currentColors != null) { return currentColors; } @@ -260,42 +260,47 @@ public HashMap getPreviewColors(int currentAccount, int index) } } - HashMap currentColorsNoAccent = new HashMap<>(); + SparseIntArray currentColorsNoAccent; String[] wallpaperLink = new String[1]; if (themeInfo.pathToFile != null) { - currentColorsNoAccent.putAll(Theme.getThemeFileValues(new File(themeInfo.pathToFile), null, wallpaperLink)); + currentColorsNoAccent = Theme.getThemeFileValues(new File(themeInfo.pathToFile), null, wallpaperLink); } else if (themeInfo.assetName != null) { - currentColorsNoAccent.putAll(Theme.getThemeFileValues(null, themeInfo.assetName, wallpaperLink)); + currentColorsNoAccent = Theme.getThemeFileValues(null, themeInfo.assetName, wallpaperLink); + } else { + currentColorsNoAccent = new SparseIntArray(); } items.get(index).wallpaperLink = wallpaperLink[0]; if (accent != null) { - currentColors = new HashMap<>(currentColorsNoAccent); + currentColors = currentColorsNoAccent.clone(); accent.fillAccentColors(currentColorsNoAccent, currentColors); - currentColorsNoAccent.clear(); } else { currentColors = currentColorsNoAccent; } - HashMap fallbackKeys = Theme.getFallbackKeys(); - items.get(index).currentPreviewColors = new HashMap<>(); + SparseIntArray fallbackKeys = Theme.getFallbackKeys(); + items.get(index).currentPreviewColors = new SparseIntArray(); for (int i = 0; i < previewColorKeys.length; i++) { - String key = previewColorKeys[i]; - items.get(index).currentPreviewColors.put(key, currentColors.get(key)); - - if (!items.get(index).currentPreviewColors.containsKey(key)) { - Integer color = currentColors.get(fallbackKeys.get(key)); - currentColors.put(key, color); + int key = previewColorKeys[i]; + int colorIndex = currentColors.indexOfKey(key); + if (colorIndex >= 0) { + items.get(index).currentPreviewColors.put(key, currentColors.valueAt(colorIndex)); + } else { + int fallbackKey = fallbackKeys.get(key, -1); + if (fallbackKey >= 0) { + int fallbackIndex = currentColors.indexOfKey(fallbackKey); + if (fallbackIndex >= 0) { + items.get(index).currentPreviewColors.put(key, currentColors.valueAt(fallbackIndex)); + } + } } } - currentColors.clear(); - return items.get(index).currentPreviewColors; } - public HashMap createColors(int currentAccount, int index) { - HashMap currentColors; + public SparseIntArray createColors(int currentAccount, int index) { + SparseIntArray currentColors; Theme.ThemeInfo themeInfo = getThemeInfo(index); Theme.ThemeAccent accent = null; @@ -312,36 +317,40 @@ public HashMap createColors(int currentAccount, int index) { } } - HashMap currentColorsNoAccent = new HashMap<>(); + SparseIntArray currentColorsNoAccent; String[] wallpaperLink = new String[1]; if (themeInfo.pathToFile != null) { - currentColorsNoAccent.putAll(Theme.getThemeFileValues(new File(themeInfo.pathToFile), null, wallpaperLink)); + currentColorsNoAccent = Theme.getThemeFileValues(new File(themeInfo.pathToFile), null, wallpaperLink); } else if (themeInfo.assetName != null) { - currentColorsNoAccent.putAll(Theme.getThemeFileValues(null, themeInfo.assetName, wallpaperLink)); + currentColorsNoAccent = Theme.getThemeFileValues(null, themeInfo.assetName, wallpaperLink); + } else { + currentColorsNoAccent = new SparseIntArray(); } items.get(index).wallpaperLink = wallpaperLink[0]; if (accent != null) { - currentColors = new HashMap<>(currentColorsNoAccent); + currentColors = currentColorsNoAccent.clone(); accent.fillAccentColors(currentColorsNoAccent, currentColors); - currentColorsNoAccent.clear(); } else { currentColors = currentColorsNoAccent; } - HashMap fallbackKeys = Theme.getFallbackKeys(); - for (Map.Entry fallbackEntry : fallbackKeys.entrySet()) { - String colorKey = fallbackEntry.getKey(); - if (!currentColors.containsKey(colorKey)) { - Integer color = currentColors.get(fallbackEntry.getValue()); - currentColors.put(colorKey, color); + SparseIntArray fallbackKeys = Theme.getFallbackKeys(); + for (int i = 0; i < fallbackKeys.size(); i++) { + int colorKey = fallbackKeys.keyAt(i); + int fallbackKey = fallbackKeys.valueAt(i); + if (currentColors.indexOfKey(colorKey) < 0) { + int fallbackIndex = currentColors.indexOfKey(fallbackKey); + if (fallbackIndex >= 0) { + currentColors.put(colorKey, currentColors.valueAt(fallbackIndex)); + } } } - HashMap defaultColors = Theme.getDefaultColors(); - for (Map.Entry entry : defaultColors.entrySet()) { - if (!currentColors.containsKey(entry.getKey())) { - currentColors.put(entry.getKey(), entry.getValue()); + int[] defaultColors = Theme.getDefaultColors(); + for (int i = 0; i < defaultColors.length; i++) { + if (currentColors.indexOfKey(i) < 0) { + currentColors.put(i, defaultColors[i]); } } return currentColors; @@ -361,9 +370,13 @@ public void loadWallpaper(int index, ResultCallback> callback } long themeId = getTlTheme(index).id; - ChatThemeController.getWallpaperBitmap(themeId, cachedBitmap -> { + loadWallpaperImage(themeId, wallPaper, callback); + } + + public static void loadWallpaperImage(long hash, TLRPC.WallPaper wallPaper, ResultCallback> callback) { + ChatThemeController.getWallpaperBitmap(hash, cachedBitmap -> { if (cachedBitmap != null && callback != null) { - callback.onComplete(new Pair<>(themeId, cachedBitmap)); + callback.onComplete(new Pair<>(hash, cachedBitmap)); return; } ImageLocation imageLocation = ImageLocation.getForDocument(wallPaper.document); @@ -386,9 +399,9 @@ public void loadWallpaper(int index, ResultCallback> callback bitmap = ((BitmapDrawable) holder.drawable).getBitmap(); } if (callback != null) { - callback.onComplete(new Pair<>(themeId, bitmap)); + callback.onComplete(new Pair<>(hash, bitmap)); } - ChatThemeController.saveWallpaperBitmap(bitmap, themeId); + ChatThemeController.saveWallpaperBitmap(bitmap, hash); }); ImageLoader.getInstance().loadImageForImageReceiver(imageReceiver); }); @@ -493,14 +506,16 @@ public static void fillTlTheme(Theme.ThemeInfo themeInfo) { } } - public static HashMap getPreviewColors(Theme.ThemeInfo themeInfo) { - HashMap currentColorsNoAccent = new HashMap<>(); + public static SparseIntArray getPreviewColors(Theme.ThemeInfo themeInfo) { + SparseIntArray currentColorsNoAccent; if (themeInfo.pathToFile != null) { - currentColorsNoAccent.putAll(Theme.getThemeFileValues(new File(themeInfo.pathToFile), null, null)); + currentColorsNoAccent = Theme.getThemeFileValues(new File(themeInfo.pathToFile), null, null); } else if (themeInfo.assetName != null) { - currentColorsNoAccent.putAll(Theme.getThemeFileValues(null, themeInfo.assetName, null)); + currentColorsNoAccent = Theme.getThemeFileValues(null, themeInfo.assetName, null); + } else { + currentColorsNoAccent = new SparseIntArray(); } - HashMap currentColors = new HashMap<>(currentColorsNoAccent); + SparseIntArray currentColors = currentColorsNoAccent.clone(); Theme.ThemeAccent themeAccent = themeInfo.getAccent(false); if (themeAccent != null) { themeAccent.fillAccentColors(currentColorsNoAccent, currentColors); @@ -517,52 +532,16 @@ public void loadPreviewColors(int currentAccount) { if (items.get(i) == null) { continue; } - HashMap colorsMap = getPreviewColors(currentAccount, i); - Integer color = colorsMap.get(Theme.key_chat_inBubble); - if (color == null) { - color = Theme.getDefaultColor(Theme.key_chat_inBubble); - } - items.get(i).inBubbleColor = color; - color = colorsMap.get(Theme.key_chat_outBubble); - if (color == null) { - color = Theme.getDefaultColor(Theme.key_chat_outBubble); - } - items.get(i).outBubbleColor = color; - color = colorsMap.get(Theme.key_featuredStickers_addButton); - if (color == null) { - color = Theme.getDefaultColor(Theme.key_featuredStickers_addButton); - } - items.get(i).outLineColor = color; - color = colorsMap.get(Theme.key_chat_wallpaper); - if (color == null) { - items.get(i).patternBgColor = 0; - } else { - items.get(i).patternBgColor = color; - } - color = colorsMap.get(Theme.key_chat_wallpaper_gradient_to1); - if (color == null) { - items.get(i).patternBgGradientColor1 = 0; - } else { - items.get(i).patternBgGradientColor1 = color; - } - color = colorsMap.get(Theme.key_chat_wallpaper_gradient_to2); - if (color == null) { - items.get(i).patternBgGradientColor2 = 0; - } else { - items.get(i).patternBgGradientColor2 = color; - } - color = colorsMap.get(Theme.key_chat_wallpaper_gradient_to3); - if (color == null) { - items.get(i).patternBgGradientColor3 = 0; - } else { - items.get(i).patternBgGradientColor3 = color; - } - color = colorsMap.get(Theme.key_chat_wallpaper_gradient_rotation); - if (color == null) { - items.get(i).patternBgRotation = 0; - } else { - items.get(i).patternBgRotation = color; - } + SparseIntArray colorsMap = getPreviewColors(currentAccount, i); + items.get(i).inBubbleColor = getOrDefault(colorsMap, Theme.key_chat_inBubble); + items.get(i).outBubbleColor = getOrDefault(colorsMap, Theme.key_chat_outBubble); + items.get(i).outLineColor = getOrDefault(colorsMap, Theme.key_featuredStickers_addButton); + items.get(i).patternBgColor = colorsMap.get(Theme.key_chat_wallpaper, 0); + items.get(i).patternBgGradientColor1 = colorsMap.get(Theme.key_chat_wallpaper_gradient_to1, 0); + items.get(i).patternBgGradientColor2 = colorsMap.get(Theme.key_chat_wallpaper_gradient_to2, 0); + items.get(i).patternBgGradientColor3 = colorsMap.get(Theme.key_chat_wallpaper_gradient_to3, 0); + items.get(i).patternBgRotation = colorsMap.get(Theme.key_chat_wallpaper_gradient_rotation, 0); + if (items.get(i).themeInfo != null && items.get(i).themeInfo.getKey().equals("Blue")) { int accentId = items.get(i).accentId >= 0 ? items.get(i).accentId : items.get(i).themeInfo.currentAccentId; if (accentId == 99) { @@ -575,6 +554,14 @@ public void loadPreviewColors(int currentAccount) { } } + private int getOrDefault(SparseIntArray colorsMap, int key) { + int index = colorsMap.indexOfKey(key); + if (index >= 0) { + return colorsMap.valueAt(index); + } + return Theme.getDefaultColor(key); + } + public ThemeItem getThemeItem(int index) { return items.get(index); } @@ -617,7 +604,7 @@ public static class ThemeItem { TLRPC.TL_theme tlTheme; int settingsIndex; public int accentId = -1; - public HashMap currentPreviewColors; + public SparseIntArray currentPreviewColors; private String wallpaperLink; public int inBubbleColor; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/FloatingToolbar.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/FloatingToolbar.java index c04edc056a..1d531c5e15 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/FloatingToolbar.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/FloatingToolbar.java @@ -1206,7 +1206,7 @@ private View createMenuItemButton(Context context, MenuItem menuItem, int iconTe textView.setFocusable(false); textView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); textView.setFocusableInTouchMode(false); - int selectorColor = Theme.getColor(Theme.key_listSelector); + int selectorColor = getThemedColor(Theme.key_listSelector); if (currentStyle == STYLE_DIALOG) { textView.setTextColor(getThemedColor(Theme.key_dialogTextBlack)); } else if (currentStyle == STYLE_BLACK) { @@ -1273,9 +1273,8 @@ private ViewGroup createContentContainer(Context context) { return contentContainer; } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } private static PopupWindow createPopupWindow(ViewGroup content) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/INavigationLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/INavigationLayout.java index 797f227df6..24e979f6cf 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/INavigationLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/INavigationLayout.java @@ -5,15 +5,18 @@ import android.content.Intent; import android.graphics.Canvas; import android.graphics.drawable.Drawable; +import android.util.SparseIntArray; import android.view.Menu; import android.view.View; import android.view.ViewGroup; +import android.view.Window; import android.widget.FrameLayout; +import androidx.core.util.Supplier; + import org.telegram.ui.Components.BackButtonMenu; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; public interface INavigationLayout { @@ -79,6 +82,15 @@ static INavigationLayout newLayout(Context context) { return new ActionBarLayout(context); } + static INavigationLayout newLayout(Context context, Supplier supplier) { + return new ActionBarLayout(context) { + @Override + public BottomSheet getBottomSheet() { + return supplier.get(); + } + }; + } + default void removeFragmentFromStack(BaseFragment fragment) { removeFragmentFromStack(fragment, false); } @@ -254,6 +266,17 @@ default void dismissDialogs() { } } + default Window getWindow() { + if (getParentActivity() != null) { + return getParentActivity().getWindow(); + } + return null; + } + + default BottomSheet getBottomSheet() { + return null; + } + interface INavigationLayoutDelegate { default boolean needPresentFragment(INavigationLayout layout, NavigationParams params) { return needPresentFragment(params.fragment, params.removeLast, params.noAnimation, layout); @@ -358,8 +381,8 @@ public interface onAnimationProgress { } class StartColorsProvider implements Theme.ResourcesProvider { - HashMap colors = new HashMap<>(); - String[] keysToSave = new String[] { + SparseIntArray colors = new SparseIntArray(); + int[] keysToSave = new int[] { Theme.key_chat_outBubble, Theme.key_chat_outBubbleGradient1, Theme.key_chat_outBubbleGradient2, @@ -369,18 +392,22 @@ class StartColorsProvider implements Theme.ResourcesProvider { }; @Override - public Integer getColor(String key) { - return colors.get(key); + public int getColor(int key) { + int index = colors.indexOfKey(key); + if (index >= 0) { + return colors.valueAt(index); + } + return Theme.getColor(key); } @Override - public Integer getCurrentColor(String key) { + public int getCurrentColor(int key) { return colors.get(key); } public void saveColors(Theme.ResourcesProvider fragmentResourceProvider) { colors.clear(); - for (String key : keysToSave) { + for (int key : keysToSave) { colors.put(key, fragmentResourceProvider.getCurrentColor(key)); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/RoundVideoShadow.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/RoundVideoShadow.java new file mode 100644 index 0000000000..f4eb6887be --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/RoundVideoShadow.java @@ -0,0 +1,57 @@ +package org.telegram.ui.ActionBar; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.drawable.Drawable; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.telegram.messenger.AndroidUtilities; + +public class RoundVideoShadow extends Drawable { + + Paint eraserPaint; + Paint paint; + + public RoundVideoShadow() { +// eraserPaint = new Paint(Paint.ANTI_ALIAS_FLAG); +// eraserPaint.setColor(0); +// eraserPaint.setStyle(Paint.Style.FILL); +// eraserPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + + paint = new Paint(Paint.ANTI_ALIAS_FLAG); + paint.setShadowLayer(AndroidUtilities.dp(4), 0, 0, 0x5f000000); + } + + @Override + public void draw(@NonNull Canvas canvas) { + float cx = getBounds().centerX(); + float cy = getBounds().centerY(); +// for (int a = 0; a < 2; a++) { +// canvas.drawCircle(cx, cy, AndroidUtilities.roundMessageSize / 2f - AndroidUtilities.dp(1), a == 0 ? paint : eraserPaint); +// } + float r = (getBounds().width() - AndroidUtilities.dp(8)) / 2f; + canvas.drawCircle(cx, cy - AndroidUtilities.dp(1), r, paint); + } + + @Override + public void setAlpha(int alpha) { + paint.setAlpha(alpha); + // eraserPaint.setAlpha(alpha); + } + + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) { + + } + + @Override + public int getOpacity() { + return 0; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/SimpleTextView.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/SimpleTextView.java index 5e4ac1cc34..b2cd36b35c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/SimpleTextView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/SimpleTextView.java @@ -240,7 +240,8 @@ private void updateFadePaints() { if (forceEllipsizeByGradientLeft != null) { ellipsizeLeft = forceEllipsizeByGradientLeft; } else { - ellipsizeLeft = getAlignment() == Layout.Alignment.ALIGN_NORMAL && LocaleController.isRTL || getAlignment() == Layout.Alignment.ALIGN_OPPOSITE && !LocaleController.isRTL; + ellipsizeLeft = false; +// ellipsizeLeft = getAlignment() == Layout.Alignment.ALIGN_NORMAL && LocaleController.isRTL || getAlignment() == Layout.Alignment.ALIGN_OPPOSITE && !LocaleController.isRTL; } if ((fadeEllpsizePaint == null || fadeEllpsizePaintWidth != AndroidUtilities.dp(ellipsizeByGradientWidthDp) || ellipsizeByGradientLeft != ellipsizeLeft) && ellipsizeByGradient) { if (fadeEllpsizePaint == null) { @@ -418,7 +419,7 @@ protected boolean createLayout(int width) { spoilersPool.addAll(spoilers); spoilers.clear(); if (layout != null && layout.getText() instanceof Spannable) { - SpoilerEffect.addSpoilers(this, layout, spoilersPool, spoilers); + SpoilerEffect.addSpoilers(this, layout, -2, -2, spoilersPool, spoilers); } calcOffset(width); } catch (Exception ignore) { @@ -616,14 +617,19 @@ public boolean setText(CharSequence value, boolean force) { return false; } text = value; - if (!(text == null && value == null || text != null && text.equals(value))) { - scrollingOffset = 0; - } currentScrollDelay = SCROLL_DELAY_MS; recreateLayoutMaybe(); return true; } + public void resetScrolling() { + scrollingOffset = 0; + } + + public void copyScrolling(SimpleTextView textView) { + scrollingOffset = textView.scrollingOffset; + } + public void setDrawablePadding(int value) { if (drawablePadding == value) { return; @@ -832,7 +838,7 @@ protected void onDraw(Canvas canvas) { } if (layout != null) { - if (rightDrawableOutside || ellipsizeByGradient) { + if (rightDrawableOutside || ellipsizeByGradient || paddingRight > 0) { canvas.save(); canvas.clipRect(0, 0, getMaxTextWidth() - paddingRight - AndroidUtilities.dp(rightDrawable != null && !(rightDrawable instanceof AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable) && rightDrawableOutside ? 2 : 0), getMeasuredHeight()); } @@ -910,7 +916,7 @@ protected void onDraw(Canvas canvas) { } updateScrollAnimation(); Emoji.emojiDrawingUseAlpha = true; - if (rightDrawableOutside) { + if (rightDrawableOutside || ellipsizeByGradient || paddingRight > 0) { canvas.restore(); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/Theme.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/Theme.java index 51e521a153..4579d4ecf7 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/Theme.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/Theme.java @@ -68,7 +68,6 @@ import android.util.StateSet; import android.view.View; import android.widget.FrameLayout; -import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -102,6 +101,7 @@ import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.SerializedData; import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ChatActivity; import org.telegram.ui.Components.AudioVisualizerDrawable; import org.telegram.ui.Components.BackgroundGradientDrawable; import org.telegram.ui.Components.Bulletin; @@ -124,6 +124,7 @@ import org.telegram.ui.Components.StatusDrawable; import org.telegram.ui.Components.ThemeEditorView; import org.telegram.ui.Components.TypingDotsDrawable; +import org.telegram.ui.LaunchActivity; import org.telegram.ui.RoundVideoProgressShadow; import org.telegram.ui.ThemeActivity; import org.telegram.messenger.support.SparseLongArray; @@ -144,7 +145,7 @@ import java.util.HashSet; import java.util.Locale; import java.util.Map; -import java.util.Set; +import java.util.Objects; import java.util.concurrent.CountDownLatch; import tw.nekomimi.nekogram.NekoConfig; @@ -159,11 +160,29 @@ public class Theme { public static final int MSG_OUT_COLOR_BLACK = 0xff212121; public static final int MSG_OUT_COLOR_WHITE = 0xffffffff; public static final int default_shadow_color = ColorUtils.setAlphaComponent(Color.BLACK, 27); + public static boolean disallowChangeServiceMessageColor; public static void applyDefaultShadow(Paint paint) { paint.setShadowLayer(dpf2(1), 0, dpf2(0.33f), default_shadow_color); } + public static Paint getThemePaint(String key, ResourcesProvider resourcesProvider) { + if (resourcesProvider != null) { + final Paint paint = resourcesProvider.getPaint(key); + if (paint != null) { + return paint; + } + } + return getThemePaint(key); + } + + public static ColorFilter getAnimatedEmojiColorFilter(ResourcesProvider resourcesProvider) { + if (resourcesProvider != null) { + return resourcesProvider.getAnimatedEmojiColorFilter(); + } + return Theme.chat_animatedEmojiTextColorFilter; + } + public static class BackgroundDrawableSettings { public Drawable wallpaper; @@ -197,12 +216,13 @@ public Path getPath() { private Rect backupRect = new Rect(); - private final ResourcesProvider resourcesProvider; + private ResourcesProvider resourcesProvider; private final boolean isOut; private int topY; private boolean isTopNear; private boolean isBottomNear; + private boolean botButtonsBottom; public boolean themePreview; public static MotionBackgroundDrawable[] motionBackground = new MotionBackgroundDrawable[3]; @@ -214,11 +234,17 @@ public Path getPath() { private int[][] currentBackgroundDrawableRadius = new int[][]{ {-1, -1, -1, -1}, - {-1, -1, -1, -1}}; - private Drawable[][] backgroundDrawable = new Drawable[2][4]; + {-1, -1, -1, -1}, + {-1, -1, -1, -1}, + {-1, -1, -1, -1} + }; + private Drawable[][] backgroundDrawable = new Drawable[4][4]; private int[][] backgroundDrawableColor = new int[][]{ {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, - {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}}; + {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, + {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, + {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff} + }; public static final int TYPE_TEXT = 0; public static final int TYPE_MEDIA = 1; @@ -292,21 +318,27 @@ public Matrix getMatrix() { return matrix; } - protected int getColor(String key) { + protected int getColor(int key) { if (currentType == TYPE_PREVIEW) { return Theme.getColor(key); } - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + if (resourcesProvider != null) { + return resourcesProvider.getColor(key); + } + return Theme.getColor(key); } - protected Integer getCurrentColor(String key) { + protected int getCurrentColor(int key) { if (currentType == TYPE_PREVIEW) { return Theme.getColor(key); } return resourcesProvider != null ? resourcesProvider.getCurrentColor(key) : Theme.currentColors.get(key); } + public void setBotButtonsBottom(boolean botButtonsBottom) { + this.botButtonsBottom = botButtonsBottom; + } + public void setTop(int top, int backgroundWidth, int backgroundHeight, boolean topNear, boolean bottomNear) { setTop(top, backgroundWidth, backgroundHeight, backgroundHeight, 0, 0, topNear, bottomNear); } @@ -316,35 +348,25 @@ public void setTop(int top, int backgroundWidth, int backgroundHeight, int heigh crossfadeFromDrawable.setTop(top, backgroundWidth, backgroundHeight, heightOffset, blurredViewTopOffset, blurredViewBottomOffset, topNear, bottomNear); } int color; - Integer gradientColor1; - Integer gradientColor2; - Integer gradientColor3; + int gradientColor1; + int gradientColor2; + int gradientColor3; boolean animatedGradient; if (isOut) { color = getColor(isSelected ? key_chat_outBubbleSelected : key_chat_outBubble); gradientColor1 = getCurrentColor(key_chat_outBubbleGradient1); gradientColor2 = getCurrentColor(key_chat_outBubbleGradient2); gradientColor3 = getCurrentColor(key_chat_outBubbleGradient3); - Integer val = getCurrentColor(key_chat_outBubbleGradientAnimated); - animatedGradient = val != null && val != 0; + animatedGradient = getCurrentColor(key_chat_outBubbleGradientAnimated) != 0; } else { color = getColor(isSelected ? key_chat_inBubbleSelected : key_chat_inBubble); - gradientColor1 = null; - gradientColor2 = null; - gradientColor3 = null; - animatedGradient = false; - } - if (gradientColor1 != null) { - color = getColor(key_chat_outBubble); - } - if (gradientColor1 == null) { gradientColor1 = 0; - } - if (gradientColor2 == null) { gradientColor2 = 0; - } - if (gradientColor3 == null) { gradientColor3 = 0; + animatedGradient = false; + } + if (gradientColor1 != 0) { + color = getColor(key_chat_outBubble); } int num = 0; if (themePreview) { @@ -476,7 +498,16 @@ public Drawable getBackgroundDrawable() { } else { idx = 0; } - int idx2 = isSelected ? 1 : 0; + int idx2; + if (isSelected && botButtonsBottom) { + idx2 = 3; + } else if (isSelected) { + idx2 = 1; + } else if (botButtonsBottom) { + idx2 = 2; + } else { + idx2 = 0; + } boolean forceSetColor = false; boolean drawWithShadow = gradientShader == null && !isSelected && !isCrossfadeBackground; @@ -723,6 +754,7 @@ public void draw(Canvas canvas, Paint paintToUse) { return; } } + int padding = dp(2); int rad; int nearRad; @@ -776,13 +808,14 @@ public void draw(Canvas canvas, Paint paintToUse) { } if (isOut) { if (drawFullBubble || currentType == TYPE_PREVIEW || paintToUse != null || drawFullBottom) { + int radToUse = botButtonsBottom ? nearRad : rad; if (currentType == TYPE_MEDIA) { - path.moveTo(bounds.right - dp(8) - rad, bounds.bottom - padding); + path.moveTo(bounds.right - dp(8) - radToUse, bounds.bottom - padding); } else { path.moveTo(bounds.right - dp(2.6f), bounds.bottom - padding); } - path.lineTo(bounds.left + padding + rad, bounds.bottom - padding); - rect.set(bounds.left + padding, bounds.bottom - padding - rad * 2, bounds.left + padding + rad * 2, bounds.bottom - padding); + path.lineTo(bounds.left + padding + radToUse, bounds.bottom - padding); + rect.set(bounds.left + padding, bounds.bottom - padding - radToUse * 2, bounds.left + padding + radToUse * 2, bounds.bottom - padding); path.arcTo(rect, 90, 90, false); } else { path.moveTo(bounds.right - dp(8), top - topY + currentBackgroundHeight); @@ -831,13 +864,15 @@ public void draw(Canvas canvas, Paint paintToUse) { } } else { if (drawFullBubble || currentType == TYPE_PREVIEW || paintToUse != null || drawFullBottom) { + int radToUse = botButtonsBottom ? nearRad : rad; + if (currentType == TYPE_MEDIA) { - path.moveTo(bounds.left + dp(8) + rad, bounds.bottom - padding); + path.moveTo(bounds.left + dp(8) + radToUse, bounds.bottom - padding); } else { path.moveTo(bounds.left + dp(2.6f), bounds.bottom - padding); } - path.lineTo(bounds.right - padding - rad, bounds.bottom - padding); - rect.set(bounds.right - padding - rad * 2, bounds.bottom - padding - rad * 2, bounds.right - padding, bounds.bottom - padding); + path.lineTo(bounds.right - padding - radToUse, bounds.bottom - padding); + rect.set(bounds.right - padding - radToUse * 2, bounds.bottom - padding - radToUse * 2, bounds.right - padding, bounds.bottom - padding); path.arcTo(rect, 90, -90, false); } else { path.moveTo(bounds.left + dp(8), top - topY + currentBackgroundHeight); @@ -902,7 +937,7 @@ public void setDrawFullBubble(boolean drawFullBuble) { @Override public void setAlpha(int alpha) { - if (this.alpha != alpha) { + if (this.alpha != alpha || this.paint.getAlpha() != alpha) { this.alpha = alpha; paint.setAlpha(alpha); if (isOut) { @@ -952,6 +987,10 @@ public void setRoundingRadius(float rounding) { this.overrideRounding = rounding; } + public void setResourceProvider(ResourcesProvider resourcesProvider) { + this.resourcesProvider = resourcesProvider; + } + public static class PathDrawParams { Path path = new Path(); Rect lastRect = new Rect(); @@ -1117,7 +1156,7 @@ private void checkCurrentWallpaper(ArrayList accents, boolean load) private void checkCurrentWallpaperInternal(ArrayList accents, boolean load) { if (accents != null && currentTheme.themeAccents != null && !currentTheme.themeAccents.isEmpty()) { if (accents.contains(currentTheme.getAccent(false))) { - reloadWallpaper(); + reloadWallpaper(true); } } if (load) { @@ -1144,7 +1183,7 @@ private Bitmap createWallpaperForAccent(Bitmap patternBitmap, boolean svg, File return null; } ThemeInfo themeInfo = accent.parentTheme; - HashMap values = getThemeFileValues(null, themeInfo.assetName, null); + SparseIntArray values = getThemeFileValues(null, themeInfo.assetName, null); checkIsDark(values, themeInfo); int backgroundAccent = accent.accentColor; @@ -1155,8 +1194,8 @@ private Bitmap createWallpaperForAccent(Bitmap patternBitmap, boolean svg, File if (backgroundColor != 0) { backgroundAccent = backgroundColor; } - Integer color = values.get(key_chat_wallpaper_gradient_to1); - if (color != null) { + int color = values.get(key_chat_wallpaper_gradient_to1); + if (color != 0) { backgroundGradientColor1 = changeColorAccent(themeInfo, backgroundAccent, color); } } else { @@ -1165,23 +1204,23 @@ private Bitmap createWallpaperForAccent(Bitmap patternBitmap, boolean svg, File int backgroundGradientColor2 = (int) accent.backgroundGradientOverrideColor2; if (backgroundGradientColor2 == 0 && accent.backgroundGradientOverrideColor2 == 0) { - Integer color = values.get(key_chat_wallpaper_gradient_to2); - if (color != null) { + int color = values.get(key_chat_wallpaper_gradient_to2); + if (color != 0) { backgroundGradientColor2 = changeColorAccent(themeInfo, backgroundAccent, color); } } int backgroundGradientColor3 = (int) accent.backgroundGradientOverrideColor3; if (backgroundGradientColor3 == 0 && accent.backgroundGradientOverrideColor3 == 0) { - Integer color = values.get(key_chat_wallpaper_gradient_to3); - if (color != null) { + int color = values.get(key_chat_wallpaper_gradient_to3); + if (color != 0) { backgroundGradientColor3 = changeColorAccent(themeInfo, backgroundAccent, color); } } if (backgroundColor == 0) { - Integer color = values.get(key_chat_wallpaper); - if (color != null) { + int color = values.get(key_chat_wallpaper); + if (color != 0) { backgroundColor = changeColorAccent(themeInfo, backgroundAccent, color); } } @@ -1341,7 +1380,7 @@ public ThemeAccent(ThemeAccent other) { this.overrideWallpaper = other.overrideWallpaper; } - public boolean fillAccentColors(HashMap currentColorsNoAccent, HashMap currentColors) { + public boolean fillAccentColors(SparseIntArray currentColorsNoAccent, SparseIntArray currentColors) { boolean isMyMessagesGradientColorsNear = false; float[] hsvTemp1 = getTempHsv(1); @@ -1352,25 +1391,22 @@ public boolean fillAccentColors(HashMap currentColorsNoAccent, boolean isDarkTheme = parentTheme.isDark(); if (accentColor != parentTheme.accentBaseColor || accentColor2 != 0) { - Set currentKeySet = currentColorsNoAccent.keySet(); - Set defaultKeySet = defaultColors.keySet(); - HashSet keys = new HashSet<>(currentKeySet.size() + defaultKeySet.size()); - keys.addAll(currentKeySet); - keys.addAll(defaultKeySet); - keys.removeAll(themeAccentExclusionKeys); - - for (String key : keys) { - Integer color = currentColorsNoAccent.get(key); - if (color == null) { - String fallbackKey = fallbackKeys.get(key); - if (fallbackKey != null && currentColorsNoAccent.get(fallbackKey) != null) { + for (int i = 0; i < defaultColors.length; i++) { + int key = i; + if (themeAccentExclusionKeys.contains(key)) { + continue; + } + int index = currentColorsNoAccent.indexOfKey(key); + int color; + if (index < 0) { + int fallbackKey = fallbackKeys.get(key, -1); + if (fallbackKey >= 0 && currentColorsNoAccent.indexOfKey(fallbackKey) >= 0) { continue; } + color = defaultColors[key]; + } else { + color = currentColorsNoAccent.valueAt(index); } - if (color == null) { - color = defaultColors.get(key); - } - int newColor = changeColorAccent(hsvTemp1, hsvTemp2, color, isDarkTheme); if (newColor != color) { currentColors.put(key, newColor); @@ -1380,9 +1416,9 @@ public boolean fillAccentColors(HashMap currentColorsNoAccent, int myMessagesAccent = myMessagesAccentColor; if ((myMessagesAccentColor != 0 || accentColor != 0) && myMessagesGradientAccentColor1 != 0) { int firstColor = myMessagesAccentColor != 0 ? myMessagesAccentColor : accentColor; - Integer color = currentColorsNoAccent.get(key_chat_outBubble); - if (color == null) { - color = defaultColors.get(key_chat_outBubble); + int color = currentColorsNoAccent.get(key_chat_outBubble); + if (color == 0) { + color = defaultColors[key_chat_outBubble]; } int newColor = changeColorAccent(hsvTemp1, hsvTemp2, color, isDarkTheme); int distance1 = AndroidUtilities.getColorDistance(firstColor, newColor); @@ -1414,19 +1450,17 @@ public boolean fillAccentColors(HashMap currentColorsNoAccent, Color.colorToHSV(myMessagesAccent, hsvTemp2); } - for (String key : myMessagesColorKeys) { - Integer color = currentColorsNoAccent.get(key); - if (color == null) { - String fallbackKey = fallbackKeys.get(key); - if (fallbackKey != null && currentColorsNoAccent.get(fallbackKey) != null) { + for (int key = myMessagesStartIndex; key < myMessagesEndIndex; key++) { + int index = currentColorsNoAccent.indexOfKey(key); + int color; + if (index < 0) { + int fallbackKey = fallbackKeys.get(key, -1); + if (fallbackKey >= 0 && currentColorsNoAccent.get(fallbackKey, -1) >= 0) { continue; } - } - if (color == null) { - color = defaultColors.get(key); - } - if (color == null) { - continue; + color = defaultColors[key]; + } else { + color = currentColorsNoAccent.valueAt(index); } int newColor = changeColorAccent(hsvTemp1, hsvTemp2, color, isDarkTheme); if (newColor != color) { @@ -1436,19 +1470,17 @@ public boolean fillAccentColors(HashMap currentColorsNoAccent, if (changeMyMessagesColors) { Color.colorToHSV(myMessagesAccent, hsvTemp2); - for (String key : myMessagesBubblesColorKeys) { - Integer color = currentColorsNoAccent.get(key); - if (color == null) { - String fallbackKey = fallbackKeys.get(key); - if (fallbackKey != null && currentColorsNoAccent.get(fallbackKey) != null) { + for (int key = myMessagesBubblesStartIndex; key < myMessagesBubblesEndIndex; key++) { + int index = currentColorsNoAccent.indexOfKey(key); + int color; + if (index < 0) { + int fallbackKey = fallbackKeys.get(key, -1); + if (fallbackKey >= 0 && currentColorsNoAccent.get(fallbackKey, -1) >= 0) { continue; } - } - if (color == null) { - color = defaultColors.get(key); - } - if (color == null) { - continue; + color = defaultColors[key]; + } else { + color = currentColorsNoAccent.valueAt(index); } int newColor = changeColorAccent(hsvTemp1, hsvTemp2, color, isDarkTheme); if (newColor != color) { @@ -1560,7 +1592,7 @@ public boolean fillAccentColors(HashMap currentColorsNoAccent, } } if (isMyMessagesGradientColorsNear) { - int outColor = currentColors.containsKey(key_chat_outLoader) + int outColor = currentColors.indexOfKey(key_chat_outLoader) >= 0 ? currentColors.get(key_chat_outLoader) : Color.TRANSPARENT; if (AndroidUtilities.getColorDistance(0xffffffff, outColor) < 5000) { @@ -1582,42 +1614,42 @@ public boolean fillAccentColors(HashMap currentColorsNoAccent, if (backgroundOverride != 0) { currentColors.put(key_chat_wallpaper, backgroundOverride); } else if (backgroundOverrideColor != 0) { - currentColors.remove(key_chat_wallpaper); + currentColors.delete(key_chat_wallpaper); } int backgroundGradientOverride1 = (int) backgroundGradientOverrideColor1; if (backgroundGradientOverride1 != 0) { currentColors.put(key_chat_wallpaper_gradient_to1, backgroundGradientOverride1); } else if (backgroundGradientOverrideColor1 != 0) { - currentColors.remove(key_chat_wallpaper_gradient_to1); + currentColors.delete(key_chat_wallpaper_gradient_to1); } int backgroundGradientOverride2 = (int) backgroundGradientOverrideColor2; if (backgroundGradientOverride2 != 0) { currentColors.put(key_chat_wallpaper_gradient_to2, backgroundGradientOverride2); } else if (backgroundGradientOverrideColor2 != 0) { - currentColors.remove(key_chat_wallpaper_gradient_to2); + currentColors.delete(key_chat_wallpaper_gradient_to2); } int backgroundGradientOverride3 = (int) backgroundGradientOverrideColor3; if (backgroundGradientOverride3 != 0) { currentColors.put(key_chat_wallpaper_gradient_to3, backgroundGradientOverride3); } else if (backgroundGradientOverrideColor3 != 0) { - currentColors.remove(key_chat_wallpaper_gradient_to3); + currentColors.delete(key_chat_wallpaper_gradient_to3); } if (backgroundRotation != 45) { currentColors.put(key_chat_wallpaper_gradient_rotation, backgroundRotation); } - Integer outBubble = currentColors.get(key_chat_outBubble); - if (outBubble == null) { + int outBubble = currentColors.get(key_chat_outBubble); + if (outBubble == 0) { outBubble = getColor(key_chat_outBubble); } - Integer inBubble = currentColors.get(key_chat_inBubble); - if (inBubble == null) { + int inBubble = currentColors.get(key_chat_inBubble); + if (inBubble == 0) { inBubble = getColor(key_chat_inBubble); } int gradientAverageColor = outBubble; if (info != null && info.emoticon != null && !isDarkTheme) { - currentColors.remove(key_chat_selectedBackground); + currentColors.delete(key_chat_selectedBackground); gradientAverageColor = averageColor(currentColors, key_chat_wallpaper_gradient_to1, key_chat_wallpaper_gradient_to2, key_chat_wallpaper_gradient_to3); if (gradientAverageColor == 0) { gradientAverageColor = averageColor(currentColors, key_chat_wallpaper); @@ -1646,19 +1678,19 @@ public boolean fillAccentColors(HashMap currentColorsNoAccent, currentColors.put(key_chat_outBubbleLocationPlaceholder, locationPlaceholderColor(accentHue, outBubble, isDarkTheme)); currentColors.put(key_chat_inBubbleLocationPlaceholder, locationPlaceholderColor(accentHue, inBubble, isDarkTheme)); - Integer inMsgLink = currentColors.get(key_chat_messageLinkIn); - if (inMsgLink == null) { + int inMsgLink = currentColors.get(key_chat_messageLinkIn); + if (inMsgLink == 0) { inMsgLink = getColor(key_chat_messageLinkIn); } - Integer outMsgLink = currentColors.get(key_chat_messageLinkOut); - if (outMsgLink == null) { + int outMsgLink = currentColors.get(key_chat_messageLinkOut); + if (outMsgLink == 0) { outMsgLink = getColor(key_chat_messageLinkOut); } currentColors.put(key_chat_linkSelectBackground, linkSelectionBackground(inMsgLink, inBubble, isDarkTheme)); currentColors.put(key_chat_outLinkSelectBackground, linkSelectionBackground(outMsgLink, outBubble, isDarkTheme)); - Integer submenuBackground = currentColors.get(key_actionBarDefaultSubmenuBackground); - if (submenuBackground == null) { + int submenuBackground = currentColors.get(key_actionBarDefaultSubmenuBackground); + if (submenuBackground == 0) { submenuBackground = getColor(key_actionBarDefaultSubmenuBackground); } currentColors.put(key_actionBarDefaultSubmenuSeparator, Color.argb( @@ -1740,10 +1772,10 @@ private int locationPlaceholderColor(float accentHue, int bubbleColor, boolean i return Color.HSVToColor(0x5a, tempHSV); } } - private int averageColor(HashMap colors, String ...keys) { + private int averageColor(SparseIntArray colors, int ...keys) { int r = 0, g = 0, b = 0, c = 0; for (int i = 0; i < keys.length; ++i) { - if (!colors.containsKey(keys[i])) { + if (colors.indexOfKey(keys[i]) < 0) { continue; } try { @@ -1773,8 +1805,8 @@ public File saveToFile() { dir.mkdirs(); File path = new File(dir, String.format(Locale.US, "%s_%d.attheme", parentTheme.getKey(), id)); - HashMap currentColorsNoAccent = getThemeFileValues(null, parentTheme.assetName, null); - HashMap currentColors = new HashMap<>(currentColorsNoAccent); + SparseIntArray currentColorsNoAccent = getThemeFileValues(null, parentTheme.assetName, null); + SparseIntArray currentColors = currentColorsNoAccent.clone(); fillAccentColors(currentColorsNoAccent, currentColors); String wallpaperLink = null; @@ -1784,24 +1816,24 @@ public File saveToFile() { if (patternMotion) { modes.append("motion"); } - Integer selectedColor = currentColors.get(key_chat_wallpaper); - if (selectedColor == null) { + int selectedColor = currentColors.get(key_chat_wallpaper); + if (selectedColor == 0) { selectedColor = 0xffffffff; } - Integer selectedGradientColor1 = currentColors.get(key_chat_wallpaper_gradient_to1); - if (selectedGradientColor1 == null) { + int selectedGradientColor1 = currentColors.get(key_chat_wallpaper_gradient_to1); + if (selectedGradientColor1 == 0) { selectedGradientColor1 = 0; } - Integer selectedGradientColor2 = currentColors.get(key_chat_wallpaper_gradient_to2); - if (selectedGradientColor2 == null) { + int selectedGradientColor2 = currentColors.get(key_chat_wallpaper_gradient_to2); + if (selectedGradientColor2 == 0) { selectedGradientColor2 = 0; } - Integer selectedGradientColor3 = currentColors.get(key_chat_wallpaper_gradient_to3); - if (selectedGradientColor3 == null) { + int selectedGradientColor3 = currentColors.get(key_chat_wallpaper_gradient_to3); + if (selectedGradientColor3 == 0) { selectedGradientColor3 = 0; } - Integer selectedGradientRotation = currentColors.get(key_chat_wallpaper_gradient_rotation); - if (selectedGradientRotation == null) { + int selectedGradientRotation = currentColors.get(key_chat_wallpaper_gradient_rotation); + if (selectedGradientRotation == 0) { selectedGradientRotation = 45; } String color = String.format("%02x%02x%02x", (byte) (selectedColor >> 16) & 0xff, (byte) (selectedColor >> 8) & 0xff, (byte) (selectedColor & 0xff)).toLowerCase(); @@ -1825,14 +1857,15 @@ public File saveToFile() { } StringBuilder result = new StringBuilder(); - for (HashMap.Entry entry : currentColors.entrySet()) { - String key = entry.getKey(); + for (int i = 0; i < currentColors.size(); i++) { + int key = currentColors.keyAt(i); + int color = currentColors.valueAt(i); if (wallpaperLink != null) { - if (key_chat_wallpaper.equals(key) || key_chat_wallpaper_gradient_to1.equals(key) || key_chat_wallpaper_gradient_to2.equals(key) || key_chat_wallpaper_gradient_to3.equals(key)) { + if (key_chat_wallpaper == key || key_chat_wallpaper_gradient_to1 == key || key_chat_wallpaper_gradient_to2 == key || key_chat_wallpaper_gradient_to3 == key) { continue; } } - result.append(key).append("=").append(entry.getValue()).append("\n"); + result.append(key).append("=").append(color).append("\n"); } FileOutputStream stream = null; try { @@ -1934,9 +1967,13 @@ public static class OverrideWallpaperInfo { public float intensity; public long wallpaperId; public long accessHash; + public long dialogId; public ThemeInfo parentTheme; public ThemeAccent parentAccent; + public float uploadingProgress; + public ArrayList requestIds; + public TLRPC.WallPaper prevUserWallpaper; public OverrideWallpaperInfo() { @@ -2380,7 +2417,7 @@ public boolean isDark() { } if (isDark == UNKNOWN && pathToFile != null) { String[] wallpaperLink = new String[1]; - HashMap colors = getThemeFileValues(new File(pathToFile), null, wallpaperLink); + SparseIntArray colors = getThemeFileValues(new File(pathToFile), null, wallpaperLink); checkIsDark(colors, this); } return isDark == DARK; @@ -2837,25 +2874,24 @@ public void didReceivedNotification(int id, int account, Object... args) { public interface ResourcesProvider { - Integer getColor(String key); + int getColor(int key); - default int getColorOrDefault(String key) { - Integer color = getColor(key); - return color != null ? color : Theme.getColor(key); + default int getColorOrDefault(int key) { + return getColor(key); } - default Integer getCurrentColor(String key) { + default int getCurrentColor(int key) { return getColor(key); } - default void setAnimatedColor(String key, int color) {} + default void setAnimatedColor(int key, int color) {} default Drawable getDrawable(String drawableKey) { return null; } default Paint getPaint(String paintKey) { - return null; + return Theme.getThemePaint(paintKey); } default boolean hasGradientService() { @@ -2865,6 +2901,10 @@ default boolean hasGradientService() { default void applyServiceShaderMatrix(int w, int h, float translationX, float translationY) { Theme.applyServiceShaderMatrix(w, h, translationX, translationY); } + + default ColorFilter getAnimatedEmojiColorFilter() { + return Theme.chat_animatedEmojiTextColorFilter; + } } private static final Object sync = new Object(); @@ -2979,7 +3019,7 @@ public void run() { public static Paint avatar_backgroundPaint; public static Drawable listSelector; - public static Drawable[] avatarDrawables = new Drawable[13]; + public static Drawable[] avatarDrawables = new Drawable[14]; public static Drawable moveUpDrawable; @@ -3170,7 +3210,6 @@ public void run() { public static Drawable chat_inlineResultFile; public static Drawable chat_inlineResultAudio; public static Drawable chat_inlineResultLocation; - public static Drawable chat_redLocationIcon; public static Drawable chat_msgOutLocationDrawable; public static Drawable chat_contextResult_shadowUnderSwitchDrawable; public static Drawable chat_shareIconDrawable; @@ -3199,7 +3238,6 @@ public void run() { public static Drawable chat_msgCallDownRedDrawable; public static Drawable chat_msgCallDownGreenDrawable; - public static Drawable chat_msgAvatarLiveLocationDrawable; public static Drawable chat_attachEmptyDrawable; public static RLottieDrawable[] chat_attachButtonDrawables = new RLottieDrawable[6]; public static Drawable[] chat_locationDrawable = new Drawable[2]; @@ -3219,768 +3257,780 @@ public void run() { private static AudioVisualizerDrawable chat_msgAudioVisualizeDrawable; private static HashMap animatedOutVisualizerDrawables; - public static final String key_dialogBackground = "dialogBackground"; - public static final String key_dialogBackgroundGray = "dialogBackgroundGray"; - public static final String key_dialogTextBlack = "dialogTextBlack"; - public static final String key_dialogTextLink = "dialogTextLink"; - public static final String key_dialogLinkSelection = "dialogLinkSelection"; - public static final String key_dialogTextRed = "dialogTextRed"; - public static final String key_dialogTextBlue = "dialogTextBlue"; - public static final String key_dialogTextBlue2 = "dialogTextBlue2"; - public static final String key_dialogTextBlue4 = "dialogTextBlue4"; - public static final String key_dialogTextGray = "dialogTextGray"; - public static final String key_dialogTextGray2 = "dialogTextGray2"; - public static final String key_dialogTextGray3 = "dialogTextGray3"; - public static final String key_dialogTextGray4 = "dialogTextGray4"; - public static final String key_dialogTextHint = "dialogTextHint"; - public static final String key_dialogInputField = "dialogInputField"; - public static final String key_dialogInputFieldActivated = "dialogInputFieldActivated"; - public static final String key_dialogCheckboxSquareBackground = "dialogCheckboxSquareBackground"; - public static final String key_dialogCheckboxSquareCheck = "dialogCheckboxSquareCheck"; - public static final String key_dialogCheckboxSquareUnchecked = "dialogCheckboxSquareUnchecked"; - public static final String key_dialogCheckboxSquareDisabled = "dialogCheckboxSquareDisabled"; - public static final String key_dialogScrollGlow = "dialogScrollGlow"; - public static final String key_dialogRoundCheckBox = "dialogRoundCheckBox"; - public static final String key_dialogRoundCheckBoxCheck = "dialogRoundCheckBoxCheck"; - public static final String key_dialogRadioBackground = "dialogRadioBackground"; - public static final String key_dialogRadioBackgroundChecked = "dialogRadioBackgroundChecked"; - public static final String key_dialogLineProgress = "dialogLineProgress"; - public static final String key_dialogLineProgressBackground = "dialogLineProgressBackground"; - public static final String key_dialogButton = "dialogButton"; - public static final String key_dialogButtonSelector = "dialogButtonSelector"; - public static final String key_dialogIcon = "dialogIcon"; - public static final String key_dialogRedIcon = "dialogRedIcon"; - public static final String key_dialogGrayLine = "dialogGrayLine"; - public static final String key_dialogTopBackground = "dialogTopBackground"; - public static final String key_dialogCameraIcon = "dialogCameraIcon"; - public static final String key_dialog_inlineProgressBackground = "dialog_inlineProgressBackground"; - public static final String key_dialog_inlineProgress = "dialog_inlineProgress"; - public static final String key_dialogSearchBackground = "dialogSearchBackground"; - public static final String key_dialogSearchHint = "dialogSearchHint"; - public static final String key_dialogSearchIcon = "dialogSearchIcon"; - public static final String key_dialogSearchText = "dialogSearchText"; - public static final String key_dialogFloatingButton = "dialogFloatingButton"; - public static final String key_dialogFloatingButtonPressed = "dialogFloatingButtonPressed"; - public static final String key_dialogFloatingIcon = "dialogFloatingIcon"; - public static final String key_dialogShadowLine = "dialogShadowLine"; - public static final String key_dialogEmptyImage = "dialogEmptyImage"; - public static final String key_dialogEmptyText = "dialogEmptyText"; - public static final String key_dialogSwipeRemove = "dialogSwipeRemove"; - public static final String key_dialogReactionMentionBackground = "dialogReactionMentionBackground"; - - public static final String key_windowBackgroundWhite = "windowBackgroundWhite"; - public static final String key_windowBackgroundUnchecked = "windowBackgroundUnchecked"; - public static final String key_windowBackgroundChecked = "windowBackgroundChecked"; - public static final String key_windowBackgroundCheckText = "windowBackgroundCheckText"; - public static final String key_progressCircle = "progressCircle"; - public static final String key_listSelector = "listSelectorSDK21"; - public static final String key_windowBackgroundWhiteInputField = "windowBackgroundWhiteInputField"; - public static final String key_windowBackgroundWhiteInputFieldActivated = "windowBackgroundWhiteInputFieldActivated"; - public static final String key_windowBackgroundWhiteGrayIcon = "windowBackgroundWhiteGrayIcon"; - public static final String key_windowBackgroundWhiteBlueText = "windowBackgroundWhiteBlueText"; - public static final String key_windowBackgroundWhiteBlueText2 = "windowBackgroundWhiteBlueText2"; - public static final String key_windowBackgroundWhiteBlueText3 = "windowBackgroundWhiteBlueText3"; - public static final String key_windowBackgroundWhiteBlueText4 = "windowBackgroundWhiteBlueText4"; - public static final String key_windowBackgroundWhiteBlueText5 = "windowBackgroundWhiteBlueText5"; - public static final String key_windowBackgroundWhiteBlueText6 = "windowBackgroundWhiteBlueText6"; - public static final String key_windowBackgroundWhiteBlueText7 = "windowBackgroundWhiteBlueText7"; - public static final String key_windowBackgroundWhiteBlueButton = "windowBackgroundWhiteBlueButton"; - public static final String key_windowBackgroundWhiteBlueIcon = "windowBackgroundWhiteBlueIcon"; - public static final String key_windowBackgroundWhiteGreenText = "windowBackgroundWhiteGreenText"; - public static final String key_windowBackgroundWhiteGreenText2 = "windowBackgroundWhiteGreenText2"; - public static final String key_windowBackgroundWhiteRedText = "windowBackgroundWhiteRedText"; - public static final String key_windowBackgroundWhiteRedText2 = "windowBackgroundWhiteRedText2"; - public static final String key_windowBackgroundWhiteRedText3 = "windowBackgroundWhiteRedText3"; - public static final String key_windowBackgroundWhiteRedText4 = "windowBackgroundWhiteRedText4"; - public static final String key_windowBackgroundWhiteRedText5 = "windowBackgroundWhiteRedText5"; - public static final String key_windowBackgroundWhiteGrayText = "windowBackgroundWhiteGrayText"; - public static final String key_windowBackgroundWhiteGrayText2 = "windowBackgroundWhiteGrayText2"; - public static final String key_windowBackgroundWhiteGrayText3 = "windowBackgroundWhiteGrayText3"; - public static final String key_windowBackgroundWhiteGrayText4 = "windowBackgroundWhiteGrayText4"; - public static final String key_windowBackgroundWhiteGrayText5 = "windowBackgroundWhiteGrayText5"; - public static final String key_windowBackgroundWhiteGrayText6 = "windowBackgroundWhiteGrayText6"; - public static final String key_windowBackgroundWhiteGrayText7 = "windowBackgroundWhiteGrayText7"; - public static final String key_windowBackgroundWhiteGrayText8 = "windowBackgroundWhiteGrayText8"; - public static final String key_windowBackgroundWhiteBlackText = "windowBackgroundWhiteBlackText"; - public static final String key_windowBackgroundWhiteHintText = "windowBackgroundWhiteHintText"; - public static final String key_windowBackgroundWhiteValueText = "windowBackgroundWhiteValueText"; - public static final String key_windowBackgroundWhiteLinkText = "windowBackgroundWhiteLinkText"; - public static final String key_windowBackgroundWhiteLinkSelection = "windowBackgroundWhiteLinkSelection"; - public static final String key_windowBackgroundWhiteBlueHeader = "windowBackgroundWhiteBlueHeader"; - public static final String key_switchTrack = "switchTrack"; - public static final String key_switchTrackChecked = "switchTrackChecked"; - public static final String key_switchTrackBlue = "switchTrackBlue"; - public static final String key_switchTrackBlueChecked = "switchTrackBlueChecked"; - public static final String key_switchTrackBlueThumb = "switchTrackBlueThumb"; - public static final String key_switchTrackBlueThumbChecked = "switchTrackBlueThumbChecked"; - public static final String key_switchTrackBlueSelector = "switchTrackBlueSelector"; - public static final String key_switchTrackBlueSelectorChecked = "switchTrackBlueSelectorChecked"; - public static final String key_switch2Track = "switch2Track"; - public static final String key_switch2TrackChecked = "switch2TrackChecked"; - public static final String key_checkboxSquareBackground = "checkboxSquareBackground"; - public static final String key_checkboxSquareCheck = "checkboxSquareCheck"; - public static final String key_checkboxSquareUnchecked = "checkboxSquareUnchecked"; - public static final String key_checkboxSquareDisabled = "checkboxSquareDisabled"; - public static final String key_windowBackgroundGray = "windowBackgroundGray"; - public static final String key_windowBackgroundGrayShadow = "windowBackgroundGrayShadow"; - public static final String key_emptyListPlaceholder = "emptyListPlaceholder"; - public static final String key_divider = "divider"; - public static final String key_graySection = "graySection"; - public static final String key_graySectionText = "key_graySectionText"; - public static final String key_radioBackground = "radioBackground"; - public static final String key_radioBackgroundChecked = "radioBackgroundChecked"; - public static final String key_checkbox = "checkbox"; - public static final String key_checkboxDisabled = "checkboxDisabled"; - public static final String key_checkboxCheck = "checkboxCheck"; - public static final String key_fastScrollActive = "fastScrollActive"; - public static final String key_fastScrollInactive = "fastScrollInactive"; - public static final String key_fastScrollText = "fastScrollText"; - - public static final String key_inappPlayerPerformer = "inappPlayerPerformer"; - public static final String key_inappPlayerTitle = "inappPlayerTitle"; - public static final String key_inappPlayerBackground = "inappPlayerBackground"; - public static final String key_inappPlayerPlayPause = "inappPlayerPlayPause"; - public static final String key_inappPlayerClose = "inappPlayerClose"; - - public static final String key_returnToCallBackground = "returnToCallBackground"; - public static final String key_returnToCallMutedBackground = "returnToCallMutedBackground"; - public static final String key_returnToCallText = "returnToCallText"; - - public static final String key_contextProgressInner1 = "contextProgressInner1"; - public static final String key_contextProgressOuter1 = "contextProgressOuter1"; - public static final String key_contextProgressInner2 = "contextProgressInner2"; - public static final String key_contextProgressOuter2 = "contextProgressOuter2"; - public static final String key_contextProgressInner3 = "contextProgressInner3"; - public static final String key_contextProgressOuter3 = "contextProgressOuter3"; - public static final String key_contextProgressInner4 = "contextProgressInner4"; - public static final String key_contextProgressOuter4 = "contextProgressOuter4"; - - public static final String key_avatar_text = "avatar_text"; - public static final String key_avatar_backgroundSaved = "avatar_backgroundSaved"; - public static final String key_avatar_background2Saved = "avatar_background2Saved"; - public static final String key_avatar_backgroundArchived = "avatar_backgroundArchived"; - public static final String key_avatar_backgroundArchivedHidden = "avatar_backgroundArchivedHidden"; - public static final String key_avatar_backgroundRed = "avatar_backgroundRed"; - public static final String key_avatar_backgroundOrange = "avatar_backgroundOrange"; - public static final String key_avatar_backgroundViolet = "avatar_backgroundViolet"; - public static final String key_avatar_backgroundGreen = "avatar_backgroundGreen"; - public static final String key_avatar_backgroundCyan = "avatar_backgroundCyan"; - public static final String key_avatar_backgroundBlue = "avatar_backgroundBlue"; - public static final String key_avatar_backgroundPink = "avatar_backgroundPink"; - public static final String key_avatar_background2Red = "avatar_background2Red"; - public static final String key_avatar_background2Orange = "avatar_background2Orange"; - public static final String key_avatar_background2Violet = "avatar_background2Violet"; - public static final String key_avatar_background2Green = "avatar_background2Green"; - public static final String key_avatar_background2Cyan = "avatar_background2Cyan"; - public static final String key_avatar_background2Blue = "avatar_background2Blue"; - public static final String key_avatar_background2Pink = "avatar_background2Pink"; - - public static final String key_avatar_backgroundInProfileBlue = "avatar_backgroundInProfileBlue"; - public static final String key_avatar_backgroundActionBarBlue = "avatar_backgroundActionBarBlue"; - public static final String key_avatar_actionBarSelectorBlue = "avatar_actionBarSelectorBlue"; - public static final String key_avatar_actionBarIconBlue = "avatar_actionBarIconBlue"; - public static final String key_avatar_subtitleInProfileBlue = "avatar_subtitleInProfileBlue"; - - public static final String key_avatar_nameInMessageRed = "avatar_nameInMessageRed"; - public static final String key_avatar_nameInMessageOrange = "avatar_nameInMessageOrange"; - public static final String key_avatar_nameInMessageViolet = "avatar_nameInMessageViolet"; - public static final String key_avatar_nameInMessageGreen = "avatar_nameInMessageGreen"; - public static final String key_avatar_nameInMessageCyan = "avatar_nameInMessageCyan"; - public static final String key_avatar_nameInMessageBlue = "avatar_nameInMessageBlue"; - public static final String key_avatar_nameInMessagePink = "avatar_nameInMessagePink"; - - public static String[] keys_avatar_background = {key_avatar_backgroundRed, key_avatar_backgroundOrange, key_avatar_backgroundViolet, key_avatar_backgroundGreen, key_avatar_backgroundCyan, key_avatar_backgroundBlue, key_avatar_backgroundPink}; - public static String[] keys_avatar_background2 = {key_avatar_background2Red, key_avatar_background2Orange, key_avatar_background2Violet, key_avatar_background2Green, key_avatar_background2Cyan, key_avatar_background2Blue, key_avatar_background2Pink}; - public static String[] keys_avatar_nameInMessage = {key_avatar_nameInMessageRed, key_avatar_nameInMessageOrange, key_avatar_nameInMessageViolet, key_avatar_nameInMessageGreen, key_avatar_nameInMessageCyan, key_avatar_nameInMessageBlue, key_avatar_nameInMessagePink}; - - public static final String key_actionBarDefault = "actionBarDefault"; - public static final String key_actionBarDefaultSelector = "actionBarDefaultSelector"; - public static final String key_actionBarWhiteSelector = "actionBarWhiteSelector"; - public static final String key_actionBarDefaultIcon = "actionBarDefaultIcon"; - public static final String key_actionBarActionModeDefault = "actionBarActionModeDefault"; - public static final String key_actionBarActionModeDefaultTop = "actionBarActionModeDefaultTop"; - public static final String key_actionBarActionModeDefaultIcon = "actionBarActionModeDefaultIcon"; - public static final String key_actionBarActionModeDefaultSelector = "actionBarActionModeDefaultSelector"; - public static final String key_actionBarDefaultTitle = "actionBarDefaultTitle"; - public static final String key_actionBarDefaultSubtitle = "actionBarDefaultSubtitle"; - public static final String key_actionBarDefaultSearch = "actionBarDefaultSearch"; - public static final String key_actionBarDefaultSearchPlaceholder = "actionBarDefaultSearchPlaceholder"; - public static final String key_actionBarDefaultSubmenuItem = "actionBarDefaultSubmenuItem"; - public static final String key_actionBarDefaultSubmenuItemIcon = "actionBarDefaultSubmenuItemIcon"; - public static final String key_actionBarDefaultSubmenuBackground = "actionBarDefaultSubmenuBackground"; - public static final String key_actionBarDefaultSubmenuSeparator = "actionBarDefaultSubmenuSeparator"; - public static final String key_actionBarTabActiveText = "actionBarTabActiveText"; - public static final String key_actionBarTabUnactiveText = "actionBarTabUnactiveText"; - public static final String key_actionBarTabLine = "actionBarTabLine"; - public static final String key_actionBarTabSelector = "actionBarTabSelector"; - public static final String key_actionBarDefaultArchived = "actionBarDefaultArchived"; - public static final String key_actionBarDefaultArchivedSelector = "actionBarDefaultArchivedSelector"; - public static final String key_actionBarDefaultArchivedIcon = "actionBarDefaultArchivedIcon"; - public static final String key_actionBarDefaultArchivedTitle = "actionBarDefaultArchivedTitle"; - public static final String key_actionBarDefaultArchivedSearch = "actionBarDefaultArchivedSearch"; - public static final String key_actionBarDefaultArchivedSearchPlaceholder = "actionBarDefaultSearchArchivedPlaceholder"; - - public static final String key_actionBarBrowser = "actionBarBrowser"; - - public static final String key_chats_onlineCircle = "chats_onlineCircle"; - public static final String key_chats_unreadCounter = "chats_unreadCounter"; - public static final String key_chats_unreadCounterMuted = "chats_unreadCounterMuted"; - public static final String key_chats_unreadCounterText = "chats_unreadCounterText"; - public static final String key_chats_name = "chats_name"; - public static final String key_chats_nameArchived = "chats_nameArchived"; - public static final String key_chats_secretName = "chats_secretName"; - public static final String key_chats_secretIcon = "chats_secretIcon"; - public static final String key_chats_pinnedIcon = "chats_pinnedIcon"; - public static final String key_chats_archiveBackground = "chats_archiveBackground"; - public static final String key_chats_archivePinBackground = "chats_archivePinBackground"; - public static final String key_chats_archiveIcon = "chats_archiveIcon"; - public static final String key_chats_archiveText = "chats_archiveText"; - public static final String key_chats_message = "chats_message"; - public static final String key_chats_messageArchived = "chats_messageArchived"; - public static final String key_chats_message_threeLines = "chats_message_threeLines"; - public static final String key_chats_draft = "chats_draft"; - public static final String key_chats_nameMessage = "chats_nameMessage"; - public static final String key_chats_nameMessageArchived = "chats_nameMessageArchived"; - public static final String key_chats_nameMessage_threeLines = "chats_nameMessage_threeLines"; - public static final String key_chats_nameMessageArchived_threeLines = "chats_nameMessageArchived_threeLines"; - public static final String key_chats_attachMessage = "chats_attachMessage"; - public static final String key_chats_actionMessage = "chats_actionMessage"; - public static final String key_chats_date = "chats_date"; - public static final String key_chats_pinnedOverlay = "chats_pinnedOverlay"; - public static final String key_chats_tabletSelectedOverlay = "chats_tabletSelectedOverlay"; - public static final String key_chats_sentCheck = "chats_sentCheck"; - public static final String key_chats_sentReadCheck = "chats_sentReadCheck"; - public static final String key_chats_sentClock = "chats_sentClock"; - public static final String key_chats_sentError = "chats_sentError"; - public static final String key_chats_sentErrorIcon = "chats_sentErrorIcon"; - public static final String key_chats_verifiedBackground = "chats_verifiedBackground"; - public static final String key_chats_verifiedCheck = "chats_verifiedCheck"; - public static final String key_chats_muteIcon = "chats_muteIcon"; - public static final String key_chats_mentionIcon = "chats_mentionIcon"; - public static final String key_chats_menuTopShadow = "chats_menuTopShadow"; - public static final String key_chats_menuTopShadowCats = "chats_menuTopShadowCats"; - public static final String key_chats_menuBackground = "chats_menuBackground"; - public static final String key_chats_menuItemText = "chats_menuItemText"; - public static final String key_chats_menuItemCheck = "chats_menuItemCheck"; - public static final String key_chats_menuItemIcon = "chats_menuItemIcon"; - public static final String key_chats_menuName = "chats_menuName"; - public static final String key_chats_menuPhone = "chats_menuPhone"; - public static final String key_chats_menuPhoneCats = "chats_menuPhoneCats"; - public static final String key_chats_menuTopBackgroundCats = "chats_menuTopBackgroundCats"; - public static final String key_chats_menuTopBackground = "chats_menuTopBackground"; - public static final String key_chats_actionIcon = "chats_actionIcon"; - public static final String key_chats_actionBackground = "chats_actionBackground"; - public static final String key_chats_actionPressedBackground = "chats_actionPressedBackground"; - public static final String key_chats_archivePullDownBackground = "chats_archivePullDownBackground"; - public static final String key_chats_archivePullDownBackgroundActive = "chats_archivePullDownBackgroundActive"; - public static final String key_chats_tabUnreadActiveBackground = "chats_tabUnreadActiveBackground"; - public static final String key_chats_tabUnreadUnactiveBackground = "chats_tabUnreadUnactiveBackground"; - - public static final String key_chat_attachCheckBoxCheck = "chat_attachCheckBoxCheck"; - public static final String key_chat_attachCheckBoxBackground = "chat_attachCheckBoxBackground"; - public static final String key_chat_attachPhotoBackground = "chat_attachPhotoBackground"; - public static final String key_chat_attachActiveTab = "chat_attachActiveTab"; - public static final String key_chat_attachUnactiveTab = "chat_attachUnactiveTab"; - public static final String key_chat_attachPermissionImage = "chat_attachPermissionImage"; - public static final String key_chat_attachPermissionMark = "chat_attachPermissionMark"; - public static final String key_chat_attachPermissionText = "chat_attachPermissionText"; - public static final String key_chat_attachEmptyImage = "chat_attachEmptyImage"; - - public static final String key_chat_inPollCorrectAnswer = "chat_inPollCorrectAnswer"; - public static final String key_chat_outPollCorrectAnswer = "chat_outPollCorrectAnswer"; - public static final String key_chat_inPollWrongAnswer = "chat_inPollWrongAnswer"; - public static final String key_chat_outPollWrongAnswer = "chat_outPollWrongAnswer"; - - public static final String key_chat_attachIcon = "chat_attachIcon"; - public static final String key_chat_attachGalleryBackground = "chat_attachGalleryBackground"; - public static final String key_chat_attachGalleryText = "chat_attachGalleryText"; - public static final String key_chat_attachAudioBackground = "chat_attachAudioBackground"; - public static final String key_chat_attachAudioText = "chat_attachAudioText"; - public static final String key_chat_attachFileBackground = "chat_attachFileBackground"; - public static final String key_chat_attachFileText = "chat_attachFileText"; - public static final String key_chat_attachContactBackground = "chat_attachContactBackground"; - public static final String key_chat_attachContactText = "chat_attachContactText"; - public static final String key_chat_attachLocationBackground = "chat_attachLocationBackground"; - public static final String key_chat_attachLocationText = "chat_attachLocationText"; - public static final String key_chat_attachPollBackground = "chat_attachPollBackground"; - public static final String key_chat_attachPollText = "chat_attachPollText"; - - public static final String key_chat_status = "chat_status"; - public static final String key_chat_inRedCall = "chat_inUpCall"; - public static final String key_chat_inGreenCall = "chat_inDownCall"; - public static final String key_chat_outGreenCall = "chat_outUpCall"; - public static final String key_chat_inBubble = "chat_inBubble"; - public static final String key_chat_inBubbleSelected = "chat_inBubbleSelected"; - public static final String key_chat_inBubbleSelectedOverlay = "chat_inBubbleSelectedOverlay"; - public static final String key_chat_inBubbleShadow = "chat_inBubbleShadow"; - public static final String key_chat_outBubble = "chat_outBubble"; - public static final String key_chat_outBubbleGradient1 = "chat_outBubbleGradient"; - public static final String key_chat_outBubbleGradient2 = "chat_outBubbleGradient2"; - public static final String key_chat_outBubbleGradient3 = "chat_outBubbleGradient3"; - public static final String key_chat_outBubbleGradientAnimated = "chat_outBubbleGradientAnimated"; - public static final String key_chat_outBubbleGradientSelectedOverlay = "chat_outBubbleGradientSelectedOverlay"; - public static final String key_chat_outBubbleSelected = "chat_outBubbleSelected"; - public static final String key_chat_outBubbleSelectedOverlay = "chat_outBubbleSelectedOverlay"; - public static final String key_chat_outBubbleShadow = "chat_outBubbleShadow"; - public static final String key_chat_messageTextIn = "chat_messageTextIn"; - public static final String key_chat_messageTextOut = "chat_messageTextOut"; - public static final String key_chat_messageLinkIn = "chat_messageLinkIn"; - public static final String key_chat_messageLinkOut = "chat_messageLinkOut"; - public static final String key_chat_serviceText = "chat_serviceText"; - public static final String key_chat_serviceLink = "chat_serviceLink"; - public static final String key_chat_serviceIcon = "chat_serviceIcon"; - public static final String key_chat_serviceBackground = "chat_serviceBackground"; - public static final String key_chat_serviceBackgroundSelected = "chat_serviceBackgroundSelected"; - public static final String key_chat_serviceBackgroundSelector = "chat_serviceBackgroundSelector"; - public static final String key_chat_muteIcon = "chat_muteIcon"; - public static final String key_chat_lockIcon = "chat_lockIcon"; - public static final String key_chat_outSentCheck = "chat_outSentCheck"; - public static final String key_chat_outSentCheckSelected = "chat_outSentCheckSelected"; - public static final String key_chat_outSentCheckRead = "chat_outSentCheckRead"; - public static final String key_chat_outSentCheckReadSelected = "chat_outSentCheckReadSelected"; - public static final String key_chat_outSentClock = "chat_outSentClock"; - public static final String key_chat_outSentClockSelected = "chat_outSentClockSelected"; - public static final String key_chat_inSentClock = "chat_inSentClock"; - public static final String key_chat_inSentClockSelected = "chat_inSentClockSelected"; - public static final String key_chat_mediaSentCheck = "chat_mediaSentCheck"; - public static final String key_chat_mediaSentClock = "chat_mediaSentClock"; - public static final String key_chat_inMediaIcon = "chat_inMediaIcon"; - public static final String key_chat_outMediaIcon = "chat_outMediaIcon"; - public static final String key_chat_inMediaIconSelected = "chat_inMediaIconSelected"; - public static final String key_chat_outMediaIconSelected = "chat_outMediaIconSelected"; - public static final String key_chat_mediaTimeBackground = "chat_mediaTimeBackground"; - public static final String key_chat_outViews = "chat_outViews"; - public static final String key_chat_outViewsSelected = "chat_outViewsSelected"; - public static final String key_chat_inViews = "chat_inViews"; - public static final String key_chat_inViewsSelected = "chat_inViewsSelected"; - public static final String key_chat_mediaViews = "chat_mediaViews"; - public static final String key_chat_outMenu = "chat_outMenu"; - public static final String key_chat_outMenuSelected = "chat_outMenuSelected"; - public static final String key_chat_inMenu = "chat_inMenu"; - public static final String key_chat_inMenuSelected = "chat_inMenuSelected"; - public static final String key_chat_mediaMenu = "chat_mediaMenu"; - public static final String key_chat_outInstant = "chat_outInstant"; - public static final String key_chat_outInstantSelected = "chat_outInstantSelected"; - public static final String key_chat_inInstant = "chat_inInstant"; - public static final String key_chat_inInstantSelected = "chat_inInstantSelected"; - public static final String key_chat_sentError = "chat_sentError"; - public static final String key_chat_sentErrorIcon = "chat_sentErrorIcon"; - public static final String key_chat_selectedBackground = "chat_selectedBackground"; - public static final String key_chat_previewDurationText = "chat_previewDurationText"; - public static final String key_chat_previewGameText = "chat_previewGameText"; - public static final String key_chat_inPreviewInstantText = "chat_inPreviewInstantText"; - public static final String key_chat_outPreviewInstantText = "chat_outPreviewInstantText"; - public static final String key_chat_secretTimeText = "chat_secretTimeText"; - public static final String key_chat_stickerNameText = "chat_stickerNameText"; - public static final String key_chat_botButtonText = "chat_botButtonText"; - public static final String key_chat_inForwardedNameText = "chat_inForwardedNameText"; - public static final String key_chat_outForwardedNameText = "chat_outForwardedNameText"; - public static final String key_chat_inPsaNameText = "chat_inPsaNameText"; - public static final String key_chat_outPsaNameText = "chat_outPsaNameText"; - public static final String key_chat_inViaBotNameText = "chat_inViaBotNameText"; - public static final String key_chat_outViaBotNameText = "chat_outViaBotNameText"; - public static final String key_chat_stickerViaBotNameText = "chat_stickerViaBotNameText"; - public static final String key_chat_inReplyLine = "chat_inReplyLine"; - public static final String key_chat_outReplyLine = "chat_outReplyLine"; - public static final String key_chat_stickerReplyLine = "chat_stickerReplyLine"; - public static final String key_chat_inReplyNameText = "chat_inReplyNameText"; - public static final String key_chat_outReplyNameText = "chat_outReplyNameText"; - public static final String key_chat_stickerReplyNameText = "chat_stickerReplyNameText"; - public static final String key_chat_inReplyMessageText = "chat_inReplyMessageText"; - public static final String key_chat_outReplyMessageText = "chat_outReplyMessageText"; - public static final String key_chat_inReplyMediaMessageText = "chat_inReplyMediaMessageText"; - public static final String key_chat_outReplyMediaMessageText = "chat_outReplyMediaMessageText"; - public static final String key_chat_inReplyMediaMessageSelectedText = "chat_inReplyMediaMessageSelectedText"; - public static final String key_chat_outReplyMediaMessageSelectedText = "chat_outReplyMediaMessageSelectedText"; - public static final String key_chat_stickerReplyMessageText = "chat_stickerReplyMessageText"; - public static final String key_chat_inPreviewLine = "chat_inPreviewLine"; - public static final String key_chat_outPreviewLine = "chat_outPreviewLine"; - public static final String key_chat_inSiteNameText = "chat_inSiteNameText"; - public static final String key_chat_outSiteNameText = "chat_outSiteNameText"; - public static final String key_chat_inContactNameText = "chat_inContactNameText"; - public static final String key_chat_outContactNameText = "chat_outContactNameText"; - public static final String key_chat_inContactPhoneText = "chat_inContactPhoneText"; - public static final String key_chat_inContactPhoneSelectedText = "chat_inContactPhoneSelectedText"; - public static final String key_chat_outContactPhoneText = "chat_outContactPhoneText"; - public static final String key_chat_outContactPhoneSelectedText = "chat_outContactPhoneSelectedText"; - public static final String key_chat_mediaProgress = "chat_mediaProgress"; - public static final String key_chat_inAudioProgress = "chat_inAudioProgress"; - public static final String key_chat_outAudioProgress = "chat_outAudioProgress"; - public static final String key_chat_inAudioSelectedProgress = "chat_inAudioSelectedProgress"; - public static final String key_chat_outAudioSelectedProgress = "chat_outAudioSelectedProgress"; - public static final String key_chat_mediaTimeText = "chat_mediaTimeText"; - public static final String key_chat_inAdminText = "chat_adminText"; - public static final String key_chat_inAdminSelectedText = "chat_adminSelectedText"; - public static final String key_chat_outAdminText = "chat_outAdminText"; - public static final String key_chat_outAdminSelectedText = "chat_outAdminSelectedText"; - public static final String key_chat_inTimeText = "chat_inTimeText"; - public static final String key_chat_outTimeText = "chat_outTimeText"; - public static final String key_chat_inTimeSelectedText = "chat_inTimeSelectedText"; - public static final String key_chat_outTimeSelectedText = "chat_outTimeSelectedText"; - public static final String key_chat_inAudioPerformerText = "chat_inAudioPerfomerText"; - public static final String key_chat_inAudioPerformerSelectedText = "chat_inAudioPerfomerSelectedText"; - public static final String key_chat_outAudioPerformerText = "chat_outAudioPerfomerText"; - public static final String key_chat_outAudioPerformerSelectedText = "chat_outAudioPerfomerSelectedText"; - public static final String key_chat_inAudioTitleText = "chat_inAudioTitleText"; - public static final String key_chat_outAudioTitleText = "chat_outAudioTitleText"; - public static final String key_chat_inAudioDurationText = "chat_inAudioDurationText"; - public static final String key_chat_outAudioDurationText = "chat_outAudioDurationText"; - public static final String key_chat_inAudioDurationSelectedText = "chat_inAudioDurationSelectedText"; - public static final String key_chat_outAudioDurationSelectedText = "chat_outAudioDurationSelectedText"; - public static final String key_chat_inAudioSeekbar = "chat_inAudioSeekbar"; - public static final String key_chat_inAudioCacheSeekbar = "chat_inAudioCacheSeekbar"; - public static final String key_chat_outAudioSeekbar = "chat_outAudioSeekbar"; - public static final String key_chat_outAudioCacheSeekbar = "chat_outAudioCacheSeekbar"; - public static final String key_chat_inAudioSeekbarSelected = "chat_inAudioSeekbarSelected"; - public static final String key_chat_outAudioSeekbarSelected = "chat_outAudioSeekbarSelected"; - public static final String key_chat_inAudioSeekbarFill = "chat_inAudioSeekbarFill"; - public static final String key_chat_outAudioSeekbarFill = "chat_outAudioSeekbarFill"; - public static final String key_chat_inVoiceSeekbar = "chat_inVoiceSeekbar"; - public static final String key_chat_outVoiceSeekbar = "chat_outVoiceSeekbar"; - public static final String key_chat_inVoiceSeekbarSelected = "chat_inVoiceSeekbarSelected"; - public static final String key_chat_outVoiceSeekbarSelected = "chat_outVoiceSeekbarSelected"; - public static final String key_chat_inVoiceSeekbarFill = "chat_inVoiceSeekbarFill"; - public static final String key_chat_outVoiceSeekbarFill = "chat_outVoiceSeekbarFill"; - public static final String key_chat_inFileProgress = "chat_inFileProgress"; - public static final String key_chat_outFileProgress = "chat_outFileProgress"; - public static final String key_chat_inFileProgressSelected = "chat_inFileProgressSelected"; - public static final String key_chat_outFileProgressSelected = "chat_outFileProgressSelected"; - public static final String key_chat_inFileNameText = "chat_inFileNameText"; - public static final String key_chat_outFileNameText = "chat_outFileNameText"; - public static final String key_chat_inFileInfoText = "chat_inFileInfoText"; - public static final String key_chat_outFileInfoText = "chat_outFileInfoText"; - public static final String key_chat_inFileInfoSelectedText = "chat_inFileInfoSelectedText"; - public static final String key_chat_outFileInfoSelectedText = "chat_outFileInfoSelectedText"; - public static final String key_chat_inFileBackground = "chat_inFileBackground"; - public static final String key_chat_outFileBackground = "chat_outFileBackground"; - public static final String key_chat_inFileBackgroundSelected = "chat_inFileBackgroundSelected"; - public static final String key_chat_outFileBackgroundSelected = "chat_outFileBackgroundSelected"; - public static final String key_chat_inVenueInfoText = "chat_inVenueInfoText"; - public static final String key_chat_outVenueInfoText = "chat_outVenueInfoText"; - public static final String key_chat_inVenueInfoSelectedText = "chat_inVenueInfoSelectedText"; - public static final String key_chat_outVenueInfoSelectedText = "chat_outVenueInfoSelectedText"; - public static final String key_chat_mediaInfoText = "chat_mediaInfoText"; - public static final String key_chat_linkSelectBackground = "chat_linkSelectBackground"; - public static final String key_chat_outLinkSelectBackground = "chat_outLinkSelectBackground"; - public static final String key_chat_textSelectBackground = "chat_textSelectBackground"; - public static final String key_chat_wallpaper = "chat_wallpaper"; - public static final String key_chat_wallpaper_gradient_to1 = "chat_wallpaper_gradient_to"; - public static final String key_chat_wallpaper_gradient_to2 = "key_chat_wallpaper_gradient_to2"; - public static final String key_chat_wallpaper_gradient_to3 = "key_chat_wallpaper_gradient_to3"; - public static final String key_chat_wallpaper_gradient_rotation = "chat_wallpaper_gradient_rotation"; - public static final String key_chat_messagePanelBackground = "chat_messagePanelBackground"; - public static final String key_chat_messagePanelShadow = "chat_messagePanelShadow"; - public static final String key_chat_messagePanelText = "chat_messagePanelText"; - public static final String key_chat_messagePanelHint = "chat_messagePanelHint"; - public static final String key_chat_messagePanelCursor = "chat_messagePanelCursor"; - public static final String key_chat_messagePanelIcons = "chat_messagePanelIcons"; - public static final String key_chat_messagePanelSend = "chat_messagePanelSend"; - public static final String key_chat_messagePanelVoiceLock = "key_chat_messagePanelVoiceLock"; - public static final String key_chat_messagePanelVoiceLockBackground = "key_chat_messagePanelVoiceLockBackground"; - public static final String key_chat_messagePanelVoiceLockShadow = "key_chat_messagePanelVoiceLockShadow"; - public static final String key_chat_topPanelBackground = "chat_topPanelBackground"; - public static final String key_chat_topPanelClose = "chat_topPanelClose"; - public static final String key_chat_topPanelLine = "chat_topPanelLine"; - public static final String key_chat_topPanelTitle = "chat_topPanelTitle"; - public static final String key_chat_topPanelMessage = "chat_topPanelMessage"; - public static final String key_chat_reportSpam = "chat_reportSpam"; - public static final String key_chat_addContact = "chat_addContact"; - public static final String key_chat_inLoader = "chat_inLoader"; - public static final String key_chat_inLoaderSelected = "chat_inLoaderSelected"; - public static final String key_chat_outLoader = "chat_outLoader"; - public static final String key_chat_outLoaderSelected = "chat_outLoaderSelected"; - public static final String key_chat_inLoaderPhoto = "chat_inLoaderPhoto"; - public static final String key_chat_mediaLoaderPhoto = "chat_mediaLoaderPhoto"; - public static final String key_chat_mediaLoaderPhotoSelected = "chat_mediaLoaderPhotoSelected"; - public static final String key_chat_mediaLoaderPhotoIcon = "chat_mediaLoaderPhotoIcon"; - public static final String key_chat_mediaLoaderPhotoIconSelected = "chat_mediaLoaderPhotoIconSelected"; - public static final String key_chat_inLocationBackground = "chat_inLocationBackground"; - public static final String key_chat_inLocationIcon = "chat_inLocationIcon"; - public static final String key_chat_outLocationIcon = "chat_outLocationIcon"; - public static final String key_chat_inContactBackground = "chat_inContactBackground"; - public static final String key_chat_inContactIcon = "chat_inContactIcon"; - public static final String key_chat_outContactBackground = "chat_outContactBackground"; - public static final String key_chat_outContactIcon = "chat_outContactIcon"; - public static final String key_chat_replyPanelIcons = "chat_replyPanelIcons"; - public static final String key_chat_replyPanelClose = "chat_replyPanelClose"; - public static final String key_chat_replyPanelName = "chat_replyPanelName"; - public static final String key_chat_replyPanelLine = "chat_replyPanelLine"; - public static final String key_chat_searchPanelIcons = "chat_searchPanelIcons"; - public static final String key_chat_searchPanelText = "chat_searchPanelText"; - public static final String key_chat_secretChatStatusText = "chat_secretChatStatusText"; - public static final String key_chat_fieldOverlayText = "chat_fieldOverlayText"; - public static final String key_chat_stickersHintPanel = "chat_stickersHintPanel"; - public static final String key_chat_botSwitchToInlineText = "chat_botSwitchToInlineText"; - public static final String key_chat_unreadMessagesStartArrowIcon = "chat_unreadMessagesStartArrowIcon"; - public static final String key_chat_unreadMessagesStartText = "chat_unreadMessagesStartText"; - public static final String key_chat_unreadMessagesStartBackground = "chat_unreadMessagesStartBackground"; - public static final String key_chat_inlineResultIcon = "chat_inlineResultIcon"; - public static final String key_chat_emojiPanelBackground = "chat_emojiPanelBackground"; - public static final String key_chat_emojiSearchBackground = "chat_emojiSearchBackground"; - public static final String key_chat_emojiSearchIcon = "chat_emojiSearchIcon"; - public static final String key_chat_emojiPanelShadowLine = "chat_emojiPanelShadowLine"; - public static final String key_chat_emojiPanelEmptyText = "chat_emojiPanelEmptyText"; - public static final String key_chat_emojiPanelIcon = "chat_emojiPanelIcon"; - public static final String key_chat_emojiBottomPanelIcon = "chat_emojiBottomPanelIcon"; - public static final String key_chat_emojiPanelIconSelected = "chat_emojiPanelIconSelected"; - public static final String key_chat_emojiPanelStickerPackSelector = "chat_emojiPanelStickerPackSelector"; - public static final String key_chat_emojiPanelStickerPackSelectorLine = "chat_emojiPanelStickerPackSelectorLine"; - public static final String key_chat_emojiPanelBackspace = "chat_emojiPanelBackspace"; - public static final String key_chat_emojiPanelTrendingTitle = "chat_emojiPanelTrendingTitle"; - public static final String key_chat_emojiPanelStickerSetName = "chat_emojiPanelStickerSetName"; - public static final String key_chat_emojiPanelStickerSetNameHighlight = "chat_emojiPanelStickerSetNameHighlight"; - public static final String key_chat_emojiPanelStickerSetNameIcon = "chat_emojiPanelStickerSetNameIcon"; - public static final String key_chat_emojiPanelTrendingDescription = "chat_emojiPanelTrendingDescription"; - public static final String key_chat_botKeyboardButtonText = "chat_botKeyboardButtonText"; - public static final String key_chat_botKeyboardButtonBackground = "chat_botKeyboardButtonBackground"; - public static final String key_chat_botKeyboardButtonBackgroundPressed = "chat_botKeyboardButtonBackgroundPressed"; - public static final String key_chat_emojiPanelNewTrending = "chat_emojiPanelNewTrending"; - public static final String key_chat_messagePanelVoicePressed = "chat_messagePanelVoicePressed"; - public static final String key_chat_messagePanelVoiceBackground = "chat_messagePanelVoiceBackground"; - public static final String key_chat_messagePanelVoiceDelete = "chat_messagePanelVoiceDelete"; - public static final String key_chat_messagePanelVoiceDuration = "chat_messagePanelVoiceDuration"; - public static final String key_chat_recordedVoicePlayPause = "chat_recordedVoicePlayPause"; - public static final String key_chat_recordedVoiceProgress = "chat_recordedVoiceProgress"; - public static final String key_chat_recordedVoiceProgressInner = "chat_recordedVoiceProgressInner"; - public static final String key_chat_recordedVoiceDot = "chat_recordedVoiceDot"; - public static final String key_chat_recordedVoiceBackground = "chat_recordedVoiceBackground"; - public static final String key_chat_recordVoiceCancel = "chat_recordVoiceCancel"; - public static final String key_chat_recordTime = "chat_recordTime"; - public static final String key_chat_messagePanelCancelInlineBot = "chat_messagePanelCancelInlineBot"; - public static final String key_chat_gifSaveHintText = "chat_gifSaveHintText"; - public static final String key_chat_gifSaveHintBackground = "chat_gifSaveHintBackground"; - public static final String key_chat_goDownButton = "chat_goDownButton"; - public static final String key_chat_goDownButtonIcon = "chat_goDownButtonIcon"; - public static final String key_chat_goDownButtonCounter = "chat_goDownButtonCounter"; - public static final String key_chat_goDownButtonCounterBackground = "chat_goDownButtonCounterBackground"; - public static final String key_chat_outTextSelectionHighlight = "chat_outTextSelectionHighlight"; - public static final String key_chat_inTextSelectionHighlight = "chat_inTextSelectionHighlight"; - public static final String key_chat_TextSelectionCursor = "chat_TextSelectionCursor"; - public static final String key_chat_outTextSelectionCursor = "chat_outTextSelectionCursor"; - public static final String key_chat_inBubbleLocationPlaceholder = "chat_inBubbleLocationPlaceholder"; - public static final String key_chat_outBubbleLocationPlaceholder = "chat_outBubbleLocationPlaceholder"; - public static final String key_chat_BlurAlpha = "chat_BlurAlpha"; - - public static final String key_voipgroup_listSelector = "voipgroup_listSelector"; - public static final String key_voipgroup_inviteMembersBackground = "voipgroup_inviteMembersBackground"; - public static final String key_voipgroup_actionBar = "voipgroup_actionBar"; - public static final String key_voipgroup_actionBarItems = "voipgroup_actionBarItems"; - public static final String key_voipgroup_actionBarItemsSelector = "voipgroup_actionBarItemsSelector"; - public static final String key_voipgroup_actionBarUnscrolled = "voipgroup_actionBarUnscrolled"; - public static final String key_voipgroup_listViewBackgroundUnscrolled = "voipgroup_listViewBackgroundUnscrolled"; - public static final String key_voipgroup_lastSeenTextUnscrolled = "voipgroup_lastSeenTextUnscrolled"; - public static final String key_voipgroup_mutedIconUnscrolled = "voipgroup_mutedIconUnscrolled"; - public static final String key_voipgroup_nameText = "voipgroup_nameText"; - public static final String key_voipgroup_lastSeenText = "voipgroup_lastSeenText"; - public static final String key_voipgroup_listeningText = "voipgroup_listeningText"; - public static final String key_voipgroup_speakingText = "voipgroup_speakingText"; - public static final String key_voipgroup_mutedIcon = "voipgroup_mutedIcon"; - public static final String key_voipgroup_mutedByAdminIcon = "voipgroup_mutedByAdminIcon"; - public static final String key_voipgroup_listViewBackground = "voipgroup_listViewBackground"; - public static final String key_voipgroup_dialogBackground = "voipgroup_dialogBackground"; - public static final String key_voipgroup_leaveCallMenu = "voipgroup_leaveCallMenu"; - public static final String key_voipgroup_checkMenu = "voipgroup_checkMenu"; - public static final String key_voipgroup_soundButton = "voipgroup_soundButton"; - public static final String key_voipgroup_soundButtonActive = "voipgroup_soundButtonActive"; - public static final String key_voipgroup_soundButtonActiveScrolled = "voipgroup_soundButtonActiveScrolled"; - public static final String key_voipgroup_soundButton2 = "voipgroup_soundButton2"; - public static final String key_voipgroup_soundButtonActive2 = "voipgroup_soundButtonActive2"; - public static final String key_voipgroup_soundButtonActive2Scrolled = "voipgroup_soundButtonActive2Scrolled"; - public static final String key_voipgroup_leaveButton = "voipgroup_leaveButton"; - public static final String key_voipgroup_leaveButtonScrolled = "voipgroup_leaveButtonScrolled"; - public static final String key_voipgroup_muteButton = "voipgroup_muteButton"; - public static final String key_voipgroup_muteButton2 = "voipgroup_muteButton2"; - public static final String key_voipgroup_muteButton3 = "voipgroup_muteButton3"; - public static final String key_voipgroup_unmuteButton = "voipgroup_unmuteButton"; - public static final String key_voipgroup_unmuteButton2 = "voipgroup_unmuteButton2"; - public static final String key_voipgroup_disabledButton = "voipgroup_disabledButton"; - public static final String key_voipgroup_disabledButtonActive = "voipgroup_disabledButtonActive"; - public static final String key_voipgroup_disabledButtonActiveScrolled = "voipgroup_disabledButtonActiveScrolled"; - public static final String key_voipgroup_connectingProgress = "voipgroup_connectingProgress"; - public static final String key_voipgroup_scrollUp = "voipgroup_scrollUp"; - public static final String key_voipgroup_searchPlaceholder = "voipgroup_searchPlaceholder"; - public static final String key_voipgroup_searchBackground = "voipgroup_searchBackground"; - public static final String key_voipgroup_searchText = "voipgroup_searchText"; - public static final String key_voipgroup_overlayGreen1 = "voipgroup_overlayGreen1"; - public static final String key_voipgroup_overlayGreen2 = "voipgroup_overlayGreen2"; - public static final String key_voipgroup_overlayBlue1 = "voipgroup_overlayBlue1"; - public static final String key_voipgroup_overlayBlue2 = "voipgroup_overlayBlue2"; - public static final String key_voipgroup_topPanelGreen1 = "voipgroup_topPanelGreen1"; - public static final String key_voipgroup_topPanelGreen2 = "voipgroup_topPanelGreen2"; - public static final String key_voipgroup_topPanelBlue1 = "voipgroup_topPanelBlue1"; - public static final String key_voipgroup_topPanelBlue2 = "voipgroup_topPanelBlue2"; - public static final String key_voipgroup_topPanelGray = "voipgroup_topPanelGray"; - public static final String key_voipgroup_overlayAlertGradientMuted = "voipgroup_overlayAlertGradientMuted"; - public static final String key_voipgroup_overlayAlertGradientMuted2 = "voipgroup_overlayAlertGradientMuted2"; - public static final String key_voipgroup_overlayAlertGradientUnmuted = "voipgroup_overlayAlertGradientUnmuted"; - public static final String key_voipgroup_overlayAlertGradientUnmuted2 = "voipgroup_overlayAlertGradientUnmuted2"; - public static final String key_voipgroup_overlayAlertMutedByAdmin = "voipgroup_overlayAlertMutedByAdmin"; - public static final String key_voipgroup_overlayAlertMutedByAdmin2 = "kvoipgroup_overlayAlertMutedByAdmin2"; - public static final String key_voipgroup_mutedByAdminGradient = "voipgroup_mutedByAdminGradient"; - public static final String key_voipgroup_mutedByAdminGradient2 = "voipgroup_mutedByAdminGradient2"; - public static final String key_voipgroup_mutedByAdminGradient3 = "voipgroup_mutedByAdminGradient3"; - public static final String key_voipgroup_mutedByAdminMuteButton = "voipgroup_mutedByAdminMuteButton"; - public static final String key_voipgroup_mutedByAdminMuteButtonDisabled = "voipgroup_mutedByAdminMuteButtonDisabled"; - public static final String key_voipgroup_windowBackgroundWhiteInputField = "voipgroup_windowBackgroundWhiteInputField"; - public static final String key_voipgroup_windowBackgroundWhiteInputFieldActivated = "voipgroup_windowBackgroundWhiteInputFieldActivated"; - - public static final String key_passport_authorizeBackground = "passport_authorizeBackground"; - public static final String key_passport_authorizeBackgroundSelected = "passport_authorizeBackgroundSelected"; - public static final String key_passport_authorizeText = "passport_authorizeText"; - - public static final String key_profile_creatorIcon = "profile_creatorIcon"; - public static final String key_profile_title = "profile_title"; - public static final String key_profile_actionIcon = "profile_actionIcon"; - public static final String key_profile_actionBackground = "profile_actionBackground"; - public static final String key_profile_actionPressedBackground = "profile_actionPressedBackground"; - public static final String key_profile_verifiedBackground = "profile_verifiedBackground"; - public static final String key_profile_verifiedCheck = "profile_verifiedCheck"; - public static final String key_profile_status = "profile_status"; - - public static final String key_profile_tabText = "profile_tabText"; - public static final String key_profile_tabSelectedText = "profile_tabSelectedText"; - public static final String key_profile_tabSelectedLine = "profile_tabSelectedLine"; - public static final String key_profile_tabSelector = "profile_tabSelector"; - - public static final String key_sharedMedia_startStopLoadIcon = "sharedMedia_startStopLoadIcon"; - public static final String key_sharedMedia_linkPlaceholder = "sharedMedia_linkPlaceholder"; - public static final String key_sharedMedia_linkPlaceholderText = "sharedMedia_linkPlaceholderText"; - public static final String key_sharedMedia_photoPlaceholder = "sharedMedia_photoPlaceholder"; - - public static final String key_featuredStickers_addedIcon = "featuredStickers_addedIcon"; - public static final String key_featuredStickers_buttonProgress = "featuredStickers_buttonProgress"; - public static final String key_featuredStickers_addButton = "featuredStickers_addButton"; - public static final String key_featuredStickers_addButtonPressed = "featuredStickers_addButtonPressed"; - public static final String key_featuredStickers_removeButtonText = "featuredStickers_removeButtonText"; - public static final String key_featuredStickers_buttonText = "featuredStickers_buttonText"; - public static final String key_featuredStickers_unread = "featuredStickers_unread"; - - public static final String key_stickers_menu = "stickers_menu"; - public static final String key_stickers_menuSelector = "stickers_menuSelector"; - - public static final String key_changephoneinfo_image2 = "changephoneinfo_image2"; - - public static final String key_groupcreate_hintText = "groupcreate_hintText"; - public static final String key_groupcreate_cursor = "groupcreate_cursor"; - public static final String key_groupcreate_sectionShadow = "groupcreate_sectionShadow"; - public static final String key_groupcreate_sectionText = "groupcreate_sectionText"; - public static final String key_groupcreate_spanText = "groupcreate_spanText"; - public static final String key_groupcreate_spanBackground = "groupcreate_spanBackground"; - public static final String key_groupcreate_spanDelete = "groupcreate_spanDelete"; - - public static final String key_contacts_inviteBackground = "contacts_inviteBackground"; - public static final String key_contacts_inviteText = "contacts_inviteText"; - - public static final String key_login_progressInner = "login_progressInner"; - public static final String key_login_progressOuter = "login_progressOuter"; - - public static final String key_picker_enabledButton = "picker_enabledButton"; - public static final String key_picker_disabledButton = "picker_disabledButton"; - public static final String key_picker_badge = "picker_badge"; - public static final String key_picker_badgeText = "picker_badgeText"; - - public static final String key_location_sendLocationBackground = "location_sendLocationBackground"; - public static final String key_location_sendLocationIcon = "location_sendLocationIcon"; - public static final String key_location_sendLocationText = "location_sendLocationText"; - public static final String key_location_sendLiveLocationBackground = "location_sendLiveLocationBackground"; - public static final String key_location_sendLiveLocationIcon = "location_sendLiveLocationIcon"; - public static final String key_location_sendLiveLocationText = "location_sendLiveLocationText"; - public static final String key_location_liveLocationProgress = "location_liveLocationProgress"; - public static final String key_location_placeLocationBackground = "location_placeLocationBackground"; - public static final String key_location_actionIcon = "location_actionIcon"; - public static final String key_location_actionActiveIcon = "location_actionActiveIcon"; - public static final String key_location_actionBackground = "location_actionBackground"; - public static final String key_location_actionPressedBackground = "location_actionPressedBackground"; - - public static final String key_dialog_liveLocationProgress = "dialog_liveLocationProgress"; - - public static final String key_files_folderIcon = "files_folderIcon"; - public static final String key_files_folderIconBackground = "files_folderIconBackground"; - public static final String key_files_iconText = "files_iconText"; - - public static final String key_sessions_devicesImage = "sessions_devicesImage"; - - public static final String key_calls_callReceivedGreenIcon = "calls_callReceivedGreenIcon"; - public static final String key_calls_callReceivedRedIcon = "calls_callReceivedRedIcon"; - - public static final String key_undo_background = "undo_background"; - public static final String key_undo_cancelColor = "undo_cancelColor"; - public static final String key_undo_infoColor = "undo_infoColor"; - - public static final String key_sheet_scrollUp = "key_sheet_scrollUp"; - public static final String key_sheet_other = "key_sheet_other"; + public static int colorsCount; + public static final int key_wallpaperFileOffset = colorsCount++; + public static final int key_dialogBackground = colorsCount++; + public static final int key_dialogBackgroundGray = colorsCount++; + public static final int key_dialogTextBlack = colorsCount++; + public static final int key_dialogTextLink = colorsCount++; + public static final int key_dialogLinkSelection = colorsCount++; + public static final int key_dialogTextRed = colorsCount++; + public static final int key_dialogTextBlue = colorsCount++; + public static final int key_dialogTextBlue2 = colorsCount++; + public static final int key_dialogTextBlue4 = colorsCount++; + public static final int key_dialogTextGray = colorsCount++; + public static final int key_dialogTextGray2 = colorsCount++; + public static final int key_dialogTextGray3 = colorsCount++; + public static final int key_dialogTextGray4 = colorsCount++; + public static final int key_dialogTextHint = colorsCount++; + public static final int key_dialogInputField = colorsCount++; + public static final int key_dialogInputFieldActivated = colorsCount++; + public static final int key_dialogCheckboxSquareBackground = colorsCount++; + public static final int key_dialogCheckboxSquareCheck = colorsCount++; + public static final int key_dialogCheckboxSquareUnchecked = colorsCount++; + public static final int key_dialogCheckboxSquareDisabled = colorsCount++; + public static final int key_dialogScrollGlow = colorsCount++; + public static final int key_dialogRoundCheckBox = colorsCount++; + public static final int key_dialogRoundCheckBoxCheck = colorsCount++; + public static final int key_dialogRadioBackground = colorsCount++; + public static final int key_dialogRadioBackgroundChecked = colorsCount++; + public static final int key_dialogLineProgress = colorsCount++; + public static final int key_dialogLineProgressBackground = colorsCount++; + public static final int key_dialogButton = colorsCount++; + public static final int key_dialogButtonSelector = colorsCount++; + public static final int key_dialogIcon = colorsCount++; + public static final int key_dialogRedIcon = colorsCount++; + public static final int key_dialogGrayLine = colorsCount++; + public static final int key_dialogTopBackground = colorsCount++; + public static final int key_dialogCameraIcon = colorsCount++; + public static final int key_dialog_inlineProgressBackground = colorsCount++; + public static final int key_dialog_inlineProgress = colorsCount++; + public static final int key_dialogSearchBackground = colorsCount++; + public static final int key_dialogSearchHint = colorsCount++; + public static final int key_dialogSearchIcon = colorsCount++; + public static final int key_dialogSearchText = colorsCount++; + public static final int key_dialogFloatingButton = colorsCount++; + public static final int key_dialogFloatingButtonPressed = colorsCount++; + public static final int key_dialogFloatingIcon = colorsCount++; + public static final int key_dialogShadowLine = colorsCount++; + public static final int key_dialogEmptyImage = colorsCount++; + public static final int key_dialogEmptyText = colorsCount++; + public static final int key_dialogSwipeRemove = colorsCount++; + public static final int key_dialogReactionMentionBackground = colorsCount++; + + public static final int key_windowBackgroundWhite = colorsCount++; + public static final int key_windowBackgroundUnchecked = colorsCount++; + public static final int key_windowBackgroundChecked = colorsCount++; + public static final int key_windowBackgroundCheckText = colorsCount++; + public static final int key_progressCircle = colorsCount++; + public static final int key_listSelector = colorsCount++; + public static final int key_windowBackgroundWhiteInputField = colorsCount++; + public static final int key_windowBackgroundWhiteInputFieldActivated = colorsCount++; + public static final int key_windowBackgroundWhiteGrayIcon = colorsCount++; + public static final int key_windowBackgroundWhiteBlueText = colorsCount++; + public static final int key_windowBackgroundWhiteBlueText2 = colorsCount++; + public static final int key_windowBackgroundWhiteBlueText3 = colorsCount++; + public static final int key_windowBackgroundWhiteBlueText4 = colorsCount++; + public static final int key_windowBackgroundWhiteBlueText5 = colorsCount++; + public static final int key_windowBackgroundWhiteBlueText6 = colorsCount++; + public static final int key_windowBackgroundWhiteBlueText7 = colorsCount++; + public static final int key_windowBackgroundWhiteBlueButton = colorsCount++; + public static final int key_windowBackgroundWhiteBlueIcon = colorsCount++; + public static final int key_windowBackgroundWhiteGreenText = colorsCount++; + public static final int key_windowBackgroundWhiteGreenText2 = colorsCount++; + public static final int key_windowBackgroundWhiteRedText3 = colorsCount++; + public static final int key_windowBackgroundWhiteRedText4 = colorsCount++; + public static final int key_windowBackgroundWhiteGrayText = colorsCount++; + public static final int key_windowBackgroundWhiteGrayText2 = colorsCount++; + public static final int key_windowBackgroundWhiteGrayText3 = colorsCount++; + public static final int key_windowBackgroundWhiteGrayText4 = colorsCount++; + public static final int key_windowBackgroundWhiteGrayText5 = colorsCount++; + public static final int key_windowBackgroundWhiteGrayText6 = colorsCount++; + public static final int key_windowBackgroundWhiteGrayText7 = colorsCount++; + public static final int key_windowBackgroundWhiteGrayText8 = colorsCount++; + public static final int key_windowBackgroundWhiteBlackText = colorsCount++; + public static final int key_windowBackgroundWhiteHintText = colorsCount++; + public static final int key_windowBackgroundWhiteValueText = colorsCount++; + public static final int key_windowBackgroundWhiteLinkText = colorsCount++; + public static final int key_windowBackgroundWhiteLinkSelection = colorsCount++; + public static final int key_windowBackgroundWhiteBlueHeader = colorsCount++; + public static final int key_switchTrack = colorsCount++; + public static final int key_switchTrackChecked = colorsCount++; + public static final int key_switchTrackBlue = colorsCount++; + public static final int key_switchTrackBlueChecked = colorsCount++; + public static final int key_switchTrackBlueThumb = colorsCount++; + public static final int key_switchTrackBlueThumbChecked = colorsCount++; + public static final int key_switchTrackBlueSelector = colorsCount++; + public static final int key_switchTrackBlueSelectorChecked = colorsCount++; + public static final int key_switch2Track = colorsCount++; + public static final int key_switch2TrackChecked = colorsCount++; + public static final int key_checkboxSquareBackground = colorsCount++; + public static final int key_checkboxSquareCheck = colorsCount++; + public static final int key_checkboxSquareUnchecked = colorsCount++; + public static final int key_checkboxSquareDisabled = colorsCount++; + public static final int key_windowBackgroundGray = colorsCount++; + public static final int key_windowBackgroundGrayShadow = colorsCount++; + public static final int key_emptyListPlaceholder = colorsCount++; + public static final int key_divider = colorsCount++; + public static final int key_graySection = colorsCount++; + public static final int key_graySectionText = colorsCount++; + public static final int key_radioBackground = colorsCount++; + public static final int key_radioBackgroundChecked = colorsCount++; + public static final int key_checkbox = colorsCount++; + public static final int key_checkboxDisabled = colorsCount++; + public static final int key_checkboxCheck = colorsCount++; + public static final int key_fastScrollActive = colorsCount++; + public static final int key_fastScrollInactive = colorsCount++; + public static final int key_fastScrollText = colorsCount++; + + public static final int key_text_RedRegular = colorsCount++; + public static final int key_text_RedBold = colorsCount++; + public static final int key_fill_RedNormal = colorsCount++; + public static final int key_fill_RedDark = colorsCount++; + + public static final int key_inappPlayerPerformer = colorsCount++; + public static final int key_inappPlayerTitle = colorsCount++; + public static final int key_inappPlayerBackground = colorsCount++; + public static final int key_inappPlayerPlayPause = colorsCount++; + public static final int key_inappPlayerClose = colorsCount++; + + public static final int key_returnToCallBackground = colorsCount++; + public static final int key_returnToCallMutedBackground = colorsCount++; + public static final int key_returnToCallText = colorsCount++; + + public static final int key_contextProgressInner1 = colorsCount++; + public static final int key_contextProgressOuter1 = colorsCount++; + public static final int key_contextProgressInner2 = colorsCount++; + public static final int key_contextProgressOuter2 = colorsCount++; + public static final int key_contextProgressInner3 = colorsCount++; + public static final int key_contextProgressOuter3 = colorsCount++; + public static final int key_contextProgressInner4 = colorsCount++; + public static final int key_contextProgressOuter4 = colorsCount++; + + public static final int key_avatar_text = colorsCount++; + public static final int key_avatar_backgroundSaved = colorsCount++; + public static final int key_avatar_background2Saved = colorsCount++; + public static final int key_avatar_backgroundArchived = colorsCount++; + public static final int key_avatar_backgroundArchivedHidden = colorsCount++; + public static final int key_avatar_backgroundRed = colorsCount++; + public static final int key_avatar_backgroundOrange = colorsCount++; + public static final int key_avatar_backgroundViolet = colorsCount++; + public static final int key_avatar_backgroundGreen = colorsCount++; + public static final int key_avatar_backgroundCyan = colorsCount++; + public static final int key_avatar_backgroundBlue = colorsCount++; + public static final int key_avatar_backgroundPink = colorsCount++; + public static final int key_avatar_background2Red = colorsCount++; + public static final int key_avatar_background2Orange = colorsCount++; + public static final int key_avatar_background2Violet = colorsCount++; + public static final int key_avatar_background2Green = colorsCount++; + public static final int key_avatar_background2Cyan = colorsCount++; + public static final int key_avatar_background2Blue = colorsCount++; + public static final int key_avatar_background2Pink = colorsCount++; + + public static final int key_avatar_backgroundInProfileBlue = colorsCount++; + public static final int key_avatar_backgroundActionBarBlue = colorsCount++; + public static final int key_avatar_actionBarSelectorBlue = colorsCount++; + public static final int key_avatar_actionBarIconBlue = colorsCount++; + public static final int key_avatar_subtitleInProfileBlue = colorsCount++; + + public static final int key_avatar_nameInMessageRed = colorsCount++; + public static final int key_avatar_nameInMessageOrange = colorsCount++; + public static final int key_avatar_nameInMessageViolet = colorsCount++; + public static final int key_avatar_nameInMessageGreen = colorsCount++; + public static final int key_avatar_nameInMessageCyan = colorsCount++; + public static final int key_avatar_nameInMessageBlue = colorsCount++; + public static final int key_avatar_nameInMessagePink = colorsCount++; + + public static int[] keys_avatar_background = {key_avatar_backgroundRed, key_avatar_backgroundOrange, key_avatar_backgroundViolet, key_avatar_backgroundGreen, key_avatar_backgroundCyan, key_avatar_backgroundBlue, key_avatar_backgroundPink}; + public static int[] keys_avatar_background2 = {key_avatar_background2Red, key_avatar_background2Orange, key_avatar_background2Violet, key_avatar_background2Green, key_avatar_background2Cyan, key_avatar_background2Blue, key_avatar_background2Pink}; + public static int[] keys_avatar_nameInMessage = {key_avatar_nameInMessageRed, key_avatar_nameInMessageOrange, key_avatar_nameInMessageViolet, key_avatar_nameInMessageGreen, key_avatar_nameInMessageCyan, key_avatar_nameInMessageBlue, key_avatar_nameInMessagePink}; + + public static final int key_actionBarDefault = colorsCount++; + public static final int key_actionBarDefaultSelector = colorsCount++; + public static final int key_actionBarWhiteSelector = colorsCount++; + public static final int key_actionBarDefaultIcon = colorsCount++; + public static final int key_actionBarActionModeDefault = colorsCount++; + public static final int key_actionBarActionModeDefaultTop = colorsCount++; + public static final int key_actionBarActionModeDefaultIcon = colorsCount++; + public static final int key_actionBarActionModeDefaultSelector = colorsCount++; + public static final int key_actionBarDefaultTitle = colorsCount++; + public static final int key_actionBarDefaultSubtitle = colorsCount++; + public static final int key_actionBarDefaultSearch = colorsCount++; + public static final int key_actionBarDefaultSearchPlaceholder = colorsCount++; + public static final int key_actionBarDefaultSubmenuItem = colorsCount++; + public static final int key_actionBarDefaultSubmenuItemIcon = colorsCount++; + public static final int key_actionBarDefaultSubmenuBackground = colorsCount++; + public static final int key_actionBarDefaultSubmenuSeparator = colorsCount++; + public static final int key_actionBarTabActiveText = colorsCount++; + public static final int key_actionBarTabUnactiveText = colorsCount++; + public static final int key_actionBarTabLine = colorsCount++; + public static final int key_actionBarTabSelector = colorsCount++; + public static final int key_actionBarDefaultArchived = colorsCount++; + public static final int key_actionBarDefaultArchivedSelector = colorsCount++; + public static final int key_actionBarDefaultArchivedIcon = colorsCount++; + public static final int key_actionBarDefaultArchivedTitle = colorsCount++; + public static final int key_actionBarDefaultArchivedSearch = colorsCount++; + public static final int key_actionBarDefaultArchivedSearchPlaceholder = colorsCount++; + + public static final int key_actionBarBrowser = colorsCount++; + + public static final int key_chats_onlineCircle = colorsCount++; + public static final int key_chats_unreadCounter = colorsCount++; + public static final int key_chats_unreadCounterMuted = colorsCount++; + public static final int key_chats_unreadCounterText = colorsCount++; + public static final int key_chats_name = colorsCount++; + public static final int key_chats_nameArchived = colorsCount++; + public static final int key_chats_secretName = colorsCount++; + public static final int key_chats_secretIcon = colorsCount++; + public static final int key_chats_pinnedIcon = colorsCount++; + public static final int key_chats_archiveBackground = colorsCount++; + public static final int key_chats_archivePinBackground = colorsCount++; + public static final int key_chats_archiveIcon = colorsCount++; + public static final int key_chats_archiveText = colorsCount++; + public static final int key_chats_message = colorsCount++; + public static final int key_chats_messageArchived = colorsCount++; + public static final int key_chats_message_threeLines = colorsCount++; + public static final int key_chats_draft = colorsCount++; + public static final int key_chats_nameMessage = colorsCount++; + public static final int key_chats_nameMessageArchived = colorsCount++; + public static final int key_chats_nameMessage_threeLines = colorsCount++; + public static final int key_chats_nameMessageArchived_threeLines = colorsCount++; + public static final int key_chats_attachMessage = colorsCount++; + public static final int key_chats_actionMessage = colorsCount++; + public static final int key_chats_date = colorsCount++; + public static final int key_chats_pinnedOverlay = colorsCount++; + public static final int key_chats_tabletSelectedOverlay = colorsCount++; + public static final int key_chats_sentCheck = colorsCount++; + public static final int key_chats_sentReadCheck = colorsCount++; + public static final int key_chats_sentClock = colorsCount++; + public static final int key_chats_sentError = colorsCount++; + public static final int key_chats_sentErrorIcon = colorsCount++; + public static final int key_chats_verifiedBackground = colorsCount++; + public static final int key_chats_verifiedCheck = colorsCount++; + public static final int key_chats_muteIcon = colorsCount++; + public static final int key_chats_mentionIcon = colorsCount++; + public static final int key_chats_menuTopShadow = colorsCount++; + public static final int key_chats_menuTopShadowCats = colorsCount++; + public static final int key_chats_menuBackground = colorsCount++; + public static final int key_chats_menuItemText = colorsCount++; + public static final int key_chats_menuItemCheck = colorsCount++; + public static final int key_chats_menuItemIcon = colorsCount++; + public static final int key_chats_menuName = colorsCount++; + public static final int key_chats_menuPhone = colorsCount++; + public static final int key_chats_menuPhoneCats = colorsCount++; + public static final int key_chats_menuTopBackgroundCats = colorsCount++; + public static final int key_chats_menuTopBackground = colorsCount++; + public static final int key_chats_actionIcon = colorsCount++; + public static final int key_chats_actionBackground = colorsCount++; + public static final int key_chats_actionPressedBackground = colorsCount++; + public static final int key_chats_archivePullDownBackground = colorsCount++; + public static final int key_chats_archivePullDownBackgroundActive = colorsCount++; + public static final int key_chats_tabUnreadActiveBackground = colorsCount++; + public static final int key_chats_tabUnreadUnactiveBackground = colorsCount++; + + public static final int key_chat_attachCheckBoxCheck = colorsCount++; + public static final int key_chat_attachCheckBoxBackground = colorsCount++; + public static final int key_chat_attachPhotoBackground = colorsCount++; + public static final int key_chat_attachActiveTab = colorsCount++; + public static final int key_chat_attachUnactiveTab = colorsCount++; + public static final int key_chat_attachPermissionImage = colorsCount++; + public static final int key_chat_attachPermissionMark = colorsCount++; + public static final int key_chat_attachPermissionText = colorsCount++; + public static final int key_chat_attachEmptyImage = colorsCount++; + + public static final int key_chat_inPollCorrectAnswer = colorsCount++; + public static final int key_chat_outPollCorrectAnswer = colorsCount++; + public static final int key_chat_inPollWrongAnswer = colorsCount++; + public static final int key_chat_outPollWrongAnswer = colorsCount++; + + public static final int key_chat_attachIcon = colorsCount++; + public static final int key_chat_attachGalleryBackground = colorsCount++; + public static final int key_chat_attachGalleryText = colorsCount++; + public static final int key_chat_attachAudioBackground = colorsCount++; + public static final int key_chat_attachAudioText = colorsCount++; + public static final int key_chat_attachFileBackground = colorsCount++; + public static final int key_chat_attachFileText = colorsCount++; + public static final int key_chat_attachContactBackground = colorsCount++; + public static final int key_chat_attachContactText = colorsCount++; + public static final int key_chat_attachLocationBackground = colorsCount++; + public static final int key_chat_attachLocationText = colorsCount++; + public static final int key_chat_attachPollBackground = colorsCount++; + public static final int key_chat_attachPollText = colorsCount++; + + public static final int key_chat_status = colorsCount++; + public static final int key_chat_inGreenCall = colorsCount++; + public static final int key_chat_inBubble = colorsCount++; + public static final int key_chat_inBubbleSelectedOverlay = colorsCount++; + public static final int key_chat_inBubbleShadow = colorsCount++; + + //my messages bubbles + public static final int myMessagesBubblesStartIndex = colorsCount; + public static final int key_chat_outBubble = colorsCount++; + public static final int key_chat_outBubbleSelected = colorsCount++; + public static final int key_chat_outBubbleShadow = colorsCount++; + public static final int key_chat_outBubbleGradient1 = colorsCount++; + public static final int key_chat_outBubbleGradient2 = colorsCount++; + public static final int key_chat_outBubbleGradient3 = colorsCount++; + public static final int myMessagesBubblesEndIndex = colorsCount; + + //my messages + public static final int myMessagesStartIndex = colorsCount; + public static final int key_chat_outGreenCall = colorsCount++; + public static final int key_chat_outSentCheck = colorsCount++; + public static final int key_chat_outSentCheckSelected = colorsCount++; + public static final int key_chat_outSentCheckRead = colorsCount++; + public static final int key_chat_outSentCheckReadSelected = colorsCount++; + public static final int key_chat_outSentClock = colorsCount++; + public static final int key_chat_outSentClockSelected = colorsCount++; + public static final int key_chat_outMediaIcon = colorsCount++; + public static final int key_chat_outMediaIconSelected = colorsCount++; + public static final int key_chat_outViews = colorsCount++; + public static final int key_chat_outViewsSelected = colorsCount++; + public static final int key_chat_outMenu = colorsCount++; + public static final int key_chat_outMenuSelected = colorsCount++; + public static final int key_chat_outInstant = colorsCount++; + public static final int key_chat_outInstantSelected = colorsCount++; + public static final int key_chat_outPreviewInstantText = colorsCount++; + public static final int key_chat_outForwardedNameText = colorsCount++; + public static final int key_chat_outViaBotNameText = colorsCount++; + public static final int key_chat_outReplyLine = colorsCount++; + public static final int key_chat_outReplyNameText = colorsCount++; + public static final int key_chat_outReplyMessageText = colorsCount++; + public static final int key_chat_outReplyMediaMessageText = colorsCount++; + public static final int key_chat_outReplyMediaMessageSelectedText = colorsCount++; + public static final int key_chat_outPreviewLine = colorsCount++; + public static final int key_chat_outSiteNameText = colorsCount++; + public static final int key_chat_outContactNameText = colorsCount++; + public static final int key_chat_outContactPhoneText = colorsCount++; + public static final int key_chat_outContactPhoneSelectedText = colorsCount++; + public static final int key_chat_outAudioPerformerText = colorsCount++; + public static final int key_chat_outAudioPerformerSelectedText = colorsCount++; + public static final int key_chat_outTimeSelectedText = colorsCount++; + public static final int key_chat_outAdminText = colorsCount++; + public static final int key_chat_outAdminSelectedText = colorsCount++; + public static final int key_chat_outAudioProgress = colorsCount++; + public static final int key_chat_outAudioSelectedProgress = colorsCount++; + public static final int key_chat_outTimeText = colorsCount++; + public static final int key_chat_outAudioTitleText = colorsCount++; + public static final int key_chat_outAudioDurationText = colorsCount++; + public static final int key_chat_outAudioDurationSelectedText = colorsCount++; + public static final int key_chat_outAudioSeekbar = colorsCount++; + public static final int key_chat_outAudioCacheSeekbar = colorsCount++; + public static final int key_chat_outAudioSeekbarSelected = colorsCount++; + public static final int key_chat_outAudioSeekbarFill = colorsCount++; + public static final int key_chat_outVoiceSeekbar = colorsCount++; + public static final int key_chat_outVoiceSeekbarSelected = colorsCount++; + public static final int key_chat_outVoiceSeekbarFill = colorsCount++; + public static final int key_chat_outFileProgress = colorsCount++; + public static final int key_chat_outFileProgressSelected = colorsCount++; + public static final int key_chat_outFileNameText = colorsCount++; + public static final int key_chat_outFileInfoText = colorsCount++; + public static final int key_chat_outFileInfoSelectedText = colorsCount++; + public static final int key_chat_outFileBackground = colorsCount++; + public static final int key_chat_outFileBackgroundSelected = colorsCount++; + public static final int key_chat_outVenueInfoText = colorsCount++; + public static final int key_chat_outVenueInfoSelectedText = colorsCount++; + public static final int key_chat_outLinkSelectBackground = colorsCount++; + public static final int key_chat_outLoader = colorsCount++; + public static final int key_chat_outLoaderSelected = colorsCount++; + public static final int key_chat_outLocationIcon = colorsCount++; + public static final int key_chat_outContactBackground = colorsCount++; + public static final int key_chat_outContactIcon = colorsCount++; + public static final int myMessagesEndIndex = colorsCount; + + public static final int key_chat_outTextSelectionHighlight = colorsCount++; + public static final int key_chat_outTextSelectionCursor = colorsCount++; + public static final int key_chat_outBubbleLocationPlaceholder = colorsCount++; + public static final int key_chat_outBubbleSelectedOverlay = colorsCount++; + public static final int key_chat_outPsaNameText = colorsCount++; + public static final int key_chat_outBubbleGradientAnimated = colorsCount++; + public static final int key_chat_outBubbleGradientSelectedOverlay = colorsCount++; + public static final int key_chat_inBubbleSelected = colorsCount++; + public static final int key_chat_messageTextIn = colorsCount++; + public static final int key_chat_messageTextOut = colorsCount++; + public static final int key_chat_messageLinkIn = colorsCount++; + public static final int key_chat_messageLinkOut = colorsCount++; + public static final int key_chat_serviceText = colorsCount++; + public static final int key_chat_serviceLink = colorsCount++; + public static final int key_chat_serviceIcon = colorsCount++; + public static final int key_chat_serviceBackground = colorsCount++; + public static final int key_chat_serviceBackgroundSelected = colorsCount++; + public static final int key_chat_serviceBackgroundSelector = colorsCount++; + public static final int key_chat_muteIcon = colorsCount++; + public static final int key_chat_lockIcon = colorsCount++; + public static final int key_chat_inSentClock = colorsCount++; + public static final int key_chat_inSentClockSelected = colorsCount++; + public static final int key_chat_mediaSentCheck = colorsCount++; + public static final int key_chat_mediaSentClock = colorsCount++; + public static final int key_chat_inMediaIcon = colorsCount++; + public static final int key_chat_inMediaIconSelected = colorsCount++; + + public static final int key_chat_mediaTimeBackground = colorsCount++; + public static final int key_chat_inViews = colorsCount++; + public static final int key_chat_inViewsSelected = colorsCount++; + public static final int key_chat_mediaViews = colorsCount++; + public static final int key_chat_inMenu = colorsCount++; + public static final int key_chat_inMenuSelected = colorsCount++; + public static final int key_chat_mediaMenu = colorsCount++; + public static final int key_chat_inInstant = colorsCount++; + public static final int key_chat_inInstantSelected = colorsCount++; + public static final int key_chat_sentError = colorsCount++; + public static final int key_chat_sentErrorIcon = colorsCount++; + public static final int key_chat_selectedBackground = colorsCount++; + public static final int key_chat_previewDurationText = colorsCount++; + public static final int key_chat_previewGameText = colorsCount++; + public static final int key_chat_inPreviewInstantText = colorsCount++; + public static final int key_chat_secretTimeText = colorsCount++; + public static final int key_chat_stickerNameText = colorsCount++; + public static final int key_chat_botButtonText = colorsCount++; + public static final int key_chat_inForwardedNameText = colorsCount++; + public static final int key_chat_inPsaNameText = colorsCount++; + public static final int key_chat_inViaBotNameText = colorsCount++; + public static final int key_chat_stickerViaBotNameText = colorsCount++; + public static final int key_chat_inReplyLine = colorsCount++; + public static final int key_chat_stickerReplyLine = colorsCount++; + public static final int key_chat_inReplyNameText = colorsCount++; + public static final int key_chat_stickerReplyNameText = colorsCount++; + public static final int key_chat_inReplyMessageText = colorsCount++; + public static final int key_chat_inReplyMediaMessageText = colorsCount++; + public static final int key_chat_inReplyMediaMessageSelectedText = colorsCount++; + public static final int key_chat_stickerReplyMessageText = colorsCount++; + public static final int key_chat_inPreviewLine = colorsCount++; + public static final int key_chat_inSiteNameText = colorsCount++; + public static final int key_chat_inContactNameText = colorsCount++; + public static final int key_chat_inContactPhoneText = colorsCount++; + public static final int key_chat_inContactPhoneSelectedText = colorsCount++; + public static final int key_chat_mediaProgress = colorsCount++; + public static final int key_chat_inAudioProgress = colorsCount++; + public static final int key_chat_inAudioSelectedProgress = colorsCount++; + public static final int key_chat_mediaTimeText = colorsCount++; + public static final int key_chat_inAdminText = colorsCount++; + public static final int key_chat_inAdminSelectedText = colorsCount++; + public static final int key_chat_inTimeText = colorsCount++; + public static final int key_chat_inTimeSelectedText = colorsCount++; + public static final int key_chat_inAudioPerformerText = colorsCount++; + public static final int key_chat_inAudioPerformerSelectedText = colorsCount++; + public static final int key_chat_inAudioTitleText = colorsCount++; + public static final int key_chat_inAudioDurationText = colorsCount++; + public static final int key_chat_inAudioDurationSelectedText = colorsCount++; + public static final int key_chat_inAudioSeekbar = colorsCount++; + public static final int key_chat_inAudioCacheSeekbar = colorsCount++; + public static final int key_chat_inAudioSeekbarSelected = colorsCount++; + public static final int key_chat_inAudioSeekbarFill = colorsCount++; + public static final int key_chat_inVoiceSeekbar = colorsCount++; + public static final int key_chat_inVoiceSeekbarSelected = colorsCount++; + public static final int key_chat_inVoiceSeekbarFill = colorsCount++; + public static final int key_chat_inFileProgress = colorsCount++; + public static final int key_chat_inFileProgressSelected = colorsCount++; + public static final int key_chat_inFileNameText = colorsCount++; + public static final int key_chat_inFileInfoText = colorsCount++; + public static final int key_chat_inFileInfoSelectedText = colorsCount++; + public static final int key_chat_inFileBackground = colorsCount++; + public static final int key_chat_inFileBackgroundSelected = colorsCount++; + public static final int key_chat_inVenueInfoText = colorsCount++; + public static final int key_chat_inVenueInfoSelectedText = colorsCount++; + public static final int key_chat_mediaInfoText = colorsCount++; + public static final int key_chat_linkSelectBackground = colorsCount++; + public static final int key_chat_textSelectBackground = colorsCount++; + public static final int key_chat_wallpaper = colorsCount++; + public static final int key_chat_wallpaper_gradient_to1 = colorsCount++; + public static final int key_chat_wallpaper_gradient_to2 = colorsCount++; + public static final int key_chat_wallpaper_gradient_to3 = colorsCount++; + public static final int key_chat_wallpaper_gradient_rotation = colorsCount++; + public static final int key_chat_messagePanelBackground = colorsCount++; + public static final int key_chat_messagePanelShadow = colorsCount++; + public static final int key_chat_messagePanelText = colorsCount++; + public static final int key_chat_messagePanelHint = colorsCount++; + public static final int key_chat_messagePanelCursor = colorsCount++; + public static final int key_chat_messagePanelIcons = colorsCount++; + public static final int key_chat_messagePanelSend = colorsCount++; + public static final int key_chat_messagePanelVoiceLock = colorsCount++; + public static final int key_chat_messagePanelVoiceLockBackground = colorsCount++; + public static final int key_chat_messagePanelVoiceLockShadow = colorsCount++; + public static final int key_chat_topPanelBackground = colorsCount++; + public static final int key_chat_topPanelClose = colorsCount++; + public static final int key_chat_topPanelLine = colorsCount++; + public static final int key_chat_topPanelTitle = colorsCount++; + public static final int key_chat_topPanelMessage = colorsCount++; + public static final int key_chat_addContact = colorsCount++; + public static final int key_chat_inLoader = colorsCount++; + public static final int key_chat_inLoaderSelected = colorsCount++; + public static final int key_chat_inLoaderPhoto = colorsCount++; + public static final int key_chat_mediaLoaderPhoto = colorsCount++; + public static final int key_chat_mediaLoaderPhotoSelected = colorsCount++; + public static final int key_chat_mediaLoaderPhotoIcon = colorsCount++; + public static final int key_chat_mediaLoaderPhotoIconSelected = colorsCount++; + public static final int key_chat_inLocationBackground = colorsCount++; + public static final int key_chat_inLocationIcon = colorsCount++; + public static final int key_chat_inContactBackground = colorsCount++; + public static final int key_chat_inContactIcon = colorsCount++; + public static final int key_chat_replyPanelIcons = colorsCount++; + public static final int key_chat_replyPanelClose = colorsCount++; + public static final int key_chat_replyPanelName = colorsCount++; + public static final int key_chat_replyPanelLine = colorsCount++; + public static final int key_chat_searchPanelIcons = colorsCount++; + public static final int key_chat_searchPanelText = colorsCount++; + public static final int key_chat_secretChatStatusText = colorsCount++; + public static final int key_chat_fieldOverlayText = colorsCount++; + public static final int key_chat_stickersHintPanel = colorsCount++; + public static final int key_chat_botSwitchToInlineText = colorsCount++; + public static final int key_chat_unreadMessagesStartArrowIcon = colorsCount++; + public static final int key_chat_unreadMessagesStartText = colorsCount++; + public static final int key_chat_unreadMessagesStartBackground = colorsCount++; + public static final int key_chat_inlineResultIcon = colorsCount++; + public static final int key_chat_emojiPanelBackground = colorsCount++; + public static final int key_chat_emojiSearchBackground = colorsCount++; + public static final int key_chat_emojiSearchIcon = colorsCount++; + public static final int key_chat_emojiPanelShadowLine = colorsCount++; + public static final int key_chat_emojiPanelEmptyText = colorsCount++; + public static final int key_chat_emojiPanelIcon = colorsCount++; + public static final int key_chat_emojiBottomPanelIcon = colorsCount++; + public static final int key_chat_emojiPanelIconSelected = colorsCount++; + public static final int key_chat_emojiPanelStickerPackSelector = colorsCount++; + public static final int key_chat_emojiPanelStickerPackSelectorLine = colorsCount++; + public static final int key_chat_emojiPanelBackspace = colorsCount++; + public static final int key_chat_emojiPanelTrendingTitle = colorsCount++; + public static final int key_chat_emojiPanelStickerSetName = colorsCount++; + public static final int key_chat_emojiPanelStickerSetNameHighlight = colorsCount++; + public static final int key_chat_emojiPanelStickerSetNameIcon = colorsCount++; + public static final int key_chat_emojiPanelTrendingDescription = colorsCount++; + public static final int key_chat_botKeyboardButtonText = colorsCount++; + public static final int key_chat_botKeyboardButtonBackground = colorsCount++; + public static final int key_chat_botKeyboardButtonBackgroundPressed = colorsCount++; + public static final int key_chat_emojiPanelNewTrending = colorsCount++; + public static final int key_chat_messagePanelVoicePressed = colorsCount++; + public static final int key_chat_messagePanelVoiceBackground = colorsCount++; + public static final int key_chat_messagePanelVoiceDelete = colorsCount++; + public static final int key_chat_messagePanelVoiceDuration = colorsCount++; + public static final int key_chat_recordedVoicePlayPause = colorsCount++; + public static final int key_chat_recordedVoiceProgress = colorsCount++; + public static final int key_chat_recordedVoiceProgressInner = colorsCount++; + public static final int key_chat_recordedVoiceDot = colorsCount++; + public static final int key_chat_recordedVoiceBackground = colorsCount++; + public static final int key_chat_recordVoiceCancel = colorsCount++; + public static final int key_chat_recordTime = colorsCount++; + public static final int key_chat_messagePanelCancelInlineBot = colorsCount++; + public static final int key_chat_gifSaveHintText = colorsCount++; + public static final int key_chat_gifSaveHintBackground = colorsCount++; + public static final int key_chat_goDownButton = colorsCount++; + public static final int key_chat_goDownButtonIcon = colorsCount++; + public static final int key_chat_goDownButtonCounter = colorsCount++; + public static final int key_chat_goDownButtonCounterBackground = colorsCount++; + public static final int key_chat_inTextSelectionHighlight = colorsCount++; + public static final int key_chat_TextSelectionCursor = colorsCount++; + public static final int key_chat_inBubbleLocationPlaceholder = colorsCount++; + public static final int key_chat_BlurAlpha = colorsCount++; + + public static final int key_voipgroup_listSelector = colorsCount++; + public static final int key_voipgroup_inviteMembersBackground = colorsCount++; + public static final int key_voipgroup_actionBar = colorsCount++; + public static final int key_voipgroup_actionBarItems = colorsCount++; + public static final int key_voipgroup_actionBarItemsSelector = colorsCount++; + public static final int key_voipgroup_actionBarUnscrolled = colorsCount++; + public static final int key_voipgroup_listViewBackgroundUnscrolled = colorsCount++; + public static final int key_voipgroup_lastSeenTextUnscrolled = colorsCount++; + public static final int key_voipgroup_mutedIconUnscrolled = colorsCount++; + public static final int key_voipgroup_nameText = colorsCount++; + public static final int key_voipgroup_lastSeenText = colorsCount++; + public static final int key_voipgroup_listeningText = colorsCount++; + public static final int key_voipgroup_speakingText = colorsCount++; + public static final int key_voipgroup_mutedIcon = colorsCount++; + public static final int key_voipgroup_mutedByAdminIcon = colorsCount++; + public static final int key_voipgroup_listViewBackground = colorsCount++; + public static final int key_voipgroup_dialogBackground = colorsCount++; + public static final int key_voipgroup_leaveCallMenu = colorsCount++; + public static final int key_voipgroup_checkMenu = colorsCount++; + public static final int key_voipgroup_soundButton = colorsCount++; + public static final int key_voipgroup_soundButtonActive = colorsCount++; + public static final int key_voipgroup_soundButtonActiveScrolled = colorsCount++; + public static final int key_voipgroup_soundButton2 = colorsCount++; + public static final int key_voipgroup_soundButtonActive2 = colorsCount++; + public static final int key_voipgroup_soundButtonActive2Scrolled = colorsCount++; + public static final int key_voipgroup_leaveButton = colorsCount++; + public static final int key_voipgroup_leaveButtonScrolled = colorsCount++; + public static final int key_voipgroup_muteButton = colorsCount++; + public static final int key_voipgroup_muteButton2 = colorsCount++; + public static final int key_voipgroup_muteButton3 = colorsCount++; + public static final int key_voipgroup_unmuteButton = colorsCount++; + public static final int key_voipgroup_unmuteButton2 = colorsCount++; + public static final int key_voipgroup_disabledButton = colorsCount++; + public static final int key_voipgroup_disabledButtonActive = colorsCount++; + public static final int key_voipgroup_disabledButtonActiveScrolled = colorsCount++; + public static final int key_voipgroup_connectingProgress = colorsCount++; + public static final int key_voipgroup_scrollUp = colorsCount++; + public static final int key_voipgroup_searchPlaceholder = colorsCount++; + public static final int key_voipgroup_searchBackground = colorsCount++; + public static final int key_voipgroup_searchText = colorsCount++; + public static final int key_voipgroup_overlayGreen1 = colorsCount++; + public static final int key_voipgroup_overlayGreen2 = colorsCount++; + public static final int key_voipgroup_overlayBlue1 = colorsCount++; + public static final int key_voipgroup_overlayBlue2 = colorsCount++; + public static final int key_voipgroup_topPanelGreen1 = colorsCount++; + public static final int key_voipgroup_topPanelGreen2 = colorsCount++; + public static final int key_voipgroup_topPanelBlue1 = colorsCount++; + public static final int key_voipgroup_topPanelBlue2 = colorsCount++; + public static final int key_voipgroup_topPanelGray = colorsCount++; + public static final int key_voipgroup_overlayAlertGradientMuted = colorsCount++; + public static final int key_voipgroup_overlayAlertGradientMuted2 = colorsCount++; + public static final int key_voipgroup_overlayAlertGradientUnmuted = colorsCount++; + public static final int key_voipgroup_overlayAlertGradientUnmuted2 = colorsCount++; + public static final int key_voipgroup_overlayAlertMutedByAdmin = colorsCount++; + public static final int key_voipgroup_overlayAlertMutedByAdmin2 = colorsCount++; + public static final int key_voipgroup_mutedByAdminGradient = colorsCount++; + public static final int key_voipgroup_mutedByAdminGradient2 = colorsCount++; + public static final int key_voipgroup_mutedByAdminGradient3 = colorsCount++; + public static final int key_voipgroup_mutedByAdminMuteButton = colorsCount++; + public static final int key_voipgroup_mutedByAdminMuteButtonDisabled = colorsCount++; + public static final int key_voipgroup_windowBackgroundWhiteInputField = colorsCount++; + public static final int key_voipgroup_windowBackgroundWhiteInputFieldActivated = colorsCount++; + + public static final int key_passport_authorizeBackground = colorsCount++; + public static final int key_passport_authorizeBackgroundSelected = colorsCount++; + public static final int key_passport_authorizeText = colorsCount++; + + public static final int key_profile_creatorIcon = colorsCount++; + public static final int key_profile_title = colorsCount++; + public static final int key_profile_actionIcon = colorsCount++; + public static final int key_profile_actionBackground = colorsCount++; + public static final int key_profile_actionPressedBackground = colorsCount++; + public static final int key_profile_verifiedBackground = colorsCount++; + public static final int key_profile_verifiedCheck = colorsCount++; + public static final int key_profile_status = colorsCount++; + + public static final int key_profile_tabText = colorsCount++; + public static final int key_profile_tabSelectedText = colorsCount++; + public static final int key_profile_tabSelectedLine = colorsCount++; + public static final int key_profile_tabSelector = colorsCount++; + + public static final int key_sharedMedia_startStopLoadIcon = colorsCount++; + public static final int key_sharedMedia_linkPlaceholder = colorsCount++; + public static final int key_sharedMedia_linkPlaceholderText = colorsCount++; + public static final int key_sharedMedia_photoPlaceholder = colorsCount++; + + public static final int key_featuredStickers_addedIcon = colorsCount++; + public static final int key_featuredStickers_buttonProgress = colorsCount++; + public static final int key_featuredStickers_addButton = colorsCount++; + public static final int key_featuredStickers_addButtonPressed = colorsCount++; + public static final int key_featuredStickers_removeButtonText = colorsCount++; + public static final int key_featuredStickers_buttonText = colorsCount++; + public static final int key_featuredStickers_unread = colorsCount++; + + public static final int key_stickers_menu = colorsCount++; + public static final int key_stickers_menuSelector = colorsCount++; + + public static final int key_changephoneinfo_image2 = colorsCount++; + + public static final int key_groupcreate_hintText = colorsCount++; + public static final int key_groupcreate_cursor = colorsCount++; + public static final int key_groupcreate_sectionShadow = colorsCount++; + public static final int key_groupcreate_sectionText = colorsCount++; + public static final int key_groupcreate_spanText = colorsCount++; + public static final int key_groupcreate_spanBackground = colorsCount++; + public static final int key_groupcreate_spanDelete = colorsCount++; + + public static final int key_contacts_inviteBackground = colorsCount++; + public static final int key_contacts_inviteText = colorsCount++; + + public static final int key_login_progressInner = colorsCount++; + public static final int key_login_progressOuter = colorsCount++; + + public static final int key_picker_enabledButton = colorsCount++; + public static final int key_picker_disabledButton = colorsCount++; + public static final int key_picker_badge = colorsCount++; + public static final int key_picker_badgeText = colorsCount++; + + public static final int key_location_sendLocationBackground = colorsCount++; + public static final int key_location_sendLocationIcon = colorsCount++; + public static final int key_location_sendLocationText = colorsCount++; + public static final int key_location_sendLiveLocationBackground = colorsCount++; + public static final int key_location_sendLiveLocationIcon = colorsCount++; + public static final int key_location_sendLiveLocationText = colorsCount++; + public static final int key_location_liveLocationProgress = colorsCount++; + public static final int key_location_placeLocationBackground = colorsCount++; + public static final int key_location_actionIcon = colorsCount++; + public static final int key_location_actionActiveIcon = colorsCount++; + public static final int key_location_actionBackground = colorsCount++; + public static final int key_location_actionPressedBackground = colorsCount++; + + public static final int key_dialog_liveLocationProgress = colorsCount++; + + public static final int key_files_folderIcon = colorsCount++; + public static final int key_files_folderIconBackground = colorsCount++; + public static final int key_files_iconText = colorsCount++; + + public static final int key_sessions_devicesImage = colorsCount++; + + public static final int key_calls_callReceivedGreenIcon = colorsCount++; + public static final int key_calls_callReceivedRedIcon = colorsCount++; + + public static final int key_undo_background = colorsCount++; + public static final int key_undo_cancelColor = colorsCount++; + public static final int key_undo_infoColor = colorsCount++; + + public static final int key_sheet_scrollUp = colorsCount++; + public static final int key_sheet_other = colorsCount++; //ununsed - public static final String key_player_actionBarSelector = "player_actionBarSelector"; - public static final String key_player_actionBarTitle = "player_actionBarTitle"; - public static final String key_player_actionBarSubtitle = "player_actionBarSubtitle"; - public static final String key_player_actionBarItems = "player_actionBarItems"; - public static final String key_player_background = "player_background"; - public static final String key_player_time = "player_time"; - public static final String key_player_progressBackground = "player_progressBackground"; - public static final String key_player_progressCachedBackground = "key_player_progressCachedBackground"; - public static final String key_player_progress = "player_progress"; - public static final String key_player_button = "player_button"; - public static final String key_player_buttonActive = "player_buttonActive"; - - public static final String key_statisticChartSignature = "statisticChartSignature"; - public static final String key_statisticChartSignatureAlpha = "statisticChartSignatureAlpha"; - public static final String key_statisticChartHintLine = "statisticChartHintLine"; - public static final String key_statisticChartActiveLine = "statisticChartActiveLine"; - public static final String key_statisticChartInactivePickerChart = "statisticChartInactivePickerChart"; - public static final String key_statisticChartActivePickerChart = "statisticChartActivePickerChart"; - public static final String key_statisticChartRipple = "statisticChartRipple"; - public static final String key_statisticChartBackZoomColor = "statisticChartBackZoomColor"; - public static final String key_statisticChartChevronColor = "statisticChartChevronColor"; - public static final String key_statisticChartLine_blue = "statisticChartLine_blue"; - public static final String key_statisticChartLine_green = "statisticChartLine_green"; - public static final String key_statisticChartLine_red = "statisticChartLine_red"; - public static final String key_statisticChartLine_golden = "statisticChartLine_golden"; - public static final String key_statisticChartLine_lightblue = "statisticChartLine_lightblue"; - public static final String key_statisticChartLine_lightgreen = "statisticChartLine_lightgreen"; - public static final String key_statisticChartLine_orange = "statisticChartLine_orange"; - public static final String key_statisticChartLine_indigo = "statisticChartLine_indigo"; - public static final String key_statisticChartLine_purple = "statisticChartLine_purple"; - public static final String key_statisticChartLine_cyan = "statisticChartLine_cyan"; - public static final String key_statisticChartLineEmpty = "statisticChartLineEmpty"; - - public static final String key_color_lightblue = "color_lightblue"; - public static final String key_color_blue = "color_blue"; - public static final String key_color_green = "color_green"; - public static final String key_color_lightgreen = "color_lightgreen"; - public static final String key_color_red = "color_red"; - public static final String key_color_orange = "color_orange"; - public static final String key_color_yellow = "color_yellow"; - public static final String key_color_purple = "color_purple"; - public static final String key_color_cyan = "color_cyan"; - public static final String[] keys_colors = { key_color_lightblue, key_color_blue, key_color_green, key_color_lightgreen, key_color_red, key_color_orange, key_color_yellow, key_color_purple, key_color_cyan }; - - public static final String key_chat_outReactionButtonBackground = "chat_outReactionButtonBackground"; - public static final String key_chat_inReactionButtonBackground = "chat_inReactionButtonBackground"; - public static final String key_chat_outReactionButtonText = "chat_outReactionButtonText"; - public static final String key_chat_inReactionButtonText = "chat_inReactionButtonText"; - public static final String key_chat_inReactionButtonTextSelected = "chat_inReactionButtonTextSelected"; - public static final String key_chat_outReactionButtonTextSelected = "chat_outReactionButtonTextSelected"; - - public static final String key_premiumGradient0 = "premiumGradient0"; - public static final String key_premiumGradient1 = "premiumGradient1"; - public static final String key_premiumGradient2 = "premiumGradient2"; - public static final String key_premiumGradient3 = "premiumGradient3"; - public static final String key_premiumGradient4 = "premiumGradient4"; - public static final String key_premiumGradientBackground1 = "premiumGradientBackground1"; - public static final String key_premiumGradientBackground2 = "premiumGradientBackground2"; - public static final String key_premiumGradientBackground3 = "premiumGradientBackground3"; - public static final String key_premiumGradientBackground4 = "premiumGradientBackground4"; - public static final String key_premiumGradientBackgroundOverlay = "premiumGradientBackgroundOverlay"; - public static final String key_premiumStartSmallStarsColor = "premiumStartSmallStarsColor"; - public static final String key_premiumStartGradient1 = "premiumStarGradient1"; - public static final String key_premiumStartGradient2 = "premiumStarGradient2"; - public static final String key_premiumStartSmallStarsColor2 = "premiumStartSmallStarsColor2"; - public static final String key_premiumGradientBottomSheet1 = "premiumGradientBottomSheet1"; - public static final String key_premiumGradientBottomSheet2 = "premiumGradientBottomSheet2"; - public static final String key_premiumGradientBottomSheet3 = "premiumGradientBottomSheet3"; - public static final String key_topics_unreadCounter = "topics_unreadCounter"; - public static final String key_topics_unreadCounterMuted = "topics_unreadCounterMuted"; + public static final int key_player_actionBarSelector = colorsCount++; + public static final int key_player_actionBarTitle = colorsCount++; + public static final int key_player_actionBarSubtitle = colorsCount++; + public static final int key_player_actionBarItems = colorsCount++; + public static final int key_player_background = colorsCount++; + public static final int key_player_time = colorsCount++; + public static final int key_player_progressBackground = colorsCount++; + public static final int key_player_progressCachedBackground = colorsCount++; + public static final int key_player_progress = colorsCount++; + public static final int key_player_button = colorsCount++; + public static final int key_player_buttonActive = colorsCount++; + + public static final int key_statisticChartSignature = colorsCount++; + public static final int key_statisticChartSignatureAlpha = colorsCount++; + public static final int key_statisticChartHintLine = colorsCount++; + public static final int key_statisticChartActiveLine = colorsCount++; + public static final int key_statisticChartInactivePickerChart = colorsCount++; + public static final int key_statisticChartActivePickerChart = colorsCount++; + public static final int key_statisticChartRipple = colorsCount++; + public static final int key_statisticChartBackZoomColor = colorsCount++; + public static final int key_statisticChartChevronColor = colorsCount++; + public static final int key_statisticChartLine_blue = colorsCount++; + public static final int key_statisticChartLine_green = colorsCount++; + public static final int key_statisticChartLine_red = colorsCount++; + public static final int key_statisticChartLine_golden = colorsCount++; + public static final int key_statisticChartLine_lightblue = colorsCount++; + public static final int key_statisticChartLine_lightgreen = colorsCount++; + public static final int key_statisticChartLine_orange = colorsCount++; + public static final int key_statisticChartLine_indigo = colorsCount++; + public static final int key_statisticChartLine_purple = colorsCount++; + public static final int key_statisticChartLine_cyan = colorsCount++; + public static final int key_statisticChartLineEmpty = colorsCount++; + + public static final int key_color_lightblue = colorsCount++; + public static final int key_color_blue = colorsCount++; + public static final int key_color_green = colorsCount++; + public static final int key_color_lightgreen = colorsCount++; + public static final int key_color_red = colorsCount++; + public static final int key_color_orange = colorsCount++; + public static final int key_color_yellow = colorsCount++; + public static final int key_color_purple = colorsCount++; + public static final int key_color_cyan = colorsCount++; + public static final int[] keys_colors = {key_color_lightblue, key_color_blue, key_color_green, key_color_lightgreen, key_color_red, key_color_orange, key_color_yellow, key_color_purple, key_color_cyan}; + + public static final int key_chat_outReactionButtonBackground = colorsCount++; + public static final int key_chat_inReactionButtonBackground = colorsCount++; + public static final int key_chat_outReactionButtonText = colorsCount++; + public static final int key_chat_inReactionButtonText = colorsCount++; + public static final int key_chat_inReactionButtonTextSelected = colorsCount++; + public static final int key_chat_outReactionButtonTextSelected = colorsCount++; + + public static final int key_premiumGradient0 = colorsCount++; + public static final int key_premiumGradient1 = colorsCount++; + public static final int key_premiumGradient2 = colorsCount++; + public static final int key_premiumGradient3 = colorsCount++; + public static final int key_premiumGradient4 = colorsCount++; + public static final int key_premiumGradientBackground1 = colorsCount++; + public static final int key_premiumGradientBackground2 = colorsCount++; + public static final int key_premiumGradientBackground3 = colorsCount++; + public static final int key_premiumGradientBackground4 = colorsCount++; + public static final int key_premiumGradientBackgroundOverlay = colorsCount++; + public static final int key_premiumStartSmallStarsColor = colorsCount++; + public static final int key_premiumStartGradient1 = colorsCount++; + public static final int key_premiumStartGradient2 = colorsCount++; + public static final int key_premiumStartSmallStarsColor2 = colorsCount++; + public static final int key_premiumGradientBottomSheet1 = colorsCount++; + public static final int key_premiumGradientBottomSheet2 = colorsCount++; + public static final int key_premiumGradientBottomSheet3 = colorsCount++; + public static final int key_topics_unreadCounter = colorsCount++; + public static final int key_topics_unreadCounterMuted = colorsCount++; public static final String key_drawable_botInline = "drawableBotInline"; public static final String key_drawable_botLink = "drawableBotLink"; @@ -4033,7 +4083,7 @@ public void run() { public static final String key_drawable_chat_pollHintDrawableIn = "drawable_chat_pollHintDrawableIn"; private static final HashMap defaultChatDrawables = new HashMap<>(); - private static final HashMap defaultChatDrawableColorKeys = new HashMap<>(); + private static final HashMap defaultChatDrawableColorKeys = new HashMap<>(); public static final String key_paint_chatActionBackground = "paintChatActionBackground"; public static final String key_paint_chatActionBackgroundSelected = "paintChatActionBackgroundSelected"; @@ -4042,19 +4092,17 @@ public void run() { public static final String key_paint_chatActionText2 = "paintChatActionText2"; public static final String key_paint_chatBotButton = "paintChatBotButton"; public static final String key_paint_chatComposeBackground = "paintChatComposeBackground"; + public static final String key_paint_divider = "paintDivider"; public static final String key_paint_chatTimeBackground = "paintChatTimeBackground"; private static final HashMap defaultChatPaints = new HashMap<>(); - private static final HashMap defaultChatPaintColors = new HashMap<>(); - - private static HashSet myMessagesColorKeys = new HashSet<>(); - private static HashSet myMessagesBubblesColorKeys = new HashSet<>(); - private static HashSet myMessagesGradientColorsNearKeys = new HashSet<>(); - private static HashMap defaultColors = new HashMap<>(); - private static HashMap fallbackKeys = new HashMap<>(); - private static HashSet themeAccentExclusionKeys = new HashSet<>(); - private static HashMap currentColorsNoAccent; - private static HashMap currentColors; - private static HashMap animatingColors; + private static final HashMap defaultChatPaintColors = new HashMap<>(); + + private static int[] defaultColors; + private static SparseIntArray fallbackKeys = new SparseIntArray(); + private static HashSet themeAccentExclusionKeys = new HashSet<>(); + private static SparseIntArray currentColorsNoAccent; + private static SparseIntArray currentColors; + private static SparseIntArray animatingColors; private static boolean shouldDrawGradientIcons; private static ThreadLocal hsvTemp1Local = new ThreadLocal<>(); @@ -4067,753 +4115,7 @@ public void run() { private static RoundVideoProgressShadow roundPlayDrawable; static { - defaultColors.put(key_dialogBackground, 0xffffffff); - defaultColors.put(key_dialogBackgroundGray, 0xfff0f0f0); - defaultColors.put(key_dialogTextBlack, 0xff222222); - defaultColors.put(key_dialogTextLink, 0xff2678b6); - defaultColors.put(key_dialogLinkSelection, 0x3362a9e3); - defaultColors.put(key_dialogTextRed, 0xffcd5a5a); - defaultColors.put(key_dialogTextBlue, 0xff2f8cc9); - defaultColors.put(key_dialogTextBlue2, 0xff3a95d5); - defaultColors.put(key_dialogTextBlue4, 0xff19a7e8); - defaultColors.put(key_dialogTextGray, 0xff348bc1); - defaultColors.put(key_dialogTextGray2, 0xff757575); - defaultColors.put(key_dialogTextGray3, 0xff999999); - defaultColors.put(key_dialogTextGray4, 0xffb3b3b3); - defaultColors.put(key_dialogTextHint, 0xff979797); - defaultColors.put(key_dialogIcon, 0xff676b70); - defaultColors.put(key_dialogRedIcon, 0xffe14d4d); - defaultColors.put(key_dialogGrayLine, 0xffd2d2d2); - defaultColors.put(key_dialogTopBackground, 0xff6fb2e5); - defaultColors.put(key_dialogInputField, 0xffdbdbdb); - defaultColors.put(key_dialogInputFieldActivated, 0xff37a9f0); - defaultColors.put(key_dialogCheckboxSquareBackground, 0xff43a0df); - defaultColors.put(key_dialogCheckboxSquareCheck, 0xffffffff); - defaultColors.put(key_dialogCheckboxSquareUnchecked, 0xff737373); - defaultColors.put(key_dialogCheckboxSquareDisabled, 0xffb0b0b0); - defaultColors.put(key_dialogRadioBackground, 0xffb3b3b3); - defaultColors.put(key_dialogRadioBackgroundChecked, 0xff37a9f0); - defaultColors.put(key_dialogLineProgress, 0xff527da3); - defaultColors.put(key_dialogLineProgressBackground, 0xffdbdbdb); - defaultColors.put(key_dialogButton, 0xff4991cc); - defaultColors.put(key_dialogButtonSelector, 0x0f000000); - defaultColors.put(key_dialogScrollGlow, 0xfff5f6f7); - defaultColors.put(key_dialogRoundCheckBox, 0xff4cb4f5); - defaultColors.put(key_dialogRoundCheckBoxCheck, 0xffffffff); - defaultColors.put(key_dialogCameraIcon, 0xffffffff); - defaultColors.put(key_dialog_inlineProgressBackground, 0xf6f0f2f5); - defaultColors.put(key_dialog_inlineProgress, 0xff6b7378); - defaultColors.put(key_dialogSearchBackground, 0xfff2f4f5); - defaultColors.put(key_dialogSearchHint, 0xff98a0a7); - defaultColors.put(key_dialogSearchIcon, 0xffa1a8af); - defaultColors.put(key_dialogSearchText, 0xff222222); - defaultColors.put(key_dialogFloatingButton, 0xff4cb4f5); - defaultColors.put(key_dialogFloatingButtonPressed, 0x0f000000); - defaultColors.put(key_dialogFloatingIcon, 0xffffffff); - defaultColors.put(key_dialogShadowLine, 0x12000000); - defaultColors.put(key_dialogEmptyImage, 0xff9fa4a8); - defaultColors.put(key_dialogEmptyText, 0xff8c9094); - defaultColors.put(key_dialogSwipeRemove, 0xffe56555); - defaultColors.put(key_dialogReactionMentionBackground, 0xffF05459); - - defaultColors.put(key_windowBackgroundWhite, 0xffffffff); - defaultColors.put(key_windowBackgroundUnchecked, 0xff9da7b1); - defaultColors.put(key_windowBackgroundChecked, 0xff579ed9); - defaultColors.put(key_windowBackgroundCheckText, 0xffffffff); - defaultColors.put(key_progressCircle, 0xff1c93e3); - defaultColors.put(key_windowBackgroundWhiteGrayIcon, 0xff81868b); - defaultColors.put(key_windowBackgroundWhiteBlueText, 0xff4092cd); - defaultColors.put(key_windowBackgroundWhiteBlueText2, 0xff3a95d5); - defaultColors.put(key_windowBackgroundWhiteBlueText3, 0xff2678b6); - defaultColors.put(key_windowBackgroundWhiteBlueText4, 0xff1c93e3); - defaultColors.put(key_windowBackgroundWhiteBlueText5, 0xff4c8eca); - defaultColors.put(key_windowBackgroundWhiteBlueText6, 0xff3a8ccf); - defaultColors.put(key_windowBackgroundWhiteBlueText7, 0xff377aae); - defaultColors.put(key_windowBackgroundWhiteBlueButton, 0xff1e88d3); - defaultColors.put(key_windowBackgroundWhiteBlueIcon, 0xff379de5); - defaultColors.put(key_windowBackgroundWhiteGreenText, 0xff26972c); - defaultColors.put(key_windowBackgroundWhiteGreenText2, 0xff37a818); - defaultColors.put(key_windowBackgroundWhiteRedText, 0xffcd5a5a); - defaultColors.put(key_windowBackgroundWhiteRedText2, 0xffdb5151); - defaultColors.put(key_windowBackgroundWhiteRedText3, 0xffd24949); - defaultColors.put(key_windowBackgroundWhiteRedText4, 0xffcf3030); - defaultColors.put(key_windowBackgroundWhiteRedText5, 0xffed3939); - defaultColors.put(key_windowBackgroundWhiteGrayText, 0xff838c96); - defaultColors.put(key_windowBackgroundWhiteGrayText2, 0xff82868a); - defaultColors.put(key_windowBackgroundWhiteGrayText3, 0xff999999); - defaultColors.put(key_windowBackgroundWhiteGrayText4, 0xff808080); - defaultColors.put(key_windowBackgroundWhiteGrayText5, 0xffa3a3a3); - defaultColors.put(key_windowBackgroundWhiteGrayText6, 0xff757575); - defaultColors.put(key_windowBackgroundWhiteGrayText7, 0xffc6c6c6); - defaultColors.put(key_windowBackgroundWhiteGrayText8, 0xff6d6d72); - defaultColors.put(key_windowBackgroundWhiteBlackText, 0xff222222); - defaultColors.put(key_windowBackgroundWhiteHintText, 0xffa8a8a8); - defaultColors.put(key_windowBackgroundWhiteValueText, 0xff3a95d5); - defaultColors.put(key_windowBackgroundWhiteLinkText, 0xff2678b6); - defaultColors.put(key_windowBackgroundWhiteLinkSelection, 0x3362a9e3); - defaultColors.put(key_windowBackgroundWhiteBlueHeader, 0xff3a95d5); - defaultColors.put(key_windowBackgroundWhiteInputField, 0xffdbdbdb); - defaultColors.put(key_windowBackgroundWhiteInputFieldActivated, 0xff37a9f0); - defaultColors.put(key_switchTrack, 0xffb0b5ba); - defaultColors.put(key_switchTrackChecked, 0xff52ade9); - defaultColors.put(key_switchTrackBlue, 0xff828e99); - defaultColors.put(key_switchTrackBlueChecked, 0xff3c88c7); - defaultColors.put(key_switchTrackBlueThumb, 0xffffffff); - defaultColors.put(key_switchTrackBlueThumbChecked, 0xffffffff); - defaultColors.put(key_switchTrackBlueSelector, 0x17404a53); - defaultColors.put(key_switchTrackBlueSelectorChecked, 0x21024781); - defaultColors.put(key_switch2Track, 0xfff57e7e); - defaultColors.put(key_switch2TrackChecked, 0xff52ade9); - defaultColors.put(key_checkboxSquareBackground, 0xff43a0df); - defaultColors.put(key_checkboxSquareCheck, 0xffffffff); - defaultColors.put(key_checkboxSquareUnchecked, 0xff737373); - defaultColors.put(key_checkboxSquareDisabled, 0xffb0b0b0); - defaultColors.put(key_listSelector, 0x0f000000); - defaultColors.put(key_radioBackground, 0xffb3b3b3); - defaultColors.put(key_radioBackgroundChecked, 0xff37a9f0); - defaultColors.put(key_windowBackgroundGray, 0xfff0f0f0); - defaultColors.put(key_windowBackgroundGrayShadow, 0xff000000); - defaultColors.put(key_emptyListPlaceholder, 0xff959595); - defaultColors.put(key_divider, 0xffd9d9d9); - defaultColors.put(key_graySection, 0xfff5f5f5); - defaultColors.put(key_graySectionText, 0xff82878A); - defaultColors.put(key_contextProgressInner1, 0xffbfdff6); - defaultColors.put(key_contextProgressOuter1, 0xff2b96e2); - defaultColors.put(key_contextProgressInner2, 0xffbfdff6); - defaultColors.put(key_contextProgressOuter2, 0xffffffff); - defaultColors.put(key_contextProgressInner3, 0xffb3b3b3); - defaultColors.put(key_contextProgressOuter3, 0xffffffff); - defaultColors.put(key_contextProgressInner4, 0xffcacdd0); - defaultColors.put(key_contextProgressOuter4, 0xff2f3438); - defaultColors.put(key_fastScrollActive, 0xff52a3db); - defaultColors.put(key_fastScrollInactive, 0xffc9cdd1); - defaultColors.put(key_fastScrollText, 0xffffffff); - - defaultColors.put(key_avatar_text, 0xffffffff); - - defaultColors.put(key_avatar_backgroundSaved, 0xff69BFFA); - defaultColors.put(key_avatar_background2Saved, 0xff3D9DE0); - defaultColors.put(key_avatar_backgroundArchived, 0xffB8C2CC); - defaultColors.put(key_avatar_backgroundArchivedHidden, 0xff66bffa); - defaultColors.put(key_avatar_backgroundRed, 0xffFF845E); - defaultColors.put(key_avatar_backgroundOrange, 0xffFEBB5B); - defaultColors.put(key_avatar_backgroundViolet, 0xffB694F9); - defaultColors.put(key_avatar_backgroundGreen, 0xff9AD164); - defaultColors.put(key_avatar_backgroundCyan, 0xff5BCBE3); - defaultColors.put(key_avatar_backgroundBlue, 0xff5CAFFA); - defaultColors.put(key_avatar_backgroundPink, 0xffFF8AAC); - - defaultColors.put(key_avatar_background2Red, 0xffD45246); - defaultColors.put(key_avatar_background2Orange, 0xffF68136); - defaultColors.put(key_avatar_background2Violet, 0xff6C61DF); - defaultColors.put(key_avatar_background2Green, 0xff46BA43); - defaultColors.put(key_avatar_background2Cyan, 0xff359AD4); - defaultColors.put(key_avatar_background2Blue, 0xff408ACF); - defaultColors.put(key_avatar_background2Pink, 0xffD95574); - - defaultColors.put(key_avatar_backgroundInProfileBlue, 0xff5085b1); - defaultColors.put(key_avatar_backgroundActionBarBlue, 0xff598fba); - defaultColors.put(key_avatar_subtitleInProfileBlue, 0xffd7eafa); - defaultColors.put(key_avatar_actionBarSelectorBlue, 0xff4981ad); - defaultColors.put(key_avatar_actionBarIconBlue, 0xffffffff); - - defaultColors.put(key_avatar_nameInMessageRed, 0xffca5650); - defaultColors.put(key_avatar_nameInMessageOrange, 0xffd87b29); - defaultColors.put(key_avatar_nameInMessageViolet, 0xff9B66DC); - defaultColors.put(key_avatar_nameInMessageGreen, 0xff50b232); - defaultColors.put(key_avatar_nameInMessageCyan, 0xff379eb8); - defaultColors.put(key_avatar_nameInMessageBlue, 0xff4e92cc); - defaultColors.put(key_avatar_nameInMessagePink, 0xffCF5C95); - - defaultColors.put(key_actionBarDefault, 0xff527da3); - defaultColors.put(key_actionBarDefaultIcon, 0xffffffff); - defaultColors.put(key_actionBarActionModeDefault, 0xffffffff); - defaultColors.put(key_actionBarActionModeDefaultTop, 0x10000000); - defaultColors.put(key_actionBarActionModeDefaultIcon, 0xff676a6f); - defaultColors.put(key_actionBarDefaultTitle, 0xffffffff); - defaultColors.put(key_actionBarDefaultSubtitle, 0xffd5e8f7); - defaultColors.put(key_actionBarDefaultSelector, 0xff406d94); - defaultColors.put(key_actionBarWhiteSelector, 0x1d000000); - defaultColors.put(key_actionBarDefaultSearch, 0xffffffff); - defaultColors.put(key_actionBarDefaultSearchPlaceholder, 0x88ffffff); - defaultColors.put(key_actionBarDefaultSubmenuItem, 0xff222222); - defaultColors.put(key_actionBarDefaultSubmenuItemIcon, 0xff676b70); - defaultColors.put(key_actionBarDefaultSubmenuBackground, 0xffffffff); - defaultColors.put(key_actionBarDefaultSubmenuSeparator, 0xfff5f5f5); - defaultColors.put(key_actionBarActionModeDefaultSelector, 0xffe2e2e2); - defaultColors.put(key_actionBarTabActiveText, 0xffffffff); - defaultColors.put(key_actionBarTabUnactiveText, 0xffd5e8f7); - defaultColors.put(key_actionBarTabLine, 0xffffffff); - defaultColors.put(key_actionBarTabSelector, 0xff406d94); - - defaultColors.put(key_actionBarBrowser, 0xffffffff); - - defaultColors.put(key_actionBarDefaultArchived, 0xff6f7a87); - defaultColors.put(key_actionBarDefaultArchivedSelector, 0xff5e6772); - defaultColors.put(key_actionBarDefaultArchivedIcon, 0xffffffff); - defaultColors.put(key_actionBarDefaultArchivedTitle, 0xffffffff); - defaultColors.put(key_actionBarDefaultArchivedSearch, 0xffffffff); - defaultColors.put(key_actionBarDefaultArchivedSearchPlaceholder, 0x88ffffff); - - defaultColors.put(key_chats_onlineCircle, 0xff4bcb1c); - defaultColors.put(key_chats_unreadCounter, 0xff4ecc5e); - defaultColors.put(key_chats_unreadCounterMuted, 0xffc6c9cc); - defaultColors.put(key_chats_unreadCounterText, 0xffffffff); - defaultColors.put(key_chats_archiveBackground, 0xff66a9e0); - defaultColors.put(key_chats_archivePinBackground, 0xff9faab3); - defaultColors.put(key_chats_archiveIcon, 0xffffffff); - defaultColors.put(key_chats_archiveText, 0xffffffff); - defaultColors.put(key_chats_name, 0xff222222); - defaultColors.put(key_chats_nameArchived, 0xff525252); - defaultColors.put(key_chats_secretName, 0xff00a60e); - defaultColors.put(key_chats_secretIcon, 0xff19b126); - defaultColors.put(key_chats_pinnedIcon, 0xffa8a8a8); - defaultColors.put(key_chats_message, 0xff8b8d8f); - defaultColors.put(key_chats_messageArchived, 0xff919191); - defaultColors.put(key_chats_message_threeLines, 0xff8e9091); - defaultColors.put(key_chats_draft, 0xffdd4b39); - defaultColors.put(key_chats_nameMessage, 0xff3c7eb0); - defaultColors.put(key_chats_nameMessageArchived, 0xff8b8d8f); - defaultColors.put(key_chats_nameMessage_threeLines, 0xff424449); - defaultColors.put(key_chats_nameMessageArchived_threeLines, 0xff5e5e5e); - defaultColors.put(key_chats_attachMessage, 0xff3c7eb0); - defaultColors.put(key_chats_actionMessage, 0xff3c7eb0); - defaultColors.put(key_chats_date, 0xff95999C); - defaultColors.put(key_chats_pinnedOverlay, 0x08000000); - defaultColors.put(key_chats_tabletSelectedOverlay, 0x0f000000); - defaultColors.put(key_chats_sentCheck, 0xff46aa36); - defaultColors.put(key_chats_sentReadCheck, 0xff46aa36); - defaultColors.put(key_chats_sentClock, 0xff75bd5e); - defaultColors.put(key_chats_sentError, 0xffd55252); - defaultColors.put(key_chats_sentErrorIcon, 0xffffffff); - defaultColors.put(key_chats_verifiedBackground, 0xff33a8e6); - defaultColors.put(key_chats_verifiedCheck, 0xffffffff); - defaultColors.put(key_chats_muteIcon, 0xffbdc1c4); - defaultColors.put(key_chats_mentionIcon, 0xffffffff); - defaultColors.put(key_chats_menuBackground, 0xffffffff); - defaultColors.put(key_chats_menuItemText, 0xff444444); - defaultColors.put(key_chats_menuItemCheck, 0xff598fba); - defaultColors.put(key_chats_menuItemIcon, 0xff889198); - defaultColors.put(key_chats_menuName, 0xffffffff); - defaultColors.put(key_chats_menuPhone, 0xffffffff); - defaultColors.put(key_chats_menuPhoneCats, 0xffc2e5ff); - defaultColors.put(key_chats_actionIcon, 0xffffffff); - defaultColors.put(key_chats_actionBackground, 0xff65a9e0); - defaultColors.put(key_chats_actionPressedBackground, 0xff569dd6); - defaultColors.put(key_chats_menuTopBackgroundCats, 0xff598fba); - defaultColors.put(key_chats_archivePullDownBackground, 0xffc6c9cc); - defaultColors.put(key_chats_archivePullDownBackgroundActive, 0xff66a9e0); - - defaultColors.put(key_chat_attachCheckBoxCheck, 0xffffffff); - defaultColors.put(key_chat_attachCheckBoxBackground, 0xff39b2f7); - defaultColors.put(key_chat_attachPhotoBackground, 0x0c000000); - defaultColors.put(key_chat_attachActiveTab, 0xff33a7f5); - defaultColors.put(key_chat_attachUnactiveTab, 0xff92999e); - defaultColors.put(key_chat_attachPermissionImage, 0xff333333); - defaultColors.put(key_chat_attachPermissionMark, 0xffe25050); - defaultColors.put(key_chat_attachPermissionText, 0xff6f777a); - defaultColors.put(key_chat_attachEmptyImage, 0xffcccccc); - - defaultColors.put(key_chat_attachIcon, 0xffffffff); - defaultColors.put(key_chat_attachGalleryBackground, 0xff459df5); - defaultColors.put(key_chat_attachGalleryText, 0xff2e8de9); - defaultColors.put(key_chat_attachAudioBackground, 0xffeb6060); - defaultColors.put(key_chat_attachAudioText, 0xffde4747); - defaultColors.put(key_chat_attachFileBackground, 0xff34b9f1); - defaultColors.put(key_chat_attachFileText, 0xff14a8e4); - defaultColors.put(key_chat_attachContactBackground, 0xfff2c04b); - defaultColors.put(key_chat_attachContactText, 0xffdfa000); - defaultColors.put(key_chat_attachLocationBackground, 0xff60c255); - defaultColors.put(key_chat_attachLocationText, 0xff3cab2f); - defaultColors.put(key_chat_attachPollBackground, 0xfff2c04b); - defaultColors.put(key_chat_attachPollText, 0xffdfa000); - - defaultColors.put(key_chat_inPollCorrectAnswer, 0xff60c255); - defaultColors.put(key_chat_outPollCorrectAnswer, 0xff60c255); - defaultColors.put(key_chat_inPollWrongAnswer, 0xffeb6060); - defaultColors.put(key_chat_outPollWrongAnswer, 0xffeb6060); - - defaultColors.put(key_chat_status, 0xffd5e8f7); - defaultColors.put(key_chat_inGreenCall, 0xff00c853); - defaultColors.put(key_chat_inRedCall, 0xffff4848); - defaultColors.put(key_chat_outGreenCall, 0xff00c853); - defaultColors.put(key_chat_lockIcon, 0xffffffff); - defaultColors.put(key_chat_muteIcon, 0xffb1cce3); - defaultColors.put(key_chat_inBubble, 0xffffffff); - defaultColors.put(key_chat_inBubbleSelected, 0xffecf7fd); - defaultColors.put(key_chat_inBubbleShadow, 0xff1d3753); - defaultColors.put(key_chat_outBubble, 0xffefffde); - defaultColors.put(key_chat_outBubbleGradientSelectedOverlay, 0x14000000); - defaultColors.put(key_chat_outBubbleSelected, 0xffd9f7c5); - defaultColors.put(key_chat_outBubbleShadow, 0xff1e750c); - defaultColors.put(key_chat_inMediaIcon, 0xffffffff); - defaultColors.put(key_chat_inMediaIconSelected, 0xffeff8fe); - defaultColors.put(key_chat_outMediaIcon, 0xffefffde); - defaultColors.put(key_chat_outMediaIconSelected, 0xffe1f8cf); - defaultColors.put(key_chat_messageTextIn, 0xff000000); - defaultColors.put(key_chat_messageTextOut, 0xff000000); - defaultColors.put(key_chat_messageLinkIn, 0xff2678b6); - defaultColors.put(key_chat_messageLinkOut, 0xff2678b6); - defaultColors.put(key_chat_serviceText, 0xffffffff); - defaultColors.put(key_chat_serviceLink, 0xffffffff); - defaultColors.put(key_chat_serviceIcon, 0xffffffff); - defaultColors.put(key_chat_mediaTimeBackground, 0x66000000); - defaultColors.put(key_chat_outSentCheck, 0xff5db050); - defaultColors.put(key_chat_outSentCheckSelected, 0xff5db050); - defaultColors.put(key_chat_outSentCheckRead, 0xff5db050); - defaultColors.put(key_chat_outSentCheckReadSelected, 0xff5db050); - defaultColors.put(key_chat_outSentClock, 0xff75bd5e); - defaultColors.put(key_chat_outSentClockSelected, 0xff75bd5e); - defaultColors.put(key_chat_inSentClock, 0xffa1aab3); - defaultColors.put(key_chat_inSentClockSelected, 0xff93bdca); - defaultColors.put(key_chat_mediaSentCheck, 0xffffffff); - defaultColors.put(key_chat_mediaSentClock, 0xffffffff); - defaultColors.put(key_chat_inViews, 0xffa1aab3); - defaultColors.put(key_chat_inViewsSelected, 0xff93bdca); - defaultColors.put(key_chat_outViews, 0xff6eb257); - defaultColors.put(key_chat_outViewsSelected, 0xff6eb257); - defaultColors.put(key_chat_mediaViews, 0xffffffff); - defaultColors.put(key_chat_inMenu, 0xffb6bdc5); - defaultColors.put(key_chat_inMenuSelected, 0xff98c1ce); - defaultColors.put(key_chat_outMenu, 0xff91ce7e); - defaultColors.put(key_chat_outMenuSelected, 0xff91ce7e); - defaultColors.put(key_chat_mediaMenu, 0xffffffff); - defaultColors.put(key_chat_outInstant, 0xff55ab4f); - defaultColors.put(key_chat_outInstantSelected, 0xff489943); - defaultColors.put(key_chat_inInstant, 0xff3a8ccf); - defaultColors.put(key_chat_inInstantSelected, 0xff3079b5); - defaultColors.put(key_chat_sentError, 0xffdb3535); - defaultColors.put(key_chat_sentErrorIcon, 0xffffffff); - defaultColors.put(key_chat_selectedBackground, 0x280a90f0); - defaultColors.put(key_chat_previewDurationText, 0xffffffff); - defaultColors.put(key_chat_previewGameText, 0xffffffff); - defaultColors.put(key_chat_inPreviewInstantText, 0xff3a8ccf); - defaultColors.put(key_chat_outPreviewInstantText, 0xff55ab4f); - defaultColors.put(key_chat_secretTimeText, 0xffe4e2e0); - defaultColors.put(key_chat_stickerNameText, 0xffffffff); - defaultColors.put(key_chat_botButtonText, 0xffffffff); - defaultColors.put(key_chat_inForwardedNameText, 0xff3886c7); - defaultColors.put(key_chat_outForwardedNameText, 0xff55ab4f); - defaultColors.put(key_chat_inPsaNameText, 0xff5a9c39); - defaultColors.put(key_chat_outPsaNameText, 0xff5a9c39); - defaultColors.put(key_chat_inViaBotNameText, 0xff3a8ccf); - defaultColors.put(key_chat_outViaBotNameText, 0xff55ab4f); - defaultColors.put(key_chat_stickerViaBotNameText, 0xffffffff); - defaultColors.put(key_chat_inReplyLine, 0xff599fd8); - defaultColors.put(key_chat_outReplyLine, 0xff6eb969); - defaultColors.put(key_chat_stickerReplyLine, 0xffffffff); - defaultColors.put(key_chat_inReplyNameText, 0xff3a8ccf); - defaultColors.put(key_chat_outReplyNameText, 0xff55ab4f); - defaultColors.put(key_chat_stickerReplyNameText, 0xffffffff); - defaultColors.put(key_chat_inReplyMessageText, 0xff000000); - defaultColors.put(key_chat_outReplyMessageText, 0xff000000); - defaultColors.put(key_chat_inReplyMediaMessageText, 0xffa1aab3); - defaultColors.put(key_chat_outReplyMediaMessageText, 0xff65b05b); - defaultColors.put(key_chat_inReplyMediaMessageSelectedText, 0xff89b4c1); - defaultColors.put(key_chat_outReplyMediaMessageSelectedText, 0xff65b05b); - defaultColors.put(key_chat_stickerReplyMessageText, 0xffffffff); - defaultColors.put(key_chat_inPreviewLine, 0xff70b4e8); - defaultColors.put(key_chat_outPreviewLine, 0xff88c97b); - defaultColors.put(key_chat_inSiteNameText, 0xff3a8ccf); - defaultColors.put(key_chat_outSiteNameText, 0xff55ab4f); - defaultColors.put(key_chat_inContactNameText, 0xff4e9ad4); - defaultColors.put(key_chat_outContactNameText, 0xff55ab4f); - defaultColors.put(key_chat_inContactPhoneText, 0xff2f3438); - defaultColors.put(key_chat_inContactPhoneSelectedText, 0xff2f3438); - defaultColors.put(key_chat_outContactPhoneText, 0xff354234); - defaultColors.put(key_chat_outContactPhoneSelectedText, 0xff354234); - defaultColors.put(key_chat_mediaProgress, 0xffffffff); - defaultColors.put(key_chat_inAudioProgress, 0xffffffff); - defaultColors.put(key_chat_outAudioProgress, 0xffefffde); - defaultColors.put(key_chat_inAudioSelectedProgress, 0xffeff8fe); - defaultColors.put(key_chat_outAudioSelectedProgress, 0xffe1f8cf); - defaultColors.put(key_chat_mediaTimeText, 0xffffffff); - defaultColors.put(key_chat_inAdminText, 0xffc0c6cb); - defaultColors.put(key_chat_inAdminSelectedText, 0xff89b4c1); - defaultColors.put(key_chat_outAdminText, 0xff70b15c); - defaultColors.put(key_chat_outAdminSelectedText, 0xff70b15c); - defaultColors.put(key_chat_inTimeText, 0xffa1aab3); - defaultColors.put(key_chat_inTimeSelectedText, 0xff89b4c1); - defaultColors.put(key_chat_outTimeText, 0xff70b15c); - defaultColors.put(key_chat_outTimeSelectedText, 0xff70b15c); - defaultColors.put(key_chat_inAudioPerformerText, 0xff2f3438); - defaultColors.put(key_chat_inAudioPerformerSelectedText, 0xff2f3438); - defaultColors.put(key_chat_outAudioPerformerText, 0xff354234); - defaultColors.put(key_chat_outAudioPerformerSelectedText, 0xff354234); - defaultColors.put(key_chat_inAudioTitleText, 0xff4e9ad4); - defaultColors.put(key_chat_outAudioTitleText, 0xff55ab4f); - defaultColors.put(key_chat_inAudioDurationText, 0xffa1aab3); - defaultColors.put(key_chat_outAudioDurationText, 0xff65b05b); - defaultColors.put(key_chat_inAudioDurationSelectedText, 0xff89b4c1); - defaultColors.put(key_chat_outAudioDurationSelectedText, 0xff65b05b); - defaultColors.put(key_chat_inAudioSeekbar, 0xffe4eaf0); - defaultColors.put(key_chat_inAudioCacheSeekbar, 0x3fe4eaf0); - defaultColors.put(key_chat_outAudioSeekbar, 0xffbbe3ac); - defaultColors.put(key_chat_outAudioCacheSeekbar, 0x3fbbe3ac); - defaultColors.put(key_chat_inAudioSeekbarSelected, 0xffbcdee8); - defaultColors.put(key_chat_outAudioSeekbarSelected, 0xffa9dd96); - defaultColors.put(key_chat_inAudioSeekbarFill, 0xff72b5e8); - defaultColors.put(key_chat_outAudioSeekbarFill, 0xff78c272); - defaultColors.put(key_chat_inVoiceSeekbar, 0xffdee5eb); - defaultColors.put(key_chat_outVoiceSeekbar, 0xffbbe3ac); - defaultColors.put(key_chat_inVoiceSeekbarSelected, 0xffbcdee8); - defaultColors.put(key_chat_outVoiceSeekbarSelected, 0xffa9dd96); - defaultColors.put(key_chat_inVoiceSeekbarFill, 0xff72b5e8); - defaultColors.put(key_chat_outVoiceSeekbarFill, 0xff78c272); - defaultColors.put(key_chat_inFileProgress, 0xffebf0f5); - defaultColors.put(key_chat_outFileProgress, 0xffdaf5c3); - defaultColors.put(key_chat_inFileProgressSelected, 0xffcbeaf6); - defaultColors.put(key_chat_outFileProgressSelected, 0xffc5eca7); - defaultColors.put(key_chat_inFileNameText, 0xff4e9ad4); - defaultColors.put(key_chat_outFileNameText, 0xff55ab4f); - defaultColors.put(key_chat_inFileInfoText, 0xffa1aab3); - defaultColors.put(key_chat_outFileInfoText, 0xff65b05b); - defaultColors.put(key_chat_inFileInfoSelectedText, 0xff89b4c1); - defaultColors.put(key_chat_outFileInfoSelectedText, 0xff65b05b); - defaultColors.put(key_chat_inFileBackground, 0xffebf0f5); - defaultColors.put(key_chat_outFileBackground, 0xffdaf5c3); - defaultColors.put(key_chat_inFileBackgroundSelected, 0xffcbeaf6); - defaultColors.put(key_chat_outFileBackgroundSelected, 0xffc5eca7); - defaultColors.put(key_chat_inVenueInfoText, 0xffa1aab3); - defaultColors.put(key_chat_outVenueInfoText, 0xff65b05b); - defaultColors.put(key_chat_inVenueInfoSelectedText, 0xff89b4c1); - defaultColors.put(key_chat_outVenueInfoSelectedText, 0xff65b05b); - defaultColors.put(key_chat_mediaInfoText, 0xffffffff); - defaultColors.put(key_chat_linkSelectBackground, 0x3362a9e3); - defaultColors.put(key_chat_outLinkSelectBackground, 0x3362a9e3); - defaultColors.put(key_chat_textSelectBackground, 0x6662a9e3); - defaultColors.put(key_chat_emojiPanelBackground, 0xfff0f2f5); - defaultColors.put(key_chat_emojiSearchBackground, 0xffe5e9ee); - defaultColors.put(key_chat_emojiSearchIcon, 0xff94a1af); - defaultColors.put(key_chat_emojiPanelShadowLine, 0x12000000); - defaultColors.put(key_chat_emojiPanelEmptyText, 0xff949ba1); - defaultColors.put(key_chat_emojiPanelIcon, 0xff9da4ab); - defaultColors.put(key_chat_emojiBottomPanelIcon, 0xff8c9197); - defaultColors.put(key_chat_emojiPanelIconSelected, 0xff5E6976); - defaultColors.put(key_chat_emojiPanelStickerPackSelector, 0xffe2e5e7); - defaultColors.put(key_chat_emojiPanelStickerPackSelectorLine, 0xff56abf0); - defaultColors.put(key_chat_emojiPanelBackspace, 0xff8c9197); - defaultColors.put(key_chat_emojiPanelTrendingTitle, 0xff222222); - defaultColors.put(key_chat_emojiPanelStickerSetName, 0xff828b94); - defaultColors.put(key_chat_emojiPanelStickerSetNameHighlight, 0xff278ddb); - defaultColors.put(key_chat_emojiPanelStickerSetNameIcon, 0xffb1b6bc); - defaultColors.put(key_chat_emojiPanelTrendingDescription, 0xff8a8a8a); - defaultColors.put(key_chat_botKeyboardButtonText, 0xff36474f); - defaultColors.put(key_chat_botKeyboardButtonBackground, 0xffe4e7e9); - defaultColors.put(key_chat_botKeyboardButtonBackgroundPressed, 0xffccd1d4); - defaultColors.put(key_chat_unreadMessagesStartArrowIcon, 0xffa2b5c7); - defaultColors.put(key_chat_unreadMessagesStartText, 0xff5695cc); - defaultColors.put(key_chat_unreadMessagesStartBackground, 0xffffffff); - defaultColors.put(key_chat_inLocationBackground, 0xffebf0f5); - defaultColors.put(key_chat_inLocationIcon, 0xffa2b5c7); - defaultColors.put(key_chat_outLocationIcon, 0xff87bf78); - defaultColors.put(key_chat_inContactBackground, 0xff72b5e8); - defaultColors.put(key_chat_inContactIcon, 0xffffffff); - defaultColors.put(key_chat_outContactBackground, 0xff78c272); - defaultColors.put(key_chat_outContactIcon, 0xffefffde); - defaultColors.put(key_chat_searchPanelIcons, 0xff676a6f); - defaultColors.put(key_chat_searchPanelText, 0xff676a6f); - defaultColors.put(key_chat_secretChatStatusText, 0xff7f7f7f); - defaultColors.put(key_chat_fieldOverlayText, 0xff3a8ccf); - defaultColors.put(key_chat_stickersHintPanel, 0xffffffff); - defaultColors.put(key_chat_replyPanelIcons, 0xff57a8e6); - defaultColors.put(key_chat_replyPanelClose, 0xff8e959b); - defaultColors.put(key_chat_replyPanelName, 0xff3a8ccf); - defaultColors.put(key_chat_replyPanelLine, 0xffe8e8e8); - defaultColors.put(key_chat_messagePanelBackground, 0xffffffff); - defaultColors.put(key_chat_messagePanelText, 0xff000000); - defaultColors.put(key_chat_messagePanelHint, 0xffa4acb3); - defaultColors.put(key_chat_messagePanelCursor, 0xff54a1db); - defaultColors.put(key_chat_messagePanelShadow, 0xff000000); - defaultColors.put(key_chat_messagePanelIcons, 0xff8e959b); - defaultColors.put(key_chat_recordedVoicePlayPause, 0xffffffff); - defaultColors.put(key_chat_recordedVoiceDot, 0xffda564d); - defaultColors.put(key_chat_recordedVoiceBackground, 0xff5DADE8); - defaultColors.put(key_chat_recordedVoiceProgress, 0xffB1DEFF); - defaultColors.put(key_chat_recordedVoiceProgressInner, 0xffffffff); - defaultColors.put(key_chat_recordVoiceCancel, 0xff3A95D4); - defaultColors.put(key_chat_messagePanelSend, 0xff62b0eb); - defaultColors.put(key_chat_messagePanelVoiceLock, 0xffa4a4a4); - defaultColors.put(key_chat_messagePanelVoiceLockBackground, 0xffffffff); - defaultColors.put(key_chat_messagePanelVoiceLockShadow, 0xff000000); - defaultColors.put(key_chat_recordTime, 0xff8e959b); - defaultColors.put(key_chat_emojiPanelNewTrending, 0xff4da6ea); - defaultColors.put(key_chat_gifSaveHintText, 0xffffffff); - defaultColors.put(key_chat_gifSaveHintBackground, 0xcc111111); - defaultColors.put(key_chat_goDownButton, 0xffffffff); - defaultColors.put(key_chat_goDownButtonIcon, 0xff8e959b); - defaultColors.put(key_chat_goDownButtonCounter, 0xffffffff); - defaultColors.put(key_chat_goDownButtonCounterBackground, 0xff4da2e8); - defaultColors.put(key_chat_messagePanelCancelInlineBot, 0xffadadad); - defaultColors.put(key_chat_messagePanelVoicePressed, 0xffffffff); - defaultColors.put(key_chat_messagePanelVoiceBackground, 0xff5DA6DE); - defaultColors.put(key_chat_messagePanelVoiceDelete, 0xff737373); - defaultColors.put(key_chat_messagePanelVoiceDuration, 0xffffffff); - defaultColors.put(key_chat_inlineResultIcon, 0xff5795cc); - defaultColors.put(key_chat_topPanelBackground, 0xffffffff); - defaultColors.put(key_chat_topPanelClose, 0xff8b969b); - defaultColors.put(key_chat_topPanelLine, 0xff6c9fd2); - defaultColors.put(key_chat_topPanelTitle, 0xff3a8ccf); - defaultColors.put(key_chat_topPanelMessage, 0xff878e91); - defaultColors.put(key_chat_reportSpam, 0xffcf5957); - defaultColors.put(key_chat_addContact, 0xff4a82b5); - defaultColors.put(key_chat_inLoader, 0xff72b5e8); - defaultColors.put(key_chat_inLoaderSelected, 0xff65abe0); - defaultColors.put(key_chat_outLoader, 0xff78c272); - defaultColors.put(key_chat_outLoaderSelected, 0xff6ab564); - defaultColors.put(key_chat_inLoaderPhoto, 0xffa2b8c8); - defaultColors.put(key_chat_mediaLoaderPhoto, 0x66000000); - defaultColors.put(key_chat_mediaLoaderPhotoSelected, 0x7f000000); - defaultColors.put(key_chat_mediaLoaderPhotoIcon, 0xffffffff); - defaultColors.put(key_chat_mediaLoaderPhotoIconSelected, 0xffd9d9d9); - defaultColors.put(key_chat_serviceBackgroundSelector, 0x20ffffff); - - defaultColors.put(key_profile_creatorIcon, 0xff3a95d5); - defaultColors.put(key_profile_actionIcon, 0xff81868a); - defaultColors.put(key_profile_actionBackground, 0xffffffff); - defaultColors.put(key_profile_actionPressedBackground, 0xfff2f2f2); - defaultColors.put(key_profile_verifiedBackground, 0xffb2d6f8); - defaultColors.put(key_profile_verifiedCheck, 0xff4983b8); - defaultColors.put(key_profile_title, 0xffffffff); - defaultColors.put(key_profile_status, 0xffd7eafa); - - defaultColors.put(key_profile_tabText, 0xff878c90); - defaultColors.put(key_profile_tabSelectedText, 0xff3a95d5); - defaultColors.put(key_profile_tabSelectedLine, 0xff4fa6e9); - defaultColors.put(key_profile_tabSelector, 0x0f000000); - - defaultColors.put(key_player_actionBarSelector, 0x0f000000); - defaultColors.put(key_player_actionBarTitle, 0xff2f3438); - defaultColors.put(key_player_actionBarSubtitle, 0xff8a8a8a); - defaultColors.put(key_player_actionBarItems, 0xff8a8a8a); - defaultColors.put(key_player_background, 0xffffffff); - defaultColors.put(key_player_time, 0xff8c9296); - defaultColors.put(key_player_progressBackground, 0xffEBEDF0); - defaultColors.put(key_player_progressCachedBackground, 0xffC5DCF0); - defaultColors.put(key_player_progress, 0xff54AAEB); - defaultColors.put(key_player_button, 0xff333333); - defaultColors.put(key_player_buttonActive, 0xff4ca8ea); - - defaultColors.put(key_sheet_scrollUp, 0xffe1e4e8); - defaultColors.put(key_sheet_other, 0xffc9cdd3); - - defaultColors.put(key_files_folderIcon, 0xffffffff); - defaultColors.put(key_files_folderIconBackground, 0xff5dafeb); - defaultColors.put(key_files_iconText, 0xffffffff); - - defaultColors.put(key_sessions_devicesImage, 0xff969696); - - defaultColors.put(key_passport_authorizeBackground, 0xff45abef); - defaultColors.put(key_passport_authorizeBackgroundSelected, 0xff409ddb); - defaultColors.put(key_passport_authorizeText, 0xffffffff); - - defaultColors.put(key_location_sendLocationBackground, 0xff469df6); - defaultColors.put(key_location_sendLocationIcon, 0xffffffff); - defaultColors.put(key_location_sendLocationText, 0xff1c8ad8); - defaultColors.put(key_location_sendLiveLocationBackground, 0xff4fc244); - defaultColors.put(key_location_sendLiveLocationIcon, 0xffffffff); - defaultColors.put(key_location_sendLiveLocationText, 0xff36ab24); - defaultColors.put(key_location_liveLocationProgress, 0xff359fe5); - defaultColors.put(key_location_placeLocationBackground, 0xff4ca8ea); - defaultColors.put(key_location_actionIcon, 0xff3a4045); - defaultColors.put(key_location_actionActiveIcon, 0xff4290e6); - defaultColors.put(key_location_actionBackground, 0xffffffff); - defaultColors.put(key_location_actionPressedBackground, 0xfff2f2f2); - - defaultColors.put(key_dialog_liveLocationProgress, 0xff359fe5); - - defaultColors.put(key_calls_callReceivedGreenIcon, 0xff00c853); - defaultColors.put(key_calls_callReceivedRedIcon, 0xffff4848); - - defaultColors.put(key_featuredStickers_addedIcon, 0xff50a8eb); - defaultColors.put(key_featuredStickers_buttonProgress, 0xffffffff); - defaultColors.put(key_featuredStickers_addButton, 0xff50a8eb); - defaultColors.put(key_featuredStickers_addButtonPressed, 0xff439bde); - defaultColors.put(key_featuredStickers_removeButtonText, 0xff5093d3); - defaultColors.put(key_featuredStickers_buttonText, 0xffffffff); - defaultColors.put(key_featuredStickers_unread, 0xff4da6ea); - - defaultColors.put(key_inappPlayerPerformer, 0xff2f3438); - defaultColors.put(key_inappPlayerTitle, 0xff2f3438); - defaultColors.put(key_inappPlayerBackground, 0xffffffff); - defaultColors.put(key_inappPlayerPlayPause, 0xff62b0eb); - defaultColors.put(key_inappPlayerClose, 0xff8b969b); - - defaultColors.put(key_returnToCallBackground, 0xff44a1e3); - defaultColors.put(key_returnToCallMutedBackground, 0xff9DA7B1); - defaultColors.put(key_returnToCallText, 0xffffffff); - - defaultColors.put(key_sharedMedia_startStopLoadIcon, 0xff36a2ee); - defaultColors.put(key_sharedMedia_linkPlaceholder, 0xfff0f3f5); - defaultColors.put(key_sharedMedia_linkPlaceholderText, 0xffb7bec3); - defaultColors.put(key_sharedMedia_photoPlaceholder, 0xffedf3f7); - - defaultColors.put(key_checkbox, 0xff5ec245); - defaultColors.put(key_checkboxCheck, 0xffffffff); - defaultColors.put(key_checkboxDisabled, 0xffb0b9c2); - - defaultColors.put(key_stickers_menu, 0xffb6bdc5); - defaultColors.put(key_stickers_menuSelector, 0x0f000000); - - defaultColors.put(key_changephoneinfo_image2, 0xff50a7ea); - - defaultColors.put(key_groupcreate_hintText, 0xffa1aab3); - defaultColors.put(key_groupcreate_cursor, 0xff52a3db); - defaultColors.put(key_groupcreate_sectionShadow, 0xff000000); - defaultColors.put(key_groupcreate_sectionText, 0xff7c8288); - defaultColors.put(key_groupcreate_spanText, 0xff222222); - defaultColors.put(key_groupcreate_spanBackground, 0xfff2f2f2); - defaultColors.put(key_groupcreate_spanDelete, 0xffffffff); - - defaultColors.put(key_contacts_inviteBackground, 0xff55be61); - defaultColors.put(key_contacts_inviteText, 0xffffffff); - - defaultColors.put(key_login_progressInner, 0xffe1eaf2); - defaultColors.put(key_login_progressOuter, 0xff62a0d0); - - defaultColors.put(key_picker_enabledButton, 0xff19a7e8); - defaultColors.put(key_picker_disabledButton, 0xff999999); - defaultColors.put(key_picker_badge, 0xff29b6f7); - defaultColors.put(key_picker_badgeText, 0xffffffff); - - defaultColors.put(key_chat_botSwitchToInlineText, 0xff4391cc); - - defaultColors.put(key_undo_background, 0xea272f38); - defaultColors.put(key_undo_cancelColor, 0xff85caff); - defaultColors.put(key_undo_infoColor, 0xffffffff); - - defaultColors.put(key_chat_outTextSelectionHighlight, 0x2E3F9923); - defaultColors.put(key_chat_inTextSelectionHighlight, 0x5062A9E3); - defaultColors.put(key_chat_TextSelectionCursor, 0xFF419FE8); - defaultColors.put(key_chat_outTextSelectionCursor, 0xFF419FE8); - defaultColors.put(key_chat_outBubbleLocationPlaceholder, 0x1e307311); - defaultColors.put(key_chat_inBubbleLocationPlaceholder, 0x1e506373); - defaultColors.put(key_chat_BlurAlpha, 0xFF000000); - - defaultColors.put(key_statisticChartSignature, 0x7f252529); - defaultColors.put(key_statisticChartSignatureAlpha, 0x7f252529); - defaultColors.put(key_statisticChartHintLine, 0x1a182D3B); - defaultColors.put(key_statisticChartActiveLine, 0x33000000); - defaultColors.put(key_statisticChartInactivePickerChart, 0x99e2eef9); - defaultColors.put(key_statisticChartActivePickerChart, 0xd8baccd9); - - defaultColors.put(key_statisticChartRipple, 0x2c7e9db7); - defaultColors.put(key_statisticChartBackZoomColor, 0xff108BE3); - defaultColors.put(key_statisticChartChevronColor, 0xffD2D5D7); - - defaultColors.put(key_statisticChartLine_blue, 0xff327FE5); - defaultColors.put(key_statisticChartLine_green, 0xff61C752); - defaultColors.put(key_statisticChartLine_red, 0xffE05356); - defaultColors.put(key_statisticChartLine_golden, 0xffEBA52D); - defaultColors.put(key_statisticChartLine_lightblue, 0xff58A8ED); - defaultColors.put(key_statisticChartLine_lightgreen, 0xff8FCF39); - defaultColors.put(key_statisticChartLine_orange, 0xffF28C39); - defaultColors.put(key_statisticChartLine_indigo, 0xff7F79F3); - defaultColors.put(key_statisticChartLine_purple, 0xff9F79E8); - defaultColors.put(key_statisticChartLine_cyan, 0xff40D0CA); - defaultColors.put(key_statisticChartLineEmpty, 0xFFEEEEEE); - - defaultColors.put(key_color_blue, 0xff327FE5); - defaultColors.put(key_color_green, 0xff61C752); - defaultColors.put(key_color_red, 0xffE05356); - defaultColors.put(key_color_yellow, 0xffEBA52D); - defaultColors.put(key_color_lightblue, 0xff58A8ED); - defaultColors.put(key_color_lightgreen, 0xff8FCF39); - defaultColors.put(key_color_orange, 0xffF28C39); - defaultColors.put(key_color_purple, 0xff9F79E8); - defaultColors.put(key_color_cyan, 0xff40D0CA); - - defaultColors.put(key_voipgroup_checkMenu, 0xff6BB6F9); - defaultColors.put(key_voipgroup_muteButton, 0xff77E55C); - defaultColors.put(key_voipgroup_muteButton2, 0xff7DDCAA); - defaultColors.put(key_voipgroup_muteButton3, 0xff56C7FE); - defaultColors.put(key_voipgroup_searchText, 0xffffffff); - defaultColors.put(key_voipgroup_searchPlaceholder, 0xff858D94); - defaultColors.put(key_voipgroup_searchBackground, 0xff303B47); - defaultColors.put(key_voipgroup_leaveCallMenu, 0xffFF7575); - defaultColors.put(key_voipgroup_scrollUp, 0xff394654); - defaultColors.put(key_voipgroup_soundButton, 0x7d2C414D); - defaultColors.put(key_voipgroup_soundButtonActive, 0x7d22A4EB); - defaultColors.put(key_voipgroup_soundButtonActiveScrolled, 0x8233B4FF); - defaultColors.put(key_voipgroup_soundButton2, 0x7d28593A); - defaultColors.put(key_voipgroup_soundButtonActive2, 0x7d18B751); - defaultColors.put(key_voipgroup_soundButtonActive2Scrolled, 0x8224BF46); - defaultColors.put(key_voipgroup_leaveButton, 0x7dF75C5C); - defaultColors.put(key_voipgroup_leaveButtonScrolled, 0x82D14D54); - defaultColors.put(key_voipgroup_connectingProgress, 0xff28BAFF); - defaultColors.put(key_voipgroup_disabledButton, 0xff1C2229); - defaultColors.put(key_voipgroup_disabledButtonActive, 0xff2C3A45); - defaultColors.put(key_voipgroup_disabledButtonActiveScrolled, 0x8277A1FC); - defaultColors.put(key_voipgroup_unmuteButton, 0xff539EF8); - defaultColors.put(key_voipgroup_unmuteButton2, 0xff66D4FB); - defaultColors.put(key_voipgroup_actionBarUnscrolled, 0xff191F26); - defaultColors.put(key_voipgroup_listViewBackgroundUnscrolled, 0xff222A33); - defaultColors.put(key_voipgroup_lastSeenTextUnscrolled, 0xff858D94); - defaultColors.put(key_voipgroup_mutedIconUnscrolled, 0xff7E868C); - defaultColors.put(key_voipgroup_actionBar, 0xff0F1317); - defaultColors.put(key_voipgroup_actionBarItems, 0xffffffff); - defaultColors.put(key_voipgroup_actionBarItemsSelector, 0x1eBADBFF); - defaultColors.put(key_voipgroup_mutedByAdminIcon, 0xffFF7070); - defaultColors.put(key_voipgroup_mutedIcon, 0xff6F7980); - defaultColors.put(key_voipgroup_lastSeenText, 0xff79838A); - defaultColors.put(key_voipgroup_nameText, 0xffffffff); - defaultColors.put(key_voipgroup_listViewBackground, 0xff1C2229); - defaultColors.put(key_voipgroup_dialogBackground, 0xff1C2229); - defaultColors.put(key_voipgroup_listeningText, 0xff4DB8FF); - defaultColors.put(key_voipgroup_speakingText, 0xff77EE7D); - defaultColors.put(key_voipgroup_listSelector, 0x0effffff); - defaultColors.put(key_voipgroup_inviteMembersBackground, 0xff222A33); - defaultColors.put(key_voipgroup_overlayBlue1, 0xff2BCEFF); - defaultColors.put(key_voipgroup_overlayBlue2, 0xff0976E3); - defaultColors.put(key_voipgroup_overlayGreen1, 0xff12B522); - defaultColors.put(key_voipgroup_overlayGreen2, 0xff00D6C1); - defaultColors.put(key_voipgroup_topPanelBlue1, 0xff60C7FB); - defaultColors.put(key_voipgroup_topPanelBlue2, 0xff519FF9); - defaultColors.put(key_voipgroup_topPanelGreen1, 0xff52CE5D); - defaultColors.put(key_voipgroup_topPanelGreen2, 0xff00B1C0); - defaultColors.put(key_voipgroup_topPanelGray, 0xff8599aa); - - defaultColors.put(key_voipgroup_overlayAlertGradientMuted, 0xff236D92); - defaultColors.put(key_voipgroup_overlayAlertGradientMuted2, 0xff2C4D6B); - defaultColors.put(key_voipgroup_overlayAlertGradientUnmuted, 0xff0C8A8C); - defaultColors.put(key_voipgroup_overlayAlertGradientUnmuted2, 0xff284C75); - defaultColors.put(key_voipgroup_mutedByAdminGradient, 0xff57A4FE); - defaultColors.put(key_voipgroup_mutedByAdminGradient2, 0xffF05459); - defaultColors.put(key_voipgroup_mutedByAdminGradient3, 0xff766EE9); - defaultColors.put(key_voipgroup_overlayAlertMutedByAdmin, 0xff67709E); - defaultColors.put(key_voipgroup_overlayAlertMutedByAdmin2, 0xff2F5078); - defaultColors.put(key_voipgroup_mutedByAdminMuteButton, 0x7F78A3FF); - defaultColors.put(key_voipgroup_mutedByAdminMuteButtonDisabled, 0x3378A3FF); - defaultColors.put(key_voipgroup_windowBackgroundWhiteInputField, 0xffdbdbdb); - defaultColors.put(key_voipgroup_windowBackgroundWhiteInputFieldActivated, 0xff37a9f0); - - defaultColors.put(key_chat_outReactionButtonBackground, 0xff78c272); - defaultColors.put(key_chat_inReactionButtonBackground, 0xff72b5e8); - defaultColors.put(key_chat_inReactionButtonText, 0xff3a8ccf); - defaultColors.put(key_chat_outReactionButtonText, 0xff55ab4f); - defaultColors.put(key_chat_inReactionButtonTextSelected, 0xffffffff); - defaultColors.put(key_chat_outReactionButtonTextSelected, 0xffffffff); - - defaultColors.put(key_premiumGradient0, 0xff4ACD43); - defaultColors.put(key_premiumGradient1, 0xff55A5FF); - defaultColors.put(key_premiumGradient2, 0xffA767FF); - defaultColors.put(key_premiumGradient3, 0xffDB5C9D); - defaultColors.put(key_premiumGradient4, 0xffF38926); - - defaultColors.put(key_premiumGradientBackground1, 0xff55A5FF); - defaultColors.put(key_premiumGradientBackground2, 0xffA767FF); - defaultColors.put(key_premiumGradientBackground3, 0xffDB5C9D); - defaultColors.put(key_premiumGradientBackground4, 0xffF38926); - defaultColors.put(key_premiumGradientBackgroundOverlay, Color.WHITE); - defaultColors.put(key_premiumStartGradient1, 0xffFFFFFF); - defaultColors.put(key_premiumStartGradient2, 0xffE3ECFA); - defaultColors.put(key_premiumStartSmallStarsColor, ColorUtils.setAlphaComponent(Color.WHITE, 90)); - defaultColors.put(key_premiumStartSmallStarsColor2, ColorUtils.setAlphaComponent(Color.WHITE, 90)); - defaultColors.put(key_premiumGradientBottomSheet1, 0xff5B9DE7); - defaultColors.put(key_premiumGradientBottomSheet2, 0xffAB87DD); - defaultColors.put(key_premiumGradientBottomSheet3, 0xffE794BE); - defaultColors.put(key_topics_unreadCounter, 0xff4ecc5e); - defaultColors.put(key_topics_unreadCounterMuted, 0xff8b8d8f); + defaultColors = ThemeColors.createDefaultColors(); fallbackKeys.put(key_chat_inAdminText, key_chat_inTimeText); fallbackKeys.put(key_chat_inAdminSelectedText, key_chat_inTimeSelectedText); @@ -4885,7 +4187,6 @@ public void run() { fallbackKeys.put(key_checkboxDisabled, key_chats_unreadCounterMuted); fallbackKeys.put(key_chat_status, key_actionBarDefaultSubtitle); fallbackKeys.put(key_chat_inGreenCall, key_calls_callReceivedGreenIcon); - fallbackKeys.put(key_chat_inRedCall, key_calls_callReceivedRedIcon); fallbackKeys.put(key_chat_outGreenCall, key_calls_callReceivedGreenIcon); fallbackKeys.put(key_actionBarTabActiveText, key_actionBarDefaultTitle); fallbackKeys.put(key_actionBarTabUnactiveText, key_actionBarDefaultSubtitle); @@ -4895,8 +4196,6 @@ public void run() { fallbackKeys.put(key_chats_menuTopBackgroundCats, key_avatar_backgroundActionBarBlue); fallbackKeys.put(key_chat_outLinkSelectBackground, key_chat_linkSelectBackground); fallbackKeys.put(key_actionBarDefaultSubmenuSeparator, key_windowBackgroundGray); - //fallbackKeys.put(key_chat_attachActiveTab, 0xff33a7f5); - //fallbackKeys.put(key_chat_attachUnactiveTab, 0xff92999e); fallbackKeys.put(key_chat_attachPermissionImage, key_dialogTextBlack); fallbackKeys.put(key_chat_attachPermissionMark, key_chat_sentError); fallbackKeys.put(key_chat_attachPermissionText, key_dialogTextBlack); @@ -4975,10 +4274,19 @@ public void run() { fallbackKeys.put(key_statisticChartLine_indigo, key_color_purple); fallbackKeys.put(key_statisticChartLine_cyan, key_color_cyan); - themeAccentExclusionKeys.addAll(Arrays.asList(keys_avatar_background)); - themeAccentExclusionKeys.addAll(Arrays.asList(keys_avatar_background2)); - themeAccentExclusionKeys.addAll(Arrays.asList(keys_avatar_nameInMessage)); - themeAccentExclusionKeys.addAll(Arrays.asList(keys_colors)); + for (int i = 0; i < keys_avatar_background.length; i++) { + themeAccentExclusionKeys.add(keys_avatar_background[i]); + } + for (int i = 0; i < keys_avatar_background2.length; i++) { + themeAccentExclusionKeys.add(keys_avatar_background2[i]); + } + for (int i = 0; i < keys_avatar_nameInMessage.length; i++) { + themeAccentExclusionKeys.add(keys_avatar_nameInMessage[i]); + } + for (int i = 0; i < keys_colors.length; i++) { + themeAccentExclusionKeys.add(keys_colors[i]); + } + themeAccentExclusionKeys.add(key_chat_attachFileBackground); themeAccentExclusionKeys.add(key_chat_attachGalleryBackground); themeAccentExclusionKeys.add(key_chat_attachFileText); @@ -5054,82 +4362,24 @@ public void run() { themeAccentExclusionKeys.add(key_voipgroup_mutedByAdminMuteButtonDisabled); themeAccentExclusionKeys.add(key_voipgroup_windowBackgroundWhiteInputField); themeAccentExclusionKeys.add(key_voipgroup_windowBackgroundWhiteInputFieldActivated); + themeAccentExclusionKeys.add(key_premiumGradient0); themeAccentExclusionKeys.add(key_premiumGradient1); themeAccentExclusionKeys.add(key_premiumGradient2); themeAccentExclusionKeys.add(key_premiumGradient3); themeAccentExclusionKeys.add(key_premiumGradient4); - - myMessagesBubblesColorKeys.add(key_chat_outBubble); - myMessagesBubblesColorKeys.add(key_chat_outBubbleSelected); - myMessagesBubblesColorKeys.add(key_chat_outBubbleShadow); - myMessagesBubblesColorKeys.add(key_chat_outBubbleGradient1); - - myMessagesColorKeys.add(key_chat_outGreenCall); - myMessagesColorKeys.add(key_chat_outSentCheck); - myMessagesColorKeys.add(key_chat_outSentCheckSelected); - myMessagesColorKeys.add(key_chat_outSentCheckRead); - myMessagesColorKeys.add(key_chat_outSentCheckReadSelected); - myMessagesColorKeys.add(key_chat_outSentClock); - myMessagesColorKeys.add(key_chat_outSentClockSelected); - myMessagesColorKeys.add(key_chat_outMediaIcon); - myMessagesColorKeys.add(key_chat_outMediaIconSelected); - myMessagesColorKeys.add(key_chat_outViews); - myMessagesColorKeys.add(key_chat_outViewsSelected); - myMessagesColorKeys.add(key_chat_outMenu); - myMessagesColorKeys.add(key_chat_outMenuSelected); - myMessagesColorKeys.add(key_chat_outInstant); - myMessagesColorKeys.add(key_chat_outInstantSelected); - myMessagesColorKeys.add(key_chat_outPreviewInstantText); - myMessagesColorKeys.add(key_chat_outForwardedNameText); - myMessagesColorKeys.add(key_chat_outViaBotNameText); - myMessagesColorKeys.add(key_chat_outReplyLine); - myMessagesColorKeys.add(key_chat_outReplyNameText); - myMessagesColorKeys.add(key_chat_outReplyMessageText); - myMessagesColorKeys.add(key_chat_outReplyMediaMessageText); - myMessagesColorKeys.add(key_chat_outReplyMediaMessageSelectedText); - myMessagesColorKeys.add(key_chat_outPreviewLine); - myMessagesColorKeys.add(key_chat_outSiteNameText); - myMessagesColorKeys.add(key_chat_outContactNameText); - myMessagesColorKeys.add(key_chat_outContactPhoneText); - myMessagesColorKeys.add(key_chat_outContactPhoneSelectedText); - myMessagesColorKeys.add(key_chat_outAudioProgress); - myMessagesColorKeys.add(key_chat_outAudioSelectedProgress); - myMessagesColorKeys.add(key_chat_outTimeText); - myMessagesColorKeys.add(key_chat_outTimeSelectedText); - myMessagesColorKeys.add(key_chat_outAudioPerformerText); - myMessagesColorKeys.add(key_chat_outAudioPerformerSelectedText); - myMessagesColorKeys.add(key_chat_outAudioTitleText); - myMessagesColorKeys.add(key_chat_outAudioDurationText); - myMessagesColorKeys.add(key_chat_outAudioDurationSelectedText); - myMessagesColorKeys.add(key_chat_outAudioSeekbar); - myMessagesColorKeys.add(key_chat_outAudioCacheSeekbar); - myMessagesColorKeys.add(key_chat_outAudioSeekbarSelected); - myMessagesColorKeys.add(key_chat_outAudioSeekbarFill); - myMessagesColorKeys.add(key_chat_outVoiceSeekbar); - myMessagesColorKeys.add(key_chat_outVoiceSeekbarSelected); - myMessagesColorKeys.add(key_chat_outVoiceSeekbarFill); - myMessagesColorKeys.add(key_chat_outFileProgress); - myMessagesColorKeys.add(key_chat_outFileProgressSelected); - myMessagesColorKeys.add(key_chat_outFileNameText); - myMessagesColorKeys.add(key_chat_outFileInfoText); - myMessagesColorKeys.add(key_chat_outFileInfoSelectedText); - myMessagesColorKeys.add(key_chat_outFileBackground); - myMessagesColorKeys.add(key_chat_outFileBackgroundSelected); - myMessagesColorKeys.add(key_chat_outVenueInfoText); - myMessagesColorKeys.add(key_chat_outVenueInfoSelectedText); - myMessagesColorKeys.add(key_chat_outLoader); - myMessagesColorKeys.add(key_chat_outLoaderSelected); - myMessagesColorKeys.add(key_chat_outLocationIcon); - myMessagesColorKeys.add(key_chat_outContactBackground); - myMessagesColorKeys.add(key_chat_outContactIcon); - myMessagesColorKeys.add(key_chat_messageTextOut); - myMessagesColorKeys.add(key_chat_messageLinkOut); + themeAccentExclusionKeys.add(key_premiumGradientBackground1); + themeAccentExclusionKeys.add(key_premiumGradientBackground2); + themeAccentExclusionKeys.add(key_premiumGradientBackground3); + themeAccentExclusionKeys.add(key_premiumGradientBackground4); + themeAccentExclusionKeys.add(key_premiumStartSmallStarsColor); + themeAccentExclusionKeys.add(key_premiumStartGradient1); + themeAccentExclusionKeys.add(key_premiumStartGradient2); themes = new ArrayList<>(); otherThemes = new ArrayList<>(); themesDict = new HashMap<>(); - currentColorsNoAccent = new HashMap<>(); - currentColors = new HashMap<>(); + currentColorsNoAccent = new SparseIntArray(); + currentColors = new SparseIntArray(); SharedPreferences themeConfig = ApplicationLoader.applicationContext.getSharedPreferences("themeconfig", Activity.MODE_PRIVATE); @@ -5626,7 +4876,6 @@ public void run() { } applyTheme(applyingTheme, false, false, switchToTheme == 2); AndroidUtilities.runOnUIThread(Theme::checkAutoNightThemeConditions); - } private static void sortAccents(ThemeInfo info) { @@ -6139,6 +5388,9 @@ public static Drawable createSelectorWithBackgroundDrawable(int backgroundColor, public static Drawable getSelectorDrawable(boolean whiteBackground) { return getSelectorDrawable(getColor(key_listSelector), whiteBackground); } + public static Drawable getSelectorDrawable(boolean whiteBackground, ResourcesProvider resourcesProvider) { + return getSelectorDrawable(getColor(key_listSelector, resourcesProvider), whiteBackground); + } public static Drawable getSelectorDrawable(int color, boolean whiteBackground) { if (whiteBackground) { @@ -6148,8 +5400,8 @@ public static Drawable getSelectorDrawable(int color, boolean whiteBackground) { } } - public static Drawable getSelectorDrawable(int color, String backgroundColor) { - if (backgroundColor != null) { + public static Drawable getSelectorDrawable(int color, int backgroundColor) { + if (backgroundColor >= 0) { if (Build.VERSION.SDK_INT >= 21) { Drawable maskDrawable = new ColorDrawable(0xffffffff); ColorStateList colorStateList = new ColorStateList( @@ -6341,15 +5593,15 @@ public static class AdaptiveRipple { public static final float RADIUS_TO_BOUNDS = -1; public static final float RADIUS_OUT_BOUNDS = -2; - private static final String defaultBackgroundColorKey = Theme.key_windowBackgroundWhite; + private static final int defaultBackgroundColorKey = Theme.key_windowBackgroundWhite; public static Drawable circle() { return circle(Theme.getColor(defaultBackgroundColorKey), RADIUS_TO_BOUNDS); } - public static Drawable circle(String backgroundColorKey) { + public static Drawable circleByKey(int backgroundColorKey) { return circle(Theme.getColor(backgroundColorKey), RADIUS_TO_BOUNDS); } - public static Drawable circle(String backgroundColorKey, float radius) { + public static Drawable circleByKey(int backgroundColorKey, float radius) { return circle(Theme.getColor(backgroundColorKey), radius); } public static Drawable circle(int backgroundColor) { @@ -6365,16 +5617,16 @@ public static Drawable filledCircle() { public static Drawable filledCircle(Drawable background) { return filledCircle(background, Theme.getColor(defaultBackgroundColorKey), RADIUS_TO_BOUNDS); } - public static Drawable filledCircle(String backgroundColorKey) { + public static Drawable filledCircleByKey(int backgroundColorKey) { return filledCircle(null, Theme.getColor(backgroundColorKey), RADIUS_TO_BOUNDS); } - public static Drawable filledCircle(Drawable background, String backgroundColorKey) { + public static Drawable filledCircle(Drawable background, int backgroundColorKey) { return filledCircle(background, Theme.getColor(backgroundColorKey), RADIUS_TO_BOUNDS); } - public static Drawable filledCircle(String backgroundColorKey, float radius) { + public static Drawable filledCircleByKey(int backgroundColorKey, float radius) { return filledCircle(null, Theme.getColor(backgroundColorKey), radius); } - public static Drawable filledCircle(Drawable background, String backgroundColorKey, float radius) { + public static Drawable filledCircleByKey(Drawable background, int backgroundColorKey, float radius) { return filledCircle(background, Theme.getColor(backgroundColorKey), radius); } public static Drawable filledCircle(int backgroundColor) { @@ -6390,11 +5642,11 @@ public static Drawable filledCircle(Drawable background, int backgroundColor, fl public static Drawable rect() { return rect(Theme.getColor(defaultBackgroundColorKey)); } - public static Drawable rect(String backgroundColorKey) { + public static Drawable rectByKey(int backgroundColorKey) { return rect(Theme.getColor(backgroundColorKey)); } - public static Drawable rect(String backgroundColorKey, float ...radii) { + public static Drawable rectByKey(int backgroundColorKey, float ...radii) { return rect(Theme.getColor(backgroundColorKey), radii); } public static Drawable rect(int backgroundColor) { @@ -6411,16 +5663,16 @@ public static Drawable filledRect(Drawable background) { int backgroundColor = background instanceof ColorDrawable ? ((ColorDrawable) background).getColor() : Theme.getColor(defaultBackgroundColorKey); return filledRect(background, backgroundColor, 0); } - public static Drawable filledRect(String backgroundColorKey) { + public static Drawable filledRectByKey(int backgroundColorKey) { return filledRect(Theme.getColor(backgroundColorKey)); } - public static Drawable filledRect(Drawable background, String backgroundColorKey) { + public static Drawable filledRectByKey(Drawable background, int backgroundColorKey) { return filledRect(background, Theme.getColor(backgroundColorKey)); } - public static Drawable filledRect(String backgroundColorKey, float ...radii) { + public static Drawable filledRectByKey(int backgroundColorKey, float ...radii) { return filledRect(Theme.getColor(backgroundColorKey), radii); } - public static Drawable filledRect(Drawable background, String backgroundColorKey, float ...radii) { + public static Drawable filledRectByKey(Drawable background, int backgroundColorKey, float ...radii) { return filledRect(background, Theme.getColor(backgroundColorKey), radii); } public static Drawable filledRect(int backgroundColor) { @@ -6683,7 +5935,6 @@ public static void setMaskDrawableRad(Drawable rippleDrawable, int top, int bott Drawable layer = drawable.getDrawable(a); if (layer instanceof RippleRadMaskDrawable) { ((RippleRadMaskDrawable) layer).setRadius(top, bottom); -// drawable.setDrawableByLayerId(android.R.id.mask, new RippleRadMaskDrawable(top, bottom)); break; } } @@ -6701,7 +5952,6 @@ public static void setMaskDrawableRad(Drawable rippleDrawable, float topLeftRad, Drawable layer = drawable.getDrawable(a); if (layer instanceof RippleRadMaskDrawable) { ((RippleRadMaskDrawable) layer).setRadius(topLeftRad, topRightRad, bottomRightRad, bottomLeftRad); -// drawable.setDrawableByLayerId(android.R.id.mask, new RippleRadMaskDrawable(top, bottom)); break; } } @@ -6797,7 +6047,7 @@ public static boolean isCustomWallpaperColor() { public static void resetCustomWallpaper(boolean temporary) { if (temporary) { isApplyingAccent = false; - reloadWallpaper(); + reloadWallpaper(true); } else { currentTheme.setOverrideWallpaper(null); } @@ -6812,7 +6062,7 @@ public static ThemeInfo fillThemeValues(File file, String themeName, TLRPC.TL_th themeInfo.account = UserConfig.selectedAccount; String[] wallpaperLink = new String[1]; - HashMap colors = getThemeFileValues(new File(themeInfo.pathToFile), null, wallpaperLink); + SparseIntArray colors = getThemeFileValues(new File(themeInfo.pathToFile), null, wallpaperLink); checkIsDark(colors, themeInfo); if (!TextUtils.isEmpty(wallpaperLink[0])) { @@ -6976,7 +6226,7 @@ private static void applyTheme(ThemeInfo themeInfo, boolean save, boolean remove SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putString("theme", themeInfo.getKey()); - editor.commit(); + editor.apply(); } String[] wallpaperLink = new String[1]; if (themeInfo.assetName != null) { @@ -6984,8 +6234,7 @@ private static void applyTheme(ThemeInfo themeInfo, boolean save, boolean remove } else { currentColorsNoAccent = getThemeFileValues(new File(themeInfo.pathToFile), null, wallpaperLink); } - Integer offset = currentColorsNoAccent.get("wallpaperFileOffset"); - themedWallpaperFileOffset = offset != null ? offset : -1; + themedWallpaperFileOffset = currentColorsNoAccent.get(key_wallpaperFileOffset, -1); if (!TextUtils.isEmpty(wallpaperLink[0])) { themedWallpaperLink = wallpaperLink[0]; String newPathToFile = new File(ApplicationLoader.getFilesDirFixed(), Utilities.MD5(themedWallpaperLink) + ".wp").getAbsolutePath(); @@ -7109,8 +6358,7 @@ private static void applyThemeInBackground(ThemeInfo themeInfo, boolean save, bo String[] wallpaperLink = new String[1]; Runnable next = () -> { try { - Integer offset = currentColorsNoAccent.get("wallpaperFileOffset"); - themedWallpaperFileOffset = offset != null ? offset : -1; + themedWallpaperFileOffset = currentColorsNoAccent.get(key_wallpaperFileOffset, -1); if (!TextUtils.isEmpty(wallpaperLink[0])) { themedWallpaperLink = wallpaperLink[0]; String newPathToFile = new File(ApplicationLoader.getFilesDirFixed(), Utilities.MD5(themedWallpaperLink) + ".wp").getAbsolutePath(); @@ -7268,15 +6516,15 @@ public static void refreshThemeColors() { } public static void refreshThemeColors(boolean bg, boolean messages) { - currentColors.clear(); - currentColors.putAll(currentColorsNoAccent); + currentColors = currentColorsNoAccent.clone(); shouldDrawGradientIcons = true; ThemeAccent accent = currentTheme.getAccent(false); if (accent != null) { shouldDrawGradientIcons = accent.fillAccentColors(currentColorsNoAccent, currentColors); } if (!messages) { - reloadWallpaper(); + boolean async = !(LaunchActivity.getLastFragment() instanceof ChatActivity); + reloadWallpaper(async); } applyCommonTheme(); applyDialogsTheme(); @@ -7565,7 +6813,7 @@ private static void saveOtherThemes(boolean full, boolean migration) { } } - public static HashMap getDefaultColors() { + public static int[] getDefaultColors() { return defaultColors; } @@ -7924,10 +7172,11 @@ public static void saveCurrentTheme(ThemeInfo themeInfo, boolean finalSave, bool themedWallpaper = wallpaper; } ThemeAccent accent = currentTheme.getAccent(false); - HashMap colorsMap = currentTheme.firstAccentIsDefault && accent.id == DEFALT_THEME_ACCENT_ID ? defaultColors : currentColors; + boolean useDefaultColors = currentTheme.firstAccentIsDefault && accent.id == DEFALT_THEME_ACCENT_ID; + SparseIntArray colorsMap = useDefaultColors ? null : currentColors; StringBuilder result = new StringBuilder(); - if (colorsMap != defaultColors) { + if (!useDefaultColors) { int outBubbleColor = accent != null ? accent.myMessagesAccentColor : 0; int outBubbleGradient1 = accent != null ? accent.myMessagesGradientAccentColor1 : 0; int outBubbleGradient2 = accent != null ? accent.myMessagesGradientAccentColor2 : 0; @@ -7944,14 +7193,28 @@ public static void saveCurrentTheme(ThemeInfo themeInfo, boolean finalSave, bool colorsMap.put(key_chat_outBubbleGradientAnimated, accent != null && accent.myMessagesAnimated ? 1 : 0); } } - for (HashMap.Entry entry : colorsMap.entrySet()) { - String key = entry.getKey(); - if (wallpaperToSave instanceof BitmapDrawable || wallpaperLink != null) { - if (key_chat_wallpaper.equals(key) || key_chat_wallpaper_gradient_to1.equals(key) || key_chat_wallpaper_gradient_to2.equals(key) || key_chat_wallpaper_gradient_to3.equals(key)) { - continue; + if (useDefaultColors) { + for (int i = 0; i < defaultColors.length; i++) { + int key = i; + int color = defaultColors[i]; + if (wallpaperToSave instanceof BitmapDrawable || wallpaperLink != null) { + if (key_chat_wallpaper == key || key_chat_wallpaper_gradient_to1 == key || key_chat_wallpaper_gradient_to2 == key || key_chat_wallpaper_gradient_to3 == key) { + continue; + } + } + result.append(ThemeColors.getStringName(key)).append("=").append(color).append("\n"); + } + } else { + for (int i = 0; i < colorsMap.size(); i++) { + int key = colorsMap.keyAt(i); + int color = colorsMap.valueAt(i); + if (wallpaperToSave instanceof BitmapDrawable || wallpaperLink != null) { + if (key_chat_wallpaper == key || key_chat_wallpaper_gradient_to1 == key || key_chat_wallpaper_gradient_to2 == key || key_chat_wallpaper_gradient_to3 == key) { + continue; + } } + result.append(ThemeColors.getStringName(key)).append("=").append(color).append("\n"); } - result.append(key).append("=").append(entry.getValue()).append("\n"); } FileOutputStream stream = null; try { @@ -7996,14 +7259,14 @@ public static void saveCurrentTheme(ThemeInfo themeInfo, boolean finalSave, bool if (currentTheme != currentNightTheme) { currentDayTheme = currentTheme; } - if (colorsMap == defaultColors) { + if (useDefaultColors) { currentColorsNoAccent.clear(); refreshThemeColors(); } SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putString("theme", currentDayTheme.getKey()); - editor.commit(); + editor.apply(); } } catch (Exception e) { FileLog.e(e); @@ -8394,22 +7657,23 @@ public static File getAssetFile(String assetName) { return file; } - public static int getPreviewColor(HashMap colors, String key) { - Integer color = colors.get(key); - if (color == null) { - color = defaultColors.get(key); + public static int getPreviewColor(SparseIntArray colors, int key) { + int index = colors.indexOfKey(key); + if (index >= 0) { + return colors.valueAt(index); + } else { + return defaultColors[key]; } - return color; } public static String createThemePreviewImage(String pathToFile, String wallpaperPath, Theme.ThemeAccent accent) { try { String[] wallpaperLink = new String[1]; - HashMap colors = getThemeFileValues(new File(pathToFile), null, wallpaperLink); + SparseIntArray colors = getThemeFileValues(new File(pathToFile), null, wallpaperLink); if (accent != null) { checkIsDark(colors, accent.parentTheme); } - Integer wallpaperFileOffset = colors.get("wallpaperFileOffset"); + int wallpaperFileOffset = currentColorsNoAccent.get(key_wallpaperFileOffset, -1); Bitmap bitmap = Bitmaps.createBitmap(560, 678, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); @@ -8421,13 +7685,13 @@ public static String createThemePreviewImage(String pathToFile, String wallpaper int messageFieldIconColor = getPreviewColor(colors, key_chat_messagePanelIcons); int messageInColor = getPreviewColor(colors, key_chat_inBubble); int messageOutColor = getPreviewColor(colors, key_chat_outBubble); - Integer messageOutGradientColor = colors.get(key_chat_outBubbleGradient1); - Integer backgroundColor = colors.get(key_chat_wallpaper); - Integer gradientToColor1 = colors.get(key_chat_wallpaper_gradient_to1); - Integer gradientToColor2 = colors.get(key_chat_wallpaper_gradient_to2); - Integer gradientToColor3 = colors.get(key_chat_wallpaper_gradient_to3); + int messageOutGradientColor = colors.get(key_chat_outBubbleGradient1); + int backgroundColor = colors.get(key_chat_wallpaper); + int gradientToColor1 = colors.get(key_chat_wallpaper_gradient_to1); + int gradientToColor2 = colors.get(key_chat_wallpaper_gradient_to2); + int gradientToColor3 = colors.get(key_chat_wallpaper_gradient_to3); - int defaultBackgroundColor = backgroundColor != null ? backgroundColor : 0; + int defaultBackgroundColor = backgroundColor; int backgroundOverrideColor = accent != null ? (int) accent.backgroundOverrideColor : 0; int backColor; if (backgroundOverrideColor == 0 && accent != null && accent.backgroundOverrideColor != 0) { @@ -8436,7 +7700,7 @@ public static String createThemePreviewImage(String pathToFile, String wallpaper backColor = backgroundOverrideColor != 0 ? backgroundOverrideColor : defaultBackgroundColor; } - int defaultBackgroundGradient1 = gradientToColor1 != null ? gradientToColor1 : 0; + int defaultBackgroundGradient1 = gradientToColor1; int backgroundGradientOverrideColor1 = accent != null ? (int) accent.backgroundGradientOverrideColor1 : 0; int color1; if (backgroundGradientOverrideColor1 == 0 && accent != null && accent.backgroundGradientOverrideColor1 != 0) { @@ -8444,7 +7708,7 @@ public static String createThemePreviewImage(String pathToFile, String wallpaper } else { color1 = backgroundGradientOverrideColor1 != 0 ? backgroundGradientOverrideColor1 : defaultBackgroundGradient1; } - int defaultBackgroundGradient2 = gradientToColor2 != null ? gradientToColor2 : 0; + int defaultBackgroundGradient2 = gradientToColor2; int backgroundGradientOverrideColor2 = accent != null ? (int) accent.backgroundGradientOverrideColor2 : 0; int color2; if (backgroundGradientOverrideColor2 == 0 && accent != null && accent.backgroundGradientOverrideColor2 != 0) { @@ -8452,7 +7716,7 @@ public static String createThemePreviewImage(String pathToFile, String wallpaper } else { color2 = backgroundGradientOverrideColor2 != 0 ? backgroundGradientOverrideColor2 : defaultBackgroundGradient2; } - int defaultBackgroundGradient3 = gradientToColor3 != null ? gradientToColor3 : 0; + int defaultBackgroundGradient3 = gradientToColor3; int backgroundGradientOverrideColor3 = accent != null ? (int) accent.backgroundGradientOverrideColor3 : 0; int color3; if (backgroundGradientOverrideColor3 == 0 && accent != null && accent.backgroundGradientOverrideColor3 != 0) { @@ -8496,16 +7760,17 @@ public static String createThemePreviewImage(String pathToFile, String wallpaper for (int a = 0; a < 2; a++) { msgDrawable[a] = new MessageDrawable(MessageDrawable.TYPE_PREVIEW, a == 1, false) { @Override - protected int getColor(String key) { - Integer color = colors.get(key); - if (color == null) { - color = defaultColors.get(key); + protected int getColor(int key) { + int index = colors.indexOfKey(key); + if (index > 0) { + return colors.valueAt(index); + } else { + return defaultColors[key]; } - return color; } @Override - protected Integer getCurrentColor(String key) { + protected int getCurrentColor(int key) { return colors.get(key); } }; @@ -8529,11 +7794,17 @@ protected Integer getCurrentColor(String key) { options.inSampleSize *= 2; } while (options.inSampleSize < scale); } + options.inPreferredConfig = Bitmap.Config.ALPHA_8; options.inJustDecodeBounds = false; Bitmap wallpaper = BitmapFactory.decodeFile(wallpaperPath, options); if (wallpaper != null) { if (color2 != 0 && accent != null) { MotionBackgroundDrawable wallpaperDrawable = new MotionBackgroundDrawable(backColor, color1, color2, color3, true); + if (bitmap != null && bitmap.getConfig() != Bitmap.Config.ALPHA_8) { + Bitmap toRecycle = bitmap; + bitmap = bitmap.copy(Bitmap.Config.ALPHA_8, false); + toRecycle.recycle(); + } wallpaperDrawable.setPatternBitmap((int) (accent.patternIntensity * 100), wallpaper); wallpaperDrawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight()); wallpaperDrawable.draw(canvas); @@ -8559,12 +7830,11 @@ protected Integer getCurrentColor(String key) { if (color2 != 0) { wallpaperDrawable = new MotionBackgroundDrawable(backColor, color1, color2, color3, true); } else { - Integer gradientRotation = colors.get(key_chat_wallpaper_gradient_rotation); - if (gradientRotation == null) { + int gradientRotation = colors.get(key_chat_wallpaper_gradient_rotation, -1); + if (gradientRotation == -1) { gradientRotation = 45; } - int gradientToColorInt = gradientToColor2 == null ? 0 : gradientToColor2; - final int[] gradientColors = {backColor, gradientToColorInt}; + final int[] gradientColors = {backColor, gradientToColor2}; wallpaperDrawable = BackgroundGradientDrawable.createDitheredGradientBitmapDrawable(gradientRotation, gradientColors, bitmap.getWidth(), bitmap.getHeight() - 120); quality = 90; } @@ -8572,7 +7842,7 @@ protected Integer getCurrentColor(String key) { wallpaperDrawable.setBounds(0, 120, bitmap.getWidth(), bitmap.getHeight() - 120); wallpaperDrawable.draw(canvas); hasBackground = true; - } else if (wallpaperFileOffset != null && wallpaperFileOffset >= 0 || !TextUtils.isEmpty(wallpaperLink[0])) { + } else if (wallpaperFileOffset >= 0 || !TextUtils.isEmpty(wallpaperLink[0])) { FileInputStream stream = null; File pathToWallpaper = null; try { @@ -8689,7 +7959,7 @@ protected Integer getCurrentColor(String key) { return null; } - private static void checkIsDark(HashMap colors, Theme.ThemeInfo info) { + private static void checkIsDark(SparseIntArray colors, Theme.ThemeInfo info) { if (info == null || colors == null) { return; } @@ -8704,19 +7974,19 @@ private static void checkIsDark(HashMap colors, Theme.ThemeInfo } } - public static void getThemeFileValuesInBackground(File file, String assetName, String[] wallpaperLink, Utilities.Callback> onDone) { + public static void getThemeFileValuesInBackground(File file, String assetName, String[] wallpaperLink, Utilities.Callback onDone) { Utilities.themeQueue.postRunnable(() -> { onDone.run(getThemeFileValues(file, assetName, wallpaperLink)); }); } - public static HashMap getThemeFileValues(File file, String assetName, String[] wallpaperLink) { + public static SparseIntArray getThemeFileValues(File file, String assetName, String[] wallpaperLink) { return getThemeFileValues(file, assetName, wallpaperLink, false); } - public static HashMap getThemeFileValues(File file, String assetName, String[] wallpaperLink, boolean monetAmoled) { + public static SparseIntArray getThemeFileValues(File file, String assetName, String[] wallpaperLink, boolean monetAmoled) { FileInputStream stream = null; - HashMap stringMap = new HashMap<>(500); + SparseIntArray stringMap = new SparseIntArray(); try { byte[] bytes = new byte[1024]; int currentPosition = 0; @@ -8759,7 +8029,10 @@ public static HashMap getThemeFileValues(File file, String asse } else { value = Utilities.parseInt(param); } - stringMap.put(key, value); + int keyFromString = ThemeColors.stringKeyToInt(key); + if (keyFromString >= 0) { + stringMap.put(keyFromString, value); + } } } start += len; @@ -8774,7 +8047,7 @@ public static HashMap getThemeFileValues(File file, String asse break; } } - stringMap.put("wallpaperFileOffset", wallpaperFileOffset); + stringMap.put(key_wallpaperFileOffset, wallpaperFileOffset); } catch (Throwable e) { FileLog.e(e); } finally { @@ -8826,7 +8099,7 @@ public static void createCommonResources(Context context) { avatarDrawables[10] = resources.getDrawable(R.drawable.msg_folders_private); avatarDrawables[11] = resources.getDrawable(R.drawable.chats_replies); avatarDrawables[12] = resources.getDrawable(R.drawable.other_chats); - + avatarDrawables[13] = resources.getDrawable(R.drawable.msg_stories_closefriends); if (dialogs_archiveAvatarDrawable != null) { dialogs_archiveAvatarDrawable.setCallback(null); @@ -9138,8 +8411,6 @@ public static void createCommonMessageResources() { chat_topicTextPaint.setTextSize(AndroidUtilities.dp(smallerDp - 1)); chat_forwardNamePaint.setTextSize(AndroidUtilities.dp(smallerDp)); chat_adminPaint.setTextSize(AndroidUtilities.dp(smallerDp - 1)); -// float timeDp = 2 * (SharedConfig.fontSize - 16) / 3f + 12; -// chat_timePaint.setTextSize(AndroidUtilities.dp(timeDp)); } } @@ -9331,12 +8602,10 @@ public static void createChatResources(Context context, boolean fontsOnly) { calllog_msgCallUpGreenDrawable = resources.getDrawable(R.drawable.ic_call_made_green_18dp).mutate(); calllog_msgCallDownRedDrawable = resources.getDrawable(R.drawable.ic_call_received_green_18dp).mutate(); calllog_msgCallDownGreenDrawable = resources.getDrawable(R.drawable.ic_call_received_green_18dp).mutate(); - chat_msgAvatarLiveLocationDrawable = resources.getDrawable(R.drawable.livepin).mutate(); chat_inlineResultFile = resources.getDrawable(R.drawable.bot_file); chat_inlineResultAudio = resources.getDrawable(R.drawable.bot_music); chat_inlineResultLocation = resources.getDrawable(R.drawable.bot_location); - chat_redLocationIcon = resources.getDrawable(R.drawable.map_pin).mutate(); chat_botLinkDrawable = resources.getDrawable(R.drawable.bot_link); chat_botInlineDrawable = resources.getDrawable(R.drawable.bot_lines); @@ -9410,29 +8679,7 @@ public static void createChatResources(Context context, boolean fontsOnly) { chat_composeShadowDrawable = context.getResources().getDrawable(R.drawable.compose_panel_shadow).mutate(); chat_composeShadowRoundDrawable = context.getResources().getDrawable(R.drawable.sheet_shadow_round).mutate(); - try { - int bitmapSize = AndroidUtilities.roundMessageSize + AndroidUtilities.dp(6); - Bitmap bitmap = Bitmap.createBitmap(bitmapSize, bitmapSize, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - Paint eraserPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - eraserPaint.setColor(0); - eraserPaint.setStyle(Paint.Style.FILL); - eraserPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); - - Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); - paint.setShadowLayer(AndroidUtilities.dp(4), 0, 0, 0x5f000000); - for (int a = 0; a < 2; a++) { - canvas.drawCircle(bitmapSize / 2, bitmapSize / 2, AndroidUtilities.roundMessageSize / 2 - AndroidUtilities.dp(1), a == 0 ? paint : eraserPaint); - } - try { - canvas.setBitmap(null); - } catch (Exception ignore) { - - } - chat_roundVideoShadow = new BitmapDrawable(bitmap); - } catch (Throwable ignore) { - - } + chat_roundVideoShadow = new RoundVideoShadow(); defaultChatDrawables.clear(); defaultChatDrawableColorKeys.clear(); @@ -9444,15 +8691,15 @@ public static void createChatResources(Context context, boolean fontsOnly) { addChatDrawable(key_drawable_goIcon, chat_goIconDrawable, key_chat_serviceIcon); addChatDrawable(key_drawable_commentSticker, chat_commentStickerDrawable, key_chat_serviceIcon); addChatDrawable(key_drawable_msgError, chat_msgErrorDrawable, key_chat_sentErrorIcon); - addChatDrawable(key_drawable_msgIn, chat_msgInDrawable, null); - addChatDrawable(key_drawable_msgInSelected, chat_msgInSelectedDrawable, null); - addChatDrawable(key_drawable_msgInMedia, chat_msgInMediaDrawable, null); - addChatDrawable(key_drawable_msgInMediaSelected, chat_msgInMediaSelectedDrawable, null); + addChatDrawable(key_drawable_msgIn, chat_msgInDrawable, -1); + addChatDrawable(key_drawable_msgInSelected, chat_msgInSelectedDrawable, -1); + addChatDrawable(key_drawable_msgInMedia, chat_msgInMediaDrawable, -1); + addChatDrawable(key_drawable_msgInMediaSelected, chat_msgInMediaSelectedDrawable, -1); addChatDrawable(key_drawable_msgInInstant, chat_msgInInstantDrawable, key_chat_inInstant); - addChatDrawable(key_drawable_msgOut, chat_msgOutDrawable, null); - addChatDrawable(key_drawable_msgOutSelected, chat_msgOutSelectedDrawable, null); - addChatDrawable(key_drawable_msgOutMedia, chat_msgOutMediaDrawable, null); - addChatDrawable(key_drawable_msgOutMediaSelected, chat_msgOutMediaSelectedDrawable, null); + addChatDrawable(key_drawable_msgOut, chat_msgOutDrawable, -1); + addChatDrawable(key_drawable_msgOutSelected, chat_msgOutSelectedDrawable, -1); + addChatDrawable(key_drawable_msgOutMedia, chat_msgOutMediaDrawable, -1); + addChatDrawable(key_drawable_msgOutMediaSelected, chat_msgOutMediaSelectedDrawable, -1); addChatDrawable(key_drawable_msgOutCallAudio, chat_msgOutCallDrawable[0], key_chat_outInstant); addChatDrawable(key_drawable_msgOutCallAudioSelected, chat_msgOutCallSelectedDrawable[0], key_chat_outInstantSelected); addChatDrawable(key_drawable_msgOutCallVideo, chat_msgOutCallDrawable[1], key_chat_outInstant); @@ -9652,12 +8899,12 @@ public static void applyChatTheme(boolean fontsOnly, boolean bg) { } setDrawableColorByKey(chat_msgCallUpGreenDrawable, key_chat_outGreenCall); - setDrawableColorByKey(chat_msgCallDownRedDrawable, key_chat_inRedCall); + setDrawableColorByKey(chat_msgCallDownRedDrawable, key_fill_RedNormal); setDrawableColorByKey(chat_msgCallDownGreenDrawable, key_chat_inGreenCall); - setDrawableColorByKey(calllog_msgCallUpRedDrawable, key_calls_callReceivedRedIcon); + setDrawableColorByKey(calllog_msgCallUpRedDrawable, key_fill_RedNormal); setDrawableColorByKey(calllog_msgCallUpGreenDrawable, key_calls_callReceivedGreenIcon); - setDrawableColorByKey(calllog_msgCallDownRedDrawable, key_calls_callReceivedRedIcon); + setDrawableColorByKey(calllog_msgCallDownRedDrawable, key_fill_RedNormal); setDrawableColorByKey(calllog_msgCallDownGreenDrawable, key_calls_callReceivedGreenIcon); for (int i = 0; i < chat_status_drawables.length; i++) { @@ -9699,7 +8946,7 @@ public static void applyChatTheme(boolean fontsOnly, boolean bg) { setDrawableColor(chat_attachEmptyDrawable, getColor(key_chat_attachEmptyImage)); - if (!bg) { + if (!bg && !disallowChangeServiceMessageColor) { applyChatServiceMessageColor(); applyChatMessageSelectedBackgroundColor(); } @@ -9708,7 +8955,9 @@ public static void applyChatTheme(boolean fontsOnly, boolean bg) { } public static void applyChatServiceMessageColor() { - applyChatServiceMessageColor(null, null, wallpaper); + if (wallpaper != null) { + applyChatServiceMessageColor(null, null, wallpaper); + } } public static boolean hasGradientService() { @@ -9716,7 +8965,12 @@ public static boolean hasGradientService() { } private static int[] viewPos = new int[2]; + public static void applyServiceShaderMatrixForView(View view, View background) { + applyServiceShaderMatrixForView(view, background, null); + } + + public static void applyServiceShaderMatrixForView(View view, View background, ResourcesProvider resourcesProvider) { if (view == null || background == null) { return; } @@ -9724,7 +8978,11 @@ public static void applyServiceShaderMatrixForView(View view, View background) { int x = viewPos[0]; int y = viewPos[1]; background.getLocationOnScreen(viewPos); - applyServiceShaderMatrix(background.getMeasuredWidth(), background.getMeasuredHeight(), x, y - viewPos[1]); + if (resourcesProvider != null) { + resourcesProvider.applyServiceShaderMatrix(background.getMeasuredWidth(), background.getMeasuredHeight(), x, y - viewPos[1]); + } else { + applyServiceShaderMatrix(background.getMeasuredWidth(), background.getMeasuredHeight(), x, y - viewPos[1]); + } } public static void applyServiceShaderMatrix(int w, int h, float translationX, float translationY) { @@ -9758,33 +9016,34 @@ public static void applyChatServiceMessageColor(int[] custom, Drawable wallpaper if (chat_actionBackgroundPaint == null) { return; } - Integer serviceColor; - Integer servicePressedColor; + int serviceColor; + int serviceColor2; + int servicePressedColor; + int servicePressedColor2; serviceMessageColor = serviceMessageColorBackup; serviceSelectedMessageColor = serviceSelectedMessageColorBackup; if (custom != null && custom.length >= 2) { - serviceColor = custom[0]; - servicePressedColor = custom[1]; + serviceColor2 = serviceColor = custom[0]; + servicePressedColor2 = servicePressedColor = custom[1]; serviceMessageColor = custom[0]; serviceSelectedMessageColor = custom[1]; } else { - serviceColor = currentColors.get(key_chat_serviceBackground); - servicePressedColor = currentColors.get(key_chat_serviceBackgroundSelected); - } - Integer serviceColor2 = serviceColor; - Integer servicePressedColor2 = servicePressedColor; + int serviceIndex = currentColors.indexOfKey(key_chat_serviceBackground); + if (serviceIndex >= 0) { + serviceColor2 = serviceColor = currentColors.valueAt(serviceIndex); + } else { + serviceColor = serviceMessageColor; + serviceColor2 = serviceMessage2Color; + } - if (serviceColor == null) { - serviceColor = serviceMessageColor; - serviceColor2 = serviceMessage2Color; - } - if (servicePressedColor == null) { - servicePressedColor = serviceSelectedMessageColor; - } - if (servicePressedColor2 == null) { - servicePressedColor2 = serviceSelectedMessage2Color; + int servicePressedIndex = currentColors.indexOfKey(key_chat_serviceBackgroundSelected); + if (servicePressedIndex >= 0) { + servicePressedColor2 = servicePressedColor = currentColors.valueAt(servicePressedIndex); + } else { + servicePressedColor = serviceSelectedMessageColor; + servicePressedColor2 = serviceSelectedMessage2Color; + } } - Drawable drawable = wallpaperOverride != null ? wallpaperOverride : currentWallpaper; boolean drawServiceGradient = drawable instanceof MotionBackgroundDrawable && SharedConfig.getDevicePerformanceClass() != SharedConfig.PERFORMANCE_CLASS_LOW && LiteMode.isEnabled(LiteMode.FLAG_CHAT_BACKGROUND); if (drawServiceGradient) { @@ -9843,7 +9102,7 @@ public static void applyChatServiceMessageColor(int[] custom, Drawable wallpaper chat_actionBackgroundPaint2.setColor(serviceColor2); currentColor = serviceColor; - if (serviceBitmapShader != null && (currentColors.get(key_chat_serviceBackground) == null || drawable instanceof MotionBackgroundDrawable)) { + if (serviceBitmapShader != null && (currentColors.indexOfKey(key_chat_serviceBackground) < 0 || drawable instanceof MotionBackgroundDrawable)) { ColorMatrix colorMatrix = new ColorMatrix(); colorMatrix.setSaturation(((MotionBackgroundDrawable) drawable).getIntensity() >= 0 ? 1.8f : 0.5f); @@ -9875,10 +9134,10 @@ public static void applyChatMessageSelectedBackgroundColor(Drawable wallpaperOve return; } - Integer selectedBackgroundColor = currentColors.get(key_chat_selectedBackground); + int selectedBackgroundColor = currentColors.get(key_chat_selectedBackground); Drawable drawable = wallpaperOverride != null ? wallpaperOverride : currentWallpaper; - boolean drawSelectedGradient = drawable instanceof MotionBackgroundDrawable && SharedConfig.getDevicePerformanceClass() != SharedConfig.PERFORMANCE_CLASS_LOW && selectedBackgroundColor == null; + boolean drawSelectedGradient = drawable instanceof MotionBackgroundDrawable && SharedConfig.getDevicePerformanceClass() != SharedConfig.PERFORMANCE_CLASS_LOW && selectedBackgroundColor == 0; if (drawSelectedGradient) { Bitmap newBitmap = ((MotionBackgroundDrawable) drawable).getBitmap(); if (serviceBitmap != newBitmap) { @@ -9890,7 +9149,7 @@ public static void applyChatMessageSelectedBackgroundColor(Drawable wallpaperOve } } - if (serviceBitmapShader != null && selectedBackgroundColor == null && drawSelectedGradient) { + if (serviceBitmapShader != null && selectedBackgroundColor == 0 && drawSelectedGradient) { ColorMatrix colorMatrix2 = new ColorMatrix(); AndroidUtilities.adjustSaturationColorMatrix(colorMatrix2, 2.5f); AndroidUtilities.multiplyBrightnessColorMatrix(colorMatrix2, .75f); @@ -9898,7 +9157,7 @@ public static void applyChatMessageSelectedBackgroundColor(Drawable wallpaperOve chat_messageBackgroundSelectedPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix2)); chat_messageBackgroundSelectedPaint.setAlpha(64); } else { - chat_messageBackgroundSelectedPaint.setColor(selectedBackgroundColor == null ? 0x40000000 : selectedBackgroundColor); + chat_messageBackgroundSelectedPaint.setColor(selectedBackgroundColor == 0 ? 0x40000000 : selectedBackgroundColor); chat_messageBackgroundSelectedPaint.setColorFilter(null); chat_messageBackgroundSelectedPaint.setShader(null); } @@ -9951,11 +9210,11 @@ public static void applyProfileTheme() { setDrawableColorByKey(profile_verifiedCheckDrawable, key_profile_verifiedCheck); } - public static Drawable getThemedDrawable(Context context, int resId, String key, Theme.ResourcesProvider resourcesProvider) { + public static Drawable getThemedDrawableByKey(Context context, int resId, int key, Theme.ResourcesProvider resourcesProvider) { return getThemedDrawable(context, resId, getColor(key, resourcesProvider)); } - public static Drawable getThemedDrawable(Context context, int resId, String key) { + public static Drawable getThemedDrawableByKey(Context context, int resId, int key) { return getThemedDrawable(context, resId, getColor(key)); } @@ -9968,10 +9227,10 @@ public static Drawable getThemedDrawable(Context context, int resId, int color) return drawable; } - public static int getDefaultColor(String key) { - Integer value = defaultColors.get(key); - if (value == null) { - if (key.equals(key_chats_menuTopShadow) || key.equals(key_chats_menuTopBackground) || key.equals(key_chats_menuTopShadowCats) || key.equals(key_chat_wallpaper_gradient_to2) || key.equals(key_chat_wallpaper_gradient_to3)) { + public static int getDefaultColor(int key) { + int value = defaultColors[key]; + if (value == 0) { + if (isMyMessagesBubbles(key) || key == key_chats_menuTopShadow || key == key_chats_menuTopBackground || key == key_chats_menuTopShadowCats || key == key_chat_wallpaper_gradient_to2 || key == key_chat_wallpaper_gradient_to3) { return 0; } return 0xffff0000; @@ -9979,45 +9238,29 @@ public static int getDefaultColor(String key) { return value; } - public static boolean hasThemeKey(String key) { - return currentColors.containsKey(key); - } - - public static Integer getColorOrNull(String key) { - Integer color = currentColors.get(key); - if (color == null) { - String fallbackKey = fallbackKeys.get(key); - if (fallbackKey != null) { - color = currentColors.get(key); - } - if (color == null) { - color = defaultColors.get(key); - } - } - if (color != null && (key_windowBackgroundWhite.equals(key) || key_windowBackgroundGray.equals(key) || key_actionBarDefault.equals(key) || key_actionBarDefaultArchived.equals(key))) { - color |= 0xff000000; - } - return color; + public static boolean hasThemeKey(int key) { + return currentColors.indexOfKey(key) >= 0; } public static void setAnimatingColor(boolean animating) { - animatingColors = animating ? new HashMap<>() : null; + animatingColors = animating ? new SparseIntArray() : null; } public static boolean isAnimatingColor() { return animatingColors != null; } - public static void setAnimatedColor(String key, int value) { + public static void setAnimatedColor(int key, int value) { if (animatingColors == null) { return; } animatingColors.put(key, value); } - public static int getDefaultAccentColor(String key) { - Integer color = currentColorsNoAccent.get(key); - if (color != null) { + public static int getDefaultAccentColor(int key) { + int index = currentColorsNoAccent.indexOfKey(key); + if (index >= 0) { + int color = currentColorsNoAccent.valueAt(index); ThemeAccent accent = currentTheme.getAccent(false); if (accent == null) { return 0; @@ -10031,131 +9274,133 @@ public static int getDefaultAccentColor(String key) { return 0; } - public static int getNonAnimatedColor(String key) { + public static int getNonAnimatedColor(int key) { return getColor(key, null, true); } - public static int getColor(String key, ResourcesProvider provider) { + public static int getColor(int key, ResourcesProvider provider) { if (provider != null) { - Integer colorInteger = provider.getColor(key); - if (colorInteger != null) { - return colorInteger; - } + return provider.getColor(key); } return getColor(key); } - public static int getColor(String key) { + + public static int getColor(int key) { return getColor(key, null, false); } - public static int getColor(String key, boolean[] isDefault) { + public static int getColor(int key, boolean[] isDefault) { return getColor(key, isDefault, false); } - public static int getColor(String key, boolean[] isDefault, boolean ignoreAnimation) { + public static int getColor(int key, boolean[] isDefault, boolean ignoreAnimation) { if (!ignoreAnimation && animatingColors != null) { - Integer color = animatingColors.get(key); - if (color != null) { - return color; + int index = animatingColors.indexOfKey(key); + if (index >= 0) { + return animatingColors.valueAt(index); } } - if (serviceBitmapShader != null && (key_chat_serviceText.equals(key) || key_chat_serviceLink.equals(key) || key_chat_serviceIcon.equals(key) - || key_chat_stickerReplyLine.equals(key) || key_chat_stickerReplyNameText.equals(key) || key_chat_stickerReplyMessageText.equals(key))) { + if (serviceBitmapShader != null && (key_chat_serviceText == key || key_chat_serviceLink == key || key_chat_serviceIcon == key + || key_chat_stickerReplyLine == key || key_chat_stickerReplyNameText == key || key_chat_stickerReplyMessageText == key)) { return 0xffffffff; } if (currentTheme == defaultTheme) { boolean useDefault; - if (myMessagesBubblesColorKeys.contains(key)) { + if (isMyMessagesBubbles(key)) { useDefault = currentTheme.isDefaultMyMessagesBubbles(); - } else if (myMessagesColorKeys.contains(key)) { + } else if (isMyMessages(key)) { useDefault = currentTheme.isDefaultMyMessages(); - } else if (key_chat_wallpaper.equals(key) || key_chat_wallpaper_gradient_to1.equals(key) || key_chat_wallpaper_gradient_to2.equals(key) || key_chat_wallpaper_gradient_to3.equals(key)) { + } else if (key_chat_wallpaper == key || key_chat_wallpaper_gradient_to1 == key || key_chat_wallpaper_gradient_to2 == key || key_chat_wallpaper_gradient_to3 == key) { useDefault = false; } else { useDefault = currentTheme.isDefaultMainAccent(); } if (useDefault) { - if (key.equals(key_chat_serviceBackground)) { + if (key == key_chat_serviceBackground) { return serviceMessageColor; - } else if (key.equals(key_chat_serviceBackgroundSelected)) { + } else if (key == key_chat_serviceBackgroundSelected) { return serviceSelectedMessageColor; } return getDefaultColor(key); } } - Integer color = currentColors.get(key); - if (color == null) { - String fallbackKey = fallbackKeys.get(key); - if (fallbackKey != null) { - color = currentColors.get(fallbackKey); - } - if (color == null) { - if (isDefault != null) { - isDefault[0] = true; + int index = currentColors.indexOfKey(key); + int color; + if (index < 0) { + int fallbackKey = fallbackKeys.get(key, -1); + if (fallbackKey != -1) { + int fallbackIndex = currentColors.indexOfKey(fallbackKey); + if (fallbackIndex >= 0) { + return currentColors.valueAt(fallbackIndex); } - if (key.equals(key_chat_serviceBackground)) { - return serviceMessageColor; - } else if (key.equals(key_chat_serviceBackgroundSelected)) { - return serviceSelectedMessageColor; - } - return getDefaultColor(key); } + + if (isDefault != null) { + isDefault[0] = true; + } + if (key == key_chat_serviceBackground) { + return serviceMessageColor; + } else if (key == key_chat_serviceBackgroundSelected) { + return serviceSelectedMessageColor; + } + return getDefaultColor(key); + } else { + color = currentColors.valueAt(index); } - if (key_windowBackgroundWhite.equals(key) || key_windowBackgroundGray.equals(key) || key_actionBarDefault.equals(key) || key_actionBarDefaultArchived.equals(key)) { + if (key_windowBackgroundWhite == key || key_windowBackgroundGray == key || key_actionBarDefault == key || key_actionBarDefaultArchived == key) { color |= 0xff000000; } return color; } - public static void setColor(String key, int color, boolean useDefault) { - if (key.equals(key_chat_wallpaper) || key.equals(key_chat_wallpaper_gradient_to1) || key.equals(key_chat_wallpaper_gradient_to2) || key.equals(key_chat_wallpaper_gradient_to3) || key.equals(key_windowBackgroundWhite) || key.equals(key_windowBackgroundGray) || key.equals(key_actionBarDefault) || key.equals(key_actionBarDefaultArchived)) { + private static boolean isMyMessagesBubbles(int key) { + return key >= myMessagesBubblesStartIndex && key < myMessagesBubblesEndIndex; + } + + + private static boolean isMyMessages(int key) { + return key >= myMessagesStartIndex && key < myMessagesEndIndex; + } + + public static void setColor(int key, int color, boolean useDefault) { + if (key == key_chat_wallpaper || key == key_chat_wallpaper_gradient_to1 || key == key_chat_wallpaper_gradient_to2 || key == key_chat_wallpaper_gradient_to3 || key == key_windowBackgroundWhite || key == key_windowBackgroundGray || key == key_actionBarDefault || key == key_actionBarDefaultArchived) { color = 0xff000000 | color; } if (useDefault) { - currentColors.remove(key); + currentColors.delete(key); } else { currentColors.put(key, color); } - switch (key) { - case key_chat_selectedBackground: - applyChatMessageSelectedBackgroundColor(); - break; - case key_chat_serviceBackground: - case key_chat_serviceBackgroundSelected: - applyChatServiceMessageColor(); - break; - case key_chat_wallpaper: - case key_chat_wallpaper_gradient_to1: - case key_chat_wallpaper_gradient_to2: - case key_chat_wallpaper_gradient_to3: - case key_chat_wallpaper_gradient_rotation: - reloadWallpaper(); - break; - case key_actionBarDefault: - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.needCheckSystemBarColors); - } - break; - case key_windowBackgroundGray: - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.needCheckSystemBarColors); - } - break; + if (key == key_chat_selectedBackground) { + applyChatMessageSelectedBackgroundColor(); + } else if (key == key_chat_serviceBackground || key == key_chat_serviceBackgroundSelected) { + applyChatServiceMessageColor(); + } else if (key == key_chat_wallpaper || key == key_chat_wallpaper_gradient_to1 || key == key_chat_wallpaper_gradient_to2 + || key == key_chat_wallpaper_gradient_to3 || key == key_chat_wallpaper_gradient_rotation) { + reloadWallpaper(true); + } else if (key == key_actionBarDefault) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.needCheckSystemBarColors); + } + } else if (key == key_windowBackgroundGray) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.needCheckSystemBarColors); + } } } - public static void setDefaultColor(String key, int color) { - defaultColors.put(key, color); + public static void setDefaultColor(int key, int color) { + defaultColors[key] = color; } public static void setThemeWallpaper(ThemeInfo themeInfo, Bitmap bitmap, File path) { - currentColors.remove(key_chat_wallpaper); - currentColors.remove(key_chat_wallpaper_gradient_to1); - currentColors.remove(key_chat_wallpaper_gradient_to2); - currentColors.remove(key_chat_wallpaper_gradient_to3); - currentColors.remove(key_chat_wallpaper_gradient_rotation); + currentColors.delete(key_chat_wallpaper); + currentColors.delete(key_chat_wallpaper_gradient_to1); + currentColors.delete(key_chat_wallpaper_gradient_to2); + currentColors.delete(key_chat_wallpaper_gradient_to3); + currentColors.delete(key_chat_wallpaper_gradient_rotation); themedWallpaperLink = null; themeInfo.setOverrideWallpaper(null); if (bitmap != null) { @@ -10169,7 +9414,7 @@ public static void setThemeWallpaper(ThemeInfo themeInfo, Bitmap bitmap, File pa themedWallpaper = null; wallpaper = null; saveCurrentTheme(themeInfo, false, false, false); - reloadWallpaper(); + reloadWallpaper(true); } } @@ -10190,10 +9435,7 @@ public static void setDrawableColor(Drawable drawable, int color) { } } - public static void setDrawableColorByKey(Drawable drawable, String key) { - if (key == null) { - return; - } + public static void setDrawableColorByKey(Drawable drawable, int key) { setDrawableColor(drawable, getColor(key)); } @@ -10282,14 +9524,14 @@ public static boolean hasWallpaperFromTheme() { if (currentTheme.firstAccentIsDefault && currentTheme.currentAccentId == DEFALT_THEME_ACCENT_ID) { return false; } - return currentColors.containsKey(key_chat_wallpaper) || themedWallpaperFileOffset > 0 || !TextUtils.isEmpty(themedWallpaperLink); + return currentColors.indexOfKey(key_chat_wallpaper) >= 0 || themedWallpaperFileOffset > 0 || !TextUtils.isEmpty(themedWallpaperLink); } public static boolean isCustomTheme() { return isCustomTheme; } - public static void reloadWallpaper() { + public static void reloadWallpaper(boolean async) { if (backgroundGradientDisposable != null) { backgroundGradientDisposable.dispose(); backgroundGradientDisposable = null; @@ -10301,7 +9543,7 @@ public static void reloadWallpaper() { } wallpaper = null; themedWallpaper = null; - loadWallpaper(); + loadWallpaper(async); } private static void calcBackgroundColor(Drawable drawable, int save) { @@ -10315,11 +9557,15 @@ private static void calcBackgroundColor(Drawable drawable, int save) { } public static int getServiceMessageColor() { - Integer serviceColor = currentColors.get(key_chat_serviceBackground); - return serviceColor == null ? serviceMessageColor : serviceColor; + int index = currentColors.indexOfKey(key_chat_serviceBackground); + if (index >= 0) { + return currentColors.valueAt(index); + } else { + return serviceMessageColor; + } } - public static void loadWallpaper() { + public static void loadWallpaper(boolean async) { if (wallpaper != null) { return; } @@ -10351,43 +9597,60 @@ public static void loadWallpaper() { } TLRPC.Document finalWallpaperDocument = wallpaperDocument; - Utilities.themeQueue.postRunnable(wallpaperLoadTask = () -> { - BackgroundDrawableSettings settings = createBackgroundDrawable( - currentTheme, - overrideWallpaper, - currentColors, - wallpaperFile, - themedWallpaperLink, - themedWallpaperFileOffset, - intensity, - previousPhase, - defaultTheme, - hasPreviousTheme, - isApplyingAccent, - wallpaperMotion, - finalWallpaperDocument - ); - isWallpaperMotion = settings.isWallpaperMotion != null ? settings.isWallpaperMotion : isWallpaperMotion; - isPatternWallpaper = settings.isPatternWallpaper != null ? settings.isPatternWallpaper : isPatternWallpaper; - isCustomTheme = settings.isCustomTheme != null ? settings.isCustomTheme : isCustomTheme; - patternIntensity = intensity; - wallpaper = settings.wallpaper != null ? settings.wallpaper : wallpaper; - Drawable drawable = settings.wallpaper; - calcBackgroundColor(drawable, 1); - - AndroidUtilities.runOnUIThread(() -> { - wallpaperLoadTask = null; - createCommonChatResources(); + if (async) { + Utilities.themeQueue.postRunnable(wallpaperLoadTask = () -> { + Drawable drawable = loadWallpaperInternal(overrideWallpaper, wallpaperFile, intensity, wallpaperMotion, finalWallpaperDocument, defaultTheme); + AndroidUtilities.runOnUIThread(() -> { + wallpaperLoadTask = null; + createCommonChatResources(); + if (!disallowChangeServiceMessageColor) { + applyChatServiceMessageColor(null, null, drawable); + applyChatMessageSelectedBackgroundColor(null, drawable); + } + NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.didSetNewWallpapper); + }); + }); + } else { + Drawable drawable = loadWallpaperInternal(overrideWallpaper, wallpaperFile, intensity, wallpaperMotion, finalWallpaperDocument, defaultTheme); + createCommonChatResources(); + if (!disallowChangeServiceMessageColor) { applyChatServiceMessageColor(null, null, drawable); applyChatMessageSelectedBackgroundColor(null, drawable); - NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.didSetNewWallpapper); - }); - }); + } + NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.didSetNewWallpapper); + } } + private static Drawable loadWallpaperInternal(OverrideWallpaperInfo overrideWallpaper, File wallpaperFile, int intensity, boolean wallpaperMotion, TLRPC.Document finalWallpaperDocument, boolean defaultTheme) { + BackgroundDrawableSettings settings = createBackgroundDrawable( + currentTheme, + overrideWallpaper, + currentColors, + wallpaperFile, + themedWallpaperLink, + themedWallpaperFileOffset, + intensity, + previousPhase, + defaultTheme, + hasPreviousTheme, + isApplyingAccent, + wallpaperMotion, + finalWallpaperDocument + ); + isWallpaperMotion = settings.isWallpaperMotion != null ? settings.isWallpaperMotion : isWallpaperMotion; + isPatternWallpaper = settings.isPatternWallpaper != null ? settings.isPatternWallpaper : isPatternWallpaper; + isCustomTheme = settings.isCustomTheme != null ? settings.isCustomTheme : isCustomTheme; + patternIntensity = intensity; + wallpaper = settings.wallpaper != null ? settings.wallpaper : wallpaper; + Drawable drawable = settings.wallpaper; + calcBackgroundColor(drawable, 1); + return drawable; + } + + public static BackgroundDrawableSettings createBackgroundDrawable( ThemeInfo currentTheme, - HashMap currentColors, + SparseIntArray currentColors, String wallpaperLink, int prevoiusPhase ) { @@ -10399,15 +9662,15 @@ public static BackgroundDrawableSettings createBackgroundDrawable( int intensity = overrideWallpaper != null ? (int) (overrideWallpaper.intensity * 100) : (int) (accent != null ? (accent.patternIntensity * 100) : currentTheme.patternIntensity); - Integer offset = currentColorsNoAccent.get("wallpaperFileOffset"); - int wallpaperFileOffset = offset != null ? offset : -1; + + int wallpaperFileOffset = currentColorsNoAccent.get(key_wallpaperFileOffset, -1); return createBackgroundDrawable(currentTheme, overrideWallpaper, currentColors, wallpaperFile, wallpaperLink, wallpaperFileOffset, intensity, prevoiusPhase, defaultTheme, false, false, wallpaperMotion, null); } public static BackgroundDrawableSettings createBackgroundDrawable( ThemeInfo currentTheme, OverrideWallpaperInfo overrideWallpaper, - HashMap currentColors, + SparseIntArray currentColors, File wallpaperFile, String themedWallpaperLink, int themedWallpaperFileOffset, @@ -10430,20 +9693,24 @@ public static BackgroundDrawableSettings createBackgroundDrawable( settings.isPatternWallpaper = currentTheme.patternBgColor != 0; } if (!overrideTheme) { - Integer backgroundColor = defaultTheme ? null : currentColors.get(key_chat_wallpaper); - Integer gradientToColor3 = currentColors.get(key_chat_wallpaper_gradient_to3); - if (gradientToColor3 == null) { - gradientToColor3 = 0; - } - Integer gradientToColor2 = currentColors.get(key_chat_wallpaper_gradient_to2); - gradientToColor2 = currentColors.get(key_chat_wallpaper_gradient_to2); - Integer gradientToColor1 = currentColors.get(key_chat_wallpaper_gradient_to1); + int backgroundColor = defaultTheme ? 0 : currentColors.get(key_chat_wallpaper); + int gradientToColor3 = currentColors.get(key_chat_wallpaper_gradient_to3); + int gradientToColor2 = currentColors.get(key_chat_wallpaper_gradient_to2); + int gradientToColor1 = currentColors.get(key_chat_wallpaper_gradient_to1); if (wallpaperFile != null && wallpaperFile.exists()) { try { - if (backgroundColor != null && gradientToColor1 != null && gradientToColor2 != null) { + if (backgroundColor != 0 && gradientToColor1 != 0 && gradientToColor2 != 0) { MotionBackgroundDrawable motionBackgroundDrawable = new MotionBackgroundDrawable(backgroundColor, gradientToColor1, gradientToColor2, gradientToColor3, false); - motionBackgroundDrawable.setPatternBitmap(intensity, BitmapFactory.decodeFile(wallpaperFile.getAbsolutePath())); + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inPreferredConfig = Bitmap.Config.ALPHA_8; + Bitmap patternBitmap = BitmapFactory.decodeFile(wallpaperFile.getAbsolutePath(), options); + if (patternBitmap != null && patternBitmap.getConfig() != Bitmap.Config.ALPHA_8) { + Bitmap toRecycle = patternBitmap; + patternBitmap = patternBitmap.copy(Bitmap.Config.ALPHA_8, false); + toRecycle.recycle(); + } + motionBackgroundDrawable.setPatternBitmap(intensity, patternBitmap); motionBackgroundDrawable.setPatternColorFilter(motionBackgroundDrawable.getPatternColor()); settings.wallpaper = motionBackgroundDrawable; } else { @@ -10455,12 +9722,12 @@ public static BackgroundDrawableSettings createBackgroundDrawable( } catch (Throwable e) { FileLog.e(e); } - } else if (backgroundColor != null) { - Integer rotation = currentColors.get(key_chat_wallpaper_gradient_rotation); - if (rotation == null) { + } else if (backgroundColor != 0) { + int rotation = currentColors.get(key_chat_wallpaper_gradient_rotation, -1); + if (rotation == -1) { rotation = 45; } - if (gradientToColor1 != null && gradientToColor2 != null) { + if (gradientToColor1 != 0 && gradientToColor2 != 0) { MotionBackgroundDrawable motionBackgroundDrawable = new MotionBackgroundDrawable(backgroundColor, gradientToColor1, gradientToColor2, gradientToColor3, false); Bitmap patternBitmap = null; @@ -10482,7 +9749,7 @@ public static BackgroundDrawableSettings createBackgroundDrawable( motionBackgroundDrawable.setPatternBitmap(intensity, patternBitmap); motionBackgroundDrawable.setPhase(previousPhase); settings.wallpaper = motionBackgroundDrawable; - } else if (gradientToColor1 == null || gradientToColor1.equals(backgroundColor)) { + } else if (gradientToColor1 == 0 || gradientToColor1 == backgroundColor) { settings.wallpaper = new ColorDrawable(backgroundColor); } else { final int[] colors = {backgroundColor, gradientToColor1}; @@ -10552,6 +9819,16 @@ public void onSizeReady(int width, int height) { } } settings.wallpaper = motionBackgroundDrawable; + } else if (settings.isPatternWallpaper) { + File toFile = new File(ApplicationLoader.getFilesDirFixed(), overrideWallpaper.fileName); + if (toFile.exists()) { + Bitmap bitmap = loadScreenSizedBitmap(new FileInputStream(toFile), 0); + if (bitmap != null) { + settings.wallpaper = new BitmapDrawable(bitmap); + settings.wallpaper.setFilterBitmap(true); + settings.isCustomTheme = true; + } + } } else if (overrideWallpaper.gradientColor1 != 0) { final int[] colors = {selectedColor, overrideWallpaper.gradientColor1}; final BackgroundGradientDrawable.Orientation orientation = BackgroundGradientDrawable.getGradientOrientation(overrideWallpaper.rotation); @@ -10688,19 +9965,19 @@ private static Bitmap loadScreenSizedBitmap(FileInputStream stream, int offset) } public static Drawable getThemedWallpaper(boolean thumb, View ownerView) { - Integer backgroundColor = currentColors.get(key_chat_wallpaper); + int backgroundColor = currentColors.get(key_chat_wallpaper); File file = null; MotionBackgroundDrawable motionBackgroundDrawable = null; int offset = 0; - if (backgroundColor != null) { - Integer gradientToColor1 = currentColors.get(key_chat_wallpaper_gradient_to1); - Integer gradientToColor2 = currentColors.get(key_chat_wallpaper_gradient_to2); - Integer gradientToColor3 = currentColors.get(key_chat_wallpaper_gradient_to3); - Integer rotation = currentColors.get(key_chat_wallpaper_gradient_rotation); - if (rotation == null) { + if (backgroundColor != 0) { + int gradientToColor1 = currentColors.get(key_chat_wallpaper_gradient_to1); + int gradientToColor2 = currentColors.get(key_chat_wallpaper_gradient_to2); + int gradientToColor3 = currentColors.get(key_chat_wallpaper_gradient_to3); + int rotation = currentColors.get(key_chat_wallpaper_gradient_rotation, -1); + if (rotation == -1) { rotation = 45; } - if (gradientToColor1 == null) { + if (gradientToColor1 == 0) { return new ColorDrawable(backgroundColor); } else { ThemeAccent accent = currentTheme.getAccent(false); @@ -10710,8 +9987,8 @@ public static Drawable getThemedWallpaper(boolean thumb, View ownerView) { file = wallpaperFile; } } - if (gradientToColor2 != null) { - motionBackgroundDrawable = new MotionBackgroundDrawable(backgroundColor, gradientToColor1, gradientToColor2, gradientToColor3 != null ? gradientToColor3 : 0, true); + if (gradientToColor2 != 0) { + motionBackgroundDrawable = new MotionBackgroundDrawable(backgroundColor, gradientToColor1, gradientToColor2, gradientToColor3, true); if (file == null) { return motionBackgroundDrawable; } @@ -10775,6 +10052,7 @@ public void onSizeReady(int width, int height) { photoH /= 2; } } + opts.inPreferredConfig = Bitmap.Config.ALPHA_8; opts.inJustDecodeBounds = false; opts.inSampleSize = scaleFactor; Bitmap bitmap = BitmapFactory.decodeStream(stream, null, opts); @@ -10786,6 +10064,11 @@ public void onSizeReady(int width, int height) { } else { intensity = 100; } + if (bitmap != null && bitmap.getConfig() != Bitmap.Config.ALPHA_8) { + Bitmap toRecycle = bitmap; + bitmap = bitmap.copy(Bitmap.Config.ALPHA_8, false); + toRecycle.recycle(); + } motionBackgroundDrawable.setPatternBitmap(intensity, bitmap); motionBackgroundDrawable.setPatternColorFilter(motionBackgroundDrawable.getPatternColor()); return motionBackgroundDrawable; @@ -10943,11 +10226,11 @@ public static RoundVideoProgressShadow getRadialSeekbarShadowDrawable() { return roundPlayDrawable; } - public static HashMap getFallbackKeys() { + public static SparseIntArray getFallbackKeys() { return fallbackKeys; } - public static String getFallbackKey(String key) { + public static int getFallbackKey(int key) { return fallbackKeys.get(key); } @@ -10959,7 +10242,7 @@ public static Drawable getThemeDrawable(String drawableKey) { return defaultChatDrawables.get(drawableKey); } - public static String getThemeDrawableColorKey(String drawableKey) { + public static int getThemeDrawableColorKey(String drawableKey) { return defaultChatDrawableColorKeys.get(drawableKey); } @@ -10968,25 +10251,24 @@ public static Map getThemePaintsMap() { } public static Paint getThemePaint(String paintKey) { + if (Objects.equals(paintKey, Theme.key_paint_divider)) { + return dividerPaint; + } return defaultChatPaints.get(paintKey); } - public static String getThemePaintColorKey(String paintKey) { + public static int getThemePaintColorKey(String paintKey) { return defaultChatPaintColors.get(paintKey); } - private static void addChatDrawable(String key, Drawable drawable, String colorKey) { + private static void addChatDrawable(String key, Drawable drawable, int colorKey) { defaultChatDrawables.put(key, drawable); - if (colorKey != null) { - defaultChatDrawableColorKeys.put(key, colorKey); - } + defaultChatDrawableColorKeys.put(key, colorKey); } - private static void addChatPaint(String key, Paint paint, String colorKey) { + private static void addChatPaint(String key, Paint paint, int colorKey) { defaultChatPaints.put(key, paint); - if (colorKey != null) { - defaultChatPaintColors.put(key, colorKey); - } + defaultChatPaintColors.put(key, colorKey); } public static boolean isCurrentThemeDay() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ThemeColors.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ThemeColors.java new file mode 100644 index 0000000000..b588f2d8ae --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ThemeColors.java @@ -0,0 +1,1525 @@ +package org.telegram.ui.ActionBar; + +import static org.telegram.ui.ActionBar.Theme.*; + +import android.graphics.Color; +import android.util.Log; +import android.util.SparseArray; + +import androidx.core.graphics.ColorUtils; + +import java.util.HashMap; + +public class ThemeColors { + + private static SparseArray colorKeysMap; + private static HashMap colorKeysStringMap; + public static int[] createDefaultColors() { + int[] defaultColors = new int[Theme.colorsCount]; + + defaultColors[key_wallpaperFileOffset] = 0; + defaultColors[key_dialogBackground] = 0xffffffff; + defaultColors[key_dialogBackgroundGray] = 0xfff0f0f0; + defaultColors[key_dialogTextBlack] = 0xff222222; + defaultColors[key_dialogTextLink] = 0xff2678b6; + defaultColors[key_dialogLinkSelection] = 0x3362a9e3; + defaultColors[key_dialogTextRed] = 0xffcd5a5a; + defaultColors[key_dialogTextBlue] = 0xff2f8cc9; + defaultColors[key_dialogTextBlue2] = 0xff3a95d5; + defaultColors[key_dialogTextBlue4] = 0xff19a7e8; + defaultColors[key_dialogTextGray] = 0xff348bc1; + defaultColors[key_dialogTextGray2] = 0xff757575; + defaultColors[key_dialogTextGray3] = 0xff999999; + defaultColors[key_dialogTextGray4] = 0xffb3b3b3; + defaultColors[key_dialogTextHint] = 0xff979797; + defaultColors[key_dialogIcon] = 0xff676b70; + defaultColors[key_dialogRedIcon] = 0xffe14d4d; + defaultColors[key_dialogGrayLine] = 0xffd2d2d2; + defaultColors[key_dialogTopBackground] = 0xff6fb2e5; + defaultColors[key_dialogInputField] = 0xffdbdbdb; + defaultColors[key_dialogInputFieldActivated] = 0xff37a9f0; + defaultColors[key_dialogCheckboxSquareBackground] = 0xff43a0df; + defaultColors[key_dialogCheckboxSquareCheck] = 0xffffffff; + defaultColors[key_dialogCheckboxSquareUnchecked] = 0xff737373; + defaultColors[key_dialogCheckboxSquareDisabled] = 0xffb0b0b0; + defaultColors[key_dialogRadioBackground] = 0xffb3b3b3; + defaultColors[key_dialogRadioBackgroundChecked] = 0xff37a9f0; + defaultColors[key_dialogLineProgress] = 0xff527da3; + defaultColors[key_dialogLineProgressBackground] = 0xffdbdbdb; + defaultColors[key_dialogButton] = 0xff4991cc; + defaultColors[key_dialogButtonSelector] = 0x0f000000; + defaultColors[key_dialogScrollGlow] = 0xfff5f6f7; + defaultColors[key_dialogRoundCheckBox] = 0xff4cb4f5; + defaultColors[key_dialogRoundCheckBoxCheck] = 0xffffffff; + defaultColors[key_dialogCameraIcon] = 0xffffffff; + defaultColors[key_dialog_inlineProgressBackground] = 0xf6f0f2f5; + defaultColors[key_dialog_inlineProgress] = 0xff6b7378; + defaultColors[key_dialogSearchBackground] = 0xfff2f4f5; + defaultColors[key_dialogSearchHint] = 0xff98a0a7; + defaultColors[key_dialogSearchIcon] = 0xffa1a8af; + defaultColors[key_dialogSearchText] = 0xff222222; + defaultColors[key_dialogFloatingButton] = 0xff4cb4f5; + defaultColors[key_dialogFloatingButtonPressed] = 0x0f000000; + defaultColors[key_dialogFloatingIcon] = 0xffffffff; + defaultColors[key_dialogShadowLine] = 0x12000000; + defaultColors[key_dialogEmptyImage] = 0xff9fa4a8; + defaultColors[key_dialogEmptyText] = 0xff8c9094; + defaultColors[key_dialogSwipeRemove] = 0xffe56555; + defaultColors[key_dialogReactionMentionBackground] = 0xffF05459; + + defaultColors[key_windowBackgroundWhite] = 0xffffffff; + defaultColors[key_windowBackgroundUnchecked] = 0xff9da7b1; + defaultColors[key_windowBackgroundChecked] = 0xff579ed9; + defaultColors[key_windowBackgroundCheckText] = 0xffffffff; + defaultColors[key_progressCircle] = 0xff1c93e3; + defaultColors[key_windowBackgroundWhiteGrayIcon] = 0xff81868b; + defaultColors[key_windowBackgroundWhiteBlueText] = 0xff4092cd; + defaultColors[key_windowBackgroundWhiteBlueText2] = 0xff3a95d5; + defaultColors[key_windowBackgroundWhiteBlueText3] = 0xff2678b6; + defaultColors[key_windowBackgroundWhiteBlueText4] = 0xff1c93e3; + defaultColors[key_windowBackgroundWhiteBlueText5] = 0xff4c8eca; + defaultColors[key_windowBackgroundWhiteBlueText6] = 0xff3a8ccf; + defaultColors[key_windowBackgroundWhiteBlueText7] = 0xff377aae; + defaultColors[key_windowBackgroundWhiteBlueButton] = 0xff1e88d3; + defaultColors[key_windowBackgroundWhiteBlueIcon] = 0xff379de5; + defaultColors[key_windowBackgroundWhiteGreenText] = 0xff26972c; + defaultColors[key_windowBackgroundWhiteGreenText2] = 0xff37a818; + defaultColors[key_windowBackgroundWhiteRedText3] = 0xffd24949; + defaultColors[key_windowBackgroundWhiteRedText4] = 0xffcf3030; + defaultColors[key_text_RedRegular] = 0xffcc2929; + defaultColors[key_text_RedBold] = 0xffcc4747; + defaultColors[key_fill_RedNormal] = 0xffeb5e5e; + defaultColors[key_windowBackgroundWhiteGrayText] = 0xff838c96; + defaultColors[key_windowBackgroundWhiteGrayText2] = 0xff82868a; + defaultColors[key_windowBackgroundWhiteGrayText3] = 0xff999999; + defaultColors[key_windowBackgroundWhiteGrayText4] = 0xff808080; + defaultColors[key_windowBackgroundWhiteGrayText5] = 0xffa3a3a3; + defaultColors[key_windowBackgroundWhiteGrayText6] = 0xff757575; + defaultColors[key_windowBackgroundWhiteGrayText7] = 0xffc6c6c6; + defaultColors[key_windowBackgroundWhiteGrayText8] = 0xff6d6d72; + defaultColors[key_windowBackgroundWhiteBlackText] = 0xff222222; + defaultColors[key_windowBackgroundWhiteHintText] = 0xffa8a8a8; + defaultColors[key_windowBackgroundWhiteValueText] = 0xff3a95d5; + defaultColors[key_windowBackgroundWhiteLinkText] = 0xff2678b6; + defaultColors[key_windowBackgroundWhiteLinkSelection] = 0x3362a9e3; + defaultColors[key_windowBackgroundWhiteBlueHeader] = 0xff3a95d5; + defaultColors[key_windowBackgroundWhiteInputField] = 0xffdbdbdb; + defaultColors[key_windowBackgroundWhiteInputFieldActivated] = 0xff37a9f0; + defaultColors[key_switchTrack] = 0xffb0b5ba; + defaultColors[key_switchTrackChecked] = 0xff52ade9; + defaultColors[key_switchTrackBlue] = 0xff828e99; + defaultColors[key_switchTrackBlueChecked] = 0xff3c88c7; + defaultColors[key_switchTrackBlueThumb] = 0xffffffff; + defaultColors[key_switchTrackBlueThumbChecked] = 0xffffffff; + defaultColors[key_switchTrackBlueSelector] = 0x17404a53; + defaultColors[key_switchTrackBlueSelectorChecked] = 0x21024781; + defaultColors[key_switch2Track] = 0xfff57e7e; + defaultColors[key_switch2TrackChecked] = 0xff52ade9; + defaultColors[key_checkboxSquareBackground] = 0xff43a0df; + defaultColors[key_checkboxSquareCheck] = 0xffffffff; + defaultColors[key_checkboxSquareUnchecked] = 0xff737373; + defaultColors[key_checkboxSquareDisabled] = 0xffb0b0b0; + defaultColors[key_listSelector] = 0x0f000000; + defaultColors[key_radioBackground] = 0xffb3b3b3; + defaultColors[key_radioBackgroundChecked] = 0xff37a9f0; + defaultColors[key_windowBackgroundGray] = 0xfff0f0f0; + defaultColors[key_windowBackgroundGrayShadow] = 0xff000000; + defaultColors[key_emptyListPlaceholder] = 0xff959595; + defaultColors[key_divider] = 0xffd9d9d9; + defaultColors[key_graySection] = 0xfff5f5f5; + defaultColors[key_graySectionText] = 0xff82878A; + defaultColors[key_contextProgressInner1] = 0xffbfdff6; + defaultColors[key_contextProgressOuter1] = 0xff2b96e2; + defaultColors[key_contextProgressInner2] = 0xffbfdff6; + defaultColors[key_contextProgressOuter2] = 0xffffffff; + defaultColors[key_contextProgressInner3] = 0xffb3b3b3; + defaultColors[key_contextProgressOuter3] = 0xffffffff; + defaultColors[key_contextProgressInner4] = 0xffcacdd0; + defaultColors[key_contextProgressOuter4] = 0xff2f3438; + defaultColors[key_fastScrollActive] = 0xff52a3db; + defaultColors[key_fastScrollInactive] = 0xffc9cdd1; + defaultColors[key_fastScrollText] = 0xffffffff; + + defaultColors[key_avatar_text] = 0xffffffff; + + defaultColors[key_avatar_backgroundSaved] = 0xff69BFFA; + defaultColors[key_avatar_background2Saved] = 0xff3D9DE0; + defaultColors[key_avatar_backgroundArchived] = 0xffB8C2CC; + defaultColors[key_avatar_backgroundArchivedHidden] = 0xff66bffa; + defaultColors[key_avatar_backgroundRed] = 0xffFF845E; + defaultColors[key_avatar_backgroundOrange] = 0xffFEBB5B; + defaultColors[key_avatar_backgroundViolet] = 0xffB694F9; + defaultColors[key_avatar_backgroundGreen] = 0xff9AD164; + defaultColors[key_avatar_backgroundCyan] = 0xff5BCBE3; + defaultColors[key_avatar_backgroundBlue] = 0xff5CAFFA; + defaultColors[key_avatar_backgroundPink] = 0xffFF8AAC; + + defaultColors[key_avatar_background2Red] = 0xffD45246; + defaultColors[key_avatar_background2Orange] = 0xffF68136; + defaultColors[key_avatar_background2Violet] = 0xff6C61DF; + defaultColors[key_avatar_background2Green] = 0xff46BA43; + defaultColors[key_avatar_background2Cyan] = 0xff359AD4; + defaultColors[key_avatar_background2Blue] = 0xff408ACF; + defaultColors[key_avatar_background2Pink] = 0xffD95574; + + defaultColors[key_avatar_backgroundInProfileBlue] = 0xff5085b1; + defaultColors[key_avatar_backgroundActionBarBlue] = 0xff598fba; + defaultColors[key_avatar_subtitleInProfileBlue] = 0xffd7eafa; + defaultColors[key_avatar_actionBarSelectorBlue] = 0xff4981ad; + defaultColors[key_avatar_actionBarIconBlue] = 0xffffffff; + + defaultColors[key_avatar_nameInMessageRed] = 0xffca5650; + defaultColors[key_avatar_nameInMessageOrange] = 0xffd87b29; + defaultColors[key_avatar_nameInMessageViolet] = 0xff9B66DC; + defaultColors[key_avatar_nameInMessageGreen] = 0xff50b232; + defaultColors[key_avatar_nameInMessageCyan] = 0xff379eb8; + defaultColors[key_avatar_nameInMessageBlue] = 0xff4e92cc; + defaultColors[key_avatar_nameInMessagePink] = 0xffCF5C95; + + defaultColors[key_actionBarDefault] = 0xff527da3; + defaultColors[key_actionBarDefaultIcon] = 0xffffffff; + defaultColors[key_actionBarActionModeDefault] = 0xffffffff; + defaultColors[key_actionBarActionModeDefaultTop] = 0x10000000; + defaultColors[key_actionBarActionModeDefaultIcon] = 0xff676a6f; + defaultColors[key_actionBarDefaultTitle] = 0xffffffff; + defaultColors[key_actionBarDefaultSubtitle] = 0xffd5e8f7; + defaultColors[key_actionBarDefaultSelector] = 0xff406d94; + defaultColors[key_actionBarWhiteSelector] = 0x1d000000; + defaultColors[key_actionBarDefaultSearch] = 0xffffffff; + defaultColors[key_actionBarDefaultSearchPlaceholder] = 0x88ffffff; + defaultColors[key_actionBarDefaultSubmenuItem] = 0xff222222; + defaultColors[key_actionBarDefaultSubmenuItemIcon] = 0xff676b70; + defaultColors[key_actionBarDefaultSubmenuBackground] = 0xffffffff; + defaultColors[key_actionBarDefaultSubmenuSeparator] = 0xfff5f5f5; + defaultColors[key_actionBarActionModeDefaultSelector] = 0xffe2e2e2; + defaultColors[key_actionBarTabActiveText] = 0xffffffff; + defaultColors[key_actionBarTabUnactiveText] = 0xffd5e8f7; + defaultColors[key_actionBarTabLine] = 0xffffffff; + defaultColors[key_actionBarTabSelector] = 0xff406d94; + + defaultColors[key_actionBarBrowser] = 0xffffffff; + + defaultColors[key_actionBarDefaultArchived] = 0xff6f7a87; + defaultColors[key_actionBarDefaultArchivedSelector] = 0xff5e6772; + defaultColors[key_actionBarDefaultArchivedIcon] = 0xffffffff; + defaultColors[key_actionBarDefaultArchivedTitle] = 0xffffffff; + defaultColors[key_actionBarDefaultArchivedSearch] = 0xffffffff; + defaultColors[key_actionBarDefaultArchivedSearchPlaceholder] = 0x88ffffff; + + defaultColors[key_chats_onlineCircle] = 0xff4bcb1c; + defaultColors[key_chats_unreadCounter] = 0xff4ecc5e; + defaultColors[key_chats_unreadCounterMuted] = 0xffc6c9cc; + defaultColors[key_chats_unreadCounterText] = 0xffffffff; + defaultColors[key_chats_archiveBackground] = 0xff66a9e0; + defaultColors[key_chats_archivePinBackground] = 0xff9faab3; + defaultColors[key_chats_archiveIcon] = 0xffffffff; + defaultColors[key_chats_archiveText] = 0xffffffff; + defaultColors[key_chats_name] = 0xff222222; + defaultColors[key_chats_nameArchived] = 0xff525252; + defaultColors[key_chats_secretName] = 0xff00a60e; + defaultColors[key_chats_secretIcon] = 0xff19b126; + defaultColors[key_chats_pinnedIcon] = 0xffa8a8a8; + defaultColors[key_chats_message] = 0xff8b8d8f; + defaultColors[key_chats_messageArchived] = 0xff919191; + defaultColors[key_chats_message_threeLines] = 0xff8e9091; + defaultColors[key_chats_draft] = 0xffdd4b39; + defaultColors[key_chats_nameMessage] = 0xff3c7eb0; + defaultColors[key_chats_nameMessageArchived] = 0xff8b8d8f; + defaultColors[key_chats_nameMessage_threeLines] = 0xff424449; + defaultColors[key_chats_nameMessageArchived_threeLines] = 0xff5e5e5e; + defaultColors[key_chats_attachMessage] = 0xff3c7eb0; + defaultColors[key_chats_actionMessage] = 0xff3c7eb0; + defaultColors[key_chats_date] = 0xff95999C; + defaultColors[key_chats_pinnedOverlay] = 0x08000000; + defaultColors[key_chats_tabletSelectedOverlay] = 0x0f000000; + defaultColors[key_chats_sentCheck] = 0xff46aa36; + defaultColors[key_chats_sentReadCheck] = 0xff46aa36; + defaultColors[key_chats_sentClock] = 0xff75bd5e; + defaultColors[key_chats_sentError] = 0xffd55252; + defaultColors[key_chats_sentErrorIcon] = 0xffffffff; + defaultColors[key_chats_verifiedBackground] = 0xff33a8e6; + defaultColors[key_chats_verifiedCheck] = 0xffffffff; + defaultColors[key_chats_muteIcon] = 0xffbdc1c4; + defaultColors[key_chats_mentionIcon] = 0xffffffff; + defaultColors[key_chats_menuBackground] = 0xffffffff; + defaultColors[key_chats_menuItemText] = 0xff444444; + defaultColors[key_chats_menuItemCheck] = 0xff598fba; + defaultColors[key_chats_menuItemIcon] = 0xff889198; + defaultColors[key_chats_menuName] = 0xffffffff; + defaultColors[key_chats_menuPhone] = 0xffffffff; + defaultColors[key_chats_menuPhoneCats] = 0xffc2e5ff; + defaultColors[key_chats_actionIcon] = 0xffffffff; + defaultColors[key_chats_actionBackground] = 0xff65a9e0; + defaultColors[key_chats_actionPressedBackground] = 0xff569dd6; + defaultColors[key_chats_menuTopBackgroundCats] = 0xff598fba; + defaultColors[key_chats_archivePullDownBackground] = 0xffc6c9cc; + defaultColors[key_chats_archivePullDownBackgroundActive] = 0xff66a9e0; + + defaultColors[key_chat_attachCheckBoxCheck] = 0xffffffff; + defaultColors[key_chat_attachCheckBoxBackground] = 0xff39b2f7; + defaultColors[key_chat_attachPhotoBackground] = 0x0c000000; + defaultColors[key_chat_attachActiveTab] = 0xff33a7f5; + defaultColors[key_chat_attachUnactiveTab] = 0xff92999e; + defaultColors[key_chat_attachPermissionImage] = 0xff333333; + defaultColors[key_chat_attachPermissionMark] = 0xffe25050; + defaultColors[key_chat_attachPermissionText] = 0xff6f777a; + defaultColors[key_chat_attachEmptyImage] = 0xffcccccc; + + defaultColors[key_chat_attachIcon] = 0xffffffff; + defaultColors[key_chat_attachGalleryBackground] = 0xff459df5; + defaultColors[key_chat_attachGalleryText] = 0xff2e8de9; + defaultColors[key_chat_attachAudioBackground] = 0xffeb6060; + defaultColors[key_chat_attachAudioText] = 0xffde4747; + defaultColors[key_chat_attachFileBackground] = 0xff34b9f1; + defaultColors[key_chat_attachFileText] = 0xff14a8e4; + defaultColors[key_chat_attachContactBackground] = 0xfff2c04b; + defaultColors[key_chat_attachContactText] = 0xffdfa000; + defaultColors[key_chat_attachLocationBackground] = 0xff60c255; + defaultColors[key_chat_attachLocationText] = 0xff3cab2f; + defaultColors[key_chat_attachPollBackground] = 0xfff2c04b; + defaultColors[key_chat_attachPollText] = 0xffdfa000; + + defaultColors[key_chat_inPollCorrectAnswer] = 0xff60c255; + defaultColors[key_chat_outPollCorrectAnswer] = 0xff60c255; + defaultColors[key_chat_inPollWrongAnswer] = 0xffeb6060; + defaultColors[key_chat_outPollWrongAnswer] = 0xffeb6060; + + defaultColors[key_chat_status] = 0xffd5e8f7; + defaultColors[key_chat_inGreenCall] = 0xff00c853; + defaultColors[key_chat_outGreenCall] = 0xff00c853; + defaultColors[key_chat_lockIcon] = 0xffffffff; + defaultColors[key_chat_muteIcon] = 0xffb1cce3; + defaultColors[key_chat_inBubble] = 0xffffffff; + defaultColors[key_chat_inBubbleSelected] = 0xffecf7fd; + defaultColors[key_chat_inBubbleShadow] = 0xff1d3753; + defaultColors[key_chat_outBubble] = 0xffefffde; + defaultColors[key_chat_outBubbleGradientSelectedOverlay] = 0x14000000; + defaultColors[key_chat_outBubbleSelected] = 0xffd9f7c5; + defaultColors[key_chat_outBubbleShadow] = 0xff1e750c; + defaultColors[key_chat_inMediaIcon] = 0xffffffff; + defaultColors[key_chat_inMediaIconSelected] = 0xffeff8fe; + defaultColors[key_chat_outMediaIcon] = 0xffefffde; + defaultColors[key_chat_outMediaIconSelected] = 0xffe1f8cf; + defaultColors[key_chat_messageTextIn] = 0xff000000; + defaultColors[key_chat_messageTextOut] = 0xff000000; + defaultColors[key_chat_messageLinkIn] = 0xff2678b6; + defaultColors[key_chat_messageLinkOut] = 0xff2678b6; + defaultColors[key_chat_serviceText] = 0xffffffff; + defaultColors[key_chat_serviceLink] = 0xffffffff; + defaultColors[key_chat_serviceIcon] = 0xffffffff; + defaultColors[key_chat_mediaTimeBackground] = 0x66000000; + defaultColors[key_chat_outSentCheck] = 0xff5db050; + defaultColors[key_chat_outSentCheckSelected] = 0xff5db050; + defaultColors[key_chat_outSentCheckRead] = 0xff5db050; + defaultColors[key_chat_outSentCheckReadSelected] = 0xff5db050; + defaultColors[key_chat_outSentClock] = 0xff75bd5e; + defaultColors[key_chat_outSentClockSelected] = 0xff75bd5e; + defaultColors[key_chat_inSentClock] = 0xffa1aab3; + defaultColors[key_chat_inSentClockSelected] = 0xff93bdca; + defaultColors[key_chat_mediaSentCheck] = 0xffffffff; + defaultColors[key_chat_mediaSentClock] = 0xffffffff; + defaultColors[key_chat_inViews] = 0xffa1aab3; + defaultColors[key_chat_inViewsSelected] = 0xff93bdca; + defaultColors[key_chat_outViews] = 0xff6eb257; + defaultColors[key_chat_outViewsSelected] = 0xff6eb257; + defaultColors[key_chat_mediaViews] = 0xffffffff; + defaultColors[key_chat_inMenu] = 0xffb6bdc5; + defaultColors[key_chat_inMenuSelected] = 0xff98c1ce; + defaultColors[key_chat_outMenu] = 0xff91ce7e; + defaultColors[key_chat_outMenuSelected] = 0xff91ce7e; + defaultColors[key_chat_mediaMenu] = 0xffffffff; + defaultColors[key_chat_outInstant] = 0xff55ab4f; + defaultColors[key_chat_outInstantSelected] = 0xff489943; + defaultColors[key_chat_inInstant] = 0xff3a8ccf; + defaultColors[key_chat_inInstantSelected] = 0xff3079b5; + defaultColors[key_chat_sentError] = 0xffdb3535; + defaultColors[key_chat_sentErrorIcon] = 0xffffffff; + defaultColors[key_chat_selectedBackground] = 0x280a90f0; + defaultColors[key_chat_previewDurationText] = 0xffffffff; + defaultColors[key_chat_previewGameText] = 0xffffffff; + defaultColors[key_chat_inPreviewInstantText] = 0xff3a8ccf; + defaultColors[key_chat_outPreviewInstantText] = 0xff55ab4f; + defaultColors[key_chat_secretTimeText] = 0xffe4e2e0; + defaultColors[key_chat_stickerNameText] = 0xffffffff; + defaultColors[key_chat_botButtonText] = 0xffffffff; + defaultColors[key_chat_inForwardedNameText] = 0xff3886c7; + defaultColors[key_chat_outForwardedNameText] = 0xff55ab4f; + defaultColors[key_chat_inPsaNameText] = 0xff5a9c39; + defaultColors[key_chat_outPsaNameText] = 0xff5a9c39; + defaultColors[key_chat_inViaBotNameText] = 0xff3a8ccf; + defaultColors[key_chat_outViaBotNameText] = 0xff55ab4f; + defaultColors[key_chat_stickerViaBotNameText] = 0xffffffff; + defaultColors[key_chat_inReplyLine] = 0xff599fd8; + defaultColors[key_chat_outReplyLine] = 0xff6eb969; + defaultColors[key_chat_stickerReplyLine] = 0xffffffff; + defaultColors[key_chat_inReplyNameText] = 0xff3a8ccf; + defaultColors[key_chat_outReplyNameText] = 0xff55ab4f; + defaultColors[key_chat_stickerReplyNameText] = 0xffffffff; + defaultColors[key_chat_inReplyMessageText] = 0xff000000; + defaultColors[key_chat_outReplyMessageText] = 0xff000000; + defaultColors[key_chat_inReplyMediaMessageText] = 0xffa1aab3; + defaultColors[key_chat_outReplyMediaMessageText] = 0xff65b05b; + defaultColors[key_chat_inReplyMediaMessageSelectedText] = 0xff89b4c1; + defaultColors[key_chat_outReplyMediaMessageSelectedText] = 0xff65b05b; + defaultColors[key_chat_stickerReplyMessageText] = 0xffffffff; + defaultColors[key_chat_inPreviewLine] = 0xff70b4e8; + defaultColors[key_chat_outPreviewLine] = 0xff88c97b; + defaultColors[key_chat_inSiteNameText] = 0xff3a8ccf; + defaultColors[key_chat_outSiteNameText] = 0xff55ab4f; + defaultColors[key_chat_inContactNameText] = 0xff4e9ad4; + defaultColors[key_chat_outContactNameText] = 0xff55ab4f; + defaultColors[key_chat_inContactPhoneText] = 0xff2f3438; + defaultColors[key_chat_inContactPhoneSelectedText] = 0xff2f3438; + defaultColors[key_chat_outContactPhoneText] = 0xff354234; + defaultColors[key_chat_outContactPhoneSelectedText] = 0xff354234; + defaultColors[key_chat_mediaProgress] = 0xffffffff; + defaultColors[key_chat_inAudioProgress] = 0xffffffff; + defaultColors[key_chat_outAudioProgress] = 0xffefffde; + defaultColors[key_chat_inAudioSelectedProgress] = 0xffeff8fe; + defaultColors[key_chat_outAudioSelectedProgress] = 0xffe1f8cf; + defaultColors[key_chat_mediaTimeText] = 0xffffffff; + defaultColors[key_chat_inAdminText] = 0xffc0c6cb; + defaultColors[key_chat_inAdminSelectedText] = 0xff89b4c1; + defaultColors[key_chat_outAdminText] = 0xff70b15c; + defaultColors[key_chat_outAdminSelectedText] = 0xff70b15c; + defaultColors[key_chat_inTimeText] = 0xffa1aab3; + defaultColors[key_chat_inTimeSelectedText] = 0xff89b4c1; + defaultColors[key_chat_outTimeText] = 0xff70b15c; + defaultColors[key_chat_outTimeSelectedText] = 0xff70b15c; + defaultColors[key_chat_inAudioPerformerText] = 0xff2f3438; + defaultColors[key_chat_inAudioPerformerSelectedText] = 0xff2f3438; + defaultColors[key_chat_outAudioPerformerText] = 0xff354234; + defaultColors[key_chat_outAudioPerformerSelectedText] = 0xff354234; + defaultColors[key_chat_inAudioTitleText] = 0xff4e9ad4; + defaultColors[key_chat_outAudioTitleText] = 0xff55ab4f; + defaultColors[key_chat_inAudioDurationText] = 0xffa1aab3; + defaultColors[key_chat_outAudioDurationText] = 0xff65b05b; + defaultColors[key_chat_inAudioDurationSelectedText] = 0xff89b4c1; + defaultColors[key_chat_outAudioDurationSelectedText] = 0xff65b05b; + defaultColors[key_chat_inAudioSeekbar] = 0xffe4eaf0; + defaultColors[key_chat_inAudioCacheSeekbar] = 0x3fe4eaf0; + defaultColors[key_chat_outAudioSeekbar] = 0xffbbe3ac; + defaultColors[key_chat_outAudioCacheSeekbar] = 0x3fbbe3ac; + defaultColors[key_chat_inAudioSeekbarSelected] = 0xffbcdee8; + defaultColors[key_chat_outAudioSeekbarSelected] = 0xffa9dd96; + defaultColors[key_chat_inAudioSeekbarFill] = 0xff72b5e8; + defaultColors[key_chat_outAudioSeekbarFill] = 0xff78c272; + defaultColors[key_chat_inVoiceSeekbar] = 0xffdee5eb; + defaultColors[key_chat_outVoiceSeekbar] = 0xffbbe3ac; + defaultColors[key_chat_inVoiceSeekbarSelected] = 0xffbcdee8; + defaultColors[key_chat_outVoiceSeekbarSelected] = 0xffa9dd96; + defaultColors[key_chat_inVoiceSeekbarFill] = 0xff72b5e8; + defaultColors[key_chat_outVoiceSeekbarFill] = 0xff78c272; + defaultColors[key_chat_inFileProgress] = 0xffebf0f5; + defaultColors[key_chat_outFileProgress] = 0xffdaf5c3; + defaultColors[key_chat_inFileProgressSelected] = 0xffcbeaf6; + defaultColors[key_chat_outFileProgressSelected] = 0xffc5eca7; + defaultColors[key_chat_inFileNameText] = 0xff4e9ad4; + defaultColors[key_chat_outFileNameText] = 0xff55ab4f; + defaultColors[key_chat_inFileInfoText] = 0xffa1aab3; + defaultColors[key_chat_outFileInfoText] = 0xff65b05b; + defaultColors[key_chat_inFileInfoSelectedText] = 0xff89b4c1; + defaultColors[key_chat_outFileInfoSelectedText] = 0xff65b05b; + defaultColors[key_chat_inFileBackground] = 0xffebf0f5; + defaultColors[key_chat_outFileBackground] = 0xffdaf5c3; + defaultColors[key_chat_inFileBackgroundSelected] = 0xffcbeaf6; + defaultColors[key_chat_outFileBackgroundSelected] = 0xffc5eca7; + defaultColors[key_chat_inVenueInfoText] = 0xffa1aab3; + defaultColors[key_chat_outVenueInfoText] = 0xff65b05b; + defaultColors[key_chat_inVenueInfoSelectedText] = 0xff89b4c1; + defaultColors[key_chat_outVenueInfoSelectedText] = 0xff65b05b; + defaultColors[key_chat_mediaInfoText] = 0xffffffff; + defaultColors[key_chat_linkSelectBackground] = 0x3362a9e3; + defaultColors[key_chat_outLinkSelectBackground] = 0x3362a9e3; + defaultColors[key_chat_textSelectBackground] = 0x6662a9e3; + defaultColors[key_chat_emojiPanelBackground] = 0xfff0f2f5; + defaultColors[key_chat_emojiSearchBackground] = 0xffe5e9ee; + defaultColors[key_chat_emojiSearchIcon] = 0xff94a1af; + defaultColors[key_chat_emojiPanelShadowLine] = 0x12000000; + defaultColors[key_chat_emojiPanelEmptyText] = 0xff949ba1; + defaultColors[key_chat_emojiPanelIcon] = 0xff9da4ab; + defaultColors[key_chat_emojiBottomPanelIcon] = 0xff8c9197; + defaultColors[key_chat_emojiPanelIconSelected] = 0xff5E6976; + defaultColors[key_chat_emojiPanelStickerPackSelector] = 0xffe2e5e7; + defaultColors[key_chat_emojiPanelStickerPackSelectorLine] = 0xff56abf0; + defaultColors[key_chat_emojiPanelBackspace] = 0xff8c9197; + defaultColors[key_chat_emojiPanelTrendingTitle] = 0xff222222; + defaultColors[key_chat_emojiPanelStickerSetName] = 0xff828b94; + defaultColors[key_chat_emojiPanelStickerSetNameHighlight] = 0xff278ddb; + defaultColors[key_chat_emojiPanelStickerSetNameIcon] = 0xffb1b6bc; + defaultColors[key_chat_emojiPanelTrendingDescription] = 0xff8a8a8a; + defaultColors[key_chat_botKeyboardButtonText] = 0xff36474f; + defaultColors[key_chat_botKeyboardButtonBackground] = 0xffe4e7e9; + defaultColors[key_chat_botKeyboardButtonBackgroundPressed] = 0xffccd1d4; + defaultColors[key_chat_unreadMessagesStartArrowIcon] = 0xffa2b5c7; + defaultColors[key_chat_unreadMessagesStartText] = 0xff5695cc; + defaultColors[key_chat_unreadMessagesStartBackground] = 0xffffffff; + defaultColors[key_chat_inLocationBackground] = 0xffebf0f5; + defaultColors[key_chat_inLocationIcon] = 0xffa2b5c7; + defaultColors[key_chat_outLocationIcon] = 0xff87bf78; + defaultColors[key_chat_inContactBackground] = 0xff72b5e8; + defaultColors[key_chat_inContactIcon] = 0xffffffff; + defaultColors[key_chat_outContactBackground] = 0xff78c272; + defaultColors[key_chat_outContactIcon] = 0xffefffde; + defaultColors[key_chat_searchPanelIcons] = 0xff676a6f; + defaultColors[key_chat_searchPanelText] = 0xff676a6f; + defaultColors[key_chat_secretChatStatusText] = 0xff7f7f7f; + defaultColors[key_chat_fieldOverlayText] = 0xff3a8ccf; + defaultColors[key_chat_stickersHintPanel] = 0xffffffff; + defaultColors[key_chat_replyPanelIcons] = 0xff57a8e6; + defaultColors[key_chat_replyPanelClose] = 0xff8e959b; + defaultColors[key_chat_replyPanelName] = 0xff3a8ccf; + defaultColors[key_chat_replyPanelLine] = 0xffe8e8e8; + defaultColors[key_chat_messagePanelBackground] = 0xffffffff; + defaultColors[key_chat_messagePanelText] = 0xff000000; + defaultColors[key_chat_messagePanelHint] = 0xffa4acb3; + defaultColors[key_chat_messagePanelCursor] = 0xff54a1db; + defaultColors[key_chat_messagePanelShadow] = 0xff000000; + defaultColors[key_chat_messagePanelIcons] = 0xff8e959b; + defaultColors[key_chat_recordedVoicePlayPause] = 0xffffffff; + defaultColors[key_chat_recordedVoiceDot] = 0xffda564d; + defaultColors[key_chat_recordedVoiceBackground] = 0xff5DADE8; + defaultColors[key_chat_recordedVoiceProgress] = 0xffB1DEFF; + defaultColors[key_chat_recordedVoiceProgressInner] = 0xffffffff; + defaultColors[key_chat_recordVoiceCancel] = 0xff3A95D4; + defaultColors[key_chat_messagePanelSend] = 0xff62b0eb; + defaultColors[key_chat_messagePanelVoiceLock] = 0xffa4a4a4; + defaultColors[key_chat_messagePanelVoiceLockBackground] = 0xffffffff; + defaultColors[key_chat_messagePanelVoiceLockShadow] = 0xff000000; + defaultColors[key_chat_recordTime] = 0xff8e959b; + defaultColors[key_chat_emojiPanelNewTrending] = 0xff4da6ea; + defaultColors[key_chat_gifSaveHintText] = 0xffffffff; + defaultColors[key_chat_gifSaveHintBackground] = 0xcc111111; + defaultColors[key_chat_goDownButton] = 0xffffffff; + defaultColors[key_chat_goDownButtonIcon] = 0xff8e959b; + defaultColors[key_chat_goDownButtonCounter] = 0xffffffff; + defaultColors[key_chat_goDownButtonCounterBackground] = 0xff4da2e8; + defaultColors[key_chat_messagePanelCancelInlineBot] = 0xffadadad; + defaultColors[key_chat_messagePanelVoicePressed] = 0xffffffff; + defaultColors[key_chat_messagePanelVoiceBackground] = 0xff5DA6DE; + defaultColors[key_chat_messagePanelVoiceDelete] = 0xff737373; + defaultColors[key_chat_messagePanelVoiceDuration] = 0xffffffff; + defaultColors[key_chat_inlineResultIcon] = 0xff5795cc; + defaultColors[key_chat_topPanelBackground] = 0xffffffff; + defaultColors[key_chat_topPanelClose] = 0xff8b969b; + defaultColors[key_chat_topPanelLine] = 0xff6c9fd2; + defaultColors[key_chat_topPanelTitle] = 0xff3a8ccf; + defaultColors[key_chat_topPanelMessage] = 0xff878e91; + defaultColors[key_chat_addContact] = 0xff4a82b5; + defaultColors[key_chat_inLoader] = 0xff72b5e8; + defaultColors[key_chat_inLoaderSelected] = 0xff65abe0; + defaultColors[key_chat_outLoader] = 0xff78c272; + defaultColors[key_chat_outLoaderSelected] = 0xff6ab564; + defaultColors[key_chat_inLoaderPhoto] = 0xffa2b8c8; + defaultColors[key_chat_mediaLoaderPhoto] = 0x66000000; + defaultColors[key_chat_mediaLoaderPhotoSelected] = 0x7f000000; + defaultColors[key_chat_mediaLoaderPhotoIcon] = 0xffffffff; + defaultColors[key_chat_mediaLoaderPhotoIconSelected] = 0xffd9d9d9; + defaultColors[key_chat_serviceBackgroundSelector] = 0x20ffffff; + + defaultColors[key_profile_creatorIcon] = 0xff3a95d5; + defaultColors[key_profile_actionIcon] = 0xff81868a; + defaultColors[key_profile_actionBackground] = 0xffffffff; + defaultColors[key_profile_actionPressedBackground] = 0xfff2f2f2; + defaultColors[key_profile_verifiedBackground] = 0xffb2d6f8; + defaultColors[key_profile_verifiedCheck] = 0xff4983b8; + defaultColors[key_profile_title] = 0xffffffff; + defaultColors[key_profile_status] = 0xffd7eafa; + + defaultColors[key_profile_tabText] = 0xff878c90; + defaultColors[key_profile_tabSelectedText] = 0xff3a95d5; + defaultColors[key_profile_tabSelectedLine] = 0xff4fa6e9; + defaultColors[key_profile_tabSelector] = 0x0f000000; + + defaultColors[key_player_actionBarSelector] = 0x0f000000; + defaultColors[key_player_actionBarTitle] = 0xff2f3438; + defaultColors[key_player_actionBarSubtitle] = 0xff8a8a8a; + defaultColors[key_player_actionBarItems] = 0xff8a8a8a; + defaultColors[key_player_background] = 0xffffffff; + defaultColors[key_player_time] = 0xff8c9296; + defaultColors[key_player_progressBackground] = 0xffEBEDF0; + defaultColors[key_player_progressCachedBackground] = 0xffC5DCF0; + defaultColors[key_player_progress] = 0xff54AAEB; + defaultColors[key_player_button] = 0xff333333; + defaultColors[key_player_buttonActive] = 0xff4ca8ea; + + defaultColors[key_sheet_scrollUp] = 0xffe1e4e8; + defaultColors[key_sheet_other] = 0xffc9cdd3; + + defaultColors[key_files_folderIcon] = 0xffffffff; + defaultColors[key_files_folderIconBackground] = 0xff5dafeb; + defaultColors[key_files_iconText] = 0xffffffff; + + defaultColors[key_sessions_devicesImage] = 0xff969696; + + defaultColors[key_passport_authorizeBackground] = 0xff45abef; + defaultColors[key_passport_authorizeBackgroundSelected] = 0xff409ddb; + defaultColors[key_passport_authorizeText] = 0xffffffff; + + defaultColors[key_location_sendLocationBackground] = 0xff469df6; + defaultColors[key_location_sendLocationIcon] = 0xffffffff; + defaultColors[key_location_sendLocationText] = 0xff1c8ad8; + defaultColors[key_location_sendLiveLocationBackground] = 0xff4fc244; + defaultColors[key_location_sendLiveLocationIcon] = 0xffffffff; + defaultColors[key_location_sendLiveLocationText] = 0xff36ab24; + defaultColors[key_location_liveLocationProgress] = 0xff359fe5; + defaultColors[key_location_placeLocationBackground] = 0xff4ca8ea; + defaultColors[key_location_actionIcon] = 0xff3a4045; + defaultColors[key_location_actionActiveIcon] = 0xff4290e6; + defaultColors[key_location_actionBackground] = 0xffffffff; + defaultColors[key_location_actionPressedBackground] = 0xfff2f2f2; + + defaultColors[key_dialog_liveLocationProgress] = 0xff359fe5; + + defaultColors[key_calls_callReceivedGreenIcon] = 0xff00c853; + defaultColors[key_calls_callReceivedRedIcon] = 0xffff4848; + + defaultColors[key_featuredStickers_addedIcon] = 0xff50a8eb; + defaultColors[key_featuredStickers_buttonProgress] = 0xffffffff; + defaultColors[key_featuredStickers_addButton] = 0xff50a8eb; + defaultColors[key_featuredStickers_addButtonPressed] = 0xff439bde; + defaultColors[key_featuredStickers_removeButtonText] = 0xff5093d3; + defaultColors[key_featuredStickers_buttonText] = 0xffffffff; + defaultColors[key_featuredStickers_unread] = 0xff4da6ea; + + defaultColors[key_inappPlayerPerformer] = 0xff2f3438; + defaultColors[key_inappPlayerTitle] = 0xff2f3438; + defaultColors[key_inappPlayerBackground] = 0xffffffff; + defaultColors[key_inappPlayerPlayPause] = 0xff62b0eb; + defaultColors[key_inappPlayerClose] = 0xff8b969b; + + defaultColors[key_returnToCallBackground] = 0xff44a1e3; + defaultColors[key_returnToCallMutedBackground] = 0xff9DA7B1; + defaultColors[key_returnToCallText] = 0xffffffff; + + defaultColors[key_sharedMedia_startStopLoadIcon] = 0xff36a2ee; + defaultColors[key_sharedMedia_linkPlaceholder] = 0xfff0f3f5; + defaultColors[key_sharedMedia_linkPlaceholderText] = 0xffb7bec3; + defaultColors[key_sharedMedia_photoPlaceholder] = 0xffedf3f7; + + defaultColors[key_checkbox] = 0xff5ec245; + defaultColors[key_checkboxCheck] = 0xffffffff; + defaultColors[key_checkboxDisabled] = 0xffb0b9c2; + + defaultColors[key_stickers_menu] = 0xffb6bdc5; + defaultColors[key_stickers_menuSelector] = 0x0f000000; + + defaultColors[key_changephoneinfo_image2] = 0xff50a7ea; + + defaultColors[key_groupcreate_hintText] = 0xffa1aab3; + defaultColors[key_groupcreate_cursor] = 0xff52a3db; + defaultColors[key_groupcreate_sectionShadow] = 0xff000000; + defaultColors[key_groupcreate_sectionText] = 0xff7c8288; + defaultColors[key_groupcreate_spanText] = 0xff222222; + defaultColors[key_groupcreate_spanBackground] = 0xfff2f2f2; + defaultColors[key_groupcreate_spanDelete] = 0xffffffff; + + defaultColors[key_contacts_inviteBackground] = 0xff55be61; + defaultColors[key_contacts_inviteText] = 0xffffffff; + + defaultColors[key_login_progressInner] = 0xffe1eaf2; + defaultColors[key_login_progressOuter] = 0xff62a0d0; + + defaultColors[key_picker_enabledButton] = 0xff19a7e8; + defaultColors[key_picker_disabledButton] = 0xff999999; + defaultColors[key_picker_badge] = 0xff29b6f7; + defaultColors[key_picker_badgeText] = 0xffffffff; + + defaultColors[key_chat_botSwitchToInlineText] = 0xff4391cc; + + defaultColors[key_undo_background] = 0xea272f38; + defaultColors[key_undo_cancelColor] = 0xff85caff; + defaultColors[key_undo_infoColor] = 0xffffffff; + + defaultColors[key_chat_outTextSelectionHighlight] = 0x2E3F9923; + defaultColors[key_chat_inTextSelectionHighlight] = 0x5062A9E3; + defaultColors[key_chat_TextSelectionCursor] = 0xFF419FE8; + defaultColors[key_chat_outTextSelectionCursor] = 0xFF419FE8; + defaultColors[key_chat_outBubbleLocationPlaceholder] = 0x1e307311; + defaultColors[key_chat_inBubbleLocationPlaceholder] = 0x1e506373; + defaultColors[key_chat_BlurAlpha] = 0xFF000000; + + defaultColors[key_statisticChartSignature] = 0x7f252529; + defaultColors[key_statisticChartSignatureAlpha] = 0x7f252529; + defaultColors[key_statisticChartHintLine] = 0x1a182D3B; + defaultColors[key_statisticChartActiveLine] = 0x33000000; + defaultColors[key_statisticChartInactivePickerChart] = 0x99e2eef9; + defaultColors[key_statisticChartActivePickerChart] = 0xd8baccd9; + + defaultColors[key_statisticChartRipple] = 0x2c7e9db7; + defaultColors[key_statisticChartBackZoomColor] = 0xff108BE3; + defaultColors[key_statisticChartChevronColor] = 0xffD2D5D7; + + defaultColors[key_statisticChartLine_blue] = 0xff327FE5; + defaultColors[key_statisticChartLine_green] = 0xff61C752; + defaultColors[key_statisticChartLine_red] = 0xffE05356; + defaultColors[key_statisticChartLine_golden] = 0xffEBA52D; + defaultColors[key_statisticChartLine_lightblue] = 0xff58A8ED; + defaultColors[key_statisticChartLine_lightgreen] = 0xff8FCF39; + defaultColors[key_statisticChartLine_orange] = 0xffF28C39; + defaultColors[key_statisticChartLine_indigo] = 0xff7F79F3; + defaultColors[key_statisticChartLine_purple] = 0xff9F79E8; + defaultColors[key_statisticChartLine_cyan] = 0xff40D0CA; + defaultColors[key_statisticChartLineEmpty] = 0xFFEEEEEE; + + defaultColors[key_color_blue] = 0xff327FE5; + defaultColors[key_color_green] = 0xff61C752; + defaultColors[key_color_red] = 0xffE05356; + defaultColors[key_color_yellow] = 0xffEBA52D; + defaultColors[key_color_lightblue] = 0xff58A8ED; + defaultColors[key_color_lightgreen] = 0xff8FCF39; + defaultColors[key_color_orange] = 0xffF28C39; + defaultColors[key_color_purple] = 0xff9F79E8; + defaultColors[key_color_cyan] = 0xff40D0CA; + + defaultColors[key_voipgroup_checkMenu] = 0xff6BB6F9; + defaultColors[key_voipgroup_muteButton] = 0xff77E55C; + defaultColors[key_voipgroup_muteButton2] = 0xff7DDCAA; + defaultColors[key_voipgroup_muteButton3] = 0xff56C7FE; + defaultColors[key_voipgroup_searchText] = 0xffffffff; + defaultColors[key_voipgroup_searchPlaceholder] = 0xff858D94; + defaultColors[key_voipgroup_searchBackground] = 0xff303B47; + defaultColors[key_voipgroup_leaveCallMenu] = 0xffFF7575; + defaultColors[key_voipgroup_scrollUp] = 0xff394654; + defaultColors[key_voipgroup_soundButton] = 0x7d2C414D; + defaultColors[key_voipgroup_soundButtonActive] = 0x7d22A4EB; + defaultColors[key_voipgroup_soundButtonActiveScrolled] = 0x8233B4FF; + defaultColors[key_voipgroup_soundButton2] = 0x7d28593A; + defaultColors[key_voipgroup_soundButtonActive2] = 0x7d18B751; + defaultColors[key_voipgroup_soundButtonActive2Scrolled] = 0x8224BF46; + defaultColors[key_voipgroup_leaveButton] = 0x7dF75C5C; + defaultColors[key_voipgroup_leaveButtonScrolled] = 0x82D14D54; + defaultColors[key_voipgroup_connectingProgress] = 0xff28BAFF; + defaultColors[key_voipgroup_disabledButton] = 0xff1C2229; + defaultColors[key_voipgroup_disabledButtonActive] = 0xff2C3A45; + defaultColors[key_voipgroup_disabledButtonActiveScrolled] = 0x8277A1FC; + defaultColors[key_voipgroup_unmuteButton] = 0xff539EF8; + defaultColors[key_voipgroup_unmuteButton2] = 0xff66D4FB; + defaultColors[key_voipgroup_actionBarUnscrolled] = 0xff191F26; + defaultColors[key_voipgroup_listViewBackgroundUnscrolled] = 0xff222A33; + defaultColors[key_voipgroup_lastSeenTextUnscrolled] = 0xff858D94; + defaultColors[key_voipgroup_mutedIconUnscrolled] = 0xff7E868C; + defaultColors[key_voipgroup_actionBar] = 0xff0F1317; + defaultColors[key_voipgroup_actionBarItems] = 0xffffffff; + defaultColors[key_voipgroup_actionBarItemsSelector] = 0x1eBADBFF; + defaultColors[key_voipgroup_mutedByAdminIcon] = 0xffFF7070; + defaultColors[key_voipgroup_mutedIcon] = 0xff6F7980; + defaultColors[key_voipgroup_lastSeenText] = 0xff79838A; + defaultColors[key_voipgroup_nameText] = 0xffffffff; + defaultColors[key_voipgroup_listViewBackground] = 0xff1C2229; + defaultColors[key_voipgroup_dialogBackground] = 0xff1C2229; + defaultColors[key_voipgroup_listeningText] = 0xff4DB8FF; + defaultColors[key_voipgroup_speakingText] = 0xff77EE7D; + defaultColors[key_voipgroup_listSelector] = 0x0effffff; + defaultColors[key_voipgroup_inviteMembersBackground] = 0xff222A33; + defaultColors[key_voipgroup_overlayBlue1] = 0xff2BCEFF; + defaultColors[key_voipgroup_overlayBlue2] = 0xff0976E3; + defaultColors[key_voipgroup_overlayGreen1] = 0xff12B522; + defaultColors[key_voipgroup_overlayGreen2] = 0xff00D6C1; + defaultColors[key_voipgroup_topPanelBlue1] = 0xff60C7FB; + defaultColors[key_voipgroup_topPanelBlue2] = 0xff519FF9; + defaultColors[key_voipgroup_topPanelGreen1] = 0xff52CE5D; + defaultColors[key_voipgroup_topPanelGreen2] = 0xff00B1C0; + defaultColors[key_voipgroup_topPanelGray] = 0xff8599aa; + + defaultColors[key_voipgroup_overlayAlertGradientMuted] = 0xff236D92; + defaultColors[key_voipgroup_overlayAlertGradientMuted2] = 0xff2C4D6B; + defaultColors[key_voipgroup_overlayAlertGradientUnmuted] = 0xff0C8A8C; + defaultColors[key_voipgroup_overlayAlertGradientUnmuted2] = 0xff284C75; + defaultColors[key_voipgroup_mutedByAdminGradient] = 0xff57A4FE; + defaultColors[key_voipgroup_mutedByAdminGradient2] = 0xffF05459; + defaultColors[key_voipgroup_mutedByAdminGradient3] = 0xff766EE9; + defaultColors[key_voipgroup_overlayAlertMutedByAdmin] = 0xff67709E; + defaultColors[key_voipgroup_overlayAlertMutedByAdmin2] = 0xff2F5078; + defaultColors[key_voipgroup_mutedByAdminMuteButton] = 0x7F78A3FF; + defaultColors[key_voipgroup_mutedByAdminMuteButtonDisabled] = 0x3378A3FF; + defaultColors[key_voipgroup_windowBackgroundWhiteInputField] = 0xffdbdbdb; + defaultColors[key_voipgroup_windowBackgroundWhiteInputFieldActivated] = 0xff37a9f0; + + defaultColors[key_chat_outReactionButtonBackground] = 0xff78c272; + defaultColors[key_chat_inReactionButtonBackground] = 0xff72b5e8; + defaultColors[key_chat_inReactionButtonText] = 0xff3a8ccf; + defaultColors[key_chat_outReactionButtonText] = 0xff55ab4f; + defaultColors[key_chat_inReactionButtonTextSelected] = 0xffffffff; + defaultColors[key_chat_outReactionButtonTextSelected] = 0xffffffff; + + defaultColors[key_premiumGradient0] = 0xff4ACD43; + defaultColors[key_premiumGradient1] = 0xff55A5FF; + defaultColors[key_premiumGradient2] = 0xffA767FF; + defaultColors[key_premiumGradient3] = 0xffDB5C9D; + defaultColors[key_premiumGradient4] = 0xffF38926; + + defaultColors[key_premiumGradientBackground1] = 0xff55A5FF; + defaultColors[key_premiumGradientBackground2] = 0xffA767FF; + defaultColors[key_premiumGradientBackground3] = 0xffDB5C9D; + defaultColors[key_premiumGradientBackground4] = 0xffF38926; + defaultColors[key_premiumGradientBackgroundOverlay] = Color.WHITE; + defaultColors[key_premiumStartGradient1] = 0xffFFFFFF; + defaultColors[key_premiumStartGradient2] = 0xffE3ECFA; + defaultColors[key_premiumStartSmallStarsColor] = ColorUtils.setAlphaComponent(Color.WHITE, 90); + defaultColors[key_premiumStartSmallStarsColor2] = ColorUtils.setAlphaComponent(Color.WHITE, 90); + defaultColors[key_premiumGradientBottomSheet1] = 0xff5B9DE7; + defaultColors[key_premiumGradientBottomSheet2] = 0xffAB87DD; + defaultColors[key_premiumGradientBottomSheet3] = 0xffE794BE; + defaultColors[key_topics_unreadCounter] = 0xff4ecc5e; + defaultColors[key_topics_unreadCounterMuted] = 0xff8b8d8f; + + return defaultColors; + } + + public static SparseArray createColorKeysMap() { + SparseArray colorKeysMap = new SparseArray<>(); + colorKeysMap.put(key_wallpaperFileOffset, "wallpaperFileOffset"); + colorKeysMap.put(key_dialogBackground, "dialogBackground"); + colorKeysMap.put(key_dialogBackgroundGray, "dialogBackgroundGray"); + colorKeysMap.put(key_dialogTextBlack, "dialogTextBlack"); + colorKeysMap.put(key_dialogTextLink, "dialogTextLink"); + colorKeysMap.put(key_dialogLinkSelection, "dialogLinkSelection"); + colorKeysMap.put(key_dialogTextRed, "dialogTextRed"); + colorKeysMap.put(key_dialogTextBlue, "dialogTextBlue"); + colorKeysMap.put(key_dialogTextBlue2, "dialogTextBlue2"); + colorKeysMap.put(key_dialogTextBlue4, "dialogTextBlue4"); + colorKeysMap.put(key_dialogTextGray, "dialogTextGray"); + colorKeysMap.put(key_dialogTextGray2, "dialogTextGray2"); + colorKeysMap.put(key_dialogTextGray3, "dialogTextGray3"); + colorKeysMap.put(key_dialogTextGray4, "dialogTextGray4"); + colorKeysMap.put(key_dialogTextHint, "dialogTextHint"); + colorKeysMap.put(key_dialogInputField, "dialogInputField"); + colorKeysMap.put(key_dialogInputFieldActivated, "dialogInputFieldActivated"); + colorKeysMap.put(key_dialogCheckboxSquareBackground, "dialogCheckboxSquareBackground"); + colorKeysMap.put(key_dialogCheckboxSquareCheck, "dialogCheckboxSquareCheck"); + colorKeysMap.put(key_dialogCheckboxSquareUnchecked, "dialogCheckboxSquareUnchecked"); + colorKeysMap.put(key_dialogCheckboxSquareDisabled, "dialogCheckboxSquareDisabled"); + colorKeysMap.put(key_dialogScrollGlow, "dialogScrollGlow"); + colorKeysMap.put(key_dialogRoundCheckBox, "dialogRoundCheckBox"); + colorKeysMap.put(key_dialogRoundCheckBoxCheck, "dialogRoundCheckBoxCheck"); + colorKeysMap.put(key_dialogRadioBackground, "dialogRadioBackground"); + colorKeysMap.put(key_dialogRadioBackgroundChecked, "dialogRadioBackgroundChecked"); + colorKeysMap.put(key_dialogLineProgress, "dialogLineProgress"); + colorKeysMap.put(key_dialogLineProgressBackground, "dialogLineProgressBackground"); + colorKeysMap.put(key_dialogButton, "dialogButton"); + colorKeysMap.put(key_dialogButtonSelector, "dialogButtonSelector"); + colorKeysMap.put(key_dialogIcon, "dialogIcon"); + colorKeysMap.put(key_dialogRedIcon, "dialogRedIcon"); + colorKeysMap.put(key_dialogGrayLine, "dialogGrayLine"); + colorKeysMap.put(key_dialogTopBackground, "dialogTopBackground"); + colorKeysMap.put(key_dialogCameraIcon, "dialogCameraIcon"); + colorKeysMap.put(key_dialog_inlineProgressBackground, "dialog_inlineProgressBackground"); + colorKeysMap.put(key_dialog_inlineProgress, "dialog_inlineProgress"); + colorKeysMap.put(key_dialogSearchBackground, "dialogSearchBackground"); + colorKeysMap.put(key_dialogSearchHint, "dialogSearchHint"); + colorKeysMap.put(key_dialogSearchIcon, "dialogSearchIcon"); + colorKeysMap.put(key_dialogSearchText, "dialogSearchText"); + colorKeysMap.put(key_dialogFloatingButton, "dialogFloatingButton"); + colorKeysMap.put(key_dialogFloatingButtonPressed, "dialogFloatingButtonPressed"); + colorKeysMap.put(key_dialogFloatingIcon, "dialogFloatingIcon"); + colorKeysMap.put(key_dialogShadowLine, "dialogShadowLine"); + colorKeysMap.put(key_dialogEmptyImage, "dialogEmptyImage"); + colorKeysMap.put(key_dialogEmptyText, "dialogEmptyText"); + colorKeysMap.put(key_dialogSwipeRemove, "dialogSwipeRemove"); + colorKeysMap.put(key_dialogReactionMentionBackground, "dialogReactionMentionBackground"); + colorKeysMap.put(key_windowBackgroundWhite, "windowBackgroundWhite"); + colorKeysMap.put(key_windowBackgroundUnchecked, "windowBackgroundUnchecked"); + colorKeysMap.put(key_windowBackgroundChecked, "windowBackgroundChecked"); + colorKeysMap.put(key_windowBackgroundCheckText, "windowBackgroundCheckText"); + colorKeysMap.put(key_progressCircle, "progressCircle"); + colorKeysMap.put(key_listSelector, "listSelectorSDK21"); + colorKeysMap.put(key_windowBackgroundWhiteInputField, "windowBackgroundWhiteInputField"); + colorKeysMap.put(key_windowBackgroundWhiteInputFieldActivated, "windowBackgroundWhiteInputFieldActivated"); + colorKeysMap.put(key_windowBackgroundWhiteGrayIcon, "windowBackgroundWhiteGrayIcon"); + colorKeysMap.put(key_windowBackgroundWhiteBlueText, "windowBackgroundWhiteBlueText"); + colorKeysMap.put(key_windowBackgroundWhiteBlueText2, "windowBackgroundWhiteBlueText2"); + colorKeysMap.put(key_windowBackgroundWhiteBlueText3, "windowBackgroundWhiteBlueText3"); + colorKeysMap.put(key_windowBackgroundWhiteBlueText4, "windowBackgroundWhiteBlueText4"); + colorKeysMap.put(key_windowBackgroundWhiteBlueText5, "windowBackgroundWhiteBlueText5"); + colorKeysMap.put(key_windowBackgroundWhiteBlueText6, "windowBackgroundWhiteBlueText6"); + colorKeysMap.put(key_windowBackgroundWhiteBlueText7, "windowBackgroundWhiteBlueText7"); + colorKeysMap.put(key_windowBackgroundWhiteBlueButton, "windowBackgroundWhiteBlueButton"); + colorKeysMap.put(key_windowBackgroundWhiteBlueIcon, "windowBackgroundWhiteBlueIcon"); + colorKeysMap.put(key_windowBackgroundWhiteGreenText, "windowBackgroundWhiteGreenText"); + colorKeysMap.put(key_windowBackgroundWhiteGreenText2, "windowBackgroundWhiteGreenText2"); + colorKeysMap.put(key_windowBackgroundWhiteRedText3, "windowBackgroundWhiteRedText3"); + colorKeysMap.put(key_windowBackgroundWhiteRedText4, "windowBackgroundWhiteRedText4"); + colorKeysMap.put(key_windowBackgroundWhiteGrayText, "windowBackgroundWhiteGrayText"); + colorKeysMap.put(key_windowBackgroundWhiteGrayText2, "windowBackgroundWhiteGrayText2"); + colorKeysMap.put(key_windowBackgroundWhiteGrayText3, "windowBackgroundWhiteGrayText3"); + colorKeysMap.put(key_windowBackgroundWhiteGrayText4, "windowBackgroundWhiteGrayText4"); + colorKeysMap.put(key_windowBackgroundWhiteGrayText5, "windowBackgroundWhiteGrayText5"); + colorKeysMap.put(key_windowBackgroundWhiteGrayText6, "windowBackgroundWhiteGrayText6"); + colorKeysMap.put(key_windowBackgroundWhiteGrayText7, "windowBackgroundWhiteGrayText7"); + colorKeysMap.put(key_windowBackgroundWhiteGrayText8, "windowBackgroundWhiteGrayText8"); + colorKeysMap.put(key_windowBackgroundWhiteBlackText, "windowBackgroundWhiteBlackText"); + colorKeysMap.put(key_windowBackgroundWhiteHintText, "windowBackgroundWhiteHintText"); + colorKeysMap.put(key_windowBackgroundWhiteValueText, "windowBackgroundWhiteValueText"); + colorKeysMap.put(key_windowBackgroundWhiteLinkText, "windowBackgroundWhiteLinkText"); + colorKeysMap.put(key_windowBackgroundWhiteLinkSelection, "windowBackgroundWhiteLinkSelection"); + colorKeysMap.put(key_windowBackgroundWhiteBlueHeader, "windowBackgroundWhiteBlueHeader"); + colorKeysMap.put(key_switchTrack, "switchTrack"); + colorKeysMap.put(key_switchTrackChecked, "switchTrackChecked"); + colorKeysMap.put(key_switchTrackBlue, "switchTrackBlue"); + colorKeysMap.put(key_switchTrackBlueChecked, "switchTrackBlueChecked"); + colorKeysMap.put(key_switchTrackBlueThumb, "switchTrackBlueThumb"); + colorKeysMap.put(key_switchTrackBlueThumbChecked, "switchTrackBlueThumbChecked"); + colorKeysMap.put(key_switchTrackBlueSelector, "switchTrackBlueSelector"); + colorKeysMap.put(key_switchTrackBlueSelectorChecked, "switchTrackBlueSelectorChecked"); + colorKeysMap.put(key_switch2Track, "switch2Track"); + colorKeysMap.put(key_switch2TrackChecked, "switch2TrackChecked"); + colorKeysMap.put(key_checkboxSquareBackground, "checkboxSquareBackground"); + colorKeysMap.put(key_checkboxSquareCheck, "checkboxSquareCheck"); + colorKeysMap.put(key_checkboxSquareUnchecked, "checkboxSquareUnchecked"); + colorKeysMap.put(key_checkboxSquareDisabled, "checkboxSquareDisabled"); + colorKeysMap.put(key_windowBackgroundGray, "windowBackgroundGray"); + colorKeysMap.put(key_windowBackgroundGrayShadow, "windowBackgroundGrayShadow"); + colorKeysMap.put(key_emptyListPlaceholder, "emptyListPlaceholder"); + colorKeysMap.put(key_divider, "divider"); + colorKeysMap.put(key_graySection, "graySection"); + colorKeysMap.put(key_graySectionText, "key_graySectionText"); + colorKeysMap.put(key_radioBackground, "radioBackground"); + colorKeysMap.put(key_radioBackgroundChecked, "radioBackgroundChecked"); + colorKeysMap.put(key_checkbox, "checkbox"); + colorKeysMap.put(key_checkboxDisabled, "checkboxDisabled"); + colorKeysMap.put(key_checkboxCheck, "checkboxCheck"); + colorKeysMap.put(key_fastScrollActive, "fastScrollActive"); + colorKeysMap.put(key_fastScrollInactive, "fastScrollInactive"); + colorKeysMap.put(key_fastScrollText, "fastScrollText"); + colorKeysMap.put(key_text_RedRegular, "text_RedRegular"); + colorKeysMap.put(key_text_RedBold, "text_RedBold"); + colorKeysMap.put(key_fill_RedNormal, "fill_RedNormal"); + colorKeysMap.put(key_fill_RedDark, "fill_RedDark"); + colorKeysMap.put(key_inappPlayerPerformer, "inappPlayerPerformer"); + colorKeysMap.put(key_inappPlayerTitle, "inappPlayerTitle"); + colorKeysMap.put(key_inappPlayerBackground, "inappPlayerBackground"); + colorKeysMap.put(key_inappPlayerPlayPause, "inappPlayerPlayPause"); + colorKeysMap.put(key_inappPlayerClose, "inappPlayerClose"); + colorKeysMap.put(key_returnToCallBackground, "returnToCallBackground"); + colorKeysMap.put(key_returnToCallMutedBackground, "returnToCallMutedBackground"); + colorKeysMap.put(key_returnToCallText, "returnToCallText"); + colorKeysMap.put(key_contextProgressInner1, "contextProgressInner1"); + colorKeysMap.put(key_contextProgressOuter1, "contextProgressOuter1"); + colorKeysMap.put(key_contextProgressInner2, "contextProgressInner2"); + colorKeysMap.put(key_contextProgressOuter2, "contextProgressOuter2"); + colorKeysMap.put(key_contextProgressInner3, "contextProgressInner3"); + colorKeysMap.put(key_contextProgressOuter3, "contextProgressOuter3"); + colorKeysMap.put(key_contextProgressInner4, "contextProgressInner4"); + colorKeysMap.put(key_contextProgressOuter4, "contextProgressOuter4"); + colorKeysMap.put(key_avatar_text, "avatar_text"); + colorKeysMap.put(key_avatar_backgroundSaved, "avatar_backgroundSaved"); + colorKeysMap.put(key_avatar_background2Saved, "avatar_background2Saved"); + colorKeysMap.put(key_avatar_backgroundArchived, "avatar_backgroundArchived"); + colorKeysMap.put(key_avatar_backgroundArchivedHidden, "avatar_backgroundArchivedHidden"); + colorKeysMap.put(key_avatar_backgroundRed, "avatar_backgroundRed"); + colorKeysMap.put(key_avatar_backgroundOrange, "avatar_backgroundOrange"); + colorKeysMap.put(key_avatar_backgroundViolet, "avatar_backgroundViolet"); + colorKeysMap.put(key_avatar_backgroundGreen, "avatar_backgroundGreen"); + colorKeysMap.put(key_avatar_backgroundCyan, "avatar_backgroundCyan"); + colorKeysMap.put(key_avatar_backgroundBlue, "avatar_backgroundBlue"); + colorKeysMap.put(key_avatar_backgroundPink, "avatar_backgroundPink"); + colorKeysMap.put(key_avatar_background2Red, "avatar_background2Red"); + colorKeysMap.put(key_avatar_background2Orange, "avatar_background2Orange"); + colorKeysMap.put(key_avatar_background2Violet, "avatar_background2Violet"); + colorKeysMap.put(key_avatar_background2Green, "avatar_background2Green"); + colorKeysMap.put(key_avatar_background2Cyan, "avatar_background2Cyan"); + colorKeysMap.put(key_avatar_background2Blue, "avatar_background2Blue"); + colorKeysMap.put(key_avatar_background2Pink, "avatar_background2Pink"); + colorKeysMap.put(key_avatar_backgroundInProfileBlue, "avatar_backgroundInProfileBlue"); + colorKeysMap.put(key_avatar_backgroundActionBarBlue, "avatar_backgroundActionBarBlue"); + colorKeysMap.put(key_avatar_actionBarSelectorBlue, "avatar_actionBarSelectorBlue"); + colorKeysMap.put(key_avatar_actionBarIconBlue, "avatar_actionBarIconBlue"); + colorKeysMap.put(key_avatar_subtitleInProfileBlue, "avatar_subtitleInProfileBlue"); + colorKeysMap.put(key_avatar_nameInMessageRed, "avatar_nameInMessageRed"); + colorKeysMap.put(key_avatar_nameInMessageOrange, "avatar_nameInMessageOrange"); + colorKeysMap.put(key_avatar_nameInMessageViolet, "avatar_nameInMessageViolet"); + colorKeysMap.put(key_avatar_nameInMessageGreen, "avatar_nameInMessageGreen"); + colorKeysMap.put(key_avatar_nameInMessageCyan, "avatar_nameInMessageCyan"); + colorKeysMap.put(key_avatar_nameInMessageBlue, "avatar_nameInMessageBlue"); + colorKeysMap.put(key_avatar_nameInMessagePink, "avatar_nameInMessagePink"); + colorKeysMap.put(key_actionBarDefault, "actionBarDefault"); + colorKeysMap.put(key_actionBarDefaultSelector, "actionBarDefaultSelector"); + colorKeysMap.put(key_actionBarWhiteSelector, "actionBarWhiteSelector"); + colorKeysMap.put(key_actionBarDefaultIcon, "actionBarDefaultIcon"); + colorKeysMap.put(key_actionBarActionModeDefault, "actionBarActionModeDefault"); + colorKeysMap.put(key_actionBarActionModeDefaultTop, "actionBarActionModeDefaultTop"); + colorKeysMap.put(key_actionBarActionModeDefaultIcon, "actionBarActionModeDefaultIcon"); + colorKeysMap.put(key_actionBarActionModeDefaultSelector, "actionBarActionModeDefaultSelector"); + colorKeysMap.put(key_actionBarDefaultTitle, "actionBarDefaultTitle"); + colorKeysMap.put(key_actionBarDefaultSubtitle, "actionBarDefaultSubtitle"); + colorKeysMap.put(key_actionBarDefaultSearch, "actionBarDefaultSearch"); + colorKeysMap.put(key_actionBarDefaultSearchPlaceholder, "actionBarDefaultSearchPlaceholder"); + colorKeysMap.put(key_actionBarDefaultSubmenuItem, "actionBarDefaultSubmenuItem"); + colorKeysMap.put(key_actionBarDefaultSubmenuItemIcon, "actionBarDefaultSubmenuItemIcon"); + colorKeysMap.put(key_actionBarDefaultSubmenuBackground, "actionBarDefaultSubmenuBackground"); + colorKeysMap.put(key_actionBarDefaultSubmenuSeparator, "actionBarDefaultSubmenuSeparator"); + colorKeysMap.put(key_actionBarTabActiveText, "actionBarTabActiveText"); + colorKeysMap.put(key_actionBarTabUnactiveText, "actionBarTabUnactiveText"); + colorKeysMap.put(key_actionBarTabLine, "actionBarTabLine"); + colorKeysMap.put(key_actionBarTabSelector, "actionBarTabSelector"); + colorKeysMap.put(key_actionBarDefaultArchived, "actionBarDefaultArchived"); + colorKeysMap.put(key_actionBarDefaultArchivedSelector, "actionBarDefaultArchivedSelector"); + colorKeysMap.put(key_actionBarDefaultArchivedIcon, "actionBarDefaultArchivedIcon"); + colorKeysMap.put(key_actionBarDefaultArchivedTitle, "actionBarDefaultArchivedTitle"); + colorKeysMap.put(key_actionBarDefaultArchivedSearch, "actionBarDefaultArchivedSearch"); + colorKeysMap.put(key_actionBarDefaultArchivedSearchPlaceholder, "actionBarDefaultSearchArchivedPlaceholder"); + colorKeysMap.put(key_actionBarBrowser, "actionBarBrowser"); + colorKeysMap.put(key_chats_onlineCircle, "chats_onlineCircle"); + colorKeysMap.put(key_chats_unreadCounter, "chats_unreadCounter"); + colorKeysMap.put(key_chats_unreadCounterMuted, "chats_unreadCounterMuted"); + colorKeysMap.put(key_chats_unreadCounterText, "chats_unreadCounterText"); + colorKeysMap.put(key_chats_name, "chats_name"); + colorKeysMap.put(key_chats_nameArchived, "chats_nameArchived"); + colorKeysMap.put(key_chats_secretName, "chats_secretName"); + colorKeysMap.put(key_chats_secretIcon, "chats_secretIcon"); + colorKeysMap.put(key_chats_pinnedIcon, "chats_pinnedIcon"); + colorKeysMap.put(key_chats_archiveBackground, "chats_archiveBackground"); + colorKeysMap.put(key_chats_archivePinBackground, "chats_archivePinBackground"); + colorKeysMap.put(key_chats_archiveIcon, "chats_archiveIcon"); + colorKeysMap.put(key_chats_archiveText, "chats_archiveText"); + colorKeysMap.put(key_chats_message, "chats_message"); + colorKeysMap.put(key_chats_messageArchived, "chats_messageArchived"); + colorKeysMap.put(key_chats_message_threeLines, "chats_message_threeLines"); + colorKeysMap.put(key_chats_draft, "chats_draft"); + colorKeysMap.put(key_chats_nameMessage, "chats_nameMessage"); + colorKeysMap.put(key_chats_nameMessageArchived, "chats_nameMessageArchived"); + colorKeysMap.put(key_chats_nameMessage_threeLines, "chats_nameMessage_threeLines"); + colorKeysMap.put(key_chats_nameMessageArchived_threeLines, "chats_nameMessageArchived_threeLines"); + colorKeysMap.put(key_chats_attachMessage, "chats_attachMessage"); + colorKeysMap.put(key_chats_actionMessage, "chats_actionMessage"); + colorKeysMap.put(key_chats_date, "chats_date"); + colorKeysMap.put(key_chats_pinnedOverlay, "chats_pinnedOverlay"); + colorKeysMap.put(key_chats_tabletSelectedOverlay, "chats_tabletSelectedOverlay"); + colorKeysMap.put(key_chats_sentCheck, "chats_sentCheck"); + colorKeysMap.put(key_chats_sentReadCheck, "chats_sentReadCheck"); + colorKeysMap.put(key_chats_sentClock, "chats_sentClock"); + colorKeysMap.put(key_chats_sentError, "chats_sentError"); + colorKeysMap.put(key_chats_sentErrorIcon, "chats_sentErrorIcon"); + colorKeysMap.put(key_chats_verifiedBackground, "chats_verifiedBackground"); + colorKeysMap.put(key_chats_verifiedCheck, "chats_verifiedCheck"); + colorKeysMap.put(key_chats_muteIcon, "chats_muteIcon"); + colorKeysMap.put(key_chats_mentionIcon, "chats_mentionIcon"); + colorKeysMap.put(key_chats_menuTopShadow, "chats_menuTopShadow"); + colorKeysMap.put(key_chats_menuTopShadowCats, "chats_menuTopShadowCats"); + colorKeysMap.put(key_chats_menuBackground, "chats_menuBackground"); + colorKeysMap.put(key_chats_menuItemText, "chats_menuItemText"); + colorKeysMap.put(key_chats_menuItemCheck, "chats_menuItemCheck"); + colorKeysMap.put(key_chats_menuItemIcon, "chats_menuItemIcon"); + colorKeysMap.put(key_chats_menuName, "chats_menuName"); + colorKeysMap.put(key_chats_menuPhone, "chats_menuPhone"); + colorKeysMap.put(key_chats_menuPhoneCats, "chats_menuPhoneCats"); + colorKeysMap.put(key_chats_menuTopBackgroundCats, "chats_menuTopBackgroundCats"); + colorKeysMap.put(key_chats_menuTopBackground, "chats_menuTopBackground"); + colorKeysMap.put(key_chats_actionIcon, "chats_actionIcon"); + colorKeysMap.put(key_chats_actionBackground, "chats_actionBackground"); + colorKeysMap.put(key_chats_actionPressedBackground, "chats_actionPressedBackground"); + colorKeysMap.put(key_chats_archivePullDownBackground, "chats_archivePullDownBackground"); + colorKeysMap.put(key_chats_archivePullDownBackgroundActive, "chats_archivePullDownBackgroundActive"); + colorKeysMap.put(key_chats_tabUnreadActiveBackground, "chats_tabUnreadActiveBackground"); + colorKeysMap.put(key_chats_tabUnreadUnactiveBackground, "chats_tabUnreadUnactiveBackground"); + colorKeysMap.put(key_chat_attachCheckBoxCheck, "chat_attachCheckBoxCheck"); + colorKeysMap.put(key_chat_attachCheckBoxBackground, "chat_attachCheckBoxBackground"); + colorKeysMap.put(key_chat_attachPhotoBackground, "chat_attachPhotoBackground"); + colorKeysMap.put(key_chat_attachActiveTab, "chat_attachActiveTab"); + colorKeysMap.put(key_chat_attachUnactiveTab, "chat_attachUnactiveTab"); + colorKeysMap.put(key_chat_attachPermissionImage, "chat_attachPermissionImage"); + colorKeysMap.put(key_chat_attachPermissionMark, "chat_attachPermissionMark"); + colorKeysMap.put(key_chat_attachPermissionText, "chat_attachPermissionText"); + colorKeysMap.put(key_chat_attachEmptyImage, "chat_attachEmptyImage"); + colorKeysMap.put(key_chat_inPollCorrectAnswer, "chat_inPollCorrectAnswer"); + colorKeysMap.put(key_chat_outPollCorrectAnswer, "chat_outPollCorrectAnswer"); + colorKeysMap.put(key_chat_inPollWrongAnswer, "chat_inPollWrongAnswer"); + colorKeysMap.put(key_chat_outPollWrongAnswer, "chat_outPollWrongAnswer"); + colorKeysMap.put(key_chat_attachIcon, "chat_attachIcon"); + colorKeysMap.put(key_chat_attachGalleryBackground, "chat_attachGalleryBackground"); + colorKeysMap.put(key_chat_attachGalleryText, "chat_attachGalleryText"); + colorKeysMap.put(key_chat_attachAudioBackground, "chat_attachAudioBackground"); + colorKeysMap.put(key_chat_attachAudioText, "chat_attachAudioText"); + colorKeysMap.put(key_chat_attachFileBackground, "chat_attachFileBackground"); + colorKeysMap.put(key_chat_attachFileText, "chat_attachFileText"); + colorKeysMap.put(key_chat_attachContactBackground, "chat_attachContactBackground"); + colorKeysMap.put(key_chat_attachContactText, "chat_attachContactText"); + colorKeysMap.put(key_chat_attachLocationBackground, "chat_attachLocationBackground"); + colorKeysMap.put(key_chat_attachLocationText, "chat_attachLocationText"); + colorKeysMap.put(key_chat_attachPollBackground, "chat_attachPollBackground"); + colorKeysMap.put(key_chat_attachPollText, "chat_attachPollText"); + colorKeysMap.put(key_chat_status, "chat_status"); + colorKeysMap.put(key_chat_inGreenCall, "chat_inDownCall"); + colorKeysMap.put(key_chat_outGreenCall, "chat_outUpCall"); + colorKeysMap.put(key_chat_inBubble, "chat_inBubble"); + colorKeysMap.put(key_chat_inBubbleSelected, "chat_inBubbleSelected"); + colorKeysMap.put(key_chat_inBubbleSelectedOverlay, "chat_inBubbleSelectedOverlay"); + colorKeysMap.put(key_chat_inBubbleShadow, "chat_inBubbleShadow"); + colorKeysMap.put(key_chat_outBubble, "chat_outBubble"); + colorKeysMap.put(key_chat_outBubbleGradient1, "chat_outBubbleGradient"); + colorKeysMap.put(key_chat_outBubbleGradient2, "chat_outBubbleGradient2"); + colorKeysMap.put(key_chat_outBubbleGradient3, "chat_outBubbleGradient3"); + colorKeysMap.put(key_chat_outBubbleGradientAnimated, "chat_outBubbleGradientAnimated"); + colorKeysMap.put(key_chat_outBubbleGradientSelectedOverlay, "chat_outBubbleGradientSelectedOverlay"); + colorKeysMap.put(key_chat_outBubbleSelected, "chat_outBubbleSelected"); + colorKeysMap.put(key_chat_outBubbleSelectedOverlay, "chat_outBubbleSelectedOverlay"); + colorKeysMap.put(key_chat_outBubbleShadow, "chat_outBubbleShadow"); + colorKeysMap.put(key_chat_messageTextIn, "chat_messageTextIn"); + colorKeysMap.put(key_chat_messageTextOut, "chat_messageTextOut"); + colorKeysMap.put(key_chat_messageLinkIn, "chat_messageLinkIn"); + colorKeysMap.put(key_chat_messageLinkOut, "chat_messageLinkOut"); + colorKeysMap.put(key_chat_serviceText, "chat_serviceText"); + colorKeysMap.put(key_chat_serviceLink, "chat_serviceLink"); + colorKeysMap.put(key_chat_serviceIcon, "chat_serviceIcon"); + colorKeysMap.put(key_chat_serviceBackground, "chat_serviceBackground"); + colorKeysMap.put(key_chat_serviceBackgroundSelected, "chat_serviceBackgroundSelected"); + colorKeysMap.put(key_chat_serviceBackgroundSelector, "chat_serviceBackgroundSelector"); + colorKeysMap.put(key_chat_muteIcon, "chat_muteIcon"); + colorKeysMap.put(key_chat_lockIcon, "chat_lockIcon"); + colorKeysMap.put(key_chat_outSentCheck, "chat_outSentCheck"); + colorKeysMap.put(key_chat_outSentCheckSelected, "chat_outSentCheckSelected"); + colorKeysMap.put(key_chat_outSentCheckRead, "chat_outSentCheckRead"); + colorKeysMap.put(key_chat_outSentCheckReadSelected, "chat_outSentCheckReadSelected"); + colorKeysMap.put(key_chat_outSentClock, "chat_outSentClock"); + colorKeysMap.put(key_chat_outSentClockSelected, "chat_outSentClockSelected"); + colorKeysMap.put(key_chat_inSentClock, "chat_inSentClock"); + colorKeysMap.put(key_chat_inSentClockSelected, "chat_inSentClockSelected"); + colorKeysMap.put(key_chat_mediaSentCheck, "chat_mediaSentCheck"); + colorKeysMap.put(key_chat_mediaSentClock, "chat_mediaSentClock"); + colorKeysMap.put(key_chat_inMediaIcon, "chat_inMediaIcon"); + colorKeysMap.put(key_chat_outMediaIcon, "chat_outMediaIcon"); + colorKeysMap.put(key_chat_inMediaIconSelected, "chat_inMediaIconSelected"); + colorKeysMap.put(key_chat_outMediaIconSelected, "chat_outMediaIconSelected"); + colorKeysMap.put(key_chat_mediaTimeBackground, "chat_mediaTimeBackground"); + colorKeysMap.put(key_chat_outViews, "chat_outViews"); + colorKeysMap.put(key_chat_outViewsSelected, "chat_outViewsSelected"); + colorKeysMap.put(key_chat_inViews, "chat_inViews"); + colorKeysMap.put(key_chat_inViewsSelected, "chat_inViewsSelected"); + colorKeysMap.put(key_chat_mediaViews, "chat_mediaViews"); + colorKeysMap.put(key_chat_outMenu, "chat_outMenu"); + colorKeysMap.put(key_chat_outMenuSelected, "chat_outMenuSelected"); + colorKeysMap.put(key_chat_inMenu, "chat_inMenu"); + colorKeysMap.put(key_chat_inMenuSelected, "chat_inMenuSelected"); + colorKeysMap.put(key_chat_mediaMenu, "chat_mediaMenu"); + colorKeysMap.put(key_chat_outInstant, "chat_outInstant"); + colorKeysMap.put(key_chat_outInstantSelected, "chat_outInstantSelected"); + colorKeysMap.put(key_chat_inInstant, "chat_inInstant"); + colorKeysMap.put(key_chat_inInstantSelected, "chat_inInstantSelected"); + colorKeysMap.put(key_chat_sentError, "chat_sentError"); + colorKeysMap.put(key_chat_sentErrorIcon, "chat_sentErrorIcon"); + colorKeysMap.put(key_chat_selectedBackground, "chat_selectedBackground"); + colorKeysMap.put(key_chat_previewDurationText, "chat_previewDurationText"); + colorKeysMap.put(key_chat_previewGameText, "chat_previewGameText"); + colorKeysMap.put(key_chat_inPreviewInstantText, "chat_inPreviewInstantText"); + colorKeysMap.put(key_chat_outPreviewInstantText, "chat_outPreviewInstantText"); + colorKeysMap.put(key_chat_secretTimeText, "chat_secretTimeText"); + colorKeysMap.put(key_chat_stickerNameText, "chat_stickerNameText"); + colorKeysMap.put(key_chat_botButtonText, "chat_botButtonText"); + colorKeysMap.put(key_chat_inForwardedNameText, "chat_inForwardedNameText"); + colorKeysMap.put(key_chat_outForwardedNameText, "chat_outForwardedNameText"); + colorKeysMap.put(key_chat_inPsaNameText, "chat_inPsaNameText"); + colorKeysMap.put(key_chat_outPsaNameText, "chat_outPsaNameText"); + colorKeysMap.put(key_chat_inViaBotNameText, "chat_inViaBotNameText"); + colorKeysMap.put(key_chat_outViaBotNameText, "chat_outViaBotNameText"); + colorKeysMap.put(key_chat_stickerViaBotNameText, "chat_stickerViaBotNameText"); + colorKeysMap.put(key_chat_inReplyLine, "chat_inReplyLine"); + colorKeysMap.put(key_chat_outReplyLine, "chat_outReplyLine"); + colorKeysMap.put(key_chat_stickerReplyLine, "chat_stickerReplyLine"); + colorKeysMap.put(key_chat_inReplyNameText, "chat_inReplyNameText"); + colorKeysMap.put(key_chat_outReplyNameText, "chat_outReplyNameText"); + colorKeysMap.put(key_chat_stickerReplyNameText, "chat_stickerReplyNameText"); + colorKeysMap.put(key_chat_inReplyMessageText, "chat_inReplyMessageText"); + colorKeysMap.put(key_chat_outReplyMessageText, "chat_outReplyMessageText"); + colorKeysMap.put(key_chat_inReplyMediaMessageText, "chat_inReplyMediaMessageText"); + colorKeysMap.put(key_chat_outReplyMediaMessageText, "chat_outReplyMediaMessageText"); + colorKeysMap.put(key_chat_inReplyMediaMessageSelectedText, "chat_inReplyMediaMessageSelectedText"); + colorKeysMap.put(key_chat_outReplyMediaMessageSelectedText, "chat_outReplyMediaMessageSelectedText"); + colorKeysMap.put(key_chat_stickerReplyMessageText, "chat_stickerReplyMessageText"); + colorKeysMap.put(key_chat_inPreviewLine, "chat_inPreviewLine"); + colorKeysMap.put(key_chat_outPreviewLine, "chat_outPreviewLine"); + colorKeysMap.put(key_chat_inSiteNameText, "chat_inSiteNameText"); + colorKeysMap.put(key_chat_outSiteNameText, "chat_outSiteNameText"); + colorKeysMap.put(key_chat_inContactNameText, "chat_inContactNameText"); + colorKeysMap.put(key_chat_outContactNameText, "chat_outContactNameText"); + colorKeysMap.put(key_chat_inContactPhoneText, "chat_inContactPhoneText"); + colorKeysMap.put(key_chat_inContactPhoneSelectedText, "chat_inContactPhoneSelectedText"); + colorKeysMap.put(key_chat_outContactPhoneText, "chat_outContactPhoneText"); + colorKeysMap.put(key_chat_outContactPhoneSelectedText, "chat_outContactPhoneSelectedText"); + colorKeysMap.put(key_chat_mediaProgress, "chat_mediaProgress"); + colorKeysMap.put(key_chat_inAudioProgress, "chat_inAudioProgress"); + colorKeysMap.put(key_chat_outAudioProgress, "chat_outAudioProgress"); + colorKeysMap.put(key_chat_inAudioSelectedProgress, "chat_inAudioSelectedProgress"); + colorKeysMap.put(key_chat_outAudioSelectedProgress, "chat_outAudioSelectedProgress"); + colorKeysMap.put(key_chat_mediaTimeText, "chat_mediaTimeText"); + colorKeysMap.put(key_chat_inAdminText, "chat_adminText"); + colorKeysMap.put(key_chat_inAdminSelectedText, "chat_adminSelectedText"); + colorKeysMap.put(key_chat_outAdminText, "chat_outAdminText"); + colorKeysMap.put(key_chat_outAdminSelectedText, "chat_outAdminSelectedText"); + colorKeysMap.put(key_chat_inTimeText, "chat_inTimeText"); + colorKeysMap.put(key_chat_outTimeText, "chat_outTimeText"); + colorKeysMap.put(key_chat_inTimeSelectedText, "chat_inTimeSelectedText"); + colorKeysMap.put(key_chat_outTimeSelectedText, "chat_outTimeSelectedText"); + colorKeysMap.put(key_chat_inAudioPerformerText, "chat_inAudioPerfomerText"); + colorKeysMap.put(key_chat_inAudioPerformerSelectedText, "chat_inAudioPerfomerSelectedText"); + colorKeysMap.put(key_chat_outAudioPerformerText, "chat_outAudioPerfomerText"); + colorKeysMap.put(key_chat_outAudioPerformerSelectedText, "chat_outAudioPerfomerSelectedText"); + colorKeysMap.put(key_chat_inAudioTitleText, "chat_inAudioTitleText"); + colorKeysMap.put(key_chat_outAudioTitleText, "chat_outAudioTitleText"); + colorKeysMap.put(key_chat_inAudioDurationText, "chat_inAudioDurationText"); + colorKeysMap.put(key_chat_outAudioDurationText, "chat_outAudioDurationText"); + colorKeysMap.put(key_chat_inAudioDurationSelectedText, "chat_inAudioDurationSelectedText"); + colorKeysMap.put(key_chat_outAudioDurationSelectedText, "chat_outAudioDurationSelectedText"); + colorKeysMap.put(key_chat_inAudioSeekbar, "chat_inAudioSeekbar"); + colorKeysMap.put(key_chat_inAudioCacheSeekbar, "chat_inAudioCacheSeekbar"); + colorKeysMap.put(key_chat_outAudioSeekbar, "chat_outAudioSeekbar"); + colorKeysMap.put(key_chat_outAudioCacheSeekbar, "chat_outAudioCacheSeekbar"); + colorKeysMap.put(key_chat_inAudioSeekbarSelected, "chat_inAudioSeekbarSelected"); + colorKeysMap.put(key_chat_outAudioSeekbarSelected, "chat_outAudioSeekbarSelected"); + colorKeysMap.put(key_chat_inAudioSeekbarFill, "chat_inAudioSeekbarFill"); + colorKeysMap.put(key_chat_outAudioSeekbarFill, "chat_outAudioSeekbarFill"); + colorKeysMap.put(key_chat_inVoiceSeekbar, "chat_inVoiceSeekbar"); + colorKeysMap.put(key_chat_outVoiceSeekbar, "chat_outVoiceSeekbar"); + colorKeysMap.put(key_chat_inVoiceSeekbarSelected, "chat_inVoiceSeekbarSelected"); + colorKeysMap.put(key_chat_outVoiceSeekbarSelected, "chat_outVoiceSeekbarSelected"); + colorKeysMap.put(key_chat_inVoiceSeekbarFill, "chat_inVoiceSeekbarFill"); + colorKeysMap.put(key_chat_outVoiceSeekbarFill, "chat_outVoiceSeekbarFill"); + colorKeysMap.put(key_chat_inFileProgress, "chat_inFileProgress"); + colorKeysMap.put(key_chat_outFileProgress, "chat_outFileProgress"); + colorKeysMap.put(key_chat_inFileProgressSelected, "chat_inFileProgressSelected"); + colorKeysMap.put(key_chat_outFileProgressSelected, "chat_outFileProgressSelected"); + colorKeysMap.put(key_chat_inFileNameText, "chat_inFileNameText"); + colorKeysMap.put(key_chat_outFileNameText, "chat_outFileNameText"); + colorKeysMap.put(key_chat_inFileInfoText, "chat_inFileInfoText"); + colorKeysMap.put(key_chat_outFileInfoText, "chat_outFileInfoText"); + colorKeysMap.put(key_chat_inFileInfoSelectedText, "chat_inFileInfoSelectedText"); + colorKeysMap.put(key_chat_outFileInfoSelectedText, "chat_outFileInfoSelectedText"); + colorKeysMap.put(key_chat_inFileBackground, "chat_inFileBackground"); + colorKeysMap.put(key_chat_outFileBackground, "chat_outFileBackground"); + colorKeysMap.put(key_chat_inFileBackgroundSelected, "chat_inFileBackgroundSelected"); + colorKeysMap.put(key_chat_outFileBackgroundSelected, "chat_outFileBackgroundSelected"); + colorKeysMap.put(key_chat_inVenueInfoText, "chat_inVenueInfoText"); + colorKeysMap.put(key_chat_outVenueInfoText, "chat_outVenueInfoText"); + colorKeysMap.put(key_chat_inVenueInfoSelectedText, "chat_inVenueInfoSelectedText"); + colorKeysMap.put(key_chat_outVenueInfoSelectedText, "chat_outVenueInfoSelectedText"); + colorKeysMap.put(key_chat_mediaInfoText, "chat_mediaInfoText"); + colorKeysMap.put(key_chat_linkSelectBackground, "chat_linkSelectBackground"); + colorKeysMap.put(key_chat_outLinkSelectBackground, "chat_outLinkSelectBackground"); + colorKeysMap.put(key_chat_textSelectBackground, "chat_textSelectBackground"); + colorKeysMap.put(key_chat_wallpaper, "chat_wallpaper"); + colorKeysMap.put(key_chat_wallpaper_gradient_to1, "chat_wallpaper_gradient_to"); + colorKeysMap.put(key_chat_wallpaper_gradient_to2, "key_chat_wallpaper_gradient_to2"); + colorKeysMap.put(key_chat_wallpaper_gradient_to3, "key_chat_wallpaper_gradient_to3"); + colorKeysMap.put(key_chat_wallpaper_gradient_rotation, "chat_wallpaper_gradient_rotation"); + colorKeysMap.put(key_chat_messagePanelBackground, "chat_messagePanelBackground"); + colorKeysMap.put(key_chat_messagePanelShadow, "chat_messagePanelShadow"); + colorKeysMap.put(key_chat_messagePanelText, "chat_messagePanelText"); + colorKeysMap.put(key_chat_messagePanelHint, "chat_messagePanelHint"); + colorKeysMap.put(key_chat_messagePanelCursor, "chat_messagePanelCursor"); + colorKeysMap.put(key_chat_messagePanelIcons, "chat_messagePanelIcons"); + colorKeysMap.put(key_chat_messagePanelSend, "chat_messagePanelSend"); + colorKeysMap.put(key_chat_messagePanelVoiceLock, "key_chat_messagePanelVoiceLock"); + colorKeysMap.put(key_chat_messagePanelVoiceLockBackground, "key_chat_messagePanelVoiceLockBackground"); + colorKeysMap.put(key_chat_messagePanelVoiceLockShadow, "key_chat_messagePanelVoiceLockShadow"); + colorKeysMap.put(key_chat_topPanelBackground, "chat_topPanelBackground"); + colorKeysMap.put(key_chat_topPanelClose, "chat_topPanelClose"); + colorKeysMap.put(key_chat_topPanelLine, "chat_topPanelLine"); + colorKeysMap.put(key_chat_topPanelTitle, "chat_topPanelTitle"); + colorKeysMap.put(key_chat_topPanelMessage, "chat_topPanelMessage"); + colorKeysMap.put(key_chat_addContact, "chat_addContact"); + colorKeysMap.put(key_chat_inLoader, "chat_inLoader"); + colorKeysMap.put(key_chat_inLoaderSelected, "chat_inLoaderSelected"); + colorKeysMap.put(key_chat_outLoader, "chat_outLoader"); + colorKeysMap.put(key_chat_outLoaderSelected, "chat_outLoaderSelected"); + colorKeysMap.put(key_chat_inLoaderPhoto, "chat_inLoaderPhoto"); + colorKeysMap.put(key_chat_mediaLoaderPhoto, "chat_mediaLoaderPhoto"); + colorKeysMap.put(key_chat_mediaLoaderPhotoSelected, "chat_mediaLoaderPhotoSelected"); + colorKeysMap.put(key_chat_mediaLoaderPhotoIcon, "chat_mediaLoaderPhotoIcon"); + colorKeysMap.put(key_chat_mediaLoaderPhotoIconSelected, "chat_mediaLoaderPhotoIconSelected"); + colorKeysMap.put(key_chat_inLocationBackground, "chat_inLocationBackground"); + colorKeysMap.put(key_chat_inLocationIcon, "chat_inLocationIcon"); + colorKeysMap.put(key_chat_outLocationIcon, "chat_outLocationIcon"); + colorKeysMap.put(key_chat_inContactBackground, "chat_inContactBackground"); + colorKeysMap.put(key_chat_inContactIcon, "chat_inContactIcon"); + colorKeysMap.put(key_chat_outContactBackground, "chat_outContactBackground"); + colorKeysMap.put(key_chat_outContactIcon, "chat_outContactIcon"); + colorKeysMap.put(key_chat_replyPanelIcons, "chat_replyPanelIcons"); + colorKeysMap.put(key_chat_replyPanelClose, "chat_replyPanelClose"); + colorKeysMap.put(key_chat_replyPanelName, "chat_replyPanelName"); + colorKeysMap.put(key_chat_replyPanelLine, "chat_replyPanelLine"); + colorKeysMap.put(key_chat_searchPanelIcons, "chat_searchPanelIcons"); + colorKeysMap.put(key_chat_searchPanelText, "chat_searchPanelText"); + colorKeysMap.put(key_chat_secretChatStatusText, "chat_secretChatStatusText"); + colorKeysMap.put(key_chat_fieldOverlayText, "chat_fieldOverlayText"); + colorKeysMap.put(key_chat_stickersHintPanel, "chat_stickersHintPanel"); + colorKeysMap.put(key_chat_botSwitchToInlineText, "chat_botSwitchToInlineText"); + colorKeysMap.put(key_chat_unreadMessagesStartArrowIcon, "chat_unreadMessagesStartArrowIcon"); + colorKeysMap.put(key_chat_unreadMessagesStartText, "chat_unreadMessagesStartText"); + colorKeysMap.put(key_chat_unreadMessagesStartBackground, "chat_unreadMessagesStartBackground"); + colorKeysMap.put(key_chat_inlineResultIcon, "chat_inlineResultIcon"); + colorKeysMap.put(key_chat_emojiPanelBackground, "chat_emojiPanelBackground"); + colorKeysMap.put(key_chat_emojiSearchBackground, "chat_emojiSearchBackground"); + colorKeysMap.put(key_chat_emojiSearchIcon, "chat_emojiSearchIcon"); + colorKeysMap.put(key_chat_emojiPanelShadowLine, "chat_emojiPanelShadowLine"); + colorKeysMap.put(key_chat_emojiPanelEmptyText, "chat_emojiPanelEmptyText"); + colorKeysMap.put(key_chat_emojiPanelIcon, "chat_emojiPanelIcon"); + colorKeysMap.put(key_chat_emojiBottomPanelIcon, "chat_emojiBottomPanelIcon"); + colorKeysMap.put(key_chat_emojiPanelIconSelected, "chat_emojiPanelIconSelected"); + colorKeysMap.put(key_chat_emojiPanelStickerPackSelector, "chat_emojiPanelStickerPackSelector"); + colorKeysMap.put(key_chat_emojiPanelStickerPackSelectorLine, "chat_emojiPanelStickerPackSelectorLine"); + colorKeysMap.put(key_chat_emojiPanelBackspace, "chat_emojiPanelBackspace"); + colorKeysMap.put(key_chat_emojiPanelTrendingTitle, "chat_emojiPanelTrendingTitle"); + colorKeysMap.put(key_chat_emojiPanelStickerSetName, "chat_emojiPanelStickerSetName"); + colorKeysMap.put(key_chat_emojiPanelStickerSetNameHighlight, "chat_emojiPanelStickerSetNameHighlight"); + colorKeysMap.put(key_chat_emojiPanelStickerSetNameIcon, "chat_emojiPanelStickerSetNameIcon"); + colorKeysMap.put(key_chat_emojiPanelTrendingDescription, "chat_emojiPanelTrendingDescription"); + colorKeysMap.put(key_chat_botKeyboardButtonText, "chat_botKeyboardButtonText"); + colorKeysMap.put(key_chat_botKeyboardButtonBackground, "chat_botKeyboardButtonBackground"); + colorKeysMap.put(key_chat_botKeyboardButtonBackgroundPressed, "chat_botKeyboardButtonBackgroundPressed"); + colorKeysMap.put(key_chat_emojiPanelNewTrending, "chat_emojiPanelNewTrending"); + colorKeysMap.put(key_chat_messagePanelVoicePressed, "chat_messagePanelVoicePressed"); + colorKeysMap.put(key_chat_messagePanelVoiceBackground, "chat_messagePanelVoiceBackground"); + colorKeysMap.put(key_chat_messagePanelVoiceDelete, "chat_messagePanelVoiceDelete"); + colorKeysMap.put(key_chat_messagePanelVoiceDuration, "chat_messagePanelVoiceDuration"); + colorKeysMap.put(key_chat_recordedVoicePlayPause, "chat_recordedVoicePlayPause"); + colorKeysMap.put(key_chat_recordedVoiceProgress, "chat_recordedVoiceProgress"); + colorKeysMap.put(key_chat_recordedVoiceProgressInner, "chat_recordedVoiceProgressInner"); + colorKeysMap.put(key_chat_recordedVoiceDot, "chat_recordedVoiceDot"); + colorKeysMap.put(key_chat_recordedVoiceBackground, "chat_recordedVoiceBackground"); + colorKeysMap.put(key_chat_recordVoiceCancel, "chat_recordVoiceCancel"); + colorKeysMap.put(key_chat_recordTime, "chat_recordTime"); + colorKeysMap.put(key_chat_messagePanelCancelInlineBot, "chat_messagePanelCancelInlineBot"); + colorKeysMap.put(key_chat_gifSaveHintText, "chat_gifSaveHintText"); + colorKeysMap.put(key_chat_gifSaveHintBackground, "chat_gifSaveHintBackground"); + colorKeysMap.put(key_chat_goDownButton, "chat_goDownButton"); + colorKeysMap.put(key_chat_goDownButtonIcon, "chat_goDownButtonIcon"); + colorKeysMap.put(key_chat_goDownButtonCounter, "chat_goDownButtonCounter"); + colorKeysMap.put(key_chat_goDownButtonCounterBackground, "chat_goDownButtonCounterBackground"); + colorKeysMap.put(key_chat_outTextSelectionHighlight, "chat_outTextSelectionHighlight"); + colorKeysMap.put(key_chat_inTextSelectionHighlight, "chat_inTextSelectionHighlight"); + colorKeysMap.put(key_chat_TextSelectionCursor, "chat_TextSelectionCursor"); + colorKeysMap.put(key_chat_outTextSelectionCursor, "chat_outTextSelectionCursor"); + colorKeysMap.put(key_chat_inBubbleLocationPlaceholder, "chat_inBubbleLocationPlaceholder"); + colorKeysMap.put(key_chat_outBubbleLocationPlaceholder, "chat_outBubbleLocationPlaceholder"); + colorKeysMap.put(key_chat_BlurAlpha, "chat_BlurAlpha"); + + colorKeysMap.put(key_voipgroup_listSelector, "voipgroup_listSelector"); + colorKeysMap.put(key_voipgroup_inviteMembersBackground, "voipgroup_inviteMembersBackground"); + colorKeysMap.put(key_voipgroup_actionBar, "voipgroup_actionBar"); + colorKeysMap.put(key_voipgroup_actionBarItems, "voipgroup_actionBarItems"); + colorKeysMap.put(key_voipgroup_actionBarItemsSelector, "voipgroup_actionBarItemsSelector"); + colorKeysMap.put(key_voipgroup_actionBarUnscrolled, "voipgroup_actionBarUnscrolled"); + colorKeysMap.put(key_voipgroup_listViewBackgroundUnscrolled, "voipgroup_listViewBackgroundUnscrolled"); + colorKeysMap.put(key_voipgroup_lastSeenTextUnscrolled, "voipgroup_lastSeenTextUnscrolled"); + colorKeysMap.put(key_voipgroup_mutedIconUnscrolled, "voipgroup_mutedIconUnscrolled"); + colorKeysMap.put(key_voipgroup_nameText, "voipgroup_nameText"); + colorKeysMap.put(key_voipgroup_lastSeenText, "voipgroup_lastSeenText"); + colorKeysMap.put(key_voipgroup_listeningText, "voipgroup_listeningText"); + colorKeysMap.put(key_voipgroup_speakingText, "voipgroup_speakingText"); + colorKeysMap.put(key_voipgroup_mutedIcon, "voipgroup_mutedIcon"); + colorKeysMap.put(key_voipgroup_mutedByAdminIcon, "voipgroup_mutedByAdminIcon"); + colorKeysMap.put(key_voipgroup_listViewBackground, "voipgroup_listViewBackground"); + colorKeysMap.put(key_voipgroup_dialogBackground, "voipgroup_dialogBackground"); + colorKeysMap.put(key_voipgroup_leaveCallMenu, "voipgroup_leaveCallMenu"); + colorKeysMap.put(key_voipgroup_checkMenu, "voipgroup_checkMenu"); + colorKeysMap.put(key_voipgroup_soundButton, "voipgroup_soundButton"); + colorKeysMap.put(key_voipgroup_soundButtonActive, "voipgroup_soundButtonActive"); + colorKeysMap.put(key_voipgroup_soundButtonActiveScrolled, "voipgroup_soundButtonActiveScrolled"); + colorKeysMap.put(key_voipgroup_soundButton2, "voipgroup_soundButton2"); + colorKeysMap.put(key_voipgroup_soundButtonActive2, "voipgroup_soundButtonActive2"); + colorKeysMap.put(key_voipgroup_soundButtonActive2Scrolled, "voipgroup_soundButtonActive2Scrolled"); + colorKeysMap.put(key_voipgroup_leaveButton, "voipgroup_leaveButton"); + colorKeysMap.put(key_voipgroup_leaveButtonScrolled, "voipgroup_leaveButtonScrolled"); + colorKeysMap.put(key_voipgroup_muteButton, "voipgroup_muteButton"); + colorKeysMap.put(key_voipgroup_muteButton2, "voipgroup_muteButton2"); + colorKeysMap.put(key_voipgroup_muteButton3, "voipgroup_muteButton3"); + colorKeysMap.put(key_voipgroup_unmuteButton, "voipgroup_unmuteButton"); + colorKeysMap.put(key_voipgroup_unmuteButton2, "voipgroup_unmuteButton2"); + colorKeysMap.put(key_voipgroup_disabledButton, "voipgroup_disabledButton"); + colorKeysMap.put(key_voipgroup_disabledButtonActive, "voipgroup_disabledButtonActive"); + colorKeysMap.put(key_voipgroup_disabledButtonActiveScrolled, "voipgroup_disabledButtonActiveScrolled"); + colorKeysMap.put(key_voipgroup_connectingProgress, "voipgroup_connectingProgress"); + colorKeysMap.put(key_voipgroup_scrollUp, "voipgroup_scrollUp"); + colorKeysMap.put(key_voipgroup_searchPlaceholder, "voipgroup_searchPlaceholder"); + colorKeysMap.put(key_voipgroup_searchBackground, "voipgroup_searchBackground"); + colorKeysMap.put(key_voipgroup_searchText, "voipgroup_searchText"); + colorKeysMap.put(key_voipgroup_overlayGreen1, "voipgroup_overlayGreen1"); + colorKeysMap.put(key_voipgroup_overlayGreen2, "voipgroup_overlayGreen2"); + colorKeysMap.put(key_voipgroup_overlayBlue1, "voipgroup_overlayBlue1"); + colorKeysMap.put(key_voipgroup_overlayBlue2, "voipgroup_overlayBlue2"); + colorKeysMap.put(key_voipgroup_topPanelGreen1, "voipgroup_topPanelGreen1"); + colorKeysMap.put(key_voipgroup_topPanelGreen2, "voipgroup_topPanelGreen2"); + colorKeysMap.put(key_voipgroup_topPanelBlue1, "voipgroup_topPanelBlue1"); + colorKeysMap.put(key_voipgroup_topPanelBlue2, "voipgroup_topPanelBlue2"); + colorKeysMap.put(key_voipgroup_topPanelGray, "voipgroup_topPanelGray"); + colorKeysMap.put(key_voipgroup_overlayAlertGradientMuted, "voipgroup_overlayAlertGradientMuted"); + colorKeysMap.put(key_voipgroup_overlayAlertGradientMuted2, "voipgroup_overlayAlertGradientMuted2"); + colorKeysMap.put(key_voipgroup_overlayAlertGradientUnmuted, "voipgroup_overlayAlertGradientUnmuted"); + colorKeysMap.put(key_voipgroup_overlayAlertGradientUnmuted2, "voipgroup_overlayAlertGradientUnmuted2"); + colorKeysMap.put(key_voipgroup_overlayAlertMutedByAdmin, "voipgroup_overlayAlertMutedByAdmin"); + colorKeysMap.put(key_voipgroup_overlayAlertMutedByAdmin2, "kvoipgroup_overlayAlertMutedByAdmin2"); + colorKeysMap.put(key_voipgroup_mutedByAdminGradient, "voipgroup_mutedByAdminGradient"); + colorKeysMap.put(key_voipgroup_mutedByAdminGradient2, "voipgroup_mutedByAdminGradient2"); + colorKeysMap.put(key_voipgroup_mutedByAdminGradient3, "voipgroup_mutedByAdminGradient3"); + colorKeysMap.put(key_voipgroup_mutedByAdminMuteButton, "voipgroup_mutedByAdminMuteButton"); + colorKeysMap.put(key_voipgroup_mutedByAdminMuteButtonDisabled, "voipgroup_mutedByAdminMuteButtonDisabled"); + colorKeysMap.put(key_voipgroup_windowBackgroundWhiteInputField, "voipgroup_windowBackgroundWhiteInputField"); + colorKeysMap.put(key_voipgroup_windowBackgroundWhiteInputFieldActivated, "voipgroup_windowBackgroundWhiteInputFieldActivated"); + colorKeysMap.put(key_passport_authorizeBackground, "passport_authorizeBackground"); + colorKeysMap.put(key_passport_authorizeBackgroundSelected, "passport_authorizeBackgroundSelected"); + colorKeysMap.put(key_passport_authorizeText, "passport_authorizeText"); + colorKeysMap.put(key_profile_creatorIcon, "profile_creatorIcon"); + colorKeysMap.put(key_profile_title, "profile_title"); + colorKeysMap.put(key_profile_actionIcon, "profile_actionIcon"); + colorKeysMap.put(key_profile_actionBackground, "profile_actionBackground"); + colorKeysMap.put(key_profile_actionPressedBackground, "profile_actionPressedBackground"); + colorKeysMap.put(key_profile_verifiedBackground, "profile_verifiedBackground"); + colorKeysMap.put(key_profile_verifiedCheck, "profile_verifiedCheck"); + colorKeysMap.put(key_profile_status, "profile_status"); + colorKeysMap.put(key_profile_tabText, "profile_tabText"); + colorKeysMap.put(key_profile_tabSelectedText, "profile_tabSelectedText"); + colorKeysMap.put(key_profile_tabSelectedLine, "profile_tabSelectedLine"); + colorKeysMap.put(key_profile_tabSelector, "profile_tabSelector"); + colorKeysMap.put(key_sharedMedia_startStopLoadIcon, "sharedMedia_startStopLoadIcon"); + colorKeysMap.put(key_sharedMedia_linkPlaceholder, "sharedMedia_linkPlaceholder"); + colorKeysMap.put(key_sharedMedia_linkPlaceholderText, "sharedMedia_linkPlaceholderText"); + colorKeysMap.put(key_sharedMedia_photoPlaceholder, "sharedMedia_photoPlaceholder"); + colorKeysMap.put(key_featuredStickers_addedIcon, "featuredStickers_addedIcon"); + colorKeysMap.put(key_featuredStickers_buttonProgress, "featuredStickers_buttonProgress"); + colorKeysMap.put(key_featuredStickers_addButton, "featuredStickers_addButton"); + colorKeysMap.put(key_featuredStickers_addButtonPressed, "featuredStickers_addButtonPressed"); + colorKeysMap.put(key_featuredStickers_removeButtonText, "featuredStickers_removeButtonText"); + colorKeysMap.put(key_featuredStickers_buttonText, "featuredStickers_buttonText"); + colorKeysMap.put(key_featuredStickers_unread, "featuredStickers_unread"); + colorKeysMap.put(key_stickers_menu, "stickers_menu"); + colorKeysMap.put(key_stickers_menuSelector, "stickers_menuSelector"); + colorKeysMap.put(key_changephoneinfo_image2, "changephoneinfo_image2"); + colorKeysMap.put(key_groupcreate_hintText, "groupcreate_hintText"); + colorKeysMap.put(key_groupcreate_cursor, "groupcreate_cursor"); + colorKeysMap.put(key_groupcreate_sectionShadow, "groupcreate_sectionShadow"); + colorKeysMap.put(key_groupcreate_sectionText, "groupcreate_sectionText"); + colorKeysMap.put(key_groupcreate_spanText, "groupcreate_spanText"); + colorKeysMap.put(key_groupcreate_spanBackground, "groupcreate_spanBackground"); + colorKeysMap.put(key_groupcreate_spanDelete, "groupcreate_spanDelete"); + colorKeysMap.put(key_contacts_inviteBackground, "contacts_inviteBackground"); + colorKeysMap.put(key_contacts_inviteText, "contacts_inviteText"); + colorKeysMap.put(key_login_progressInner, "login_progressInner"); + colorKeysMap.put(key_login_progressOuter, "login_progressOuter"); + colorKeysMap.put(key_picker_enabledButton, "picker_enabledButton"); + colorKeysMap.put(key_picker_disabledButton, "picker_disabledButton"); + colorKeysMap.put(key_picker_badge, "picker_badge"); + colorKeysMap.put(key_picker_badgeText, "picker_badgeText"); + colorKeysMap.put(key_location_sendLocationBackground, "location_sendLocationBackground"); + colorKeysMap.put(key_location_sendLocationIcon, "location_sendLocationIcon"); + colorKeysMap.put(key_location_sendLocationText, "location_sendLocationText"); + colorKeysMap.put(key_location_sendLiveLocationBackground, "location_sendLiveLocationBackground"); + colorKeysMap.put(key_location_sendLiveLocationIcon, "location_sendLiveLocationIcon"); + colorKeysMap.put(key_location_sendLiveLocationText, "location_sendLiveLocationText"); + colorKeysMap.put(key_location_liveLocationProgress, "location_liveLocationProgress"); + colorKeysMap.put(key_location_placeLocationBackground, "location_placeLocationBackground"); + colorKeysMap.put(key_location_actionIcon, "location_actionIcon"); + colorKeysMap.put(key_location_actionActiveIcon, "location_actionActiveIcon"); + colorKeysMap.put(key_location_actionBackground, "location_actionBackground"); + colorKeysMap.put(key_location_actionPressedBackground, "location_actionPressedBackground"); + colorKeysMap.put(key_dialog_liveLocationProgress, "dialog_liveLocationProgress"); + colorKeysMap.put(key_files_folderIcon, "files_folderIcon"); + colorKeysMap.put(key_files_folderIconBackground, "files_folderIconBackground"); + colorKeysMap.put(key_files_iconText, "files_iconText"); + colorKeysMap.put(key_sessions_devicesImage, "sessions_devicesImage"); + colorKeysMap.put(key_calls_callReceivedGreenIcon, "calls_callReceivedGreenIcon"); + colorKeysMap.put(key_calls_callReceivedRedIcon, "calls_callReceivedRedIcon"); + colorKeysMap.put(key_undo_background, "undo_background"); + colorKeysMap.put(key_undo_cancelColor, "undo_cancelColor"); + colorKeysMap.put(key_undo_infoColor, "undo_infoColor"); + colorKeysMap.put(key_sheet_scrollUp, "key_sheet_scrollUp"); + colorKeysMap.put(key_sheet_other, "key_sheet_other"); + colorKeysMap.put(key_player_actionBarSelector, "player_actionBarSelector"); + colorKeysMap.put(key_player_actionBarTitle, "player_actionBarTitle"); + colorKeysMap.put(key_player_actionBarSubtitle, "player_actionBarSubtitle"); + colorKeysMap.put(key_player_actionBarItems, "player_actionBarItems"); + colorKeysMap.put(key_player_background, "player_background"); + colorKeysMap.put(key_player_time, "player_time"); + colorKeysMap.put(key_player_progressBackground, "player_progressBackground"); + colorKeysMap.put(key_player_progressCachedBackground, "key_player_progressCachedBackground"); + colorKeysMap.put(key_player_progress, "player_progress"); + colorKeysMap.put(key_player_button, "player_button"); + colorKeysMap.put(key_player_buttonActive, "player_buttonActive"); + + colorKeysMap.put(key_statisticChartSignature, "statisticChartSignature"); + colorKeysMap.put(key_statisticChartSignatureAlpha, "statisticChartSignatureAlpha"); + colorKeysMap.put(key_statisticChartHintLine, "statisticChartHintLine"); + colorKeysMap.put(key_statisticChartActiveLine, "statisticChartActiveLine"); + colorKeysMap.put(key_statisticChartInactivePickerChart, "statisticChartInactivePickerChart"); + colorKeysMap.put(key_statisticChartActivePickerChart, "statisticChartActivePickerChart"); + colorKeysMap.put(key_statisticChartRipple, "statisticChartRipple"); + colorKeysMap.put(key_statisticChartBackZoomColor, "statisticChartBackZoomColor"); + colorKeysMap.put(key_statisticChartChevronColor, "statisticChartChevronColor"); + colorKeysMap.put(key_statisticChartLine_blue, "statisticChartLine_blue"); + colorKeysMap.put(key_statisticChartLine_green, "statisticChartLine_green"); + colorKeysMap.put(key_statisticChartLine_red, "statisticChartLine_red"); + colorKeysMap.put(key_statisticChartLine_golden, "statisticChartLine_golden"); + colorKeysMap.put(key_statisticChartLine_lightblue, "statisticChartLine_lightblue"); + colorKeysMap.put(key_statisticChartLine_lightgreen, "statisticChartLine_lightgreen"); + colorKeysMap.put(key_statisticChartLine_orange, "statisticChartLine_orange"); + colorKeysMap.put(key_statisticChartLine_indigo, "statisticChartLine_indigo"); + colorKeysMap.put(key_statisticChartLine_purple, "statisticChartLine_purple"); + colorKeysMap.put(key_statisticChartLine_cyan, "statisticChartLine_cyan"); + colorKeysMap.put(key_statisticChartLineEmpty, "statisticChartLineEmpty"); + colorKeysMap.put(key_color_lightblue, "color_lightblue"); + colorKeysMap.put(key_color_blue, "color_blue"); + colorKeysMap.put(key_color_green, "color_green"); + colorKeysMap.put(key_color_lightgreen, "color_lightgreen"); + colorKeysMap.put(key_color_red, "color_red"); + colorKeysMap.put(key_color_orange, "color_orange"); + colorKeysMap.put(key_color_yellow, "color_yellow"); + colorKeysMap.put(key_color_purple, "color_purple"); + colorKeysMap.put(key_color_cyan, "color_cyan"); + colorKeysMap.put(key_chat_outReactionButtonBackground, "chat_outReactionButtonBackground"); + colorKeysMap.put(key_chat_inReactionButtonBackground, "chat_inReactionButtonBackground"); + colorKeysMap.put(key_chat_outReactionButtonText, "chat_outReactionButtonText"); + colorKeysMap.put(key_chat_inReactionButtonText, "chat_inReactionButtonText"); + colorKeysMap.put(key_chat_inReactionButtonTextSelected, "chat_inReactionButtonTextSelected"); + colorKeysMap.put(key_chat_outReactionButtonTextSelected, "chat_outReactionButtonTextSelected"); + colorKeysMap.put(key_premiumGradient0, "premiumGradient0"); + colorKeysMap.put(key_premiumGradient1, "premiumGradient1"); + colorKeysMap.put(key_premiumGradient2, "premiumGradient2"); + colorKeysMap.put(key_premiumGradient3, "premiumGradient3"); + colorKeysMap.put(key_premiumGradient4, "premiumGradient4"); + colorKeysMap.put(key_premiumGradientBackground1, "premiumGradientBackground1"); + colorKeysMap.put(key_premiumGradientBackground2, "premiumGradientBackground2"); + colorKeysMap.put(key_premiumGradientBackground3, "premiumGradientBackground3"); + colorKeysMap.put(key_premiumGradientBackground4, "premiumGradientBackground4"); + colorKeysMap.put(key_premiumGradientBackgroundOverlay, "premiumGradientBackgroundOverlay"); + colorKeysMap.put(key_premiumStartSmallStarsColor, "premiumStartSmallStarsColor"); + colorKeysMap.put(key_premiumStartGradient1, "premiumStarGradient1"); + colorKeysMap.put(key_premiumStartGradient2, "premiumStarGradient2"); + colorKeysMap.put(key_premiumStartSmallStarsColor2, "premiumStartSmallStarsColor2"); + colorKeysMap.put(key_premiumGradientBottomSheet1, "premiumGradientBottomSheet1"); + colorKeysMap.put(key_premiumGradientBottomSheet2, "premiumGradientBottomSheet2"); + colorKeysMap.put(key_premiumGradientBottomSheet3, "premiumGradientBottomSheet3"); + colorKeysMap.put(key_topics_unreadCounter, "topics_unreadCounter"); + colorKeysMap.put(key_topics_unreadCounterMuted, "topics_unreadCounterMuted"); + return colorKeysMap; + } + + private static HashMap createColorKeysStringMap() { + if (colorKeysMap == null) { + colorKeysMap = createColorKeysMap(); + } + HashMap map = new HashMap<>(); + for (int i = 0; i < colorKeysMap.size(); i++) { + map.put(colorKeysMap.valueAt(i), colorKeysMap.keyAt(i)); + } + return map; + } + + public static int stringKeyToInt(String key) { + if (colorKeysStringMap == null) { + colorKeysStringMap = createColorKeysStringMap(); + } + Integer i = colorKeysStringMap.get(key); + if (i == null) { + return -1; + } else { + return colorKeysStringMap.get(key); + } + } + + + public static String getStringName(int currentKey) { + if (colorKeysMap == null) { + colorKeysMap = createColorKeysMap(); + } + return colorKeysMap.get(currentKey); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ThemeDescription.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ThemeDescription.java index 23cbdec0e1..15a22e2e3c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ThemeDescription.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ThemeDescription.java @@ -19,7 +19,6 @@ import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.StateListDrawable; import android.os.Build; -import android.text.Spanned; import android.text.SpannedString; import android.text.TextPaint; import android.view.View; @@ -59,7 +58,6 @@ import org.telegram.ui.Components.ScamDrawable; import org.telegram.ui.Components.SeekBarView; import org.telegram.ui.Components.TypefaceSpan; -import org.telegram.ui.Components.VideoTimelineView; import java.lang.reflect.Field; import java.util.ArrayList; @@ -105,7 +103,7 @@ public class ThemeDescription { private Paint[] paintToUpdate; private Drawable[] drawablesToUpdate; private Class[] listClasses; - private String currentKey; + private int currentKey; private String lottieLayerName; private ThemeDescriptionDelegate delegate; private int previousColor; @@ -124,7 +122,7 @@ public interface ThemeDescriptionDelegate { default void onAnimationProgress(float progress) {} } - public ThemeDescription(View view, int flags, Class[] classes, Paint[] paint, Drawable[] drawables, ThemeDescriptionDelegate themeDescriptionDelegate, String key, Object unused) { + public ThemeDescription(View view, int flags, Class[] classes, Paint[] paint, Drawable[] drawables, ThemeDescriptionDelegate themeDescriptionDelegate, int key, Object unused) { currentKey = key; paintToUpdate = paint; drawablesToUpdate = drawables; @@ -137,7 +135,7 @@ public ThemeDescription(View view, int flags, Class[] classes, Paint[] paint, Dr } } - public ThemeDescription(View view, int flags, Class[] classes, Paint paint, Drawable[] drawables, ThemeDescriptionDelegate themeDescriptionDelegate, String key) { + public ThemeDescription(View view, int flags, Class[] classes, Paint paint, Drawable[] drawables, ThemeDescriptionDelegate themeDescriptionDelegate, int key) { currentKey = key; if (paint != null) { paintToUpdate = new Paint[]{paint}; @@ -152,7 +150,7 @@ public ThemeDescription(View view, int flags, Class[] classes, Paint paint, Draw } } - public ThemeDescription(View view, int flags, Class[] classes, RLottieDrawable[] drawables, String layerName, String key) { + public ThemeDescription(View view, int flags, Class[] classes, RLottieDrawable[] drawables, String layerName, int key) { currentKey = key; lottieLayerName = layerName; drawablesToUpdate = drawables; @@ -164,11 +162,11 @@ public ThemeDescription(View view, int flags, Class[] classes, RLottieDrawable[] } } - public ThemeDescription(View view, int flags, Class[] classes, String[] classesFields, Paint[] paint, Drawable[] drawables, ThemeDescriptionDelegate themeDescriptionDelegate, String key) { + public ThemeDescription(View view, int flags, Class[] classes, String[] classesFields, Paint[] paint, Drawable[] drawables, ThemeDescriptionDelegate themeDescriptionDelegate, int key) { this(view, flags, classes, classesFields, paint, drawables, -1, themeDescriptionDelegate, key); } - public ThemeDescription(View view, int flags, Class[] classes, String[] classesFields, Paint[] paint, Drawable[] drawables, int alpha, ThemeDescriptionDelegate themeDescriptionDelegate, String key) { + public ThemeDescription(View view, int flags, Class[] classes, String[] classesFields, Paint[] paint, Drawable[] drawables, int alpha, ThemeDescriptionDelegate themeDescriptionDelegate, int key) { currentKey = key; paintToUpdate = paint; drawablesToUpdate = drawables; @@ -185,7 +183,7 @@ public ThemeDescription(View view, int flags, Class[] classes, String[] classesF } } - public ThemeDescription(View view, int flags, Class[] classes, String[] classesFields, String layerName, String key) { + public ThemeDescription(View view, int flags, Class[] classes, String[] classesFields, String layerName, int key) { currentKey = key; lottieLayerName = layerName; viewToInvalidate = view; @@ -209,13 +207,13 @@ public void setColor(int color, boolean useDefault) { setColor(color, useDefault, true); } - private boolean checkTag(String key, View view) { - if (key == null || view == null) { + private boolean checkTag(int key, View view) { + if (key < 0 || view == null) { return false; } Object viewTag = view.getTag(); - if (viewTag instanceof String) { - return ((String) viewTag).contains(key); + if (viewTag instanceof Integer) { + return ((Integer) viewTag) == key; } return false; } @@ -766,7 +764,7 @@ private void processViewColor(View child, int color) { } } - public String getCurrentKey() { + public int getCurrentKey() { return currentKey; } @@ -800,6 +798,6 @@ public void setPreviousColor() { } public String getTitle() { - return currentKey; + return ThemeColors.getStringName(currentKey); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionIntroActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionIntroActivity.java index fa21e8e457..e6b97392b1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionIntroActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionIntroActivity.java @@ -606,7 +606,7 @@ protected void onDraw(Canvas canvas) { buttonTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); buttonTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); int buttonRadiusDp = currentType == ACTION_TYPE_SET_PASSCODE || currentType == ACTION_TYPE_CHANGE_PHONE_NUMBER || currentType == ACTION_TYPE_CHANNEL_CREATE ? 6 : 4; - buttonTextView.setBackground(Theme.AdaptiveRipple.filledRect(Theme.key_featuredStickers_addButton, buttonRadiusDp)); + buttonTextView.setBackground(Theme.AdaptiveRipple.filledRectByKey(Theme.key_featuredStickers_addButton, buttonRadiusDp)); viewGroup.addView(buttonTextView); buttonTextView.setOnClickListener(v -> { if (getParentActivity() == null) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/ContactsAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/ContactsAdapter.java index e2c4563893..b9bbbb9a69 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/ContactsAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/ContactsAdapter.java @@ -32,6 +32,7 @@ import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.ActionBar; +import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Cells.DividerCell; import org.telegram.ui.Cells.GraySectionCell; @@ -43,10 +44,13 @@ import org.telegram.ui.Components.ContactsEmptyView; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.Stories.DialogStoriesCell; +import org.telegram.ui.Stories.StoriesController; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.Objects; public class ContactsAdapter extends RecyclerListView.SectionsAdapter { @@ -64,8 +68,13 @@ public class ContactsAdapter extends RecyclerListView.SectionsAdapter { private boolean disableSections; private boolean hasGps; private boolean isEmpty; + public boolean hasStories; + public ArrayList userStories = new ArrayList<>(); - public ContactsAdapter(Context context, int onlyUsersType, boolean showPhoneBook, LongSparseArray usersToIgnore, int flags, boolean gps) { + DialogStoriesCell dialogStoriesCell; + BaseFragment fragment; + + public ContactsAdapter(Context context, BaseFragment fragment, int onlyUsersType, boolean showPhoneBook, LongSparseArray usersToIgnore, int flags, boolean gps) { mContext = context; onlyUsers = onlyUsersType; needPhonebook = showPhoneBook; @@ -73,6 +82,17 @@ public ContactsAdapter(Context context, int onlyUsersType, boolean showPhoneBook isAdmin = flags != 0; isChannel = flags == 2; hasGps = gps; + this.fragment = fragment; + } + + public void setStories(ArrayList stories, boolean animated) { +// boolean hasStories = !stories.isEmpty(); +// userStories.clear(); +// userStories.addAll(stories); +// if (this.hasStories != hasStories) { +// this.hasStories = hasStories; +// } +// update(true); } public void setDisableSections(boolean value) { @@ -165,6 +185,23 @@ public void setIsScrolling(boolean value) { } public Object getItem(int section, int position) { + if (getItemViewType(section, position) == 2) { + if (hasStories) { + return "Stories"; + } else { + return "Header"; + } + } + if (hasStories && section == 1) { + if (position == userStories.size()) { + return "Header"; + } else { + return userStories.get(position).user_id; + } + } else if (hasStories && section > 1) { + section--; + } + HashMap> usersSectionsDict = onlyUsers == 2 ? ContactsController.getInstance(currentAccount).usersMutualSectionsDict : ContactsController.getInstance(currentAccount).usersSectionsDict; ArrayList sortedUsersSectionsArray = onlyUsers == 2 ? ContactsController.getInstance(currentAccount).sortedUsersMutualSectionsArray : ContactsController.getInstance(currentAccount).sortedUsersSectionsArray; @@ -204,8 +241,32 @@ public Object getItem(int section, int position) { return null; } + + public int getHash(int section, int position) { + int sectionIndex = section; + if (hasStories && section == 1) { + if (position == userStories.size()) { + return Objects.hash(sectionIndex * -49612, getItem(section, position)); + } + return Objects.hash(section * -54323, getItem(section, position)); + } else if (hasStories && section > 1) { + sectionIndex--; + } + Object item = getItem(section, position); + return Objects.hash(sectionIndex * -49612, item); + } + @Override public boolean isEnabled(RecyclerView.ViewHolder holder, int section, int row) { + if (hasStories && section == 1) { + if (row == userStories.size()) { + return false; + } else { + return true; + } + } else if (hasStories && section > 1) { + section--; + } HashMap> usersSectionsDict = onlyUsers == 2 ? ContactsController.getInstance(currentAccount).usersMutualSectionsDict : ContactsController.getInstance(currentAccount).usersSectionsDict; ArrayList sortedUsersSectionsArray = onlyUsers == 2 ? ContactsController.getInstance(currentAccount).sortedUsersMutualSectionsArray : ContactsController.getInstance(currentAccount).sortedUsersSectionsArray; @@ -267,14 +328,26 @@ public int getSectionCount() { if (needPhonebook) { //count++; } + if (hasStories) { + count++; + } return count; } @Override public int getCountForSection(int section) { + return getCountForSectionInternal(section); + } + + private int getCountForSectionInternal(int section) { HashMap> usersSectionsDict = onlyUsers == 2 ? ContactsController.getInstance(currentAccount).usersMutualSectionsDict : ContactsController.getInstance(currentAccount).usersSectionsDict; ArrayList sortedUsersSectionsArray = onlyUsers == 2 ? ContactsController.getInstance(currentAccount).sortedUsersMutualSectionsArray : ContactsController.getInstance(currentAccount).sortedUsersSectionsArray; + if (hasStories && section == 1) { + return userStories.size() + 1; + } else if (hasStories && section > 1) { + section--; + } if (onlyUsers != 0 && !isAdmin) { if (isEmpty) { return 1; @@ -331,6 +404,12 @@ public View getSectionHeaderView(int section, View view) { view = new LetterSectionCell(mContext); } LetterSectionCell cell = (LetterSectionCell) view; + if (hasStories && section == 1) { + cell.setLetter(""); + return cell; + } else if (hasStories && section > 1) { + section--; + } if (sortType == SORT_TYPE_BY_TIME || disableSections || isEmpty) { cell.setLetter(""); } else { @@ -402,23 +481,73 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { frameLayout.addView(emptyView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); view = frameLayout; break; + case 6: + if (dialogStoriesCell == null) { + dialogStoriesCell = new DialogStoriesCell(mContext, fragment, currentAccount, DialogStoriesCell.TYPE_ARCHIVE) { + @Override + public void onUserLongPressed(View view, long dialogId) { + onStoryLongPressed(view, dialogId); + } + }; + dialogStoriesCell.setProgressToCollapse(0, false); + } else { + AndroidUtilities.removeFromParent(dialogStoriesCell); + } + FrameLayout storiesContainer = new FrameLayout(mContext); + storiesContainer.addView(dialogStoriesCell, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, 8, 0, 0)); + view = storiesContainer; + break; case 5: default: view = new ShadowSectionCell(mContext); - Drawable drawable = Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow); + Drawable drawable = Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow); CombinedDrawable combinedDrawable = new CombinedDrawable(new ColorDrawable(Theme.getColor(Theme.key_windowBackgroundGray)), drawable); combinedDrawable.setFullsize(true); view.setBackgroundDrawable(combinedDrawable); break; + } return new RecyclerListView.Holder(view); } @Override public void onBindViewHolder(int section, int position, RecyclerView.ViewHolder holder) { + if (hasStories && section == 1) { + switch (holder.getItemViewType()) { + case 0: + UserCell userCell = (UserCell) holder.itemView; + userCell.setAvatarPadding(6); + userCell.storyParams.drawSegments = true; + StoriesController storiesController = MessagesController.getInstance(currentAccount).getStoriesController(); + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(userStories.get(position).user_id); + if (storiesController.hasUnreadStories(user.id)) { + int newStories = storiesController.getUnreadStoriesCount(user.id); + userCell.setData(user, ContactsController.formatName(user), LocaleController.formatPluralString("NewStories", newStories, newStories).toLowerCase(), 0); + } else { + int storiesCount = userStories.get(position).stories.size(); + userCell.setData(user, ContactsController.formatName(user), LocaleController.formatPluralString("Stories", storiesCount, storiesCount).toLowerCase(), 0); + } + + break; + case 2: + GraySectionCell sectionCell = (GraySectionCell) holder.itemView; + if (sortType == SORT_TYPE_NONE) { + sectionCell.setText(LocaleController.getString("Contacts", R.string.Contacts)); + } else if (sortType == SORT_TYPE_BY_NAME) { + sectionCell.setText(LocaleController.getString("SortedByName", R.string.SortedByName)); + } else { + sectionCell.setText(LocaleController.getString("SortedByLastSeen", R.string.SortedByLastSeen)); + } + break; + } + return; + } else if (hasStories && section > 1) { + section--; + } switch (holder.getItemViewType()) { case 0: UserCell userCell = (UserCell) holder.itemView; + userCell.storyParams.drawSegments = false; userCell.setAvatarPadding(sortType == SORT_TYPE_BY_TIME || disableSections ? 6 : 58); ArrayList arr; if (sortType == SORT_TYPE_BY_TIME) { @@ -478,7 +607,9 @@ public void onBindViewHolder(int section, int position, RecyclerView.ViewHolder break; case 2: GraySectionCell sectionCell = (GraySectionCell) holder.itemView; - if (sortType == SORT_TYPE_NONE) { + if (hasStories) { + sectionCell.setText(LocaleController.getString("HiddenStories", R.string.HiddenStories)); + } else if (sortType == SORT_TYPE_NONE) { sectionCell.setText(LocaleController.getString("Contacts", R.string.Contacts)); } else if (sortType == SORT_TYPE_BY_NAME) { sectionCell.setText(LocaleController.getString("SortedByName", R.string.SortedByName)); @@ -493,6 +624,15 @@ public void onBindViewHolder(int section, int position, RecyclerView.ViewHolder public int getItemViewType(int section, int position) { HashMap> usersSectionsDict = onlyUsers == 2 ? ContactsController.getInstance(currentAccount).usersMutualSectionsDict : ContactsController.getInstance(currentAccount).usersSectionsDict; ArrayList sortedUsersSectionsArray = onlyUsers == 2 ? ContactsController.getInstance(currentAccount).sortedUsersMutualSectionsArray : ContactsController.getInstance(currentAccount).sortedUsersSectionsArray; + if (hasStories && section == 1) { + if (position == userStories.size()) { + return 2; + } else { + return 0; + } + } else if (hasStories && section > 1) { + section--; + } if (onlyUsers != 0 && !isAdmin) { if (isEmpty) { return 4; @@ -558,4 +698,25 @@ public void getPositionForScrollProgress(RecyclerListView listView, float progre position[0] = (int) (getItemCount() * progress); position[1] = 0; } + + public void onStoryLongPressed(View view, long dialogId) { + + } + + public void removeStory(long dialogId) { + for (int i = 0; i < userStories.size(); i++) { + if (userStories.get(i).user_id == dialogId) { + userStories.remove(i); + + if (userStories.isEmpty()) { + notifyItemRangeRemoved(getCountForSection(0) + i - 1, 2); + hasStories = false; + updateHashes(); + } else { + notifyItemRemoved(getCountForSection(0) + i); + } + break; + } + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsAdapter.java index 7bf08053bc..1a897adb7d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsAdapter.java @@ -18,6 +18,7 @@ import android.os.Build; import android.os.SystemClock; import android.util.TypedValue; +import android.view.ContextMenu; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; @@ -29,6 +30,9 @@ import androidx.recyclerview.widget.RecyclerView; import androidx.viewpager.widget.ViewPager; +import com.google.android.exoplayer2.util.Log; + +import org.checkerframework.checker.units.qual.A; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ChatObject; import org.telegram.messenger.ContactsController; @@ -36,10 +40,12 @@ import org.telegram.messenger.FileLog; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MessagesController; +import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; import org.telegram.messenger.SharedConfig; import org.telegram.messenger.UserConfig; import org.telegram.messenger.UserObject; +import org.telegram.messenger.support.LongSparseIntArray; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; @@ -49,6 +55,7 @@ import org.telegram.ui.Cells.DialogCell; import org.telegram.ui.Cells.DialogMeUrlCell; import org.telegram.ui.Cells.DialogsEmptyCell; +import org.telegram.ui.Cells.DialogsHintCell; import org.telegram.ui.Cells.DialogsRequestedEmptyCell; import org.telegram.ui.Cells.HeaderCell; import org.telegram.ui.Cells.ProfileSearchCell; @@ -57,6 +64,7 @@ import org.telegram.ui.Cells.TextCell; import org.telegram.ui.Cells.TextInfoPrivacyCell; import org.telegram.ui.Cells.UserCell; +import org.telegram.ui.Components.ArchiveHelp; import org.telegram.ui.Components.BlurredRecyclerView; import org.telegram.ui.Components.CombinedDrawable; import org.telegram.ui.Components.FlickerLoadingView; @@ -65,6 +73,10 @@ import org.telegram.ui.Components.PullForegroundDrawable; import org.telegram.ui.Components.RecyclerListView; import org.telegram.ui.DialogsActivity; +import org.telegram.ui.Stories.DialogStoriesCell; +import org.telegram.ui.Stories.StoriesController; +import org.telegram.ui.Stories.StoriesListPlaceProvider; +import org.telegram.ui.Stories.StoriesUtilities; import java.util.ArrayList; import java.util.Collections; @@ -81,14 +93,17 @@ public class DialogsAdapter extends RecyclerListView.SelectionAdapter implements VIEW_TYPE_USER = 6, VIEW_TYPE_HEADER = 7, VIEW_TYPE_SHADOW = 8, - VIEW_TYPE_ARCHIVE = 9, - VIEW_TYPE_LAST_EMPTY = 10, + // VIEW_TYPE_ARCHIVE = 9, + VIEW_TYPE_LAST_EMPTY = 10, VIEW_TYPE_NEW_CHAT_HINT = 11, VIEW_TYPE_TEXT = 12, VIEW_TYPE_CONTACTS_FLICKER = 13, VIEW_TYPE_HEADER_2 = 14, VIEW_TYPE_REQUIREMENTS = 15, - VIEW_TYPE_REQUIRED_EMPTY = 16; + VIEW_TYPE_REQUIRED_EMPTY = 16, + VIEW_TYPE_FOLDER_UPDATE_HINT = 17, + VIEW_TYPE_STORIES = 18, + VIEW_TYPE_ARCHIVE_FULLSCREEN = 19; private Context mContext; private ArchiveHintCell archiveHintCell; @@ -104,12 +119,13 @@ public class DialogsAdapter extends RecyclerListView.SelectionAdapter implements private boolean isOnlySelect; private ArrayList selectedDialogs; private boolean hasHints; + private boolean hasChatlistHint; private int currentAccount; private boolean dialogsListFrozen; - private boolean showArchiveHint; private boolean isReordering; private long lastSortTime; private boolean collapsedView; + private boolean firstUpdate = true; RecyclerListView recyclerListView; private PullForegroundDrawable pullForegroundDrawable; ArrayList itemInternals = new ArrayList<>(); @@ -122,7 +138,6 @@ public class DialogsAdapter extends RecyclerListView.SelectionAdapter implements private DialogsActivity parentFragment; private boolean isTransitionSupport; - private boolean fromDiffUtils; private TLRPC.RequestPeerType requestPeerType; public boolean isEmpty; @@ -136,11 +151,7 @@ public DialogsAdapter(DialogsActivity fragment, Context context, int type, int f hasHints = folder == 0 && type == 0 && !onlySelect; selectedDialogs = selected; currentAccount = account; - if (folderId == 1) { - SharedPreferences preferences = MessagesController.getGlobalMainSettings(); - showArchiveHint = preferences.getBoolean("archivehint", true); - preferences.edit().putBoolean("archivehint", false).commit(); - } + // setHasStableIds(true); if (folder == 0) { this.preloader = new DialogsPreloader(); } @@ -160,12 +171,13 @@ public void onReorderStateChanged(boolean reordering) { } public int fixPosition(int position) { + if (hasChatlistHint) { + position--; + } if (hasHints) { position -= 2 + MessagesController.getInstance(currentAccount).hintDialogs.size(); } - if (showArchiveHint) { - position -= 2; - } else if (dialogsType == DialogsActivity.DIALOGS_TYPE_IMPORT_HISTORY_GROUPS || dialogsType == DialogsActivity.DIALOGS_TYPE_IMPORT_HISTORY) { + if (dialogsType == DialogsActivity.DIALOGS_TYPE_IMPORT_HISTORY_GROUPS || dialogsType == DialogsActivity.DIALOGS_TYPE_IMPORT_HISTORY) { position -= 2; } else if (dialogsType == DialogsActivity.DIALOGS_TYPE_IMPORT_HISTORY_USERS) { position -= 1; @@ -190,6 +202,11 @@ public int getDialogsCount() { return dialogsCount; } + @Override + public long getItemId(int position) { + return itemInternals.get(position).stableId; + } + @Override public int getItemCount() { currentCount = itemInternals.size(); @@ -205,29 +222,39 @@ public int findDialogPosition(long dialogId) { return -1; } - public int fixScrollGap(RecyclerListView animationSupportListView, int p, int offset, boolean hasHidenArchive, boolean oppened) { - int itemsToEnd = getItemCount() - p ; + public int fixScrollGap(RecyclerListView animationSupportListView, int p, int offset, boolean hasHidenArchive, boolean hasStories, boolean hasTabs, boolean oppened) { + int itemsToEnd = getItemCount() - p; int cellHeight = AndroidUtilities.dp(SharedConfig.useThreeLinesLayout ? 78 : 72); int bottom = offset + animationSupportListView.getPaddingTop() + itemsToEnd * cellHeight + itemsToEnd - 1; //fix height changed - int top = offset + animationSupportListView.getPaddingTop() - p * cellHeight - p; + int top = offset + animationSupportListView.getPaddingTop() - p * cellHeight - p; + int additionalHeight = 0; + if (hasStories) { + additionalHeight += AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP); + } else if (hasTabs) { + additionalHeight += AndroidUtilities.dp(44); + } if (oppened) { - bottom -= AndroidUtilities.dp(44); + bottom -= additionalHeight; } else { - bottom += AndroidUtilities.dp(44); + bottom += additionalHeight; } if (hasHidenArchive) { top += cellHeight; } - if (top > animationSupportListView.getPaddingTop()) { - return offset + animationSupportListView.getPaddingTop() - top; - } - if (bottom < animationSupportListView.getMeasuredHeight()) { - return offset + (animationSupportListView.getMeasuredHeight() - bottom); + int paddingTop = animationSupportListView.getPaddingTop(); + if (top > paddingTop) { + return offset + paddingTop - top; } +// if (bottom < animationSupportListView.getMeasuredHeight()) { +// return offset + (animationSupportListView.getMeasuredHeight() - bottom); +// } return offset; } + int stableIdPointer = 10; + LongSparseIntArray dialogsStableIds = new LongSparseIntArray(); + private class ItemInternal extends AdapterWithDiffUtils.Item { TLRPC.Dialog dialog; @@ -236,10 +263,35 @@ private class ItemInternal extends AdapterWithDiffUtils.Item { boolean isForumCell; private boolean pinned; private boolean isFolder; + TLRPC.TL_chatlists_chatlistUpdates chatlistUpdates; + private int emptyType; + + public ItemInternal(TLRPC.TL_chatlists_chatlistUpdates updates) { + super(VIEW_TYPE_FOLDER_UPDATE_HINT, true); + this.chatlistUpdates = updates; + stableId = stableIdPointer++; + } + + private final int stableId; public ItemInternal(int viewType, TLRPC.Dialog dialog) { super(viewType, true); this.dialog = dialog; + if (dialog != null) { + int currentId = dialogsStableIds.get(dialog.id, -1); + if (currentId >= 0) { + stableId = currentId; + } else { + stableId = stableIdPointer++; + dialogsStableIds.put(dialog.id, stableId); + } + } else { + if (viewType == VIEW_TYPE_ARCHIVE_FULLSCREEN) { + stableId = 5; + } else { + stableId = stableIdPointer++; + } + } if (dialog != null) { if (dialogsType == 7 || dialogsType == 8) { MessagesController.DialogFilter filter = MessagesController.getInstance(currentAccount).selectedDialogFilter[dialogsType == 8 ? 1 : 0]; @@ -255,15 +307,43 @@ public ItemInternal(int viewType, TLRPC.Dialog dialog) { public ItemInternal(int viewTypeMeUrl, TLRPC.RecentMeUrl recentMeUrl) { super(viewTypeMeUrl, true); this.recentMeUrl = recentMeUrl; + stableId = stableIdPointer++; } public ItemInternal(int viewTypeEmpty) { super(viewTypeEmpty, true); + this.emptyType = emptyType; + if (viewTypeEmpty == VIEW_TYPE_LAST_EMPTY) { + stableId = 1; + } else { + if (viewType == VIEW_TYPE_ARCHIVE_FULLSCREEN) { + stableId = 5; + } else { + stableId = stableIdPointer++; + } + } + } + + public ItemInternal(int viewTypeEmpty, int emptyType) { + super(viewTypeEmpty, true); + this.emptyType = emptyType; + stableId = stableIdPointer++; } public ItemInternal(int viewTypeUser, TLRPC.TL_contact tl_contact) { super(viewTypeUser, true); contact = tl_contact; + if (contact != null) { + int currentId = dialogsStableIds.get(contact.user_id, -1); + if (currentId > 0) { + stableId = currentId; + } else { + stableId = stableIdPointer++; + dialogsStableIds.put(contact.user_id, stableId); + } + } else { + stableId = stableIdPointer++; + } } boolean compare(ItemInternal itemInternal) { @@ -285,6 +365,12 @@ boolean compare(ItemInternal itemInternal) { if (viewType == VIEW_TYPE_USER) { return contact != null && itemInternal.contact != null && contact.user_id == itemInternal.contact.user_id; } + if (viewType == VIEW_TYPE_EMPTY) { + return emptyType == itemInternal.emptyType; + } + if (viewType == VIEW_TYPE_LAST_EMPTY) { + return false; + } return true; } @@ -380,7 +466,7 @@ public void updateHasHints() { hasHints = folderId == 0 && dialogsType == DialogsActivity.DIALOGS_TYPE_DEFAULT && !isOnlySelect && !MessagesController.getInstance(currentAccount).hintDialogs.isEmpty(); } - public void updateList(RecyclerListView recyclerListView, boolean hasHiddenArchive, float tabsTranslation) { + public void updateList(RecyclerListView recyclerListView, boolean hasHiddenArchive, float tabsTranslation, boolean hasStories) { oldItems.clear(); oldItems.addAll(itemInternals); updateItemList(); @@ -400,17 +486,23 @@ public void updateList(RecyclerListView recyclerListView, boolean hasHiddenArchi } } if (view != null) { - float offset = view.getTop() - recyclerListView.getPaddingTop() + tabsTranslation; - if (hasHiddenArchive && position == 0 && view.getTop() - recyclerListView.getPaddingTop() + tabsTranslation < AndroidUtilities.dp(SharedConfig.useThreeLinesLayout ? 78 : 72)) { + float offset = view.getTop() - recyclerListView.getPaddingTop(); + if (!hasStories) { + // offset += tabsTranslation; + } else { + tabsTranslation = 0; + } + if (hasHiddenArchive && position == 0 && recyclerListView.getPaddingTop() - view.getTop() - view.getMeasuredHeight() + tabsTranslation < 0) { position = 1; offset = tabsTranslation; } +// if (firstUpdate && hasStories) { +// offset -= AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP); +// } +// firstUpdate = false; layoutManager.scrollToPositionWithOffset(position, (int) offset); } } - - - fromDiffUtils = true; DiffUtil.calculateDiff(new DiffUtil.Callback() { @Override public int getOldListSize() { @@ -432,7 +524,6 @@ public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { return oldItems.get(oldItemPosition).viewType == itemInternals.get(newItemPosition).viewType; } }).dispatchUpdatesTo(this); - fromDiffUtils = false; } @Override @@ -447,9 +538,6 @@ public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) { if (holder.itemView instanceof DialogCell) { DialogCell dialogCell = (DialogCell) holder.itemView; dialogCell.onReorderStateChanged(isReordering, false); - int position = fixPosition(holder.getAdapterPosition()); - // dialogCell.setDialogIndex(position); - // dialogCell.collapsed = collapsedView; dialogCell.checkCurrentDialogIndex(dialogsListFrozen); dialogCell.setChecked(selectedDialogs.contains(dialogCell.getDialogId()), false); } @@ -459,9 +547,9 @@ public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) { public boolean isEnabled(RecyclerView.ViewHolder holder) { int viewType = holder.getItemViewType(); return viewType != VIEW_TYPE_FLICKER && viewType != VIEW_TYPE_EMPTY && viewType != VIEW_TYPE_DIVIDER && - viewType != VIEW_TYPE_SHADOW && viewType != VIEW_TYPE_HEADER && viewType != VIEW_TYPE_ARCHIVE && + viewType != VIEW_TYPE_SHADOW && viewType != VIEW_TYPE_HEADER && viewType != VIEW_TYPE_LAST_EMPTY && viewType != VIEW_TYPE_NEW_CHAT_HINT && viewType != VIEW_TYPE_CONTACTS_FLICKER && - viewType != VIEW_TYPE_REQUIREMENTS && viewType != VIEW_TYPE_REQUIRED_EMPTY; + viewType != VIEW_TYPE_REQUIREMENTS && viewType != VIEW_TYPE_REQUIRED_EMPTY && viewType != VIEW_TYPE_STORIES && viewType != VIEW_TYPE_ARCHIVE_FULLSCREEN; } @Override @@ -470,7 +558,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewT switch (viewType) { case VIEW_TYPE_DIALOG: if (dialogsType == DialogsActivity.DIALOGS_TYPE_ADD_USERS_TO || - dialogsType == DialogsActivity.DIALOGS_TYPE_BOT_REQUEST_PEER) { + dialogsType == DialogsActivity.DIALOGS_TYPE_BOT_REQUEST_PEER) { view = new ProfileSearchCell(mContext); } else { DialogCell dialogCell = new DialogCell(parentFragment, mContext, true, false, currentAccount, null); @@ -531,7 +619,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { }; frameLayout.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundGray)); View v = new View(mContext); - v.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + v.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); frameLayout.addView(v, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); view = frameLayout; break; @@ -564,15 +652,19 @@ protected void onButtonClick() { break; case VIEW_TYPE_SHADOW: { view = new ShadowSectionCell(mContext); - Drawable drawable = Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow); + Drawable drawable = Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow); CombinedDrawable combinedDrawable = new CombinedDrawable(new ColorDrawable(Theme.getColor(Theme.key_windowBackgroundGray)), drawable); combinedDrawable.setFullsize(true); view.setBackgroundDrawable(combinedDrawable); break; } - case VIEW_TYPE_ARCHIVE: - archiveHintCell = new ArchiveHintCell(mContext); - view = archiveHintCell; + case VIEW_TYPE_ARCHIVE_FULLSCREEN: + LastEmptyView lastEmptyView = new LastEmptyView(mContext); + lastEmptyView.addView( + new ArchiveHelp(mContext, currentAccount, null, DialogsAdapter.this::onArchiveSettingsClick, null), + LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.CENTER, 0, -(int) (DialogStoriesCell.HEIGHT_IN_DP * .5f), 0, 0) + ); + view = lastEmptyView; break; case VIEW_TYPE_LAST_EMPTY: { view = new LastEmptyView(mContext); @@ -627,21 +719,34 @@ protected void onTextDraw() { } } }; - Drawable drawable = Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow); + Drawable drawable = Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow); CombinedDrawable combinedDrawable = new CombinedDrawable(new ColorDrawable(Theme.getColor(Theme.key_windowBackgroundGray)), drawable); combinedDrawable.setFullsize(true); view.setBackgroundDrawable(combinedDrawable); break; } + case VIEW_TYPE_FOLDER_UPDATE_HINT: + view = new DialogsHintCell(mContext); + break; case VIEW_TYPE_TEXT: default: { view = new TextCell(mContext); if (dialogsType == DialogsActivity.DIALOGS_TYPE_BOT_REQUEST_PEER) { view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); } + break; + } + case VIEW_TYPE_STORIES: { + view = new View(mContext) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP), MeasureSpec.EXACTLY)); + } + }; + break; } } - view.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, viewType == 5 ? RecyclerView.LayoutParams.MATCH_PARENT : RecyclerView.LayoutParams.WRAP_CONTENT)); + view.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, viewType == VIEW_TYPE_EMPTY || viewType == VIEW_TYPE_ARCHIVE_FULLSCREEN ? RecyclerView.LayoutParams.MATCH_PARENT : RecyclerView.LayoutParams.WRAP_CONTENT)); return new RecyclerListView.Holder(view); } @@ -649,6 +754,10 @@ public void onCreateGroupForThisClick() { } + protected void onArchiveSettingsClick() { + + } + public int lastDialogsEmptyType = -1; public int dialogsEmptyType() { @@ -658,6 +767,8 @@ public int dialogsEmptyType() { } else { return DialogsEmptyCell.TYPE_FILTER_ADDING_CHATS; } + } else if (folderId == 1) { + return DialogsEmptyCell.TYPE_FILTER_NO_CHATS_TO_DISPLAY; } else { return onlineContacts != null ? DialogsEmptyCell.TYPE_WELCOME_WITH_CONTACTS : DialogsEmptyCell.TYPE_WELCOME_NO_CONTACTS; } @@ -758,7 +869,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int i) { case VIEW_TYPE_EMPTY: { DialogsEmptyCell cell = (DialogsEmptyCell) holder.itemView; int fromDialogsEmptyType = lastDialogsEmptyType; - cell.setType(lastDialogsEmptyType = dialogsEmptyType()); + cell.setType(lastDialogsEmptyType = dialogsEmptyType(), isOnlySelect); if (dialogsType != 7 && dialogsType != 8) { cell.setOnUtyanAnimationEndListener(() -> parentFragment.setScrollDisabled(false)); cell.setOnUtyanAnimationUpdateListener(progress -> parentFragment.setContactsAlpha(progress)); @@ -864,12 +975,37 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int i) { cell.set(requestPeerType); break; } + case VIEW_TYPE_FOLDER_UPDATE_HINT: { + DialogsHintCell hintCell = (DialogsHintCell) holder.itemView; + ItemInternal item = itemInternals.get(i); + if (item.chatlistUpdates != null) { + int count = item.chatlistUpdates.missing_peers.size(); + hintCell.setText( + AndroidUtilities.replaceSingleTag( + LocaleController.formatPluralString("FolderUpdatesTitle", count), + Theme.key_windowBackgroundWhiteValueText, + 0, + null + ), + LocaleController.formatPluralString("FolderUpdatesSubtitle", count) + ); + } + break; + } } if (i >= dialogsCount + 1) { holder.itemView.setAlpha(1f); } } + public TLRPC.TL_chatlists_chatlistUpdates getChatlistUpdate() { + ItemInternal item = itemInternals.get(0); + if (item != null && item.viewType == VIEW_TYPE_FOLDER_UPDATE_HINT) { + return item.chatlistUpdates; + } + return null; + } + public void setForceUpdatingContacts(boolean forceUpdatingContacts) { this.forceUpdatingContacts = forceUpdatingContacts; } @@ -897,8 +1033,9 @@ public void moveDialogs(RecyclerListView recyclerView, int fromPosition, int toP toDialog.pinnedNum = oldNum; } Collections.swap(dialogs, fromIndex, toIndex); - updateList(recyclerView, false, 0); + updateList(recyclerView, false, 0, false); } + @Override public void notifyItemMoved(int fromPosition, int toPosition) { super.notifyItemMoved(fromPosition, toPosition); @@ -941,6 +1078,38 @@ public boolean canClickButtonInside() { return selectedDialogs.isEmpty(); } + @Override + public void openStory(DialogCell dialogCell, Runnable onDone) { + MessagesController messagesController = MessagesController.getInstance(currentAccount); + if (MessagesController.getInstance(currentAccount).getStoriesController().hasStories(dialogCell.getDialogId())) { + parentFragment.getOrCreateStoryViewer().doOnAnimationReady(onDone); + parentFragment.getOrCreateStoryViewer().open(parentFragment.getContext(), dialogCell.getDialogId(), StoriesListPlaceProvider.of((RecyclerListView) dialogCell.getParent())); + return; + } + } + + @Override + public void showChatPreview(DialogCell cell) { + parentFragment.showChatPreview(cell); + } + + @Override + public void openHiddenStories() { + StoriesController storiesController = MessagesController.getInstance(currentAccount).getStoriesController(); + if (storiesController.getHiddenList().isEmpty()) { + return; + } + boolean unreadOnly = storiesController.getUnreadState(storiesController.getHiddenList().get(0).user_id) != StoriesController.STATE_READ; + ArrayList peerIds = new ArrayList<>(); + for (int i = 0; i < storiesController.getHiddenList().size(); i++) { + if (!unreadOnly || storiesController.getUnreadState(storiesController.getHiddenList().get(i).user_id) != StoriesController.STATE_READ) { + peerIds.add(storiesController.getHiddenList().get(i).user_id); + } + } + + parentFragment.getOrCreateStoryViewer().open(mContext, null, peerIds, 0, null, null, StoriesListPlaceProvider.of(recyclerListView, true), false); + } + public void setIsTransitionSupport() { this.isTransitionSupport = true; } @@ -1087,7 +1256,7 @@ public void setForceShowEmptyCell(boolean forceShowEmptyCell) { this.forceShowEmptyCell = forceShowEmptyCell; } - public class LastEmptyView extends View { + public class LastEmptyView extends FrameLayout { public boolean moving; @@ -1097,16 +1266,28 @@ public LastEmptyView(Context context) { protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int size = itemInternals.size(); - boolean hasArchive = dialogsType == 0 && MessagesController.getInstance(currentAccount).dialogs_dict.get(DialogObject.makeFolderDialogId(1)) != null; + boolean hasArchive = folderId == 0 && dialogsType == 0 && MessagesController.getInstance(currentAccount).dialogs_dict.get(DialogObject.makeFolderDialogId(1)) != null; View parent = (View) getParent(); int height; int blurOffset = 0; if (parent instanceof BlurredRecyclerView) { blurOffset = ((BlurredRecyclerView) parent).blurTopPadding; } + boolean collapsedView = DialogsAdapter.this.collapsedView; int paddingTop = parent.getPaddingTop(); paddingTop -= blurOffset; - if (size == 0 || paddingTop == 0 && !hasArchive) { + if (folderId == 1 && size == 1 && itemInternals.get(0).viewType == VIEW_TYPE_ARCHIVE_FULLSCREEN) { + height = MeasureSpec.getSize(heightMeasureSpec); + if (height == 0) { + height = parent.getMeasuredHeight(); + } + if (height == 0) { + height = AndroidUtilities.displaySize.y - ActionBar.getCurrentActionBarHeight() - (Build.VERSION.SDK_INT >= 21 ? AndroidUtilities.statusBarHeight : 0); + } + if (parentFragment.hasStories) { + height += AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP); + } + } else if (size == 0 || paddingTop == 0 && !hasArchive) { height = 0; } else { height = MeasureSpec.getSize(heightMeasureSpec); @@ -1137,23 +1318,33 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { height = height - dialogsHeight + archiveHeight; if (paddingTop != 0) { height -= AndroidUtilities.statusBarHeight; - if (height < 0) { - height = 0; + if (parentFragment.hasStories && !collapsedView && !isTransitionSupport) { + height -= ActionBar.getCurrentActionBarHeight(); + } else if (collapsedView) { + height -= paddingTop; } } } else if (dialogsHeight - height < archiveHeight) { height = archiveHeight - (dialogsHeight - height); if (paddingTop != 0) { height -= AndroidUtilities.statusBarHeight; - } - if (height < 0) { - height = 0; + if (parentFragment.hasStories && !collapsedView && !isTransitionSupport) { + height -= ActionBar.getCurrentActionBarHeight(); + } else if (collapsedView) { + height -= paddingTop; + } } } else { height = 0; } } - setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), height); + if (height < 0) { + height = 0; + } + if (isTransitionSupport) { + height += AndroidUtilities.dp(1000); + } + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); } } @@ -1164,8 +1355,15 @@ private void updateItemList() { MessagesController messagesController = MessagesController.getInstance(currentAccount); ArrayList array = parentFragment.getDialogsArray(currentAccount, dialogsType, folderId, dialogsListFrozen); + if (array == null) { + array = new ArrayList<>(); + } dialogsCount = array.size(); isEmpty = false; + if (dialogsCount == 0 && parentFragment.isArchive()) { + itemInternals.add(new ItemInternal(VIEW_TYPE_ARCHIVE_FULLSCREEN)); + return; + } if (!hasHints && dialogsType == 0 && folderId == 0 && messagesController.isDialogsEndReached(folderId) && !forceUpdatingContacts) { if (messagesController.getAllFoldersDialogsCount() <= 10 && ContactsController.getInstance(currentAccount).doneLoadingContacts && !ContactsController.getInstance(currentAccount).contacts.isEmpty()) { @@ -1189,11 +1387,23 @@ private void updateItemList() { } } + hasChatlistHint = false; + if (dialogsType == 7 || dialogsType == 8) { + MessagesController.DialogFilter filter = messagesController.selectedDialogFilter[dialogsType - 7]; + if (filter != null && filter.isChatlist()) { + messagesController.checkChatlistFolderUpdate(filter.id, false); + TLRPC.TL_chatlists_chatlistUpdates updates = messagesController.getChatlistFolderUpdates(filter.id); + if (updates != null && updates.missing_peers.size() > 0) { + hasChatlistHint = true; + itemInternals.add(new ItemInternal(updates)); + } + } + } + if (requestPeerType != null) { itemInternals.add(new ItemInternal(VIEW_TYPE_REQUIREMENTS)); } - boolean stopUpdate = false; if (collapsedView || isTransitionSupport) { for (int k = 0; k < array.size(); k++) { if (dialogsType == 2 && array.get(k) instanceof DialogsActivity.DialogsHeader) { @@ -1202,19 +1412,29 @@ private void updateItemList() { itemInternals.add(new ItemInternal(VIEW_TYPE_DIALOG, array.get(k))); } } + itemInternals.add(new ItemInternal(VIEW_TYPE_LAST_EMPTY)); return; } + boolean stopUpdate = false; if (dialogsCount == 0 && forceUpdatingContacts) { isEmpty = true; - itemInternals.add(new ItemInternal(requestPeerType == null ? VIEW_TYPE_EMPTY : VIEW_TYPE_REQUIRED_EMPTY)); + if (requestPeerType != null) { + itemInternals.add(new ItemInternal(VIEW_TYPE_REQUIRED_EMPTY)); + } else { + itemInternals.add(new ItemInternal(VIEW_TYPE_EMPTY, dialogsEmptyType())); + } itemInternals.add(new ItemInternal(VIEW_TYPE_SHADOW)); itemInternals.add(new ItemInternal(VIEW_TYPE_HEADER)); itemInternals.add(new ItemInternal(VIEW_TYPE_CONTACTS_FLICKER)); - } else if (onlineContacts != null && !onlineContacts.isEmpty()) { + } else if (onlineContacts != null && !onlineContacts.isEmpty() && dialogsType != 7 && dialogsType != 8) { if (dialogsCount == 0) { isEmpty = true; - itemInternals.add(new ItemInternal(requestPeerType == null ? VIEW_TYPE_EMPTY : VIEW_TYPE_REQUIRED_EMPTY)); + if (requestPeerType != null) { + itemInternals.add(new ItemInternal(VIEW_TYPE_REQUIRED_EMPTY)); + } else { + itemInternals.add(new ItemInternal(VIEW_TYPE_EMPTY, dialogsEmptyType())); + } itemInternals.add(new ItemInternal(VIEW_TYPE_SHADOW)); itemInternals.add(new ItemInternal(VIEW_TYPE_HEADER)); } else { @@ -1236,9 +1456,6 @@ private void updateItemList() { itemInternals.add(new ItemInternal(VIEW_TYPE_ME_URL, MessagesController.getInstance(currentAccount).hintDialogs.get(k))); } itemInternals.add(new ItemInternal(VIEW_TYPE_DIVIDER)); - } else if (showArchiveHint) { - itemInternals.add(new ItemInternal(VIEW_TYPE_ARCHIVE)); - itemInternals.add(new ItemInternal(VIEW_TYPE_SHADOW)); } else if (dialogsType == DialogsActivity.DIALOGS_TYPE_IMPORT_HISTORY_GROUPS || dialogsType == DialogsActivity.DIALOGS_TYPE_IMPORT_HISTORY) { itemInternals.add(new ItemInternal(VIEW_TYPE_HEADER)); itemInternals.add(new ItemInternal(VIEW_TYPE_TEXT)); @@ -1265,7 +1482,11 @@ private void updateItemList() { } } else if (dialogsCount == 0) { isEmpty = true; - itemInternals.add(new ItemInternal(requestPeerType == null ? VIEW_TYPE_EMPTY : VIEW_TYPE_REQUIRED_EMPTY)); + if (requestPeerType != null) { + itemInternals.add(new ItemInternal(VIEW_TYPE_REQUIRED_EMPTY)); + } else { + itemInternals.add(new ItemInternal(VIEW_TYPE_EMPTY, dialogsEmptyType())); + } } else { if (folderId == 0 && dialogsCount > 10 && dialogsType == DialogsActivity.DIALOGS_TYPE_DEFAULT) { itemInternals.add(new ItemInternal(VIEW_TYPE_NEW_CHAT_HINT)); @@ -1273,5 +1494,27 @@ private void updateItemList() { itemInternals.add(new ItemInternal(VIEW_TYPE_LAST_EMPTY)); } } + + if (!messagesController.hiddenUndoChats.isEmpty()) { + for (int i = 0; i < itemInternals.size(); ++i) { + ItemInternal item = itemInternals.get(i); + if (item.viewType == VIEW_TYPE_DIALOG && item.dialog != null && messagesController.isHiddenByUndo(item.dialog.id)) { + itemInternals.remove(i); + i--; + } + } + } + } + + public int getItemHeight(int position) { + int cellHeight = AndroidUtilities.dp(SharedConfig.useThreeLinesLayout ? 78 : 72); + if (itemInternals.get(position).viewType == VIEW_TYPE_DIALOG) { + if (itemInternals.get(position).isForumCell && !collapsedView) { + return AndroidUtilities.dp(SharedConfig.useThreeLinesLayout ? 86 : 91); + } else { + return cellHeight; + } + } + return 0; } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsSearchAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsSearchAdapter.java index c9bb01dd2c..8a954076c2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsSearchAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsSearchAdapter.java @@ -22,8 +22,6 @@ import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; import org.telegram.PhoneFormat.PhoneFormat; import org.telegram.SQLite.SQLiteCursor; import org.telegram.SQLite.SQLitePreparedStatement; @@ -579,6 +577,7 @@ private boolean resentSearchAvailable() { dialogsType != DialogsActivity.DIALOGS_TYPE_USERS_ONLY && dialogsType != DialogsActivity.DIALOGS_TYPE_CHANNELS_ONLY && dialogsType != DialogsActivity.DIALOGS_TYPE_GROUPS_ONLY && + dialogsType != DialogsActivity.DIALOGS_TYPE_BOT_SHARE && dialogsType != DialogsActivity.DIALOGS_TYPE_IMPORT_HISTORY_GROUPS && dialogsType != DialogsActivity.DIALOGS_TYPE_BOT_REQUEST_PEER ); @@ -1287,10 +1286,12 @@ public boolean isGlobalSearch(int i) { globalCount = 4; } int contactsCount = searchContacts.size(); - if (i >= 0 && i < contactsCount) { - return false; + if (contactsCount > 0) { + if (i >= 0 && i < contactsCount) { + return false; + } + i -= contactsCount + 1; } - i -= contactsCount + 1; if (i >= 0 && i < localCount) { return false; } @@ -1753,7 +1754,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { case VIEW_TYPE_ADD_BY_PHONE: { String str = (String) getItem(position); TextCell cell = (TextCell) holder.itemView; - cell.setColors(null, Theme.key_windowBackgroundWhiteBlueText2); + cell.setColors(-1, Theme.key_windowBackgroundWhiteBlueText2); cell.setText(LocaleController.formatString("AddContactByPhone", R.string.AddContactByPhone, PhoneFormat.getInstance().format("+" + str)), false); break; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DrawerLayoutAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DrawerLayoutAdapter.java index 83032cfc53..ea7274b388 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DrawerLayoutAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DrawerLayoutAdapter.java @@ -350,10 +350,15 @@ private void resetItems() { UserConfig me = UserConfig.getInstance(UserConfig.selectedAccount); if (me != null && me.isPremium()) { if (me.getEmojiStatus() != null) { - items.add(new Item(15, LocaleController.getString("ChangeEmojiStatus", R.string.ChangeEmojiStatus), 0, R.raw.emoji_status_change_to_set)); + items.add(new Item(15, LocaleController.getString("ChangeEmojiStatus", R.string.ChangeEmojiStatus), R.drawable.msg_status_edit)); } else { - items.add(new Item(15, LocaleController.getString("SetEmojiStatus", R.string.SetEmojiStatus), 0, R.raw.emoji_status_set_to_change)); + items.add(new Item(15, LocaleController.getString("SetEmojiStatus", R.string.SetEmojiStatus), R.drawable.msg_status_set)); } + } + if (MessagesController.getInstance(UserConfig.selectedAccount).storiesEnabled()) { + items.add(new Item(16, LocaleController.getString("ProfileMyStories", R.string.ProfileMyStories), R.drawable.msg_menu_stories)); + items.add(null); // divider + } else if (me != null && me.isPremium()) { items.add(null); // divider } @@ -433,7 +438,6 @@ public CheckItem getItem(int position) { private static class Item { public int icon; - public int lottieIcon; public String text; public int id; @@ -443,15 +447,8 @@ public Item(int id, String text, int icon) { this.text = text; } - public Item(int id, String text, int icon, int lottieIcon) { - this.icon = icon; - this.lottieIcon = lottieIcon; - this.id = id; - this.text = text; - } - public void bind(DrawerActionCell actionCell) { - actionCell.setTextAndIcon(id, text, icon, lottieIcon); + actionCell.setTextAndIcon(id, text, icon); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/FiltersView.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/FiltersView.java index a108fc04fb..da7039ba48 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/FiltersView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/FiltersView.java @@ -69,11 +69,11 @@ public class FiltersView extends RecyclerListView { public final static int FILTER_INDEX_VOICE = 4; public final static MediaFilterData[] filters = new MediaFilterData[]{ - new MediaFilterData(R.drawable.search_media_filled, LocaleController.getString("SharedMediaTab2", R.string.SharedMediaTab2), new TLRPC.TL_inputMessagesFilterPhotoVideo(), FILTER_TYPE_MEDIA), - new MediaFilterData(R.drawable.search_links_filled, LocaleController.getString("SharedLinksTab2", R.string.SharedLinksTab2), new TLRPC.TL_inputMessagesFilterUrl(), FILTER_TYPE_LINKS), - new MediaFilterData(R.drawable.search_files_filled, LocaleController.getString("SharedFilesTab2", R.string.SharedFilesTab2), new TLRPC.TL_inputMessagesFilterDocument(), FILTER_TYPE_FILES), - new MediaFilterData(R.drawable.search_music_filled, LocaleController.getString("SharedMusicTab2", R.string.SharedMusicTab2), new TLRPC.TL_inputMessagesFilterMusic(), FILTER_TYPE_MUSIC), - new MediaFilterData(R.drawable.search_voice_filled, LocaleController.getString("SharedVoiceTab2", R.string.SharedVoiceTab2), new TLRPC.TL_inputMessagesFilterRoundVoice(), FILTER_TYPE_VOICE) + new MediaFilterData(R.drawable.search_media_filled, R.string.SharedMediaTab2, new TLRPC.TL_inputMessagesFilterPhotoVideo(), FILTER_TYPE_MEDIA), + new MediaFilterData(R.drawable.search_links_filled, R.string.SharedLinksTab2, new TLRPC.TL_inputMessagesFilterUrl(), FILTER_TYPE_LINKS), + new MediaFilterData(R.drawable.search_files_filled, R.string.SharedFilesTab2, new TLRPC.TL_inputMessagesFilterDocument(), FILTER_TYPE_FILES), + new MediaFilterData(R.drawable.search_music_filled, R.string.SharedMusicTab2, new TLRPC.TL_inputMessagesFilterMusic(), FILTER_TYPE_MUSIC), + new MediaFilterData(R.drawable.search_voice_filled, R.string.SharedVoiceTab2, new TLRPC.TL_inputMessagesFilterRoundVoice(), FILTER_TYPE_VOICE) }; private ArrayList usersFilters = new ArrayList<>(); @@ -259,7 +259,7 @@ public void setUsersAndDates(ArrayList localUsers, ArrayList d } } if (archive) { - FiltersView.MediaFilterData filterData = new FiltersView.MediaFilterData(R.drawable.chats_archive, LocaleController.getString("ArchiveSearchFilter", R.string.ArchiveSearchFilter), null, FiltersView.FILTER_TYPE_ARCHIVE); + FiltersView.MediaFilterData filterData = new FiltersView.MediaFilterData(R.drawable.chats_archive, R.string.ArchiveSearchFilter, null, FiltersView.FILTER_TYPE_ARCHIVE); usersFilters.add(filterData); } if (getAdapter() != null) { @@ -769,9 +769,8 @@ public void setData(MediaFilterData data) { titleView.setText(data.title); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + protected int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } @@ -788,7 +787,8 @@ public ViewHolder(@NonNull FilterView itemView) { public static class MediaFilterData { public final int iconResFilled; - public final String title; + public int titleResId; + private String title; public final int filterType; public final TLRPC.MessagesFilter filter; public TLObject chat; @@ -802,6 +802,20 @@ public MediaFilterData(int iconResFilled, String title, TLRPC.MessagesFilter fil this.filterType = filterType; } + public MediaFilterData(int iconResFilled, int titleResId, TLRPC.MessagesFilter filter, int filterType) { + this.iconResFilled = iconResFilled; + this.titleResId = titleResId; + this.filter = filter; + this.filterType = filterType; + } + + public String getTitle() { + if (title != null) { + return title; + } + return LocaleController.getString(titleResId); + } + public void setUser(TLObject chat) { this.chat = chat; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/LocationActivityAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/LocationActivityAdapter.java index ee9fe2d3c8..f2adcc30fb 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/LocationActivityAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/LocationActivityAdapter.java @@ -12,14 +12,11 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.location.Location; -import android.os.Build; import android.text.TextUtils; import android.view.View; import android.view.ViewGroup; -import android.view.ViewPropertyAnimator; import android.widget.FrameLayout; -import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.LocaleController; import org.telegram.messenger.LocationController; import org.telegram.messenger.MessageObject; @@ -27,7 +24,6 @@ import org.telegram.messenger.UserConfig; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.Theme; -import org.telegram.ui.Cells.EmptyCell; import org.telegram.ui.Cells.HeaderCell; import org.telegram.ui.Cells.LocationCell; import org.telegram.ui.Cells.LocationDirectionCell; @@ -324,7 +320,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType } case 9: { view = new ShadowSectionCell(mContext); - Drawable drawable = Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow); + Drawable drawable = Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow); CombinedDrawable combinedDrawable = new CombinedDrawable(new ColorDrawable(getThemedColor(Theme.key_windowBackgroundGray)), drawable); combinedDrawable.setFullsize(true); view.setBackgroundDrawable(combinedDrawable); @@ -527,8 +523,7 @@ public boolean isEnabled(RecyclerView.ViewHolder holder) { return viewType == 1 || viewType == 3 || viewType == 7; } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MentionsAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MentionsAdapter.java index 7f7fc80dbe..2ee60a1a28 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MentionsAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MentionsAdapter.java @@ -67,9 +67,14 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.Iterator; public class MentionsAdapter extends RecyclerListView.SelectionAdapter implements NotificationCenter.NotificationCenterDelegate { + private boolean allowStickers = true; + private boolean allowBots = true; + private boolean allowChats = true; + public interface MentionsAdapterDelegate { void needChangePanelVisibility(boolean show); void onItemCountUpdate(int oldCount, int newCount); @@ -114,6 +119,10 @@ public interface MentionsAdapterDelegate { private int channelLastReqId; private int channelReqId; private boolean isSearchingMentions; + private TLRPC.User user; + private TLRPC.Chat chat; + + private boolean searchInDailogs = false; private EmojiView.ChooseStickerActionTracker mentionsStickersActionTracker; @@ -693,7 +702,7 @@ public String getBotCaption() { if (foundContextBot != null) { return foundContextBot.bot_inline_placeholder; } else if (searchingContextUsername != null && searchingContextUsername.equals("gif")) { - return "Search GIFs"; + return LocaleController.getString("SearchGifsTitle", R.string.SearchGifsTitle); } return null; } @@ -710,7 +719,7 @@ private void searchForContextBotResults(final boolean cache, final TLRPC.User us ConnectionsManager.getInstance(currentAccount).cancelRequest(contextQueryReqid, true); contextQueryReqid = 0; } - if (!inlineMediaEnabled) { + if (!inlineMediaEnabled || !allowBots) { if (delegate != null) { delegate.onContextSearch(false); } @@ -744,9 +753,7 @@ private void searchForContextBotResults(final boolean cache, final TLRPC.User us if (searchResultBotContextSwitch == null) { searchResultBotContextSwitch = res.switch_pm; } - if (searchResultBotWebViewSwitch == null) { - searchResultBotWebViewSwitch = res.switch_webview; - } + searchResultBotWebViewSwitch = res.switch_webview; for (int a = 0; a < res.results.size(); a++) { TLRPC.BotInlineResult result = res.results.get(a); if (!(result.document instanceof TLRPC.TL_document) && !(result.photo instanceof TLRPC.TL_photo) && !"game".equals(result.type) && result.content == null && result.send_message instanceof TLRPC.TL_botInlineMessageMediaAuto) { @@ -813,6 +820,12 @@ private void searchForContextBotResults(final boolean cache, final TLRPC.User us public void searchUsernameOrHashtag(CharSequence charSequence, int position, ArrayList messageObjects, boolean usernameOnly, boolean forSearch) { final String text = charSequence == null ? "" : charSequence.toString(); + TLRPC.Chat currentChat = chat; + TLRPC.User currentUser = user; + if (parentFragment != null) { + currentChat = parentFragment.getCurrentChat(); + currentUser = parentFragment.getCurrentUser(); + } if (cancelDelayRunnable != null) { AndroidUtilities.cancelRunOnUIThread(cancelDelayRunnable); cancelDelayRunnable = null; @@ -868,7 +881,7 @@ public void searchUsernameOrHashtag(CharSequence charSequence, int position, Arr isValidEmoji = spans == null || spans.length == 0; } - if (isValidEmoji && parentFragment != null && (parentFragment.getCurrentChat() == null || ChatObject.canSendStickers(parentFragment.getCurrentChat()))) { + if (allowStickers && isValidEmoji && (currentChat == null || ChatObject.canSendStickers(currentChat))) { stickersToLoad.clear(); if (SharedConfig.suggestStickers == 2 || !isValidEmoji) { if (visibleByStickersSearch && SharedConfig.suggestStickers == 2) { @@ -1021,8 +1034,8 @@ public int compare(StickerResult lhs, StickerResult rhs) { char ch = text.charAt(a); if (a == 0 || text.charAt(a - 1) == ' ' || text.charAt(a - 1) == '\n' || ch == ':') { if (ch == '@') { - if (needUsernames || needBotContext && a == 0) { - if (info == null && a != 0) { + if (searchInDailogs || (needUsernames || needBotContext && a == 0)) { + if (!searchInDailogs && info == null && a != 0) { lastText = text; lastPosition = position; messages = messageObjects; @@ -1068,15 +1081,21 @@ public int compare(StickerResult lhs, StickerResult rhs) { } } if (foundType == -1) { + contextMedia = false; + searchResultBotContext = null; delegate.needChangePanelVisibility(false); return; } if (foundType == 0) { + contextMedia = false; + searchResultBotContext = null; final ArrayList users = new ArrayList<>(); - for (int a = 0; a < Math.min(100, messageObjects.size()); a++) { - long from_id = messageObjects.get(a).getFromChatId(); - if (from_id > 0 && !users.contains(from_id)) { - users.add(from_id); + if (messageObjects != null) { + for (int a = 0; a < Math.min(100, messageObjects.size()); a++) { + long from_id = messageObjects.get(a).getFromChatId(); + if (from_id > 0 && !users.contains(from_id)) { + users.add(from_id); + } } } final String usernameString = result.toString().toLowerCase(); @@ -1113,17 +1132,30 @@ public int compare(StickerResult lhs, StickerResult rhs) { chat = messagesController.getChat(info.id); threadId = 0; } else { - chat = null; + chat = currentChat; threadId = 0; } + TLRPC.User me = UserConfig.getInstance(currentAccount).getCurrentUser(); if (chat != null && info != null && info.participants != null && (!ChatObject.isChannel(chat) || chat.megagroup)) { - for (int a = (forSearch ? -1 : 0); a < info.participants.participants.size(); a++) { + for (int a = -2; a < info.participants.participants.size(); a++) { String username; String firstName; String lastName; TLObject object; long id; - if (a == -1) { + if (a == -2) { + if (me == null || !usernameOnly) { + continue; + } + firstName = me.first_name; + lastName = me.last_name; + username = UserObject.getPublicUsername(me); + object = me; + id = me.id; + } else if (a == -1) { + if (!forSearch) { + continue; + } if (usernameString.length() == 0) { newResult.add(chat); continue; @@ -1135,8 +1167,11 @@ public int compare(StickerResult lhs, StickerResult rhs) { id = -chat.id; } else { TLRPC.ChatParticipant chatParticipant = info.participants.participants.get(a); + if (me != null && chatParticipant.user_id == me.id) { + continue; + } TLRPC.User user = messagesController.getUser(chatParticipant.user_id); - if (user == null || !usernameOnly && UserObject.isUserSelf(user) || newResultsHashMap.indexOfKey(user.id) >= 0) { + if (user == null || UserObject.isUserSelf(user) || newResultsHashMap.indexOfKey(user.id) >= 0) { continue; } if (usernameString.length() == 0) { @@ -1160,6 +1195,59 @@ public int compare(StickerResult lhs, StickerResult rhs) { } } } + if (searchInDailogs) { + ArrayList dialogs = MessagesController.getInstance(currentAccount).getAllDialogs(); + // ArrayList contacts = ContactsController.getInstance(currentAccount).contacts; + for (int a = 0; a < dialogs.size(); a++) { + String username; + String firstName; + String lastName; + TLObject object; + long id = dialogs.get(a).id; + if (id > 0) { + TLRPC.User user = messagesController.getUser(dialogs.get(a).id); + if (user == null || UserObject.isUserSelf(user) || newResultsHashMap.indexOfKey(user.id) >= 0) { + continue; + } + if (usernameString.length() == 0) { + if (!user.deleted) { + newResult.add(user); + continue; + } + } + firstName = user.first_name; + lastName = user.last_name; + username = UserObject.getPublicUsername(user); + object = user; + id = user.id; + if (!TextUtils.isEmpty(username) && username.toLowerCase().startsWith(usernameString) || + !TextUtils.isEmpty(firstName) && firstName.toLowerCase().startsWith(usernameString) || + !TextUtils.isEmpty(lastName) && lastName.toLowerCase().startsWith(usernameString) || + hasSpace && ContactsController.formatName(firstName, lastName).toLowerCase().startsWith(usernameString)) { + newResult.add(object); + newMap.put(id, object); + } + } else if (!TextUtils.isEmpty(usernameString)) { + TLRPC.Chat chat1 = messagesController.getChat(-dialogs.get(a).id); + if (chat1 == null || chat1.username == null || newResultsHashMap.indexOfKey(chat1.id) >= 0) { + continue; + } + if (usernameString.length() == 0) { + newResult.add(chat1); + continue; + } + firstName = chat1.title; + username = chat1.username; + object = chat1; + id = chat1.id; + if (!TextUtils.isEmpty(username) && username.toLowerCase().startsWith(usernameString) || + !TextUtils.isEmpty(firstName) && firstName.toLowerCase().startsWith(usernameString)){ + newResult.add(object); + newMap.put(id, object); + } + } + } + } Collections.sort(newResult, new Comparator() { private long getId(TLObject object) { @@ -1199,7 +1287,7 @@ public int compare(TLObject lhs, TLObject rhs) { searchResultCommandsHelp = null; searchResultCommandsUsers = null; searchResultSuggestions = null; - if (chat != null && chat.megagroup && usernameString.length() > 0) { + if ((chat != null && chat.megagroup || searchInDailogs) && usernameString.length() > 0) { if (newResult.size() < 5) { AndroidUtilities.runOnUIThread(cancelDelayRunnable = () -> { cancelDelayRunnable = null; @@ -1288,6 +1376,8 @@ public void run() { searchResultCommandsHelp = null; searchResultCommandsUsers = null; searchResultSuggestions = null; + contextMedia = false; + searchResultBotContext = null; notifyDataSetChanged(); delegate.needChangePanelVisibility(!searchResultHashtags.isEmpty()); } else if (foundType == 2) { @@ -1314,6 +1404,8 @@ public void run() { searchResultCommands = newResult; searchResultCommandsHelp = newResultHelp; searchResultCommandsUsers = newResultUsers; + contextMedia = false; + searchResultBotContext = null; notifyDataSetChanged(); delegate.needChangePanelVisibility(!newResult.isEmpty()); } else if (foundType == 3) { @@ -1361,6 +1453,17 @@ public void setIsReversed(boolean isReversed) { private void showUsersResult(ArrayList newResult, LongSparseArray newMap, boolean notify) { searchResultUsernames = newResult; + if ((!allowBots || !allowChats) && searchResultUsernames != null) { + Iterator i = searchResultUsernames.iterator(); + while (i.hasNext()) { + TLObject obj = i.next(); + if (obj instanceof TLRPC.Chat && !allowChats) { + i.remove(); + } else if (obj instanceof TLRPC.User && (((TLRPC.User) obj).bot || UserObject.isService(((TLRPC.User) obj).id))) { + i.remove(); + } + } + } searchResultUsernamesMap = newMap; if (cancelDelayRunnable != null) { AndroidUtilities.cancelRunOnUIThread(cancelDelayRunnable); @@ -1581,7 +1684,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType break; case 4: default: - view = new StickerCell(mContext); + view = new StickerCell(mContext, resourcesProvider); break; } return new RecyclerListView.Holder(view); @@ -1620,21 +1723,22 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { ((ContextLinkCell) holder.itemView).setLink(searchResultBotContext.get(position), foundContextBot, contextMedia, position != searchResultBotContext.size() - 1, hasTop && position == 0, "gif".equals(searchingContextUsername)); } } else { + MentionCell cell = (MentionCell) holder.itemView; if (searchResultUsernames != null) { TLObject object = searchResultUsernames.get(position); if (object instanceof TLRPC.User) { - ((MentionCell) holder.itemView).setUser((TLRPC.User) object); + cell.setUser((TLRPC.User) object); } else if (object instanceof TLRPC.Chat) { - ((MentionCell) holder.itemView).setChat((TLRPC.Chat) object); + cell.setChat((TLRPC.Chat) object); } } else if (searchResultHashtags != null) { - ((MentionCell) holder.itemView).setText(searchResultHashtags.get(position)); + cell.setText(searchResultHashtags.get(position)); } else if (searchResultSuggestions != null) { - ((MentionCell) holder.itemView).setEmojiSuggestion(searchResultSuggestions.get(position)); + cell.setEmojiSuggestion(searchResultSuggestions.get(position)); } else if (searchResultCommands != null) { - ((MentionCell) holder.itemView).setBotCommand(searchResultCommands.get(position), searchResultCommandsHelp.get(position), searchResultCommandsUsers != null ? searchResultCommandsUsers.get(position) : null); + cell.setBotCommand(searchResultCommands.get(position), searchResultCommandsHelp.get(position), searchResultCommandsUsers != null ? searchResultCommandsUsers.get(position) : null); } - ((MentionCell) holder.itemView).setDivider(USE_DIVIDERS && (isReversed ? position > 0 : position < getItemCount() - 1)); + cell.setDivider(USE_DIVIDERS && (isReversed ? position > 0 : position < getItemCount() - 1)); } } @@ -1665,8 +1769,32 @@ public boolean isShown() { } } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); + } + + public void setDialogId(long dialogId) { + dialog_id = dialogId; + } + + public void setUserOrChar(TLRPC.User user, TLRPC.Chat chat) { + this.user = user; + this.chat = chat; + } + + public void setSearchInDailogs(boolean searchInDailogs) { + this.searchInDailogs = searchInDailogs; + } + + public void setAllowStickers(boolean allowStickers) { + this.allowStickers = allowStickers; + } + + public void setAllowBots(boolean allowBots) { + this.allowBots = allowBots; + } + + public void setAllowChats(boolean allowChats) { + this.allowChats = allowChats; } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/SearchAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/SearchAdapter.java index f87e0bd065..30c592cb9a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/SearchAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/SearchAdapter.java @@ -447,7 +447,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { case 2: { String str = (String) getItem(position); TextCell cell = (TextCell) holder.itemView; - cell.setColors(null, Theme.key_windowBackgroundWhiteBlueText2); + cell.setColors(-1, Theme.key_windowBackgroundWhiteBlueText2); cell.setText(LocaleController.formatString("AddContactByPhone", R.string.AddContactByPhone, PhoneFormat.getInstance().format("+" + str)), false); break; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/StickersSearchAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/StickersSearchAdapter.java index 5cc0a6235b..b5ad8075ca 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/StickersSearchAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/StickersSearchAdapter.java @@ -338,7 +338,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType View view = null; switch (viewType) { case 0: - StickerEmojiCell stickerEmojiCell = new StickerEmojiCell(context, false) { + StickerEmojiCell stickerEmojiCell = new StickerEmojiCell(context, false, resourcesProvider) { public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(82), MeasureSpec.EXACTLY)); } @@ -675,8 +675,7 @@ public void getThemeDescriptions(List descriptions, RecyclerLi descriptions.add(new ThemeDescription(emptyTextView, ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_chat_emojiPanelEmptyText)); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } \ No newline at end of file diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ArchiveSettingsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ArchiveSettingsActivity.java new file mode 100644 index 0000000000..0bbf390009 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/ArchiveSettingsActivity.java @@ -0,0 +1,309 @@ +package org.telegram.ui; + +import android.content.Context; +import android.text.TextUtils; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.DefaultItemAnimator; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.BotWebViewVibrationEffect; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.NotificationsController; +import org.telegram.messenger.R; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.ActionBar; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Cells.HeaderCell; +import org.telegram.ui.Cells.TextCheckCell; +import org.telegram.ui.Cells.TextInfoPrivacyCell; +import org.telegram.ui.Cells.TextSettingsCell; +import org.telegram.ui.Components.Bulletin; +import org.telegram.ui.Components.BulletinFactory; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.ListView.AdapterWithDiffUtils; +import org.telegram.ui.Components.Premium.PremiumFeatureBottomSheet; +import org.telegram.ui.Components.Premium.PremiumPreviewBottomSheet; +import org.telegram.ui.Components.RecyclerListView; + +import java.util.ArrayList; +import java.util.Objects; + +public class ArchiveSettingsActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { + + private RecyclerListView listView; + private ListAdapter adapter; + + private boolean changed = false; + private TLRPC.TL_globalPrivacySettings settings; + + private int shiftDp = -3; + + @Override + public View createView(Context context) { + actionBar.setBackButtonImage(R.drawable.ic_ab_back); + actionBar.setAllowOverlayTitle(true); + actionBar.setTitle(LocaleController.getString("ArchiveSettings")); + actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { + @Override + public void onItemClick(int id) { + if (id == -1) { + finishFragment(); + } + } + }); + + fragmentView = new FrameLayout(context); + FrameLayout frameLayout = (FrameLayout) fragmentView; + frameLayout.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundGray)); + + listView = new RecyclerListView(context); + listView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) { + @Override + public boolean supportsPredictiveItemAnimations() { + return false; + } + }); + listView.setVerticalScrollBarEnabled(false); + listView.setLayoutAnimation(null); + listView.setAdapter(adapter = new ListAdapter()); + DefaultItemAnimator itemAnimator = new DefaultItemAnimator(); + itemAnimator.setDurations(350); + itemAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + itemAnimator.setDelayAnimations(false); + itemAnimator.setSupportsChangeAnimations(false); + listView.setItemAnimator(itemAnimator); + frameLayout.addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + listView.setOnItemClickListener((view, position) -> { + if (position < 0 || position >= items.size()) { + return; + } + ItemInner item = items.get(position); + if (item.id == 1) { + settings.keep_archived_unmuted = !settings.keep_archived_unmuted; + ((TextCheckCell) view).setChecked(settings.keep_archived_unmuted); + changed = true; + } else if (item.id == 4) { + settings.keep_archived_folders = !settings.keep_archived_folders; + ((TextCheckCell) view).setChecked(settings.keep_archived_folders); + changed = true; + } else if (item.id == 7) { + if (!getUserConfig().isPremium() && !getMessagesController().autoarchiveAvailable && !settings.archive_and_mute_new_noncontact_peers) { + final Bulletin.SimpleLayout layout = new Bulletin.SimpleLayout(getContext(), getResourceProvider()); + layout.textView.setText(AndroidUtilities.replaceSingleTag(LocaleController.getString(R.string.UnlockPremium), Theme.key_undo_cancelColor, 0, () -> { + presentFragment(new PremiumPreviewFragment("settings")); + })); + layout.textView.setSingleLine(false); + layout.textView.setPadding(0, AndroidUtilities.dp(4), 0, AndroidUtilities.dp(4)); + layout.imageView.setImageResource(R.drawable.msg_settings_premium); + Bulletin.make(this, layout, 3500).show(); + + AndroidUtilities.shakeViewSpring(view, shiftDp = -shiftDp); + BotWebViewVibrationEffect.APP_ERROR.vibrate(); + return; + } + settings.archive_and_mute_new_noncontact_peers = !settings.archive_and_mute_new_noncontact_peers; + ((TextCheckCell) view).setChecked(settings.archive_and_mute_new_noncontact_peers); + changed = true; + } + }); + + getContactsController().loadGlobalPrivacySetting(); + settings = getContactsController().getGlobalPrivacySettings(); + if (settings == null) { + settings = new TLRPC.TL_globalPrivacySettings(); + } + updateItems(false); + + return fragmentView; + } + + private final ArrayList oldItems = new ArrayList<>(), items = new ArrayList<>(); + + private void updateItems(boolean animated) { + oldItems.clear(); + oldItems.addAll(items); + items.clear(); + + items.add(new ItemInner(VIEW_TYPE_HEADER, 0, LocaleController.getString("ArchiveSettingUnmutedFolders"))); + items.add(new ItemInner(VIEW_TYPE_CHECK, 1, LocaleController.getString("ArchiveSettingUnmutedFoldersCheck"))); + items.add(new ItemInner(VIEW_TYPE_SHADOW, 2, LocaleController.getString("ArchiveSettingUnmutedFoldersInfo"))); + + final boolean hasFolders = getMessagesController().getDialogFilters().size() > 1; + if (hasFolders) { + items.add(new ItemInner(VIEW_TYPE_HEADER, 3, LocaleController.getString("ArchiveSettingUnmutedChats"))); + items.add(new ItemInner(VIEW_TYPE_CHECK, 4, LocaleController.getString("ArchiveSettingUnmutedChatsCheck"))); + items.add(new ItemInner(VIEW_TYPE_SHADOW, 5, LocaleController.getString("ArchiveSettingUnmutedChatsInfo"))); + } + + items.add(new ItemInner(VIEW_TYPE_HEADER, 6, LocaleController.getString("NewChatsFromNonContacts"))); + items.add(new ItemInner(VIEW_TYPE_CHECK, 7, LocaleController.getString("NewChatsFromNonContactsCheck"))); + items.add(new ItemInner(VIEW_TYPE_SHADOW, 8, LocaleController.getString("ArchiveAndMuteInfo"))); + + if (adapter == null) { + return; + } + + if (animated) { + adapter.setItems(oldItems, items); + } else { + adapter.notifyDataSetChanged(); + } + } + + private final static int VIEW_TYPE_HEADER = 0; + private final static int VIEW_TYPE_CHECK = 1; + private final static int VIEW_TYPE_SHADOW = 2; + + private static class ItemInner extends AdapterWithDiffUtils.Item { + public CharSequence text; + public int id; + public ItemInner(int viewType, int id, CharSequence text) { + super(viewType, false); + this.id = id; + this.text = text; + } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ItemInner item = (ItemInner) o; + return id == item.id && Objects.equals(text, item.text); + } + } + + private class ListAdapter extends AdapterWithDiffUtils { + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view; + if (viewType == VIEW_TYPE_HEADER) { + view = new HeaderCell(getContext()); + view.setBackgroundColor(getThemedColor(Theme.key_windowBackgroundWhite)); + } else if (viewType == VIEW_TYPE_CHECK) { + view = new TextCheckCell(getContext()); + view.setBackgroundColor(getThemedColor(Theme.key_windowBackgroundWhite)); + } else { + view = new TextInfoPrivacyCell(getContext()); + } + return new RecyclerListView.Holder(view); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + if (position < 0 || position >= items.size()) { + return; + } + ItemInner item = items.get(position); + final boolean divider = position + 1 < items.size() && items.get(position + 1).viewType == item.viewType; + if (holder.getItemViewType() == VIEW_TYPE_HEADER) { + ((HeaderCell) holder.itemView).setText(item.text); + } else if (holder.getItemViewType() == VIEW_TYPE_SHADOW) { + TextInfoPrivacyCell cell = (TextInfoPrivacyCell) holder.itemView; + if (TextUtils.isEmpty(item.text)) { + cell.setFixedSize(12); + cell.setText(null); + } else { + cell.setFixedSize(0); + cell.setText(item.text); + } + if (divider) { + cell.setBackground(Theme.getThemedDrawableByKey(getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + } else { + cell.setBackground(Theme.getThemedDrawableByKey(getContext(), R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + } + } else if (holder.getItemViewType() == VIEW_TYPE_CHECK) { + TextCheckCell cell = (TextCheckCell) holder.itemView; + boolean checked; + if (item.id == 1) { + checked = settings.keep_archived_unmuted; + cell.setCheckBoxIcon(0); + } else if (item.id == 4) { + checked = settings.keep_archived_folders; + cell.setCheckBoxIcon(0); + } else if (item.id == 7) { + checked = settings.archive_and_mute_new_noncontact_peers; + cell.setCheckBoxIcon(getUserConfig().isPremium() || getMessagesController().autoarchiveAvailable ? 0 : R.drawable.permission_locked); + } else { + return; + } + cell.setTextAndCheck(item.text, checked, divider); + } + } + + @Override + public int getItemCount() { + return items.size(); + } + + @Override + public boolean isEnabled(RecyclerView.ViewHolder holder) { + return holder.getItemViewType() != VIEW_TYPE_SHADOW; + } + + @Override + public int getItemViewType(int position) { + if (position < 0 || position >= items.size()) { + return 0; + } + return items.get(position).viewType; + } + } + + @Override + public boolean onFragmentCreate() { + getNotificationCenter().addObserver(this, NotificationCenter.privacyRulesUpdated); + return super.onFragmentCreate(); + } + + @Override + public void onFragmentDestroy() { + getNotificationCenter().removeObserver(this, NotificationCenter.privacyRulesUpdated); + super.onFragmentDestroy(); + + if (changed) { + TLRPC.TL_account_setGlobalPrivacySettings req = new TLRPC.TL_account_setGlobalPrivacySettings(); + req.settings = settings; + getConnectionsManager().sendRequest(req, (response, error) -> {}); + changed = false; + } + } + + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.privacyRulesUpdated) { + settings = getContactsController().getGlobalPrivacySettings(); + if (settings == null) { + settings = new TLRPC.TL_globalPrivacySettings(); + } + if (listView != null) { + for (int i = 0; i < listView.getChildCount(); ++i) { + View child = listView.getChildAt(i); + int position = listView.getChildAdapterPosition(child); + if (position < 0 || position >= items.size()) { + continue; + } + ItemInner item = items.get(position); + if (item.id == 1) { + ((TextCheckCell) child).setChecked(settings.keep_archived_unmuted); + } else if (item.id == 4) { + ((TextCheckCell) child).setChecked(settings.keep_archived_folders); + } else if (item.id == 7) { + ((TextCheckCell) child).setChecked(settings.archive_and_mute_new_noncontact_peers); + } + } + } + changed = false; + } else if (id == NotificationCenter.dialogFiltersUpdated) { + updateItems(true); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ArchivedStickersActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ArchivedStickersActivity.java index 5367e05f19..7bfa84f82d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ArchivedStickersActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ArchivedStickersActivity.java @@ -93,6 +93,8 @@ public View createView(Context context) { actionBar.setAllowOverlayTitle(true); if (currentType == MediaDataController.TYPE_IMAGE) { actionBar.setTitle(LocaleController.getString("ArchivedStickers", R.string.ArchivedStickers)); + } else if (currentType == MediaDataController.TYPE_EMOJIPACKS) { + actionBar.setTitle(LocaleController.getString("ArchivedEmojiPacks", R.string.ArchivedEmojiPacks)); } else { actionBar.setTitle(LocaleController.getString("ArchivedMasks", R.string.ArchivedMasks)); } @@ -173,7 +175,7 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { private void updateRows() { rowCount = 0; if (!sets.isEmpty()) { - archiveInfoRow = currentType == MediaDataController.TYPE_IMAGE ? rowCount++ : -1; + archiveInfoRow = currentType == MediaDataController.TYPE_IMAGE || currentType == MediaDataController.TYPE_EMOJIPACKS ? rowCount++ : -1; stickersStartRow = rowCount; stickersEndRow = rowCount + sets.size(); rowCount += sets.size(); @@ -342,7 +344,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (position == archiveInfoRow) { cell.setTopPadding(17); cell.setBottomPadding(10); - cell.setText(LocaleController.getString("ArchivedStickersInfo", R.string.ArchivedStickersInfo)); + cell.setText(currentType == MediaDataController.TYPE_EMOJIPACKS ? LocaleController.getString("ArchivedEmojiInfo", R.string.ArchivedEmojiInfo) : LocaleController.getString("ArchivedStickersInfo", R.string.ArchivedStickersInfo)); } else { cell.setTopPadding(10); cell.setBottomPadding(17); @@ -366,11 +368,11 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType break; case 1: view = new LoadingCell(mContext); - view.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + view.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); break; case 2: view = new TextInfoPrivacyCell(mContext); - view.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + view.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); break; } view.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java index be9dff9cc5..af1446b86d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java @@ -92,6 +92,7 @@ import androidx.annotation.Keep; import androidx.annotation.NonNull; import androidx.collection.LongSparseArray; +import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.GridLayoutManagerFixed; @@ -104,6 +105,7 @@ import org.json.JSONObject; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.ChatObject; import org.telegram.messenger.DownloadController; @@ -312,12 +314,16 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg PinchToZoomHelper pinchToZoomHelper; - private int allowAnimationIndex = -1; + private final AnimationNotificationsLocker notificationsLocker = new AnimationNotificationsLocker(new int[]{ + NotificationCenter.dialogsNeedReload, + NotificationCenter.closeChats + }); private final String BOTTOM_SHEET_VIEW_TAG = "bottomSheet"; @SuppressLint("StaticFieldLeak") private static volatile ArticleViewer Instance = null; + private Drawable chat_redLocationIcon; public static ArticleViewer getInstance() { ArticleViewer localInstance = Instance; @@ -398,8 +404,9 @@ private static class TL_pageBlockEmbedPostCaption extends TLRPC.TL_pageBlockEmbe } public class DrawingText implements TextSelectionHelper.TextLayoutBlock { - public View latestParentView; + private View latestParentView; + private boolean isDrawing; public StaticLayout textLayout; public LinkPath textPath; public LinkPath markPath; @@ -413,6 +420,7 @@ public class DrawingText implements TextSelectionHelper.TextLayoutBlock { public CharSequence prefix; public void draw(Canvas canvas, View view) { + isDrawing = true; latestParentView = view; if (!searchResults.isEmpty()) { @@ -459,6 +467,13 @@ public void draw(Canvas canvas, View view) { canvas.drawRect(-AndroidUtilities.dp(2) + x, 0, x + width + AndroidUtilities.dp(2), getHeight(), urlPaint); } textLayout.draw(canvas); + isDrawing = false; + } + + public void invalidateParent() { + if (!isDrawing && latestParentView != null) { + latestParentView.invalidate(); + } } public CharSequence getText() { @@ -757,7 +772,7 @@ public boolean dispatchTouchEvent(MotionEvent ev) { MotionEvent textSelectionEv = MotionEvent.obtain(ev); textSelectionEv.offsetLocation(-containerView.getX(), -containerView.getY()); - if (textSelectionHelper.isSelectionMode() && textSelectionHelper.getOverlayView(getContext()).onTouchEvent(textSelectionEv)) { + if (textSelectionHelper.isInSelectionMode() && textSelectionHelper.getOverlayView(getContext()).onTouchEvent(textSelectionEv)) { return true; } @@ -765,7 +780,7 @@ public boolean dispatchTouchEvent(MotionEvent ev) { ev.setAction(MotionEvent.ACTION_CANCEL); } - if (ev.getAction() == MotionEvent.ACTION_DOWN && textSelectionHelper.isSelectionMode() && (ev.getY() < containerView.getTop() || ev.getY() > containerView.getBottom())) { + if (ev.getAction() == MotionEvent.ACTION_DOWN && textSelectionHelper.isInSelectionMode() && (ev.getY() < containerView.getTop() || ev.getY() > containerView.getBottom())) { if (textSelectionHelper.getOverlayView(getContext()).onTouchEvent(textSelectionEv)) { return super.dispatchTouchEvent(ev); } else { @@ -906,7 +921,7 @@ private void prepareForMoving(MotionEvent ev) { } public boolean handleTouchEvent(MotionEvent event) { - if (pageSwitchAnimation == null && !closeAnimationInProgress && fullscreenVideoContainer.getVisibility() != VISIBLE && !textSelectionHelper.isSelectionMode()) { + if (pageSwitchAnimation == null && !closeAnimationInProgress && fullscreenVideoContainer.getVisibility() != VISIBLE && !textSelectionHelper.isInSelectionMode()) { if (event != null && event.getAction() == MotionEvent.ACTION_DOWN && !startedTracking && !maybeStartTracking) { startedTrackingPointerId = event.getPointerId(0); maybeStartTracking = true; @@ -1035,7 +1050,7 @@ public void onAnimationEnd(Animator animator) { tracker.recycle(); tracker = null; } - if (textSelectionHelper != null && !textSelectionHelper.isSelectionMode()) { + if (textSelectionHelper != null && !textSelectionHelper.isInSelectionMode()) { textSelectionHelper.clear(); } } @@ -1136,7 +1151,7 @@ public void run() { } else { textSelectionHelper.trySelect(pressedLinkOwnerView); } - if (textSelectionHelper.isSelectionMode() && !NekoConfig.disableVibration.Bool()) { + if (textSelectionHelper.isInSelectionMode() && !NekoConfig.disableVibration.Bool()) { windowView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); } } else if (pressedLinkOwnerLayout != null && pressedLinkOwnerView != null) { @@ -1742,7 +1757,7 @@ public boolean dispatchTouchEvent(MotionEvent ev) { TextSelectionHelper.TextSelectionOverlay selectionOverlay = textSelectionHelperBottomSheet.getOverlayView(getContext()); MotionEvent textSelectionEv = MotionEvent.obtain(ev); textSelectionEv.offsetLocation(-linearLayout.getX(), -linearLayout.getY()); - if (textSelectionHelperBottomSheet.isSelectionMode() && textSelectionHelperBottomSheet.getOverlayView(getContext()).onTouchEvent(textSelectionEv)) { + if (textSelectionHelperBottomSheet.isInSelectionMode() && textSelectionHelperBottomSheet.getOverlayView(getContext()).onTouchEvent(textSelectionEv)) { return true; } @@ -1750,7 +1765,7 @@ public boolean dispatchTouchEvent(MotionEvent ev) { ev.setAction(MotionEvent.ACTION_CANCEL); } - if (ev.getAction() == MotionEvent.ACTION_DOWN && textSelectionHelperBottomSheet.isSelectionMode() && (ev.getY() < linearLayout.getTop() || ev.getY() > linearLayout.getBottom())) { + if (ev.getAction() == MotionEvent.ACTION_DOWN && textSelectionHelperBottomSheet.isInSelectionMode() && (ev.getY() < linearLayout.getTop() || ev.getY() > linearLayout.getBottom())) { if (textSelectionHelperBottomSheet.getOverlayView(getContext()).onTouchEvent(textSelectionEv)) { return super.dispatchTouchEvent(ev); } else { @@ -1771,7 +1786,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { builder.setDelegate(new BottomSheet.BottomSheetDelegate() { @Override public boolean canDismiss() { - if (textSelectionHelperBottomSheet != null && textSelectionHelperBottomSheet.isSelectionMode()) { + if (textSelectionHelperBottomSheet != null && textSelectionHelperBottomSheet.isInSelectionMode()){ textSelectionHelperBottomSheet.clear(); return false; } @@ -1782,7 +1797,7 @@ public boolean canDismiss() { frameLayout.addView(linearLayout, LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT); frameLayout.addView(overlayView, LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT); builder.setCustomView(frameLayout); - if (textSelectionHelper.isSelectionMode()) { + if (textSelectionHelper.isInSelectionMode()) { textSelectionHelper.clear(); } showDialog(linkSheet = builder.create()); @@ -3214,7 +3229,7 @@ public void setTranslationX(float translationX) { }); listView[i].setOnItemClickListener((view, position, x, y) -> { if (textSelectionHelper != null) { - if (textSelectionHelper.isSelectionMode()) { + if (textSelectionHelper.isInSelectionMode()) { textSelectionHelper.clear(); return; } @@ -4330,7 +4345,7 @@ private boolean open(final MessageObject messageObject, TLRPC.WebPage webpage, S @Override public void onAnimationEnd(Animator animation) { AndroidUtilities.runOnUIThread(() -> { - NotificationCenter.getInstance(currentAccount).onAnimationFinish(allowAnimationIndex); + notificationsLocker.unlock(); if (animationEndRunnable != null) { animationEndRunnable.run(); animationEndRunnable = null; @@ -4340,7 +4355,7 @@ public void onAnimationEnd(Animator animation) { }); transitionAnimationStartTime = System.currentTimeMillis(); AndroidUtilities.runOnUIThread(() -> { - allowAnimationIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(allowAnimationIndex, new int[]{NotificationCenter.dialogsNeedReload, NotificationCenter.closeChats}); + notificationsLocker.lock(); animatorSet.start(); }); if (Build.VERSION.SDK_INT >= 18) { @@ -4618,7 +4633,7 @@ public void close(boolean byBackPress, boolean force) { return; } } - if (textSelectionHelper.isSelectionMode()) { + if (textSelectionHelper.isInSelectionMode()) { textSelectionHelper.clear(); return; } @@ -6615,7 +6630,7 @@ protected void onDraw(Canvas canvas) { if (currentBlock == null) { return; } - radialProgress.setColors(Theme.key_chat_inLoader, Theme.key_chat_inLoaderSelected, Theme.key_chat_inMediaIcon, Theme.key_chat_inMediaIconSelected); + radialProgress.setColorKeys(Theme.key_chat_inLoader, Theme.key_chat_inLoaderSelected, Theme.key_chat_inMediaIcon, Theme.key_chat_inMediaIconSelected); radialProgress.setProgressColor(Theme.getColor(Theme.key_chat_inFileProgress)); radialProgress.draw(canvas); canvas.save(); @@ -6689,7 +6704,7 @@ public void updatePlayingMessageProgress() { for (int a = 0; a < currentDocument.attributes.size(); a++) { TLRPC.DocumentAttribute attribute = currentDocument.attributes.get(a); if (attribute instanceof TLRPC.TL_documentAttributeAudio) { - duration = attribute.duration; + duration = (int) attribute.duration; break; } } @@ -7583,7 +7598,7 @@ protected void onScrollChanged(int l, int t, int oldl, int oldt) { pressedLinkOwnerView = null; } updateChildTextPositions(); - if (textSelectionHelper != null && textSelectionHelper.isSelectionMode()) { + if (textSelectionHelper != null && textSelectionHelper.isInSelectionMode()) { textSelectionHelper.invalidate(); } } @@ -10442,13 +10457,16 @@ protected void onDraw(Canvas canvas) { imageView.draw(canvas); if (currentMapProvider == 2 && imageView.hasNotThumb()) { - int w = (int) (Theme.chat_redLocationIcon.getIntrinsicWidth() * 0.8f); - int h = (int) (Theme.chat_redLocationIcon.getIntrinsicHeight() * 0.8f); + if (chat_redLocationIcon == null) { + chat_redLocationIcon = ContextCompat.getDrawable(getContext(), R.drawable.map_pin).mutate(); + } + int w = (int) (chat_redLocationIcon.getIntrinsicWidth() * 0.8f); + int h = (int) (chat_redLocationIcon.getIntrinsicHeight() * 0.8f); int x = (int) (imageView.getImageX() + (imageView.getImageWidth() - w) / 2); int y = (int) (imageView.getImageY() + (imageView.getImageHeight() / 2 - h)); - Theme.chat_redLocationIcon.setAlpha((int) (255 * imageView.getCurrentAlpha())); - Theme.chat_redLocationIcon.setBounds(x, y, x + w, y + h); - Theme.chat_redLocationIcon.draw(canvas); + chat_redLocationIcon.setAlpha((int) (255 * imageView.getCurrentAlpha())); + chat_redLocationIcon.setBounds(x, y, x + w, y + h); + chat_redLocationIcon.draw(canvas); } int count = 0; if (captionLayout != null) { @@ -10751,7 +10769,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (textLayout != null) { height += AndroidUtilities.dp(8 + 8) + textLayout.getHeight(); if (parentAdapter.isRtl) { - textX = (int) Math.floor(width - textLayout.getLineWidth(0) - AndroidUtilities.dp(16)); + textX = (int) Math.floor(width - textLayout.getLineLeft(0) - textLayout.getLineWidth(0) - AndroidUtilities.dp(16)); } else { textX = AndroidUtilities.dp(18); } @@ -11112,7 +11130,7 @@ protected void onDraw(Canvas canvas) { if (Build.VERSION.SDK_INT >= 23) { scrollView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> { - if (textSelectionHelper != null && textSelectionHelper.isSelectionMode()) { + if (textSelectionHelper != null && textSelectionHelper.isInSelectionMode()) { textSelectionHelper.invalidate(); } }); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/CacheChatsExceptionsFragment.java b/TMessagesProj/src/main/java/org/telegram/ui/CacheChatsExceptionsFragment.java index ad6d3a6e1e..f5d2b745a7 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/CacheChatsExceptionsFragment.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/CacheChatsExceptionsFragment.java @@ -255,7 +255,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int case VIEW_TYPE_DELETE_ALL: textCell = new TextCell(parent.getContext()); textCell.setText(LocaleController.getString("NotificationsDeleteAllException", R.string.NotificationsDeleteAllException), false); - textCell.setColors(null, Theme.key_windowBackgroundWhiteRedText5); + textCell.setColors(-1, Theme.key_text_RedRegular); view = textCell; view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); break; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/CacheControlActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/CacheControlActivity.java index dbf2ad64fb..feeec3f61f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/CacheControlActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/CacheControlActivity.java @@ -23,7 +23,6 @@ import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.RectF; -import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; @@ -68,11 +67,9 @@ import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import com.jakewharton.processphoenix.ProcessPhoenix; - -import org.telegram.messenger.AccountInstance; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.BotWebViewVibrationEffect; +import org.telegram.messenger.BuildVars; import org.telegram.messenger.CacheByChatsController; import org.telegram.messenger.Emoji; import org.telegram.messenger.ApplicationLoader; @@ -89,6 +86,7 @@ import org.telegram.messenger.ImageLoader; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MediaDataController; +import org.telegram.messenger.MessageObject; import org.telegram.messenger.MessagesController; import org.telegram.messenger.MessagesStorage; import org.telegram.messenger.NotificationCenter; @@ -145,9 +143,6 @@ import org.telegram.ui.Storage.CacheModel; import java.io.File; -import java.nio.file.Files; -import java.nio.file.attribute.BasicFileAttributes; -import java.nio.file.attribute.FileTime; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -163,6 +158,7 @@ public class CacheControlActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { + private static final int VIEW_TYPE_TEXT_SETTINGS = 0; private static final int VIEW_TYPE_INFO = 1; private static final int VIEW_TYPE_STORAGE = 2; private static final int VIEW_TYPE_HEADER = 3; @@ -170,12 +166,10 @@ public class CacheControlActivity extends BaseFragment implements NotificationCe private static final int VIEW_TYPE_CHAT = 5; private static final int VIEW_FLICKER_LOADING_DIALOG = 6; private static final int VIEW_TYPE_KEEP_MEDIA_CELL = 7; - private static final int VIEW_TYPE_TEXT_SETTINGS = 0; private static final int VIEW_TYPE_CACHE_VIEW_PAGER = 8; - private static final int VIEW_TYPE_CHART = 9; private static final int VIEW_TYPE_CHART_HEADER = 10; - public static final int VIEW_TYPE_SECTION = 11; + private static final int VIEW_TYPE_SECTION = 11; private static final int VIEW_TYPE_SECTION_LOADING = 12; private static final int VIEW_TYPE_CLEAR_CACHE_BUTTON = 13; private static final int VIEW_TYPE_MAX_CACHE_SIZE = 14; @@ -183,6 +177,7 @@ public class CacheControlActivity extends BaseFragment implements NotificationCe public static final int KEEP_MEDIA_TYPE_USER = 0; public static final int KEEP_MEDIA_TYPE_GROUP = 1; public static final int KEEP_MEDIA_TYPE_CHANNEL = 2; + public static final int KEEP_MEDIA_TYPE_STORIES = 3; public static final long UNKNOWN_CHATS_DIALOG_ID = Long.MAX_VALUE; @@ -193,11 +188,12 @@ public class CacheControlActivity extends BaseFragment implements NotificationCe private LinearLayoutManager layoutManager; AlertDialog progressDialog; - private boolean[] selected = new boolean[] { true, true, true, true, true, true, true, true, true }; + private boolean[] selected = new boolean[] { true, true, true, true, true, true, true, true, true, true }; private long databaseSize = -1; private long cacheSize = -1, cacheEmojiSize = -1, cacheTempSize = -1, cacheCustomEmojiSize = -1; private long documentsSize = -1; private long audioSize = -1; + private long storiesSize = -1; private long musicSize = -1; private long photoSize = -1; private long videoSize = -1; @@ -239,14 +235,17 @@ public class CacheControlActivity extends BaseFragment implements NotificationCe public final static int TYPE_VOICE = 4; public final static int TYPE_ANIMATED_STICKERS_CACHE = 5; public final static int TYPE_OTHER = 6; + public final static int TYPE_STORIES = 7; private static final int delete_id = 1; private static final int other_id = 2; private static final int clear_database_id = 3; + private static final int reset_database_id = 4; private boolean loadingDialogs; private NestedSizeNotifierLayout nestedSizeNotifierLayout; private ActionBarMenuSubItem clearDatabaseItem; + private ActionBarMenuSubItem resetDatabaseItem; private void updateDatabaseItemSize() { if (clearDatabaseItem != null) { SpannableStringBuilder string = new SpannableStringBuilder(); @@ -273,7 +272,7 @@ public static void calculateTotalSize(Utilities.Callback onDone) { return; } } - Utilities.globalQueue.postRunnable(() -> { + Utilities.cacheClearQueue.postRunnable(() -> { canceled = false; long cacheSize = getDirectorySize(FileLoader.checkDirectory(FileLoader.MEDIA_DIR_CACHE), 5); long cacheTempSize = getDirectorySize(FileLoader.checkDirectory(FileLoader.MEDIA_DIR_CACHE), 4); @@ -288,7 +287,8 @@ public static void calculateTotalSize(Utilities.Callback onDone) { long stickersCacheSize = getDirectorySize(new File(FileLoader.checkDirectory(FileLoader.MEDIA_DIR_CACHE), "acache"), 0); stickersCacheSize += getDirectorySize(FileLoader.checkDirectory(FileLoader.MEDIA_DIR_CACHE), 3); long audioSize = getDirectorySize(FileLoader.checkDirectory(FileLoader.MEDIA_DIR_AUDIO), 0); - final long totalSize = lastTotalSizeCalculated = cacheSize + cacheTempSize + videoSize + audioSize + photoSize + documentsSize + musicSize + stickersCacheSize; + long storiesSize = getDirectorySize(FileLoader.checkDirectory(FileLoader.MEDIA_DIR_STORIES), 0); + final long totalSize = lastTotalSizeCalculated = cacheSize + cacheTempSize + videoSize + audioSize + photoSize + documentsSize + musicSize + stickersCacheSize + storiesSize; lastTotalSizeCalculatedTime = System.currentTimeMillis(); if (!canceled) { AndroidUtilities.runOnUIThread(() -> { @@ -309,53 +309,56 @@ public static void getDeviceTotalSize(Utilities.Callback2 onDone) { } return; } - File path; - if (Build.VERSION.SDK_INT >= 19) { - ArrayList storageDirs = AndroidUtilities.getRootDirs(); - String dir = (path = storageDirs.get(0)).getAbsolutePath(); - if (!TextUtils.isEmpty(SharedConfig.storageCacheDir)) { - for (int a = 0, N = storageDirs.size(); a < N; a++) { - File file = storageDirs.get(a); - if (file.getAbsolutePath().startsWith(SharedConfig.storageCacheDir) && file.canWrite()) { - path = file; - break; + Utilities.cacheClearQueue.postRunnable(() -> { + File path; + if (Build.VERSION.SDK_INT >= 19) { + ArrayList storageDirs = AndroidUtilities.getRootDirs(); + String dir = (path = storageDirs.get(0)).getAbsolutePath(); + if (!TextUtils.isEmpty(SharedConfig.storageCacheDir)) { + for (int a = 0, N = storageDirs.size(); a < N; a++) { + File file = storageDirs.get(a); + if (file.getAbsolutePath().startsWith(SharedConfig.storageCacheDir) && file.canWrite()) { + path = file; + break; + } } } - } - } else { - path = new File(SharedConfig.storageCacheDir); - } - try { - StatFs stat = new StatFs(path.getPath()); - long blockSize; - long blockSizeExternal; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - blockSize = stat.getBlockSizeLong(); - } else { - blockSize = stat.getBlockSize(); - } - long availableBlocks; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - availableBlocks = stat.getAvailableBlocksLong(); - } else { - availableBlocks = stat.getAvailableBlocks(); - } - long blocksTotal; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - blocksTotal = stat.getBlockCountLong(); } else { - blocksTotal = stat.getBlockCount(); + path = new File(SharedConfig.storageCacheDir); } + try { + StatFs stat = new StatFs(path.getPath()); + long blockSize; + long blockSizeExternal; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + blockSize = stat.getBlockSizeLong(); + } else { + blockSize = stat.getBlockSize(); + } + long availableBlocks; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + availableBlocks = stat.getAvailableBlocksLong(); + } else { + availableBlocks = stat.getAvailableBlocks(); + } + long blocksTotal; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + blocksTotal = stat.getBlockCountLong(); + } else { + blocksTotal = stat.getBlockCount(); + } - lastDeviceTotalSize = blocksTotal * blockSize; - lastDeviceTotalFreeSize = availableBlocks * blockSize; - if (onDone != null) { - onDone.run(lastDeviceTotalSize, lastDeviceTotalFreeSize); + AndroidUtilities.runOnUIThread(() -> { + lastDeviceTotalSize = blocksTotal * blockSize; + lastDeviceTotalFreeSize = availableBlocks * blockSize; + if (onDone != null) { + onDone.run(lastDeviceTotalSize, lastDeviceTotalFreeSize); + } + }); + } catch (Exception e) { + FileLog.e(e); } - return; - } catch (Exception e) { - FileLog.e(e); - } + }); } @Override @@ -415,10 +418,11 @@ public boolean onFragmentCreate() { } stickersCacheSize += cacheCustomEmojiSize; audioSize = getDirectorySize(FileLoader.checkDirectory(FileLoader.MEDIA_DIR_AUDIO), 0); + storiesSize = getDirectorySize(FileLoader.checkDirectory(FileLoader.MEDIA_DIR_STORIES), 0); if (canceled) { return; } - totalSize = lastTotalSizeCalculated = cacheSize + cacheTempSize + videoSize + audioSize + photoSize + documentsSize + musicSize + stickersCacheSize; + totalSize = lastTotalSizeCalculated = cacheSize + cacheTempSize + videoSize + audioSize + photoSize + documentsSize + musicSize + storiesSize + stickersCacheSize; lastTotalSizeCalculatedTime = System.currentTimeMillis(); File path = EnvUtil.getTelegramPath(); @@ -470,13 +474,13 @@ public boolean onFragmentCreate() { private void updateChart() { if (cacheChart != null) { if (!calculating && totalSize > 0) { - CacheChart.SegmentSize[] segments = new CacheChart.SegmentSize[9]; + CacheChart.SegmentSize[] segments = new CacheChart.SegmentSize[10]; for (int i = 0; i < itemInners.size(); ++i) { ItemInner item = itemInners.get(i); if (item.viewType == VIEW_TYPE_SECTION) { if (item.index < 0) { if (collapsed) { - segments[8] = CacheChart.SegmentSize.of(item.size, selected[8]); + segments[9] = CacheChart.SegmentSize.of(item.size, selected[9]); } } else { segments[item.index] = CacheChart.SegmentSize.of(item.size, selected[item.index]); @@ -504,7 +508,7 @@ private void loadDialogEntities() { CacheModel cacheModel = new CacheModel(false); LongSparseArray dilogsFilesEntities = new LongSparseArray<>(); - fillDialogsEntitiesRecursive(FileLoader.checkDirectory(FileLoader.MEDIA_DIR_CACHE), TYPE_OTHER, dilogsFilesEntities, null); + fillDialogsEntitiesRecursive(FileLoader.checkDirectory(FileLoader.MEDIA_DIR_CACHE), TYPE_OTHER, dilogsFilesEntities, cacheModel); fillDialogsEntitiesRecursive(FileLoader.checkDirectory(FileLoader.MEDIA_DIR_IMAGE), TYPE_PHOTOS, dilogsFilesEntities, cacheModel); fillDialogsEntitiesRecursive(FileLoader.checkDirectory(FileLoader.MEDIA_DIR_IMAGE_PUBLIC), TYPE_PHOTOS, dilogsFilesEntities, cacheModel); @@ -513,6 +517,7 @@ private void loadDialogEntities() { fillDialogsEntitiesRecursive(FileLoader.checkDirectory(FileLoader.MEDIA_DIR_VIDEO_PUBLIC), TYPE_VIDEOS, dilogsFilesEntities, cacheModel); fillDialogsEntitiesRecursive(FileLoader.checkDirectory(FileLoader.MEDIA_DIR_AUDIO), TYPE_VOICE, dilogsFilesEntities, cacheModel); + fillDialogsEntitiesRecursive(FileLoader.checkDirectory(FileLoader.MEDIA_DIR_STORIES), TYPE_OTHER, dilogsFilesEntities, cacheModel); fillDialogsEntitiesRecursive(FileLoader.checkDirectory(FileLoader.MEDIA_DIR_DOCUMENT), TYPE_DOCUMENTS, dilogsFilesEntities, cacheModel); fillDialogsEntitiesRecursive(FileLoader.checkDirectory(FileLoader.MEDIA_DIR_FILES), TYPE_DOCUMENTS, dilogsFilesEntities, cacheModel); @@ -642,13 +647,17 @@ public void fillDialogsEntitiesRecursive(final File fromFolder, int type, LongSp addToType = TYPE_MUSIC; } CacheModel.FileInfo fileInfo = new CacheModel.FileInfo(fileEntry); - fileInfo.type = addToType; + fileInfo.size = fileEntry.length(); if (fileMetadata != null) { fileInfo.dialogId = fileMetadata.dialogId; fileInfo.messageId = fileMetadata.messageId; fileInfo.messageType = fileMetadata.messageType; + if (fileInfo.messageType == MessageObject.TYPE_STORY && fileInfo.size > 0) { + addToType = TYPE_STORIES; + } } - fileInfo.size = fileEntry.length(); + fileInfo.type = addToType; + if (fileInfo.dialogId != 0) { DialogFileEntities dilogEntites = dilogsFilesEntities.get(fileInfo.dialogId, null); if (dilogEntites == null) { @@ -657,7 +666,7 @@ public void fillDialogsEntitiesRecursive(final File fromFolder, int type, LongSp } dilogEntites.addFile(fileInfo, addToType); } - if (cacheModel != null) { + if (cacheModel != null && addToType != TYPE_OTHER) { cacheModel.add(addToType, fileInfo); } //TODO measure for other accounts @@ -740,19 +749,22 @@ private void updateRows(boolean animated) { sections.add(ItemInner.asCheckBox(LocaleController.getString(R.string.LocalDocumentCache), 2, documentsSize, Theme.key_statisticChartLine_green)); } if (musicSize > 0) { - sections.add(ItemInner.asCheckBox(LocaleController.getString(R.string.LocalMusicCache), 3, musicSize, Theme.key_statisticChartLine_red)); + sections.add(ItemInner.asCheckBox(LocaleController.getString(R.string.LocalMusicCache), 3, musicSize, Theme.key_statisticChartLine_purple)); } if (audioSize > 0) { sections.add(ItemInner.asCheckBox(LocaleController.getString(R.string.LocalAudioCache), 4, audioSize, Theme.key_statisticChartLine_lightgreen)); } + if (storiesSize > 0) { + sections.add(ItemInner.asCheckBox(LocaleController.getString(R.string.LocalStoriesCache), 5, storiesSize, Theme.key_statisticChartLine_red)); + } if (stickersCacheSize > 0) { - sections.add(ItemInner.asCheckBox(LocaleController.getString(R.string.LocalStickersCache), 5, stickersCacheSize, Theme.key_statisticChartLine_orange)); + sections.add(ItemInner.asCheckBox(LocaleController.getString(R.string.LocalStickersCache), 6, stickersCacheSize, Theme.key_statisticChartLine_orange)); } if (cacheSize > 0) { - sections.add(ItemInner.asCheckBox(LocaleController.getString(R.string.LocalProfilePhotosCache), 6, cacheSize, Theme.key_statisticChartLine_cyan)); + sections.add(ItemInner.asCheckBox(LocaleController.getString(R.string.LocalProfilePhotosCache), 7, cacheSize, Theme.key_statisticChartLine_cyan)); } if (cacheTempSize > 0) { - sections.add(ItemInner.asCheckBox(LocaleController.getString(R.string.LocalMiscellaneousCache), 7, cacheTempSize, Theme.key_statisticChartLine_purple)); + sections.add(ItemInner.asCheckBox(LocaleController.getString(R.string.LocalMiscellaneousCache), 8, cacheTempSize, Theme.key_statisticChartLine_purple)); } if (!sections.isEmpty()) { Collections.sort(sections, (a, b) -> Long.compare(b.size, a.size)); @@ -760,13 +772,13 @@ private void updateRows(boolean animated) { hasCache = true; if (tempSizes == null) { - tempSizes = new float[9]; + tempSizes = new float[10]; } for (int i = 0; i < tempSizes.length; ++i) { tempSizes[i] = (float) size(i); } if (percents == null) { - percents = new int[9]; + percents = new int[10]; } AndroidUtilities.roundPercents(tempSizes, percents); @@ -780,7 +792,7 @@ private void updateRows(boolean animated) { sum += sections.get(i).size; sumPercents += percents[sections.get(i).index]; } - percents[8] = sumPercents; + percents[9] = sumPercents; itemInners.add(ItemInner.asCheckBox(LocaleController.getString(R.string.LocalOther), -1, sum, Theme.key_statisticChartLine_golden)); if (!collapsed) { itemInners.addAll(sections.subList(MAX_NOT_COLLAPSED, sections.size())); @@ -803,6 +815,7 @@ private void updateRows(boolean animated) { itemInners.add(new ItemInner(VIEW_TYPE_KEEP_MEDIA_CELL, KEEP_MEDIA_TYPE_USER)); itemInners.add(new ItemInner(VIEW_TYPE_KEEP_MEDIA_CELL, KEEP_MEDIA_TYPE_GROUP)); itemInners.add(new ItemInner(VIEW_TYPE_KEEP_MEDIA_CELL, KEEP_MEDIA_TYPE_CHANNEL)); + itemInners.add(new ItemInner(VIEW_TYPE_KEEP_MEDIA_CELL, KEEP_MEDIA_TYPE_STORIES)); itemInners.add(ItemInner.asInfo(LocaleController.getString("KeepMediaInfoPart", R.string.KeepMediaInfoPart))); if (totalDeviceSize > 0) { @@ -964,6 +977,9 @@ public static void cleanDirJava(String fileName, int docType, int[] p, Utilities } if (entry.isDirectory()) { + if ("drafts".equals(entry.getName())) { + continue; + } cleanDirJava(fileName + "/" + name, docType, p, onProgress); } else { entry.delete(); @@ -980,7 +996,7 @@ private void cleanupFoldersInternal(Utilities.Callback2 onProgre long clearedSize = 0; boolean allItemsClear = true; final int[] clearDirI = new int[] { 0 }; - int clearDirCount = (selected[0] ? 2 : 0) + (selected[1] ? 2 : 0) + (selected[2] ? 2 : 0) + (selected[3] ? 2 : 0) + (selected[4] ? 1 : 0) + (selected[5] ? 2 : 0) + (selected[6] ? 1 : 0) + (selected[7] ? 1 : 0); + int clearDirCount = (selected[0] ? 2 : 0) + (selected[1] ? 2 : 0) + (selected[2] ? 2 : 0) + (selected[3] ? 2 : 0) + (selected[4] ? 1 : 0) + (selected[5] ? 2 : 0) + (selected[6] ? 1 : 0) + (selected[7] ? 1 : 0) + (selected[8] ? 1 : 0); long time = System.currentTimeMillis(); Utilities.Callback updateProgress = t -> { onProgress.run(clearDirI[0] / (float) clearDirCount + (1f / clearDirCount) * MathUtils.clamp(t, 0, 1), false); @@ -989,7 +1005,7 @@ private void cleanupFoldersInternal(Utilities.Callback2 onProgre final long now = System.currentTimeMillis(); onProgress.run(clearDirI[0] / (float) clearDirCount, now - time > 250); }; - for (int a = 0; a < 8; a++) { + for (int a = 0; a < 9; a++) { if (!selected[a]) { allItemsClear = false; continue; @@ -1014,13 +1030,16 @@ private void cleanupFoldersInternal(Utilities.Callback2 onProgre type = FileLoader.MEDIA_DIR_AUDIO; clearedSize += audioSize; } else if (a == 5) { + type = FileLoader.MEDIA_DIR_STORIES; + clearedSize += storiesSize; + } else if (a == 6) { type = 100; clearedSize += stickersCacheSize + cacheEmojiSize + cacheCustomEmojiSize; - } else if (a == 6) { + } else if (a == 7) { clearedSize += cacheSize; documentsMusicType = 5; type = FileLoader.MEDIA_DIR_CACHE; - } else if (a == 7) { + } else if (a == 8) { clearedSize += cacheTempSize; documentsMusicType = 4; type = FileLoader.MEDIA_DIR_CACHE; @@ -1088,6 +1107,8 @@ private void cleanupFoldersInternal(Utilities.Callback2 onProgre } } else if (type == FileLoader.MEDIA_DIR_AUDIO) { audioSize = getDirectorySize(FileLoader.checkDirectory(FileLoader.MEDIA_DIR_AUDIO), documentsMusicType); + } else if (type == FileLoader.MEDIA_DIR_STORIES) { + storiesSize = getDirectorySize(FileLoader.checkDirectory(FileLoader.MEDIA_DIR_STORIES), documentsMusicType); } else if (type == FileLoader.MEDIA_DIR_DOCUMENT) { if (documentsMusicType == 1) { documentsSize = getDirectorySize(FileLoader.checkDirectory(FileLoader.MEDIA_DIR_DOCUMENT), documentsMusicType); @@ -1113,7 +1134,7 @@ private void cleanupFoldersInternal(Utilities.Callback2 onProgre } } final boolean imagesClearedFinal = imagesCleared; - totalSize = lastTotalSizeCalculated = cacheSize + cacheTempSize + videoSize + audioSize + photoSize + documentsSize + musicSize + stickersCacheSize; + totalSize = lastTotalSizeCalculated = cacheSize + cacheTempSize + videoSize + audioSize + photoSize + documentsSize + musicSize + stickersCacheSize + storiesSize; lastTotalSizeCalculatedTime = System.currentTimeMillis(); Arrays.fill(selected, true); @@ -1201,16 +1222,17 @@ private long size(int type) { case 2: return documentsSize; case 3: return musicSize; case 4: return audioSize; - case 5: return stickersCacheSize; - case 6: return cacheSize; - case 7: return cacheTempSize; + case 5: return storiesSize; + case 6: return stickersCacheSize; + case 7: return cacheSize; + case 8: return cacheTempSize; default: return 0; } } private int sectionsSelected() { int count = 0; - for (int i = 0; i < 8; ++i) { + for (int i = 0; i < 9; ++i) { if (selected[i] && size(i) > 0) { count++; } @@ -1253,7 +1275,9 @@ public void onItemClick(int id) { } else if (id == delete_id) { clearSelectedFiles(); } else if (id == clear_database_id) { - clearDatabase(); + clearDatabase(false); + } else if (id == reset_database_id) { + clearDatabase(true); } } }); @@ -1279,7 +1303,7 @@ public void onItemClick(int id) { actionModeClearButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); actionModeClearButton.setPadding(AndroidUtilities.dp(14), 0, AndroidUtilities.dp(14), 0); actionModeClearButton.setTextColor(Theme.getColor(Theme.key_featuredStickers_buttonText)); - actionModeClearButton.setBackground(Theme.AdaptiveRipple.filledRect(Theme.key_featuredStickers_addButton, 6)); + actionModeClearButton.setBackground(Theme.AdaptiveRipple.filledRectByKey(Theme.key_featuredStickers_addButton, 6)); actionModeClearButton.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); actionModeClearButton.setGravity(Gravity.CENTER); actionModeClearButton.setText(LocaleController.getString("CacheClear", R.string.CacheClear)); @@ -1288,8 +1312,16 @@ public void onItemClick(int id) { ActionBarMenuItem otherItem = actionBar.createMenu().addItem(other_id, R.drawable.ic_ab_other); clearDatabaseItem = otherItem.addSubItem(clear_database_id, R.drawable.msg_delete, LocaleController.getString("ClearLocalDatabase", R.string.ClearLocalDatabase)); - clearDatabaseItem.setIconColor(Theme.getColor(Theme.key_dialogRedIcon)); - clearDatabaseItem.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + clearDatabaseItem.setIconColor(Theme.getColor(Theme.key_text_RedRegular)); + clearDatabaseItem.setTextColor(Theme.getColor(Theme.key_text_RedBold)); + clearDatabaseItem.setSelectorColor(Theme.multAlpha(Theme.getColor(Theme.key_text_RedRegular), .12f)); + + if (BuildVars.DEBUG_PRIVATE_VERSION) { + resetDatabaseItem = otherItem.addSubItem(reset_database_id, R.drawable.msg_delete, "Full Reset Database"); + resetDatabaseItem.setIconColor(Theme.getColor(Theme.key_text_RedRegular)); + resetDatabaseItem.setTextColor(Theme.getColor(Theme.key_text_RedBold)); + resetDatabaseItem.setSelectorColor(Theme.multAlpha(Theme.getColor(Theme.key_text_RedRegular), .12f)); + } updateDatabaseItemSize(); listAdapter = new ListAdapter(context); @@ -1430,7 +1462,7 @@ private void clearSelectedFiles() { showDialog(dialog); TextView button = (TextView) dialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } @@ -1490,7 +1522,7 @@ private void cleanupDialogFiles(DialogFileEntities dialogEntities, StorageDiagra HashSet filesToRemove = new HashSet<>(); long totalSizeBefore = totalSize; - for (int a = 0; a < 7; a++) { + for (int a = 0; a < 8; a++) { if (clearViewData != null) { if (clearViewData[a] == null || !clearViewData[a].clear) { continue; @@ -1517,7 +1549,22 @@ private void cleanupDialogFiles(DialogFileEntities dialogEntities, StorageDiagra audioSize -= entitiesToDelete.totalSize; } else if (a == TYPE_ANIMATED_STICKERS_CACHE) { stickersCacheSize -= entitiesToDelete.totalSize; - } else { + } else if (a == TYPE_STORIES) { + for (int i = 0; i < entitiesToDelete.files.size(); i++) { + CacheModel.FileInfo fileInfo = entitiesToDelete.files.get(i); + int type = getTypeByPath(entitiesToDelete.files.get(i).file.getAbsolutePath()); + if (type == TYPE_STORIES) { + storiesSize -= fileInfo.size; + } else if (type == TYPE_PHOTOS) { + photoSize -= fileInfo.size; + } else if (type == TYPE_VIDEOS) { + videoSize -= fileInfo.size; + } else { + cacheSize -= fileInfo.size; + } + } + // cacheSize -= entitiesToDelete.totalSize; + }else { cacheSize -= entitiesToDelete.totalSize; } } @@ -1536,11 +1583,11 @@ private void cleanupDialogFiles(DialogFileEntities dialogEntities, StorageDiagra photoSize -= fileInfo.size; } else if (fileInfo.type == TYPE_VIDEOS) { videoSize -= fileInfo.size; - } else if (fileInfo.size == TYPE_DOCUMENTS) { + } else if (fileInfo.type == TYPE_DOCUMENTS) { documentsSize -= fileInfo.size; - } else if (fileInfo.size == TYPE_MUSIC) { + } else if (fileInfo.type == TYPE_MUSIC) { musicSize -= fileInfo.size; - } else if (fileInfo.size == TYPE_VOICE) { + } else if (fileInfo.type == TYPE_VOICE) { audioSize -= fileInfo.size; } } @@ -1572,7 +1619,33 @@ private void cleanupDialogFiles(DialogFileEntities dialogEntities, StorageDiagra }); } - private void clearDatabase() { + private int getTypeByPath(String absolutePath) { + if (pathContains(absolutePath, FileLoader.MEDIA_DIR_STORIES)) { + return TYPE_STORIES; + } + if (pathContains(absolutePath, FileLoader.MEDIA_DIR_IMAGE)) { + return TYPE_PHOTOS; + } + if (pathContains(absolutePath, FileLoader.MEDIA_DIR_IMAGE_PUBLIC)) { + return TYPE_PHOTOS; + } + if (pathContains(absolutePath, FileLoader.MEDIA_DIR_VIDEO)) { + return TYPE_VIDEOS; + } + if (pathContains(absolutePath, FileLoader.MEDIA_DIR_VIDEO_PUBLIC)) { + return TYPE_VIDEOS; + } + return TYPE_OTHER; + } + + private boolean pathContains(String path, int mediaDirType) { + if (path == null || FileLoader.checkDirectory(mediaDirType) == null) { + return false; + } + return path.contains(FileLoader.checkDirectory(mediaDirType).getAbsolutePath()); + } + + private void clearDatabase(boolean fullReset) { AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); builder.setTitle(LocaleController.getString("LocalDatabaseClearTextTitle", R.string.LocalDatabaseClearTextTitle)); SpannableStringBuilder message = new SpannableStringBuilder(); @@ -1589,13 +1662,17 @@ private void clearDatabase() { progressDialog.setCanCancel(false); progressDialog.showDelayed(500); MessagesController.getInstance(currentAccount).clearQueryTime(); - getMessagesStorage().clearLocalDatabase(); + if (fullReset) { + getMessagesStorage().fullReset(); + } else { + getMessagesStorage().clearLocalDatabase(); + } }); AlertDialog alertDialog = builder.create(); showDialog(alertDialog); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } @@ -1953,48 +2030,69 @@ public ClearCacheButtonInternal(Context context) { super(context); ((MarginLayoutParams) button.getLayoutParams()).topMargin = AndroidUtilities.dp(5); button.setOnClickListener(e -> { - BottomSheet bottomSheet = new BottomSheet(getContext(), false) { - @Override - protected boolean canDismissWithTouchOutside() { - return false; - } - }; - bottomSheet.fixNavigationBar(); - bottomSheet.setCanDismissWithSwipe(false); - bottomSheet.setCancelable(false); - ClearingCacheView cacheView = new ClearingCacheView(getContext()); - bottomSheet.setCustomView(cacheView); - - final boolean[] done = new boolean[] { false }; - final float[] progress = new float[] { 0 }; - final boolean[] nextSection = new boolean[] { false }; - Runnable updateProgress = () -> { - cacheView.setProgress(progress[0]); - if (nextSection[0]) { - updateRows(); - } - }; + AlertDialog dialog = new AlertDialog.Builder(getContext()) + .setTitle(LocaleController.getString("ClearCache", R.string.ClearCache) + (TextUtils.isEmpty(valueTextView.getText()) ? "" : " (" + valueTextView.getText() + ")")) + .setMessage(LocaleController.getString("StorageUsageInfo", R.string.StorageUsageInfo)) + .setPositiveButton(textView.getText(), (di, v) -> doClearCache()) + .setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null) + .create(); + showDialog(dialog); + View clearButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE); + if (clearButton instanceof TextView) { + ((TextView) clearButton).setTextColor(Theme.getColor(Theme.key_text_RedRegular)); + clearButton.setBackground(Theme.getRoundRectSelectorDrawable(AndroidUtilities.dp(6), Theme.multAlpha(Theme.getColor(Theme.key_text_RedRegular), .12f))); + } + }); + } - AndroidUtilities.runOnUIThread(() -> { - if (!done[0]) { - showDialog(bottomSheet); - } - }, 150); - - cleanupFolders( - (progressValue, next) -> { - progress[0] = progressValue; - nextSection[0] = next; - AndroidUtilities.cancelRunOnUIThread(updateProgress); - AndroidUtilities.runOnUIThread(updateProgress); - }, - () -> AndroidUtilities.runOnUIThread(() -> { - done[0] = true; - cacheView.setProgress(1F); + private void doClearCache() { + BottomSheet bottomSheet = new BottomSheet(getContext(), false) { + @Override + protected boolean canDismissWithTouchOutside() { + return false; + } + }; + bottomSheet.fixNavigationBar(); + bottomSheet.setCanDismissWithSwipe(false); + bottomSheet.setCancelable(false); + ClearingCacheView cacheView = new ClearingCacheView(getContext()); + bottomSheet.setCustomView(cacheView); + + final boolean[] done = new boolean[] { false }; + final float[] progress = new float[] { 0 }; + final boolean[] nextSection = new boolean[] { false }; + Runnable updateProgress = () -> { + cacheView.setProgress(progress[0]); + if (nextSection[0]) { + updateRows(); + } + }; + + final long[] start = new long[] { -1 }; + AndroidUtilities.runOnUIThread(() -> { + if (!done[0]) { + start[0] = System.currentTimeMillis(); + showDialog(bottomSheet); + } + }, 150); + + cleanupFolders( + (progressValue, next) -> { + progress[0] = progressValue; + nextSection[0] = next; + AndroidUtilities.cancelRunOnUIThread(updateProgress); + AndroidUtilities.runOnUIThread(updateProgress); + }, + () -> AndroidUtilities.runOnUIThread(() -> { + done[0] = true; + cacheView.setProgress(1F); + if (start[0] > 0) { + AndroidUtilities.runOnUIThread(bottomSheet::dismiss, Math.max(0, 1000 - (System.currentTimeMillis() - start[0]))); + } else { bottomSheet.dismiss(); - }) - ); - }); + } + }) + ); } public void updateSize() { @@ -2004,9 +2102,10 @@ public void updateSize() { (selected[2] ? documentsSize : 0) + (selected[3] ? musicSize : 0) + (selected[4] ? audioSize : 0) + - (selected[5] ? stickersCacheSize : 0) + - (selected[6] ? cacheSize : 0) + - (selected[7] ? cacheTempSize : 0) + (selected[5] ? storiesSize : 0) + + (selected[6] ? stickersCacheSize : 0) + + (selected[7] ? cacheSize : 0) + + (selected[8] ? cacheTempSize : 0) ); setSize( isAllSectionsSelected(), @@ -2069,8 +2168,14 @@ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setClassName("android.widget.Button"); } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + super.onInterceptTouchEvent(ev); + return true; + } }; - button.setBackground(Theme.AdaptiveRipple.filledRect(Theme.key_featuredStickers_addButton, 8)); + button.setBackground(Theme.AdaptiveRipple.filledRectByKey(Theme.key_featuredStickers_addButton, 8)); button.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); if (LocaleController.isRTL) { @@ -2562,6 +2667,8 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { textCell2.setTextAndValueAndColorfulIcon(LocaleController.getString("GroupChats", R.string.GroupChats), value, true, R.drawable.msg_filled_menu_groups, getThemedColor(Theme.key_statisticChartLine_green), true); } else if (itemInners.get(position).keepMediaType == KEEP_MEDIA_TYPE_CHANNEL) { textCell2.setTextAndValueAndColorfulIcon(LocaleController.getString("CacheChannels", R.string.CacheChannels), value, true, R.drawable.msg_filled_menu_channels, getThemedColor(Theme.key_statisticChartLine_golden), true); + } else if (itemInners.get(position).keepMediaType == KEEP_MEDIA_TYPE_STORIES) { + textCell2.setTextAndValueAndColorfulIcon(LocaleController.getString("CacheStories", R.string.CacheStories), value, false, R.drawable.msg_filled_stories, getThemedColor(Theme.key_statisticChartLine_red), false); } textCell2.setSubtitle(subtitle); break; @@ -2585,7 +2692,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { // privacyCell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); // } else { privacyCell.setText(AndroidUtilities.replaceTags(item.text)); - privacyCell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + privacyCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); // } break; case VIEW_TYPE_STORAGE: @@ -2650,7 +2757,7 @@ public ArrayList getThemeDescriptions() { } if (actionTextView != null) { - actionTextView.setBackground(Theme.AdaptiveRipple.filledRect(Theme.key_featuredStickers_addButton, 4)); + actionTextView.setBackground(Theme.AdaptiveRipple.filledRectByKey(Theme.key_featuredStickers_addButton, 4)); } }; ArrayList arrayList = new ArrayList<>(); @@ -2890,7 +2997,7 @@ public void setChecked(boolean checked, boolean animated) { } if (checkBox == null) { checkBox = new CheckBox2(getContext(), 21, resourcesProvider); - checkBox.setColor(null, Theme.key_windowBackgroundWhite, Theme.key_checkboxCheck); + checkBox.setColor(-1, Theme.key_windowBackgroundWhite, Theme.key_checkboxCheck); checkBox.setDrawUnchecked(false); checkBox.setDrawBackgroundAsArc(3); addView(checkBox, LayoutHelper.createFrame(24, 24, 0, 38, 25, 0, 0)); @@ -3015,7 +3122,7 @@ public static class ItemInner extends AdapterWithDiffUtils.Item { public int index; public long size; - String colorKey; + int colorKey; public boolean pad; boolean last; @@ -3042,11 +3149,11 @@ private ItemInner(int viewType) { super(viewType, true); } - public static ItemInner asCheckBox(CharSequence text, int index, long size, String colorKey) { + public static ItemInner asCheckBox(CharSequence text, int index, long size, int colorKey) { return asCheckBox(text, index, size, colorKey, false); } - public static ItemInner asCheckBox(CharSequence text, int index, long size, String colorKey, boolean last) { + public static ItemInner asCheckBox(CharSequence text, int index, long size, int colorKey, boolean last) { ItemInner item = new ItemInner(VIEW_TYPE_SECTION); item.index = index; item.headerName = text; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/CachedMediaLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/CachedMediaLayout.java index 415e7ef767..eea6186adb 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/CachedMediaLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/CachedMediaLayout.java @@ -59,6 +59,7 @@ import org.telegram.ui.Components.RecyclerListView; import org.telegram.ui.Components.ViewPagerFixed; import org.telegram.ui.Storage.CacheModel; +import org.telegram.ui.Stories.StoriesListPlaceProvider; import java.io.File; import java.util.ArrayList; @@ -70,7 +71,8 @@ public class CachedMediaLayout extends FrameLayout implements NestedSizeNotifier private static final int PAGE_TYPE_MEDIA = 1; private static final int PAGE_TYPE_DOCUMENTS = 2; private static final int PAGE_TYPE_MUSIC = 3; - private static final int PAGE_TYPE_VOICE = 4; + private static final int PAGE_TYPE_STORIES = 4; + private static final int PAGE_TYPE_VOICE = 5; private static final int VIEW_TYPE_CHAT = 1; private static final int VIEW_TYPE_FILE_ENTRY = 2; @@ -100,7 +102,8 @@ public CachedMediaLayout(@NonNull Context context, BaseFragment parentFragment) int CacheTabChats; allPages[PAGE_TYPE_CHATS] = new Page(LocaleController.getString("FilterChats", R.string.FilterChats), PAGE_TYPE_CHATS, new DialogsAdapter()); - allPages[PAGE_TYPE_MEDIA] = new Page(LocaleController.getString("MediaTab", R.string.MediaTab), PAGE_TYPE_MEDIA, new MediaAdapter()); + //allPages[PAGE_TYPE_STORIES] = new Page(LocaleController.getString("FilterStories", R.string.FilterStories), PAGE_TYPE_STORIES, new MediaAdapter(true)); + allPages[PAGE_TYPE_MEDIA] = new Page(LocaleController.getString("MediaTab", R.string.MediaTab), PAGE_TYPE_MEDIA, new MediaAdapter(false)); allPages[PAGE_TYPE_DOCUMENTS] = new Page(LocaleController.getString("SharedFilesTab2", R.string.SharedFilesTab2), PAGE_TYPE_DOCUMENTS, new DocumentsAdapter()); allPages[PAGE_TYPE_MUSIC] = new Page(LocaleController.getString("Music", R.string.Music), PAGE_TYPE_MUSIC, new MusicAdapter()); // allPages[PAGE_TYPE_VOICE] = new Page(LocaleController.getString("Voice", R.string.Voice), PAGE_TYPE_VOICE, new VoiceAdapter()); @@ -155,7 +158,17 @@ public void onItemClick(View view, int position) { ItemInner itemInner = adapter.itemInners.get(position); //if (cacheModel.getSelectedFiles() == 0) { if (view instanceof SharedPhotoVideoCell2) { - openPhoto(itemInner, (MediaAdapter) adapter, recyclerListView, (SharedPhotoVideoCell2)view); + boolean isStory = ((MediaAdapter) adapter).isStories; + if (isStory) { + TLRPC.StoryItem storyItem = new TLRPC.TL_storyItem(); + storyItem.dialogId = itemInner.file.dialogId; + storyItem.id = Objects.hash(itemInner.file.file.getAbsolutePath()); + storyItem.attachPath = itemInner.file.file.getAbsolutePath(); + storyItem.date = -1; + parentFragment.getOrCreateStoryViewer().open(context, storyItem, StoriesListPlaceProvider.of(recyclerListView)); + } else { + openPhoto(itemInner, (MediaAdapter) adapter, recyclerListView, (SharedPhotoVideoCell2) view); + } return; } @@ -235,7 +248,7 @@ public void onItemClick(View view, int position) { public void bindView(View view, int position, int viewType) { RecyclerListView recyclerListView = (RecyclerListView) view; recyclerListView.setAdapter(pages.get(position).adapter); - if (pages.get(position).type == PAGE_TYPE_MEDIA) { + if (pages.get(position).type == PAGE_TYPE_MEDIA || pages.get(position).type == PAGE_TYPE_STORIES) { recyclerListView.setLayoutManager(new GridLayoutManager(view.getContext(), 3)); } else { recyclerListView.setLayoutManager(new LinearLayoutManager(view.getContext())); @@ -316,7 +329,6 @@ private void openItem(CacheModel.FileInfo fileInfo, CacheCell cacheCell) { if (fileIsMedia(fileInfo.file)) { ArrayList photoEntries = new ArrayList<>(); photoEntries.add(new MediaController.PhotoEntry(0, 0, 0, fileInfo.file.getPath(), 0, fileInfo.type == TYPE_VIDEOS, 0, 0, 0)); - ; PhotoViewer.getInstance().openPhotoForSelect(photoEntries, 0, PhotoViewer.SELECT_TYPE_NO_SELECT, false, placeProvider, null); } else { AndroidUtilities.openForView(fileInfo.file, fileInfo.file.getName(), null, parentFragment.getParentActivity(), null); @@ -378,6 +390,8 @@ public void update() { pages.add(allPages[i]); } else if (allPages[i].type == PAGE_TYPE_VOICE && !cacheModel.voice.isEmpty()) { pages.add(allPages[i]); + } else if (allPages[i].type == PAGE_TYPE_STORIES && !cacheModel.stories.isEmpty()) { + pages.add(allPages[i]); } } } @@ -588,6 +602,8 @@ void update() { files = cacheModel.music; } else if (type == PAGE_TYPE_VOICE) { files = cacheModel.voice; + } else if (type == PAGE_TYPE_STORIES) { + files = cacheModel.stories; } if (files != null) { for (int i = 0; i < files.size(); i++) { @@ -641,8 +657,10 @@ private class MediaAdapter extends BaseFilesAdapter { private SharedPhotoVideoCell2.SharedResources sharedResources; - private MediaAdapter() { - super(PAGE_TYPE_MEDIA); + boolean isStories; + private MediaAdapter(boolean stories) { + super(stories ? PAGE_TYPE_STORIES : PAGE_TYPE_MEDIA); + this.isStories = stories; } ArrayList photoEntries = new ArrayList<>(); @@ -674,6 +692,7 @@ public void onCheckBoxPressed() { } CombinedDrawable thumb; + private int storiesPointer; @Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { @@ -686,7 +705,17 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi boolean animated = file == cell.getTag(); cell.setTag(file); int size = (int) Math.max(100, AndroidUtilities.getRealScreenSize().x / AndroidUtilities.density); - if (file.type == TYPE_VIDEOS) { + if (isStories) { + boolean isVideo = file.file.getAbsolutePath().endsWith(".mp4"); + if (isVideo) { + cell.imageReceiver.setImage(ImageLocation.getForPath(file.file.getAbsolutePath()), size + "_" + size + "_pframe", thumb, null, null, 0); + } else { + cell.imageReceiver.setImage(ImageLocation.getForPath(file.file.getAbsolutePath()), size + "_" + size, thumb, null, null, 0); + } + cell.storyId = Objects.hash(file.file.getAbsolutePath()); + cell.isStory = true; + cell.setVideoText(AndroidUtilities.formatFileSize(file.size), true); + } else if (file.type == TYPE_VIDEOS) { cell.imageReceiver.setImage(ImageLocation.getForPath("vthumb://" + 0 + ":" + file.file.getAbsolutePath()), size + "_" + size, thumb, null, null, 0); cell.setVideoText(AndroidUtilities.formatFileSize(file.size), true); } else { @@ -837,6 +866,7 @@ private void checkMessageObjectForAudio(CacheModel.FileInfo fileInfo, int positi message.media.flags |= 3; message.media.document = new TLRPC.TL_document(); message.flags |= TLRPC.MESSAGE_FLAG_HAS_MEDIA | TLRPC.MESSAGE_FLAG_HAS_FROM_ID; + message.dialog_id = fileInfo.dialogId; String ext = FileLoader.getFileExtension(fileInfo.file); @@ -997,29 +1027,39 @@ public CacheCell(@NonNull Context context) { checkBox = new CheckBox2(context, 21); checkBox.setDrawBackgroundAsArc(14); checkBox.setColor(Theme.key_checkbox, Theme.key_radioBackground, Theme.key_checkboxCheck); - addView(checkBox, LayoutHelper.createFrame(24, 24, Gravity.LEFT | Gravity.CENTER_VERTICAL, 18, 0, 0, 0)); View checkBoxClickableView = new View(getContext()); checkBoxClickableView.setOnClickListener(v -> { onCheckBoxPressed(); }); - addView(checkBoxClickableView, LayoutHelper.createFrame(40, 40, Gravity.LEFT | Gravity.CENTER_VERTICAL, 0, 0, 0, 0)); container = new FrameLayout(context); - addView(container, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 48, 0, 90, 0)); sizeTextView = new TextView(context); sizeTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); sizeTextView.setGravity(Gravity.RIGHT); sizeTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlueText)); - addView(sizeTextView, LayoutHelper.createFrame(69, LayoutHelper.WRAP_CONTENT, Gravity.RIGHT | Gravity.CENTER_VERTICAL, 0, 0, 21, 0)); - + if (LocaleController.isRTL) { + addView(checkBox, LayoutHelper.createFrame(24, 24, Gravity.RIGHT | Gravity.CENTER_VERTICAL, 0, 0, 18, 0)); + addView(checkBoxClickableView, LayoutHelper.createFrame(40, 40, Gravity.RIGHT | Gravity.CENTER_VERTICAL, 0, 0, 0, 0)); + addView(container, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 90, 0, 40, 0)); + addView(sizeTextView, LayoutHelper.createFrame(69, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.CENTER_VERTICAL, 0, 0, 0, 0)); + } else { + addView(checkBox, LayoutHelper.createFrame(24, 24, Gravity.LEFT | Gravity.CENTER_VERTICAL, 18, 0, 0, 0)); + addView(checkBoxClickableView, LayoutHelper.createFrame(40, 40, Gravity.LEFT | Gravity.CENTER_VERTICAL, 0, 0, 0, 0)); + addView(container, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 48, 0, 90, 0)); + addView(sizeTextView, LayoutHelper.createFrame(69, LayoutHelper.WRAP_CONTENT, Gravity.RIGHT | Gravity.CENTER_VERTICAL, 0, 0, 21, 0)); + } } @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); if (drawDivider) { - canvas.drawLine(getMeasuredWidth() - AndroidUtilities.dp(90), getMeasuredHeight() - 1, getMeasuredWidth(), getMeasuredHeight() - 1, Theme.dividerPaint); + if (LocaleController.isRTL) { + canvas.drawLine(0, getMeasuredHeight() - 1, getMeasuredWidth() - AndroidUtilities.dp(48), getMeasuredHeight() - 1, Theme.dividerPaint); + } else { + canvas.drawLine(getMeasuredWidth() - AndroidUtilities.dp(90), getMeasuredHeight() - 1, getMeasuredWidth(), getMeasuredHeight() - 1, Theme.dividerPaint); + } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/CalendarActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/CalendarActivity.java index 9353b98060..13910a0f9f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/CalendarActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/CalendarActivity.java @@ -41,6 +41,7 @@ import org.telegram.messenger.MessageObject; import org.telegram.messenger.MessagesController; import org.telegram.messenger.MessagesStorage; +import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; import org.telegram.messenger.Utilities; import org.telegram.tgnet.TLRPC; @@ -61,17 +62,22 @@ import org.telegram.ui.Components.RecyclerListView; import org.telegram.ui.Components.SharedMediaLayout; import org.telegram.ui.Components.spoilers.SpoilerEffect; +import org.telegram.ui.Stories.StoriesController; +import org.telegram.ui.Stories.StoryViewer; import java.time.YearMonth; import java.util.ArrayList; import java.util.Calendar; +import java.util.SortedSet; import tw.nekomimi.nekogram.NekoConfig; -public class CalendarActivity extends BaseFragment { +public class CalendarActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { public final static int TYPE_CHAT_ACTIVITY = 0; public final static int TYPE_MEDIA_CALENDAR = 1; + public final static int TYPE_PROFILE_STORIES = 2; + public final static int TYPE_ARCHIVED_STORIES = 3; FrameLayout contentView; @@ -126,6 +132,11 @@ public class CalendarActivity extends BaseFragment { private int calendarType; + private StoriesController.StoriesList storiesList; + private StoryViewer.PlaceProvider storiesPlaceProvider; + private int storiesPlaceDay; + private StoryViewer.HolderDrawAbove storiesPlaceDrawAbove; + private Path path = new Path(); private SpoilerEffect mediaSpoilerEffect = new SpoilerEffect(); @@ -151,14 +162,104 @@ public boolean onFragmentCreate() { topicId = getArguments().getInt("topic_id"); calendarType = getArguments().getInt("type"); + if (calendarType == TYPE_PROFILE_STORIES) { + storiesList = MessagesController.getInstance(currentAccount).getStoriesController().getStoriesList(dialogId, StoriesController.StoriesList.TYPE_PINNED); + } else if (calendarType == TYPE_ARCHIVED_STORIES) { + storiesList = MessagesController.getInstance(currentAccount).getStoriesController().getStoriesList(getUserConfig().clientUserId, StoriesController.StoriesList.TYPE_ARCHIVE); + } + if (storiesList != null) { + storiesPlaceProvider = new StoryViewer.PlaceProvider() { + @Override + public boolean findView(long dialogId, int messageId, int storyId, int type, StoryViewer.TransitionViewHolder holder) { + if (listView == null) { + return false; + } + + for (int i = 0; i < listView.getChildCount(); ++i) { + View child = listView.getChildAt(i); + if (!(child instanceof MonthView)) { + continue; + } + MonthView monthView = (MonthView) child; + if (monthView.messagesByDays == null) { + continue; + } + for (int j = 0; j < monthView.messagesByDays.size(); ++j) { + PeriodDay day = monthView.messagesByDays.valueAt(j); + if (day.storyItems != null && day.storyItems.contains(storyId)) { + int key = storiesPlaceDay = monthView.messagesByDays.keyAt(j); + ImageReceiver imageReceiver = monthView.imagesByDays.get(key); + + if (imageReceiver == null) { + return false; + } + + holder.storyImage = imageReceiver; + if (storiesPlaceDrawAbove == null) { + storiesPlaceDrawAbove = (canvas, bounds, alpha) -> { + blackoutPaint.setAlpha((int) (80 * alpha)); + float r = AndroidUtilities.lerp(0, Math.min(bounds.width(), bounds.height()) / 2f, alpha); + canvas.drawRoundRect(bounds, r, r, blackoutPaint); + + float textAlpha = Utilities.clamp((alpha - .5f) / .5f, 1, 0); + if (textAlpha > 0) { + int oldAlpha = activeTextPaint.getAlpha(); + activeTextPaint.setAlpha((int) (oldAlpha * textAlpha)); + canvas.save(); + float scale = Math.min(2, Math.min(bounds.height(), bounds.width()) / AndroidUtilities.dp(44)); + canvas.scale(scale, scale, bounds.centerX(), bounds.centerY()); + canvas.drawText(Integer.toString(1 + storiesPlaceDay), bounds.centerX(), bounds.centerY() + AndroidUtilities.dp(5), activeTextPaint); + canvas.restore(); + activeTextPaint.setAlpha(oldAlpha); + } + }; + } + holder.drawAbove = storiesPlaceDrawAbove; + holder.view = monthView; + holder.clipParent = fragmentView; + holder.clipTop = AndroidUtilities.dp(36); + holder.clipBottom = fragmentView.getBottom(); + holder.avatarImage = null; + return true; + } + } + } + + return false; + } + + @Override + public void preLayout(long currentDialogId, int messageId, Runnable o) { + if (listView == null) { + o.run(); + } + listView.post(o); + } + }; + } + if (dialogId >= 0) { canClearHistory = true; } else { canClearHistory = false; } + + if (storiesList != null) { + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.storiesListUpdated); + } + return super.onFragmentCreate(); } + @Override + public void onFragmentDestroy() { + super.onFragmentDestroy(); + + if (storiesList != null) { + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.storiesListUpdated); + } + } + @Override public View createView(Context context) { textPaint.setTextSize(AndroidUtilities.dp(16)); @@ -338,9 +439,9 @@ public void run(boolean forAll) { selectDaysButton.setBackground(Theme.createSelectorDrawable(ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_chat_fieldOverlayText), (int) (0.2f * 255)), 2)); - removeDaysButton.setBackground(Theme.createSelectorDrawable(ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_dialogTextRed), (int) (0.2f * 255)), 2)); + removeDaysButton.setBackground(Theme.createSelectorDrawable(ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_text_RedBold), (int) (0.2f * 255)), 2)); selectDaysButton.setTextColor(Theme.getColor(Theme.key_chat_fieldOverlayText)); - removeDaysButton.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + removeDaysButton.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } return fragmentView; @@ -361,6 +462,12 @@ private void loadNext() { if (loading || endReached) { return; } + if (storiesList != null) { + updateFromStoriesList(); + storiesList.load(false, 100); + loading = storiesList.isLoading(); + return; + } loading = true; TLRPC.TL_messages_getSearchResultsCalendar req = new TLRPC.TL_messages_getSearchResultsCalendar(); if (photosVideosTypeFilter == SharedMediaLayout.FILTER_PHOTOS_ONLY) { @@ -475,6 +582,88 @@ private void checkLoadNext() { } } + private void updateFromStoriesList() { + loading = storiesList.isLoading(); + + Calendar calendar = Calendar.getInstance(); + messagesByYearMounth.clear(); + minDate = Integer.MAX_VALUE; + for (int i = 0; i < storiesList.messageObjects.size(); ++i) { + MessageObject messageObject = storiesList.messageObjects.get(i); + minDate = Math.min(minDate, messageObject.messageOwner.date); + calendar.setTimeInMillis(messageObject.messageOwner.date * 1000L); + int month = calendar.get(Calendar.YEAR) * 100 + calendar.get(Calendar.MONTH); + SparseArray messagesByDays = messagesByYearMounth.get(month); + if (messagesByDays == null) { + messagesByDays = new SparseArray<>(); + messagesByYearMounth.put(month, messagesByDays); + } + int index = calendar.get(Calendar.DAY_OF_MONTH) - 1; + PeriodDay periodDay = messagesByDays.get(index); + if (periodDay == null) { + periodDay = new PeriodDay(); + periodDay.storyItems = new ArrayList<>(); + } + periodDay.storyItems.add(messageObject.getId()); + periodDay.messageObject = messageObject; + periodDay.date = (int) (calendar.getTimeInMillis() / 1000L); +// startOffset += res.periods.get(i).count; +// periodDay.startOffset = startOffset; + messagesByDays.put(index, periodDay); + if (month < minMontYear || minMontYear == 0) { + minMontYear = month; + } + } + + int maxDate = (int) (System.currentTimeMillis() / 1000L); + + for (int date = minDate; date < maxDate; date += 86400) { + calendar.setTimeInMillis(date * 1000L); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + + int month = calendar.get(Calendar.YEAR) * 100 + calendar.get(Calendar.MONTH); + SparseArray messagesByDays = messagesByYearMounth.get(month); + if (messagesByDays == null) { + messagesByDays = new SparseArray<>(); + messagesByYearMounth.put(month, messagesByDays); + } + int index = calendar.get(Calendar.DAY_OF_MONTH) - 1; + if (messagesByDays.get(index, null) == null) { + PeriodDay periodDay = new PeriodDay(); + periodDay.hasImage = false; + periodDay.date = (int) (calendar.getTimeInMillis() / 1000L); + messagesByDays.put(index, periodDay); + } + } + + endReached = storiesList.isFull(); + if (isOpened) { + checkEnterItems = true; + } + listView.invalidate(); + int newMonthCount = (int) (((calendar.getTimeInMillis() / 1000) - minDate) / 2629800) + 1; + adapter.notifyItemRangeChanged(0, monthCount); + if (newMonthCount > monthCount) { + adapter.notifyItemRangeInserted(monthCount + 1, newMonthCount); + monthCount = newMonthCount; + } + if (endReached) { + resumeDelayedFragmentAnimation(); + } + } + + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.storiesListUpdated) { + if (storiesList == (StoriesController.StoriesList) args[0]) { + updateFromStoriesList(); + } + } + } + private class CalendarAdapter extends RecyclerView.Adapter { @NonNull @@ -611,11 +800,15 @@ public boolean onSingleTapUp(MotionEvent e) { if (parentLayout == null) { return false; } - if (calendarType == TYPE_MEDIA_CALENDAR && messagesByDays != null) { + if (calendarType == TYPE_MEDIA_CALENDAR && messagesByDays != null || storiesList != null) { PeriodDay day = getDayAtCoord(e.getX(), e.getY()); if (day != null && day.messageObject != null && callback != null) { - callback.onDateSelected(day.messageObject.getId(), day.startOffset); - finishFragment(); + if (storiesList != null) { + getOrCreateStoryViewer().open(getContext(), day.messageObject.storyItem, day.messageObject.getId(), storiesList, true, storiesPlaceProvider); + } else { + callback.onDateSelected(day.messageObject.getId(), day.startOffset); + finishFragment(); + } } } if (messagesByDays != null) { @@ -1252,6 +1445,7 @@ public interface Callback { private class PeriodDay { MessageObject messageObject; + ArrayList storyItems; int startOffset; float enterAlpha = 1f; float startEnterDelay = 1f; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/CallLogActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/CallLogActivity.java index e59c911416..9d82ed2e29 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/CallLogActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/CallLogActivity.java @@ -17,7 +17,6 @@ import android.os.Build; import android.os.Bundle; import android.text.SpannableString; -import android.text.TextUtils; import android.text.style.ImageSpan; import android.util.TypedValue; import android.view.Gravity; @@ -326,7 +325,7 @@ public CallCell(Context context) { addView(imageView, LayoutHelper.createFrame(48, 48, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.CENTER_VERTICAL, 8, 0, 8, 0)); checkBox = new CheckBox2(context, 21); - checkBox.setColor(null, Theme.key_windowBackgroundWhite, Theme.key_checkboxCheck); + checkBox.setColor(-1, Theme.key_windowBackgroundWhite, Theme.key_checkboxCheck); checkBox.setDrawUnchecked(false); checkBox.setDrawBackgroundAsArc(3); addView(checkBox, LayoutHelper.createFrame(24, 24, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 42, 32, 42, 0)); @@ -411,20 +410,20 @@ public void onFragmentDestroy() { getNotificationCenter().removeObserver(this, NotificationCenter.groupCallUpdated); } - @Override - public View createView(Context context) { - greenDrawable = getParentActivity().getResources().getDrawable(R.drawable.ic_call_made_green_18dp).mutate(); - greenDrawable.setBounds(0, 0, greenDrawable.getIntrinsicWidth(), greenDrawable.getIntrinsicHeight()); - greenDrawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_calls_callReceivedGreenIcon), PorterDuff.Mode.SRC_IN)); - iconOut = new ImageSpan(greenDrawable, ImageSpan.ALIGN_BOTTOM); - greenDrawable2 = getParentActivity().getResources().getDrawable(R.drawable.ic_call_received_green_18dp).mutate(); - greenDrawable2.setBounds(0, 0, greenDrawable2.getIntrinsicWidth(), greenDrawable2.getIntrinsicHeight()); - greenDrawable2.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_calls_callReceivedGreenIcon), PorterDuff.Mode.SRC_IN)); - iconIn = new ImageSpan(greenDrawable2, ImageSpan.ALIGN_BOTTOM); - redDrawable = getParentActivity().getResources().getDrawable(R.drawable.ic_call_received_green_18dp).mutate(); - redDrawable.setBounds(0, 0, redDrawable.getIntrinsicWidth(), redDrawable.getIntrinsicHeight()); - redDrawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_calls_callReceivedRedIcon), PorterDuff.Mode.SRC_IN)); - iconMissed = new ImageSpan(redDrawable, ImageSpan.ALIGN_BOTTOM); + @Override + public View createView(Context context) { + greenDrawable = getParentActivity().getResources().getDrawable(R.drawable.ic_call_made_green_18dp).mutate(); + greenDrawable.setBounds(0, 0, greenDrawable.getIntrinsicWidth(), greenDrawable.getIntrinsicHeight()); + greenDrawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_calls_callReceivedGreenIcon), PorterDuff.Mode.MULTIPLY)); + iconOut = new ImageSpan(greenDrawable, ImageSpan.ALIGN_BOTTOM); + greenDrawable2 = getParentActivity().getResources().getDrawable(R.drawable.ic_call_received_green_18dp).mutate(); + greenDrawable2.setBounds(0, 0, greenDrawable2.getIntrinsicWidth(), greenDrawable2.getIntrinsicHeight()); + greenDrawable2.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_calls_callReceivedGreenIcon), PorterDuff.Mode.MULTIPLY)); + iconIn = new ImageSpan(greenDrawable2, ImageSpan.ALIGN_BOTTOM); + redDrawable = getParentActivity().getResources().getDrawable(R.drawable.ic_call_received_green_18dp).mutate(); + redDrawable.setBounds(0, 0, redDrawable.getIntrinsicWidth(), redDrawable.getIntrinsicHeight()); + redDrawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_fill_RedNormal), PorterDuff.Mode.MULTIPLY)); + iconMissed = new ImageSpan(redDrawable, ImageSpan.ALIGN_BOTTOM); actionBar.setBackButtonDrawable(new BackDrawable(false)); actionBar.setAllowOverlayTitle(true); @@ -628,7 +627,7 @@ private void showDeleteAlert(boolean all) { showDialog(alertDialog); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } @@ -989,11 +988,11 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType flickerLoadingView.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); flickerLoadingView.showDate(false); view = flickerLoadingView; - break; - case 2: - view = new TextInfoPrivacyCell(mContext); - view.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); - break; + break; + case 2: + view = new TextInfoPrivacyCell(mContext); + view.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + break; case 3: view = new HeaderCell(mContext, Theme.key_windowBackgroundWhiteBlueHeader, 21, 15, 2, false, getResourceProvider()); view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); @@ -1248,7 +1247,7 @@ public ArrayList getThemeDescriptions() { themeDescriptions.add(new ThemeDescription(null, 0, null, null, null, cellDelegate, Theme.key_avatar_backgroundPink)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{View.class}, null, new Drawable[]{greenDrawable, greenDrawable2, Theme.calllog_msgCallUpRedDrawable, Theme.calllog_msgCallDownRedDrawable}, null, Theme.key_calls_callReceivedGreenIcon)); - themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{View.class}, null, new Drawable[]{redDrawable, Theme.calllog_msgCallUpGreenDrawable, Theme.calllog_msgCallDownGreenDrawable}, null, Theme.key_calls_callReceivedRedIcon)); + themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{View.class}, null, new Drawable[]{redDrawable, Theme.calllog_msgCallUpGreenDrawable, Theme.calllog_msgCallDownGreenDrawable}, null, Theme.key_fill_RedNormal)); themeDescriptions.add(new ThemeDescription(flickerLoadingView, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_windowBackgroundWhite)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{ShadowSectionCell.class}, null, null, null, Theme.key_windowBackgroundGrayShadow)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/AboutLinkCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/AboutLinkCell.java index aab0f34f78..61b0a6583a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/AboutLinkCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/AboutLinkCell.java @@ -690,7 +690,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { private StaticLayout makeTextLayout(CharSequence string, int width) { if (Build.VERSION.SDK_INT >= 24) { return StaticLayout.Builder.obtain(string, 0, string.length(), Theme.profile_aboutTextPaint, width) - .setBreakStrategy(StaticLayout.BREAK_STRATEGY_HIGH_QUALITY) + .setBreakStrategy(StaticLayout.BREAK_STRATEGY_SIMPLE) .setHyphenationFrequency(StaticLayout.HYPHENATION_FREQUENCY_NONE) .setAlignment(LocaleController.isRTL ? StaticLayoutEx.ALIGN_RIGHT() : StaticLayoutEx.ALIGN_LEFT()) .build(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/AdminedChannelCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/AdminedChannelCell.java index 35d1ec741a..1bf5dd0bef 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/AdminedChannelCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/AdminedChannelCell.java @@ -54,7 +54,7 @@ public AdminedChannelCell(Context context, OnClickListener onClickListener, bool if (needCheck) { checkBox = new CheckBox2(context, 21); - checkBox.setColor(null, Theme.key_windowBackgroundWhite, Theme.key_checkboxCheck); + checkBox.setColor(-1, Theme.key_windowBackgroundWhite, Theme.key_checkboxCheck); checkBox.setDrawUnchecked(false); checkBox.setDrawBackgroundAsArc(3); addView(checkBox, LayoutHelper.createFrame(24, 24, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 0 : 42 + padding, 32, LocaleController.isRTL ? 42 + padding: 0, 0)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ArchiveHintCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ArchiveHintCell.java index b6e82613de..d72fba0513 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ArchiveHintCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ArchiveHintCell.java @@ -28,9 +28,9 @@ public ArchiveHintCell(Context context) { viewPager = new ViewPager(context) { @Override public boolean onInterceptTouchEvent(MotionEvent ev) { - if (getParent() != null) { - getParent().requestDisallowInterceptTouchEvent(true); - } +// if (getParent() != null) { +// getParent().requestDisallowInterceptTouchEvent(true); +// } return super.onInterceptTouchEvent(ev); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ArchivedStickerSetCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ArchivedStickerSetCell.java index 044d6e3d16..cba1a3a477 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ArchivedStickerSetCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ArchivedStickerSetCell.java @@ -42,6 +42,8 @@ import org.telegram.ui.Components.ProgressButton; import org.telegram.ui.Components.ViewHelper; +import java.util.ArrayList; + @SuppressLint("ViewConstructor") public class ArchivedStickerSetCell extends FrameLayout implements Checkable { @@ -154,15 +156,33 @@ public void setStickersSet(TLRPC.StickerSetCovered set, boolean divider) { setWillNotDraw(!needDivider); textView.setText(stickersSet.set.title); - valueTextView.setText(LocaleController.formatPluralString("Stickers", set.set.count)); + if (set.set.emojis) { + valueTextView.setText(LocaleController.formatPluralString("EmojiCount", set.set.count)); + } else { + valueTextView.setText(LocaleController.formatPluralString("Stickers", set.set.count)); + } - TLRPC.Document sticker; - if (set.cover != null) { + TLRPC.Document sticker = null; + if (set instanceof TLRPC.TL_stickerSetFullCovered) { + ArrayList documents = ((TLRPC.TL_stickerSetFullCovered) set).documents; + if (documents == null) { + return; + } + long thumb_document_id = set.set.thumb_document_id; + for (int i = 0; i < documents.size(); ++i) { + TLRPC.Document d = documents.get(i); + if (d != null && d.id == thumb_document_id) { + sticker = d; + break; + } + } + if (sticker == null && !documents.isEmpty()) { + sticker = documents.get(0); + } + } else if (set.cover != null) { sticker = set.cover; } else if (!set.covers.isEmpty()) { sticker = set.covers.get(0); - } else { - sticker = null; } if (sticker != null) { TLObject object = FileLoader.getClosestPhotoSizeWithSize(set.set.thumbs, 90); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/AudioPlayerCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/AudioPlayerCell.java index 5dada7cf9e..37052c5c46 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/AudioPlayerCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/AudioPlayerCell.java @@ -80,7 +80,7 @@ public AudioPlayerCell(Context context, int viewType, Theme.ResourcesProvider re this.viewType = viewType; radialProgress = new RadialProgress2(this, resourcesProvider); - radialProgress.setColors(Theme.key_chat_inLoader, Theme.key_chat_inLoaderSelected, Theme.key_chat_inMediaIcon, Theme.key_chat_inMediaIconSelected); + radialProgress.setColorKeys(Theme.key_chat_inLoader, Theme.key_chat_inLoaderSelected, Theme.key_chat_inMediaIcon, Theme.key_chat_inMediaIconSelected); TAG = DownloadController.getInstance(currentAccount).generateObserverTag(); setFocusable(true); @@ -112,7 +112,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { try { CharSequence author = currentMessageObject.getMusicAuthor().replace('\n', ' '); if (viewType == VIEW_TYPE_GLOBAL_SEARCH) { - author = new SpannableStringBuilder(author).append(' ').append(dotSpan).append(' ').append(FilteredSearchView.createFromInfoString(currentMessageObject)); + author = new SpannableStringBuilder(author).append(' ').append(dotSpan).append(' ').append(FilteredSearchView.createFromInfoString(currentMessageObject, 2)); } CharSequence authorFinal = TextUtils.ellipsize(author, Theme.chat_contextResult_descriptionTextPaint, maxWidth, TextUtils.TruncateAt.END); descriptionLayout = new StaticLayout(authorFinal, Theme.chat_contextResult_descriptionTextPaint, maxWidth + AndroidUtilities.dp(4), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); @@ -450,8 +450,7 @@ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { } } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/AvailableReactionCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/AvailableReactionCell.java index 2040678a5a..606a809b50 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/AvailableReactionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/AvailableReactionCell.java @@ -60,7 +60,7 @@ public AvailableReactionCell(@NonNull Context context, boolean checkbox, boolean if (checkbox) { checkBox = new CheckBox2(context, 26, null); checkBox.setDrawUnchecked(false); - checkBox.setColor(null, null, Theme.key_radioBackgroundChecked); + checkBox.setColor(-1, -1, Theme.key_radioBackgroundChecked); checkBox.setDrawBackgroundAsArc(-1); addView(checkBox, LayoutHelper.createFrameRelatively(26, 26, Gravity.END | Gravity.CENTER_VERTICAL, 0, 0, 22, 0)); } else { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/BotHelpCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/BotHelpCell.java index 361e96baf4..98ce0a6293 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/BotHelpCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/BotHelpCell.java @@ -385,10 +385,9 @@ public boolean animating() { public void setAnimating(boolean animating) { this.animating = animating; } - - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } private Drawable getThemedDrawable(String drawableKey) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/BrightnessControlCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/BrightnessControlCell.java index 343e66e946..ff68dfcb1c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/BrightnessControlCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/BrightnessControlCell.java @@ -26,18 +26,28 @@ public class BrightnessControlCell extends FrameLayout { + public static final int TYPE_DEFAULT = 0; + public static final int TYPE_WALLPAPER_DIM = 1; + private final int size; private ImageView leftImageView; private ImageView rightImageView; - private SeekBarView seekBarView; + public final SeekBarView seekBarView; + private int type; + Theme.ResourcesProvider resourcesProvider; - public BrightnessControlCell(Context context) { + public BrightnessControlCell(Context context, int type) { + this(context, type, null); + } + + public BrightnessControlCell(Context context, int type, Theme.ResourcesProvider resourcesProvider) { super(context); + this.type = type; + this.resourcesProvider = resourcesProvider; leftImageView = new ImageView(context); - leftImageView.setImageResource(R.drawable.msg_brightness_low); addView(leftImageView, LayoutHelper.createFrame(24, 24, Gravity.LEFT | Gravity.TOP, 17, 12, 0, 0)); - seekBarView = new SeekBarView(context, /* inPercents = */ true, null) { + seekBarView = new SeekBarView(context, /* inPercents = */ true, resourcesProvider) { @Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { @@ -66,15 +76,23 @@ public CharSequence getContentDescription() { addView(seekBarView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 38, Gravity.TOP | Gravity.LEFT, 54, 5, 54, 0)); rightImageView = new ImageView(context); - rightImageView.setImageResource(R.drawable.msg_brightness_high); addView(rightImageView, LayoutHelper.createFrame(24, 24, Gravity.RIGHT | Gravity.TOP, 0, 12, 17, 0)); + if (type == TYPE_DEFAULT) { + leftImageView.setImageResource(R.drawable.msg_brightness_low); + rightImageView.setImageResource(R.drawable.msg_brightness_high); + size = 48; + } else { + leftImageView.setImageResource(R.drawable.msg_brightness_high); + rightImageView.setImageResource(R.drawable.msg_brightness_low); + size = 43; + } } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - leftImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_windowBackgroundWhiteGrayIcon), PorterDuff.Mode.SRC_IN)); - rightImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_windowBackgroundWhiteGrayIcon), PorterDuff.Mode.SRC_IN)); + leftImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_windowBackgroundWhiteGrayIcon, resourcesProvider), PorterDuff.Mode.MULTIPLY)); + rightImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_windowBackgroundWhiteGrayIcon, resourcesProvider), PorterDuff.Mode.MULTIPLY)); } protected void didChangedValue(float value) { @@ -83,7 +101,7 @@ protected void didChangedValue(float value) { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(48), MeasureSpec.EXACTLY)); + super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(size), MeasureSpec.EXACTLY)); } public void setProgress(float value) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatActionCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatActionCell.java index 26165e08f1..3211929960 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatActionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatActionCell.java @@ -12,6 +12,7 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; @@ -61,6 +62,7 @@ import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.ChatBackgroundDrawable; import org.telegram.ui.Components.AnimatedEmojiDrawable; import org.telegram.ui.Components.AnimatedEmojiSpan; import org.telegram.ui.Components.AvatarDrawable; @@ -76,6 +78,7 @@ import org.telegram.ui.Components.spoilers.SpoilerEffect; import org.telegram.ui.LaunchActivity; import org.telegram.ui.PhotoViewer; +import org.telegram.ui.Stories.StoriesUtilities; import tw.nekomimi.nekogram.NekoConfig; import xyz.nextalone.nagram.NaConfig; @@ -103,6 +106,9 @@ public class ChatActionCell extends BaseCell implements DownloadController.FileD monthsToEmoticon.put(24, 5 + "\u20E3"); } + private int backgroundRectHeight; + private int backgroundButtonTop; + @Override public void didReceivedNotification(int id, int account, Object... args) { if (id == NotificationCenter.startSpoilers) { @@ -137,6 +143,10 @@ public void setInvalidateWithParent(View viewToInvalidate) { invalidateWithParent = viewToInvalidate; } + public boolean hasButton() { + return currentMessageObject != null && isButtonLayout(currentMessageObject) && giftPremiumButtonLayout != null; + } + public interface ChatActionCellDelegate { default void didClickImage(ChatActionCell cell) { } @@ -202,6 +212,7 @@ public interface ThemeDelegate extends Theme.ResourcesProvider { private boolean giftButtonPressed; RadialProgressView progressView; float progressToProgress; + StoriesUtilities.AvatarStoryParams avatarStoryParams = new StoriesUtilities.AvatarStoryParams(false); private RectF giftButtonRect = new RectF(); @@ -228,8 +239,8 @@ public interface ThemeDelegate extends Theme.ResourcesProvider { private int customDate; private CharSequence customText; - private String overrideBackground; - private String overrideText; + private int overrideBackground = -1; + private int overrideText = -1; private Paint overrideBackgroundPaint; private TextPaint overrideTextPaint; private int overrideColor; @@ -241,13 +252,17 @@ public interface ThemeDelegate extends Theme.ResourcesProvider { private boolean invalidateColors = false; private ChatActionCellDelegate delegate; - private ThemeDelegate themeDelegate; + private Theme.ResourcesProvider themeDelegate; private int stickerSize; private int giftRectSize; private StaticLayout giftPremiumTitleLayout; private StaticLayout giftPremiumSubtitleLayout; private StaticLayout giftPremiumButtonLayout; + TextPaint settingWallpaperPaint; + private StaticLayout settingWallpaperLayout; + private float settingWallpaperProgress; + private StaticLayout settingWallpaperProgressTextLayout; private float giftPremiumButtonWidth; private TextPaint giftTitlePaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); @@ -304,10 +319,11 @@ public ChatActionCell(Context context) { this(context, false, null); } - public ChatActionCell(Context context, boolean canDrawInParent, ThemeDelegate themeDelegate) { + public ChatActionCell(Context context, boolean canDrawInParent, Theme.ResourcesProvider resourcesProvider) { super(context); + avatarStoryParams.drawSegments = false; this.canDrawInParent = canDrawInParent; - this.themeDelegate = themeDelegate; + this.themeDelegate = resourcesProvider; imageReceiver = new ImageReceiver(this); imageReceiver.setRoundRadius(AndroidUtilities.roundMessageSize / 2); avatarDrawable = new AvatarDrawable(); @@ -387,7 +403,7 @@ public void setCustomText(CharSequence text) { } } - public void setOverrideColor(String background, String text) { + public void setOverrideColor(int background, int text) { overrideBackground = background; overrideText = text; } @@ -410,7 +426,43 @@ public void setMessageObject(MessageObject messageObject, boolean force) { DownloadController.getInstance(currentAccount).removeLoadingFileObserver(this); previousWidth = 0; imageReceiver.setAutoRepeatCount(0); - if (messageObject.type == MessageObject.TYPE_SUGGEST_PHOTO) { + if (messageObject.isStoryMention()) { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(messageObject.messageOwner.media.user_id); + avatarDrawable.setInfo(user); + TLRPC.StoryItem storyItem = messageObject.messageOwner.media.storyItem; + if (storyItem != null && storyItem.noforwards) { + imageReceiver.setForUserOrChat(user, avatarDrawable, null, true, 0, true); + } else { + StoriesUtilities.setImage(imageReceiver, storyItem); + } + imageReceiver.setRoundRadius((int) (stickerSize / 2f)); + } else if (messageObject.type == MessageObject.TYPE_ACTION_WALLPAPER) { + TLRPC.PhotoSize strippedPhotoSize = null; + if (messageObject.strippedThumb == null) { + for (int a = 0, N = messageObject.photoThumbs.size(); a < N; a++) { + TLRPC.PhotoSize photoSize = messageObject.photoThumbs.get(a); + if (photoSize instanceof TLRPC.TL_photoStrippedSize) { + strippedPhotoSize = photoSize; + break; + } + } + } + TLRPC.MessageAction action = messageObject.messageOwner.action; + if (action.wallpaper.uploadingImage != null) { + imageReceiver.setImage(ImageLocation.getForPath(action.wallpaper.uploadingImage), "150_150_wallpaper" + action.wallpaper.id + ChatBackgroundDrawable.hash(action.wallpaper.settings), null, null, ChatBackgroundDrawable.createThumb(action.wallpaper), 0, null, action.wallpaper, 1); + } else { + imageReceiver.setImage(ImageLocation.getForDocument((TLRPC.Document) messageObject.photoThumbsObject), "150_150_wallpaper" + action.wallpaper.id + ChatBackgroundDrawable.hash(action.wallpaper.settings), null, null, ChatBackgroundDrawable.createThumb(action.wallpaper), 0, null, action.wallpaper, 1); + } + imageReceiver.setRoundRadius((int) (stickerSize / 2f)); + + float uploadingInfoProgress = getUploadingInfoProgress(messageObject); + if (uploadingInfoProgress == 1f) { + radialProgress.setProgress(1f, !messageIdChanged); + radialProgress.setIcon(MediaActionDrawable.ICON_NONE, !messageIdChanged, !messageIdChanged); + } else { + radialProgress.setIcon(MediaActionDrawable.ICON_CANCEL, !messageIdChanged, !messageIdChanged); + } + } else if (messageObject.type == MessageObject.TYPE_SUGGEST_PHOTO) { imageReceiver.setRoundRadius((int) (stickerSize / 2f)); imageReceiver.setAllowStartLottieAnimation(true); imageReceiver.setDelegate(null); @@ -588,6 +640,16 @@ public void setMessageObject(MessageObject messageObject, boolean force) { requestLayout(); } + private float getUploadingInfoProgress(MessageObject messageObject) { + if (messageObject != null && messageObject.type == MessageObject.TYPE_ACTION_WALLPAPER) { + MessagesController messagesController = MessagesController.getInstance(currentAccount); + if (messagesController.uploadingWallpaper != null && TextUtils.equals(messageObject.messageOwner.action.wallpaper.uploadingImage, messagesController.uploadingWallpaper)) { + return messagesController.uploadingWallpaperInfo.uploadingProgress; + } + } + return 1; + } + public MessageObject getMessageObject() { return currentMessageObject; } @@ -626,6 +688,7 @@ protected void onDetachedFromWindow() { NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.didUpdatePremiumGiftStickers); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.diceStickersDidLoad); + avatarStoryParams.onDetachFromWindow(); } @Override @@ -675,7 +738,7 @@ public boolean onTouchEvent(MotionEvent event) { imagePressed = true; result = true; } - if (messageObject.type == MessageObject.TYPE_SUGGEST_PHOTO && backroundRect.contains(x, y)) { + if (radialProgress.getIcon() == MediaActionDrawable.ICON_NONE && (messageObject.type == MessageObject.TYPE_SUGGEST_PHOTO || messageObject.type == MessageObject.TYPE_ACTION_WALLPAPER) && backroundRect.contains(x, y)) { imagePressed = true; result = true; } @@ -698,10 +761,15 @@ public boolean onTouchEvent(MotionEvent event) { if (messageObject.type == MessageObject.TYPE_GIFT_PREMIUM) { openPremiumGiftPreview(); } else if (delegate != null) { - ImageUpdater imageUpdater = MessagesController.getInstance(currentAccount).photoSuggestion.get(messageObject.messageOwner.local_id); - if (imageUpdater != null) { - imageUpdater.cancel(); - } else { + boolean consumed = false; + if (messageObject.type == MessageObject.TYPE_SUGGEST_PHOTO) { + ImageUpdater imageUpdater = MessagesController.getInstance(currentAccount).photoSuggestion.get(messageObject.messageOwner.local_id); + if (imageUpdater != null) { + consumed = true; + imageUpdater.cancel(); + } + } + if (!consumed) { delegate.didClickImage(this); playSoundEffect(SoundEffectConstants.CLICK); } @@ -711,7 +779,7 @@ public boolean onTouchEvent(MotionEvent event) { imagePressed = false; break; case MotionEvent.ACTION_MOVE: - if (messageObject.type == MessageObject.TYPE_SUGGEST_PHOTO) { + if (isNewStyleButtonLayout()) { if (!backroundRect.contains(x, y)) { imagePressed = false; } @@ -865,11 +933,6 @@ private void createLayout(CharSequence text, int width) { paint.linkColor = paint.getColor(); textLayout = new StaticLayout(text, paint, maxWidth, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false); - spoilersPool.addAll(spoilers); - spoilers.clear(); - if (text instanceof Spannable) { - SpoilerEffect.addSpoilers(this, textLayout, (Spannable) text, spoilersPool, spoilers); - } animatedEmojiStack = AnimatedEmojiSpan.update(AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES, this, canDrawInParent && (delegate != null && !delegate.canDrawOutboundsContent()), animatedEmojiStack, textLayout); textHeight = 0; @@ -896,6 +959,12 @@ private void createLayout(CharSequence text, int width) { textX = (width - textWidth) / 2; textY = AndroidUtilities.dp(7); textXLeft = (width - textLayout.getWidth()) / 2; + + spoilersPool.addAll(spoilers); + spoilers.clear(); + if (text instanceof Spannable) { + SpoilerEffect.addSpoilers(this, textLayout, textX, textX + textWidth, (Spannable) text, spoilersPool, spoilers); + } } @@ -907,9 +976,9 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { return; } if (isButtonLayout(messageObject)) { - giftRectSize = Math.min((int) (AndroidUtilities.isTablet() ? AndroidUtilities.getMinTabletSide() * 0.6f : AndroidUtilities.displaySize.x * 0.6f), AndroidUtilities.displaySize.y - ActionBar.getCurrentActionBarHeight() - AndroidUtilities.statusBarHeight - AndroidUtilities.dp(64)); + giftRectSize = Math.min((int) (AndroidUtilities.isTablet() ? AndroidUtilities.getMinTabletSide() * 0.6f : AndroidUtilities.displaySize.x * 0.6f - AndroidUtilities.dp(34)), AndroidUtilities.displaySize.y - ActionBar.getCurrentActionBarHeight() - AndroidUtilities.statusBarHeight - AndroidUtilities.dp(64)); stickerSize = giftRectSize - AndroidUtilities.dp(106); - if (messageObject.type == MessageObject.TYPE_SUGGEST_PHOTO) { + if (isNewStyleButtonLayout()) { imageReceiver.setRoundRadius(stickerSize / 2); } else { imageReceiver.setRoundRadius(0); @@ -930,12 +999,15 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } } + int exactlyHeight = 0; if (isButtonLayout(messageObject)) { - int imageSize = stickerSize; - if (messageObject.type == MessageObject.TYPE_SUGGEST_PHOTO) { - imageSize = (int) (stickerSize * 0.7f); + int imageSize = getImageSize(messageObject); + float y; + if (isNewStyleButtonLayout()) { + y = textY + textHeight + AndroidUtilities.dp(4) + AndroidUtilities.dp(16) * 2 + imageSize + giftPremiumSubtitleLayout.getHeight() + AndroidUtilities.dp(4); + } else { + y = textY + textHeight + giftRectSize * 0.075f + imageSize + AndroidUtilities.dp(4) + AndroidUtilities.dp(4) + giftPremiumSubtitleLayout.getHeight(); } - float y = textY + textHeight + giftRectSize * 0.075f + imageSize + AndroidUtilities.dp(4) + AndroidUtilities.dp(4) + giftPremiumSubtitleLayout.getHeight(); giftPremiumAdditionalHeight = 0; if (giftPremiumTitleLayout != null) { y += giftPremiumTitleLayout.getHeight(); @@ -943,9 +1015,6 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { y -= AndroidUtilities.dp(12); giftPremiumAdditionalHeight -= AndroidUtilities.dp(30); } - if (messageObject.type == MessageObject.TYPE_SUGGEST_PHOTO) { - y += AndroidUtilities.dp(16); - } if (giftPremiumSubtitleLayout.getLineCount() > 2) { giftPremiumAdditionalHeight += (giftPremiumSubtitleLayout.getLineBottom(0) - giftPremiumSubtitleLayout.getLineTop(0)) * giftPremiumSubtitleLayout.getLineCount() - 2; @@ -954,10 +1023,14 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int h = textHeight + additionalHeight + AndroidUtilities.dp(14); - y += (h - y - giftPremiumButtonLayout.getHeight() - AndroidUtilities.dp(8)) / 2f; - float rectX = (previousWidth - giftPremiumButtonWidth) / 2f; - giftButtonRect.set(rectX - AndroidUtilities.dp(18), y - AndroidUtilities.dp(8), rectX + giftPremiumButtonWidth + AndroidUtilities.dp(18), y + giftPremiumButtonLayout.getHeight() + AndroidUtilities.dp(8)); - + if (giftPremiumButtonLayout != null) { + y += (h - y - (giftPremiumButtonLayout != null ? giftPremiumButtonLayout.getHeight() : 0) - AndroidUtilities.dp(8)) / 2f; + float rectX = (previousWidth - giftPremiumButtonWidth) / 2f; + giftButtonRect.set(rectX - AndroidUtilities.dp(18), y - AndroidUtilities.dp(8), rectX + giftPremiumButtonWidth + AndroidUtilities.dp(18), y + (giftPremiumButtonLayout != null ? giftPremiumButtonLayout.getHeight() : 0) + AndroidUtilities.dp(8)); + } else { + additionalHeight -= AndroidUtilities.dp(40); + giftPremiumAdditionalHeight -= AndroidUtilities.dp(40); + } int sizeInternal = getMeasuredWidth() << 16 + getMeasuredHeight(); starParticlesDrawable.rect.set(giftButtonRect); starParticlesDrawable.rect2.set(giftButtonRect); @@ -966,18 +1039,53 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { starParticlesDrawable.resetPositions(); } - + if (isNewStyleButtonLayout()) { + exactlyHeight = textY + textHeight + AndroidUtilities.dp(4); + backgroundRectHeight = 0; + backgroundRectHeight += AndroidUtilities.dp(16) * 2 + imageSize; + backgroundRectHeight += giftPremiumSubtitleLayout.getHeight(); + if (giftPremiumButtonLayout != null) { + backgroundButtonTop = exactlyHeight + backgroundRectHeight + AndroidUtilities.dp(10); + float rectX = (previousWidth - giftPremiumButtonWidth) / 2f; + giftButtonRect.set(rectX - AndroidUtilities.dp(18), backgroundButtonTop, rectX + giftPremiumButtonWidth + AndroidUtilities.dp(18), backgroundButtonTop + giftPremiumButtonLayout.getHeight() + AndroidUtilities.dp(8) * 2); + backgroundRectHeight += AndroidUtilities.dp(10) + giftButtonRect.height(); + } + backgroundRectHeight += AndroidUtilities.dp(16); + exactlyHeight += backgroundRectHeight; + exactlyHeight += AndroidUtilities.dp(6); + } } + if (messageObject != null && (isNewStyleButtonLayout())) { + setMeasuredDimension(width, exactlyHeight); + } else { + setMeasuredDimension(width, textHeight + additionalHeight + AndroidUtilities.dp(14)); + } + } - setMeasuredDimension(width, textHeight + additionalHeight + AndroidUtilities.dp(14)); + private boolean isNewStyleButtonLayout() { + return currentMessageObject.type == MessageObject.TYPE_SUGGEST_PHOTO || currentMessageObject.type == MessageObject.TYPE_ACTION_WALLPAPER || currentMessageObject.isStoryMention(); + } + private int getImageSize(MessageObject messageObject) { + int imageSize = stickerSize; + if (messageObject.type == MessageObject.TYPE_SUGGEST_PHOTO || isNewStyleButtonLayout()) { + imageSize = AndroidUtilities.dp(78);//Math.max(, (int) (stickerSize * 0.7f)); + } + return imageSize; } private void buildLayout() { CharSequence text = null; MessageObject messageObject = currentMessageObject; if (messageObject != null) { - if (delegate.getTopicId() == 0 && MessageObject.isTopicActionMessage(messageObject)) { + if (messageObject.isExpiredStory()) { + long dialogId = messageObject.messageOwner.media.user_id; + if (dialogId != UserConfig.getInstance(currentAccount).getClientUserId()) { + text = StoriesUtilities.createExpiredStoryString(true, "ExpiredStoryMention", R.string.ExpiredStoryMention); + } else { + text = StoriesUtilities.createExpiredStoryString(true, "ExpiredStoryMentioned", R.string.ExpiredStoryMentioned, MessagesController.getInstance(currentAccount).getUser(messageObject.getDialogId()).first_name); + } + } else if (delegate.getTopicId() == 0 && MessageObject.isTopicActionMessage(messageObject)) { TLRPC.TL_forumTopic topic = MessagesController.getInstance(currentAccount).getTopicsController().findTopic(-messageObject.getDialogId(), MessageObject.getTopicId(messageObject.messageOwner, true)); text = ForumUtilities.createActionTextWithTopic(topic, messageObject); } @@ -1041,6 +1149,37 @@ private void buildLayout() { } else { action = LocaleController.getString("ViewPhotoAction", R.string.ViewPhotoAction); } + createGiftPremiumLayouts(null, description, action, giftRectSize); + textLayout = null; + textHeight = 0; + textY = 0; + } else if (messageObject.type == MessageObject.TYPE_ACTION_WALLPAPER) { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(messageObject.isOutOwner() ? 0 : messageObject.getDialogId()); + CharSequence description; + String action = null; + if (user.id == UserConfig.getInstance(currentAccount).clientUserId) { + description = messageObject.messageText; + } else { + description = messageObject.messageText; + action = LocaleController.getString("ViewWallpaperAction", R.string.ViewWallpaperAction); + } + createGiftPremiumLayouts(null, description, action, giftRectSize); + textLayout = null; + textHeight = 0; + textY = 0; + } else if (messageObject.isStoryMention()) { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(messageObject.messageOwner.media.user_id); + CharSequence description; + String action = null; + + if (user.self) { + TLRPC.User user2 = MessagesController.getInstance(currentAccount).getUser(messageObject.getDialogId()); + description = AndroidUtilities.replaceTags(LocaleController.formatString("StoryYouMentionedTitle", R.string.StoryYouMentionedTitle, user2.first_name)); + } else { + description = AndroidUtilities.replaceTags(LocaleController.formatString("StoryMentionedTitle", R.string.StoryMentionedTitle, user.first_name)); + } + action = LocaleController.getString("StoryMentionedAction", R.string.StoryMentionedAction); + createGiftPremiumLayouts(null, description, action, giftRectSize); textLayout = null; textHeight = 0; @@ -1058,16 +1197,21 @@ private void createGiftPremiumLayouts(CharSequence title, CharSequence subtitle, } else { giftPremiumTitleLayout = null; } - if (currentMessageObject != null && currentMessageObject.type == MessageObject.TYPE_SUGGEST_PHOTO) { + if (currentMessageObject != null && isNewStyleButtonLayout()) { giftSubtitlePaint.setTextSize(AndroidUtilities.dp(13)); } else { giftSubtitlePaint.setTextSize(AndroidUtilities.dp(15)); } giftPremiumSubtitleLayout = new StaticLayout(subtitle, giftSubtitlePaint, width, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false); - SpannableStringBuilder buttonBuilder = SpannableStringBuilder.valueOf(button); - buttonBuilder.setSpan(new TypefaceSpan(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)), 0, buttonBuilder.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - giftPremiumButtonLayout = new StaticLayout(buttonBuilder, (TextPaint) getThemedPaint(Theme.key_paint_chatActionText), width, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false); - giftPremiumButtonWidth = measureLayoutWidth(giftPremiumButtonLayout); + if (button != null) { + SpannableStringBuilder buttonBuilder = SpannableStringBuilder.valueOf(button); + buttonBuilder.setSpan(new TypefaceSpan(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)), 0, buttonBuilder.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + giftPremiumButtonLayout = new StaticLayout(buttonBuilder, (TextPaint) getThemedPaint(Theme.key_paint_chatActionText), width, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false); + giftPremiumButtonWidth = measureLayoutWidth(giftPremiumButtonLayout); + } else { + giftPremiumButtonLayout = null; + giftPremiumButtonWidth = 0; + } } private float measureLayoutWidth(Layout layout) { @@ -1091,17 +1235,28 @@ protected void onDraw(Canvas canvas) { int imageSize = stickerSize; if (isButtonLayout(messageObject)) { stickerSize = giftRectSize - AndroidUtilities.dp(106); - if (messageObject.type == MessageObject.TYPE_ACTION_PHOTO) { + if (isNewStyleButtonLayout()) { + imageSize = getImageSize(messageObject); + int top = textY + textHeight + AndroidUtilities.dp(4) + AndroidUtilities.dp(16); + float x = (previousWidth - imageSize) / 2f; + float y = top; + if (messageObject.isStoryMention()) { + avatarStoryParams.storyItem = messageObject.messageOwner.media.storyItem; + } + avatarStoryParams.originalAvatarRect.set(x, y, x + imageSize, y + imageSize); + imageReceiver.setImageCoords(x, y, imageSize, imageSize); + } else if (messageObject.type == MessageObject.TYPE_ACTION_PHOTO) { imageReceiver.setImageCoords((previousWidth - stickerSize) / 2f, textY + textHeight + giftRectSize * 0.075f, stickerSize, stickerSize); } else { imageSize = (int) (stickerSize * 0.7f); imageReceiver.setImageCoords((previousWidth - imageSize) / 2f, textY + textHeight + giftRectSize * 0.075f + AndroidUtilities.dp(8), imageSize, imageSize); } - if (textPaint != null && giftTitlePaint != null && giftSubtitlePaint != null) { - if (giftTitlePaint.getColor() != textPaint.getColor()) { + textPaint = (TextPaint) getThemedPaint(Theme.key_paint_chatActionText); + if (textPaint != null) { + if (giftTitlePaint != null && giftTitlePaint.getColor() != textPaint.getColor()) { giftTitlePaint.setColor(textPaint.getColor()); } - if (giftSubtitlePaint.getColor() != textPaint.getColor()) { + if (giftSubtitlePaint != null && giftSubtitlePaint.getColor() != textPaint.getColor()) { giftSubtitlePaint.setColor(textPaint.getColor()); } } @@ -1110,7 +1265,14 @@ protected void onDraw(Canvas canvas) { drawBackground(canvas, false); if (isButtonLayout(messageObject) || (messageObject != null && messageObject.type == MessageObject.TYPE_ACTION_PHOTO)) { - imageReceiver.draw(canvas); + if (messageObject.isStoryMention()) { + long dialogId = messageObject.messageOwner.media.user_id; + avatarStoryParams.storyId = messageObject.messageOwner.media.id; + StoriesUtilities.drawAvatarWithStory(dialogId, canvas, imageReceiver, avatarStoryParams); + // imageReceiver.draw(canvas); + } else { + imageReceiver.draw(canvas); + } radialProgress.setProgressRect( imageReceiver.getImageX(), imageReceiver.getImageY(), @@ -1123,7 +1285,7 @@ protected void onDraw(Canvas canvas) { radialProgress.setProgress(imageUpdater.getCurrentImageProgress(), true); radialProgress.setCircleRadius((int) (imageReceiver.getImageWidth() * 0.5f) + 1); radialProgress.setMaxIconSize(AndroidUtilities.dp(24)); - radialProgress.setColors(Theme.key_chat_mediaLoaderPhoto, Theme.key_chat_mediaLoaderPhotoSelected, Theme.key_chat_mediaLoaderPhotoIcon, Theme.key_chat_mediaLoaderPhotoIconSelected); + radialProgress.setColorKeys(Theme.key_chat_mediaLoaderPhoto, Theme.key_chat_mediaLoaderPhotoSelected, Theme.key_chat_mediaLoaderPhotoIcon, Theme.key_chat_mediaLoaderPhotoIconSelected); if (imageUpdater.getCurrentImageProgress() == 1f) { radialProgress.setIcon(MediaActionDrawable.ICON_NONE, true, true); } else { @@ -1131,6 +1293,18 @@ protected void onDraw(Canvas canvas) { } } radialProgress.draw(canvas); + } else if (messageObject.type == MessageObject.TYPE_ACTION_WALLPAPER) { + float progress = getUploadingInfoProgress(messageObject); + radialProgress.setProgress(progress, true); + radialProgress.setCircleRadius(AndroidUtilities.dp(26)); + radialProgress.setMaxIconSize(AndroidUtilities.dp(24)); + radialProgress.setColorKeys(Theme.key_chat_mediaLoaderPhoto, Theme.key_chat_mediaLoaderPhotoSelected, Theme.key_chat_mediaLoaderPhotoIcon, Theme.key_chat_mediaLoaderPhotoIconSelected); + if (progress == 1f) { + radialProgress.setIcon(MediaActionDrawable.ICON_NONE, true, true); + } else { + radialProgress.setIcon(MediaActionDrawable.ICON_CANCEL, true, true); + } + radialProgress.draw(canvas); } } @@ -1158,10 +1332,21 @@ protected void onDraw(Canvas canvas) { if (isButtonLayout(messageObject)) { canvas.save(); - float x = (previousWidth - giftRectSize) / 2f + AndroidUtilities.dp(8), y = textY + textHeight + giftRectSize * 0.075f + (messageObject.type == MessageObject.TYPE_SUGGEST_PHOTO ? imageSize : stickerSize) + AndroidUtilities.dp(4); - if (messageObject.type == MessageObject.TYPE_SUGGEST_PHOTO) { - y += +AndroidUtilities.dp(16); + float x = (previousWidth - giftRectSize) / 2f + AndroidUtilities.dp(8); + float y; + if (isNewStyleButtonLayout()) { + float top = backroundRect != null ? backroundRect.top : (textY + textHeight + AndroidUtilities.dp(4)); + y = top + AndroidUtilities.dp(16) * 2 + imageSize; + } else { + y = textY + textHeight + giftRectSize * 0.075f + (messageObject.type == MessageObject.TYPE_SUGGEST_PHOTO ? imageSize : stickerSize) + AndroidUtilities.dp(4); + if (messageObject.type == MessageObject.TYPE_SUGGEST_PHOTO) { + y += AndroidUtilities.dp(16); + } + if (giftPremiumButtonLayout == null) { + y -= AndroidUtilities.dp(24); + } } + canvas.translate(x, y); if (giftPremiumTitleLayout != null) { giftPremiumTitleLayout.draw(canvas); @@ -1174,7 +1359,60 @@ protected void onDraw(Canvas canvas) { y += AndroidUtilities.dp(4); canvas.save(); canvas.translate(x, y); - giftPremiumSubtitleLayout.draw(canvas); + if (messageObject.type == MessageObject.TYPE_ACTION_WALLPAPER) { + if (radialProgress.getTransitionProgress() != 1f || radialProgress.getIcon() != MediaActionDrawable.ICON_NONE) { + if (settingWallpaperLayout == null) { + settingWallpaperPaint = new TextPaint(); + settingWallpaperPaint.setTextSize(AndroidUtilities.dp(13)); + settingWallpaperLayout = new StaticLayout("Setting new wallpaper...", settingWallpaperPaint, giftPremiumSubtitleLayout.getWidth(), Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false); + } + float progressLocal = getUploadingInfoProgress(messageObject); + if (settingWallpaperProgressTextLayout == null || settingWallpaperProgress != progressLocal) { + settingWallpaperProgress = progressLocal; + settingWallpaperProgressTextLayout = new StaticLayout((int) (progressLocal * 100) + "%", giftSubtitlePaint, giftPremiumSubtitleLayout.getWidth(), Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false); + } + + settingWallpaperPaint.setColor(giftSubtitlePaint.getColor()); + if (radialProgress.getIcon() == MediaActionDrawable.ICON_NONE) { + float p = radialProgress.getTransitionProgress(); + int oldColor = giftSubtitlePaint.getColor(); + settingWallpaperPaint.setAlpha((int) (Color.alpha(oldColor) * (1f - p))); + giftSubtitlePaint.setAlpha((int) (Color.alpha(oldColor) * p)); + + float s = 0.8f + 0.2f * p; + canvas.save(); + canvas.scale(s, s, giftPremiumSubtitleLayout.getWidth() / 2f, giftPremiumSubtitleLayout.getHeight() / 2f); + giftPremiumSubtitleLayout.draw(canvas); + canvas.restore(); + + giftSubtitlePaint.setAlpha((int) (Color.alpha(oldColor) * (1f - p))); + s = 0.8f + 0.2f * (1f - p); + canvas.save(); + canvas.scale(s, s, settingWallpaperLayout.getWidth() / 2f, settingWallpaperLayout.getHeight() / 2f); + settingWallpaperLayout.draw(canvas); + canvas.restore(); + + canvas.save(); + canvas.translate(0, settingWallpaperLayout.getHeight() + AndroidUtilities.dp(4)); + canvas.scale(s, s, settingWallpaperProgressTextLayout.getWidth() / 2f, settingWallpaperProgressTextLayout.getHeight() / 2f); + settingWallpaperProgressTextLayout.draw(canvas); + canvas.restore(); + + + giftSubtitlePaint.setColor(oldColor); + } else { + settingWallpaperLayout.draw(canvas); + canvas.save(); + canvas.translate(0, settingWallpaperLayout.getHeight() + AndroidUtilities.dp(4)); + settingWallpaperProgressTextLayout.draw(canvas); + canvas.restore(); + } + } else { + giftPremiumSubtitleLayout.draw(canvas); + } + } else { + giftPremiumSubtitleLayout.draw(canvas); + } canvas.restore(); if (giftPremiumTitleLayout == null) { @@ -1182,34 +1420,39 @@ protected void onDraw(Canvas canvas) { } y += giftPremiumSubtitleLayout.getHeight(); - y += (getHeight() - y - giftPremiumButtonLayout.getHeight() - AndroidUtilities.dp(8)) / 2f; + int buttonH = giftPremiumButtonLayout != null ? giftPremiumButtonLayout.getHeight() : 0; + y += (getHeight() - y - buttonH - AndroidUtilities.dp(8)) / 2f; if (themeDelegate != null) { themeDelegate.applyServiceShaderMatrix(getMeasuredWidth(), backgroundHeight, 0, viewTop + AndroidUtilities.dp(4)); } else { Theme.applyServiceShaderMatrix(getMeasuredWidth(), backgroundHeight, 0, viewTop + AndroidUtilities.dp(4)); } - Paint backgroundPaint = getThemedPaint(Theme.key_paint_chatActionBackground); - canvas.drawRoundRect(giftButtonRect, AndroidUtilities.dp(16), AndroidUtilities.dp(16), backgroundPaint); - if (hasGradientService()) { - canvas.drawRoundRect(giftButtonRect, AndroidUtilities.dp(16), AndroidUtilities.dp(16), Theme.chat_actionBackgroundGradientDarkenPaint); - } + if (giftPremiumButtonLayout != null) { + Paint backgroundPaint = getThemedPaint(Theme.key_paint_chatActionBackground); + canvas.drawRoundRect(giftButtonRect, AndroidUtilities.dp(16), AndroidUtilities.dp(16), backgroundPaint); - starsPath.rewind(); - starsPath.addRoundRect(giftButtonRect, AndroidUtilities.dp(16), AndroidUtilities.dp(16), Path.Direction.CW); - canvas.save(); - canvas.clipPath(starsPath); - if (getMessageObject().type != MessageObject.TYPE_SUGGEST_PHOTO) { - starParticlesDrawable.onDraw(canvas); - if (!starParticlesDrawable.paused) { + if (hasGradientService()) { + canvas.drawRoundRect(giftButtonRect, AndroidUtilities.dp(16), AndroidUtilities.dp(16), Theme.chat_actionBackgroundGradientDarkenPaint); + } + + if (getMessageObject().type != MessageObject.TYPE_SUGGEST_PHOTO && getMessageObject().type != MessageObject.TYPE_ACTION_WALLPAPER && getMessageObject().type != MessageObject.TYPE_STORY_MENTION) { + starsPath.rewind(); + starsPath.addRoundRect(giftButtonRect, AndroidUtilities.dp(16), AndroidUtilities.dp(16), Path.Direction.CW); + canvas.save(); + canvas.clipPath(starsPath); + + starParticlesDrawable.onDraw(canvas); + if (!starParticlesDrawable.paused) { + invalidate(); + } + canvas.restore(); + } else { + //TODO optimize invalidate(); } - } else { - //TODO optimize - invalidate(); } - canvas.restore(); if (messageObject.settingAvatar && progressToProgress != 1f) { progressToProgress += 16 / 150f; @@ -1225,15 +1468,15 @@ protected void onDraw(Canvas canvas) { canvas.save(); canvas.scale(progressToProgress, progressToProgress, giftButtonRect.centerX(), giftButtonRect.centerY()); progressView.setSize(rad); - progressView.setProgressColor(Theme.getColor(Theme.key_chat_serviceText)); + progressView.setProgressColor(Theme.getColor(Theme.key_chat_serviceText)); progressView.draw(canvas, giftButtonRect.centerX(), giftButtonRect.centerY()); canvas.restore(); } - if (progressToProgress != 1f){ + if (progressToProgress != 1f && giftPremiumButtonLayout != null) { canvas.save(); float s = 1f - progressToProgress; canvas.scale(s, s, giftButtonRect.centerX(), giftButtonRect.centerY()); - canvas.translate(x, y); + canvas.translate(x, giftButtonRect.top + AndroidUtilities.dp(8)); giftPremiumButtonLayout.draw(canvas); canvas.restore(); } @@ -1251,7 +1494,7 @@ public void drawBackground(Canvas canvas, boolean fromParent) { } Paint backgroundPaint = getThemedPaint(Theme.key_paint_chatActionBackground); textPaint = (TextPaint) getThemedPaint(Theme.key_paint_chatActionText); - if (overrideBackground != null) { + if (overrideBackground >= 0) { int color = getThemedColor(overrideBackground); if (overrideBackgroundPaint == null) { overrideBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); @@ -1417,8 +1660,15 @@ public void drawBackground(Canvas canvas, boolean fromParent) { MessageObject messageObject = currentMessageObject; if (isButtonLayout(messageObject)) { - float x = (getWidth() - giftRectSize) / 2f, y = textY + textHeight + AndroidUtilities.dp(12); - AndroidUtilities.rectTmp.set(x, y, x + giftRectSize, y + giftRectSize + giftPremiumAdditionalHeight); + float x = (getWidth() - giftRectSize) / 2f; + float y = textY + textHeight; + if (isNewStyleButtonLayout()) { + y += AndroidUtilities.dp(4); + AndroidUtilities.rectTmp.set(x, y, x + giftRectSize, y + backgroundRectHeight); + } else { + y += AndroidUtilities.dp(12); + AndroidUtilities.rectTmp.set(x, y, x + giftRectSize, y + giftRectSize + giftPremiumAdditionalHeight); + } if (backroundRect == null) { backroundRect = new RectF(); } @@ -1523,9 +1773,8 @@ public void setInvalidateColors(boolean invalidate) { invalidate(); } - private int getThemedColor(String key) { - Integer color = themeDelegate != null ? themeDelegate.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, themeDelegate); } private Paint getThemedPaint(String paintKey) { @@ -1541,7 +1790,7 @@ public void drawOutboundsContent(Canvas canvas) { } private boolean isButtonLayout(MessageObject messageObject) { - return messageObject != null && (messageObject.type == MessageObject.TYPE_GIFT_PREMIUM || messageObject.type == MessageObject.TYPE_SUGGEST_PHOTO); + return messageObject != null && (messageObject.type == MessageObject.TYPE_GIFT_PREMIUM || isNewStyleButtonLayout()); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatLoadingCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatLoadingCell.java index b58ab69033..9bca14fc9a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatLoadingCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatLoadingCell.java @@ -48,9 +48,8 @@ public void setProgressVisible(boolean value) { frameLayout.setVisibility(value ? VISIBLE : INVISIBLE); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } private Paint getThemedPaint(String paintKey) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java index 462ac3f1e6..3a5439334d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java @@ -37,6 +37,7 @@ import android.graphics.Typeface; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.RippleDrawable; import android.net.Uri; import android.os.Build; @@ -53,6 +54,7 @@ import android.text.style.CharacterStyle; import android.text.style.ClickableSpan; import android.text.style.URLSpan; +import android.util.Log; import android.util.Property; import android.util.SparseArray; import android.util.StateSet; @@ -82,7 +84,7 @@ import org.telegram.messenger.AccountInstance; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ApplicationLoader; -import org.telegram.messenger.BuildVars; +import org.telegram.messenger.ChatMessageSharedResources; import org.telegram.messenger.ChatObject; import org.telegram.messenger.ContactsController; import org.telegram.messenger.DialogObject; @@ -165,6 +167,9 @@ import org.telegram.ui.PhotoViewer; import org.telegram.ui.PinchToZoomHelper; import org.telegram.ui.SecretMediaViewer; +import org.telegram.ui.Stories.StoriesUtilities; +import org.telegram.ui.Stories.StoryViewer; +import org.telegram.ui.Stories.recorder.DominantColors; import java.io.File; import java.util.ArrayList; @@ -185,6 +190,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate, ImageReceiver.ImageReceiverDelegate, DownloadController.FileDownloadProgressListener, TextSelectionHelper.SelectableView, NotificationCenter.NotificationCenterDelegate { private final static int TIME_APPEAR_MS = 200; + private final static int UPLOADING_ALLOWABLE_ERROR = 1024 * 1024; public boolean clipToGroupBounds; public boolean drawForBlur; @@ -194,6 +200,8 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate float parentBoundsTop; int parentBoundsBottom; + public ExpiredStoryView expiredStoryView; + public RadialProgress2 getRadialProgress() { return radialProgress; } @@ -253,32 +261,32 @@ private void setAvatar(MessageObject messageObject) { } else { currentPhoto = null; } - post(() -> { - avatarDrawable.setInfo(currentUser); - avatarImage.setForUserOrChat(currentUser, avatarDrawable, null, LiteMode.isEnabled(LiteMode.FLAGS_CHAT), VectorAvatarThumbDrawable.TYPE_SMALL); - }); + avatarDrawable.setInfo(currentUser); + avatarImage.setForUserOrChat(currentUser, avatarDrawable, null, LiteMode.isEnabled(LiteMode.FLAGS_CHAT), VectorAvatarThumbDrawable.TYPE_SMALL, false); } else if (currentChat != null) { if (currentChat.photo != null) { currentPhoto = currentChat.photo.photo_small; } else { currentPhoto = null; } - post(() -> { - avatarDrawable.setInfo(currentChat); - avatarImage.setForUserOrChat(currentChat, avatarDrawable); - }); - } else if (messageObject != null && messageObject.isSponsored()) { - if (messageObject.sponsoredChatInvite != null && messageObject.sponsoredChatInvite.chat != null) { - post(() -> { - avatarDrawable.setInfo(messageObject.sponsoredChatInvite.chat); - avatarImage.setForUserOrChat(messageObject.sponsoredChatInvite.chat, avatarDrawable); - }); + avatarDrawable.setInfo(currentChat); + avatarImage.setForUserOrChat(currentChat, avatarDrawable); + } else if (messageObject.isSponsored()) { + if (messageObject.sponsoredWebPage != null) { + avatarDrawable.setInfo(messageObject.sponsoredId[0], messageObject.sponsoredWebPage.site_name, null, null); + TLRPC.Photo photo = messageObject.sponsoredWebPage.photo; + if (photo != null) { + avatarImage.setImage(ImageLocation.getForPhoto(FileLoader.getClosestPhotoSizeWithSize(photo.sizes, AndroidUtilities.dp(50), false, null, true), photo), "50_50", avatarDrawable, null, null, 0); + } + } else if (messageObject.sponsoredChatInvite != null && messageObject.sponsoredChatInvite.chat != null) { + avatarDrawable.setInfo(messageObject.sponsoredChatInvite.chat); + avatarImage.setForUserOrChat(messageObject.sponsoredChatInvite.chat, avatarDrawable); } else { avatarDrawable.setInfo(messageObject.sponsoredChatInvite); if (messageObject.sponsoredChatInvite != null) { TLRPC.Photo photo = messageObject.sponsoredChatInvite.photo; if (photo != null) { - avatarImage.setImage(ImageLocation.getForPhoto(photo.sizes.get(0), photo), "50_50", avatarDrawable, null, null, 0); + avatarImage.setImage(ImageLocation.getForPhoto(FileLoader.getClosestPhotoSizeWithSize(photo.sizes, AndroidUtilities.dp(50), false, null, true), photo), "50_50", avatarDrawable, null, null, 0); } } } @@ -590,12 +598,14 @@ default boolean shouldShowTopicButton() { private final static int DOCUMENT_ATTACH_TYPE_ROUND = 7; private final static int DOCUMENT_ATTACH_TYPE_WALLPAPER = 8; private final static int DOCUMENT_ATTACH_TYPE_THEME = 9; + private final static int DOCUMENT_ATTACH_TYPE_STORY = 10; private class BotButton { private int x; private int y; private int width; private int height; + private int positionFlags; private StaticLayout title; private TLRPC.KeyboardButton button; private TLRPC.TL_reactionCount reaction; @@ -640,6 +650,10 @@ public void onAnimationEnd(Animator animation) { } } + public boolean hasPositionFlag(int flag) { + return (positionFlags & flag) == flag; + } + private float getPressScale() { if (pressed && pressT != 1f) { pressT += (float) Math.min(40, 1000f / AndroidUtilities.screenRefreshRate) / 100f; @@ -714,6 +728,8 @@ public static class PollButton { private ImageReceiver blurredPhotoImage; private AvatarDrawable contactAvatarDrawable; private Drawable locationLoadingThumb; + private Drawable gradientDrawable; + private Paint gradientLoadingPaint; private boolean disallowLongPress; private float lastTouchX; @@ -746,7 +762,7 @@ public static class PollButton { private long lastNamesAnimationTime; private int documentAttachType; private TLRPC.Document documentAttach; - private boolean drawPhotoImage; + public boolean drawPhotoImage; private boolean hasLinkPreview; private boolean hasOldCaptionPreview; private boolean hasGamePreview; @@ -770,7 +786,7 @@ public static class PollButton { private StaticLayout instantViewLayout; private boolean drawInstantView; private boolean pollInstantViewTouchesBottom; - private int drawInstantViewType; + public int drawInstantViewType; private int imageBackgroundColor; private float imageBackgroundIntensity; private int imageBackgroundGradientColor1; @@ -801,6 +817,8 @@ public static class PollButton { private boolean accessibilityTextUnread, accessibilityTextContentUnread; private long accessibilityTextFileSize; private boolean wasTranscriptionOpen; + private Path instantLinkArrowPath; + private Paint instantLinkArrowPaint; private RoundVideoPlayingDrawable roundVideoPlayingDrawable; @@ -820,6 +838,7 @@ public static class PollButton { private StaticLayout infoLayout; private StaticLayout loadingProgressLayout; + private long loadingProgressLayoutHash; private int infoX; private int infoWidth; @@ -902,7 +921,7 @@ class LoadingDrawableLocation { private float transcribeX, transcribeY; private StaticLayout durationLayout; - private int lastTime; + private double lastTime; private int timeWidthAudio; private int timeAudioX; @@ -960,6 +979,8 @@ class LoadingDrawableLocation { private boolean autoPlayingMedia; private ArrayList botButtons = new ArrayList<>(); + private Path botButtonPath = new Path(); + private float[] botButtonRadii = new float[8]; private HashMap botButtonsByData = new HashMap<>(); private HashMap botButtonsByPosition = new HashMap<>(); private String botButtonsLayout; @@ -980,7 +1001,7 @@ class LoadingDrawableLocation { // private int TAG; - private int currentAccount = UserConfig.selectedAccount; + public int currentAccount = UserConfig.selectedAccount; private boolean invalidatesParent; @@ -1007,7 +1028,7 @@ class LoadingDrawableLocation { private boolean isMedia; private boolean isCheckPressed = true; private boolean wasLayout; - private boolean isAvatarVisible; + public boolean isAvatarVisible; private boolean isThreadPost; private boolean drawBackground = true; private int substractBackgroundHeight; @@ -1117,7 +1138,7 @@ class LoadingDrawableLocation { private float drawTimeX; private float drawTimeY; private StaticLayout timeLayout; - private int timeWidth; + public int timeWidth; private int timeTextWidth; private int timeX; private CharSequence currentTimeString; @@ -1275,6 +1296,7 @@ public void run() { private Theme.ResourcesProvider resourcesProvider; private final boolean canDrawBackgroundInParent; + private ChatMessageSharedResources sharedResources; // Public for enter transition public List replySpoilers = new ArrayList<>(); @@ -1293,13 +1315,17 @@ private boolean needHideMessage() { } public ChatMessageCell(Context context) { - this(context, false, null); + this(context, false, null, null); } - public ChatMessageCell(Context context, boolean canDrawBackgroundInParent, Theme.ResourcesProvider resourcesProvider) { + public ChatMessageCell(Context context, boolean canDrawBackgroundInParent, ChatMessageSharedResources sharedResources, Theme.ResourcesProvider resourcesProvider) { super(context); this.resourcesProvider = resourcesProvider; this.canDrawBackgroundInParent = canDrawBackgroundInParent; + this.sharedResources = sharedResources; + if (this.sharedResources == null) { + this.sharedResources = new ChatMessageSharedResources(context); + } backgroundDrawable = new MessageBackgroundDrawable(this); avatarImage = new ImageReceiver(); @@ -1516,7 +1542,7 @@ private int[] getRealSpanStartAndEnd(Spannable buffer, CharacterStyle link) { } private boolean checkTextBlockMotionEvent(MotionEvent event) { - if (!(currentMessageObject.type == MessageObject.TYPE_TEXT || currentMessageObject.type == MessageObject.TYPE_EMOJIS) || currentMessageObject.textLayoutBlocks == null || currentMessageObject.textLayoutBlocks.isEmpty() || !(currentMessageObject.messageText instanceof Spannable)) { + if (!(currentMessageObject.type == MessageObject.TYPE_TEXT || currentMessageObject.type == MessageObject.TYPE_EMOJIS || currentMessageObject.type == MessageObject.TYPE_STORY_MENTION) || currentMessageObject.textLayoutBlocks == null || currentMessageObject.textLayoutBlocks.isEmpty() || !(currentMessageObject.messageText instanceof Spannable)) { return false; } if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_UP && pressedLinkType == 1) { @@ -1829,7 +1855,7 @@ private boolean checkTranscribeButtonMotionEvent(MotionEvent event) { } private boolean checkLinkPreviewMotionEvent(MotionEvent event) { - if (currentMessageObject.type != MessageObject.TYPE_TEXT || !hasLinkPreview) { + if (currentMessageObject.type != MessageObject.TYPE_TEXT && currentMessageObject.type != MessageObject.TYPE_STORY_MENTION || !hasLinkPreview) { return false; } int x = (int) event.getX(); @@ -2267,12 +2293,12 @@ private boolean checkOtherButtonMotionEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { if (currentMessageObject.type == MessageObject.TYPE_PHONE_CALL) { int idx = currentMessageObject.isVideoCall() ? 1 : 0; - if (x >= otherX && x <= otherX + AndroidUtilities.dp(30 + (idx == 0 ? 202 : 200)) && y >= otherY - AndroidUtilities.dp(14) && y <= otherY + AndroidUtilities.dp(50)) { + if (x >= otherX && x <= otherX + AndroidUtilities.dp(30 + (LocaleController.isRTL ? 0 : 200) + (idx == 0 ? 2 : 0)) && y >= otherY - AndroidUtilities.dp(14) && y <= otherY + AndroidUtilities.dp(50)) { otherPressed = true; result = true; selectorDrawableMaskType[0] = 4; if (Build.VERSION.SDK_INT >= 21 && selectorDrawable[0] != null) { - int cx = otherX + AndroidUtilities.dp(idx == 0 ? 202 : 200) + Theme.chat_msgInCallDrawable[idx].getIntrinsicWidth() / 2; + int cx = otherX + AndroidUtilities.dp((LocaleController.isRTL ? 0 : 200) + (idx == 0 ? 2 : 0)) + Theme.chat_msgInCallDrawable[idx].getIntrinsicWidth() / 2; int cy = otherY + Theme.chat_msgInCallDrawable[idx].getIntrinsicHeight() / 2; selectorDrawable[0].setBounds(cx - AndroidUtilities.dp(20), cy - AndroidUtilities.dp(20), cx + AndroidUtilities.dp(20), cy + AndroidUtilities.dp(20)); selectorDrawable[0].setHotspot(x, y); @@ -3172,7 +3198,7 @@ public boolean onTouchEvent(MotionEvent event) { } } } else { - if (delegate != null && currentMessageObject.hasValidReplyMessageObject()) { + if (delegate != null && (currentMessageObject.hasValidReplyMessageObject() || currentMessageObject.isReplyToStory())) { delegate.didPressReplyMessage(this, currentMessageObject.getReplyMsgId()); } } @@ -3377,7 +3403,7 @@ public void updatePlayingMessageProgress() { if (infoLayout != null && (PhotoViewer.isPlayingMessage(currentMessageObject) || MediaController.getInstance().isGoingToShowMessageObject(currentMessageObject))) { return; } - int duration = 0; + double duration = 0; AnimatedFileDrawable animation = photoImage.getAnimation(); if (animation != null) { duration = currentMessageObject.audioPlayerDuration = animation.getDurationMs() / 1000; @@ -3399,7 +3425,7 @@ public void updatePlayingMessageProgress() { } } if (lastTime != duration) { - String str = AndroidUtilities.formatShortDuration(duration); + String str = AndroidUtilities.formatShortDuration((int) duration); infoWidth = (int) Math.ceil(Theme.chat_infoPaint.measureText(str)); infoLayout = new StaticLayout(str, Theme.chat_infoPaint, infoWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); lastTime = duration; @@ -3417,7 +3443,7 @@ public void updatePlayingMessageProgress() { seekBar.clearTimestamps(); } - int duration = 0; + double duration = 0; TLRPC.Document document = currentMessageObject.getDocument(); for (int a = 0; a < document.attributes.size(); a++) { TLRPC.DocumentAttribute attribute = document.attributes.get(a); @@ -3431,7 +3457,7 @@ public void updatePlayingMessageProgress() { } if (lastTime != duration) { lastTime = duration; - String timeString = AndroidUtilities.formatLongDuration(duration); + String timeString = AndroidUtilities.formatLongDuration((int) duration); timeWidthAudio = (int) Math.ceil(Theme.chat_timePaint.measureText(timeString)); durationLayout = new StaticLayout(timeString, Theme.chat_timePaint, timeWidthAudio, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); } @@ -3455,7 +3481,7 @@ public void updatePlayingMessageProgress() { seekBar.updateTimestamps(currentMessageObject, null); } - int duration = 0; + double duration = 0; if (documentAttachType == DOCUMENT_ATTACH_TYPE_AUDIO) { if (!MediaController.getInstance().isPlayingMessage(currentMessageObject)) { for (int a = 0; a < documentAttach.attributes.size(); a++) { @@ -3471,7 +3497,7 @@ public void updatePlayingMessageProgress() { if (lastTime != duration) { lastTime = duration; - String timeString = AndroidUtilities.formatLongDuration(duration); + String timeString = AndroidUtilities.formatLongDuration((int) duration); timeWidthAudio = (int) Math.ceil(Theme.chat_audioTimePaint.measureText(timeString)); durationLayout = new StaticLayout(timeString, Theme.chat_audioTimePaint, timeWidthAudio, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); } @@ -3483,7 +3509,7 @@ public void updatePlayingMessageProgress() { } if (lastTime != currentProgress) { lastTime = currentProgress; - String timeString = AndroidUtilities.formatShortDuration(currentProgress, duration); + String timeString = AndroidUtilities.formatShortDuration(currentProgress, (int) duration); int timeWidth = (int) Math.ceil(Theme.chat_audioTimePaint.measureText(timeString)); durationLayout = new StaticLayout(timeString, Theme.chat_audioTimePaint, timeWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); } @@ -3670,7 +3696,7 @@ private void didClickedImage() { } else if (buttonState == 0 || buttonState == 3) { didPressButton(true, false); } - } else if (currentMessageObject.type == MessageObject.TYPE_GEO) { + } else if (currentMessageObject.type == MessageObject.TYPE_GEO || currentMessageObject.type == MessageObject.TYPE_STORY || currentMessageObject.type == MessageObject.TYPE_STORY_MENTION) { delegate.didPressImage(this, lastTouchX, lastTouchY); } else if (documentAttachType == DOCUMENT_ATTACH_TYPE_DOCUMENT) { if (buttonState == -1) { @@ -3802,7 +3828,7 @@ private void updateCaptionSpoilers() { captionSpoilers.clear(); if (captionLayout != null && !getMessageObject().isSpoilersRevealed) { - SpoilerEffect.addSpoilers(this, captionLayout, captionSpoilersPool, captionSpoilers); + SpoilerEffect.addSpoilers(this, captionLayout, -1, captionOffsetX + captionWidth, captionSpoilersPool, captionSpoilers); } } @@ -4343,6 +4369,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe hasGamePreview = false; hasInvoicePreview = false; instantPressed = commentButtonPressed = false; + gradientDrawable = null; setInstantButtonPressed(false); if (!pollChanged && Build.VERSION.SDK_INT >= 21) { for (int a = 0; a < selectorDrawable.length; a++) { @@ -4418,6 +4445,9 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe imageBackgroundSideColor = 0; mediaBackground = false; isMedia = false; + if (expiredStoryView != null) { + expiredStoryView.visible = false; + } photoImage.animatedFileDrawableRepeatMaxCount = 0; hasPsaHint = messageObject.messageOwner.fwd_from != null && !TextUtils.isEmpty(messageObject.messageOwner.fwd_from.psa_type); if (hasPsaHint) { @@ -4441,29 +4471,8 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe if (messageIdChanged || messageObject.reactionsChanged || wasPlayingRound != isPlayingRound) { messageObject.reactionsChanged = false; - if (currentPosition == null || ((currentPosition.flags & MessageObject.POSITION_FLAG_BOTTOM) != 0)) { + if (!messageObject.isExpiredStory() && (currentPosition == null || ((currentPosition.flags & MessageObject.POSITION_FLAG_BOTTOM) != 0))) { boolean isSmall = !messageObject.shouldDrawReactionsInLayout(); - if (isSmall && messageObject.messageOwner != null && messageObject.messageOwner.reactions != null && messageObject.messageOwner.reactions.results != null) { - int userCount = 0; - int user2Count = 0; - for (int i = 0; i < messageObject.messageOwner.reactions.results.size(); i++) { - TLRPC.ReactionCount reactionCount = messageObject.messageOwner.reactions.results.get(i); - if (reactionCount.count == 2) { - userCount++; - user2Count++; - } else { - if (reactionCount.chosen) { - userCount++; - } else { - user2Count++; - } - } - if (user2Count >= 2 || userCount >= 2) { - isSmall = false; - break; - } - } - } if (currentPosition != null) { reactionsLayoutInBubble.setMessage(groupedMessages.findPrimaryMessageObject(), !messageObject.shouldDrawReactionsInLayout(), resourcesProvider); } else { @@ -4591,7 +4600,28 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe drawCommentNumber = false; } - if (messageObject.type == MessageObject.TYPE_TEXT) { + if (messageObject.isExpiredStory()) { + if (!messageIdChanged) { + requestLayout(); + } + // currentTimeString = null; + drawBackground = true; + if (expiredStoryView == null) { + expiredStoryView = new ExpiredStoryView(); + } + expiredStoryView.visible = true; + expiredStoryView.measure(this); + totalHeight = expiredStoryView.height + AndroidUtilities.dp(8); + backgroundWidth = expiredStoryView.width; + measureTime(currentMessageObject); + backgroundWidth += timeWidth + AndroidUtilities.dp(12); + drawForwardedName = false; + replyNameLayout = null; + replyTextLayout = null; + forwardedNameLayout[0] = null; + forwardedNameLayout[1] = null; + drawName = false; + } else if (messageObject.type == MessageObject.TYPE_TEXT || messageObject.type == MessageObject.TYPE_STORY_MENTION) { drawForwardedName = !isRepliesChat; int maxWidth; @@ -4626,12 +4656,17 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe hasInvoicePreview = MessageObject.getMedia(messageObject.messageOwner) instanceof TLRPC.TL_messageMediaInvoice; hasLinkPreview = !messageObject.isRestrictedMessage && MessageObject.getMedia(messageObject.messageOwner) instanceof TLRPC.TL_messageMediaWebPage && MessageObject.getMedia(messageObject.messageOwner).webpage instanceof TLRPC.TL_webPage; TLRPC.WebPage webpage = hasLinkPreview ? MessageObject.getMedia(messageObject.messageOwner).webpage : null; - drawInstantView = hasLinkPreview && MessageObject.getMedia(messageObject.messageOwner).webpage.cached_page != null; - String siteName = hasLinkPreview ? MessageObject.getMedia(messageObject.messageOwner).webpage.site_name : null; - hasEmbed = hasLinkPreview && !TextUtils.isEmpty(MessageObject.getMedia(messageObject.messageOwner).webpage.embed_url) && !messageObject.isGif() && !"instangram".equalsIgnoreCase(siteName); + if (messageObject.isStoryMention()) { + hasLinkPreview = true; + webpage = messageObject.getStoryMentionWebpage(); + } + drawInstantView = hasLinkPreview && webpage.cached_page != null; + String siteName = hasLinkPreview ? webpage.site_name : null; + hasEmbed = hasLinkPreview && !TextUtils.isEmpty(webpage.embed_url) && !messageObject.isGif() && !"instagram".equalsIgnoreCase(siteName); boolean slideshow = false; String webpageType = webpage != null ? webpage.type : null; TLRPC.Document androidThemeDocument = null; + TLRPC.StoryItem storyItem = null; TLRPC.ThemeSettings androidThemeSettings = null; if (!drawInstantView) { if ("telegram_livestream".equals(webpageType)) { @@ -4652,9 +4687,19 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } else if ("telegram_message".equals(webpageType) || "photo".equals(webpageType) && webpage != null && webpage.url != null && Browser.isInternalUri(Uri.parse(webpage.url), null)) { drawInstantView = true; drawInstantViewType = 3; + } else if ("telegram_community".equals(webpageType) || "telegram_chatlist".equals(webpageType)) { + drawInstantView = true; + drawInstantViewType = 14; + } else if ("telegram_botapp".equals(webpageType)) { + drawInstantView = true; + drawInstantViewType = 15; } else if ("telegram_theme".equals(webpageType)) { for (int b = 0, N2 = MessageObject.getMedia(messageObject.messageOwner).webpage.attributes.size(); b < N2; b++) { - TLRPC.TL_webPageAttributeTheme attribute = MessageObject.getMedia(messageObject.messageOwner).webpage.attributes.get(b); + TLRPC.WebPageAttribute attribute_ = MessageObject.getMedia(messageObject.messageOwner).webpage.attributes.get(b); + if (!(attribute_ instanceof TLRPC.TL_webPageAttributeTheme)) { + continue; + } + TLRPC.TL_webPageAttributeTheme attribute = (TLRPC.TL_webPageAttributeTheme) attribute_; ArrayList documents = attribute.documents; for (int a = 0, N = documents.size(); a < N; a++) { TLRPC.Document document = documents.get(a); @@ -4675,6 +4720,31 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe break; } } + } else if ("telegram_story".equals(webpageType)) { + for (int b = 0, N2 = webpage.attributes.size(); b < N2; b++) { + TLRPC.WebPageAttribute attribute_ = webpage.attributes.get(b); + if (!(attribute_ instanceof TLRPC.TL_webPageAttributeStory)) { + continue; + } + drawInstantView = true; + drawInstantViewType = 17; + TLRPC.TL_webPageAttributeStory attribute = (TLRPC.TL_webPageAttributeStory) attribute_; + storyItem = attribute.storyItem; + if (storyItem != null) { + storyItem.messageId = messageObject.getId(); + storyItem.dialogId = attribute.user_id; + if (storyItem instanceof TLRPC.TL_storyItemDeleted) { + drawInstantView = false; + hasLinkPreview = false; + drawInstantViewType = 0; + } + } else { + drawInstantView = false; + hasLinkPreview = false; + drawInstantViewType = 0; + } + break; + } } else if ("telegram_background".equals(webpageType)) { drawInstantView = true; drawInstantViewType = 6; @@ -4741,8 +4811,8 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } } else if (siteName != null) { siteName = siteName.toLowerCase(); - if ((siteName.equals("instagram") || siteName.equals("twitter") || "telegram_album".equals(webpageType)) && MessageObject.getMedia(messageObject.messageOwner).webpage.cached_page instanceof TLRPC.TL_page && - (MessageObject.getMedia(messageObject.messageOwner).webpage.photo instanceof TLRPC.TL_photo || MessageObject.isVideoDocument(MessageObject.getMedia(messageObject.messageOwner).webpage.document))) { + if ((siteName.equals("instagram") || siteName.equals("twitter") || "telegram_album".equals(webpageType)) && webpage.cached_page instanceof TLRPC.TL_page && + (webpage.photo instanceof TLRPC.TL_photo || MessageObject.isVideoDocument(webpage.document))) { drawInstantView = false; slideshow = true; ArrayList blocks = MessageObject.getMedia(messageObject.messageOwner).webpage.cached_page.blocks; @@ -4829,7 +4899,8 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe if (drawSideButton != 0) { linkPreviewMaxWidth -= AndroidUtilities.dp(20); } - String site_name; + int site_name_additionalWidth = 0; + CharSequence site_name; String title; CharSequence author; String description; @@ -4840,8 +4911,29 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe boolean smallImage; String type; final int smallImageSide = AndroidUtilities.dp(48), smallSideMargin = AndroidUtilities.dp(10); - if (hasLinkPreview) { - TLRPC.TL_webPage webPage = (TLRPC.TL_webPage) MessageObject.getMedia(messageObject.messageOwner).webpage; + if (drawInstantViewType == 17) { + site_name = null; + TLRPC.User user = storyItem == null ? null : MessagesController.getInstance(currentAccount).getUser(storyItem.dialogId); + if (storyItem instanceof TLRPC.TL_storyItemDeleted) { + site_name_additionalWidth += AndroidUtilities.dp(14); + site_name = StoriesUtilities.createExpiredStoryString(); + } else if (user != null) { + site_name = UserObject.getUserName(user); + } else { + site_name = webpage == null ? null : webpage.title; + } + title = null; + description = storyItem != null ? storyItem.caption : (webpage == null ? null : webpage.description); + author = null; + document = storyItem != null && storyItem.media != null && storyItem.media.document != null ? storyItem.media.document : (webpage == null ? null : webpage.document); + photo = storyItem != null && storyItem.media != null && storyItem.media.photo != null ? storyItem.media.photo : (webpage == null ? null : webpage.photo); + webDocument = null; + type = webpage == null ? null : webpage.type; + duration = storyItem != null && storyItem.media != null && storyItem.media.document != null ? (int) MessageObject.getDocumentDuration(storyItem.media.document) : 0; + smallImage = false; + isSmallImage = false; + } else if (hasLinkPreview) { + TLRPC.TL_webPage webPage = (TLRPC.TL_webPage) webpage; site_name = webPage.site_name; title = drawInstantViewType != 6 && drawInstantViewType != 7 ? webPage.title : null; author = drawInstantViewType != 6 && drawInstantViewType != 7 ? webPage.author : null; @@ -4859,7 +4951,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } type = webPage.type; duration = webPage.duration; - if (site_name != null && photo != null && site_name.toLowerCase().equals("instagram")) { + if (site_name != null && photo != null && site_name.toString().toLowerCase().equals("instagram")) { linkPreviewMaxWidth = Math.max(AndroidUtilities.displaySize.y / 3, currentMessageObject.textWidth); } boolean isSmallImageType = "app".equals(type) || "profile".equals(type) || @@ -4920,7 +5012,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe if (site_name != null) { try { - int width = (int) Math.ceil(Theme.chat_replyNamePaint.measureText(site_name) + 1); + int width = (int) Math.ceil(Theme.chat_replyNamePaint.measureText(site_name.toString()) + 1 + site_name_additionalWidth); int restLines = 0; if (!isSmallImage) { siteNameLayout = new StaticLayout(site_name, Theme.chat_replyNamePaint, Math.min(width, linkPreviewMaxWidth), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); @@ -5064,12 +5156,13 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe totalHeight += AndroidUtilities.dp(2); } int restLines = 0; - boolean allowAllLines = site_name != null && site_name.toLowerCase().equals("twitter"); + boolean allowAllLines = site_name != null && site_name.toString().toLowerCase().equals("twitter"); + boolean isRTL = AndroidUtilities.isRTL(messageObject.linkDescription); if (restLinesCount == 3 && !isSmallImage) { descriptionLayout = StaticLayoutEx.createStaticLayout(messageObject.linkDescription, Theme.chat_replyTextPaint, linkPreviewMaxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, AndroidUtilities.dp(1), false, TextUtils.TruncateAt.END, linkPreviewMaxWidth, allowAllLines ? 100 : 6); } else { restLines = restLinesCount; - descriptionLayout = generateStaticLayout(messageObject.linkDescription, Theme.chat_replyTextPaint, linkPreviewMaxWidth, linkPreviewMaxWidth - smallImageSide - smallSideMargin, restLinesCount, allowAllLines ? 100 : 6); + descriptionLayout = generateStaticLayout(messageObject.linkDescription, Theme.chat_replyTextPaint, linkPreviewMaxWidth - (isRTL ? smallImageSide : 0), linkPreviewMaxWidth - smallImageSide - (isRTL ? 0 : smallSideMargin), restLinesCount, allowAllLines ? 100 : 6); } animatedEmojiDescriptionStack = AnimatedEmojiSpan.update(AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES, this, false, animatedEmojiDescriptionStack, descriptionLayout); int height = descriptionLayout.getLineBottom(descriptionLayout.getLineCount() - 1); @@ -5131,7 +5224,45 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } int maxPhotoWidth = smallImage ? smallImageSide : linkPreviewMaxWidth; - if (document != null) { + if (drawInstantViewType == 17) { + if (storyItem != null && storyItem.media != null) { + int side = (int) (maxPhotoWidth * 16f / 9f); + if (storyItem.media.document != null) { + document = storyItem.media.document; + currentPhotoObject = FileLoader.getClosestPhotoSizeWithSize(document.thumbs, side); + if (currentMessageObject.strippedThumb == null) { + currentPhotoObjectThumb = FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 40); + } else { + currentPhotoObjectThumbStripped = currentMessageObject.strippedThumb; + } + if (currentPhotoObject != null && (currentPhotoObject.w == 0 || currentPhotoObject.h == 0)) { + for (int a = 0; a < document.attributes.size(); a++) { + TLRPC.DocumentAttribute attribute = document.attributes.get(a); + if (attribute instanceof TLRPC.TL_documentAttributeImageSize) { + currentPhotoObject.w = attribute.w; + currentPhotoObject.h = attribute.h; + break; + } + } + if (currentPhotoObject.w == 0 || currentPhotoObject.h == 0) { + currentPhotoObject.w = AndroidUtilities.dp(150); + currentPhotoObject.h = (int) (currentPhotoObject.w / 9f * 16f); + } + } + documentAttach = document; + } else if (storyItem.media.photo != null) { + photo = storyItem.media.photo; + currentPhotoObject = FileLoader.getClosestPhotoSizeWithSize(photo.sizes, side); + if (currentMessageObject.strippedThumb == null) { + currentPhotoObjectThumb = FileLoader.getClosestPhotoSizeWithSize(photo.sizes, 40); + } else { + currentPhotoObjectThumbStripped = currentMessageObject.strippedThumb; + } + } + photoParentObject = storyItem; + documentAttachType = DOCUMENT_ATTACH_TYPE_STORY; + } + } else if (document != null) { if (MessageObject.isRoundVideoDocument(document)) { currentPhotoObject = FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 90); photoParentObject = document; @@ -5364,6 +5495,8 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } else if (documentAttachType == DOCUMENT_ATTACH_TYPE_ROUND) { maxPhotoWidth = AndroidUtilities.roundMessageSize; photoImage.setAllowDecodeSingleFrame(true); + } else if (documentAttachType == DOCUMENT_ATTACH_TYPE_STORY) { + maxPhotoWidth /= 2; } if (hasInvoicePreview && maxPhotoWidth < messageObject.textWidth) { maxPhotoWidth = messageObject.textWidth + AndroidUtilities.dp(22); @@ -5420,7 +5553,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe float scale = width / (float) (maxPhotoWidth - AndroidUtilities.dp(2)); width /= scale; height /= scale; - if (site_name == null || site_name != null && !site_name.toLowerCase().equals("instagram") && documentAttachType == 0) { + if (site_name == null || site_name != null && !site_name.toString().toLowerCase().equals("instagram") && documentAttachType == 0) { if (height > AndroidUtilities.displaySize.y / 3) { height = AndroidUtilities.displaySize.y / 3; } @@ -5477,6 +5610,22 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } else { photoImage.setImage(ImageLocation.getForDocument(currentPhotoObject, document), currentPhotoFilter, ImageLocation.getForDocument(currentPhotoObjectThumb, document), "b1", currentPhotoObjectThumbStripped, 0, "jpg", messageObject, 1); } + } else if (documentAttachType == DOCUMENT_ATTACH_TYPE_STORY) { + if (document != null) { + photoImage.setImage(ImageLocation.getForDocument(currentPhotoObject, document), currentPhotoFilter, ImageLocation.getForDocument(currentPhotoObjectThumb, document), "b1", currentPhotoObjectThumbStripped, 0, "jpg", messageObject, 1); + } else if (photo != null) { + photoImage.setImage(ImageLocation.getForPhoto(currentPhotoObject, photo), currentPhotoFilter, ImageLocation.getForPhoto(currentPhotoObjectThumb, photo), "b1", currentPhotoObjectThumbStripped, 0, "jpg", messageObject, 1); + } + if (currentPhotoObject != null && (currentPhotoObject.gradientTopColor != 0 && currentPhotoObject.gradientBottomColor != 0)) { + gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, new int[] { currentPhotoObject.gradientTopColor, currentPhotoObject.gradientBottomColor }); + } else if (currentPhotoObjectThumbStripped != null && currentPhotoObjectThumbStripped.getBitmap() != null) { + final int[] colors = DominantColors.getColorsSync(false, currentPhotoObjectThumbStripped.getBitmap(), Theme.isCurrentThemeDark()); + gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, colors); + if (currentPhotoObject != null) { + currentPhotoObject.gradientTopColor = colors[0]; + currentPhotoObject.gradientBottomColor = colors[1]; + } + } } else if (documentAttachType == DOCUMENT_ATTACH_TYPE_STICKER) { boolean isWebpSticker = messageObject.isSticker(); if (!SharedConfig.loopStickers() && messageObject.isVideoSticker()) { @@ -5493,7 +5642,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe photoImage.setNeedsQualityThumb(true); photoImage.setShouldGenerateQualityThumb(true); if (SharedConfig.isAutoplayVideo() && (!currentMessageObject.hasMediaSpoilers() || currentMessageObject.isMediaSpoilersRevealed || currentMessageObject.revealingMediaSpoilers) && ( - currentMessageObject.mediaExists || + (currentMessageObject.mediaExists || currentMessageObject.attachPathExists) || messageObject.canStreamVideo() && DownloadController.getInstance(currentAccount).canDownloadMedia(currentMessageObject) )) { photoImage.setAllowDecodeSingleFrame(true); @@ -5555,7 +5704,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } drawPhotoImage = true; - if (type != null && type.equals("video") && duration != 0) { + if ((type != null && type.equals("video") || documentAttachType == DOCUMENT_ATTACH_TYPE_STORY) && duration != 0) { String str = AndroidUtilities.formatShortDuration(duration); durationWidth = (int) Math.ceil(Theme.chat_durationPaint.measureText(str)); videoInfoLayout = new StaticLayout(str, Theme.chat_durationPaint, durationWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); @@ -5690,7 +5839,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } titleLayout = new StaticLayout(TextUtils.ellipsize(text, Theme.chat_audioTitlePaint, maxWidth, TextUtils.TruncateAt.END), Theme.chat_audioTitlePaint, maxWidth + AndroidUtilities.dp(2), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); - docTitleLayout = new StaticLayout(TextUtils.ellipsize(time, Theme.chat_contactPhonePaint, maxWidth, TextUtils.TruncateAt.END), Theme.chat_contactPhonePaint, maxWidth + AndroidUtilities.dp(2), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + docTitleLayout = new StaticLayout(TextUtils.ellipsize(time, Theme.chat_contactPhonePaint, maxWidth - AndroidUtilities.dp(19), TextUtils.TruncateAt.END), Theme.chat_contactPhonePaint, maxWidth + AndroidUtilities.dp(2), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); setMessageObjectInternal(messageObject); @@ -5848,11 +5997,15 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe backgroundWidth = maxWidth = Math.min(getParentWidth() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(270)); } - createDocumentLayout(backgroundWidth, messageObject); + int durationWidth = createDocumentLayout(backgroundWidth, messageObject); setMessageObjectInternal(messageObject); totalHeight = AndroidUtilities.dp(82) + namesOffset; + if (AndroidUtilities.dp(76) + durationWidth >= backgroundWidth - timeWidth - AndroidUtilities.dp(12)) { + totalHeight += AndroidUtilities.dp(14); + } + if (currentPosition != null && currentMessagesGroup != null && currentMessagesGroup.messages.size() > 1) { if ((currentPosition.flags & MessageObject.POSITION_FLAG_TOP) == 0) { totalHeight -= AndroidUtilities.dp(6); @@ -5997,13 +6150,13 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe for (int a = 0; a < pollAvatarImages.length; a++) { if (!isBot && a < size) { pollAvatarImages[a].setImageCoords(0, 0, AndroidUtilities.dp(16), AndroidUtilities.dp(16)); - Long id = media.results.recent_voters.get(a); - TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(id); + TLRPC.Peer id = media.results.recent_voters.get(a); + TLObject user = MessagesController.getInstance(currentAccount).getUserOrChat(DialogObject.getPeerDialogId(id)); if (user != null) { pollAvatarDrawables[a].setInfo(user); pollAvatarImages[a].setForUserOrChat(user, pollAvatarDrawables[a]); } else { - pollAvatarDrawables[a].setInfo(id, "", ""); + pollAvatarDrawables[a].setInfo(DialogObject.getPeerDialogId(id), "", ""); } pollAvatarImagesVisible[a] = true; } else if (!pollUnvoteInProgress || size != 0) { @@ -6192,7 +6345,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } } } else { - drawForwardedName = messageObject.messageOwner.fwd_from != null && !(messageObject.isAnyKindOfSticker() || messageObject.isDice()); + drawForwardedName = (messageObject.messageOwner.fwd_from != null && !(messageObject.isAnyKindOfSticker() && messageObject.isDice())) || messageObject.type == MessageObject.TYPE_STORY; if (!messageObject.isAnyKindOfSticker() && messageObject.type != MessageObject.TYPE_ROUND_VIDEO) { drawName = (messageObject.isFromGroup() && messageObject.isSupergroup() || messageObject.isImportedForward() && messageObject.messageOwner.fwd_from.from_id == null) && (currentPosition == null || (currentPosition.flags & MessageObject.POSITION_FLAG_TOP) != 0); } @@ -6312,7 +6465,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } if (captionLayout != null && currentPosition != null && currentMessagesGroup != null && currentMessagesGroup.isDocuments) { reactionsLayoutInBubble.positionOffsetY += AndroidUtilities.dp(10); - } else if (!drawPhotoImage && !TextUtils.isEmpty(messageObject.caption) && ((docTitleLayout != null && docTitleLayout.getLineCount() > 1) || currentMessageObject.hasValidReplyMessageObject())) { + } else if (!drawPhotoImage && !TextUtils.isEmpty(messageObject.caption) && ((docTitleLayout != null && docTitleLayout.getLineCount() > 1) || currentMessageObject.hasValidReplyMessageObject()) && !currentMessageObject.isForwarded()) { reactionsLayoutInBubble.positionOffsetY += AndroidUtilities.dp(10); } else if (!drawPhotoImage && !TextUtils.isEmpty(messageObject.caption) && !currentMessageObject.isOutOwner()) { reactionsLayoutInBubble.positionOffsetY += AndroidUtilities.dp(10); @@ -6323,7 +6476,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe if (drawPhotoImage && captionLayout == null) { reactionsLayoutInBubble.totalHeight += AndroidUtilities.dp(8); } - if (captionLayout != null && currentMessageObject != null && currentMessageObject.isOutOwner() && currentMessageObject.isDocument() && currentMessagesGroup == null && !currentMessageObject.isForwarded() && !currentMessageObject.isReply()) { + if (!drawPhotoImage && captionLayout != null && currentMessageObject != null && currentMessageObject.isOutOwner() && currentMessageObject.isDocument() && currentMessagesGroup == null && !currentMessageObject.isForwarded() && !currentMessageObject.isReply()) { reactionsLayoutInBubble.positionOffsetY += AndroidUtilities.dp(10); } @@ -6869,6 +7022,10 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe w = infoWidth + AndroidUtilities.dp(16 + 24); } } + if (messageObject.type == MessageObject.TYPE_STORY || messageObject.type == MessageObject.TYPE_STORY_MENTION) { + w = (int) Math.min(AndroidUtilities.displaySize.x * 0.53, AndroidUtilities.dp(300)); + h = (int) (w * 16f / 9f); + } if (commentLayout != null && drawSideButton != 3 && w < totalCommentWidth + AndroidUtilities.dp(10)) { w = totalCommentWidth + AndroidUtilities.dp(10); } @@ -7209,7 +7366,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } if (SharedConfig.isAutoplayVideo() && (!currentMessageObject.hasMediaSpoilers() || currentMessageObject.isMediaSpoilersRevealed || currentMessageObject.revealingMediaSpoilers) && messageObject.type == MessageObject.TYPE_VIDEO && !messageObject.needDrawBluredPreview() && - (currentMessageObject.mediaExists || messageObject.canStreamVideo() && DownloadController.getInstance(currentAccount).canDownloadMedia(currentMessageObject)) + ((currentMessageObject.mediaExists || currentMessageObject.attachPathExists) || messageObject.canStreamVideo() && DownloadController.getInstance(currentAccount).canDownloadMedia(currentMessageObject)) ) { if (currentPosition != null) { autoPlayingMedia = (currentPosition.flags & MessageObject.POSITION_FLAG_LEFT) != 0 && (currentPosition.flags & MessageObject.POSITION_FLAG_RIGHT) != 0; @@ -7232,6 +7389,17 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } photoImage.setImage(ImageLocation.getForDocument(document), ImageLoader.AUTOPLAY_FILTER, ImageLocation.getForObject(currentPhotoObject, photoParentObject), currentPhotoFilter, ImageLocation.getForDocument(currentPhotoObjectThumb, document), currentPhotoFilterThumb, currentPhotoObjectThumbStripped, messageObject.getDocument().size, null, messageObject, 0); } + } else if (messageObject.type == MessageObject.TYPE_STORY || messageObject.type == MessageObject.TYPE_STORY_MENTION) { + TLRPC.StoryItem storyItem = messageObject.messageOwner.media.storyItem; + if (storyItem != null) { + if (storyItem instanceof TLRPC.TL_storyItemDeleted) { + photoImage.setImageBitmap(StoriesUtilities.getExpiredStoryDrawable()); + } else { + StoriesUtilities.setImage(photoImage, storyItem); + } + } else { + photoImage.clearImage(); + } } else if (messageObject.type == MessageObject.TYPE_EXTENDED_MEDIA_PREVIEW) { photoImage.setImage(null, null, ImageLocation.getForObject(currentPhotoObjectThumb, photoParentObject), currentPhotoFilterThumb, currentPhotoObjectThumbStripped, 0, null, currentMessageObject, currentMessageObject.shouldEncryptPhotoOrVideo() ? 2 : 0); } else if (messageObject.type == MessageObject.TYPE_PHOTO) { @@ -7406,7 +7574,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe invalidate(); } - if ((currentPosition == null || currentMessageObject.isMusic() || currentMessageObject.isDocument()) && !messageObject.isAnyKindOfSticker() && addedCaptionHeight == 0) { + if ((currentPosition == null || currentMessageObject.isMusic() || currentMessageObject.isDocument()) && !messageObject.isAnyKindOfSticker() && addedCaptionHeight == 0 && !messageObject.isExpiredStory()) { if (!messageObject.isRestrictedMessage && captionLayout == null && (messageObject.caption != null || messageObject.isVoiceTranscriptionOpen())) { currentCaption = messageObject.isVoiceTranscriptionOpen() ? messageObject.getVoiceTranscription() : messageObject.caption; if (currentCaption != null && !TextUtils.isEmpty(messageObject.messageOwner.voiceTranscription) && currentMessageObject.isVoiceTranscriptionOpen() && !currentMessageObject.messageOwner.voiceTranscriptionFinal) { @@ -7557,7 +7725,9 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe if (messageObject.isSponsored()) { drawInstantView = true; - if (messageObject.sponsoredChannelPost != 0) { + if (messageObject.sponsoredWebPage != null) { + drawInstantViewType = 16; + } else if (messageObject.sponsoredChannelPost != 0) { drawInstantViewType = 12; } else { drawInstantViewType = 1; @@ -7612,14 +7782,14 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } botButtonsByData.clear(); if (messageObject.messageOwner.reply_markup instanceof TLRPC.TL_replyInlineMarkup) { - for (int a = 0; a < rows; a++) { - TLRPC.TL_keyboardButtonRow row = messageObject.messageOwner.reply_markup.rows.get(a); - int buttonsCount = row.buttons.size(); + for (int row = 0; row < rows; row++) { + TLRPC.TL_keyboardButtonRow buttonRow = messageObject.messageOwner.reply_markup.rows.get(row); + int buttonsCount = buttonRow.buttons.size(); if (buttonsCount == 0) { continue; } int buttonWidth = (widthForButtons - AndroidUtilities.dp(5) * (buttonsCount - 1) - AndroidUtilities.dp(2)) / buttonsCount; - for (int b = 0; b < row.buttons.size(); b++) { + for (int column = 0; column < buttonRow.buttons.size(); column++) { BotButton botButton = new BotButton(); TLRPC.TL_keyboardButtonRequestPeer button = new TLRPC.TL_keyboardButtonRequestPeer(); button.button_id = 0; @@ -7634,9 +7804,9 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe // ((TLRPC.TL_requestPeerTypeChat) button.peer_type).user_admin_rights.post_messages = true; // ((TLRPC.TL_requestPeerTypeChat) button.peer_type).premium = false; // botButton.button = button; - botButton.button = row.buttons.get(b); + botButton.button = buttonRow.buttons.get(column); String key = Utilities.bytesToHex(botButton.button.data); - String position = a + "" + b; + String position = buttonRow + "" + column; BotButton oldButton; if (oldByPosition != null) { oldButton = oldByPosition.get(position); @@ -7652,10 +7822,22 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } botButtonsByData.put(key, botButton); botButtonsByPosition.put(position, botButton); - botButton.x = b * (buttonWidth + AndroidUtilities.dp(5)); - botButton.y = a * AndroidUtilities.dp(44 + 4) + AndroidUtilities.dp(2.5f); + botButton.x = column * (buttonWidth + AndroidUtilities.dp(5)); + botButton.y = row * AndroidUtilities.dp(44 + 4) + AndroidUtilities.dp(2.5f); botButton.width = buttonWidth; botButton.height = AndroidUtilities.dp(44); + if (column == 0) { + botButton.positionFlags |= MessageObject.POSITION_FLAG_LEFT; + } + if (column == buttonRow.buttons.size() - 1) { + botButton.positionFlags |= MessageObject.POSITION_FLAG_RIGHT; + } + if (row == 0) { + botButton.positionFlags |= MessageObject.POSITION_FLAG_TOP; + } + if (row == rows - 1) { + botButton.positionFlags |= MessageObject.POSITION_FLAG_BOTTOM; + } CharSequence buttonText; TextPaint botButtonPaint = (TextPaint) getThemedPaint(Theme.key_paint_chatBotButton); if (botButton.button instanceof TLRPC.TL_keyboardButtonBuy && (MessageObject.getMedia(messageObject.messageOwner).flags & 4) != 0) { @@ -7667,7 +7849,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } botButton.title = new StaticLayout(buttonText, botButtonPaint, buttonWidth - AndroidUtilities.dp(10), Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false); botButtons.add(botButton); - if (b == row.buttons.size() - 1) { + if (column == buttonRow.buttons.size() - 1) { maxButtonsWidth = Math.max(maxButtonsWidth, botButton.x + botButton.width); } if (messageObject.isFromUser() && botButton.button instanceof TLRPC.TL_keyboardButtonUrl) { @@ -7689,37 +7871,39 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe substractBackgroundHeight = 0; keyboardHeight = 0; } - if (drawCommentButton) { - totalHeight += AndroidUtilities.dp(shouldDrawTimeOnMedia() ? 41.3f : 43); - createSelectorDrawable(1); - } - if (drawPinnedBottom && drawPinnedTop) { - totalHeight -= AndroidUtilities.dp(2); - } else if (drawPinnedBottom) { - totalHeight -= AndroidUtilities.dp(1); - } else if (drawPinnedTop && pinnedBottom && currentPosition != null && currentPosition.siblingHeights == null) { - totalHeight -= AndroidUtilities.dp(1); - } - if (!mediaBackground) { - if (messageObject.type == MessageObject.TYPE_TEXT) { - totalHeight -= AndroidUtilities.dp(2); + //if (expiredStoryView == null || !expiredStoryView.visible) { + if (drawCommentButton) { + totalHeight += AndroidUtilities.dp(shouldDrawTimeOnMedia() ? 41.3f : 43); + createSelectorDrawable(1); } - if (drawPinnedBottom) { + if (drawPinnedBottom && drawPinnedTop) { + totalHeight -= AndroidUtilities.dp(2); + } else if (drawPinnedBottom) { totalHeight -= AndroidUtilities.dp(1); - } - if (drawPinnedTop) { + } else if (drawPinnedTop && pinnedBottom && currentPosition != null && currentPosition.siblingHeights == null) { totalHeight -= AndroidUtilities.dp(1); } - } - if (messageObject.type != MessageObject.TYPE_EMOJIS) { - if (messageObject.isAnyKindOfSticker() && totalHeight < AndroidUtilities.dp(70)) { - additionalTimeOffsetY = AndroidUtilities.dp(70) - totalHeight; - totalHeight += additionalTimeOffsetY; - } else if (messageObject.isAnimatedEmoji()) { - additionalTimeOffsetY = AndroidUtilities.dp(16); - totalHeight += AndroidUtilities.dp(16); + if (!mediaBackground) { + if (messageObject.type == MessageObject.TYPE_TEXT) { + totalHeight -= AndroidUtilities.dp(2); + } + if (drawPinnedBottom) { + totalHeight -= AndroidUtilities.dp(1); + } + if (drawPinnedTop) { + totalHeight -= AndroidUtilities.dp(1); + } } - } + if (messageObject.type != MessageObject.TYPE_EMOJIS) { + if (messageObject.isAnyKindOfSticker() && totalHeight < AndroidUtilities.dp(70)) { + additionalTimeOffsetY = AndroidUtilities.dp(70) - totalHeight; + totalHeight += additionalTimeOffsetY; + } else if (messageObject.isAnimatedEmoji()) { + additionalTimeOffsetY = AndroidUtilities.dp(16); + totalHeight += AndroidUtilities.dp(16); + } + } + // } if (!drawPhotoImage) { photoImage.setImageBitmap((Drawable) null); @@ -7766,7 +7950,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe if (forwardedNameLayout[0] != null || replyNameLayout != null || drawNameLayout) { tl = tr = minRad; } - if (captionLayout != null || drawCommentButton) { + if (captionLayout != null || drawCommentButton || botButtons != null && !botButtons.isEmpty()) { bl = br = minRad; } if (documentAttachType == DOCUMENT_ATTACH_TYPE_DOCUMENT) { @@ -7806,6 +7990,9 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe if (currentMessageObject.type == MessageObject.TYPE_GEO && MessageObject.getMedia(currentMessageObject) instanceof TLRPC.TL_messageMediaVenue) { br = bl = nearRad; } + if (documentAttachType == DOCUMENT_ATTACH_TYPE_STORY) { + tl = tr = br = bl = 0; + } photoImage.setRoundRadius(tl, tr, br, bl); } updateAnimatedEmojis(); @@ -8427,7 +8614,7 @@ private int createDocumentLayout(int maxWidth, MessageObject messageObject) { } if (MessageObject.isVoiceDocument(documentAttach)) { documentAttachType = DOCUMENT_ATTACH_TYPE_AUDIO; - int duration = 0; + double duration = 0; for (int a = 0; a < documentAttach.attributes.size(); a++) { TLRPC.DocumentAttribute attribute = documentAttach.attributes.get(a); if (attribute instanceof TLRPC.TL_documentAttributeAudio) { @@ -8440,7 +8627,7 @@ private int createDocumentLayout(int maxWidth, MessageObject messageObject) { measureTime(messageObject); int minSize = AndroidUtilities.dp(40 + 14 + 20 + 90 + 10) + timeWidth; if (!hasLinkPreview) { - String timeString = AndroidUtilities.formatLongDuration(duration); + String timeString = AndroidUtilities.formatLongDuration((int) duration); int w = (int) Math.ceil(Theme.chat_audioTimePaint.measureText(timeString)); backgroundWidth = Math.min(maxWidth, minSize + w); } @@ -8476,7 +8663,7 @@ private int createDocumentLayout(int maxWidth, MessageObject messageObject) { performerX = -(int) Math.ceil(performerLayout.getLineLeft(0)); } - int duration = 0; + double duration = 0; for (int a = 0; a < documentAttach.attributes.size(); a++) { TLRPC.DocumentAttribute attribute = documentAttach.attributes.get(a); if (attribute instanceof TLRPC.TL_documentAttributeAudio) { @@ -8484,7 +8671,7 @@ private int createDocumentLayout(int maxWidth, MessageObject messageObject) { break; } } - int durationWidth = (int) Math.ceil(Theme.chat_audioTimePaint.measureText(AndroidUtilities.formatShortDuration(duration, duration))); + int durationWidth = (int) Math.ceil(Theme.chat_audioTimePaint.measureText(AndroidUtilities.formatShortDuration((int) duration, (int) duration))); widthBeforeNewTimeLine = backgroundWidth - AndroidUtilities.dp(10 + 76) - durationWidth; availableTimeWidth = backgroundWidth - AndroidUtilities.dp(28); return durationWidth; @@ -8881,6 +9068,14 @@ private void createInstantViewButton() { } else { str = LocaleController.getString("VoipGroupJoinAsLinstener", R.string.VoipGroupJoinAsLinstener); } + } else if (drawInstantViewType == 14) { + str = LocaleController.getString("ViewChatList", R.string.ViewChatList).toUpperCase(); + } else if (drawInstantViewType == 15) { + str = LocaleController.getString(R.string.BotWebAppInstantViewOpen).toUpperCase(); + } else if (drawInstantViewType == 16) { + str = LocaleController.getString("OpenLink").toUpperCase(); + } else if (drawInstantViewType == 17) { + str = LocaleController.getString("ViewStory").toUpperCase(); } else { str = LocaleController.getString("InstantView", R.string.InstantView); } @@ -8955,7 +9150,11 @@ private int getGroupPhotosWidth() { } } - private int getExtraTextX() { + public boolean isUpdating() { + return isUpdating; + } + + int getExtraTextX() { if (SharedConfig.bubbleRadius >= 15) { return AndroidUtilities.dp(2); } else if (SharedConfig.bubbleRadius >= 11) { @@ -8989,8 +9188,16 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto if (timeTextWidth < 0) { timeTextWidth = AndroidUtilities.dp(10); } +<<<<<<< HEAD CharSequence currentTime = Emoji.replaceEmoji(currentTimeString, Theme.chat_timePaint.getFontMetricsInt(), AndroidUtilities.dp(12), false); timeLayout = new StaticLayout(currentTime, Theme.chat_timePaint, timeTextWidth + AndroidUtilities.dp(100), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); +======= + if (currentTimeString != null) { + timeLayout = new StaticLayout(currentTimeString, Theme.chat_timePaint, timeTextWidth + AndroidUtilities.dp(100), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + } else { + timeLayout = null; + } +>>>>>>> off/master if (mediaBackground) { if (currentMessageObject.isOutOwner()) { timeX = layoutWidth - timeWidth - AndroidUtilities.dp(42.0f); @@ -9250,6 +9457,9 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto if (currentMessageObject.type != MessageObject.TYPE_TEXT) { x -= AndroidUtilities.dp(2); } + if (drawInstantViewType == 17) { + x += AndroidUtilities.dp(10) + (instantWidth - photoImage.getImageWidth()) / 2; + } if (!transitionParams.imageChangeBoundsTransition || transitionParams.updatePhotoImageX) { transitionParams.updatePhotoImageX = false; photoImage.setImageCoords((float) x, photoImage.getImageY(), photoImage.getImageWidth(), photoImage.getImageHeight()); @@ -9463,7 +9673,7 @@ private void drawContent(Canvas canvas) { updateSeekBarWaveformWidth(canvas); forceNotDrawTime = currentMessagesGroup != null; photoImage.setPressed((isHighlightedAnimated || isHighlighted) && currentPosition != null ? 2 : 0); - photoImage.setVisible(!PhotoViewer.isShowingImage(currentMessageObject) && !SecretMediaViewer.getInstance().isShowingImage(currentMessageObject), false); + photoImage.setVisible(!PhotoViewer.isShowingImage(currentMessageObject) && !SecretMediaViewer.getInstance().isShowingImage(currentMessageObject) && !StoryViewer.isShowingImage(currentMessageObject), false); if (!photoImage.getVisible()) { mediaWasInvisible = true; timeWasInvisible = true; @@ -9492,8 +9702,8 @@ private void drawContent(Canvas canvas) { videoRadialProgress.setProgressColor(getThemedColor(Theme.key_chat_mediaProgress)); imageDrawn = false; - radialProgress.setCircleCrossfadeColor(null, 0.0f, 1.0f); - if (currentMessageObject.type == MessageObject.TYPE_TEXT || currentMessageObject.type == MessageObject.TYPE_EMOJIS) { + radialProgress.setCircleCrossfadeColor(-1, 0.0f, 1.0f); + if (currentMessageObject.type == MessageObject.TYPE_TEXT || currentMessageObject.type == MessageObject.TYPE_STORY_MENTION || currentMessageObject.type == MessageObject.TYPE_EMOJIS) { if (currentMessageObject.isOutOwner()) { textX = getCurrentBackgroundLeft() + AndroidUtilities.dp(11) + getExtraTextX(); } else { @@ -10096,14 +10306,14 @@ protected void onOpen() { subtitleY = (int) (photoImage.getImageY() + (docTitleLayout != null ? docTitleLayout.getLineBottom(docTitleLayout.getLineCount() - 1) + AndroidUtilities.dp(13) : AndroidUtilities.dp(8))); if (!imageDrawn) { if (currentMessageObject.isOutOwner()) { - radialProgress.setColors(Theme.key_chat_outLoader, Theme.key_chat_outLoaderSelected, Theme.key_chat_outMediaIcon, Theme.key_chat_outMediaIconSelected); + radialProgress.setColorKeys(Theme.key_chat_outLoader, Theme.key_chat_outLoaderSelected, Theme.key_chat_outMediaIcon, Theme.key_chat_outMediaIconSelected); radialProgress.setProgressColor(getThemedColor(isDrawSelectionBackground() ? Theme.key_chat_outFileProgressSelected : Theme.key_chat_outFileProgress)); - videoRadialProgress.setColors(Theme.key_chat_outLoader, Theme.key_chat_outLoaderSelected, Theme.key_chat_outMediaIcon, Theme.key_chat_outMediaIconSelected); + videoRadialProgress.setColorKeys(Theme.key_chat_outLoader, Theme.key_chat_outLoaderSelected, Theme.key_chat_outMediaIcon, Theme.key_chat_outMediaIconSelected); videoRadialProgress.setProgressColor(getThemedColor(isDrawSelectionBackground() ? Theme.key_chat_outFileProgressSelected : Theme.key_chat_outFileProgress)); } else { - radialProgress.setColors(Theme.key_chat_inLoader, Theme.key_chat_inLoaderSelected, Theme.key_chat_inMediaIcon, Theme.key_chat_inMediaIconSelected); + radialProgress.setColorKeys(Theme.key_chat_inLoader, Theme.key_chat_inLoaderSelected, Theme.key_chat_inMediaIcon, Theme.key_chat_inMediaIconSelected); radialProgress.setProgressColor(getThemedColor(isDrawSelectionBackground() ? Theme.key_chat_inFileProgressSelected : Theme.key_chat_inFileProgress)); - videoRadialProgress.setColors(Theme.key_chat_inLoader, Theme.key_chat_inLoaderSelected, Theme.key_chat_inMediaIcon, Theme.key_chat_inMediaIconSelected); + videoRadialProgress.setColorKeys(Theme.key_chat_inLoader, Theme.key_chat_inLoaderSelected, Theme.key_chat_inMediaIcon, Theme.key_chat_inMediaIconSelected); videoRadialProgress.setProgressColor(getThemedColor(isDrawSelectionBackground() ? Theme.key_chat_inFileProgressSelected : Theme.key_chat_inFileProgress)); } @@ -10119,9 +10329,9 @@ protected void onOpen() { rectPath.close(); canvas.drawPath(rectPath, Theme.chat_docBackPaint); } else { - radialProgress.setColors(Theme.key_chat_mediaLoaderPhoto, Theme.key_chat_mediaLoaderPhotoSelected, Theme.key_chat_mediaLoaderPhotoIcon, Theme.key_chat_mediaLoaderPhotoIconSelected); + radialProgress.setColorKeys(Theme.key_chat_mediaLoaderPhoto, Theme.key_chat_mediaLoaderPhotoSelected, Theme.key_chat_mediaLoaderPhotoIcon, Theme.key_chat_mediaLoaderPhotoIconSelected); radialProgress.setProgressColor(getThemedColor(Theme.key_chat_mediaProgress)); - videoRadialProgress.setColors(Theme.key_chat_mediaLoaderPhoto, Theme.key_chat_mediaLoaderPhotoSelected, Theme.key_chat_mediaLoaderPhotoIcon, Theme.key_chat_mediaLoaderPhotoIconSelected); + videoRadialProgress.setColorKeys(Theme.key_chat_mediaLoaderPhoto, Theme.key_chat_mediaLoaderPhotoSelected, Theme.key_chat_mediaLoaderPhotoIcon, Theme.key_chat_mediaLoaderPhotoIconSelected); videoRadialProgress.setProgressColor(getThemedColor(Theme.key_chat_mediaProgress)); if (buttonState == -1 && radialProgress.getIcon() != MediaActionDrawable.ICON_NONE) { @@ -10182,13 +10392,14 @@ protected void onOpen() { } } if (currentMessageObject.type == MessageObject.TYPE_GEO && !(MessageObject.getMedia(currentMessageObject.messageOwner) instanceof TLRPC.TL_messageMediaGeoLive) && currentMapProvider == 2 && photoImage.hasNotThumb()) { - int w = (int) (Theme.chat_redLocationIcon.getIntrinsicWidth() * 0.8f); - int h = (int) (Theme.chat_redLocationIcon.getIntrinsicHeight() * 0.8f); + Drawable redLocationIcon = sharedResources.getRedLocationIcon(); + int w = (int) (redLocationIcon.getIntrinsicWidth() * 0.8f); + int h = (int) (redLocationIcon.getIntrinsicHeight() * 0.8f); int x = (int) (photoImage.getImageX() + (photoImage.getImageWidth() - w) / 2); int y = (int) (photoImage.getImageY() + (photoImage.getImageHeight() / 2 - h) - AndroidUtilities.dp(16) * (1f - CubicBezierInterpolator.EASE_OUT_BACK.getInterpolation(photoImage.getCurrentAlpha()))); - Theme.chat_redLocationIcon.setAlpha((int) (255 * Math.min(1, photoImage.getCurrentAlpha() * 5))); - Theme.chat_redLocationIcon.setBounds(x, y, x + w, y + h); - Theme.chat_redLocationIcon.draw(canvas); + redLocationIcon.setAlpha((int) (255 * Math.min(1, photoImage.getCurrentAlpha() * 5))); + redLocationIcon.setBounds(x, y, x + w, y + h); + redLocationIcon.draw(canvas); if (photoImage.getCurrentAlpha() < 1) { invalidate(); } @@ -10415,14 +10626,45 @@ public void drawLinkPreview(Canvas canvas, float alpha) { if (linkPreviewY != startY) { linkPreviewY += AndroidUtilities.dp(2); } + float tx = 0; + if (!isSmallImage && drawInstantViewType == 17) { + AndroidUtilities.rectTmp2.set(linkX + AndroidUtilities.dp(10), linkPreviewY, linkX + instantWidth, linkPreviewY + (int) photoImage.getImageHeight()); + AndroidUtilities.rectTmp.set(AndroidUtilities.rectTmp2); + if (gradientDrawable == null) { + if (currentPhotoObject != null && currentPhotoObject.gradientTopColor != 0 && currentPhotoObject.gradientBottomColor != 0) { + gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, new int[] {currentPhotoObject.gradientTopColor, currentPhotoObject.gradientBottomColor}); + } else if (photoImage.getBitmap() != null) { + int[] colors = DominantColors.getColorsSync(false, photoImage.getBitmap(), Theme.isCurrentThemeDark()); + gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, colors); + if (currentPhotoObject != null) { + currentPhotoObject.gradientTopColor = colors[0]; + currentPhotoObject.gradientBottomColor = colors[1]; + } + } + } + if (gradientDrawable != null) { + canvas.save(); + if (drillHolePath != null) { + drillHolePath.rewind(); + } else { + drillHolePath = new Path(); + } + drillHolePath.addRoundRect(AndroidUtilities.rectTmp, AndroidUtilities.dp(4), AndroidUtilities.dp(4), Path.Direction.CW); + canvas.clipPath(drillHolePath); + gradientDrawable.setBounds(AndroidUtilities.rectTmp2); + gradientDrawable.draw(canvas); + canvas.restore(); + } + tx = (instantWidth - photoImage.getImageWidth() - AndroidUtilities.dp(10)) / 2f; + } if (imageBackgroundSideColor != 0) { int x = linkX + AndroidUtilities.dp(10); - photoImage.setImageCoords(x + (imageBackgroundSideWidth - photoImage.getImageWidth()) / 2, linkPreviewY, photoImage.getImageWidth(), photoImage.getImageHeight()); + photoImage.setImageCoords(tx + x + (imageBackgroundSideWidth - photoImage.getImageWidth()) / 2, linkPreviewY, photoImage.getImageWidth(), photoImage.getImageHeight()); rect.set(x, photoImage.getImageY(), x + imageBackgroundSideWidth, photoImage.getImageY2()); Theme.chat_instantViewPaint.setColor(ColorUtils.setAlphaComponent(imageBackgroundSideColor, (int) (255 * alpha))); canvas.drawRoundRect(rect, AndroidUtilities.dp(4), AndroidUtilities.dp(4), Theme.chat_instantViewPaint); } else { - photoImage.setImageCoords(linkX + AndroidUtilities.dp(10), linkPreviewY, photoImage.getImageWidth(), photoImage.getImageHeight()); + photoImage.setImageCoords(tx + linkX + AndroidUtilities.dp(10), linkPreviewY, photoImage.getImageWidth(), photoImage.getImageHeight()); } if (imageBackgroundColor != 0) { rect.set(photoImage.getImageX(), photoImage.getImageY(), photoImage.getImageX2(), photoImage.getImageY2()); @@ -10716,7 +10958,8 @@ public void drawLinkPreview(Canvas canvas, float alpha) { invalidate(); } float scale = 0.98f + 0.02f * (1f - instantButtonPressProgress); - if (scale != 1) { + boolean scaleRestore = scale != 1; + if (scaleRestore) { canvas.save(); canvas.scale(scale, scale, instantButtonRect.centerX(), instantButtonRect.centerY()); } @@ -10735,6 +10978,26 @@ public void drawLinkPreview(Canvas canvas, float alpha) { if (drawInstantViewType == 0) { setDrawableBounds(instantDrawable, instantTextLeftX + instantTextX + linkX - AndroidUtilities.dp(15), instantY + AndroidUtilities.dp(11.5f), AndroidUtilities.dp(9), AndroidUtilities.dp(13)); instantDrawable.draw(canvas); + } else if (drawInstantViewType == 16) { + if (instantLinkArrowPaint == null) { + instantLinkArrowPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + instantLinkArrowPaint.setStyle(Paint.Style.STROKE); + instantLinkArrowPaint.setStrokeCap(Paint.Cap.ROUND); + instantLinkArrowPaint.setStrokeJoin(Paint.Join.ROUND); + } + instantLinkArrowPaint.setColor(Theme.chat_instantViewPaint.getColor()); + instantLinkArrowPaint.setStrokeWidth(AndroidUtilities.dpf2(1.333f)); + if (instantLinkArrowPath == null) { + instantLinkArrowPath = new Path(); + } else { + instantLinkArrowPath.rewind(); + } + instantLinkArrowPath.moveTo(instantButtonRect.right - AndroidUtilities.dp(6+5), instantButtonRect.top + AndroidUtilities.dp(6)); + instantLinkArrowPath.lineTo(instantButtonRect.right - AndroidUtilities.dp(6), instantButtonRect.top + AndroidUtilities.dp(6)); + instantLinkArrowPath.lineTo(instantButtonRect.right - AndroidUtilities.dp(6), instantButtonRect.top + AndroidUtilities.dp(6+5)); + instantLinkArrowPath.moveTo(instantButtonRect.right - AndroidUtilities.dpf2(6+5.66f), instantButtonRect.top + AndroidUtilities.dp(6+5.66f)); + instantLinkArrowPath.lineTo(instantButtonRect.right - AndroidUtilities.dp(6), instantButtonRect.top + AndroidUtilities.dp(6)); + canvas.drawPath(instantLinkArrowPath, instantLinkArrowPaint); } if (instantViewLayout != null) { canvas.save(); @@ -10742,7 +11005,7 @@ public void drawLinkPreview(Canvas canvas, float alpha) { instantViewLayout.draw(canvas); canvas.restore(); } - if (scale != 1) { + if (scaleRestore) { canvas.restore(); } } @@ -10786,15 +11049,28 @@ private void drawBotButtons(Canvas canvas, ArrayList botButtons, int canvas.scale(s, s, rect.centerX(), rect.centerY()); } applyServiceShaderMatrix(); - canvas.drawRoundRect(rect, AndroidUtilities.dp(6), AndroidUtilities.dp(6), getThemedPaint(Theme.key_paint_chatActionBackground)); + Arrays.fill(botButtonRadii, AndroidUtilities.dp(Math.min(6.75f, SharedConfig.bubbleRadius))); + if (button.hasPositionFlag(MessageObject.POSITION_FLAG_LEFT | MessageObject.POSITION_FLAG_BOTTOM)) { + botButtonRadii[6] = botButtonRadii[7] = AndroidUtilities.dp(SharedConfig.bubbleRadius); + } + if (button.hasPositionFlag(MessageObject.POSITION_FLAG_RIGHT | MessageObject.POSITION_FLAG_BOTTOM)) { + botButtonRadii[4] = botButtonRadii[5] = AndroidUtilities.dp(SharedConfig.bubbleRadius); + } + + botButtonPath.rewind(); + botButtonPath.addRoundRect(rect, botButtonRadii, Path.Direction.CW); + + canvas.drawPath(botButtonPath, getThemedPaint(Theme.key_paint_chatActionBackground)); if (hasGradientService()) { - canvas.drawRoundRect(rect, AndroidUtilities.dp(6), AndroidUtilities.dp(6), Theme.chat_actionBackgroundGradientDarkenPaint); + canvas.drawPath(botButtonPath, Theme.chat_actionBackgroundGradientDarkenPaint); } boolean drawProgress = (button.button instanceof TLRPC.TL_keyboardButtonCallback || button.button instanceof TLRPC.TL_keyboardButtonGame || button.button instanceof TLRPC.TL_keyboardButtonBuy || button.button instanceof TLRPC.TL_keyboardButtonUrlAuth) && SendMessagesHelper.getInstance(currentAccount).isSendingCallback(currentMessageObject, button.button) || button.button instanceof TLRPC.TL_keyboardButtonRequestGeoLocation && SendMessagesHelper.getInstance(currentAccount).isSendingCurrentLocation(currentMessageObject, button.button) || button.button instanceof TLRPC.TL_keyboardButtonUrl && delegate != null && delegate.isProgressLoading(this, ChatActivity.PROGRESS_BOT_BUTTON) && delegate.getProgressLoadingBotButtonUrl(this) == button.button.url; + canvas.save(); + canvas.clipPath(botButtonPath); if (drawProgress) { if (button.loadingDrawable == null) { button.loadingDrawable = new LoadingDrawable(); @@ -10811,6 +11087,7 @@ private void drawBotButtons(Canvas canvas, ArrayList botButtons, int if (button.loadingDrawable != null && (drawProgress || button.loadingDrawable.isDisappearing())) { rect.inset(AndroidUtilities.dpf2(.625f), AndroidUtilities.dpf2(.625f)); + button.loadingDrawable.setRadii(botButtonRadii); button.loadingDrawable.setBounds(rect); button.loadingDrawable.setColors( Theme.multAlpha(Theme.getColor(Theme.key_chat_serviceBackgroundSelector, resourcesProvider), 1f), @@ -10828,6 +11105,7 @@ private void drawBotButtons(Canvas canvas, ArrayList botButtons, int button.selectorDrawable.setAlpha(0xFF); button.selectorDrawable.draw(canvas); } + canvas.restore(); canvas.save(); canvas.translate(button.x + addX + AndroidUtilities.dp(5), y + (AndroidUtilities.dp(44) - button.title.getLineBottom(button.title.getLineCount() - 1)) / 2); @@ -11052,7 +11330,8 @@ public void updateCaptionLayout() { currentMessageObject.type == MessageObject.TYPE_PHOTO || currentMessageObject.type == MessageObject.TYPE_EXTENDED_MEDIA_PREVIEW || documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO || - currentMessageObject.type == MessageObject.TYPE_GIF + currentMessageObject.type == MessageObject.TYPE_GIF || + currentMessageObject.type == MessageObject.TYPE_STORY ) { float x, y, h; if (transitionParams.imageChangeBoundsTransition) { @@ -11126,9 +11405,9 @@ private int getIconForCurrentState() { return MediaActionDrawable.ICON_PLAY; } else if (documentAttachType == DOCUMENT_ATTACH_TYPE_AUDIO || documentAttachType == DOCUMENT_ATTACH_TYPE_MUSIC) { if (currentMessageObject.isOutOwner()) { - radialProgress.setColors(Theme.key_chat_outLoader, Theme.key_chat_outLoaderSelected, Theme.key_chat_outMediaIcon, Theme.key_chat_outMediaIconSelected); + radialProgress.setColorKeys(Theme.key_chat_outLoader, Theme.key_chat_outLoaderSelected, Theme.key_chat_outMediaIcon, Theme.key_chat_outMediaIconSelected); } else { - radialProgress.setColors(Theme.key_chat_inLoader, Theme.key_chat_inLoaderSelected, Theme.key_chat_inMediaIcon, Theme.key_chat_inMediaIconSelected); + radialProgress.setColorKeys(Theme.key_chat_inLoader, Theme.key_chat_inLoaderSelected, Theme.key_chat_inMediaIcon, Theme.key_chat_inMediaIconSelected); } if (buttonState == 1) { return MediaActionDrawable.ICON_PAUSE; @@ -11141,9 +11420,9 @@ private int getIconForCurrentState() { } else { if (documentAttachType == DOCUMENT_ATTACH_TYPE_DOCUMENT && !drawPhotoImage) { if (currentMessageObject.isOutOwner()) { - radialProgress.setColors(Theme.key_chat_outLoader, Theme.key_chat_outLoaderSelected, Theme.key_chat_outMediaIcon, Theme.key_chat_outMediaIconSelected); + radialProgress.setColorKeys(Theme.key_chat_outLoader, Theme.key_chat_outLoaderSelected, Theme.key_chat_outMediaIcon, Theme.key_chat_outMediaIconSelected); } else { - radialProgress.setColors(Theme.key_chat_inLoader, Theme.key_chat_inLoaderSelected, Theme.key_chat_inMediaIcon, Theme.key_chat_inMediaIconSelected); + radialProgress.setColorKeys(Theme.key_chat_inLoader, Theme.key_chat_inLoaderSelected, Theme.key_chat_inMediaIcon, Theme.key_chat_inMediaIconSelected); } if (buttonState == -1) { return MediaActionDrawable.ICON_FILE; @@ -11153,8 +11432,8 @@ private int getIconForCurrentState() { return MediaActionDrawable.ICON_CANCEL; } } else { - radialProgress.setColors(Theme.key_chat_mediaLoaderPhoto, Theme.key_chat_mediaLoaderPhotoSelected, Theme.key_chat_mediaLoaderPhotoIcon, Theme.key_chat_mediaLoaderPhotoIconSelected); - videoRadialProgress.setColors(Theme.key_chat_mediaLoaderPhoto, Theme.key_chat_mediaLoaderPhotoSelected, Theme.key_chat_mediaLoaderPhotoIcon, Theme.key_chat_mediaLoaderPhotoIconSelected); + radialProgress.setColorKeys(Theme.key_chat_mediaLoaderPhoto, Theme.key_chat_mediaLoaderPhotoSelected, Theme.key_chat_mediaLoaderPhotoIcon, Theme.key_chat_mediaLoaderPhotoIconSelected); + videoRadialProgress.setColorKeys(Theme.key_chat_mediaLoaderPhoto, Theme.key_chat_mediaLoaderPhotoSelected, Theme.key_chat_mediaLoaderPhotoIcon, Theme.key_chat_mediaLoaderPhotoIconSelected); if (buttonState >= 0 && buttonState < 4) { if (buttonState == 0) { return MediaActionDrawable.ICON_DOWNLOAD; @@ -11190,7 +11469,7 @@ private int getIconForCurrentState() { return MediaActionDrawable.ICON_NONE; } - private int getMaxNameWidth() { + public int getMaxNameWidth() { if (documentAttachType == DOCUMENT_ATTACH_TYPE_STICKER || documentAttachType == DOCUMENT_ATTACH_TYPE_WALLPAPER || currentMessageObject.type == MessageObject.TYPE_ROUND_VIDEO) { int maxWidth; if (AndroidUtilities.isTablet()) { @@ -11240,6 +11519,11 @@ public void updateButtonState(boolean ifSame, boolean animated, boolean fromSet) if (currentMessageObject == null) { return; } + if (currentMessageObject.type == MessageObject.TYPE_STORY && currentMessageObject.isVideoStory()) { + buttonState = 2; + getIconForCurrentState(); + return; + } if (animated && (PhotoViewer.isShowingImage(currentMessageObject) || !attachedToWindow)) { animated = false; } @@ -11416,6 +11700,7 @@ public void updateButtonState(boolean ifSame, boolean animated, boolean fromSet) } else if ( currentMessageObject.type == MessageObject.TYPE_TEXT && documentAttachType != DOCUMENT_ATTACH_TYPE_DOCUMENT && + documentAttachType != DOCUMENT_ATTACH_TYPE_STORY && documentAttachType != DOCUMENT_ATTACH_TYPE_GIF && documentAttachType != DOCUMENT_ATTACH_TYPE_VIDEO && documentAttachType != DOCUMENT_ATTACH_TYPE_WALLPAPER && @@ -11498,7 +11783,7 @@ public void updateButtonState(boolean ifSame, boolean animated, boolean fromSet) invalidate(); } else { getIconForCurrentState(); - if (currentMessageObject.isSticker() || currentMessageObject.isAnimatedSticker() || currentMessageObject.isLocation() || currentMessageObject.isGif()) { + if (currentMessageObject.isSticker() || currentMessageObject.isAnimatedSticker() || currentMessageObject.isLocation() || currentMessageObject.isGif() || documentAttachType == DOCUMENT_ATTACH_TYPE_STORY) { buttonState = -1; radialProgress.setIcon(MediaActionDrawable.ICON_NONE, ifSame, false); } else { @@ -11835,7 +12120,9 @@ private void didPressButton(boolean animated, boolean video) { } } } else if (buttonState == 2) { - if (documentAttachType == DOCUMENT_ATTACH_TYPE_ROUND && currentMessageObject != null && currentMessageObject.isVoiceTranscriptionOpen()) { + if (currentMessageObject != null && currentMessageObject.type == MessageObject.TYPE_STORY) { + delegate.didPressImage(this, 0, 0); + } else if (documentAttachType == DOCUMENT_ATTACH_TYPE_ROUND && currentMessageObject != null && currentMessageObject.isVoiceTranscriptionOpen()) { if (miniButtonState == 0) { FileLoader.getInstance(currentAccount).loadFile(documentAttach, currentMessageObject, FileLoader.PRIORITY_NORMAL_UP, 0); currentMessageObject.loadingCancelled = false; @@ -12114,6 +12401,10 @@ public void onProgressUpload(String fileName, long uploadedSize, long totalSize, radialProgress.setIcon(MediaActionDrawable.ICON_CHECK, false, true); } } + + if (lastLoadingSizeTotal > 0 && Math.abs(lastLoadingSizeTotal - totalSize) > UPLOADING_ALLOWABLE_ERROR) { + lastLoadingSizeTotal = totalSize; + } createLoadingProgressLayout(uploadedSize, totalSize); if (currentFocusedVirtualView == -1 && hasFocus()) announceForAccessibility((progress * 100) + "%"); } @@ -12135,6 +12426,11 @@ private void createLoadingProgressLayout(long loadedSize, long totalSize) { loadingProgressLayout = null; return; } + long hash = (loadedSize << 16) + totalSize; + if (loadingProgressLayout != null && loadingProgressLayoutHash == hash) { + return; + } + loadingProgressLayoutHash = hash; if (lastLoadingSizeTotal == 0) { lastLoadingSizeTotal = totalSize; @@ -12185,18 +12481,6 @@ private void createLoadingProgressLayout(long loadedSize, long totalSize) { w = (int) Math.ceil(Theme.chat_infoPaint.measureText(str)); } loadingProgressLayout = new StaticLayout(str, Theme.chat_infoPaint, w, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); -// if (loadingProgressLayout != null) { -// loadingProgressLayout.copyStylesFrom(Theme.chat_infoPaint); -// loadingProgressLayout.setBounds(0, 0, w, (int) Theme.chat_infoPaint.getTextSize()); -// loadingProgressLayout.setText(str, true); -// } else { -// loadingProgressLayout = new AnimatedTextView.AnimatedTextDrawable(false, true, true); -// loadingProgressLayout.setAnimationProperties(0.3f, 0, 140, CubicBezierInterpolator.EASE_OUT); -// loadingProgressLayout.setCallback(this); -// loadingProgressLayout.copyStylesFrom(Theme.chat_infoPaint); -// loadingProgressLayout.setBounds(0, 0, w, (int) Theme.chat_infoPaint.getTextSize()); -// loadingProgressLayout.setText(str, false); -// } } @@ -12386,16 +12670,16 @@ private boolean shouldDrawSelectionOverlay() { currentBackgroundDrawable != null; } - private Integer getSelectionOverlayColor() { + private int getSelectionOverlayColor() { if (resourcesProvider == null) { - return null; + return 0; } return resourcesProvider.getColor(currentMessageObject != null && currentMessageObject.isOut() ? Theme.key_chat_outBubbleSelectedOverlay : Theme.key_chat_inBubbleSelectedOverlay); } private boolean hasSelectionOverlay() { - Integer selectionOverlayColor = getSelectionOverlayColor(); - return selectionOverlayColor != null && selectionOverlayColor != 0xffff0000; + int selectionOverlayColor = getSelectionOverlayColor(); + return selectionOverlayColor != 0 && selectionOverlayColor != 0xffff0000; } private boolean isDrawSelectionBackground() { @@ -12650,60 +12934,69 @@ private void setMessageObjectInternal(MessageObject messageObject) { currentForwardUser = MessagesController.getInstance(currentAccount).getUser(messageObject.messageOwner.fwd_from.from_id.user_id); } } - if (drawForwardedName && messageObject.needDrawForwarded() && (currentPosition == null || currentPosition.minY == 0)) { - if (messageObject.messageOwner.fwd_from.from_name != null) { + if (messageObject.type == MessageObject.TYPE_STORY || (drawForwardedName && messageObject.needDrawForwarded() && (currentPosition == null || currentPosition.minY == 0))) { + if (messageObject.type != MessageObject.TYPE_STORY && messageObject.messageOwner.fwd_from.from_name != null) { currentForwardName = messageObject.messageOwner.fwd_from.from_name; } - if (currentForwardUser != null || currentForwardChannel != null || currentForwardName != null) { - if (currentForwardChannel != null) { - if (currentForwardUser != null) { - currentForwardNameString = String.format("%s (%s)", currentForwardChannel.title, UserObject.getUserName(currentForwardUser)); - } else if (!TextUtils.isEmpty(messageObject.messageOwner.fwd_from.post_author)) { - currentForwardNameString = String.format("%s (%s)", currentForwardChannel.title, messageObject.messageOwner.fwd_from.post_author); + if (messageObject.type == MessageObject.TYPE_STORY || currentForwardUser != null || currentForwardChannel != null || currentForwardName != null) { + String forwardedString; + CharSequence lastLine; + if (messageObject.type == MessageObject.TYPE_STORY) { + currentForwardNameString = forwardedString = LocaleController.getString("ForwardedStory", R.string.ForwardedStory); + lastLine = AndroidUtilities.replaceTags(LocaleController.formatString("ForwardedStoryFrom", R.string.ForwardedStoryFrom, getNameFromDialogId(messageObject.messageOwner.media.user_id))); + forwardedNameWidth = getMaxNameWidth(); + } else { + if (currentForwardChannel != null) { + if (currentForwardUser != null) { + currentForwardNameString = String.format("%s (%s)", currentForwardChannel.title, UserObject.getUserName(currentForwardUser)); + } else if (!TextUtils.isEmpty(messageObject.messageOwner.fwd_from.post_author)) { + currentForwardNameString = String.format("%s (%s)", currentForwardChannel.title, messageObject.messageOwner.fwd_from.post_author); + } else { + currentForwardNameString = currentForwardChannel.title; + } + } else if (currentForwardUser != null) { + currentForwardNameString = UserObject.getUserName(currentForwardUser); } else { - currentForwardNameString = currentForwardChannel.title; + currentForwardNameString = currentForwardName; } - } else if (currentForwardUser != null) { - currentForwardNameString = UserObject.getUserName(currentForwardUser); - } else { - currentForwardNameString = currentForwardName; - } - forwardedNameWidth = getMaxNameWidth(); - String forwardedString = getForwardedMessageText(messageObject); - if (hasPsaHint) { - forwardedNameWidth -= AndroidUtilities.dp(36); - } - String from = LocaleController.getString("From", R.string.From); - String fromFormattedString = LocaleController.getString("FromFormatted", R.string.FromFormatted); - int idx = fromFormattedString.indexOf("%1$s"); - int fromWidth = (int) Math.ceil(Theme.chat_forwardNamePaint.measureText(from + " ")); - CharSequence name = TextUtils.ellipsize(currentForwardNameString.replace('\n', ' '), Theme.chat_replyNamePaint, forwardedNameWidth - fromWidth - viaWidth, TextUtils.TruncateAt.END); - String fromString; - try { - fromString = String.format(fromFormattedString, name); - } catch (Exception e) { - fromString = name.toString(); - } - CharSequence lastLine; - SpannableStringBuilder stringBuilder; - if (viaString != null) { - stringBuilder = new SpannableStringBuilder(String.format("%s %s %s", fromString, LocaleController.getString("ViaBot", R.string.ViaBot), viaUsername)); - viaNameWidth = (int) Math.ceil(Theme.chat_forwardNamePaint.measureText(fromString)); - stringBuilder.setSpan(new TypefaceSpan(AndroidUtilities.getTypeface("fonts/rmedium.ttf")), stringBuilder.length() - viaUsername.length() - 1, stringBuilder.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - } else { - stringBuilder = new SpannableStringBuilder(String.format(fromFormattedString, name)); - } - forwardNameCenterX = fromWidth + (int) Math.ceil(Theme.chat_forwardNamePaint.measureText(name, 0, name.length())) / 2; - if (idx >= 0 && (currentForwardName == null || messageObject.messageOwner.fwd_from.from_id != null)) { - stringBuilder.setSpan(new TypefaceSpan(AndroidUtilities.getTypeface("fonts/rmedium.ttf")), idx, idx + name.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + forwardedNameWidth = getMaxNameWidth(); + forwardedString = getForwardedMessageText(messageObject); + if (hasPsaHint) { + forwardedNameWidth -= AndroidUtilities.dp(36); + } + String from = LocaleController.getString("From", R.string.From); + String fromFormattedString = LocaleController.getString("FromFormatted", R.string.FromFormatted); + int idx = fromFormattedString.indexOf("%1$s"); + int fromWidth = (int) Math.ceil(Theme.chat_forwardNamePaint.measureText(from + " ")); + CharSequence name = TextUtils.ellipsize(currentForwardNameString.replace('\n', ' '), Theme.chat_replyNamePaint, forwardedNameWidth - fromWidth - viaWidth, TextUtils.TruncateAt.END); + String fromString; + try { + fromString = String.format(fromFormattedString, name); + } catch (Exception e) { + fromString = name.toString(); + } + + SpannableStringBuilder stringBuilder; + if (viaString != null) { + stringBuilder = new SpannableStringBuilder(String.format("%s %s %s", fromString, LocaleController.getString("ViaBot", R.string.ViaBot), viaUsername)); + viaNameWidth = (int) Math.ceil(Theme.chat_forwardNamePaint.measureText(fromString)); + stringBuilder.setSpan(new TypefaceSpan(AndroidUtilities.getTypeface("fonts/rmedium.ttf")), stringBuilder.length() - viaUsername.length() - 1, stringBuilder.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } else { + stringBuilder = new SpannableStringBuilder(String.format(fromFormattedString, name)); + } + forwardNameCenterX = fromWidth + (int) Math.ceil(Theme.chat_forwardNamePaint.measureText(name, 0, name.length())) / 2; + if (idx >= 0 && (currentForwardName == null || messageObject.messageOwner.fwd_from.from_id != null)) { + stringBuilder.setSpan(new TypefaceSpan(AndroidUtilities.getTypeface("fonts/rmedium.ttf")), idx, idx + name.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + lastLine = stringBuilder; } - lastLine = stringBuilder; lastLine = TextUtils.ellipsize(lastLine, Theme.chat_forwardNamePaint, forwardedNameWidth, TextUtils.TruncateAt.END); try { lastLine = Emoji.replaceEmoji(lastLine, Theme.chat_forwardNamePaint.getFontMetricsInt(), AndroidUtilities.dp(14), false); - } catch (Exception ignore) {} + } catch (Exception ignore) { + } try { forwardedNameLayout[1] = new StaticLayout(lastLine, Theme.chat_forwardNamePaint, forwardedNameWidth + AndroidUtilities.dp(2), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); lastLine = TextUtils.ellipsize(AndroidUtilities.replaceTags(showForwardDate(messageObject, forwardedString)), Theme.chat_forwardNamePaint, forwardedNameWidth, TextUtils.TruncateAt.END); @@ -12763,7 +13056,7 @@ protected void onClick() { topicButton = null; } - if ((!isThreadChat || messageObject.getReplyTopMsgId(isForum) != 0 || isForumGeneral) && messageObject.hasValidReplyMessageObject() || messageObject.messageOwner.fwd_from != null && messageObject.isDice()) { + if ((!isThreadChat || messageObject.getReplyTopMsgId(isForum) != 0 || isForumGeneral) && messageObject.hasValidReplyMessageObject() || messageObject.messageOwner.fwd_from != null && messageObject.isDice() || (messageObject.messageOwner.reply_to != null && messageObject.messageOwner.reply_to.story_id != 0)) { if (currentPosition == null || currentPosition.minY == 0) { if (!messageObject.isAnyKindOfSticker() && messageObject.type != MessageObject.TYPE_ROUND_VIDEO || messageObject.type == MessageObject.TYPE_EMOJIS) { namesOffset += AndroidUtilities.dp(14) + (Theme.chat_replyTextPaint.getTextSize() + Theme.chat_replyNamePaint.getTextSize()); @@ -12787,7 +13080,22 @@ protected void onClick() { CharSequence stringFinalText = null; String name = null; - if ((!isThreadChat || messageObject.getReplyTopMsgId(isForum) != 0 || isForumGeneral) && messageObject.hasValidReplyMessageObject()) { + if (messageObject.messageOwner.reply_to != null && messageObject.messageOwner.reply_to.story_id != 0) { + name = getNameFromDialogId(messageObject.messageOwner.reply_to.user_id); + if (messageObject.messageOwner.replyStory == null || messageObject.messageOwner.replyStory instanceof TLRPC.TL_storyItemDeleted) { + if (messageObject.messageOwner.replyStory == null) { + stringFinalText = LocaleController.getString("Loading", R.string.Loading); + } else { + stringFinalText = StoriesUtilities.createExpiredStoryString(); + } + needReplyImage = false; + } else { + needReplyImage = true; + StoriesUtilities.setStoryMiniImage(replyImageReceiver, messageObject.messageOwner.replyStory); + stringFinalText = StoriesUtilities.createReplyStoryString(); + maxWidth -= AndroidUtilities.dp(16) + (Theme.chat_replyTextPaint.getTextSize() + Theme.chat_replyNamePaint.getTextSize()); + } + } else if ((!isThreadChat || messageObject.getReplyTopMsgId(isForum) != 0 || isForumGeneral) && messageObject.hasValidReplyMessageObject()) { lastReplyMessage = messageObject.replyMessageObject.messageOwner; int cacheType = 1; int size = 0; @@ -12854,16 +13162,8 @@ protected void onClick() { if (name == null) { long fromId = messageObject.replyMessageObject.getFromChatId(); - if (fromId > 0) { - TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(fromId); - if (user != null) { - name = UserObject.getUserName(user); - } - } else if (fromId < 0) { - TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-fromId); - if (chat != null) { - name = chat.title; - } + if (fromId != 0) { + name = getNameFromDialogId(fromId); } else { TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(messageObject.replyMessageObject.messageOwner.peer_id.channel_id); if (chat != null) { @@ -12957,11 +13257,11 @@ protected void onClick() { forwardNameCenterX = fromWidth + (int) Math.ceil(Theme.chat_replyNamePaint.measureText(ellipsizedText, 0, ellipsizedText.length())) / 2; } } - CharSequence stringFinalName = name == null ? "" : TextUtils.ellipsize(name.replace('\n', ' '), Theme.chat_replyNamePaint, maxWidth, TextUtils.TruncateAt.END); + CharSequence stringFinalName = name; try { stringFinalName = Emoji.replaceEmoji(stringFinalName, Theme.chat_replyNamePaint.getFontMetricsInt(), AndroidUtilities.dp(14), false); - } catch (Exception ignore) { - } + } catch (Exception ignore) {} + stringFinalName = stringFinalName == null ? "" : TextUtils.ellipsize(AndroidUtilities.replaceCharSequence("\n", stringFinalName, " "), Theme.chat_replyNamePaint, maxWidth, TextUtils.TruncateAt.END); try { replyNameWidth = AndroidUtilities.dp(4) + (needReplyImage ? AndroidUtilities.dp(16) + (int) (Theme.chat_replyTextPaint.getTextSize() + Theme.chat_replyNamePaint.getTextSize()) : 0); if (stringFinalName != null) { @@ -12994,7 +13294,7 @@ protected void onClick() { } replySpoilers.clear(); if (getMessageObject().replyMessageObject != null && !getMessageObject().replyMessageObject.isSpoilersRevealed) { - SpoilerEffect.addSpoilers(this, replyTextLayout, replySpoilersPool, replySpoilers); + SpoilerEffect.addSpoilers(this, replyTextLayout, replyTextOffset, replyTextOffset + replyTextWidth, replySpoilersPool, replySpoilers); } animatedEmojiReplyStack = AnimatedEmojiSpan.update(AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES, this, false, animatedEmojiReplyStack, replyTextLayout); } @@ -13033,6 +13333,22 @@ protected void onClick() { requestLayout(); } + private String getNameFromDialogId(long fromId) { + String name = null; + if (fromId > 0) { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(fromId); + if (user != null) { + name = UserObject.getUserName(user); + } + } else if (fromId < 0) { + TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-fromId); + if (chat != null) { + name = chat.title; + } + } + return name; + } + private boolean isNeedAuthorName() { return ( isPinnedChat && currentMessageObject.type == MessageObject.TYPE_TEXT || @@ -13047,6 +13363,9 @@ private String getAuthorName() { } else if (currentChat != null) { return currentChat.title; } else if (currentMessageObject != null && currentMessageObject.isSponsored()) { + if (currentMessageObject.sponsoredWebPage != null) { + return currentMessageObject.sponsoredWebPage.site_name; + } if (currentMessageObject.sponsoredChatInvite != null && currentMessageObject.sponsoredChatInvite.title != null) { return currentMessageObject.sponsoredChatInvite.title; } @@ -13153,6 +13472,7 @@ public void setBackgroundTopY(boolean fromParent) { } } drawable.setTop((int) ((fromParent ? getY() : getTop()) + parentViewTopOffset), w, h, (int) parentViewTopOffset, blurredViewTopOffset, blurredViewBottomOffset, pinnedTop, pinnedBottom || transitionParams.changePinnedBottomProgress != 1); + drawable.setBotButtonsBottom(currentMessageObject != null && currentMessageObject.hasInlineBotButtons()); } } @@ -13171,6 +13491,7 @@ public void setBackgroundTopY(int offset) { } if (drawable != null) drawable.setTop((int) (parentViewTopOffset + offset), w, h, (int) parentViewTopOffset, blurredViewTopOffset, blurredViewBottomOffset, pinnedTop, pinnedBottom || transitionParams.changePinnedBottomProgress != 1); + drawable.setBotButtonsBottom(currentMessageObject != null && currentMessageObject.hasInlineBotButtons()); } float transitionYOffsetForDrawables; @@ -13316,6 +13637,11 @@ protected void onDraw(Canvas canvas) { clipContent = true; } drawContent(canvas); + + if (expiredStoryView != null && expiredStoryView.visible) { + expiredStoryView.draw(canvas, this); + } + if (clipContent) { canvas.restore(); } @@ -13762,6 +14088,7 @@ public void drawBackgroundInternal(Canvas canvas, boolean fromParent) { } } drawable.setTop((int) (getY() + parentViewTopOffset), w, h, (int) parentViewTopOffset, blurredViewTopOffset, blurredViewBottomOffset, pinnedTop, pinnedBottom); + drawable.setBotButtonsBottom(currentMessageObject != null && currentMessageObject.hasInlineBotButtons()); float alpha = !mediaBackground && !pinnedBottom ? transitionParams.changePinnedBottomProgress : (1f - transitionParams.changePinnedBottomProgress); drawable.setAlpha((int) (255 * alpha)); drawable.draw(canvas); @@ -13829,11 +14156,7 @@ private void animateCheckboxTranslation() { public boolean drawBackgroundInParent() { if (canDrawBackgroundInParent && currentMessageObject != null && currentMessageObject.isOutOwner()) { - if (resourcesProvider != null) { - return resourcesProvider.getCurrentColor(Theme.key_chat_outBubbleGradient1) != null; - } else { - return Theme.getColorOrNull(Theme.key_chat_outBubbleGradient1) != null; - } + return getThemedColor(Theme.key_chat_outBubbleGradient1) != 0; } return false; } @@ -14365,6 +14688,7 @@ public void drawBackground(Canvas canvas, int left, int top, int right, int bott if (currentBackgroundDrawable != null) { currentBackgroundDrawable.setTop(keyboardHeight, w, h, (int) parentViewTopOffset, blurredViewTopOffset, blurredViewBottomOffset, pinnedTop, pinnedBottom); + currentBackgroundDrawable.setBotButtonsBottom(currentMessageObject != null && currentMessageObject.hasInlineBotButtons()); Drawable currentBackgroundShadowDrawable = currentBackgroundDrawable.getShadowDrawable(); if (currentBackgroundShadowDrawable != null) { currentBackgroundShadowDrawable.setAlpha((int) (getAlpha() * 255)); @@ -14583,7 +14907,7 @@ public void drawNamesLayout(Canvas canvas, float alpha) { } } - boolean drawForwardedNameLocal = drawForwardedName; + boolean drawForwardedNameLocal = drawForwardedName || currentMessageObject.type == MessageObject.TYPE_STORY; boolean hasReply = replyNameLayout != null; StaticLayout[] forwardedNameLayoutLocal = forwardedNameLayout; float animatingAlpha = 1f; @@ -14844,7 +15168,9 @@ public void drawNamesLayout(Canvas canvas, float alpha) { Theme.chat_replyLinePaint.setColor(getThemedColor(Theme.key_chat_outReplyLine)); Theme.chat_replyNamePaint.setColor(getThemedColor(Theme.key_chat_outReplyNameText)); rippleColor = ColorUtils.setAlphaComponent(getThemedColor(Theme.key_chat_outReplyLine), 0x1e); - if (currentMessageObject.hasValidReplyMessageObject() && (currentMessageObject.replyMessageObject.type == MessageObject.TYPE_TEXT || !TextUtils.isEmpty(currentMessageObject.replyMessageObject.caption)) && !(MessageObject.getMedia(currentMessageObject.replyMessageObject.messageOwner) instanceof TLRPC.TL_messageMediaGame || MessageObject.getMedia(currentMessageObject.replyMessageObject.messageOwner) instanceof TLRPC.TL_messageMediaInvoice)) { + if (currentMessageObject.isReplyToStory()) { + Theme.chat_replyTextPaint.setColor(Theme.chat_replyNamePaint.getColor()); + } else if (currentMessageObject.hasValidReplyMessageObject() && (currentMessageObject.replyMessageObject.type == MessageObject.TYPE_TEXT || !TextUtils.isEmpty(currentMessageObject.replyMessageObject.caption)) && !(MessageObject.getMedia(currentMessageObject.replyMessageObject.messageOwner) instanceof TLRPC.TL_messageMediaGame || MessageObject.getMedia(currentMessageObject.replyMessageObject.messageOwner) instanceof TLRPC.TL_messageMediaInvoice)) { Theme.chat_replyTextPaint.setColor(getThemedColor(Theme.key_chat_outReplyMessageText)); } else { int color = getThemedColor(isDrawSelectionBackground() ? Theme.key_chat_outReplyMediaMessageSelectedText : Theme.key_chat_outReplyMediaMessageText); @@ -14861,7 +15187,9 @@ public void drawNamesLayout(Canvas canvas, float alpha) { Theme.chat_replyNamePaint.setColor(getThemedColor(AvatarDrawable.getNameColorNameForId(currentReplyUserId))); rippleColor = ColorUtils.setAlphaComponent(getThemedColor(AvatarDrawable.getNameColorNameForId(currentReplyUserId)), 0x1e); } - if (currentMessageObject.hasValidReplyMessageObject() && (currentMessageObject.replyMessageObject.type == MessageObject.TYPE_TEXT || !TextUtils.isEmpty(currentMessageObject.replyMessageObject.caption)) && !(MessageObject.getMedia(currentMessageObject.replyMessageObject.messageOwner) instanceof TLRPC.TL_messageMediaGame || MessageObject.getMedia(currentMessageObject.replyMessageObject.messageOwner) instanceof TLRPC.TL_messageMediaInvoice)) { + if (currentMessageObject.isReplyToStory()) { + Theme.chat_replyTextPaint.setColor(Theme.chat_replyNamePaint.getColor()); + } else if (currentMessageObject.hasValidReplyMessageObject() && (currentMessageObject.replyMessageObject.type == MessageObject.TYPE_TEXT || !TextUtils.isEmpty(currentMessageObject.replyMessageObject.caption)) && !(MessageObject.getMedia(currentMessageObject.replyMessageObject.messageOwner) instanceof TLRPC.TL_messageMediaGame || MessageObject.getMedia(currentMessageObject.replyMessageObject.messageOwner) instanceof TLRPC.TL_messageMediaInvoice)) { Theme.chat_replyTextPaint.setColor(getThemedColor(Theme.key_chat_inReplyMessageText)); } else { int color = getThemedColor(isDrawSelectionBackground() ? Theme.key_chat_inReplyMediaMessageSelectedText : Theme.key_chat_inReplyMediaMessageText); @@ -17010,9 +17338,10 @@ public void drawOverlays(Canvas canvas) { int cx = (int) (photoImage.getImageX() + photoImage.getImageWidth() / 2 - AndroidUtilities.dp(31)); cy = (int) (photoImage.getImageY() + photoImage.getImageHeight() / 2 - AndroidUtilities.dp(38) - AndroidUtilities.dp(16) * (1f - CubicBezierInterpolator.EASE_OUT_BACK.getInterpolation(progress))); - setDrawableBounds(Theme.chat_msgAvatarLiveLocationDrawable, cx, cy); - Theme.chat_msgAvatarLiveLocationDrawable.setAlpha((int) (255 * Math.min(1, progress * 5))); - Theme.chat_msgAvatarLiveLocationDrawable.draw(canvas); + Drawable msgAvatarLiveLocation = sharedResources.getAvatarLiveLocation(); + setDrawableBounds(msgAvatarLiveLocation, cx, cy); + msgAvatarLiveLocation.setAlpha((int) (255 * Math.min(1, progress * 5))); + msgAvatarLiveLocation.draw(canvas); locationImageReceiver.setImageCoords(cx + AndroidUtilities.dp(5.0f), cy + AndroidUtilities.dp(5.0f), AndroidUtilities.dp(52), AndroidUtilities.dp(52)); locationImageReceiver.setAlpha(Math.min(1, progress * 5)); @@ -17030,6 +17359,26 @@ public void drawOverlays(Canvas canvas) { } } } else if (currentMessageObject.type == MessageObject.TYPE_PHONE_CALL) { + Drawable icon; + Drawable phone; + int idx = currentMessageObject.isVideoCall() ? 1 : 0; + if (currentMessageObject.isOutOwner()) { + icon = Theme.chat_msgCallUpGreenDrawable; + if (currentMessageObject.isVideoCall()) { + phone = getThemedDrawable(isDrawSelectionBackground() ? Theme.key_drawable_msgOutCallVideoSelected : Theme.key_drawable_msgOutCallVideo); + } else { + phone = getThemedDrawable(isDrawSelectionBackground() ? Theme.key_drawable_msgOutCallAudioSelected : Theme.key_drawable_msgOutCallAudio); + } + } else { + TLRPC.PhoneCallDiscardReason reason = currentMessageObject.messageOwner.action.reason; + if (reason instanceof TLRPC.TL_phoneCallDiscardReasonMissed || reason instanceof TLRPC.TL_phoneCallDiscardReasonBusy) { + icon = Theme.chat_msgCallDownRedDrawable; + } else { + icon = Theme.chat_msgCallDownGreenDrawable; + } + phone = isDrawSelectionBackground() ? Theme.chat_msgInCallSelectedDrawable[idx] : Theme.chat_msgInCallDrawable[idx]; + } + if (currentMessageObject.isOutOwner()) { Theme.chat_audioTitlePaint.setColor(getThemedColor(Theme.key_chat_messageTextOut)); Theme.chat_contactPhonePaint.setColor(getThemedColor(isDrawSelectionBackground() ? Theme.key_chat_outTimeSelectedText : Theme.key_chat_outTimeText)); @@ -17049,6 +17398,9 @@ public void drawOverlays(Canvas canvas) { } } otherX = x; + if (LocaleController.isRTL) { + x += phone.getIntrinsicWidth() + AndroidUtilities.dp(12); + } if (titleLayout != null) { canvas.save(); canvas.translate(x, AndroidUtilities.dp(12) + namesOffset); @@ -17057,30 +17409,12 @@ public void drawOverlays(Canvas canvas) { } if (docTitleLayout != null) { canvas.save(); - canvas.translate(x + AndroidUtilities.dp(19), AndroidUtilities.dp(37) + namesOffset); + canvas.translate(x + AndroidUtilities.dp(LocaleController.isRTL ? -19 : 19), AndroidUtilities.dp(37) + namesOffset); docTitleLayout.draw(canvas); canvas.restore(); } - Drawable icon; - Drawable phone; - int idx = currentMessageObject.isVideoCall() ? 1 : 0; - if (currentMessageObject.isOutOwner()) { - icon = Theme.chat_msgCallUpGreenDrawable; - if (currentMessageObject.isVideoCall()) { - phone = getThemedDrawable(isDrawSelectionBackground() ? Theme.key_drawable_msgOutCallVideoSelected : Theme.key_drawable_msgOutCallVideo); - } else { - phone = getThemedDrawable(isDrawSelectionBackground() ? Theme.key_drawable_msgOutCallAudioSelected : Theme.key_drawable_msgOutCallAudio); - } - } else { - TLRPC.PhoneCallDiscardReason reason = currentMessageObject.messageOwner.action.reason; - if (reason instanceof TLRPC.TL_phoneCallDiscardReasonMissed || reason instanceof TLRPC.TL_phoneCallDiscardReasonBusy) { - icon = Theme.chat_msgCallDownRedDrawable; - } else { - icon = Theme.chat_msgCallDownGreenDrawable; - } - phone = isDrawSelectionBackground() ? Theme.chat_msgInCallSelectedDrawable[idx] : Theme.chat_msgInCallDrawable[idx]; - } - setDrawableBounds(icon, x - AndroidUtilities.dp(1), AndroidUtilities.dp(37) + namesOffset); + + setDrawableBounds(icon, x + (LocaleController.isRTL && docTitleLayout != null ? docTitleLayout.getWidth() - icon.getIntrinsicWidth() : 0) - AndroidUtilities.dp(1), AndroidUtilities.dp(37) + namesOffset); icon.draw(canvas); if (Build.VERSION.SDK_INT >= 21 && selectorDrawable[0] != null && selectorDrawableMaskType[0] == 4) { @@ -17096,7 +17430,7 @@ public void drawOverlays(Canvas canvas) { } else { otherY = AndroidUtilities.dp(19); } - setDrawableBounds(phone, x + AndroidUtilities.dp(idx == 0 ? 201 : 200), otherY); + setDrawableBounds(phone, otherX + AndroidUtilities.dp((LocaleController.isRTL ? 0 : 200) + (idx == 0 ? 1 : 0)), otherY); phone.draw(canvas); } else if (currentMessageObject.type == MessageObject.TYPE_POLL) { long newTime = System.currentTimeMillis(); @@ -17277,7 +17611,7 @@ public void drawOverlays(Canvas canvas) { int alpha = (int) (animatePollAnswerAlpha ? 255 * Math.min((pollUnvoteInProgress ? 1.0f - pollAnimationProgress : pollAnimationProgress) / 0.3f, 1.0f) : 255); if (pollVoted || pollClosed || animatePollAnswerAlpha) { if (lastPoll.quiz && pollVoted && button.chosen) { - String key; + int key; if (button.correct) { key = currentMessageObject.isOutOwner() ? Theme.key_chat_outPollCorrectAnswer : Theme.key_chat_inPollCorrectAnswer; } else { @@ -17356,13 +17690,13 @@ public void drawOverlays(Canvas canvas) { canvas.drawCircle(-AndroidUtilities.dp(22), AndroidUtilities.dp(9), AndroidUtilities.dp(8.5f), Theme.chat_instantViewRectPaint); if (lastPoll.multiple_choice) { int size = AndroidUtilities.dp(8.5f); - String key = Theme.key_checkboxCheck; + int key = Theme.key_checkboxCheck; if (currentMessageObject.isOutOwner()) { if (getThemedColor(key) == 0xffffffff) { key = Theme.key_chat_outBubble; } } - pollCheckBox[a].setColor(null, currentMessageObject.isOutOwner() ? Theme.key_chat_outAudioSeekbarFill : Theme.key_chat_inAudioSeekbarFill, key); + pollCheckBox[a].setColor(-1, currentMessageObject.isOutOwner() ? Theme.key_chat_outAudioSeekbarFill : Theme.key_chat_inAudioSeekbarFill, key); pollCheckBox[a].setBounds(-AndroidUtilities.dp(22) - size / 2, AndroidUtilities.dp(9) - size / 2, size, size); pollCheckBox[a].draw(canvas); } @@ -17547,7 +17881,7 @@ public void drawOverlays(Canvas canvas) { int size = AndroidUtilities.dp(21); mediaCheckBox.setBackgroundType(0); mediaCheckBox.setBounds((int) photoImage.getImageX2() - AndroidUtilities.dp(21 + 4), (int) photoImage.getImageY() + AndroidUtilities.dp(4), size, size); - mediaCheckBox.setColor(null, null, currentMessageObject.isOutOwner() ? Theme.key_chat_outBubbleSelected : Theme.key_chat_inBubbleSelected); + mediaCheckBox.setColor(-1, -1, currentMessageObject.isOutOwner() ? Theme.key_chat_outBubbleSelected : Theme.key_chat_inBubbleSelected); mediaCheckBox.setBackgroundDrawable(null); } mediaCheckBox.draw(canvas); @@ -18112,10 +18446,10 @@ public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) { sb.append("\n"); sb.append(LocaleController.formatString("AccDescrMusicInfo", R.string.AccDescrMusicInfo, currentMessageObject.getMusicAuthor(), currentMessageObject.getMusicTitle())); sb.append(", "); - sb.append(LocaleController.formatDuration(currentMessageObject.getDuration())); + sb.append(LocaleController.formatDuration((int) currentMessageObject.getDuration())); } else if (currentMessageObject.isVoice() || isRoundVideo) { sb.append(", "); - sb.append(LocaleController.formatDuration(currentMessageObject.getDuration())); + sb.append(LocaleController.formatDuration((int) currentMessageObject.getDuration())); sb.append(", "); if (currentMessageObject.isContentUnread()) { sb.append(LocaleController.getString("AccDescrMsgNotPlayed", R.string.AccDescrMsgNotPlayed)); @@ -18148,7 +18482,7 @@ public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) { if (documentAttach != null) { if (documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO) { sb.append(", "); - sb.append(LocaleController.formatDuration(currentMessageObject.getDuration())); + sb.append(LocaleController.formatDuration((int) currentMessageObject.getDuration())); } if (buttonState == 0 || documentAttachType == DOCUMENT_ATTACH_TYPE_DOCUMENT) { sb.append(", "); @@ -19187,10 +19521,14 @@ public boolean animateChange() { } } if (replyTextLayout != lastDrawnReplyTextLayout) { - animateReplyTextLayout = lastDrawnReplyTextLayout; - animateReplyTextOffset = lastReplyTextXOffset; - animateOutAnimateEmojiReply = AnimatedEmojiSpan.update(AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES, ChatMessageCell.this, false, animateOutAnimateEmojiReply, true, lastDrawnReplyTextLayout); - changed = true; + CharSequence newText = replyTextLayout != null ? replyTextLayout.getText() : null; + CharSequence oldText = lastDrawnReplyTextLayout != null ? lastDrawnReplyTextLayout.getText() : null; + if (!TextUtils.equals(newText, oldText)) { + animateReplyTextLayout = lastDrawnReplyTextLayout; + animateReplyTextOffset = lastReplyTextXOffset; + animateOutAnimateEmojiReply = AnimatedEmojiSpan.update(AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES, ChatMessageCell.this, false, animateOutAnimateEmojiReply, true, lastDrawnReplyTextLayout); + changed = true; + } } if (edited && !lastDrawingEdited && timeLayout != null) { String customStr = NaConfig.INSTANCE.getCustomEditedMessage().String(); @@ -19460,6 +19798,7 @@ public void resetAnimation() { animateDrawingTimeAlpha = false; transitionBotButtons.clear(); animateButton = false; + animateReplyTextLayout = null; animateReplies = false; animateRepliesLayout = null; @@ -19523,9 +19862,8 @@ public int createStatusDrawableParams() { } } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + public int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } private Drawable getThemedDrawable(String key) { @@ -19538,12 +19876,12 @@ private Drawable getThemedDrawable(String key) { return ret; } - private Paint getThemedPaint(String paintKey) { + public Paint getThemedPaint(String paintKey) { Paint paint = resourcesProvider != null ? resourcesProvider.getPaint(paintKey) : null; return paint != null ? paint : Theme.getThemePaint(paintKey); } - private boolean hasGradientService() { + public boolean hasGradientService() { return resourcesProvider != null ? resourcesProvider.hasGradientService() : Theme.hasGradientService(); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatUnreadCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatUnreadCell.java index 976c949d30..ec5fb3da99 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatUnreadCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatUnreadCell.java @@ -73,7 +73,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(40), MeasureSpec.EXACTLY)); } - private int getColor(String key) { + private int getColor(int key) { Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; return color != null ? color : Theme.getColor(key); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/CheckBoxCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/CheckBoxCell.java index c771bc6884..ddb0f98f62 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/CheckBoxCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/CheckBoxCell.java @@ -19,7 +19,6 @@ import android.view.View; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; -import android.widget.LinearLayout; import android.widget.TextView; import org.telegram.messenger.AndroidUtilities; @@ -35,24 +34,25 @@ public class CheckBoxCell extends FrameLayout { - public final static int TYPE_CHECK_BOX_ROUND = 4; - + public final static int + TYPE_CHECK_BOX_DEFAULT = 1, + TYPE_CHECK_BOX_ENTER_PHONE = 2, + TYPE_CHECK_BOX_UNKNOWN = 3, + TYPE_CHECK_BOX_ROUND = 4, + TYPE_CHECK_BOX_URL = 5; + private final Theme.ResourcesProvider resourcesProvider; - private TextView textView; - private TextView valueTextView; - private View checkBox; + private final TextView textView; + private final TextView valueTextView; + private final View checkBox; private CheckBoxSquare checkBoxSquare; private CheckBox2 checkBoxRound; + private View collapsedArrow; + + private final int currentType; + private final int checkBoxSize; private boolean needDivider; private boolean isMultiline; - private int currentType; - private int checkBoxSize = 18; - - private LinearLayout contentView; - - private Boolean collapsed; - private View collapsedArrow; - private boolean collapseArrowSet; public CheckBoxCell(Context context, int type) { this(context, type, 17, null); @@ -65,8 +65,7 @@ public CheckBoxCell(Context context, int type, Theme.ResourcesProvider resources public CheckBoxCell(Context context, int type, int padding, Theme.ResourcesProvider resourcesProvider) { super(context); this.resourcesProvider = resourcesProvider; - - currentType = type; + this.currentType = type; textView = new TextView(context) { @Override @@ -82,28 +81,28 @@ public void setText(CharSequence text, BufferType type) { } }; NotificationCenter.listenEmojiLoading(textView); - textView.setTag(getThemedColor(type == 1 || type == 5 ? Theme.key_dialogTextBlack : Theme.key_windowBackgroundWhiteBlackText)); + textView.setTag(getThemedColor(type == TYPE_CHECK_BOX_DEFAULT || type == TYPE_CHECK_BOX_URL ? Theme.key_dialogTextBlack : Theme.key_windowBackgroundWhiteBlackText)); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); textView.setLines(1); textView.setMaxLines(1); textView.setSingleLine(true); textView.setEllipsize(TextUtils.TruncateAt.END); - if (type == 3) { + if (type == TYPE_CHECK_BOX_UNKNOWN) { textView.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL); addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 29, 0, 0, 0)); textView.setPadding(0, 0, 0, AndroidUtilities.dp(3)); } else { textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); - if (type == 2) { + if (type == TYPE_CHECK_BOX_ENTER_PHONE) { addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, (LocaleController.isRTL ? 8 : 29), 0, (LocaleController.isRTL ? 29 : 8), 0)); } else { - int offset = type == 4 ? 56 : 46; - addView(textView, LayoutHelper.createFrame(type == 4 ? LayoutHelper.WRAP_CONTENT : LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, (LocaleController.isRTL ? padding : offset + (padding - 17)), 0, (LocaleController.isRTL ? offset + (padding - 17) : padding), 0)); + int offset = type == TYPE_CHECK_BOX_ROUND ? 56 : 46; + addView(textView, LayoutHelper.createFrame(type == TYPE_CHECK_BOX_ROUND ? LayoutHelper.WRAP_CONTENT : LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, (LocaleController.isRTL ? padding : offset + (padding - 17)), 0, (LocaleController.isRTL ? offset + (padding - 17) : padding), 0)); } } valueTextView = new TextView(context); - valueTextView.setTag(type == 1 || type == 5 ? Theme.key_dialogTextBlue : Theme.key_windowBackgroundWhiteValueText); + valueTextView.setTag(type == TYPE_CHECK_BOX_DEFAULT || type == TYPE_CHECK_BOX_URL ? Theme.key_dialogTextBlue : Theme.key_windowBackgroundWhiteValueText); valueTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); valueTextView.setLines(1); valueTextView.setMaxLines(1); @@ -120,13 +119,13 @@ public void setText(CharSequence text, BufferType type) { checkBoxSize = 21; addView(checkBox, LayoutHelper.createFrame(checkBoxSize, checkBoxSize, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, (LocaleController.isRTL ? 0 : padding), 16, (LocaleController.isRTL ? padding : 0), 0)); } else { - checkBox = checkBoxSquare = new CheckBoxSquare(context, type == 1 || type == 5, resourcesProvider); + checkBox = checkBoxSquare = new CheckBoxSquare(context, type == TYPE_CHECK_BOX_DEFAULT || type == TYPE_CHECK_BOX_URL, resourcesProvider); checkBoxSize = 18; - if (type == 5) { + if (type == TYPE_CHECK_BOX_URL) { addView(checkBox, LayoutHelper.createFrame(checkBoxSize, checkBoxSize, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL, (LocaleController.isRTL ? 0 : padding), 0, (LocaleController.isRTL ? padding : 0), 0)); - } else if (type == 3) { + } else if (type == TYPE_CHECK_BOX_UNKNOWN) { addView(checkBox, LayoutHelper.createFrame(checkBoxSize, checkBoxSize, Gravity.LEFT | Gravity.TOP, 0, 15, 0, 0)); - } else if (type == 2) { + } else if (type == TYPE_CHECK_BOX_ENTER_PHONE) { addView(checkBox, LayoutHelper.createFrame(checkBoxSize, checkBoxSize, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 0, 15, 0, 0)); } else { addView(checkBox, LayoutHelper.createFrame(checkBoxSize, checkBoxSize, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, (LocaleController.isRTL ? 0 : padding), 16, (LocaleController.isRTL ? padding : 0), 0)); @@ -136,9 +135,9 @@ public void setText(CharSequence text, BufferType type) { } public void updateTextColor() { - textView.setTextColor(getThemedColor(currentType == 1 || currentType == 5 ? Theme.key_dialogTextBlack : Theme.key_windowBackgroundWhiteBlackText)); - textView.setLinkTextColor(getThemedColor(currentType == 1 || currentType == 5 ? Theme.key_dialogTextLink : Theme.key_windowBackgroundWhiteLinkText)); - valueTextView.setTextColor(getThemedColor(currentType == 1 || currentType == 5 ? Theme.key_dialogTextBlue : Theme.key_windowBackgroundWhiteValueText)); + textView.setTextColor(getThemedColor(currentType == TYPE_CHECK_BOX_DEFAULT || currentType == TYPE_CHECK_BOX_URL ? Theme.key_dialogTextBlack : Theme.key_windowBackgroundWhiteBlackText)); + textView.setLinkTextColor(getThemedColor(currentType == TYPE_CHECK_BOX_DEFAULT || currentType == TYPE_CHECK_BOX_URL ? Theme.key_dialogTextLink : Theme.key_windowBackgroundWhiteLinkText)); + valueTextView.setTextColor(getThemedColor(currentType == TYPE_CHECK_BOX_DEFAULT || currentType == TYPE_CHECK_BOX_URL ? Theme.key_dialogTextBlue : Theme.key_windowBackgroundWhiteValueText)); } private View click1Container, click2Container; @@ -190,7 +189,6 @@ public void setCollapsed(Boolean collapsed) { collapsedArrow.animate().cancel(); collapsedArrow.animate().rotation(collapsed ? 0 : 180).setDuration(340).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).start(); } - this.collapsed = collapsed; } private void updateCollapseArrowTranslation() { @@ -210,13 +208,12 @@ private void updateCollapseArrowTranslation() { translateX = textView.getLeft() + textWidth + AndroidUtilities.dp(4); } collapsedArrow.setTranslationX(translateX); - collapseArrowSet = true; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); - if (currentType == 3) { + if (currentType == TYPE_CHECK_BOX_UNKNOWN) { valueTextView.measure(MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(10), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(50), MeasureSpec.EXACTLY)); textView.measure(MeasureSpec.makeMeasureSpec(width - AndroidUtilities.dp(34), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(50), MeasureSpec.EXACTLY)); checkBox.measure(MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(checkBoxSize), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(checkBoxSize), MeasureSpec.EXACTLY)); @@ -246,8 +243,8 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } if (collapsedArrow != null) { collapsedArrow.measure( - MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(16), MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(16), MeasureSpec.EXACTLY) + MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(16), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(16), MeasureSpec.EXACTLY) ); } } @@ -259,6 +256,7 @@ public void setTextColor(int color) { public void setText(CharSequence text, String value, boolean checked, boolean divider) { setText(text, value, checked, divider, false); } + public void setText(CharSequence text, String value, boolean checked, boolean divider, boolean animated) { textView.setText(text); if (checkBoxRound != null) { @@ -269,7 +267,6 @@ public void setText(CharSequence text, String value, boolean checked, boolean di valueTextView.setText(value); needDivider = divider; setWillNotDraw(!divider); - collapseArrowSet = false; } public void setPad(int pad) { @@ -286,7 +283,7 @@ public void setPad(int pad) { } } - public void setNeedDivider(boolean needDivider){ + public void setNeedDivider(boolean needDivider) { this.needDivider = needDivider; } @@ -299,7 +296,7 @@ public void setMultiline(boolean value) { textView.setMaxLines(0); textView.setSingleLine(false); textView.setEllipsize(null); - if (currentType != 5) { + if (currentType != TYPE_CHECK_BOX_URL) { textView.setPadding(0, 0, 0, AndroidUtilities.dp(5)); layoutParams.height = LayoutParams.WRAP_CONTENT; layoutParams.topMargin = AndroidUtilities.dp(10); @@ -356,9 +353,9 @@ public View getCheckBoxView() { return checkBox; } - public void setCheckBoxColor(String background, String background1, String check) { + public void setCheckBoxColor(int background, int background1, int check) { if (checkBoxRound != null) { - checkBoxRound.setColor(background,background,check); + checkBoxRound.setColor(background, background, check); } } @@ -366,7 +363,7 @@ public CheckBox2 getCheckBoxRound() { return checkBoxRound; } - public void setSquareCheckBoxColor(String uncheckedColor, String checkedColor, String checkColor) { + public void setSquareCheckBoxColor(int uncheckedColor, int checkedColor, int checkColor) { if (checkBoxSquare != null) { checkBoxSquare.setColors(uncheckedColor, checkedColor, checkColor); } @@ -388,9 +385,8 @@ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { info.setChecked(isChecked()); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } public void setIcon(int icon) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ContextLinkCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ContextLinkCell.java index 1f0b744a7d..32d5c75ae5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ContextLinkCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ContextLinkCell.java @@ -36,7 +36,6 @@ import org.telegram.messenger.ImageLoader; import org.telegram.messenger.ImageLocation; import org.telegram.messenger.ImageReceiver; -import org.telegram.messenger.LiteMode; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MediaController; import org.telegram.messenger.MessageObject; @@ -145,6 +144,7 @@ public ContextLinkCell(Context context, boolean needsCheckBox, Theme.ResourcesPr this.resourcesProvider = resourcesProvider; linkImageView = new ImageReceiver(this); + linkImageView.setAllowLoadingOnAttachedOnly(true); linkImageView.setLayerNum(1); linkImageView.setUseSharedAnimationQueue(true); letterDrawable = new LetterDrawable(resourcesProvider, LetterDrawable.STYLE_DEFAULT); @@ -158,7 +158,7 @@ public ContextLinkCell(Context context, boolean needsCheckBox, Theme.ResourcesPr checkBox = new CheckBox2(context, 21, resourcesProvider); checkBox.setVisibility(INVISIBLE); - checkBox.setColor(null, Theme.key_sharedMedia_photoPlaceholder, Theme.key_checkboxCheck); + checkBox.setColor(-1, Theme.key_sharedMedia_photoPlaceholder, Theme.key_checkboxCheck); checkBox.setDrawUnchecked(false); checkBox.setDrawBackgroundAsArc(1); addView(checkBox, LayoutHelper.createFrame(24, 24, Gravity.RIGHT | Gravity.TOP, 0, 1, 1, 0)); @@ -857,7 +857,7 @@ protected void onDraw(Canvas canvas) { private int getIconForCurrentState() { if (documentAttachType == DOCUMENT_ATTACH_TYPE_AUDIO || documentAttachType == DOCUMENT_ATTACH_TYPE_MUSIC) { - radialProgress.setColors(Theme.key_chat_inLoader, Theme.key_chat_inLoaderSelected, Theme.key_chat_inMediaIcon, Theme.key_chat_inMediaIconSelected); + radialProgress.setColorKeys(Theme.key_chat_inLoader, Theme.key_chat_inLoaderSelected, Theme.key_chat_inMediaIcon, Theme.key_chat_inMediaIconSelected); if (buttonState == 1) { return MediaActionDrawable.ICON_PAUSE; } else if (buttonState == 2) { @@ -867,7 +867,7 @@ private int getIconForCurrentState() { } return MediaActionDrawable.ICON_PLAY; } - radialProgress.setColors(Theme.key_chat_mediaLoaderPhoto, Theme.key_chat_mediaLoaderPhotoSelected, Theme.key_chat_mediaLoaderPhotoIcon, Theme.key_chat_mediaLoaderPhotoIconSelected); + radialProgress.setColorKeys(Theme.key_chat_mediaLoaderPhoto, Theme.key_chat_mediaLoaderPhotoSelected, Theme.key_chat_mediaLoaderPhotoIcon, Theme.key_chat_mediaLoaderPhotoIconSelected); return buttonState == 1 ? MediaActionDrawable.ICON_EMPTY : MediaActionDrawable.ICON_NONE; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java index 860bddab26..86bcba22e1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java @@ -72,6 +72,7 @@ import org.telegram.messenger.UserObject; import org.telegram.messenger.Utilities; import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Adapters.DialogsAdapter; @@ -88,7 +89,6 @@ import org.telegram.ui.Components.Forum.ForumBubbleDrawable; import org.telegram.ui.Components.Forum.ForumUtilities; import org.telegram.ui.Components.Premium.PremiumGradient; -import org.telegram.ui.Components.Premium.PremiumLockIconView; import org.telegram.ui.Components.PullForegroundDrawable; import org.telegram.ui.Components.RLottieDrawable; import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble; @@ -104,6 +104,10 @@ import org.telegram.ui.Components.spoilers.SpoilerEffect; import org.telegram.ui.DialogsActivity; import org.telegram.ui.RightSlidingDialogContainer; +import org.telegram.ui.Stories.StoriesController; +import org.telegram.ui.Stories.StoriesListPlaceProvider; +import org.telegram.ui.Stories.StoriesUtilities; +import org.telegram.ui.Stories.StoryViewer; import java.util.ArrayList; import java.util.Collections; @@ -116,10 +120,12 @@ import tw.nekomimi.nekogram.NekoConfig; import xyz.nextalone.nagram.NaConfig; -public class DialogCell extends BaseCell { +public class DialogCell extends BaseCell implements StoriesListPlaceProvider.AvatarOverlaysView { public boolean drawingForBlur; public boolean collapsed; + public boolean drawArchive = true; + public float rightFragmentOffset; boolean moving; private RLottieDrawable lastDrawTranslationDrawable; private int lastDrawSwipeMessageStringId; @@ -160,8 +166,31 @@ public class DialogCell extends BaseCell { private TimerDrawable timerDrawable; private Paint timerPaint; private Paint timerPaint2; + public final StoriesUtilities.AvatarStoryParams storyParams = new StoriesUtilities.AvatarStoryParams(false) { + @Override + public void openStory(long dialogId, Runnable onDone) { + if (delegate == null) { + return; + } + if (currentDialogFolderId != 0) { + delegate.openHiddenStories(); + } else { + if (delegate != null) { + delegate.openStory(DialogCell.this, onDone); + } + } + } + + @Override + public void onLongPress() { + if (delegate == null) { + return; + } + delegate.showChatPreview(DialogCell.this); + } + }; - private Path thumbPath = new Path(); + private Path thumbPath; private SpoilerEffect thumbSpoiler = new SpoilerEffect(); public void setMoving(boolean moving) { @@ -323,8 +352,8 @@ public static class CustomDialog { private float archiveBackgroundProgress; protected boolean overrideSwipeAction = false; - protected String overrideSwipeActionBackgroundColorKey; - protected String overrideSwipeActionRevealBackgroundColorKey; + protected int overrideSwipeActionBackgroundColorKey; + protected int overrideSwipeActionRevealBackgroundColorKey; protected String overrideSwipeActionStringKey; protected int overrideSwipeActionStringId; protected RLottieDrawable overrideSwipeActionDrawable; @@ -508,12 +537,14 @@ public DialogCell(DialogsActivity fragment, Context context, boolean needCheck, public DialogCell(DialogsActivity fragment, Context context, boolean needCheck, boolean forceThreeLines, int account, Theme.ResourcesProvider resourcesProvider) { super(context); + storyParams.allowLongress = true; this.resourcesProvider = resourcesProvider; parentFragment = fragment; Theme.createDialogsResources(context); avatarImage.setRoundRadius(AndroidUtilities.dp(28)); for (int i = 0; i < thumbImage.length; ++i) { thumbImage[i] = new ImageReceiver(this); + thumbImage[i].ignoreNotifications = true; thumbImage[i].setRoundRadius(AndroidUtilities.dp(2)); thumbImage[i].setAllowLoadingOnAttachedOnly(true); } @@ -521,7 +552,6 @@ public DialogCell(DialogsActivity fragment, Context context, boolean needCheck, currentAccount = account; emojiStatus = new AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable(this, AndroidUtilities.dp(22)); - emojiStatus.center = false; avatarImage.setAllowLoadingOnAttachedOnly(true); } @@ -614,7 +644,7 @@ private void checkTtl() { private void checkChatTheme() { if (message != null && message.messageOwner != null && message.messageOwner.action instanceof TLRPC.TL_messageActionSetChatTheme && lastUnreadState) { TLRPC.TL_messageActionSetChatTheme setThemeAction = (TLRPC.TL_messageActionSetChatTheme) message.messageOwner.action; - ChatThemeController.getInstance(currentAccount).setDialogTheme(currentDialogId, setThemeAction.emoticon, false); + ChatThemeController.getInstance(currentAccount).setDialogTheme(currentDialogId, setThemeAction.emoticon,false); } } @@ -682,6 +712,8 @@ protected void onDetachedFromWindow() { AnimatedEmojiSpan.release(this, animatedEmojiStack2); AnimatedEmojiSpan.release(this, animatedEmojiStack3); AnimatedEmojiSpan.release(this, animatedEmojiStackName); + storyParams.onDetachFromWindow(); + canvasButton = null; } @Override @@ -737,7 +769,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { private int computeHeight() { if (isForumCell() && !isTransitionSupport && !collapsed) { - return AndroidUtilities.dp(useForceThreeLines || SharedConfig.useThreeLinesLayout ? 86 : 91 + (useSeparator ? 1 : 0)); + return AndroidUtilities.dp(useForceThreeLines || SharedConfig.useThreeLinesLayout ? 86 : 91 + (useSeparator ? 1 : 0)); } else { return getCollapsedHeight(); } @@ -812,23 +844,27 @@ public void setPinForced(boolean value) { } private CharSequence formatArchivedDialogNames() { - ArrayList dialogs = MessagesController.getInstance(currentAccount).getDialogs(currentDialogFolderId); + final MessagesController messagesController = MessagesController.getInstance(currentAccount); + ArrayList dialogs = messagesController.getDialogs(currentDialogFolderId); currentDialogFolderDialogsCount = dialogs.size(); SpannableStringBuilder builder = new SpannableStringBuilder(); for (int a = 0, N = dialogs.size(); a < N; a++) { TLRPC.Dialog dialog = dialogs.get(a); TLRPC.User currentUser = null; TLRPC.Chat currentChat = null; + if (messagesController.isHiddenByUndo(dialog.id)) { + continue; + } if (DialogObject.isEncryptedDialog(dialog.id)) { - TLRPC.EncryptedChat encryptedChat = MessagesController.getInstance(currentAccount).getEncryptedChat(DialogObject.getEncryptedChatId(dialog.id)); + TLRPC.EncryptedChat encryptedChat = messagesController.getEncryptedChat(DialogObject.getEncryptedChatId(dialog.id)); if (encryptedChat != null) { - currentUser = MessagesController.getInstance(currentAccount).getUser(encryptedChat.user_id); + currentUser = messagesController.getUser(encryptedChat.user_id); } } else { if (DialogObject.isUserDialog(dialog.id)) { - currentUser = MessagesController.getInstance(currentAccount).getUser(dialog.id); + currentUser = messagesController.getUser(dialog.id); } else { - currentChat = MessagesController.getInstance(currentAccount).getChat(-dialog.id); + currentChat = messagesController.getChat(-dialog.id); } } String title; @@ -856,6 +892,14 @@ private CharSequence formatArchivedDialogNames() { break; } } + if (MessagesController.getInstance(currentAccount).storiesController.getTotalStoriesCount(true) > 0) { + int totalCount; + totalCount = Math.max(1, MessagesController.getInstance(currentAccount).storiesController.getTotalStoriesCount(true)); + if (builder.length() > 0) { + builder.append(", "); + } + builder.append(LocaleController.formatPluralString("Stories", totalCount)); + } return Emoji.replaceEmoji(builder, Theme.dialogs_messagePaint[paintIndex].getFontMetricsInt(), AndroidUtilities.dp(17), false); } @@ -1116,6 +1160,7 @@ public void buildLayout() { drawPremium = MessagesController.getInstance(currentAccount).isPremiumUser(user) && UserConfig.getInstance(currentAccount).clientUserId != user.id && user.id != 0; if (drawPremium) { Long emojiStatusId = UserObject.getEmojiStatusDocumentId(user); + emojiStatus.center = LocaleController.isRTL; if (emojiStatusId != null) { nameLayoutEllipsizeByGradient = true; emojiStatus.set(emojiStatusId, false); @@ -1224,7 +1269,9 @@ public void buildLayout() { currentMessagePaint = Theme.dialogs_messagePrintingPaint[paintIndex]; messageString = LocaleController.getString("HistoryCleared", R.string.HistoryCleared); } else if (message == null) { - if (encryptedChat != null) { + if (currentDialogFolderId != 0) { + messageString = formatArchivedDialogNames(); + } else if (encryptedChat != null) { currentMessagePaint = Theme.dialogs_messagePrintingPaint[paintIndex]; if (encryptedChat instanceof TLRPC.TL_encryptedChatRequested) { messageString = LocaleController.getString("EncryptionProcessing", R.string.EncryptionProcessing); @@ -1342,7 +1389,7 @@ public void buildLayout() { if (!TextUtils.isEmpty(topicName)) { SpannableStringBuilder arrowSpan = new SpannableStringBuilder("-"); ColoredImageSpan coloredImageSpan = new ColoredImageSpan(ContextCompat.getDrawable(ApplicationLoader.applicationContext, R.drawable.msg_mini_forumarrow).mutate()); - coloredImageSpan.setColorKey(useForceThreeLines || SharedConfig.useThreeLinesLayout ? null : Theme.key_chats_nameMessage); + coloredImageSpan.setColorKey(useForceThreeLines || SharedConfig.useThreeLinesLayout ? -1 : Theme.key_chats_nameMessage); arrowSpan.setSpan(coloredImageSpan, 0, 1, 0); SpannableStringBuilder nameSpannableString = new SpannableStringBuilder(); nameSpannableString.append(messageNameString).append(arrowSpan).append(topicName); @@ -1418,10 +1465,7 @@ public void buildLayout() { emoji = "\uD83D\uDCCE "; } if (message.hasHighlightedWords() && !TextUtils.isEmpty(message.messageOwner.message)) { - String str = message.messageTrimmedToHighlight; - if (message.messageTrimmedToHighlight != null) { - str = message.messageTrimmedToHighlight; - } + CharSequence text = message.messageTrimmedToHighlight; int w = getMeasuredWidth() - AndroidUtilities.dp(messagePaddingStart + 23 + 24); if (hasNameInMessage) { if (!TextUtils.isEmpty(messageNameString)) { @@ -1430,12 +1474,15 @@ public void buildLayout() { w -= currentMessagePaint.measureText(": "); } if (w > 0) { - str = AndroidUtilities.ellipsizeCenterEnd(str, message.highlightedWords.get(0), w, currentMessagePaint, 130).toString(); + text = AndroidUtilities.ellipsizeCenterEnd(text, message.highlightedWords.get(0), w, currentMessagePaint, 130).toString(); } - messageString = emoji + str; + messageString = new SpannableStringBuilder(emoji).append(text); } else { SpannableStringBuilder msgBuilder = new SpannableStringBuilder(message.caption); if (message != null && message.messageOwner != null) { + if (message != null) { + message.spoilLoginCode(); + } if (!NekoConfig.showSpoilersDirectly.Bool()) MediaDataController.addTextStyleRuns(message.messageOwner.entities, message.caption, msgBuilder, TextStyleSpan.FLAG_STYLE_SPOILER | TextStyleSpan.FLAG_STYLE_STRIKE); MediaDataController.addAnimatedEmojiSpans(message.messageOwner.entities, msgBuilder, currentMessagePaint == null ? null : currentMessagePaint.getFontMetricsInt()); @@ -1459,6 +1506,22 @@ public void buildLayout() { messageString = message.messageOwner.media.title; } else if (message.type == MessageObject.TYPE_MUSIC) { messageString = String.format("\uD83C\uDFA7 %s - %s", message.getMusicAuthor(), message.getMusicTitle()); + } else if (message.messageOwner.media instanceof TLRPC.TL_messageMediaStory && message.messageOwner.media.via_mention) { + if (message.isOut()) { + long did = message.getDialogId(); + String username = ""; + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(did); + if (user != null) { + username = UserObject.getFirstName(user); + int index; + if ((index = username.indexOf(' ')) >= 0) { + username = username.substring(0, index); + } + } + messageString = LocaleController.formatString("StoryYouMentionInDialog", R.string.StoryYouMentionInDialog, username); + } else { + messageString = LocaleController.getString("StoryMentionInDialog", R.string.StoryMentionInDialog); + } } else { if (message.hasHighlightedWords() && !TextUtils.isEmpty(message.messageOwner.message)){ messageString = message.messageTrimmedToHighlight; @@ -1466,9 +1529,12 @@ public void buildLayout() { messageString = message.messageTrimmedToHighlight; } int w = getMeasuredWidth() - AndroidUtilities.dp(messagePaddingStart + 23 ); - messageString = AndroidUtilities.ellipsizeCenterEnd(messageString, message.highlightedWords.get(0), w, currentMessagePaint, 130).toString(); + messageString = AndroidUtilities.ellipsizeCenterEnd(messageString, message.highlightedWords.get(0), w, currentMessagePaint, 130); } else { SpannableStringBuilder stringBuilder = new SpannableStringBuilder(msgText); + if (message != null) { + message.spoilLoginCode(); + } if (!NekoConfig.showSpoilersDirectly.Bool()) MediaDataController.addTextStyleRuns(message, stringBuilder, TextStyleSpan.FLAG_STYLE_SPOILER | TextStyleSpan.FLAG_STYLE_STRIKE); if (message != null && message.messageOwner != null) { @@ -1482,6 +1548,12 @@ public void buildLayout() { currentMessagePaint = Theme.dialogs_messagePrintingPaint[paintIndex]; } } + if (message.isReplyToStory()) { + SpannableStringBuilder builder = SpannableStringBuilder.valueOf(messageString); + builder.insert(0, "d "); + builder.setSpan(new ColoredImageSpan(ContextCompat.getDrawable(getContext(), R.drawable.msg_mini_replystory).mutate()), 0, 1, 0); + messageString = builder; + } if (thumbsCount > 0) { if (message.hasHighlightedWords() && !TextUtils.isEmpty(message.messageOwner.message)) { messageString = message.messageTrimmedToHighlight; @@ -1711,7 +1783,7 @@ public void buildLayout() { nameLeft += timeWidth; } if (drawNameLock) { - nameWidth -= AndroidUtilities.dp(4) + Theme.dialogs_lockDrawable.getIntrinsicWidth(); + nameWidth -= AndroidUtilities.dp(LocaleController.isRTL ? 8 : 4) + Theme.dialogs_lockDrawable.getIntrinsicWidth(); } if (drawClock) { int w = Theme.dialogs_clockDrawable.getIntrinsicWidth() + AndroidUtilities.dp(5); @@ -1838,7 +1910,7 @@ public void buildLayout() { avatarLeft = AndroidUtilities.dp(10); thumbLeft = avatarLeft + AndroidUtilities.dp(56 + 13); } - avatarImage.setImageCoords(avatarLeft, avatarTop, AndroidUtilities.dp(56), AndroidUtilities.dp(56)); + storyParams.originalAvatarRect.set(avatarLeft, avatarTop, avatarLeft + AndroidUtilities.dp(56), avatarTop + AndroidUtilities.dp(56)); for (int i = 0; i < thumbImage.length; ++i) { thumbImage[i].setImageCoords(thumbLeft + (thumbSize + 2) * i, avatarTop + AndroidUtilities.dp(31) + (twoLinesForName ? AndroidUtilities.dp(20) : 0), AndroidUtilities.dp(18), AndroidUtilities.dp(18)); } @@ -1861,7 +1933,7 @@ public void buildLayout() { avatarLeft = AndroidUtilities.dp(10); thumbLeft = avatarLeft + AndroidUtilities.dp(56 + 11); } - avatarImage.setImageCoords(avatarLeft, avatarTop, AndroidUtilities.dp(54), AndroidUtilities.dp(54)); + storyParams.originalAvatarRect.set(avatarLeft, avatarTop, avatarLeft + AndroidUtilities.dp(54), avatarTop + AndroidUtilities.dp(54)); for (int i = 0; i < thumbImage.length; ++i) { thumbImage[i].setImageCoords(thumbLeft + (thumbSize + 2) * i, avatarTop + AndroidUtilities.dp(30) + (twoLinesForName ? AndroidUtilities.dp(20) : 0), AndroidUtilities.dp(thumbSize), AndroidUtilities.dp(thumbSize)); } @@ -1885,6 +1957,7 @@ public void buildLayout() { errorLeft = AndroidUtilities.dp(11); messageLeft += w; typingLeft += w; + buttonLeft += w; messageNameLeft += w; } } else if (countString != null || mentionString != null || drawReactionMention) { @@ -1899,6 +1972,7 @@ public void buildLayout() { countLeft = AndroidUtilities.dp(20); messageLeft += w; typingLeft += w; + buttonLeft += w; messageNameLeft += w; } drawCount = true; @@ -1920,6 +1994,7 @@ public void buildLayout() { mentionLeft = AndroidUtilities.dp(20) + (countWidth != 0 ? countWidth + AndroidUtilities.dp(18) : 0); messageLeft += w; typingLeft += w; + buttonLeft += w; messageNameLeft += w; } drawMention = true; @@ -1947,6 +2022,7 @@ public void buildLayout() { } messageLeft += w; typingLeft += w; + buttonLeft += w; messageNameLeft += w; } } @@ -1957,6 +2033,7 @@ public void buildLayout() { if (LocaleController.isRTL) { messageLeft += w; typingLeft += w; + buttonLeft += w; messageNameLeft += w; } } @@ -2050,6 +2127,19 @@ public void buildLayout() { } animatedEmojiStack3 = AnimatedEmojiSpan.update(AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES, this, animatedEmojiStack3, buttonLayout); + try { + if (!TextUtils.isEmpty(typingString)) { + if (useForceThreeLines || SharedConfig.useThreeLinesLayout) { + typingLayout = StaticLayoutEx.createStaticLayout(typingString, Theme.dialogs_messagePrintingPaint[paintIndex], messageWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, AndroidUtilities.dp(1), false, TextUtils.TruncateAt.END, messageWidth, typingString != null ? 1 : 2); + } else { + typingString = TextUtils.ellipsize(typingString, currentMessagePaint, messageWidth - AndroidUtilities.dp(12), TextUtils.TruncateAt.END); + typingLayout = new StaticLayout(typingString, Theme.dialogs_messagePrintingPaint[paintIndex], messageWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + } + } + } catch (Exception e) { + FileLog.e(e); + } + try { CharSequence messageStringFinal; if ((useForceThreeLines || SharedConfig.useThreeLinesLayout) && currentDialogFolderId != 0 && currentDialogFolderDialogsCount > 1) { @@ -2075,12 +2165,13 @@ public void buildLayout() { } } + Layout.Alignment align = isForum && LocaleController.isRTL ? Layout.Alignment.ALIGN_OPPOSITE : Layout.Alignment.ALIGN_NORMAL; if (useForceThreeLines || SharedConfig.useThreeLinesLayout) { if (thumbsCount > 0 && messageNameString != null) { messageWidth += AndroidUtilities.dp(5); } messageStringFinal = Emoji.replaceEmoji(messageStringFinal, currentMessagePaint.getFontMetricsInt(), AndroidUtilities.dp(12), false); - messageLayout = StaticLayoutEx.createStaticLayout(messageStringFinal, currentMessagePaint, messageWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, AndroidUtilities.dp(1), false, TextUtils.TruncateAt.END, messageWidth, messageNameString != null ? 1 : 2); + messageLayout = StaticLayoutEx.createStaticLayout(messageStringFinal, currentMessagePaint, messageWidth, align, 1.0f, AndroidUtilities.dp(1), false, TextUtils.TruncateAt.END, messageWidth, messageNameString != null ? 1 : 2); } else { if (thumbsCount > 0) { messageWidth += AndroidUtilities.dp((thumbsCount * (thumbSize + 2) - 2) + 5); @@ -2089,29 +2180,16 @@ public void buildLayout() { } } messageStringFinal = Emoji.replaceEmoji(messageStringFinal,currentMessagePaint.getFontMetricsInt(), AndroidUtilities.dp(12), false); - messageLayout = new StaticLayout(messageStringFinal, currentMessagePaint, messageWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + messageLayout = new StaticLayout(messageStringFinal, currentMessagePaint, messageWidth, align, 1.0f, 0.0f, false); } spoilersPool.addAll(spoilers); spoilers.clear(); if (!NekoConfig.showSpoilersDirectly.Bool()) - SpoilerEffect.addSpoilers(this, messageLayout, spoilersPool, spoilers); + SpoilerEffect.addSpoilers(this, messageLayout, -2, -2, spoilersPool, spoilers); } catch (Exception e) { messageLayout = null; FileLog.e(e); } - - try { - if (!TextUtils.isEmpty(typingString)) { - if (useForceThreeLines || SharedConfig.useThreeLinesLayout) { - typingLayout = StaticLayoutEx.createStaticLayout(typingString, Theme.dialogs_messagePrintingPaint[paintIndex], messageWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, AndroidUtilities.dp(1), false, TextUtils.TruncateAt.END, messageWidth, typingString != null ? 1 : 2); - } else { - typingString = TextUtils.ellipsize(typingString, currentMessagePaint, messageWidth - AndroidUtilities.dp(12), TextUtils.TruncateAt.END); - typingLayout = new StaticLayout(typingString, Theme.dialogs_messagePrintingPaint[paintIndex], messageWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); - } - } - } catch (Exception e) { - FileLog.e(e); - } animatedEmojiStack = AnimatedEmojiSpan.update(AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES, this, animatedEmojiStack, messageLayout); double widthpx; @@ -2188,6 +2266,16 @@ public void buildLayout() { } } } + if (buttonLayout != null) { + int lineCount = buttonLayout.getLineCount(); + if (lineCount > 0) { + int rightpad = Integer.MAX_VALUE; + for (int a = 0; a < lineCount; a++) { + rightpad = (int) Math.min(rightpad, buttonLayout.getWidth() - buttonLayout.getLineRight(a)); + } + buttonLeft += rightpad; + } + } } else { if (nameLayout != null && nameLayout.getLineCount() > 0) { left = nameLayout.getLineRight(0); @@ -2381,7 +2469,7 @@ private CharSequence formatTopicsNames() { if (!MessagesController.getInstance(currentAccount).getTopicsController().endIsReached(chat.id)) { MessagesController.getInstance(currentAccount).getTopicsController().preloadTopics(chat.id); - return "Loading..."; + return LocaleController.getString("Loading", R.string.Loading); } else { return "no created topics"; } @@ -2504,7 +2592,7 @@ public void invalidate() { DialogCell.this.invalidate(); } }; - checkBox.setColor(null, Theme.key_windowBackgroundWhite, Theme.key_checkboxCheck); + checkBox.setColor(-1, Theme.key_windowBackgroundWhite, Theme.key_checkboxCheck); checkBox.setDrawUnchecked(false); checkBox.setDrawBackgroundAsArc(3); addView(checkBox); @@ -2822,7 +2910,7 @@ public boolean update(int mask, boolean animated) { avatarDrawable.setAvatarType(AvatarDrawable.AVATAR_TYPE_SAVED); avatarImage.setImage(null, null, avatarDrawable, null, user, 0); } else { - avatarImage.setForUserOrChat(user, avatarDrawable, null, true, VectorAvatarThumbDrawable.TYPE_SMALL); + avatarImage.setForUserOrChat(user, avatarDrawable, null, true, VectorAvatarThumbDrawable.TYPE_SMALL, false); } } else if (chat != null) { avatarDrawable.setInfo(chat); @@ -2993,10 +3081,10 @@ protected void onDraw(Canvas canvas) { boolean needInvalidate = false; - if ((currentDialogFolderId != 0 || isTopic && forumTopic != null && forumTopic.id == 1) && archivedChatsDrawable != null && archivedChatsDrawable.outProgress == 0.0f && translationX == 0.0f) { + if (drawArchive && (currentDialogFolderId != 0 || isTopic && forumTopic != null && forumTopic.id == 1) && archivedChatsDrawable != null && archivedChatsDrawable.outProgress == 0.0f && translationX == 0.0f) { if (!drawingForBlur) { canvas.save(); - canvas.translate(0, -translateY); + canvas.translate(0, -translateY - rightFragmentOffset); canvas.clipRect(0, 0, getMeasuredWidth(), getMeasuredHeight()); archivedChatsDrawable.draw(canvas); canvas.restore(); @@ -3196,6 +3284,8 @@ protected void onDraw(Canvas canvas) { canvas.drawRoundRect(rect, cornersRadius, cornersRadius, Theme.dialogs_tabletSeletedPaint); } + canvas.save(); + canvas.translate(0, -rightFragmentOffset * rightFragmentOpenedProgress); if (currentDialogFolderId != 0 && (!SharedConfig.archiveHidden || archiveBackgroundProgress != 0)) { Theme.dialogs_pinnedPaint.setColor(AndroidUtilities.getOffsetColor(0, Theme.getColor(Theme.key_chats_pinnedOverlay, resourcesProvider), archiveBackgroundProgress, 1.0f)); Theme.dialogs_pinnedPaint.setAlpha((int) (Theme.dialogs_pinnedPaint.getAlpha() * (1f - rightFragmentOpenedProgress))); @@ -3205,6 +3295,7 @@ protected void onDraw(Canvas canvas) { Theme.dialogs_pinnedPaint.setAlpha((int) (Theme.dialogs_pinnedPaint.getAlpha() * (1f - rightFragmentOpenedProgress))); canvas.drawRect(-xOffset, 0, getMeasuredWidth(), getMeasuredHeight() - translateY, Theme.dialogs_pinnedPaint); } + canvas.restore(); updateHelper.updateAnimationValues(); @@ -3464,7 +3555,8 @@ protected void onDraw(Canvas canvas) { } } - AndroidUtilities.rectTmp.set(buttonLeft + AndroidUtilities.dp(2), buttonTop + AndroidUtilities.dp(2), buttonLeft + buttonLayout.getLineWidth(0) + AndroidUtilities.dp(12), buttonTop + buttonLayout.getHeight()); + float buttonLayoutLeft = buttonLayout.getLineLeft(0); + AndroidUtilities.rectTmp.set(buttonLeft + buttonLayoutLeft + AndroidUtilities.dp(2), buttonTop + AndroidUtilities.dp(2), buttonLeft + buttonLayoutLeft + buttonLayout.getLineWidth(0) + AndroidUtilities.dp(12), buttonTop + buttonLayout.getHeight()); AndroidUtilities.rectTmp.inset(-AndroidUtilities.dp(8), -AndroidUtilities.dp(3)); canvasButton.addRect(AndroidUtilities.rectTmp); } @@ -3698,7 +3790,11 @@ protected void onDraw(Canvas canvas) { ); thumbImage[i].draw(canvas); if (drawSpoiler[i]) { - thumbPath.rewind(); + if (thumbPath == null) { + thumbPath = new Path(); + } else { + thumbPath.rewind(); + } thumbPath.addRoundRect(AndroidUtilities.rectTmp, thumbImage[i].getRoundRadius()[0], thumbImage[i].getRoundRadius()[1], Path.Direction.CW); canvas.save(); @@ -3736,14 +3832,170 @@ protected void onDraw(Canvas canvas) { canvas.scale(scale, scale, avatarImage.getCenterX(), avatarImage.getCenterY()); } - if (drawAvatar && (!(currentDialogFolderId != 0 || isTopic && forumTopic != null && forumTopic.id == 1) || archivedChatsDrawable == null || !archivedChatsDrawable.isDraw())) { - avatarImage.draw(canvas); + if (drawAvatar && (!(isTopic && forumTopic != null && forumTopic.id == 1) || archivedChatsDrawable == null || !archivedChatsDrawable.isDraw())) { + storyParams.drawHiddenStoriesAsSegments = currentDialogFolderId != 0; + StoriesUtilities.drawAvatarWithStory(currentDialogId, canvas, avatarImage, storyParams); } if (animatingArchiveAvatar) { canvas.restore(); } + if (avatarImage.getVisible()) { + needInvalidate = drawAvatarOverlays(canvas); + } + + if (rightFragmentOpenedProgress > 0 && currentDialogFolderId == 0) { + boolean drawCounterMuted; + if (isTopic) { + drawCounterMuted = topicMuted; + } else { + drawCounterMuted = chat != null && chat.forum && forumTopic == null ? !hasUnmutedTopics : dialogMuted; + } + int countLeftLocal = (int) (storyParams.originalAvatarRect.left + avatarImage.getImageWidth() - countWidth - AndroidUtilities.dp(5f)); + int countLeftOld = (int) (storyParams.originalAvatarRect.left + avatarImage.getImageWidth() - countWidthOld - AndroidUtilities.dp(5f)); + int countTop = (int) (avatarImage.getImageY() + avatarImage.getImageHeight() - AndroidUtilities.dp(22)); + drawCounter(canvas, drawCounterMuted, countTop, countLeftLocal, countLeftOld, rightFragmentOpenedProgress, true); + } + + if (collapseOffset != 0) { + canvas.restore(); + } + + if (translationX != 0) { + canvas.restore(); + } + if (drawArchive && (currentDialogFolderId != 0 || isTopic && forumTopic != null && forumTopic.id == 1) && translationX == 0 && archivedChatsDrawable != null) { + canvas.save(); + canvas.translate(0, -translateY - rightFragmentOffset * rightFragmentOpenedProgress); + canvas.clipRect(0, 0, getMeasuredWidth(), getMeasuredHeight()); + archivedChatsDrawable.draw(canvas); + canvas.restore(); + } + + if (useSeparator) { + int left; + if (fullSeparator || currentDialogFolderId != 0 && archiveHidden && !fullSeparator2 || fullSeparator2 && !archiveHidden) { + left = 0; + } else { + left = AndroidUtilities.dp(messagePaddingStart); + } + + if (rightFragmentOpenedProgress != 1) { + int alpha = Theme.dividerPaint.getAlpha(); + if (rightFragmentOpenedProgress != 0) { + Theme.dividerPaint.setAlpha((int) (alpha * (1f - rightFragmentOpenedProgress))); + } + float y = getMeasuredHeight() - 1 - rightFragmentOffset * rightFragmentOpenedProgress; + if (LocaleController.isRTL) { + canvas.drawLine(0, y, getMeasuredWidth() - left, y, Theme.dividerPaint); + } else { + canvas.drawLine(left, y, getMeasuredWidth(), y, Theme.dividerPaint); + } + if (rightFragmentOpenedProgress != 0) { + Theme.dividerPaint.setAlpha(alpha); + } + } + } + + if (clipProgress != 0.0f) { + if (Build.VERSION.SDK_INT != 24) { + canvas.restore(); + } else { + Theme.dialogs_pinnedPaint.setColor(Theme.getColor(Theme.key_windowBackgroundWhite, resourcesProvider)); + canvas.drawRect(0, 0, getMeasuredWidth(), topClip * clipProgress, Theme.dialogs_pinnedPaint); + canvas.drawRect(0, getMeasuredHeight() - (int) (bottomClip * clipProgress), getMeasuredWidth(), getMeasuredHeight(), Theme.dialogs_pinnedPaint); + } + } + + if (drawReorder || reorderIconProgress != 0.0f) { + if (drawReorder) { + if (reorderIconProgress < 1.0f) { + reorderIconProgress += 16f / 170.0f; + if (reorderIconProgress > 1.0f) { + reorderIconProgress = 1.0f; + } + needInvalidate = true; + } + } else { + if (reorderIconProgress > 0.0f) { + reorderIconProgress -= 16f / 170.0f; + if (reorderIconProgress < 0.0f) { + reorderIconProgress = 0.0f; + } + needInvalidate = true; + } + } + } + + if (archiveHidden) { + if (archiveBackgroundProgress > 0.0f) { + archiveBackgroundProgress -= 16f / 230.0f; + if (archiveBackgroundProgress < 0.0f) { + archiveBackgroundProgress = 0.0f; + } + if (avatarDrawable.getAvatarType() == AvatarDrawable.AVATAR_TYPE_ARCHIVED) { + avatarDrawable.setArchivedAvatarHiddenProgress(CubicBezierInterpolator.EASE_OUT_QUINT.getInterpolation(archiveBackgroundProgress)); + } + needInvalidate = true; + } + } else { + if (archiveBackgroundProgress < 1.0f) { + archiveBackgroundProgress += 16f / 230.0f; + if (archiveBackgroundProgress > 1.0f) { + archiveBackgroundProgress = 1.0f; + } + if (avatarDrawable.getAvatarType() == AvatarDrawable.AVATAR_TYPE_ARCHIVED) { + avatarDrawable.setArchivedAvatarHiddenProgress(CubicBezierInterpolator.EASE_OUT_QUINT.getInterpolation(archiveBackgroundProgress)); + } + needInvalidate = true; + } + } + + if (animatingArchiveAvatar) { + animatingArchiveAvatarProgress += 16f; + if (animatingArchiveAvatarProgress >= 170.0f) { + animatingArchiveAvatarProgress = 170.0f; + animatingArchiveAvatar = false; + } + needInvalidate = true; + } + if (drawRevealBackground) { + if (currentRevealBounceProgress < 1.0f) { + currentRevealBounceProgress += 16f / 170.0f; + if (currentRevealBounceProgress > 1.0f) { + currentRevealBounceProgress = 1.0f; + needInvalidate = true; + } + } + if (currentRevealProgress < 1.0f) { + currentRevealProgress += 16f / 300.0f; + if (currentRevealProgress > 1.0f) { + currentRevealProgress = 1.0f; + } + needInvalidate = true; + } + } else { + if (currentRevealBounceProgress == 1.0f) { + currentRevealBounceProgress = 0.0f; + needInvalidate = true; + } + if (currentRevealProgress > 0.0f) { + currentRevealProgress -= 16f / 300.0f; + if (currentRevealProgress < 0.0f) { + currentRevealProgress = 0.0f; + } + needInvalidate = true; + } + } + + if (needInvalidate) { + invalidate(); + } + } + + public boolean drawAvatarOverlays(Canvas canvas) { + boolean needInvalidate = false; if (isDialogCell && currentDialogFolderId == 0) { showTtl = ttlPeriod > 0 && !isOnline() && !hasCall; if (rightFragmentOpenedProgress != 1f && (showTtl || ttlProgress > 0)) { @@ -3758,9 +4010,9 @@ protected void onDraw(Canvas canvas) { int top = (int) (avatarImage.getImageY2() - AndroidUtilities.dp(9)); int left; if (LocaleController.isRTL) { - left = (int) (avatarImage.getImageX() + AndroidUtilities.dp(9)); + left = (int) (storyParams.originalAvatarRect.left + AndroidUtilities.dp(9)); } else { - left = (int) (avatarImage.getImageX2() - AndroidUtilities.dp(9)); + left = (int) (storyParams.originalAvatarRect.right - AndroidUtilities.dp(9)); } timerDrawable.setBounds( 0, 0, AndroidUtilities.dp(22), AndroidUtilities.dp(22) @@ -3774,7 +4026,7 @@ protected void onDraw(Canvas canvas) { } else { timerPaint.setShader(null); if (avatarImage.getBitmap() != null && !avatarImage.getBitmap().isRecycled()) { - timerPaint.setColor(PremiumLockIconView.getDominantColor(avatarImage.getBitmap())); + timerPaint.setColor(AndroidUtilities.getDominantColor(avatarImage.getBitmap())); } else if (avatarImage.getDrawable() instanceof VectorAvatarThumbDrawable){ VectorAvatarThumbDrawable vectorAvatarThumbDrawable = (VectorAvatarThumbDrawable) avatarImage.getDrawable(); timerPaint.setColor(vectorAvatarThumbDrawable.gradientTools.getAverageColor()); @@ -3799,12 +4051,12 @@ protected void onDraw(Canvas canvas) { if (user != null && !MessagesController.isSupportUser(user) && !user.bot) { boolean isOnline = isOnline(); if (isOnline || onlineProgress != 0) { - int top = (int) (avatarImage.getImageY2() - AndroidUtilities.dp(useForceThreeLines || SharedConfig.useThreeLinesLayout ? 6 : 8)); + int top = (int) (storyParams.originalAvatarRect.bottom - AndroidUtilities.dp(useForceThreeLines || SharedConfig.useThreeLinesLayout ? 6 : 8)); int left; if (LocaleController.isRTL) { - left = (int) (avatarImage.getImageX() + AndroidUtilities.dp(useForceThreeLines || SharedConfig.useThreeLinesLayout ? 10 : 6)); + left = (int) (storyParams.originalAvatarRect.left + AndroidUtilities.dp(useForceThreeLines || SharedConfig.useThreeLinesLayout ? 10 : 6)); } else { - left = (int) (avatarImage.getImageX2() - AndroidUtilities.dp(useForceThreeLines || SharedConfig.useThreeLinesLayout ? 10 : 6)); + left = (int) (storyParams.originalAvatarRect.right - AndroidUtilities.dp(useForceThreeLines || SharedConfig.useThreeLinesLayout ? 10 : 6)); } Theme.dialogs_onlineCirclePaint.setColor(Theme.getColor(Theme.key_windowBackgroundWhite, resourcesProvider)); @@ -3833,12 +4085,12 @@ protected void onDraw(Canvas canvas) { hasCall = chat.call_active && chat.call_not_empty; if ((hasCall || chatCallProgress != 0) && rightFragmentOpenedProgress < 1f) { float checkProgress = checkBox != null && checkBox.isChecked() ? 1.0f - checkBox.getProgress() : 1.0f; - int top = (int) (avatarImage.getImageY2() - AndroidUtilities.dp(useForceThreeLines || SharedConfig.useThreeLinesLayout ? 6 : 8)); + int top = (int) (storyParams.originalAvatarRect.bottom - AndroidUtilities.dp(useForceThreeLines || SharedConfig.useThreeLinesLayout ? 6 : 8)); int left; if (LocaleController.isRTL) { - left = (int) (avatarImage.getImageX() + AndroidUtilities.dp(useForceThreeLines || SharedConfig.useThreeLinesLayout ? 10 : 6)); + left = (int) (storyParams.originalAvatarRect.left + AndroidUtilities.dp(useForceThreeLines || SharedConfig.useThreeLinesLayout ? 10 : 6)); } else { - left = (int) (avatarImage.getImageX2() - AndroidUtilities.dp(useForceThreeLines || SharedConfig.useThreeLinesLayout ? 10 : 6)); + left = (int) (storyParams.originalAvatarRect.right - AndroidUtilities.dp(useForceThreeLines || SharedConfig.useThreeLinesLayout ? 10 : 6)); } if (rightFragmentOpenedProgress != 0) { @@ -3947,153 +4199,7 @@ protected void onDraw(Canvas canvas) { } ttlProgress = Utilities.clamp(ttlProgress, 1f, 0); } - - if (rightFragmentOpenedProgress > 0 && currentDialogFolderId == 0) { - boolean drawCounterMuted; - if (isTopic) { - drawCounterMuted = topicMuted; - } else { - drawCounterMuted = chat != null && chat.forum && forumTopic == null ? !hasUnmutedTopics : dialogMuted; - } - int countLeftLocal = (int) (avatarImage.getImageX() + avatarImage.getImageWidth() - countWidth - AndroidUtilities.dp(5f)); - int countLeftOld = (int) (avatarImage.getImageX() + avatarImage.getImageWidth() - countWidthOld - AndroidUtilities.dp(5f)); - int countTop = (int) (avatarImage.getImageY() + avatarImage.getImageHeight() - AndroidUtilities.dp(22)); - drawCounter(canvas, drawCounterMuted, countTop, countLeftLocal, countLeftOld, rightFragmentOpenedProgress, true); - } - - if (collapseOffset != 0) { - canvas.restore(); - } - - if (translationX != 0) { - canvas.restore(); - } - if ((currentDialogFolderId != 0 || isTopic && forumTopic != null && forumTopic.id == 1) && translationX == 0 && archivedChatsDrawable != null) { - canvas.save(); - canvas.translate(0, -translateY); - canvas.clipRect(0, 0, getMeasuredWidth(), getMeasuredHeight()); - archivedChatsDrawable.draw(canvas); - canvas.restore(); - } - - if (useSeparator) { - int left; - if (fullSeparator || currentDialogFolderId != 0 && archiveHidden && !fullSeparator2 || fullSeparator2 && !archiveHidden) { - left = 0; - } else { - left = AndroidUtilities.dp(messagePaddingStart); - } - - if (rightFragmentOpenedProgress != 1) { - int alpha = Theme.dividerPaint.getAlpha(); - if (rightFragmentOpenedProgress != 0) { - Theme.dividerPaint.setAlpha((int) (alpha * (1f - rightFragmentOpenedProgress))); - } - if (LocaleController.isRTL) { - canvas.drawLine(0, getMeasuredHeight() - 1, getMeasuredWidth() - left, getMeasuredHeight() - 1, Theme.dividerPaint); - } else { - canvas.drawLine(left, getMeasuredHeight() - 1, getMeasuredWidth(), getMeasuredHeight() - 1, Theme.dividerPaint); - } - if (rightFragmentOpenedProgress != 0) { - Theme.dividerPaint.setAlpha(alpha); - } - } - } - - if (clipProgress != 0.0f) { - if (Build.VERSION.SDK_INT != 24) { - canvas.restore(); - } else { - Theme.dialogs_pinnedPaint.setColor(Theme.getColor(Theme.key_windowBackgroundWhite, resourcesProvider)); - canvas.drawRect(0, 0, getMeasuredWidth(), topClip * clipProgress, Theme.dialogs_pinnedPaint); - canvas.drawRect(0, getMeasuredHeight() - (int) (bottomClip * clipProgress), getMeasuredWidth(), getMeasuredHeight(), Theme.dialogs_pinnedPaint); - } - } - - if (drawReorder || reorderIconProgress != 0.0f) { - if (drawReorder) { - if (reorderIconProgress < 1.0f) { - reorderIconProgress += 16f / 170.0f; - if (reorderIconProgress > 1.0f) { - reorderIconProgress = 1.0f; - } - needInvalidate = true; - } - } else { - if (reorderIconProgress > 0.0f) { - reorderIconProgress -= 16f / 170.0f; - if (reorderIconProgress < 0.0f) { - reorderIconProgress = 0.0f; - } - needInvalidate = true; - } - } - } - - if (archiveHidden) { - if (archiveBackgroundProgress > 0.0f) { - archiveBackgroundProgress -= 16f / 230.0f; - if (archiveBackgroundProgress < 0.0f) { - archiveBackgroundProgress = 0.0f; - } - if (avatarDrawable.getAvatarType() == AvatarDrawable.AVATAR_TYPE_ARCHIVED) { - avatarDrawable.setArchivedAvatarHiddenProgress(CubicBezierInterpolator.EASE_OUT_QUINT.getInterpolation(archiveBackgroundProgress)); - } - needInvalidate = true; - } - } else { - if (archiveBackgroundProgress < 1.0f) { - archiveBackgroundProgress += 16f / 230.0f; - if (archiveBackgroundProgress > 1.0f) { - archiveBackgroundProgress = 1.0f; - } - if (avatarDrawable.getAvatarType() == AvatarDrawable.AVATAR_TYPE_ARCHIVED) { - avatarDrawable.setArchivedAvatarHiddenProgress(CubicBezierInterpolator.EASE_OUT_QUINT.getInterpolation(archiveBackgroundProgress)); - } - needInvalidate = true; - } - } - - if (animatingArchiveAvatar) { - animatingArchiveAvatarProgress += 16f; - if (animatingArchiveAvatarProgress >= 170.0f) { - animatingArchiveAvatarProgress = 170.0f; - animatingArchiveAvatar = false; - } - needInvalidate = true; - } - if (drawRevealBackground) { - if (currentRevealBounceProgress < 1.0f) { - currentRevealBounceProgress += 16f / 170.0f; - if (currentRevealBounceProgress > 1.0f) { - currentRevealBounceProgress = 1.0f; - needInvalidate = true; - } - } - if (currentRevealProgress < 1.0f) { - currentRevealProgress += 16f / 300.0f; - if (currentRevealProgress > 1.0f) { - currentRevealProgress = 1.0f; - } - needInvalidate = true; - } - } else { - if (currentRevealBounceProgress == 1.0f) { - currentRevealBounceProgress = 0.0f; - needInvalidate = true; - } - if (currentRevealProgress > 0.0f) { - currentRevealProgress -= 16f / 300.0f; - if (currentRevealProgress < 0.0f) { - currentRevealProgress = 0.0f; - } - needInvalidate = true; - } - } - - if (needInvalidate) { - invalidate(); - } + return needInvalidate; } private void drawCounter(Canvas canvas, boolean drawCounterMuted, int countTop, int countLeftLocal, int countLeftOld, float globalScale, boolean outline) { @@ -4304,9 +4410,12 @@ public void startOutAnimation() { archivedChatsDrawable.outRadius = 0; archivedChatsDrawable.outImageSize = 0; } else { - archivedChatsDrawable.outCy = avatarImage.getCenterY(); - archivedChatsDrawable.outCx = avatarImage.getCenterX(); - archivedChatsDrawable.outRadius = avatarImage.getImageWidth() / 2.0f; + archivedChatsDrawable.outCy = storyParams.originalAvatarRect.centerY(); + archivedChatsDrawable.outCx = storyParams.originalAvatarRect.centerX(); + archivedChatsDrawable.outRadius = storyParams.originalAvatarRect.width() / 2.0f; + if (MessagesController.getInstance(currentAccount).getStoriesController().hasHiddenStories()) { + archivedChatsDrawable.outRadius -= AndroidUtilities.dpf2(3.5f); + } archivedChatsDrawable.outImageSize = avatarImage.getBitmapWidth(); } archivedChatsDrawable.startOutAnimation(); @@ -4531,7 +4640,7 @@ public void updateMessageThumbs() { Collections.sort(groupMessages, (a, b) -> a.getId() - b.getId()); for (int i = 0; i < Math.min(3, groupMessages.size()); ++i) { MessageObject message = groupMessages.get(i); - if (message != null && !message.needDrawBluredPreview() && (message.isPhoto() || message.isNewGif() || message.isVideo() || message.isRoundVideo())) { + if (message != null && !message.needDrawBluredPreview() && (message.isPhoto() || message.isNewGif() || message.isVideo() || message.isRoundVideo() || message.isStoryMedia())) { String type = message.isWebpage() ? message.messageOwner.media.webpage.type : null; if (!("app".equals(type) || "profile".equals(type) || "article".equals(type) || type != null && type.startsWith("telegram_"))) { setThumb(i, message); @@ -4541,7 +4650,7 @@ public void updateMessageThumbs() { } else if (message != null && currentDialogFolderId == 0) { thumbsCount = 0; hasVideoThumb = false; - if (!message.needDrawBluredPreview() && (message.isPhoto() || message.isNewGif() || message.isVideo() || message.isRoundVideo())) { + if (!message.needDrawBluredPreview() && (message.isPhoto() || message.isNewGif() || message.isVideo() || message.isRoundVideo() || message.isStoryMedia())) { String type = message.isWebpage() ? message.messageOwner.media.webpage.type : null; if (!("app".equals(type) || "profile".equals(type) || "article".equals(type) || type != null && type.startsWith("telegram_"))) { setThumb(0, message); @@ -4551,8 +4660,25 @@ public void updateMessageThumbs() { } private void setThumb(int index, MessageObject message) { - TLRPC.PhotoSize smallThumb = FileLoader.getClosestPhotoSizeWithSize(message.photoThumbs, 40); - TLRPC.PhotoSize bigThumb = FileLoader.getClosestPhotoSizeWithSize(message.photoThumbs, AndroidUtilities.getPhotoSize()); + ArrayList photoThumbs = message.photoThumbs; + TLObject photoThumbsObject = message.photoThumbsObject; + if (message.isStoryMedia()) { + TLRPC.StoryItem storyItem = message.messageOwner.media.storyItem; + if (storyItem != null && storyItem.media != null) { + if (storyItem.media.document != null) { + photoThumbs = storyItem.media.document.thumbs; + photoThumbsObject = storyItem.media.document; + } else if (storyItem.media.photo != null) { + photoThumbs = storyItem.media.photo.sizes; + photoThumbsObject = storyItem.media.photo; + } + } else { + return; + } + } + + TLRPC.PhotoSize smallThumb = FileLoader.getClosestPhotoSizeWithSize(photoThumbs, 40); + TLRPC.PhotoSize bigThumb = FileLoader.getClosestPhotoSizeWithSize(photoThumbs, AndroidUtilities.getPhotoSize()); if (smallThumb == bigThumb) { bigThumb = null; } @@ -4569,7 +4695,7 @@ private void setThumb(int index, MessageObject message) { drawSpoiler[index] = message.hasMediaSpoilers(); int size = message.type == MessageObject.TYPE_PHOTO && selectedThumb != null ? selectedThumb.size : 0; String filter = message.hasMediaSpoilers() ? "5_5_b" : "20_20"; - thumbImage[index].setImage(ImageLocation.getForObject(selectedThumb, message.photoThumbsObject), filter, ImageLocation.getForObject(smallThumb, message.photoThumbsObject), filter, size, null, message, 0); + thumbImage[index].setImage(ImageLocation.getForObject(selectedThumb, photoThumbsObject), filter, ImageLocation.getForObject(smallThumb, photoThumbsObject), filter, size, null, message, 0); thumbImage[index].setRoundRadius(message.isRoundVideo() ? AndroidUtilities.dp(18) : AndroidUtilities.dp(2)); needEmoji = false; } @@ -4660,10 +4786,7 @@ public SpannableStringBuilder getMessageStringFormatted(String messageFormat, St emoji = "\uD83D\uDCCE "; } if (message.hasHighlightedWords() && !TextUtils.isEmpty(message.messageOwner.message)) { - String str = message.messageTrimmedToHighlight; - if (message.messageTrimmedToHighlight != null) { - str = message.messageTrimmedToHighlight; - } + CharSequence text = message.messageTrimmedToHighlight; int w = getMeasuredWidth() - AndroidUtilities.dp(messagePaddingStart + 23 + 24); if (hasNameInMessage) { if (!TextUtils.isEmpty(messageNameString)) { @@ -4672,14 +4795,17 @@ public SpannableStringBuilder getMessageStringFormatted(String messageFormat, St w -= currentMessagePaint.measureText(": "); } if (w > 0) { - str = AndroidUtilities.ellipsizeCenterEnd(str, message.highlightedWords.get(0), w, currentMessagePaint, 130).toString(); + text = AndroidUtilities.ellipsizeCenterEnd(text, message.highlightedWords.get(0), w, currentMessagePaint, 130).toString(); } - stringBuilder = new SpannableStringBuilder(emoji).append(str); + stringBuilder = new SpannableStringBuilder(emoji).append(text); } else { if (mess.length() > 150) { mess = mess.subSequence(0, 150); } SpannableStringBuilder msgBuilder = new SpannableStringBuilder(mess); + if (message != null) { + message.spoilLoginCode(); + } if (!NekoConfig.showSpoilersDirectly.Bool()) MediaDataController.addTextStyleRuns(message.messageOwner.entities, mess, msgBuilder, TextStyleSpan.FLAG_STYLE_SPOILER | TextStyleSpan.FLAG_STYLE_STRIKE); if (message != null && message.messageOwner != null) { @@ -4694,7 +4820,7 @@ public SpannableStringBuilder getMessageStringFormatted(String messageFormat, St } else if (message.messageOwner.media != null && !message.isMediaEmpty()) { currentMessagePaint = Theme.dialogs_messagePrintingPaint[paintIndex]; String innerMessage; - String colorKey = Theme.key_chats_attachMessage; + int colorKey = Theme.key_chats_attachMessage; if (message.messageOwner.media instanceof TLRPC.TL_messageMediaPoll) { TLRPC.TL_messageMediaPoll mediaPoll = (TLRPC.TL_messageMediaPoll) message.messageOwner.media; if (Build.VERSION.SDK_INT >= 18) { @@ -4763,6 +4889,9 @@ public SpannableStringBuilder getMessageStringFormatted(String messageFormat, St mess = AndroidUtilities.replaceNewLines(mess); } mess = new SpannableStringBuilder(mess); + if (message != null) { + message.spoilLoginCode(); + } if (!NekoConfig.showSpoilersDirectly.Bool()) MediaDataController.addTextStyleRuns(message, (Spannable) mess, TextStyleSpan.FLAG_STYLE_SPOILER | TextStyleSpan.FLAG_STYLE_STRIKE); if (message != null && message.messageOwner != null) { @@ -4778,8 +4907,27 @@ public SpannableStringBuilder getMessageStringFormatted(String messageFormat, St return stringBuilder; } + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + if (rightFragmentOpenedProgress == 0 && storyParams.checkOnTouchEvent(ev, this)) { + return true; + } + return super.onInterceptTouchEvent(ev); + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL) { + storyParams.checkOnTouchEvent(ev, this); + } + return super.dispatchTouchEvent(ev); + } + @Override public boolean onTouchEvent(MotionEvent event) { + if (rightFragmentOpenedProgress == 0 && storyParams.checkOnTouchEvent(event, this)) { + return true; + } if (delegate == null || delegate.canClickButtonInside()) { if (lastTopicMessageUnread && canvasButton != null && buttonLayout != null && canvasButton.checkTouchEvent(event)) { return true; @@ -4830,12 +4978,16 @@ public interface DialogCellDelegate { void onButtonClicked(DialogCell dialogCell); void onButtonLongPress(DialogCell dialogCell); boolean canClickButtonInside(); + void openStory(DialogCell dialogCell, Runnable onDone); + void showChatPreview(DialogCell dialogCell); + void openHiddenStories(); } private class DialogUpdateHelper { public long lastDrawnDialogId; public long lastDrawnMessageId; + public boolean lastDrawnTranslated; public boolean lastDrawnDialogIsFolder; public long lastDrawnReadState; public int lastDrawnDraftHash; @@ -4895,8 +5047,10 @@ public boolean update() { } int draftHash = draftMessage == null ? 0 : draftMessage.message.hashCode() + (draftMessage.reply_to_msg_id << 16); boolean hasCall = chat != null && chat.call_active && chat.call_not_empty; + boolean translated = MessagesController.getInstance(currentAccount).getTranslateController().isTranslatingDialog(currentDialogId); if (lastDrawnSizeHash == sizeHash && lastDrawnMessageId == messageHash && + lastDrawnTranslated == translated && lastDrawnDialogId == currentDialogId && lastDrawnDialogIsFolder == dialog.isFolder && lastDrawnReadState == readHash && @@ -4939,6 +5093,7 @@ public boolean update() { lastTopicsCount = topicCount; lastDrawnPinned = drawPin; lastDrawnHasCall = hasCall; + lastDrawnTranslated = translated; return true; } @@ -4962,4 +5117,19 @@ public void updateAnimationValues() { } } + @Override + public void invalidate() { + if (StoryViewer.animationInProgress) { + return; + } + super.invalidate(); + } + + @Override + public void invalidate(int l, int t, int r, int b) { + if (StoryViewer.animationInProgress) { + return; + } + super.invalidate(l, t, r, b); + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogsEmptyCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogsEmptyCell.java index 9c79325c28..271d468eef 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogsEmptyCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogsEmptyCell.java @@ -125,7 +125,7 @@ public void setOnUtyanAnimationUpdateListener(Consumer onUtyanAnimationUp this.onUtyanAnimationUpdateListener = onUtyanAnimationUpdateListener; } - public void setType(@EmptyType int value) { + public void setType(@EmptyType int value, boolean forward) { if (currentType == value) { return; } @@ -142,8 +142,13 @@ public void setType(@EmptyType int value) { case TYPE_FILTER_NO_CHATS_TO_DISPLAY: imageView.setAutoRepeat(false); icon = R.raw.filter_no_chats; - help = LocaleController.getString("FilterNoChatsToDisplayInfo", R.string.FilterNoChatsToDisplayInfo); - titleView.setText(LocaleController.getString("FilterNoChatsToDisplay", R.string.FilterNoChatsToDisplay)); + if (forward) { + titleView.setText(LocaleController.getString("FilterNoChatsToForward", R.string.FilterNoChatsToForward)); + help = LocaleController.getString("FilterNoChatsToForwardInfo", R.string.FilterNoChatsToForwardInfo); + } else { + titleView.setText(LocaleController.getString("FilterNoChatsToDisplay", R.string.FilterNoChatsToDisplay)); + help = LocaleController.getString("FilterNoChatsToDisplayInfo", R.string.FilterNoChatsToDisplayInfo); + } break; default: case TYPE_FILTER_ADDING_CHATS: diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogsHintCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogsHintCell.java index 97b6678e88..a9c23b56dd 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogsHintCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogsHintCell.java @@ -53,7 +53,6 @@ public DialogsHintCell(@NonNull Context context) { chevronView = new ImageView(context); chevronView.setImageResource(R.drawable.arrow_newchat); - chevronView.setColorFilter(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText), PorterDuff.Mode.SRC_IN); addView(chevronView, LayoutHelper.createFrame(16, 16, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.CENTER_VERTICAL)); updateColors(); @@ -62,6 +61,7 @@ public DialogsHintCell(@NonNull Context context) { public void updateColors() { titleView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); messageView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText)); + chevronView.setColorFilter(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText), PorterDuff.Mode.SRC_IN); setBackground(Theme.AdaptiveRipple.filledRect()); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogsRequestedEmptyCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogsRequestedEmptyCell.java index bc299b013a..f2f900e0d7 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogsRequestedEmptyCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogsRequestedEmptyCell.java @@ -88,7 +88,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { buttonView = new TextView(context); buttonView.setGravity(Gravity.CENTER); - buttonView.setBackground(Theme.AdaptiveRipple.filledRect(Theme.key_featuredStickers_addButton, 8)); + buttonView.setBackground(Theme.AdaptiveRipple.filledRectByKey(Theme.key_featuredStickers_addButton, 8)); buttonView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); buttonView.setTextColor(Theme.getColor(Theme.key_featuredStickers_buttonText)); buttonView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); @@ -134,7 +134,7 @@ protected void onAttachedToWindow() { @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.diceStickersDidLoad); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.diceStickersDidLoad); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerActionCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerActionCell.java index e76f511a97..9873e8a90f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerActionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerActionCell.java @@ -13,6 +13,7 @@ import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.RectF; +import android.util.TypedValue; import android.view.Gravity; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; @@ -21,6 +22,7 @@ import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.FileLog; +import org.telegram.messenger.LocaleController; import org.telegram.messenger.MessagesController; import org.telegram.messenger.UserConfig; import org.telegram.ui.ActionBar.Theme; @@ -34,38 +36,37 @@ public class DrawerActionCell extends FrameLayout { private ImageView imageView; - private RLottieImageView lottieImageView; - private AnimatedTextView textView; + private TextView textView; private int currentId; private RectF rect = new RectF(); - private int currentLottieId; public DrawerActionCell(Context context) { super(context); imageView = new ImageView(context); imageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_chats_menuItemIcon), PorterDuff.Mode.SRC_IN)); - addView(imageView, LayoutHelper.createFrame(24, 24, Gravity.LEFT | Gravity.TOP, 19, 12, 0, 0)); -// addView(imageView, LayoutHelper.createFrame(24, 24, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 0 : 19, 12, LocaleController.isRTL ? 19 : 0, 0)); - lottieImageView = new RLottieImageView(context); - lottieImageView.setAutoRepeat(false); - lottieImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_chats_menuItemIcon), PorterDuff.Mode.SRC_IN)); - addView(lottieImageView, LayoutHelper.createFrame(28, 28, Gravity.LEFT | Gravity.TOP, 17, 10, 0, 0)); -// addView(lottieImageView, LayoutHelper.createFrame(28, 28, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 0 : 17, 10, LocaleController.isRTL ? 17 : 0, 0)); - - textView = new AnimatedTextView(context, true, true, true); - textView.setAnimationProperties(.6f, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + textView = new TextView(context); textView.setTextColor(Theme.getColor(Theme.key_chats_menuItemText)); - textView.setTextSize(AndroidUtilities.dp(15)); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - textView.setIgnoreRTL(true); - addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 19 + 24 + 29, 0, 16, 0)); -// addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 16 : 62, 0, LocaleController.isRTL ? 62 : 16, 0)); + textView.setGravity(Gravity.CENTER_VERTICAL); + toggleRTL(true); setWillNotDraw(false); } + private boolean wasRTL; + + public void toggleRTL(boolean force) { + if (wasRTL != LocaleController.isRTL || force) { + wasRTL = LocaleController.isRTL; + removeAllViews(); + addView(imageView, LayoutHelper.createFrame(24, 24, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 0 : 19, 12, LocaleController.isRTL ? 19 : 0, 0)); + addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 16 : 72, 0, LocaleController.isRTL ? 72 : 16, 0)); + } + } + @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); @@ -75,7 +76,7 @@ protected void onDraw(Canvas canvas) { if (suggestions.contains("VALIDATE_PHONE_NUMBER") || suggestions.contains("VALIDATE_PASSWORD")) { int countTop = AndroidUtilities.dp(12.5f); int countWidth = AndroidUtilities.dp(9); - int countLeft = getMeasuredWidth() - countWidth - AndroidUtilities.dp(25); + int countLeft = LocaleController.isRTL ? countWidth + AndroidUtilities.dp(25) : getMeasuredWidth() - countWidth - AndroidUtilities.dp(25); int x = countLeft - AndroidUtilities.dp(5.5f); rect.set(x, countTop, x + countWidth + AndroidUtilities.dp(14), countTop + AndroidUtilities.dp(23)); @@ -101,41 +102,30 @@ protected void onAttachedToWindow() { textView.setTextColor(Theme.getColor(Theme.key_chats_menuItemText)); } - public void setTextAndIcon(int id, String text, int resId, int lottieId) { + public void setTextAndIcon(int id, String text, int resId) { + toggleRTL(false); currentId = id; try { - textView.setText(text, false); - if (lottieId != 0) { - imageView.setImageDrawable(null); - lottieImageView.setAnimation(currentLottieId = lottieId, 28, 28); - } else { - imageView.setImageResource(resId); - lottieImageView.clearAnimationDrawable(); - currentLottieId = 0; - } + textView.setText(text); + imageView.setImageResource(resId); } catch (Throwable e) { FileLog.e(e); } } - public void updateText(String text) { - textView.setText(text); - } - - public void updateIcon(int lottieId) { + public void updateTextAndIcon(String text, int resId) { try { - if (lottieId != currentLottieId) { - lottieImageView.setOnAnimationEndListener(() -> { - lottieImageView.setAnimation(currentLottieId = lottieId, 28, 28); - lottieImageView.setOnAnimationEndListener(null); - }); - lottieImageView.playAnimation(); - } + textView.setText(text); + imageView.setImageResource(resId); } catch (Throwable e) { FileLog.e(e); } } + public ImageView getImageView() { + return imageView; + } + @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerProfileCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerProfileCell.java index f2a5bf10f1..b9e3ffaf7b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerProfileCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerProfileCell.java @@ -33,7 +33,6 @@ import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; -import android.widget.Toast; import androidx.palette.graphics.Palette; @@ -79,6 +78,7 @@ import org.telegram.ui.Components.RLottieDrawable; import org.telegram.ui.Components.RLottieImageView; import org.telegram.ui.Components.Reactions.AnimatedEmojiEffect; +import org.telegram.ui.Components.Reactions.HwEmojis; import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble; import org.telegram.ui.Components.SnowflakesEffect; import org.telegram.ui.ThemeActivity; @@ -194,6 +194,38 @@ protected void onDraw(Canvas canvas) { animatedStatus.translate(AndroidUtilities.rectTmp2.centerX(), AndroidUtilities.rectTmp2.centerY()); } } + + @Override + public void invalidate() { + if (HwEmojis.grab(this)) { + return; + } + super.invalidate(); + } + + @Override + public void invalidate(int l, int t, int r, int b) { + if (HwEmojis.grab(this)) { + return; + } + super.invalidate(l, t, r, b); + } + + @Override + public void invalidateDrawable(Drawable who) { + if (HwEmojis.grab(this)) { + return; + } + super.invalidateDrawable(who); + } + + @Override + public void invalidate(Rect dirty) { + if (HwEmojis.grab(this)) { + return; + } + super.invalidate(dirty); + } }; nameTextView.setRightDrawableOnClick(e -> { if (lastUser != null && lastUser.premium) { @@ -525,6 +557,7 @@ private void switchTheme(Theme.ThemeInfo themeInfo, boolean toDark) { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); + status.attach(); updateColors(); NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.emojiLoaded); for (int i : SharedConfig.activeAccounts) { @@ -535,6 +568,7 @@ protected void onAttachedToWindow() { @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); + status.detach(); NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.emojiLoaded); for (int i : SharedConfig.activeAccounts) { NotificationCenter.getInstance(i).removeObserver(this, NotificationCenter.currentUserPremiumStatusChanged); @@ -592,8 +626,8 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto @Override protected void onDraw(Canvas canvas) { Drawable backgroundDrawable = Theme.getCachedWallpaper(); - String backgroundKey = applyBackground(false); - boolean useImageBackground = !backgroundKey.equals(Theme.key_chats_menuTopBackground) && Theme.isCustomTheme() && !Theme.isPatternWallpaper() && backgroundDrawable != null && !(backgroundDrawable instanceof ColorDrawable) && !(backgroundDrawable instanceof GradientDrawable); + int backgroundKey = applyBackground(false); + boolean useImageBackground = backgroundKey != Theme.key_chats_menuTopBackground && Theme.isCustomTheme() && !Theme.isPatternWallpaper() && backgroundDrawable != null && !(backgroundDrawable instanceof ColorDrawable) && !(backgroundDrawable instanceof GradientDrawable); boolean drawCatsShadow = false; int color; if (!useAdb() && !useImageBackground && Theme.hasThemeKey(Theme.key_chats_menuTopShadowCats)) { @@ -671,10 +705,21 @@ protected void onDraw(Canvas canvas) { } else if (!drawPremium && drawPremiumProgress != 0) { drawPremiumProgress -= 16 / 220f; } + } + drawPremiumProgress = Utilities.clamp(drawPremiumProgress, 1f, 0); + if (drawPremiumProgress != 0) { + if (gradientTools == null) { + gradientTools = new PremiumGradient.PremiumGradientTools(Theme.key_premiumGradientBottomSheet1, Theme.key_premiumGradientBottomSheet2, Theme.key_premiumGradientBottomSheet3, -1); + gradientTools.x1 = 0; + gradientTools.y1 = 1.1f; + gradientTools.x2 = 1.5f; + gradientTools.y2 = -0.2f; + gradientTools.exactly = true; + } drawPremiumProgress = Utilities.clamp(drawPremiumProgress, 1f, 0); if (drawPremiumProgress != 0) { if (gradientTools == null) { - gradientTools = new PremiumGradient.PremiumGradientTools(Theme.key_premiumGradientBottomSheet1, Theme.key_premiumGradientBottomSheet2, Theme.key_premiumGradientBottomSheet3, null); + gradientTools = new PremiumGradient.PremiumGradientTools(Theme.key_premiumGradientBottomSheet1, Theme.key_premiumGradientBottomSheet2, Theme.key_premiumGradientBottomSheet3, -1); gradientTools.x1 = 0; gradientTools.y1 = 1.1f; gradientTools.x2 = 1.5f; @@ -806,10 +851,10 @@ public void setUser(TLRPC.User user, boolean accounts) { updateRightDrawable = true; } - public String applyBackground(boolean force) { - String currentTag = (String) getTag(); - String backgroundKey = Theme.hasThemeKey(Theme.key_chats_menuTopBackground) && Theme.getColor(Theme.key_chats_menuTopBackground) != 0 ? Theme.key_chats_menuTopBackground : Theme.key_chats_menuTopBackgroundCats; - if (force || !backgroundKey.equals(currentTag)) { + public Integer applyBackground(boolean force) { + Integer currentTag = (Integer) getTag(); + int backgroundKey = Theme.hasThemeKey(Theme.key_chats_menuTopBackground) && Theme.getColor(Theme.key_chats_menuTopBackground) != 0 ? Theme.key_chats_menuTopBackground : Theme.key_chats_menuTopBackgroundCats; + if (force || currentTag == null || backgroundKey != currentTag) { setBackgroundColor(Theme.getColor(backgroundKey)); setTag(backgroundKey); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerUserCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerUserCell.java index 3d0933be03..608f98cd7a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerUserCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerUserCell.java @@ -92,6 +92,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { protected void onAttachedToWindow() { super.onAttachedToWindow(); textView.setTextColor(Theme.getColor(Theme.key_chats_menuItemText)); + status.attach(); for (int i : SharedConfig.activeAccounts) { NotificationCenter.getInstance(i).addObserver(this, NotificationCenter.currentUserPremiumStatusChanged); NotificationCenter.getInstance(i).addObserver(this, NotificationCenter.updateInterfaces); @@ -102,6 +103,7 @@ protected void onAttachedToWindow() { @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); + status.detach(); for (int i : SharedConfig.activeAccounts) { NotificationCenter.getInstance(i).removeObserver(this, NotificationCenter.currentUserPremiumStatusChanged); NotificationCenter.getInstance(i).removeObserver(this, NotificationCenter.updateInterfaces); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/EmojiReplacementCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/EmojiReplacementCell.java index abc600fd19..1f90d4983f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/EmojiReplacementCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/EmojiReplacementCell.java @@ -125,8 +125,7 @@ public void invalidate() { imageView.invalidate(); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ExpiredStoryView.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ExpiredStoryView.java new file mode 100644 index 0000000000..b75b2bc20f --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ExpiredStoryView.java @@ -0,0 +1,124 @@ +package org.telegram.ui.Cells; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.RectF; +import android.text.Layout; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.SpannableStringBuilder; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.text.TextUtils; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.R; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.TypefaceSpan; +import org.telegram.ui.Stories.StoriesUtilities; + +public class ExpiredStoryView { + public boolean visible; + + StaticLayout titleLayout; + StaticLayout subtitleLayout; + + int width; + int height; + float textX; + float textY; + float verticalPadding; + float horizontalPadding; + + public void measure(ChatMessageCell parent) { + CharSequence title = StoriesUtilities.createExpiredStoryString(); + TLRPC.TL_messageMediaStory mediaStory = (TLRPC.TL_messageMediaStory) parent.getMessageObject().messageOwner.media; + TLRPC.User user = MessagesController.getInstance(parent.currentAccount).getUser(mediaStory.user_id); + String fromName = user == null ? "DELETED" : user.first_name; + int forwardedNameWidth; + if (AndroidUtilities.isTablet()) { + forwardedNameWidth = (int) (AndroidUtilities.getMinTabletSide() * 0.4f); + } else { + forwardedNameWidth = (int) (parent.getParentWidth() * 0.4f); + } + String from = LocaleController.getString("From", R.string.From); + int fromWidth = (int) Math.ceil(Theme.chat_forwardNamePaint.measureText(from + " ")); + + fromName = (String) TextUtils.ellipsize(fromName.replace('\n', ' '), Theme.chat_replyNamePaint, forwardedNameWidth - fromWidth, TextUtils.TruncateAt.END); + String fromString = LocaleController.getString("FromFormatted", R.string.FromFormatted); + int idx = fromString.indexOf("%1$s"); + CharSequence subtitle = String.format(fromString, fromName); + if (idx >= 0) { + SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(subtitle); + spannableStringBuilder.setSpan(new TypefaceSpan(AndroidUtilities.getTypeface("fonts/rmedium.ttf")), idx, idx + fromName.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + subtitle = spannableStringBuilder; + } + TextPaint titlePaint = Theme.chat_replyTextPaint; + TextPaint subtitlePaint = Theme.chat_replyTextPaint; + int w = (int) (titlePaint.measureText(title, 0, title.length()) + 1); + titleLayout = new StaticLayout(title, titlePaint, w + AndroidUtilities.dp(10), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + w = (int) (subtitlePaint.measureText( subtitle, 0, subtitle.length()) + 1); + subtitleLayout = new StaticLayout(subtitle, subtitlePaint, w + AndroidUtilities.dp(10), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + + height = 0; + verticalPadding = AndroidUtilities.dp(4); + horizontalPadding = AndroidUtilities.dp(12); + height += AndroidUtilities.dp(4) + titleLayout.getHeight() + AndroidUtilities.dp(2) + subtitleLayout.getHeight() + AndroidUtilities.dp(4) + verticalPadding * 2; + width = Math.max(titleLayout.getWidth(), subtitleLayout.getWidth()) + AndroidUtilities.dp(12) + AndroidUtilities.dp(20) + parent.getExtraTextX(); + + } + + public void draw(Canvas canvas, ChatMessageCell parent) { + float alpha = 1f; + textY = AndroidUtilities.dp(8) + verticalPadding; + if (parent.pinnedTop) { + textY -= AndroidUtilities.dp(2); + } + RectF rect = AndroidUtilities.rectTmp; + if (parent.getMessageObject().isOutOwner()) { + textX = -(parent.timeWidth + AndroidUtilities.dp(12)) + parent.getExtraTextX() + parent.getMeasuredWidth() - width + AndroidUtilities.dp(24) - horizontalPadding; + rect.set(parent.getMeasuredWidth() - width - horizontalPadding, verticalPadding, parent.getMeasuredWidth() - horizontalPadding, parent.getMeasuredHeight() - verticalPadding); + } else { + float offset = parent.isAvatarVisible ? AndroidUtilities.dp(48) : 0; + textX = offset + horizontalPadding + AndroidUtilities.dp(12); + rect.set(offset + horizontalPadding, verticalPadding, offset + horizontalPadding + width, parent.getMeasuredHeight() - verticalPadding); + } + // Theme.chat_replyNamePaint.setColor(getThemedColor(Theme.key_chat_inReplyNameText)); + + if (parent.getMessageObject().isOutOwner()) { + Theme.chat_replyTextPaint.setColor(parent.getThemedColor(Theme.key_chat_outReplyNameText)); + } else { + Theme.chat_replyTextPaint.setColor(parent.getThemedColor(Theme.key_chat_inReplyNameText)); + } + // float roundRadius = AndroidUtilities.dp(8); +// if (alpha != 1f) { +// int oldAlpha = parent.getThemedPaint(Theme.key_paint_chatActionBackground).getAlpha(); +// parent.getThemedPaint(Theme.key_paint_chatActionBackground).setAlpha((int) (alpha * oldAlpha)); +// canvas.drawRoundRect(rect, roundRadius, roundRadius, parent.getThemedPaint(Theme.key_paint_chatActionBackground)); +// parent.getThemedPaint(Theme.key_paint_chatActionBackground).setAlpha(oldAlpha); +// } else { +// canvas.drawRoundRect(rect, roundRadius, roundRadius, parent.getThemedPaint(Theme.key_paint_chatActionBackground)); +// } +// if (parent.hasGradientService()) { +// if (alpha != 1f) { +// int oldAlpha = Theme.chat_actionBackgroundGradientDarkenPaint.getAlpha(); +// Theme.chat_actionBackgroundGradientDarkenPaint.setAlpha((int) (alpha * oldAlpha)); +// canvas.drawRoundRect(rect, roundRadius, roundRadius, Theme.chat_actionBackgroundGradientDarkenPaint); +// Theme.chat_actionBackgroundGradientDarkenPaint.setAlpha(oldAlpha); +// } else { +// canvas.drawRoundRect(rect, roundRadius, roundRadius, Theme.chat_actionBackgroundGradientDarkenPaint); +// } +// } + + canvas.save(); + canvas.translate(textX, textY); + titleLayout.draw(canvas); + canvas.translate(0, titleLayout.getHeight() + AndroidUtilities.dp(2)); + subtitleLayout.draw(canvas); + canvas.restore(); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/FeaturedStickerSetCell2.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/FeaturedStickerSetCell2.java index 87acfc9e41..2b2dcca1f3 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/FeaturedStickerSetCell2.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/FeaturedStickerSetCell2.java @@ -425,9 +425,8 @@ public static void createThemeDescriptions(List descriptions, descriptions.add(new ThemeDescription(null, 0, null, null, null, delegate, Theme.key_featuredStickers_addButtonPressed)); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/FeaturedStickerSetInfoCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/FeaturedStickerSetInfoCell.java index bdd857c2c7..09e8c6ca79 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/FeaturedStickerSetInfoCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/FeaturedStickerSetInfoCell.java @@ -193,7 +193,11 @@ public void setStickerSet(TLRPC.StickerSetCovered stickerSet, boolean unread, bo } else { nameTextView.setText(stickerSet.set.title); } - infoTextView.setText(LocaleController.formatPluralString("Stickers", stickerSet.set.count)); + if (stickerSet.set.emojis) { + infoTextView.setText(LocaleController.formatPluralString("EmojiCount", stickerSet.set.count)); + } else { + infoTextView.setText(LocaleController.formatPluralString("Stickers", stickerSet.set.count)); + } isUnread = unread; if (canAddRemove) { if (hasOnClick) { @@ -320,7 +324,7 @@ protected void onDraw(Canvas canvas) { canvas.drawCircle(nameTextView.getRight() + AndroidUtilities.dp(12), AndroidUtilities.dp(20), AndroidUtilities.dp(4) * unreadProgress, paint); } if (needDivider) { - canvas.drawLine(0, 0, getWidth(), 0, Theme.dividerPaint); + canvas.drawLine(0, 0, getWidth(), 0, Theme.getThemePaint(Theme.key_paint_divider, resourcesProvider)); } } @@ -347,8 +351,7 @@ public static void createThemeDescriptions(List descriptions, descriptions.add(new ThemeDescription(null, 0, null, null, null, delegate, Theme.key_chat_emojiPanelTrendingDescription)); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/GraySectionCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/GraySectionCell.java index 198e87f004..2231f56de5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/GraySectionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/GraySectionCell.java @@ -24,6 +24,7 @@ import org.telegram.ui.Components.AnimatedTextView; import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.Premium.CarouselView; import org.telegram.ui.Components.RecyclerListView; import java.util.List; @@ -72,7 +73,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(32), MeasureSpec.EXACTLY)); } - public void setTextColor(String key) { + public void setTextColor(int key) { int color = getThemedColor(key); textView.setTextColor(color); rightTextView.setTextColor(color); @@ -82,7 +83,7 @@ public CharSequence getText() { return textView.getText(); } - public void setText(String text) { + public void setText(CharSequence text) { textView.setText(text); rightTextView.setVisibility(GONE); rightTextView.setOnClickListener(null); @@ -104,6 +105,18 @@ public void setRightText(String right, boolean moveDown) { rightTextView.setVisibility(VISIBLE); } + public void setRightText(String right, OnClickListener onClickListener) { + rightTextView.setText(right, false); + rightTextView.setOnClickListener(onClickListener); + rightTextView.setVisibility(VISIBLE); + } + + public void setRightText(String right, boolean moveDown, OnClickListener onClickListener) { + rightTextView.setText(right, true, moveDown); + rightTextView.setOnClickListener(onClickListener); + rightTextView.setVisibility(VISIBLE); + } + public static void createThemeDescriptions(List descriptions, RecyclerListView listView) { descriptions.add(new ThemeDescription(listView, 0, new Class[]{GraySectionCell.class}, new String[]{"textView"}, null, null, null, Theme.key_graySectionText)); descriptions.add(new ThemeDescription(listView, 0, new Class[]{GraySectionCell.class}, new String[]{"rightTextView"}, null, null, null, Theme.key_graySectionText)); @@ -114,8 +127,7 @@ public TextView getTextView() { return textView; } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/GroupCallInvitedCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/GroupCallInvitedCell.java index fe4c347212..c62b1365b0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/GroupCallInvitedCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/GroupCallInvitedCell.java @@ -36,7 +36,7 @@ public class GroupCallInvitedCell extends FrameLayout { private Paint dividerPaint; - private String grayIconColor = Theme.key_voipgroup_mutedIcon; + private int grayIconColor = Theme.key_voipgroup_mutedIcon; private boolean needDivider; @@ -99,10 +99,8 @@ public void setDrawDivider(boolean draw) { invalidate(); } - public void setGrayIconColor(String key, int value) { - if (!grayIconColor.equals(key)) { - grayIconColor = key; - } + public void setGrayIconColor(int key, int value) { + grayIconColor = key; muteButton.setColorFilter(new PorterDuffColorFilter(value, PorterDuff.Mode.MULTIPLY)); statusTextView.setTextColor(value); Theme.setSelectorDrawableColor(muteButton.getDrawable(), value & 0x24ffffff, true); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/GroupCallTextCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/GroupCallTextCell.java index 4f4413c593..48aa4a230a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/GroupCallTextCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/GroupCallTextCell.java @@ -138,10 +138,10 @@ public void setTextColor(int color) { textView.setTextColor(color); } - public void setColors(String icon, String text) { + public void setColorsByKey(int icon, int text) { textView.setTextColor(Theme.getColor(text)); textView.setTag(text); - if (icon != null) { + if (icon >= 0) { imageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(icon), PorterDuff.Mode.MULTIPLY)); imageView.setTag(icon); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/GroupCallUserCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/GroupCallUserCell.java index 86b6cd45db..c2f0386765 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/GroupCallUserCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/GroupCallUserCell.java @@ -125,7 +125,7 @@ public class GroupCallUserCell extends FrameLayout { muteButton.playAnimation(); }; - private String grayIconColor = Theme.key_voipgroup_mutedIcon; + private int grayIconColor = Theme.key_voipgroup_mutedIcon; private Runnable checkRaiseRunnable = () -> applyParticipantChanges(true, true); @@ -585,8 +585,8 @@ public void applyParticipantChanges(boolean animated) { applyParticipantChanges(animated, false); } - public void setGrayIconColor(String key, int value) { - if (!grayIconColor.equals(key)) { + public void setGrayIconColor(int key, int value) { + if (grayIconColor != key) { if (currentIconGray) { lastMuteColor = Theme.getColor(key); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/GroupCreateUserCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/GroupCreateUserCell.java index 7fb4ff6a73..bf500bcc2c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/GroupCreateUserCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/GroupCreateUserCell.java @@ -69,13 +69,15 @@ public class GroupCreateUserCell extends FrameLayout { private boolean forceDarkTheme; private boolean showSelfAsSaved; + Theme.ResourcesProvider resourcesProvider; public GroupCreateUserCell(Context context, int checkBoxType, int pad, boolean selfAsSaved) { - this(context, checkBoxType, pad, selfAsSaved, false); + this(context, checkBoxType, pad, selfAsSaved, false, null); } - public GroupCreateUserCell(Context context, int checkBoxType, int pad, boolean selfAsSaved, boolean forCall) { + public GroupCreateUserCell(Context context, int checkBoxType, int pad, boolean selfAsSaved, boolean forCall, Theme.ResourcesProvider resourcesProvider) { super(context); + this.resourcesProvider = resourcesProvider; this.checkBoxType = checkBoxType; forceDarkTheme = forCall; @@ -96,7 +98,7 @@ public boolean setText(CharSequence value, boolean force) { } }; NotificationCenter.listenEmojiLoading(nameTextView); - nameTextView.setTextColor(Theme.getColor(forceDarkTheme ? Theme.key_voipgroup_nameText : Theme.key_windowBackgroundWhiteBlackText)); + nameTextView.setTextColor(Theme.getColor(forceDarkTheme ? Theme.key_voipgroup_nameText : Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); nameTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); nameTextView.setTextSize(16); nameTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); @@ -109,7 +111,7 @@ public boolean setText(CharSequence value, boolean force) { if (checkBoxType == 1) { checkBox = new CheckBox2(context, 21); - checkBox.setColor(null, Theme.key_windowBackgroundWhite, Theme.key_checkboxCheck); + checkBox.setColor(-1, Theme.key_windowBackgroundWhite, Theme.key_checkboxCheck); checkBox.setDrawUnchecked(false); checkBox.setDrawBackgroundAsArc(3); addView(checkBox, LayoutHelper.createFrame(24, 24, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 0 : 40 + padding, 33, LocaleController.isRTL ? 39 + padding : 0, 0)); @@ -135,6 +137,14 @@ public void setObject(Object object, CharSequence name, CharSequence status) { update(0); } + public void setForbiddenCheck(boolean forbidden) { + checkBox.setForbidden(forbidden); + } + + public CheckBox2 getCheckBox() { + return checkBox; + } + public void setChecked(boolean checked, boolean animated) { if (checkBox != null) { checkBox.setChecked(checked, animated); @@ -266,9 +276,9 @@ public void update(int mask) { if (checkBox != null) { ((LayoutParams) checkBox.getLayoutParams()).topMargin = AndroidUtilities.dp(29) + padding; if (LocaleController.isRTL) { - ((LayoutParams) checkBox.getLayoutParams()).rightMargin = AndroidUtilities.dp(39) + padding; + ((LayoutParams) checkBox.getLayoutParams()).rightMargin = AndroidUtilities.dp(40) + padding; } else { - ((LayoutParams) checkBox.getLayoutParams()).leftMargin = AndroidUtilities.dp(45) + padding; + ((LayoutParams) checkBox.getLayoutParams()).leftMargin = AndroidUtilities.dp(40) + padding; } } @@ -325,16 +335,16 @@ public void update(int mask) { if (currentStatus == null) { if (currentUser.bot) { statusTextView.setTag(Theme.key_windowBackgroundWhiteGrayText); - statusTextView.setTextColor(Theme.getColor(forceDarkTheme ? Theme.key_voipgroup_lastSeenText : Theme.key_windowBackgroundWhiteGrayText)); + statusTextView.setTextColor(Theme.getColor(forceDarkTheme ? Theme.key_voipgroup_lastSeenText : Theme.key_windowBackgroundWhiteGrayText, resourcesProvider)); statusTextView.setText(LocaleController.getString("Bot", R.string.Bot)); } else { if (currentUser.id == UserConfig.getInstance(currentAccount).getClientUserId() || currentUser.status != null && currentUser.status.expires > ConnectionsManager.getInstance(currentAccount).getCurrentTime() || MessagesController.getInstance(currentAccount).onlinePrivacy.containsKey(currentUser.id)) { statusTextView.setTag(Theme.key_windowBackgroundWhiteBlueText); - statusTextView.setTextColor(Theme.getColor(forceDarkTheme ? Theme.key_voipgroup_listeningText : Theme.key_windowBackgroundWhiteBlueText)); + statusTextView.setTextColor(Theme.getColor(forceDarkTheme ? Theme.key_voipgroup_listeningText : Theme.key_windowBackgroundWhiteBlueText, resourcesProvider)); statusTextView.setText(LocaleController.getString("Online", R.string.Online)); } else { statusTextView.setTag(Theme.key_windowBackgroundWhiteGrayText); - statusTextView.setTextColor(Theme.getColor(forceDarkTheme ? Theme.key_voipgroup_lastSeenText : Theme.key_windowBackgroundWhiteGrayText)); + statusTextView.setTextColor(Theme.getColor(forceDarkTheme ? Theme.key_voipgroup_lastSeenText : Theme.key_windowBackgroundWhiteGrayText, resourcesProvider)); statusTextView.setText(LocaleController.formatUserStatus(currentAccount, currentUser)); } } @@ -409,7 +419,7 @@ public void update(int mask) { if (currentStatus != null) { statusTextView.setText(currentStatus, true); statusTextView.setTag(Theme.key_windowBackgroundWhiteGrayText); - statusTextView.setTextColor(Theme.getColor(forceDarkTheme ? Theme.key_voipgroup_lastSeenText : Theme.key_windowBackgroundWhiteGrayText)); + statusTextView.setTextColor(Theme.getColor(forceDarkTheme ? Theme.key_voipgroup_lastSeenText : Theme.key_windowBackgroundWhiteGrayText, resourcesProvider)); } } @@ -417,7 +427,7 @@ public void update(int mask) { protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (checkBoxType == 2 && (isChecked || checkProgress > 0.0f)) { - paint.setColor(Theme.getColor(Theme.key_checkboxSquareBackground)); + paint.setColor(Theme.getColor(Theme.key_checkboxSquareBackground, resourcesProvider)); float cx = avatarImageView.getLeft() + avatarImageView.getMeasuredWidth() / 2; float cy = avatarImageView.getTop() + avatarImageView.getMeasuredHeight() / 2; canvas.drawCircle(cx, cy, AndroidUtilities.dp(18) + AndroidUtilities.dp(4) * checkProgress, paint); @@ -426,7 +436,7 @@ protected void onDraw(Canvas canvas) { int start = AndroidUtilities.dp(LocaleController.isRTL ? 0 : 72 + padding); int end = getMeasuredWidth() - AndroidUtilities.dp(!LocaleController.isRTL ? 0 : 72 + padding); if (forceDarkTheme) { - Theme.dividerExtraPaint.setColor(Theme.getColor(Theme.key_voipgroup_actionBar)); + Theme.dividerExtraPaint.setColor(Theme.getColor(Theme.key_voipgroup_actionBar, resourcesProvider)); canvas.drawRect(start, getMeasuredHeight() - 1, end, getMeasuredHeight(), Theme.dividerExtraPaint); } else { canvas.drawRect(start, getMeasuredHeight() - 1, end, getMeasuredHeight(), Theme.dividerPaint); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/HeaderCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/HeaderCell.java index e0828fbc93..8ac611ce7d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/HeaderCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/HeaderCell.java @@ -55,15 +55,15 @@ public HeaderCell(Context context, int padding, Theme.ResourcesProvider resource this(context, Theme.key_windowBackgroundWhiteBlueHeader, padding, 15, false, resourcesProvider); } - public HeaderCell(Context context, String textColorKey, int padding, int topMargin, boolean text2) { + public HeaderCell(Context context, int textColorKey, int padding, int topMargin, boolean text2) { this(context, textColorKey, padding, topMargin, text2, null); } - public HeaderCell(Context context, String textColorKey, int padding, int topMargin, boolean text2, Theme.ResourcesProvider resourcesProvider) { + public HeaderCell(Context context, int textColorKey, int padding, int topMargin, boolean text2, Theme.ResourcesProvider resourcesProvider) { this(context, textColorKey, padding, topMargin, 0, text2, resourcesProvider); } - public HeaderCell(Context context, String textColorKey, int padding, int topMargin, int bottomMargin, boolean text2, Theme.ResourcesProvider resourcesProvider) { + public HeaderCell(Context context, int textColorKey, int padding, int topMargin, int bottomMargin, boolean text2, Theme.ResourcesProvider resourcesProvider) { super(context); this.resourcesProvider = resourcesProvider; @@ -178,8 +178,7 @@ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { info.setEnabled(true); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/HintDialogCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/HintDialogCell.java index 19b5045aef..1a254677e8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/HintDialogCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/HintDialogCell.java @@ -139,7 +139,7 @@ public void update() { } } - public void setColors(String textColorKey, String backgroundColorKey) { + public void setColors(int textColorKey, int backgroundColorKey) { nameTextView.setTextColor(Theme.getColor(textColorKey)); this.backgroundColorKey = backgroundColorKey; checkBox.setColor(Theme.key_dialogRoundCheckBox, backgroundColorKey, Theme.key_dialogRoundCheckBoxCheck); @@ -180,7 +180,7 @@ public void setDialog(long uid, boolean counter, CharSequence name) { } } - private String backgroundColorKey = Theme.key_windowBackgroundWhite; + private int backgroundColorKey = Theme.key_windowBackgroundWhite; @Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/InviteUserCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/InviteUserCell.java index 6c46d193a9..2ac0b08524 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/InviteUserCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/InviteUserCell.java @@ -54,7 +54,7 @@ public InviteUserCell(Context context, boolean needCheck) { if (needCheck) { checkBox = new CheckBox2(context, 21); - checkBox.setColor(null, Theme.key_windowBackgroundWhite, Theme.key_checkboxCheck); + checkBox.setColor(-1, Theme.key_windowBackgroundWhite, Theme.key_checkboxCheck); checkBox.setDrawUnchecked(false); checkBox.setDrawBackgroundAsArc(3); addView(checkBox, LayoutHelper.createFrame(24, 24, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 0 : 40, 40, LocaleController.isRTL ? 39 : 0, 0)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationCell.java index fbe00d3509..e903a569e2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationCell.java @@ -190,8 +190,7 @@ protected void onDraw(Canvas canvas) { } } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationDirectionCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationDirectionCell.java index f60b6e743e..63529ec6dd 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationDirectionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationDirectionCell.java @@ -47,8 +47,7 @@ public void setOnButtonClick(OnClickListener onButtonClick) { frameLayout.setOnClickListener(onButtonClick); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationLoadingCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationLoadingCell.java index d28004e136..cd43984030 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationLoadingCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationLoadingCell.java @@ -63,8 +63,7 @@ public void setLoading(boolean value) { imageView.setVisibility(value ? INVISIBLE : VISIBLE); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationPoweredCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationPoweredCell.java index c84648aea5..f93835d644 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationPoweredCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationPoweredCell.java @@ -61,8 +61,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(56), MeasureSpec.EXACTLY)); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ManageChatTextCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ManageChatTextCell.java index 84b89c7459..e504ed066e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ManageChatTextCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ManageChatTextCell.java @@ -27,7 +27,7 @@ public class ManageChatTextCell extends FrameLayout { private SimpleTextView valueTextView; private ImageView imageView; private boolean divider; - private String dividerColor; + private int dividerColor = 0; public ManageChatTextCell(Context context) { super(context); @@ -58,7 +58,7 @@ public SimpleTextView getValueTextView() { return valueTextView; } - public void setDividerColor(String key) { + public void setDividerColor(int key) { dividerColor = key; } @@ -96,11 +96,11 @@ public void setTextColor(int color) { textView.setTextColor(color); } - public void setColors(String icon, String text) { + public void setColors(int iconColorKey, int text) { textView.setTextColor(Theme.getColor(text)); textView.setTag(text); - imageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(icon), PorterDuff.Mode.SRC_IN)); - imageView.setTag(icon); + imageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(iconColorKey), PorterDuff.Mode.MULTIPLY)); + imageView.setTag(iconColorKey); } public void setText(String text, String value, int resId, boolean needDivider) { @@ -124,10 +124,10 @@ public void setText(String text, String value, int resId, int paddingTop, boolea @Override protected void onDraw(Canvas canvas) { if (divider) { - if (dividerColor != null) { + if (dividerColor != 0) { Theme.dividerExtraPaint.setColor(Theme.getColor(dividerColor)); } - canvas.drawLine(AndroidUtilities.dp(71), getMeasuredHeight() - 1, getMeasuredWidth(), getMeasuredHeight() - 1, dividerColor != null ? Theme.dividerExtraPaint : Theme.dividerPaint); + canvas.drawLine(AndroidUtilities.dp(71), getMeasuredHeight() - 1, getMeasuredWidth(), getMeasuredHeight() - 1, dividerColor != 0 ? Theme.dividerExtraPaint : Theme.dividerPaint); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ManageChatUserCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ManageChatUserCell.java index 6801104b5a..5466fc7796 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ManageChatUserCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ManageChatUserCell.java @@ -61,7 +61,7 @@ public class ManageChatUserCell extends FrameLayout { private int currentAccount = UserConfig.selectedAccount; - private String dividerColor; + private int dividerColor = -1; private ManageChatUserCellDelegate delegate; @@ -185,7 +185,7 @@ public void setNameColor(int color) { nameTextView.setTextColor(color); } - public void setDividerColor(String key) { + public void setDividerColor(int key) { dividerColor = key; } @@ -352,10 +352,10 @@ public boolean hasOverlappingRendering() { @Override protected void onDraw(Canvas canvas) { if (needDivider) { - if (dividerColor != null) { + if (dividerColor >= 0) { Theme.dividerExtraPaint.setColor(Theme.getColor(dividerColor, resourcesProvider)); } - canvas.drawLine(LocaleController.isRTL ? 0 : AndroidUtilities.dp(68), getMeasuredHeight() - 1, getMeasuredWidth() - (LocaleController.isRTL ? AndroidUtilities.dp(68) : 0), getMeasuredHeight() - 1, dividerColor != null ? Theme.dividerExtraPaint : Theme.dividerPaint); + canvas.drawLine(LocaleController.isRTL ? 0 : AndroidUtilities.dp(68), getMeasuredHeight() - 1, getMeasuredWidth() - (LocaleController.isRTL ? AndroidUtilities.dp(68) : 0), getMeasuredHeight() - 1, dividerColor >= 0 ? Theme.dividerExtraPaint : Theme.dividerPaint); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/MemberRequestCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/MemberRequestCell.java index a68fa6c35d..d931fcaab5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/MemberRequestCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/MemberRequestCell.java @@ -53,7 +53,7 @@ public MemberRequestCell(@NonNull Context context, OnClickListener clickListener int btnPadding = AndroidUtilities.dp(17); TextView addButton = new TextView(getContext()); - addButton.setBackground(Theme.AdaptiveRipple.filledRect(Theme.key_featuredStickers_addButton, 4)); + addButton.setBackground(Theme.AdaptiveRipple.filledRectByKey(Theme.key_featuredStickers_addButton, 4)); addButton.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); addButton.setMaxLines(1); addButton.setPadding(btnPadding, 0, btnPadding, 0); @@ -121,7 +121,9 @@ public void setData(LongSparseArray users, TLRPC.TL_chatInviteImport avatarImageView.setForUserOrChat(user, avatarDrawable); nameTextView.setText(UserObject.getUserName(user)); String dateText = LocaleController.formatDateAudio(importer.date, false); - if (importer.approved_by == 0) { + if (importer.via_chatlist) { + statusTextView.setText(LocaleController.getString("JoinedViaFolder", R.string.JoinedViaFolder)); + } else if (importer.approved_by == 0) { statusTextView.setText(LocaleController.formatString("RequestedToJoinAt", R.string.RequestedToJoinAt, dateText)); } else { TLRPC.User approvedByUser = users.get(importer.approved_by); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/MentionCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/MentionCell.java index 70f39d0471..f13b6490b1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/MentionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/MentionCell.java @@ -251,9 +251,8 @@ public void setIsDarkTheme(boolean isDarkTheme) { } } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } private boolean attached; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/NotificationsCheckCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/NotificationsCheckCell.java index 874b4876f2..4030f5c3a0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/NotificationsCheckCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/NotificationsCheckCell.java @@ -32,7 +32,7 @@ public class NotificationsCheckCell extends FrameLayout { private TextView textView; private TextView valueTextView; @SuppressWarnings("FieldCanBeLocal") - private ImageView moveImageView; + private ImageView imageView; private Switch checkBox; private boolean needDivider; private boolean drawLine = true; @@ -49,24 +49,22 @@ public NotificationsCheckCell(Context context, Theme.ResourcesProvider resources this(context, 21, 70, false, resourcesProvider); } - public NotificationsCheckCell(Context context, int padding, int height, boolean reorder) { - this(context, padding, height, reorder, null); + public NotificationsCheckCell(Context context, int padding, int height, boolean withImage) { + this(context, padding, height, withImage, null); } - public NotificationsCheckCell(Context context, int padding, int height, boolean reorder, Theme.ResourcesProvider resourcesProvider) { + public NotificationsCheckCell(Context context, int padding, int height, boolean withImage, Theme.ResourcesProvider resourcesProvider) { super(context); this.resourcesProvider = resourcesProvider; setWillNotDraw(false); currentHeight = height; - if (reorder) { - moveImageView = new ImageView(context); - moveImageView.setFocusable(false); - moveImageView.setScaleType(ImageView.ScaleType.CENTER); - moveImageView.setImageResource(R.drawable.poll_reorder); - moveImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_windowBackgroundWhiteGrayIcon, resourcesProvider), PorterDuff.Mode.SRC_IN)); - addView(moveImageView, LayoutHelper.createFrame(48, 48, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL, 6, 0, 6, 0)); + if (withImage) { + imageView = new ImageView(context); + imageView.setFocusable(false); + imageView.setScaleType(ImageView.ScaleType.CENTER); + addView(imageView, LayoutHelper.createFrame(48, 48, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL, 8, 0, 8, 0)); } textView = new TextView(context); @@ -77,7 +75,7 @@ public NotificationsCheckCell(Context context, int padding, int height, boolean textView.setSingleLine(true); textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); textView.setEllipsize(TextUtils.TruncateAt.END); - addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 80 : (reorder ? 64 : padding), 13 + (currentHeight - 70) / 2, LocaleController.isRTL ? (reorder ? 64 : padding) : 80, 0)); + addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 80 : (withImage ? 64 : padding), 13 + (currentHeight - 70) / 2, LocaleController.isRTL ? (withImage ? 64 : padding) : 80, 0)); valueTextView = new TextView(context); valueTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2, resourcesProvider)); @@ -88,7 +86,7 @@ public NotificationsCheckCell(Context context, int padding, int height, boolean valueTextView.setSingleLine(true); valueTextView.setPadding(0, 0, 0, 0); valueTextView.setEllipsize(TextUtils.TruncateAt.END); - addView(valueTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 80 : (reorder ? 64 : padding), 38 + (currentHeight - 70) / 2, LocaleController.isRTL ? (reorder ? 64 : padding) : 80, 0)); + addView(valueTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 80 : (withImage ? 64 : padding), 38 - (withImage ? 2 : 0) + (currentHeight - 70) / 2, LocaleController.isRTL ? (withImage ? 64 : padding) : 80, 0)); checkBox = new Switch(context, resourcesProvider); checkBox.setColors(Theme.key_switchTrack, Theme.key_switchTrackChecked, Theme.key_windowBackgroundWhite, Theme.key_windowBackgroundWhite); @@ -114,13 +112,20 @@ public void setTextAndValueAndCheck(String text, CharSequence value, boolean che } public void setTextAndValueAndCheck(String text, CharSequence value, boolean checked, int iconType, boolean multiline, boolean divider) { + setTextAndValueAndIconAndCheck(text, value, 0, checked, iconType, multiline, divider); + } + + public void setTextAndValueAndIconAndCheck(String text, CharSequence value, int iconResId, boolean checked, int iconType, boolean multiline, boolean divider) { textView.setText(text); valueTextView.setText(value); + if (imageView != null) { + imageView.setImageResource(iconResId); + imageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_dialogIcon), PorterDuff.Mode.MULTIPLY)); + } checkBox.setChecked(checked, iconType, animationsEnabled); valueTextView.setVisibility(VISIBLE); needDivider = divider; isMultiline = multiline; - if (multiline) { valueTextView.setLines(0); valueTextView.setMaxLines(0); @@ -156,7 +161,13 @@ public boolean isChecked() { @Override protected void onDraw(Canvas canvas) { if (needDivider) { - canvas.drawLine(0, getMeasuredHeight() - 1, getMeasuredWidth(), getMeasuredHeight() - 1, Theme.dividerPaint); + canvas.drawLine( + LocaleController.isRTL ? 0 : AndroidUtilities.dp(imageView != null ? 64 : 20), + getMeasuredHeight() - 1, + getMeasuredWidth() - (LocaleController.isRTL ? AndroidUtilities.dp(imageView != null ? 64 : 20) : 0), + getMeasuredHeight() - 1, + Theme.dividerPaint + ); } if (drawLine) { int x = LocaleController.isRTL ? AndroidUtilities.dp(76) : getMeasuredWidth() - AndroidUtilities.dp(76) - 1; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoAttachCameraCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoAttachCameraCell.java index 5ce4752349..6e812848be 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoAttachCameraCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoAttachCameraCell.java @@ -100,8 +100,7 @@ public Drawable getDrawable() { return backgroundView.getDrawable(); } - protected int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + protected int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoAttachPermissionCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoAttachPermissionCell.java index d93c6f125e..dd49a22d60 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoAttachPermissionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoAttachPermissionCell.java @@ -89,8 +89,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(MeasureSpec.makeMeasureSpec(itemSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(itemSize + AndroidUtilities.dp(5), MeasureSpec.EXACTLY)); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoAttachPhotoCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoAttachPhotoCell.java index 2cfdb3c5ad..be86c0f408 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoAttachPhotoCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoAttachPhotoCell.java @@ -338,7 +338,7 @@ public void setPhotoEntry(MediaController.PhotoEntry entry, boolean needCheckSho if (photoEntry.isVideo) { imageView.setImage("vthumb://" + photoEntry.imageId + ":" + photoEntry.path, null, Theme.chat_attachEmptyDrawable); } else { - imageView.setOrientation(photoEntry.orientation, true); + imageView.setOrientation(photoEntry.orientation, photoEntry.invert, true); imageView.setImage("thumb://" + photoEntry.imageId + ":" + photoEntry.path, null, Theme.chat_attachEmptyDrawable); } } else { @@ -568,8 +568,7 @@ public boolean performAccessibilityAction(int action, Bundle arguments) { return super.performAccessibilityAction(action, arguments); } - protected int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + protected int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoEditToolCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoEditToolCell.java index 23a3640d7e..e9baf126a4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoEditToolCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoEditToolCell.java @@ -142,8 +142,7 @@ public void setIconAndTextAndValue(String text, float value, int min, int max) { seekBar.setProgress((int) value, false); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerAlbumsCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerAlbumsCell.java index 3d8c3ce657..ef1c17363f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerAlbumsCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerAlbumsCell.java @@ -133,7 +133,7 @@ public void setAlbum(int a, MediaController.AlbumEntry albumEntry) { AlbumView albumView = albumViews[a]; albumView.imageView.setOrientation(0, true); if (albumEntry.coverPhoto != null && albumEntry.coverPhoto.path != null) { - albumView.imageView.setOrientation(albumEntry.coverPhoto.orientation, true); + albumView.imageView.setOrientation(albumEntry.coverPhoto.orientation, albumEntry.coverPhoto.invert, true); if (albumEntry.coverPhoto.isVideo) { albumView.imageView.setImage("vthumb://" + albumEntry.coverPhoto.imageId + ":" + albumEntry.coverPhoto.path, null, Theme.chat_attachEmptyDrawable); } else { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerPhotoCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerPhotoCell.java index 56ce5ca73e..653d76adc6 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerPhotoCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoPickerPhotoCell.java @@ -136,7 +136,7 @@ public void setImage(MediaController.PhotoEntry photoEntry) { if (photoEntry.thumbPath != null) { imageView.setImage(photoEntry.thumbPath, null, thumb); } else if (photoEntry.path != null) { - imageView.setOrientation(photoEntry.orientation, true); + imageView.setOrientation(photoEntry.orientation, photoEntry.invert, true); if (photoEntry.isVideo) { videoInfoContainer.setVisibility(View.VISIBLE); videoTextView.setText(AndroidUtilities.formatShortDuration(photoEntry.duration)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PollEditTextCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PollEditTextCell.java index 206ab03926..d0f25630e2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PollEditTextCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PollEditTextCell.java @@ -138,7 +138,7 @@ public boolean onTouchEvent(MotionEvent event) { textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); textView.setHintTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteHintText)); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); - textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); +// textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); textView.setBackgroundDrawable(null); textView.setImeOptions(textView.getImeOptions() | EditorInfo.IME_FLAG_NO_EXTRACT_UI); textView.setInputType(textView.getInputType() | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES); @@ -170,7 +170,7 @@ public boolean onTouchEvent(MotionEvent event) { addView(textView2, LayoutHelper.createFrame(48, 24, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.TOP, LocaleController.isRTL ? 20 : 0, 43, LocaleController.isRTL ? 0 : 20, 0)); checkBox = new CheckBox2(context, 21); - checkBox.setColor(null, Theme.key_windowBackgroundWhiteGrayIcon, Theme.key_checkboxCheck); + checkBox.setColor(-1, Theme.key_windowBackgroundWhiteGrayIcon, Theme.key_checkboxCheck); checkBox.setContentDescription(LocaleController.getString("AccDescrQuizCorrectAnswer", R.string.AccDescrQuizCorrectAnswer)); checkBox.setDrawUnchecked(true); checkBox.setChecked(true, false); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ProfileSearchCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ProfileSearchCell.java index 87518ea00a..03c090adeb 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ProfileSearchCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ProfileSearchCell.java @@ -19,6 +19,8 @@ import android.view.MotionEvent; import android.view.accessibility.AccessibilityNodeInfo; +import androidx.annotation.NonNull; + import org.telegram.PhoneFormat.PhoneFormat; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ChatObject; @@ -35,6 +37,7 @@ import org.telegram.messenger.UserObject; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Components.AnimatedEmojiDrawable; import org.telegram.ui.Components.AvatarDrawable; @@ -43,7 +46,10 @@ import org.telegram.ui.Components.CombinedDrawable; import org.telegram.ui.Components.Premium.PremiumGradient; import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.LaunchActivity; import org.telegram.ui.NotificationsSettingsActivity; +import org.telegram.ui.Stories.StoriesListPlaceProvider; +import org.telegram.ui.Stories.StoriesUtilities; import java.util.Locale; @@ -52,7 +58,7 @@ public class ProfileSearchCell extends BaseCell implements NotificationCenter.NotificationCenterDelegate { private CharSequence currentName; - private ImageReceiver avatarImage; + public ImageReceiver avatarImage; private AvatarDrawable avatarDrawable; private CharSequence subLabel; private Theme.ResourcesProvider resourcesProvider; @@ -102,6 +108,7 @@ public class ProfileSearchCell extends BaseCell implements NotificationCenter.No private int statusLeft; private StaticLayout statusLayout; private AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable statusDrawable; + public StoriesUtilities.AvatarStoryParams avatarStoryParams = new StoriesUtilities.AvatarStoryParams(false); private RectF rect = new RectF(); @@ -120,12 +127,18 @@ public ProfileSearchCell(Context context, Theme.ResourcesProvider resourcesProvi avatarDrawable = new AvatarDrawable(); checkBox = new CheckBox2(context, 21, resourcesProvider); - checkBox.setColor(null, Theme.key_windowBackgroundWhite, Theme.key_checkboxCheck); + checkBox.setColor(-1, Theme.key_windowBackgroundWhite, Theme.key_checkboxCheck); checkBox.setDrawUnchecked(false); checkBox.setDrawBackgroundAsArc(3); addView(checkBox); statusDrawable = new AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable(this, AndroidUtilities.dp(20)); + statusDrawable.setCallback(this); + } + + @Override + protected boolean verifyDrawable(@NonNull Drawable who) { + return statusDrawable == who || super.verifyDrawable(who); } public void setData(Object object, TLRPC.EncryptedChat ec, CharSequence n, CharSequence s, boolean needCount, boolean saved) { @@ -219,6 +232,7 @@ protected void onDetachedFromWindow() { super.onDetachedFromWindow(); avatarImage.onDetachedFromWindow(); NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.emojiLoaded); + statusDrawable.detach(); } @Override @@ -226,6 +240,7 @@ protected void onAttachedToWindow() { super.onAttachedToWindow(); avatarImage.onAttachedToWindow(); NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.emojiLoaded); + statusDrawable.attach(); } @Override @@ -329,6 +344,11 @@ public void buildLayout() { }); } } + if (!LocaleController.isRTL) { + statusLeft = AndroidUtilities.dp(AndroidUtilities.leftBaseline); + } else { + statusLeft = AndroidUtilities.dp(11); + } if (currentName != null) { nameString = currentName; @@ -372,6 +392,7 @@ public void buildLayout() { } else { actionLeft = AndroidUtilities.dp(19) + AndroidUtilities.dp(16); nameLeft += w; + statusLeft += w; } nameWidth -= AndroidUtilities.dp(32) + w; } @@ -395,6 +416,7 @@ public void buildLayout() { } else { countLeft = AndroidUtilities.dp(19); nameLeft += w; + statusLeft += w; } } else { lastUnreadCount = 0; @@ -416,12 +438,6 @@ public void buildLayout() { CharSequence statusString = null; TextPaint currentStatusPaint = Theme.dialogs_offlinePaint; - if (!LocaleController.isRTL) { - statusLeft = AndroidUtilities.dp(AndroidUtilities.leftBaseline); - } else { - statusLeft = AndroidUtilities.dp(11); - } - if (chat == null || subLabel != null) { if (subLabel != null) { statusString = subLabel; @@ -494,8 +510,7 @@ public void buildLayout() { } else { avatarLeft = AndroidUtilities.dp(11) + getPaddingLeft(); } - - avatarImage.setImageCoords(avatarLeft, AndroidUtilities.dp(7), AndroidUtilities.dp(46), AndroidUtilities.dp(46)); + avatarStoryParams.originalAvatarRect.set(avatarLeft, AndroidUtilities.dp(7), avatarLeft + AndroidUtilities.dp(46), AndroidUtilities.dp(7) + AndroidUtilities.dp(46)); double widthpx; float left; @@ -545,6 +560,7 @@ public void buildLayout() { } public void updateStatus(boolean verified, TLRPC.User user, boolean animated) { + statusDrawable.center = LocaleController.isRTL; if (verified) { statusDrawable.set(new CombinedDrawable(Theme.dialogs_verifiedDrawable, Theme.dialogs_verifiedCheckDrawable, 0, 0), animated); statusDrawable.setColor(null); @@ -695,10 +711,10 @@ protected void onDraw(Canvas canvas) { int x; if (LocaleController.isRTL) { if (nameLayout.getLineLeft(0) == 0) { - x = nameLeft - AndroidUtilities.dp(6) - statusDrawable.getIntrinsicWidth(); + x = nameLeft - AndroidUtilities.dp(3) - statusDrawable.getIntrinsicWidth(); } else { float w = nameLayout.getLineWidth(0); - x = (int) (nameLeft + nameWidth - Math.ceil(w) - AndroidUtilities.dp(6) - statusDrawable.getIntrinsicWidth()); + x = (int) (nameLeft + nameWidth - Math.ceil(w) - AndroidUtilities.dp(3) - statusDrawable.getIntrinsicWidth()); } } else { x = (int) (nameLeft + nameLayout.getLineRight(0) + AndroidUtilities.dp(6)); @@ -737,7 +753,7 @@ protected void onDraw(Canvas canvas) { actionLayout.draw(canvas); canvas.restore(); } - avatarImage.draw(canvas); + StoriesUtilities.drawAvatarWithStory(dialog_id, canvas, avatarImage, avatarStoryParams); } @Override @@ -783,6 +799,9 @@ public boolean onInterceptTouchEvent(MotionEvent ev) { @Override public boolean onTouchEvent(MotionEvent event) { + if (avatarStoryParams.checkOnTouchEvent(event, this)) { + return true; + } if (actionButton != null && actionButton.checkTouchEvent(event)) { return true; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/RadioColorCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/RadioColorCell.java index bf7f44243f..6599bce790 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/RadioColorCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/RadioColorCell.java @@ -109,8 +109,7 @@ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { info.setChecked(radioButton.isChecked()); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ReactedUserHolderView.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ReactedUserHolderView.java new file mode 100644 index 0000000000..edf2236190 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ReactedUserHolderView.java @@ -0,0 +1,285 @@ +package org.telegram.ui.Cells; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.text.TextUtils; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityNodeInfo; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.DocumentObject; +import org.telegram.messenger.Emoji; +import org.telegram.messenger.ImageLocation; +import org.telegram.messenger.MediaDataController; +import org.telegram.messenger.MessageObject; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.R; +import org.telegram.messenger.SvgHelper; +import org.telegram.messenger.UserObject; +import org.telegram.tgnet.TLObject; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.SimpleTextView; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AnimatedEmojiDrawable; +import org.telegram.ui.Components.AvatarDrawable; +import org.telegram.ui.Components.BackupImageView; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.MessageSeenCheckDrawable; +import org.telegram.ui.Components.Premium.PremiumGradient; +import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble; +import org.telegram.messenger.LocaleController; +import org.telegram.ui.Stories.StoriesUtilities; + +public class ReactedUserHolderView extends FrameLayout { + int currentAccount; + + public static int STYLE_DEFAULT = 0; + public static int STYLE_STORY = 1; + + public BackupImageView avatarView; + SimpleTextView titleView; + SimpleTextView subtitleView; + BackupImageView reactView; + AvatarDrawable avatarDrawable = new AvatarDrawable(); + View overlaySelectorView; + AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable rightDrawable; + public final static int ITEM_HEIGHT_DP = 50; + public final static int STORY_ITEM_HEIGHT_DP = 58; + Theme.ResourcesProvider resourcesProvider; + int style; + public long dialogId; + public StoriesUtilities.AvatarStoryParams params = new StoriesUtilities.AvatarStoryParams(false) { + @Override + public void openStory(long dialogId, Runnable onDone) { + ReactedUserHolderView.this.openStory(dialogId, onDone); + } + }; + + public void openStory(long dialogId, Runnable onDone) { + + } + + public static final MessageSeenCheckDrawable seenDrawable = new MessageSeenCheckDrawable(R.drawable.msg_mini_checks, Theme.key_windowBackgroundWhiteGrayText); + public static final MessageSeenCheckDrawable reactDrawable = new MessageSeenCheckDrawable(R.drawable.msg_reactions, Theme.key_windowBackgroundWhiteGrayText, 16, 16, 5.66f); + + public ReactedUserHolderView(int style, int currentAccount, @NonNull Context context, Theme.ResourcesProvider resourcesProvider) { + super(context); + this.style = style; + this.currentAccount = currentAccount; + this.resourcesProvider = resourcesProvider; + setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, AndroidUtilities.dp(ITEM_HEIGHT_DP))); + + int avatarSize = style == STYLE_STORY ? 48 : 34; + avatarView = new BackupImageView(context) { + @Override + protected void onDraw(Canvas canvas) { + if (style == STYLE_STORY) { + params.originalAvatarRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight()); + StoriesUtilities.drawAvatarWithStory(dialogId, canvas, getImageReceiver(), params); + } else { + super.onDraw(canvas); + } + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + return params.checkOnTouchEvent(event, this); + } + }; + avatarView.setRoundRadius(AndroidUtilities.dp(avatarSize)); + addView(avatarView, LayoutHelper.createFrameRelatively(avatarSize, avatarSize, Gravity.START | Gravity.CENTER_VERTICAL, 10, 0, 0, 0)); + if (style == STYLE_STORY) { + setClipChildren(false); + } + titleView = new SimpleTextView(context) { + @Override + public boolean setText(CharSequence value) { + value = Emoji.replaceEmoji(value, getPaint().getFontMetricsInt(), AndroidUtilities.dp(14), false); + return super.setText(value); + } + }; + NotificationCenter.listenEmojiLoading(titleView); + titleView.setTextSize(16); + titleView.setTextColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuItem, resourcesProvider)); + titleView.setEllipsizeByGradient(true); + titleView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); + titleView.setRightPadding(AndroidUtilities.dp(30)); + titleView.setTranslationX(LocaleController.isRTL ? AndroidUtilities.dp(30) : 0); + titleView.setRightDrawableOutside(true); + float topMargin = style == STYLE_STORY ? 7.66f : 5.33f; + float leftMargin = style == STYLE_STORY ? 73 : 55; + addView(titleView, LayoutHelper.createFrameRelatively(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.FILL_HORIZONTAL | Gravity.TOP, leftMargin, topMargin, 12, 0)); + + rightDrawable = new AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable(this, AndroidUtilities.dp(18)); + titleView.setDrawablePadding(AndroidUtilities.dp(3)); + titleView.setRightDrawable(rightDrawable); + + subtitleView = new SimpleTextView(context); + subtitleView.setTextSize(13); + subtitleView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText, resourcesProvider)); + subtitleView.setEllipsizeByGradient(true); + subtitleView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); + subtitleView.setTranslationX(LocaleController.isRTL ? AndroidUtilities.dp(30) : 0); + topMargin = style == STYLE_STORY ? 24f : 19f; + addView(subtitleView, LayoutHelper.createFrameRelatively(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.FILL_HORIZONTAL | Gravity.TOP, leftMargin, topMargin , 20, 0)); + + reactView = new BackupImageView(context); + addView(reactView, LayoutHelper.createFrameRelatively(24, 24, Gravity.END | Gravity.CENTER_VERTICAL, 0, 0, 12, 0)); + + overlaySelectorView = new View(context); + overlaySelectorView.setBackground(Theme.getSelectorDrawable(false)); + addView(overlaySelectorView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + } + + public void setUserReaction(TLRPC.User user, TLRPC.Chat chat, TLRPC.Reaction reaction, long date, boolean dateIsSeen, boolean animated) { + TLObject u = user; + if (u == null) { + u = chat; + } + if (u == null) { + return; + } + + Long documentId = u instanceof TLRPC.User ? UserObject.getEmojiStatusDocumentId((TLRPC.User) u) : null; + if (documentId == null) { + if (user != null && user.premium) { + rightDrawable.set(PremiumGradient.getInstance().premiumStarDrawableMini, false); + } else { + rightDrawable.set((Drawable) null, false); + } + } else { + rightDrawable.set(documentId, false); + } + rightDrawable.setColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); + + avatarDrawable.setInfo(u); + if (user != null) { + dialogId = user.id; + titleView.setText(UserObject.getUserName(user)); + } else { + dialogId = chat.id; + titleView.setText(chat.title); + } + + Drawable thumb = avatarDrawable; + if (user != null) { + if (user.photo != null && user.photo.strippedBitmap != null) { + thumb = user.photo.strippedBitmap; + } + } else { + if (chat.photo != null && chat.photo.strippedBitmap != null) { + thumb = chat.photo.strippedBitmap; + } + } + avatarView.setImage(ImageLocation.getForUserOrChat(u, ImageLocation.TYPE_SMALL), "50_50", thumb, u); + + String contentDescription; + boolean hasReactImage = false; + if (reaction != null) { + ReactionsLayoutInBubble.VisibleReaction visibleReaction = ReactionsLayoutInBubble.VisibleReaction.fromTLReaction(reaction); + if (visibleReaction.emojicon != null) { + TLRPC.TL_availableReaction r = MediaDataController.getInstance(currentAccount).getReactionsMap().get(visibleReaction.emojicon); + if (r != null) { + SvgHelper.SvgDrawable svgThumb = DocumentObject.getSvgThumb(r.static_icon.thumbs, Theme.key_windowBackgroundGray, 1.0f); + reactView.setImage(ImageLocation.getForDocument(r.center_icon), "40_40_lastreactframe", "webp", svgThumb, r); + hasReactImage = true; + } else { + reactView.setImageDrawable(null); + } + } else { + AnimatedEmojiDrawable drawable = new AnimatedEmojiDrawable(AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES, currentAccount, visibleReaction.documentId); + drawable.setColorFilter(Theme.getAnimatedEmojiColorFilter(resourcesProvider)); + reactView.setAnimatedEmojiDrawable(drawable); + hasReactImage = true; + } + contentDescription = LocaleController.formatString("AccDescrReactedWith", R.string.AccDescrReactedWith, titleView.getText(), visibleReaction.emojicon != null ? visibleReaction.emojicon : reaction); + } else { + reactView.setImageDrawable(null); + contentDescription = LocaleController.formatString("AccDescrPersonHasSeen", R.string.AccDescrPersonHasSeen, titleView.getText()); + } + + if (date != 0) { + contentDescription += " " + LocaleController.formatSeenDate(date); + } + setContentDescription(contentDescription); + + if (date != 0) { + subtitleView.setVisibility(View.VISIBLE); + CharSequence icon = dateIsSeen ? seenDrawable.getSpanned(getContext(), resourcesProvider) : reactDrawable.getSpanned(getContext(), resourcesProvider); + subtitleView.setText(TextUtils.concat(icon, LocaleController.formatSeenDate(date))); + subtitleView.setTranslationY(!dateIsSeen ? AndroidUtilities.dp(-1) : 0); + titleView.setTranslationY(0); + if (animated) { + titleView.setTranslationY(AndroidUtilities.dp(9)); + titleView.animate().translationY(0); + subtitleView.setAlpha(0); + subtitleView.animate().alpha(1f); + } + } else { + subtitleView.setVisibility(View.GONE); + titleView.setTranslationY(AndroidUtilities.dp(9)); + } + + titleView.setRightPadding(AndroidUtilities.dp(hasReactImage ? 30 : 0)); + titleView.setTranslationX(hasReactImage && LocaleController.isRTL ? AndroidUtilities.dp(30) : 0); + ((MarginLayoutParams) subtitleView.getLayoutParams()).rightMargin = AndroidUtilities.dp(hasReactImage && !LocaleController.isRTL ? 12 + 24 : 12); + subtitleView.setTranslationX(hasReactImage && LocaleController.isRTL ? AndroidUtilities.dp(30) : 0); + } + + public void setUserReaction(TLRPC.MessagePeerReaction reaction) { + if (reaction == null) { + return; + } + TLRPC.User user = null; + TLRPC.Chat chat = null; + long dialogId = MessageObject.getPeerId(reaction.peer_id); + if (dialogId > 0) { + user = MessagesController.getInstance(currentAccount).getUser(dialogId); + } else { + chat = MessagesController.getInstance(currentAccount).getChat(-dialogId); + } + setUserReaction(user, chat, reaction.reaction, reaction.date, reaction.dateIsSeen, false); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int h = style == STYLE_DEFAULT ? ITEM_HEIGHT_DP : STORY_ITEM_HEIGHT_DP; + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(h), MeasureSpec.EXACTLY)); + } + + @Override + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(info); + info.setEnabled(true); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (rightDrawable != null) { + rightDrawable.attach(); + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (rightDrawable != null) { + rightDrawable.detach(); + } + params.onDetachFromWindow(); + } + + public void setObject(TLRPC.User user, long date, boolean b) { + + } +} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/RequestPeerRequirementsCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/RequestPeerRequirementsCell.java index 04cf51d801..fa99fb01e2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/RequestPeerRequirementsCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/RequestPeerRequirementsCell.java @@ -89,7 +89,7 @@ public void set(TLRPC.RequestPeerType requestPeerType) { } addView(emptyView(12, Theme.getColor(Theme.key_windowBackgroundWhite)), LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - addView(emptyView(12, Theme.getThemedDrawable(getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)), LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + addView(emptyView(12, Theme.getThemedDrawableByKey(getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)), LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SendLocationCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SendLocationCell.java index 956d519127..1ab6081f25 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SendLocationCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SendLocationCell.java @@ -182,8 +182,7 @@ protected void onDraw(Canvas canvas) { canvas.drawText(text, rect.centerX() - size / 2, AndroidUtilities.dp(37), Theme.chat_livePaint); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SessionCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SessionCell.java index c88ce13724..5b7a24cf4c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SessionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SessionCell.java @@ -304,7 +304,7 @@ public static Drawable createDrawable(TLRPC.TL_authorization session) { } String deviceModel = session.device_model.toLowerCase(); int iconId; - String colorKey, colorKey2; + int colorKey, colorKey2; if (deviceModel.contains("safari")) { iconId = R.drawable.device_web_safari; colorKey = Theme.key_avatar_backgroundPink; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SettingsSuggestionCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SettingsSuggestionCell.java index 19146b46cd..cfaf78e02d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SettingsSuggestionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SettingsSuggestionCell.java @@ -64,7 +64,7 @@ public SettingsSuggestionCell(Context context, Theme.ResourcesProvider resources for (int a = 0; a < 2; a++) { TextView textView = new TextView(context); - textView.setBackground(Theme.AdaptiveRipple.filledRect(Theme.key_featuredStickers_addButton, 4)); + textView.setBackground(Theme.AdaptiveRipple.filledRectByKey(Theme.key_featuredStickers_addButton, 4)); textView.setLines(1); textView.setSingleLine(true); textView.setGravity(Gravity.CENTER_HORIZONTAL); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ShareDialogCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ShareDialogCell.java index e6afdec21b..2fab80ce6a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ShareDialogCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ShareDialogCell.java @@ -285,9 +285,8 @@ protected void onDraw(Canvas canvas) { super.onDraw(canvas); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ShareTopicCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ShareTopicCell.java index a9f3a574c7..508aa7dc10 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ShareTopicCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ShareTopicCell.java @@ -105,8 +105,7 @@ public long getCurrentTopic() { return currentTopic; } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedAudioCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedAudioCell.java index 623ac1c0a0..3e73f3a8ab 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedAudioCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedAudioCell.java @@ -57,14 +57,17 @@ public class SharedAudioCell extends FrameLayout implements DownloadController.F private int titleY = AndroidUtilities.dp(9); private StaticLayout titleLayout; + private float titleLayoutLeft, titleLayoutWidth; AnimatedEmojiSpan.EmojiGroupedSpans titleLayoutEmojis; private int descriptionY = AndroidUtilities.dp(29); AnimatedEmojiSpan.EmojiGroupedSpans descriptionLayoutEmojis; + private float descriptionLayoutLeft, descriptionLayoutWidth; private StaticLayout descriptionLayout; private int captionY = AndroidUtilities.dp(29); AnimatedEmojiSpan.EmojiGroupedSpans captionLayoutEmojis; + private float captionLayoutLeft, captionLayoutWidth; private StaticLayout captionLayout; private MessageObject currentMessageObject; @@ -93,6 +96,8 @@ public class SharedAudioCell extends FrameLayout implements DownloadController.F boolean showName = true; float showNameProgress = 0; + TextPaint titlePaint; + public SharedAudioCell(Context context) { this(context, VIEW_TYPE_DEFAULT, null); } @@ -109,14 +114,14 @@ public SharedAudioCell(Context context, int viewType, Theme.ResourcesProvider re setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); radialProgress = new RadialProgress2(this, resourcesProvider); - radialProgress.setColors(Theme.key_chat_inLoader, Theme.key_chat_inLoaderSelected, Theme.key_chat_inMediaIcon, Theme.key_chat_inMediaIconSelected); + radialProgress.setColorKeys(Theme.key_chat_inLoader, Theme.key_chat_inLoaderSelected, Theme.key_chat_inMediaIcon, Theme.key_chat_inMediaIconSelected); TAG = DownloadController.getInstance(currentAccount).generateObserverTag(); setWillNotDraw(false); checkBox = new CheckBox2(context, 22, resourcesProvider); checkBox.setVisibility(INVISIBLE); - checkBox.setColor(null, Theme.key_windowBackgroundWhite, Theme.key_checkboxCheck); + checkBox.setColor(-1, Theme.key_windowBackgroundWhite, Theme.key_checkboxCheck); checkBox.setDrawUnchecked(false); checkBox.setDrawBackgroundAsArc(3); addView(checkBox, LayoutHelper.createFrame(24, 24, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 0 : 38.1f, 32.1f, LocaleController.isRTL ? 6 : 0, 0)); @@ -131,6 +136,13 @@ public SharedAudioCell(Context context, int viewType, Theme.ResourcesProvider re captionTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); captionTextPaint.setTextSize(AndroidUtilities.dp(13)); + + if (resourcesProvider != null) { + titlePaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + titlePaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + titlePaint.setTextSize(AndroidUtilities.dp(15)); + titlePaint.setColor(getThemedColor(Theme.key_windowBackgroundWhiteBlackText)); + } } @SuppressLint("DrawAllocation") @@ -156,7 +168,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { try { CharSequence title; if (viewType == VIEW_TYPE_GLOBAL_SEARCH && (currentMessageObject.isVoice() || currentMessageObject.isRoundVideo())) { - title = FilteredSearchView.createFromInfoString(currentMessageObject); + title = FilteredSearchView.createFromInfoString(currentMessageObject, 1); } else { title = currentMessageObject.getMusicTitle().replace('\n', ' '); } @@ -164,8 +176,11 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (titleH != null) { title = titleH; } - CharSequence titleFinal = TextUtils.ellipsize(title, Theme.chat_contextResult_titleTextPaint, maxWidth - dateWidth, TextUtils.TruncateAt.END); - titleLayout = new StaticLayout(titleFinal, Theme.chat_contextResult_titleTextPaint, maxWidth + AndroidUtilities.dp(4) - dateWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + TextPaint titlePaintFinal = titlePaint != null ? titlePaint : Theme.chat_contextResult_titleTextPaint; + CharSequence titleFinal = TextUtils.ellipsize(title, titlePaintFinal, maxWidth - dateWidth, TextUtils.TruncateAt.END); + titleLayout = new StaticLayout(titleFinal, titlePaintFinal, maxWidth + AndroidUtilities.dp(4) - dateWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + titleLayoutLeft = titleLayout.getLineCount() > 0 ? titleLayout.getLineLeft(0) : 0; + titleLayoutWidth = titleLayout.getLineCount() > 0 ? titleLayout.getLineWidth(0) : 0; titleLayoutEmojis = AnimatedEmojiSpan.update(AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES, this, titleLayoutEmojis, titleLayout); } catch (Exception e) { FileLog.e(e); @@ -177,12 +192,14 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (sequence != null) { sequence = TextUtils.ellipsize(AndroidUtilities.ellipsizeCenterEnd(sequence, currentMessageObject.highlightedWords.get(0), maxWidth, captionTextPaint, 130), captionTextPaint, maxWidth, TextUtils.TruncateAt.END); captionLayout = new StaticLayout(sequence, captionTextPaint, maxWidth + AndroidUtilities.dp(4), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + captionLayoutLeft = captionLayout.getLineCount() > 0 ? captionLayout.getLineLeft(0) : 0; + captionLayoutWidth = captionLayout.getLineCount() > 0 ? captionLayout.getLineWidth(0) : 0; } captionLayoutEmojis = AnimatedEmojiSpan.update(AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES, this, captionLayoutEmojis, captionLayout); } try { if (viewType == VIEW_TYPE_GLOBAL_SEARCH && (currentMessageObject.isVoice() || currentMessageObject.isRoundVideo())) { - CharSequence duration = AndroidUtilities.formatDuration(currentMessageObject.getDuration(), false); + CharSequence duration = AndroidUtilities.formatDuration((int) currentMessageObject.getDuration(), false); TextPaint paint = viewType == VIEW_TYPE_GLOBAL_SEARCH ? description2TextPaint : Theme.chat_contextResult_descriptionTextPaint; duration = TextUtils.ellipsize(duration, paint, maxWidth, TextUtils.TruncateAt.END); descriptionLayout = new StaticLayout(duration, paint, maxWidth + AndroidUtilities.dp(4), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); @@ -193,12 +210,14 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { author = authorH; } if (viewType == VIEW_TYPE_GLOBAL_SEARCH) { - author = new SpannableStringBuilder(author).append(' ').append(dotSpan).append(' ').append(FilteredSearchView.createFromInfoString(currentMessageObject)); + author = new SpannableStringBuilder(author).append(' ').append(dotSpan).append(' ').append(FilteredSearchView.createFromInfoString(currentMessageObject, 1)); } TextPaint paint = viewType == VIEW_TYPE_GLOBAL_SEARCH ? description2TextPaint : Theme.chat_contextResult_descriptionTextPaint; author = TextUtils.ellipsize(author, paint, maxWidth, TextUtils.TruncateAt.END); descriptionLayout = new StaticLayout(author, paint, maxWidth + AndroidUtilities.dp(4), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); } + descriptionLayoutLeft = descriptionLayout.getLineCount() > 0 ? descriptionLayout.getLineLeft(0) : 0; + descriptionLayoutWidth = descriptionLayout.getLineCount() > 0 ? descriptionLayout.getLineWidth(0) : 0; descriptionLayoutEmojis = AnimatedEmojiSpan.update(AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES, this, descriptionLayoutEmojis, descriptionLayout); } catch (Exception e) { FileLog.e(e); @@ -572,9 +591,8 @@ public void didReceivedNotification(int id, int account, Object... args) { updateButtonState(false, true); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } float enterAlpha = 1f; @@ -610,8 +628,6 @@ protected void dispatchDraw(Canvas canvas) { drawReorder(canvas); super.dispatchDraw(canvas); } - - } private void drawReorder(Canvas canvas) { @@ -642,7 +658,7 @@ private void drawInternal(Canvas canvas) { } if (dateLayout != null) { canvas.save(); - canvas.translate(AndroidUtilities.dp(LocaleController.isRTL ? 8 : AndroidUtilities.leftBaseline) + (LocaleController.isRTL ? 0 : dateLayoutX), titleY); + canvas.translate(AndroidUtilities.dp(LocaleController.isRTL ? 24 : AndroidUtilities.leftBaseline) + (LocaleController.isRTL ? 0 : dateLayoutX), titleY); dateLayout.draw(canvas); canvas.restore(); } @@ -653,7 +669,7 @@ private void drawInternal(Canvas canvas) { Theme.chat_contextResult_titleTextPaint.setAlpha((int) (oldAlpha * showNameProgress)); } canvas.save(); - canvas.translate(AndroidUtilities.dp(LocaleController.isRTL ? 8 : AndroidUtilities.leftBaseline) + (LocaleController.isRTL && dateLayout != null ? dateLayout.getWidth() + AndroidUtilities.dp(4) : 0), titleY); + canvas.translate(AndroidUtilities.dp(LocaleController.isRTL ? 24 : AndroidUtilities.leftBaseline) + (LocaleController.isRTL && dateLayout != null ? dateLayout.getWidth() + AndroidUtilities.dp(LocaleController.isRTL ? 12 : 4) : 0) + (LocaleController.isRTL ? titleLayout.getWidth() - titleLayoutWidth : 0) - titleLayoutLeft, titleY); titleLayout.draw(canvas); AnimatedEmojiSpan.drawAnimatedEmojis(canvas, titleLayout, titleLayoutEmojis, 0, null, 0, 0, 0, 1f); canvas.restore(); @@ -665,7 +681,7 @@ private void drawInternal(Canvas canvas) { if (captionLayout != null) { captionTextPaint.setColor(getThemedColor(Theme.key_windowBackgroundWhiteBlackText)); canvas.save(); - canvas.translate(AndroidUtilities.dp(LocaleController.isRTL ? 8 : AndroidUtilities.leftBaseline), captionY); + canvas.translate(AndroidUtilities.dp(LocaleController.isRTL ? 24 : AndroidUtilities.leftBaseline) + (LocaleController.isRTL ? captionLayout.getWidth() - captionLayoutWidth : 0) - captionLayoutLeft, captionY); captionLayout.draw(canvas); canvas.restore(); } @@ -677,7 +693,7 @@ private void drawInternal(Canvas canvas) { Theme.chat_contextResult_descriptionTextPaint.setAlpha((int) (oldAlpha * showNameProgress)); } canvas.save(); - canvas.translate(AndroidUtilities.dp(LocaleController.isRTL ? 8 : AndroidUtilities.leftBaseline), descriptionY); + canvas.translate(AndroidUtilities.dp(LocaleController.isRTL ? 24 : AndroidUtilities.leftBaseline) + (LocaleController.isRTL ? descriptionLayout.getWidth() - descriptionLayoutWidth : 0) - descriptionLayoutLeft, descriptionY); descriptionLayout.draw(canvas); AnimatedEmojiSpan.drawAnimatedEmojis(canvas, descriptionLayout, descriptionLayoutEmojis, 0, null, 0, 0, 0, 1f); canvas.restore(); @@ -691,7 +707,11 @@ private void drawInternal(Canvas canvas) { radialProgress.draw(canvas); if (needDivider) { - canvas.drawLine(AndroidUtilities.dp(72), getHeight() - 1, getWidth() - getPaddingRight(), getHeight() - 1, Theme.dividerPaint); + if (LocaleController.isRTL) { + canvas.drawLine(0, getHeight() - 1, getWidth() - AndroidUtilities.dp(72) - getPaddingRight(), getHeight() - 1, Theme.getThemePaint(Theme.key_paint_divider, resourcesProvider)); + } else { + canvas.drawLine(AndroidUtilities.dp(72), getHeight() - 1, getWidth() - getPaddingRight(), getHeight() - 1, Theme.getThemePaint(Theme.key_paint_divider, resourcesProvider)); + } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedDocumentCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedDocumentCell.java index 42d7b3028f..bed3282481 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedDocumentCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedDocumentCell.java @@ -37,6 +37,7 @@ import org.telegram.messenger.LocaleController; import org.telegram.messenger.MediaController; import org.telegram.messenger.MessageObject; +import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; import org.telegram.messenger.UserConfig; import org.telegram.messenger.Utilities; @@ -220,6 +221,7 @@ protected void onDraw(Canvas canvas) { dateTextView.setSingleLine(true); dateTextView.setEllipsize(TextUtils.TruncateAt.END); dateTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); + NotificationCenter.listenEmojiLoading(dateTextView); if (viewType == VIEW_TYPE_PICKER) { dateTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); addView(dateTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 8 : 72, 34, LocaleController.isRTL ? 72 : 8, 0)); @@ -232,9 +234,9 @@ protected void onDraw(Canvas canvas) { progressView.setProgressColor(getThemedColor(Theme.key_sharedMedia_startStopLoadIcon)); addView(progressView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 2, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 0 : 72, 54, LocaleController.isRTL ? 72 : 0, 0)); - checkBox = new CheckBox2(context, 21); + checkBox = new CheckBox2(context, 21, resourcesProvider); checkBox.setVisibility(INVISIBLE); - checkBox.setColor(null, Theme.key_windowBackgroundWhite, Theme.key_checkboxCheck); + checkBox.setColor(-1, Theme.key_windowBackgroundWhite, Theme.key_checkboxCheck); checkBox.setDrawUnchecked(false); checkBox.setDrawBackgroundAsArc(2); if (viewType == VIEW_TYPE_PICKER) { @@ -276,8 +278,8 @@ public void setTextAndValueAndTypeAndThumb(String text, String value, String typ } } else { Drawable drawable = Theme.createCircleDrawableWithIcon(AndroidUtilities.dp(42), resId); - String iconKey; - String backKey; + int iconKey; + int backKey; if (resId == R.drawable.files_storage) { backKey = Theme.key_chat_attachLocationBackground; iconKey = Theme.key_chat_attachIcon; @@ -320,7 +322,7 @@ public void setPhotoEntry(MediaController.PhotoEntry entry) { thumbImageView.setOrientation(0, true); thumbImageView.setImage("vthumb://" + entry.imageId + ":" + entry.path, null, Theme.chat_attachEmptyDrawable); } else { - thumbImageView.setOrientation(entry.orientation, true); + thumbImageView.setOrientation(entry.orientation, entry.invert, true); thumbImageView.setImage("thumb://" + entry.imageId + ":" + entry.path, null, Theme.chat_attachEmptyDrawable); } path = entry.path; @@ -509,7 +511,7 @@ private void updateDateView() { fileSize = String.format(Locale.ENGLISH, "%s / %s", AndroidUtilities.formatFileSize(downloadedSize), AndroidUtilities.formatFileSize(message.getDocument().size)); } if (viewType == VIEW_TYPE_GLOBAL_SEARCH) { - CharSequence fromName = FilteredSearchView.createFromInfoString(message); + CharSequence fromName = FilteredSearchView.createFromInfoString(message, true, 2, dateTextView.getPaint()); dateTextView.setText(new SpannableStringBuilder().append(fileSize) .append(' ').append(dotSpan).append(' ') @@ -697,9 +699,8 @@ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { } } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } float enterAlpha = 1f; @@ -749,7 +750,7 @@ protected void dispatchDraw(Canvas canvas) { private void drawDivider(Canvas canvas) { if (needDivider) { - canvas.drawLine(AndroidUtilities.dp(72), getHeight() - 1, getWidth() - getPaddingRight(), getHeight() - 1, Theme.dividerPaint); + canvas.drawLine(AndroidUtilities.dp(72), getHeight() - 1, getWidth() - getPaddingRight(), getHeight() - 1, Theme.getThemePaint(Theme.key_paint_divider, resourcesProvider)); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedLinkCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedLinkCell.java index b268fddead..88eb4227ba 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedLinkCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedLinkCell.java @@ -213,7 +213,7 @@ public SharedLinkCell(Context context, int viewType, Theme.ResourcesProvider res checkBox = new CheckBox2(context, 21, resourcesProvider); checkBox.setVisibility(INVISIBLE); - checkBox.setColor(null, Theme.key_windowBackgroundWhite, Theme.key_checkboxCheck); + checkBox.setColor(-1, Theme.key_windowBackgroundWhite, Theme.key_checkboxCheck); checkBox.setDrawUnchecked(false); checkBox.setDrawBackgroundAsArc(2); addView(checkBox, LayoutHelper.createFrame(24, 24, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 0 : 44, 44, LocaleController.isRTL ? 44 : 0, 0)); @@ -480,7 +480,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } if (viewType == VIEW_TYPE_GLOBAL_SEARCH) { - fromInfoLayout = ChatMessageCell.generateStaticLayout(FilteredSearchView.createFromInfoString(message), description2TextPaint, maxWidth, maxWidth, 0, desctiptionLines); + fromInfoLayout = ChatMessageCell.generateStaticLayout(FilteredSearchView.createFromInfoString(message, true, 2, description2TextPaint), description2TextPaint, maxWidth, maxWidth, 0, desctiptionLines); fromInfoLayoutEmojis = AnimatedEmojiSpan.update(AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES, this, fromInfoLayoutEmojis, fromInfoLayout); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedPhotoVideoCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedPhotoVideoCell.java index 444ee8a7cd..2e1937c704 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedPhotoVideoCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedPhotoVideoCell.java @@ -131,7 +131,7 @@ protected void onDraw(Canvas canvas) { checkBox = new CheckBox2(context, 21); checkBox.setVisibility(INVISIBLE); - checkBox.setColor(null, Theme.key_sharedMedia_photoPlaceholder, Theme.key_checkboxCheck); + checkBox.setColor(-1, Theme.key_sharedMedia_photoPlaceholder, Theme.key_checkboxCheck); checkBox.setDrawUnchecked(false); checkBox.setDrawBackgroundAsArc(1); addView(checkBox, LayoutHelper.createFrame(24, 24, Gravity.RIGHT | Gravity.TOP, 0, 1, 1, 0)); @@ -191,7 +191,7 @@ public void setMessageObject(MessageObject messageObject) { imageView.setImageResource(R.drawable.photo_placeholder_in); } else if (messageObject.isVideo()) { videoInfoContainer.setVisibility(VISIBLE); - videoTextView.setText(AndroidUtilities.formatShortDuration(messageObject.getDuration())); + videoTextView.setText(AndroidUtilities.formatShortDuration((int) messageObject.getDuration())); TLRPC.Document document = messageObject.getDocument(); TLRPC.PhotoSize thumb = FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 50); TLRPC.PhotoSize qualityThumb = FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 320); @@ -253,7 +253,7 @@ protected void onDraw(Canvas canvas) { public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); if (currentMessageObject.isVideo()) { - info.setText(LocaleController.getString("AttachVideo", R.string.AttachVideo) + ", " + LocaleController.formatDuration(currentMessageObject.getDuration())); + info.setText(LocaleController.getString("AttachVideo", R.string.AttachVideo) + ", " + LocaleController.formatDuration((int) currentMessageObject.getDuration())); } else { info.setText(LocaleController.getString("AttachPhoto", R.string.AttachPhoto)); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedPhotoVideoCell2.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedPhotoVideoCell2.java index fff78f7d70..b9337b3338 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedPhotoVideoCell2.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedPhotoVideoCell2.java @@ -1,5 +1,8 @@ package org.telegram.ui.Cells; +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.AndroidUtilities.dpf2; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; @@ -9,12 +12,18 @@ import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.RectF; import android.graphics.Region; +import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; import android.text.Layout; import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; +import android.util.Log; import android.util.SparseArray; import android.view.MotionEvent; import android.view.View; @@ -36,16 +45,18 @@ import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Components.CanvasButton; import org.telegram.ui.Components.CheckBoxBase; +import org.telegram.ui.Components.CombinedDrawable; import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.FlickerLoadingView; import org.telegram.ui.Components.spoilers.SpoilerEffect; import org.telegram.ui.PhotoViewer; +import org.telegram.ui.Stories.recorder.DominantColors; public class SharedPhotoVideoCell2 extends View { - public ImageReceiver imageReceiver = new ImageReceiver(); public ImageReceiver blurImageReceiver = new ImageReceiver(); + public int storyId; int currentAccount; MessageObject currentMessageObject; int currentParentColumnsCount; @@ -63,6 +74,12 @@ public class SharedPhotoVideoCell2 extends View { float crossfadeProgress; float crossfadeToColumnsCount; float highlightProgress; + public boolean isFirst, isLast; + + private Drawable gradientDrawable; + private boolean gradientDrawableLoading; + + public boolean isStory; static long lastUpdateDownloadSettingsTime; static boolean lastAutoDownload; @@ -88,7 +105,7 @@ public SharedPhotoVideoCell2(Context context, SharedResources sharedResources, i setChecked(false, false); imageReceiver.setParentView(this); blurImageReceiver.setParentView(this); - +; imageReceiver.setDelegate((imageReceiver1, set, thumb, memCache) -> { if (set && !thumb && currentMessageObject != null && currentMessageObject.hasMediaSpoilers() && imageReceiver.getBitmap() != null) { if (blurImageReceiver.getBitmap() != null) { @@ -106,10 +123,10 @@ public void setStyle(int style) { this.style = style; if (style == STYLE_CACHE) { checkBoxBase = new CheckBoxBase(this, 21, null); - checkBoxBase.setColor(null, Theme.key_sharedMedia_photoPlaceholder, Theme.key_checkboxCheck); + checkBoxBase.setColor(-1, Theme.key_sharedMedia_photoPlaceholder, Theme.key_checkboxCheck); checkBoxBase.setDrawUnchecked(true); checkBoxBase.setBackgroundType(0); - checkBoxBase.setBounds(0, 0, AndroidUtilities.dp(24), AndroidUtilities.dp(24)); + checkBoxBase.setBounds(0, 0, dp(24), dp(24)); if (attached) { checkBoxBase.onAttachedToWindow(); } @@ -124,7 +141,6 @@ public void onCheckBoxPressed() { } - public void setMessageObject(MessageObject messageObject, int parentColumnsCount) { int oldParentColumsCount = currentParentColumnsCount; currentParentColumnsCount = parentColumnsCount; @@ -135,12 +151,15 @@ public void setMessageObject(MessageObject messageObject, int parentColumnsCount return; } currentMessageObject = messageObject; + isStory = currentMessageObject != null && currentMessageObject.isStory(); if (messageObject == null) { imageReceiver.onDetachedFromWindow(); blurImageReceiver.onDetachedFromWindow(); videoText = null; videoInfoLayot = null; showVideoLayout = false; + gradientDrawableLoading = false; + gradientDrawable = null; return; } else { if (attached) { @@ -168,10 +187,15 @@ public void setMessageObject(MessageObject messageObject, int parentColumnsCount showVideoLayout = false; if (!TextUtils.isEmpty(restrictionReason)) { showImageStub = true; + } else if (messageObject.storyItem != null && messageObject.storyItem.media instanceof TLRPC.TL_messageMediaUnsupported) { + messageObject.storyItem.dialogId = messageObject.getDialogId(); + Drawable icon = getContext().getResources().getDrawable(R.drawable.msg_emoji_recent).mutate(); + icon.setColorFilter(new PorterDuffColorFilter(0x40FFFFFF, PorterDuff.Mode.SRC_IN)); + imageReceiver.setImageBitmap(new CombinedDrawable(new ColorDrawable(0xFF333333), icon)); } else if (messageObject.isVideo()) { showVideoLayout = true; if (parentColumnsCount != 9) { - videoText = AndroidUtilities.formatShortDuration(messageObject.getDuration()); + videoText = AndroidUtilities.formatShortDuration((int) messageObject.getDuration()); } if (messageObject.mediaThumb != null) { if (messageObject.strippedThumb != null) { @@ -182,8 +206,8 @@ public void setMessageObject(MessageObject messageObject, int parentColumnsCount } else { TLRPC.Document document = messageObject.getDocument(); TLRPC.PhotoSize thumb = FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 50); - TLRPC.PhotoSize qualityThumb = FileLoader.getClosestPhotoSizeWithSize(document.thumbs, stride); - if (thumb == qualityThumb) { + TLRPC.PhotoSize qualityThumb = FileLoader.getClosestPhotoSizeWithSize(document.thumbs, stride, false, null, isStory); + if (thumb == qualityThumb && !isStory) { qualityThumb = null; } if (thumb != null) { @@ -197,7 +221,7 @@ public void setMessageObject(MessageObject messageObject, int parentColumnsCount } } } else if (MessageObject.getMedia(messageObject.messageOwner) instanceof TLRPC.TL_messageMediaPhoto && MessageObject.getMedia(messageObject.messageOwner).photo != null && !messageObject.photoThumbs.isEmpty()) { - if (messageObject.mediaExists || canAutoDownload(messageObject)) { + if (messageObject.mediaExists || canAutoDownload(messageObject) || isStory) { if (messageObject.mediaThumb != null) { if (messageObject.strippedThumb != null) { imageReceiver.setImage(messageObject.mediaThumb, imageFilter, messageObject.strippedThumb, null, messageObject, 0); @@ -206,7 +230,7 @@ public void setMessageObject(MessageObject messageObject, int parentColumnsCount } } else { TLRPC.PhotoSize currentPhotoObjectThumb = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, 50); - TLRPC.PhotoSize currentPhotoObject = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, stride, false, currentPhotoObjectThumb, false); + TLRPC.PhotoSize currentPhotoObject = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, stride, false, currentPhotoObjectThumb, isStory); if (currentPhotoObject == currentPhotoObjectThumb) { currentPhotoObjectThumb = null; } @@ -261,22 +285,29 @@ public void setVideoText(String videoText, boolean drawVideoIcon) { this.drawVideoIcon = drawVideoIcon; } - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - - float padding; + private float getPadding() { if (crossfadeProgress != 0 && (crossfadeToColumnsCount == 9 || currentParentColumnsCount == 9)) { if (crossfadeToColumnsCount == 9) { - padding = AndroidUtilities.dp(0.5f) * crossfadeProgress + AndroidUtilities.dpf2(1) * (1f - crossfadeProgress); + return dpf2(0.5f) * crossfadeProgress + dpf2(1) * (1f - crossfadeProgress); } else { - padding = AndroidUtilities.dp(1f) * crossfadeProgress + AndroidUtilities.dpf2(0.5f) * (1f - crossfadeProgress); + return dpf2(1f) * crossfadeProgress + dpf2(0.5f) * (1f - crossfadeProgress); } } else { - padding = currentParentColumnsCount == 9 ? AndroidUtilities.dpf2(0.5f) : AndroidUtilities.dpf2(1); + return currentParentColumnsCount == 9 ? dpf2(0.5f) : dpf2(1); } + } + + private final RectF bounds = new RectF(); + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + final float padding = getPadding(); + final float leftpadding = isStory && isFirst ? 0 : padding; + final float rightpadding = isStory && isLast ? 0 : padding; - float imageWidth = (getMeasuredWidth() - padding * 2) * imageScale; + float imageWidth = (getMeasuredWidth() - leftpadding - rightpadding) * imageScale; float imageHeight = (getMeasuredHeight() - padding * 2) * imageScale; if (crossfadeProgress > 0.5f && crossfadeToColumnsCount != 9 && currentParentColumnsCount != 9) { @@ -284,47 +315,66 @@ protected void onDraw(Canvas canvas) { imageHeight -= 2; } - if (currentMessageObject == null || !imageReceiver.hasBitmapImage() || imageReceiver.getCurrentAlpha() != 1.0f || imageAlpha != 1f) { + if ((currentMessageObject == null && style != STYLE_CACHE) || !imageReceiver.hasBitmapImage() || imageReceiver.getCurrentAlpha() != 1.0f || imageAlpha != 1f) { if (SharedPhotoVideoCell2.this.getParent() != null && globalGradientView != null) { globalGradientView.setParentSize(((View) SharedPhotoVideoCell2.this.getParent()).getMeasuredWidth(), SharedPhotoVideoCell2.this.getMeasuredHeight(), -getX()); globalGradientView.updateColors(); globalGradientView.updateGradient(); - float localPadding = padding; + float padPlus = 0; if (crossfadeProgress > 0.5f && crossfadeToColumnsCount != 9 && currentParentColumnsCount != 9) { - localPadding += 1; + padPlus += 1; } - canvas.drawRect(localPadding, localPadding, localPadding + imageWidth, localPadding + imageHeight, globalGradientView.getPaint()); + canvas.drawRect(leftpadding + padPlus, padding + padPlus, leftpadding + padPlus + imageWidth, padding + padPlus + imageHeight, globalGradientView.getPaint()); } invalidate(); } if (imageAlpha != 1f) { - canvas.saveLayerAlpha(0, 0, padding * 2 + imageWidth, padding * 2 + imageHeight, (int) (255 * imageAlpha), Canvas.ALL_SAVE_FLAG); + canvas.saveLayerAlpha(0, 0, leftpadding + rightpadding + imageWidth, padding * 2 + imageHeight, (int) (255 * imageAlpha), Canvas.ALL_SAVE_FLAG); } else { canvas.save(); } if ((checkBoxBase != null && checkBoxBase.isChecked()) || PhotoViewer.isShowingImage(currentMessageObject)) { - canvas.drawRect(padding, padding, imageWidth, imageHeight, sharedResources.backgroundPaint); - } - - if (checkBoxProgress > 0) { - float offset = AndroidUtilities.dp(10) * checkBoxProgress; - imageReceiver.setImageCoords(padding + offset, padding + offset, imageWidth - offset * 2, imageHeight - offset * 2); - blurImageReceiver.setImageCoords(padding + offset, padding + offset, imageWidth - offset * 2, imageHeight - offset * 2); + canvas.drawRect(leftpadding, padding, leftpadding + imageWidth - rightpadding, imageHeight, sharedResources.backgroundPaint); + } + + if (isStory && currentParentColumnsCount == 1) { + final float w = getHeight() * .72f; + if (gradientDrawable == null) { + if (!gradientDrawableLoading && imageReceiver.getBitmap() != null) { + gradientDrawableLoading = true; + DominantColors.getColors(false, imageReceiver.getBitmap(), Theme.isCurrentThemeDark(), colors -> { + if (!gradientDrawableLoading) { + return; + } + gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, colors); + invalidate(); + gradientDrawableLoading = false; + }); + } + } else { + gradientDrawable.setBounds(0, 0, getWidth(), getHeight()); + gradientDrawable.draw(canvas); + } + imageReceiver.setImageCoords((imageWidth - w) / 2, 0, w, getHeight()); + } else if (checkBoxProgress > 0) { + float offset = dp(10) * checkBoxProgress; + imageReceiver.setImageCoords(leftpadding + offset, padding + offset, imageWidth - offset * 2, imageHeight - offset * 2); + blurImageReceiver.setImageCoords(leftpadding + offset, padding + offset, imageWidth - offset * 2, imageHeight - offset * 2); } else { - float localPadding = padding; + float padPlus = 0; if (crossfadeProgress > 0.5f && crossfadeToColumnsCount != 9 && currentParentColumnsCount != 9) { - localPadding += 1; + padPlus = 1; } - imageReceiver.setImageCoords(localPadding, localPadding, imageWidth, imageHeight); - blurImageReceiver.setImageCoords(localPadding, localPadding, imageWidth, imageHeight); + imageReceiver.setImageCoords(leftpadding + padPlus, padding + padPlus, imageWidth, imageHeight); + blurImageReceiver.setImageCoords(leftpadding + padPlus, padding + padPlus, imageWidth, imageHeight); } if (!PhotoViewer.isShowingImage(currentMessageObject)) { imageReceiver.draw(canvas); if (currentMessageObject != null && currentMessageObject.hasMediaSpoilers() && !currentMessageObject.isMediaSpoilersRevealedInSharedMedia) { canvas.save(); - canvas.clipRect(padding, padding, padding + imageWidth, padding + imageHeight); + canvas.clipRect(leftpadding, padding, leftpadding + imageWidth - rightpadding, padding + imageHeight); if (spoilerRevealProgress != 0f) { path.rewind(); @@ -349,47 +399,18 @@ protected void onDraw(Canvas canvas) { } } - if (showVideoLayout) { - canvas.save(); - canvas.clipRect(padding, padding, padding + imageWidth, padding + imageHeight); - if (currentParentColumnsCount != 9 && videoInfoLayot == null && videoText != null) { - int textWidth = (int) Math.ceil(sharedResources.textPaint.measureText(videoText)); - videoInfoLayot = new StaticLayout(videoText, sharedResources.textPaint, textWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); - } - int width; - if (videoInfoLayot == null) { - width = AndroidUtilities.dp(17); - } else { - width = AndroidUtilities.dp(4) + videoInfoLayot.getWidth() + AndroidUtilities.dp(4); - } - if (drawVideoIcon) { - width += AndroidUtilities.dp(10); - } - canvas.translate(AndroidUtilities.dp(5), AndroidUtilities.dp(1) + imageHeight - AndroidUtilities.dp(17) - AndroidUtilities.dp(4)); - AndroidUtilities.rectTmp.set(0, 0, width, AndroidUtilities.dp(17)); - canvas.drawRoundRect(AndroidUtilities.rectTmp, AndroidUtilities.dp(4), AndroidUtilities.dp(4), Theme.chat_timeBackgroundPaint); - if (drawVideoIcon) { - canvas.save(); - canvas.translate(videoInfoLayot == null ? AndroidUtilities.dp(5) : AndroidUtilities.dp(4), (AndroidUtilities.dp(17) - sharedResources.playDrawable.getIntrinsicHeight()) / 2f); - sharedResources.playDrawable.setAlpha((int) (255 * imageAlpha)); - sharedResources.playDrawable.draw(canvas); - canvas.restore(); - } - if (videoInfoLayot != null) { - canvas.translate(AndroidUtilities.dp(4 + (drawVideoIcon ? 10 : 0)), (AndroidUtilities.dp(17) - videoInfoLayot.getHeight()) / 2f); - videoInfoLayot.draw(canvas); - } - canvas.restore(); - } + bounds.set(imageReceiver.getImageX(), imageReceiver.getImageY(), imageReceiver.getImageX2(), imageReceiver.getImageY2()); + bounds.set(leftpadding, padding, leftpadding + imageWidth - rightpadding, padding + imageHeight); + drawDuration(canvas, bounds, 1f); if (checkBoxBase != null && (style == STYLE_CACHE || checkBoxBase.getProgress() != 0)) { canvas.save(); float x, y; if (style == STYLE_CACHE) { - x = imageWidth + AndroidUtilities.dp(2) - AndroidUtilities.dp(25) - AndroidUtilities.dp(4); - y = AndroidUtilities.dp(4); + x = imageWidth + dp(2) - dp(25) - dp(4); + y = dp(4); } else { - x = imageWidth + AndroidUtilities.dp(2) - AndroidUtilities.dp(25); + x = imageWidth + dp(2) - dp(25); y = 0; } canvas.translate(x, y); @@ -404,6 +425,56 @@ protected void onDraw(Canvas canvas) { canvas.restore(); } + public void drawDuration(Canvas canvas, RectF bounds, float alpha) { + if (!showVideoLayout || imageReceiver != null && !imageReceiver.getVisible()) { + return; + } + + if (alpha < 1) { + alpha = (float) Math.pow(alpha, 8); + } + + canvas.save(); + canvas.translate(bounds.left, bounds.top); + canvas.clipRect(0, 0, bounds.width(), bounds.height()); + if (currentParentColumnsCount != 9 && videoInfoLayot == null && videoText != null) { + int textWidth = (int) Math.ceil(sharedResources.textPaint.measureText(videoText)); + videoInfoLayot = new StaticLayout(videoText, sharedResources.textPaint, textWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + } else if ((currentParentColumnsCount >= 9 || videoText == null) && videoInfoLayot != null) { + videoInfoLayot = null; + } + int width; + if (videoInfoLayot == null) { + width = dp(8); + } else { + width = dp(4) + videoInfoLayot.getWidth() + dp(4); + } + if (drawVideoIcon) { + width += dp(10); + } + canvas.translate(dp(5), dp(1) + bounds.height() - dp(17) - dp(4)); + AndroidUtilities.rectTmp.set(0, 0, width, dp(17)); + int oldAlpha = Theme.chat_timeBackgroundPaint.getAlpha(); + Theme.chat_timeBackgroundPaint.setAlpha((int) (oldAlpha * alpha)); + canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(4), dp(4), Theme.chat_timeBackgroundPaint); + Theme.chat_timeBackgroundPaint.setAlpha(oldAlpha); + if (drawVideoIcon) { + canvas.save(); + canvas.translate(videoInfoLayot == null ? dp(5) : dp(4), (dp(17) - sharedResources.playDrawable.getIntrinsicHeight()) / 2f); + sharedResources.playDrawable.setAlpha((int) (255 * imageAlpha * alpha)); + sharedResources.playDrawable.draw(canvas); + canvas.restore(); + } + if (videoInfoLayot != null) { + canvas.translate(dp(4 + (drawVideoIcon ? 10 : 0)), (dp(17) - videoInfoLayot.getHeight()) / 2f); + oldAlpha = sharedResources.textPaint.getAlpha(); + sharedResources.textPaint.setAlpha((int) (oldAlpha * alpha)); + videoInfoLayot.draw(canvas); + sharedResources.textPaint.setAlpha(oldAlpha); + } + canvas.restore(); + } + public boolean canRevealSpoiler() { return currentMessageObject != null && currentMessageObject.hasMediaSpoilers() && spoilerRevealProgress == 0f && !currentMessageObject.isMediaSpoilersRevealedInSharedMedia; } @@ -461,7 +532,12 @@ public void setGradientView(FlickerLoadingView globalGradientView) { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY)); + int width = MeasureSpec.getSize(widthMeasureSpec); + int height = isStory ? (int) (1.25f * width) : width; + if (isStory && currentParentColumnsCount == 1) { + height /= 2; + } + setMeasuredDimension(width, height); } public int getMessageId() { @@ -500,7 +576,7 @@ public void drawCrossafadeImage(Canvas canvas) { if (crossfadeView != null) { canvas.save(); canvas.translate(getX(), getY()); - float scale = ((getMeasuredWidth() - AndroidUtilities.dp(2)) * imageScale) / (float) (crossfadeView.getMeasuredWidth() - AndroidUtilities.dp(2)); + float scale = ((getMeasuredWidth() - dp(2)) * imageScale) / (float) (crossfadeView.getMeasuredWidth() - dp(2)); crossfadeView.setImageScale(scale, false); crossfadeView.draw(canvas); canvas.restore(); @@ -521,10 +597,10 @@ public void setChecked(final boolean checked, boolean animated) { } if (checkBoxBase == null) { checkBoxBase = new CheckBoxBase(this, 21, null); - checkBoxBase.setColor(null, Theme.key_sharedMedia_photoPlaceholder, Theme.key_checkboxCheck); + checkBoxBase.setColor(-1, Theme.key_sharedMedia_photoPlaceholder, Theme.key_checkboxCheck); checkBoxBase.setDrawUnchecked(false); checkBoxBase.setBackgroundType(1); - checkBoxBase.setBounds(0, 0, AndroidUtilities.dp(24), AndroidUtilities.dp(24)); + checkBoxBase.setBounds(0, 0, dp(24), dp(24)); if (attached) { checkBoxBase.onAttachedToWindow(); } @@ -576,6 +652,10 @@ public void moveImageToFront() { imageReceiver.moveImageToFront(); } + public int getStyle() { + return style; + } + public static class SharedResources { TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); private Paint backgroundPaint = new Paint(); @@ -584,7 +664,7 @@ public static class SharedResources { SparseArray imageFilters = new SparseArray<>(); public SharedResources(Context context, Theme.ResourcesProvider resourcesProvider) { - textPaint.setTextSize(AndroidUtilities.dp(12)); + textPaint.setTextSize(dp(12)); textPaint.setColor(Color.WHITE); textPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); playDrawable = ContextCompat.getDrawable(context, R.drawable.play_mini_video); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharingLiveLocationCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharingLiveLocationCell.java index 1c2730e9c4..ac3b01b45f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharingLiveLocationCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharingLiveLocationCell.java @@ -298,8 +298,7 @@ protected void onDraw(Canvas canvas) { canvas.drawText(text, rect.centerX() - size / 2, AndroidUtilities.dp(distanceTextView != null ? 37 : 31), Theme.chat_livePaint); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerCell.java index 4a8da35e3f..c46f9a040b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerCell.java @@ -51,8 +51,11 @@ public class StickerCell extends FrameLayout { private boolean showPremiumLock; private boolean isPremiumSticker; - public StickerCell(Context context) { + Theme.ResourcesProvider resourcesProvider; + + public StickerCell(Context context, Theme.ResourcesProvider resourcesProvider) { super(context); + this.resourcesProvider = resourcesProvider; imageView = new BackupImageView(context); imageView.setAspectFit(true); @@ -97,7 +100,7 @@ public void setSticker(TLRPC.Document document, Object parent) { } if (document != null) { TLRPC.PhotoSize thumb = FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 90); - SvgHelper.SvgDrawable svgThumb = DocumentObject.getSvgThumb(document, Theme.key_windowBackgroundGray, 1.0f); + SvgHelper.SvgDrawable svgThumb = DocumentObject.getSvgThumb(document, Theme.key_windowBackgroundGray, 1.0f, 1f, resourcesProvider); if (MessageObject.canAutoplayAnimatedSticker(document)) { if (svgThumb != null) { imageView.setImage(ImageLocation.getForDocument(document), "80_80", null, svgThumb, parentObject); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerEmojiCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerEmojiCell.java index a8d86708cd..812ae12f04 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerEmojiCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerEmojiCell.java @@ -72,14 +72,17 @@ public class StickerEmojiCell extends FrameLayout implements NotificationCenter. private final static int STICKER_SIZE = 66; private boolean drawInParentView; + private Theme.ResourcesProvider resourceProvider; - public StickerEmojiCell(Context context, boolean isEmojiPanel) { + public StickerEmojiCell(Context context, boolean isEmojiPanel, Theme.ResourcesProvider resourcesProvider) { super(context); + this.resourceProvider = resourcesProvider; fromEmojiPanel = isEmojiPanel; imageView = new ImageReceiver(); imageView.setAspectFit(true); + imageView.setAllowLoadingOnAttachedOnly(true); imageView.setLayerNum(1); emojiTextView = new EmojiTextView(context); @@ -171,10 +174,10 @@ public void setSticker(TLRPC.Document document, SendMessagesHelper.ImportingStic parentObject = parent; //boolean isVideoSticker = MessageObject.isVideoSticker(document); TLRPC.PhotoSize thumb = FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 90); - SvgHelper.SvgDrawable svgThumb = DocumentObject.getSvgThumb(document, fromEmojiPanel ? Theme.key_emptyListPlaceholder : Theme.key_windowBackgroundGray, fromEmojiPanel ? 0.2f : 1.0f); + SvgHelper.SvgDrawable svgThumb = DocumentObject.getSvgThumb(document, fromEmojiPanel ? Theme.key_emptyListPlaceholder : Theme.key_windowBackgroundGray, fromEmojiPanel ? 0.2f : 1.0f, 1f, resourceProvider); String imageFilter = fromEmojiPanel ? "66_66_pcache_compress" : "66_66"; if (MessageObject.isTextColorEmoji(document)) { - imageView.setColorFilter(Theme.chat_animatedEmojiTextColorFilter); + imageView.setColorFilter(Theme.getAnimatedEmojiColorFilter(resourceProvider)); } if (MessageObject.canAutoplayAnimatedSticker(document)) { if (fromEmojiPanel) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerSetCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerSetCell.java index 651463df8d..1ae9350de7 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerSetCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerSetCell.java @@ -36,6 +36,7 @@ import org.telegram.messenger.Emoji; import org.telegram.messenger.FileLoader; import org.telegram.messenger.ImageLocation; +import org.telegram.messenger.LiteMode; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MessageObject; import org.telegram.messenger.NotificationCenter; @@ -127,7 +128,7 @@ public StickerSetCell(Context context, Theme.ResourcesProvider resourcesProvider addView(reorderButton, LayoutHelper.createFrameRelatively(58, 58, Gravity.END)); checkBox = new CheckBox2(context, 21); - checkBox.setColor(null, Theme.key_windowBackgroundWhite, Theme.key_checkboxCheck); + checkBox.setColor(-1, Theme.key_windowBackgroundWhite, Theme.key_checkboxCheck); checkBox.setDrawUnchecked(false); checkBox.setDrawBackgroundAsArc(3); addView(checkBox, LayoutHelper.createFrameRelatively(24, 24, Gravity.START, 34, 30, 0, 0)); @@ -337,6 +338,7 @@ public void setStickersSet(TLRPC.TL_messages_stickerSet set, boolean divider, bo if (sticker == null) { sticker = documents.get(0); } + final boolean lite = !LiteMode.isEnabled(LiteMode.FLAG_ANIMATED_STICKERS_KEYBOARD); TLObject object = FileLoader.getClosestPhotoSizeWithSize(set.set.thumbs, 90); if (object == null) { object = sticker; @@ -352,19 +354,21 @@ public void setStickersSet(TLRPC.TL_messages_stickerSet set, boolean divider, bo imageLocation = ImageLocation.getForSticker(thumb, sticker, set.set.thumb_version); } + boolean allowPlay = LiteMode.isEnabled(emojis ? LiteMode.FLAG_ANIMATED_EMOJI_KEYBOARD : LiteMode.FLAG_ANIMATED_STICKERS_KEYBOARD); + String filter = "50_50" + (!allowPlay ? "_firstframe" : ""); if (object instanceof TLRPC.Document && MessageObject.isAnimatedStickerDocument(sticker, true) || MessageObject.isVideoSticker(sticker)) { if (svgThumb != null) { - imageView.setImage(ImageLocation.getForDocument(sticker), "50_50", svgThumb, 0, set); + imageView.setImage(ImageLocation.getForDocument(sticker), filter, svgThumb, 0, set); } else { - imageView.setImage(ImageLocation.getForDocument(sticker), "50_50", imageLocation, null, 0, set); + imageView.setImage(ImageLocation.getForDocument(sticker), filter, imageLocation, null, 0, set); } if (MessageObject.isTextColorEmoji(sticker)) { - imageView.setColorFilter(Theme.chat_animatedEmojiTextColorFilter); + imageView.setColorFilter(Theme.getAnimatedEmojiColorFilter(null)); } } else if (imageLocation != null && imageLocation.imageType == FileLoader.IMAGE_TYPE_LOTTIE) { - imageView.setImage(imageLocation, "50_50", "tgs", svgThumb, set); + imageView.setImage(imageLocation, filter, "tgs", svgThumb, set); } else { - imageView.setImage(imageLocation, "50_50", "webp", svgThumb, set); + imageView.setImage(imageLocation, filter, "webp", svgThumb, set); } } else { valueTextView.setText(LocaleController.formatPluralString(set.set.emojis ? "EmojiCount" : "Stickers", 0)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerSetGroupInfoCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerSetGroupInfoCell.java index cc6445b2d6..6c3192039b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerSetGroupInfoCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerSetGroupInfoCell.java @@ -42,7 +42,7 @@ public StickerSetGroupInfoCell(Context context) { addButton.setTextColor(Theme.getColor(Theme.key_featuredStickers_buttonText)); addButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); addButton.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - addButton.setBackground(Theme.AdaptiveRipple.filledRect(Theme.key_featuredStickers_addButton, 4)); + addButton.setBackground(Theme.AdaptiveRipple.filledRectByKey(Theme.key_featuredStickers_addButton, 4)); addButton.setText(LocaleController.getString("ChooseStickerSet", R.string.ChooseStickerSet).toUpperCase()); addView(addButton, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 28, Gravity.TOP | Gravity.LEFT, 17, 10, 14, 8)); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerSetNameCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerSetNameCell.java index fdec208472..fad998e811 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerSetNameCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerSetNameCell.java @@ -76,9 +76,9 @@ public StickerSetNameCell(Context context, boolean emoji, boolean supportRtl, Th } } if (supportRtl) { - lp = LayoutHelper.createFrameRelatively(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.START | Gravity.TOP, emoji ? 5 : 17, emoji ? 5 : 2, emoji ? 15 : 57, 0); + lp = LayoutHelper.createFrameRelatively(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.START | Gravity.TOP, emoji ? 5 : 15, 5, emoji ? 15 : 25, 0); } else { - lp = LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, emoji ? 5 : 17, emoji ? 5 : 2, emoji ? 15 : 57, 0); + lp = LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, emoji ? 5 : 15, 5, emoji ? 15 : 25, 0); } addView(textView, lp); @@ -100,13 +100,11 @@ public StickerSetNameCell(Context context, boolean emoji, boolean supportRtl, Th buttonView.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_chat_emojiPanelStickerSetNameIcon), PorterDuff.Mode.SRC_IN)); buttonView.setBackground(Theme.createSelectorDrawable(getThemedColor(Theme.key_listSelector), Theme.RIPPLE_MASK_CIRCLE_TO_BOUND_EDGE)); if (supportRtl) { - lp = LayoutHelper.createFrameRelatively(24, 24, Gravity.TOP | Gravity.END, 0, 0, isEmoji ? 0 : 11, 0); + lp = LayoutHelper.createFrameRelatively(24, 24, Gravity.TOP | Gravity.END, 0, 0, isEmoji ? 0 : 10, 0); } else { - lp = LayoutHelper.createFrame(24, 24, Gravity.TOP | Gravity.RIGHT, 0, 0, isEmoji ? 0 : 11, 0); - } - if (isEmoji) { - buttonView.setTranslationY(AndroidUtilities.dp(4)); + lp = LayoutHelper.createFrame(24, 24, Gravity.TOP | Gravity.RIGHT, 0, 0, isEmoji ? 0 : 10, 0); } + buttonView.setTranslationY(AndroidUtilities.dp(4)); addView(buttonView, lp); } @@ -196,7 +194,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (empty) { super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(1, MeasureSpec.EXACTLY)); } else { - super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(isEmoji ? 27 : 24), MeasureSpec.EXACTLY)); + super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(27), MeasureSpec.EXACTLY)); } } @@ -221,9 +219,8 @@ public static void createThemeDescriptions(List descriptions, descriptions.add(new ThemeDescription(null, 0, null, null, null, delegate, Theme.key_chat_emojiPanelStickerSetName)); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } public TextView getTextView() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCell.java index 076e018055..28dca70384 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCell.java @@ -43,8 +43,8 @@ public class TextCell extends FrameLayout { private ImageView valueImageView; private int leftPadding; private boolean needDivider; - private int offsetFromImage = 71; - public int heightDp = 48; + public int offsetFromImage = 71; + public int heightDp = 50; public int imageLeft = 21; private boolean inDialogs; private boolean prioritizeTitleOverValue; @@ -193,7 +193,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (checkBox != null) { checkBox.measure(MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(37), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(20), MeasureSpec.EXACTLY)); } - setMeasuredDimension(width, AndroidUtilities.dp(50) + (needDivider ? 1 : 0)); + setMeasuredDimension(width, height + (needDivider ? 1 : 0)); } @Override @@ -224,16 +224,17 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto viewLeft = AndroidUtilities.dp(imageView.getVisibility() == VISIBLE ? offsetFromImage : leftPadding); } if (subtitleView.getVisibility() == View.VISIBLE) { - viewTop = (height - textView.getTextHeight() - subtitleView.getTextHeight() - AndroidUtilities.dp(2)) / 2; + int margin = heightDp > 50 ? 4 : 2; + viewTop = (height - textView.getTextHeight() - subtitleView.getTextHeight() - AndroidUtilities.dp(margin)) / 2; textView.layout(viewLeft, viewTop, viewLeft + textView.getMeasuredWidth(), viewTop + textView.getMeasuredHeight()); - viewTop = viewTop + textView.getTextHeight() + AndroidUtilities.dp(2); + viewTop = viewTop + textView.getTextHeight() + AndroidUtilities.dp(margin); subtitleView.layout(viewLeft, viewTop, viewLeft + subtitleView.getMeasuredWidth(), viewTop + subtitleView.getMeasuredHeight()); } else { viewTop = (height - textView.getTextHeight()) / 2; textView.layout(viewLeft, viewTop, viewLeft + textView.getMeasuredWidth(), viewTop + textView.getMeasuredHeight()); } if (imageView.getVisibility() == VISIBLE) { - viewTop = AndroidUtilities.dp(5); + viewTop = AndroidUtilities.dp(heightDp > 50 ? 0 : 2) + (height - imageView.getMeasuredHeight()) / 2 - imageView.getPaddingTop(); viewLeft = !LocaleController.isRTL ? AndroidUtilities.dp(imageLeft) : width - imageView.getMeasuredWidth() - AndroidUtilities.dp(imageLeft); imageView.layout(viewLeft, viewTop, viewLeft + imageView.getMeasuredWidth(), viewTop + imageView.getMeasuredHeight()); } @@ -254,11 +255,11 @@ public void setTextColor(int color) { textView.setTextColor(color); } - public void setColors(String icon, String text) { + public void setColors(int icon, int text) { textView.setTextColor(Theme.getColor(text, resourcesProvider)); textView.setTag(text); - if (icon != null) { - imageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(icon, resourcesProvider), PorterDuff.Mode.SRC_IN)); + if (icon >= 0) { + imageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(icon, resourcesProvider), PorterDuff.Mode.MULTIPLY)); imageView.setTag(icon); } } @@ -279,7 +280,7 @@ public void setText(CharSequence text, boolean divider) { public void setTextAndIcon(CharSequence text, int resId, boolean divider) { imageLeft = 21; - offsetFromImage = 71; + offsetFromImage = getOffsetFromImage(false); textView.setText(text); valueTextView.setText(valueText = null, false); if (resId != 0) { @@ -341,7 +342,7 @@ public void setTextAndValue(CharSequence text, String value, boolean divider) { public void setTextAndValue(CharSequence text, String value, boolean animated, boolean divider) { imageLeft = 21; - offsetFromImage = 71; + offsetFromImage = getOffsetFromImage(false); textView.setText(text); valueTextView.setText(TextUtils.ellipsize(valueText = value, valueTextView.getPaint(), AndroidUtilities.displaySize.x / 2.5f, TextUtils.TruncateAt.END), animated); valueTextView.setVisibility(VISIBLE); @@ -357,7 +358,7 @@ public void setTextAndValue(CharSequence text, String value, boolean animated, b public void setTextAndValueAndColorfulIcon(String text, CharSequence value, boolean animated, int resId, int color, boolean divider) { imageLeft = 21; - offsetFromImage = 71; + offsetFromImage = getOffsetFromImage(false); textView.setText(text); valueTextView.setText(TextUtils.ellipsize(valueText = value, valueTextView.getPaint(), AndroidUtilities.displaySize.x / 2.5f, TextUtils.TruncateAt.END), animated); valueTextView.setVisibility(VISIBLE); @@ -373,7 +374,7 @@ public void setTextAndValueAndColorfulIcon(String text, CharSequence value, bool public void setTextAndSpoilersValueAndIcon(String text, CharSequence value, int resId, boolean divider) { imageLeft = 21; - offsetFromImage = 71; + offsetFromImage = getOffsetFromImage(false); textView.setText(text); valueSpoilersTextView.setVisibility(VISIBLE); valueSpoilersTextView.setText(value); @@ -393,7 +394,7 @@ public void setTextAndSpoilersValueAndIcon(String text, CharSequence value, int public void setTextAndSpoilersValueAndColorfulIcon(String text, CharSequence value, int resId, int color, boolean divider) { imageLeft = 21; - offsetFromImage = 71; + offsetFromImage = getOffsetFromImage(false); textView.setText(text); valueSpoilersTextView.setVisibility(VISIBLE); valueSpoilersTextView.setText(value); @@ -413,7 +414,7 @@ public void setTextAndValueAndIcon(CharSequence text, String value, int resId, b public void setTextAndValueAndIcon(CharSequence text, String value, boolean animated, int resId, boolean divider) { imageLeft = 21; - offsetFromImage = 71; + offsetFromImage = getOffsetFromImage(false); textView.setText(text); valueTextView.setText(TextUtils.ellipsize(valueText = value, valueTextView.getPaint(), AndroidUtilities.displaySize.x / 2.5f, TextUtils.TruncateAt.END), animated); valueTextView.setVisibility(VISIBLE); @@ -432,11 +433,10 @@ public void setTextAndValueAndIcon(CharSequence text, String value, boolean anim } public void setColorfulIcon(int color, int resId) { - offsetFromImage = 65; + offsetFromImage = getOffsetFromImage(true); imageView.setVisibility(VISIBLE); imageView.setPadding(AndroidUtilities.dp(2), AndroidUtilities.dp(2), AndroidUtilities.dp(2), AndroidUtilities.dp(2)); imageView.setTranslationX(AndroidUtilities.dp(LocaleController.isRTL ? 0 : -3)); - imageView.setTranslationY(AndroidUtilities.dp(6)); imageView.setImageResource(resId); imageView.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)); imageView.setBackground(Theme.createRoundRectDrawable(AndroidUtilities.dp(9), color)); @@ -444,7 +444,7 @@ public void setColorfulIcon(int color, int resId) { public void setTextAndCheck(CharSequence text, boolean checked, boolean divider) { imageLeft = 21; - offsetFromImage = 71; + offsetFromImage = getOffsetFromImage(false); textView.setText(text); imageView.setVisibility(GONE); valueImageView.setVisibility(GONE); @@ -453,13 +453,12 @@ public void setTextAndCheck(CharSequence text, boolean checked, boolean divider) checkBox.setVisibility(VISIBLE); checkBox.setChecked(checked, false); } - needDivider = divider; setWillNotDraw(!needDivider); } public void setTextAndCheckAndIcon(CharSequence text, boolean checked, int resId, boolean divider) { imageLeft = 21; - offsetFromImage = 71; + offsetFromImage = getOffsetFromImage(false); textView.setText(text); valueTextView.setVisibility(GONE); valueSpoilersTextView.setVisibility(GONE); @@ -477,7 +476,7 @@ public void setTextAndCheckAndIcon(CharSequence text, boolean checked, int resId public void setTextAndCheckAndIcon(CharSequence text, boolean checked, Drawable resDrawable, boolean divider) { imageLeft = 21; - offsetFromImage = 71; + offsetFromImage = getOffsetFromImage(false); textView.setText(text); valueTextView.setVisibility(GONE); valueSpoilersTextView.setVisibility(GONE); @@ -495,7 +494,7 @@ public void setTextAndCheckAndIcon(CharSequence text, boolean checked, Drawable public void setTextAndValueDrawable(CharSequence text, Drawable drawable, boolean divider) { imageLeft = 21; - offsetFromImage = 71; + offsetFromImage = getOffsetFromImage(false); textView.setText(text); valueTextView.setText(valueText = null, false); valueImageView.setVisibility(VISIBLE); @@ -511,10 +510,18 @@ public void setTextAndValueDrawable(CharSequence text, Drawable drawable, boolea } } + protected int getOffsetFromImage(boolean colourful) { + return colourful ? 65 : 71; + } + @Override protected void onDraw(Canvas canvas) { if (needDivider) { - canvas.drawLine(0, getMeasuredHeight() - 1, getMeasuredWidth(), getMeasuredHeight() - 1, Theme.dividerPaint); + Paint paint = resourcesProvider != null ? resourcesProvider.getPaint(Theme.key_paint_divider) : null; + if (paint == null) { + paint = Theme.dividerPaint; + } + canvas.drawLine(LocaleController.isRTL ? 0 : AndroidUtilities.dp(imageView.getVisibility() == VISIBLE ? (inDialogs ? 72 : 68) : 20), getMeasuredHeight() - 1, getMeasuredWidth() - (LocaleController.isRTL ? AndroidUtilities.dp(imageView.getVisibility() == VISIBLE ? (inDialogs ? 72 : 68) : 20) : 0), getMeasuredHeight() - 1, paint); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckCell.java index 007e9785fa..4cbb845b9c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckCell.java @@ -117,7 +117,7 @@ public TextCheckCell(Context context, int padding, boolean dialog, Theme.Resourc valueTextView.setSingleLine(true); valueTextView.setPadding(0, 0, 0, 0); valueTextView.setEllipsize(TextUtils.TruncateAt.END); - addView(valueTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 64 : padding, 36, LocaleController.isRTL ? padding : 64, 0)); + addView(valueTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 70 : padding, 35, LocaleController.isRTL ? padding : 70, 0)); checkBox = new Switch(context, resourcesProvider); checkBox.setColors(Theme.key_switchTrack, Theme.key_switchTrackChecked, Theme.key_windowBackgroundWhite, Theme.key_windowBackgroundWhite); @@ -137,6 +137,10 @@ public void setCheckBoxIcon(int icon) { checkBox.setIcon(icon); } + public Switch getCheckBox() { + return checkBox; + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (isMultiline) { @@ -157,12 +161,14 @@ public void setDivider(boolean divider) { setWillNotDraw(!divider); } - public void setTextAndCheck(String text, boolean checked, boolean divider) { + public void setTextAndCheck(CharSequence text, boolean checked, boolean divider) { textView.setText(text); isMultiline = false; if (checkBox != null) { + checkBox.setVisibility(View.VISIBLE); checkBox.setChecked(checked, attached); } else { + checkBoxSquare.setVisibility(View.VISIBLE); checkBoxSquare.setChecked(checked,false); } needDivider = divider; @@ -192,7 +198,7 @@ public void updateRTL() { addView(checkBox, LayoutHelper.createFrame(37, 20, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.CENTER_VERTICAL, 22, 0, 22, 0)); } - public void setColors(String key, String switchKey, String switchKeyChecked, String switchThumb, String switchThumbChecked) { + public void setColors(int key, int switchKey, int switchKeyChecked, int switchThumb, int switchThumbChecked) { textView.setTextColor(Theme.getColor(key, resourcesProvider)); checkBox.setColors(switchKey, switchKeyChecked, switchThumb, switchThumbChecked); textView.setTag(key); @@ -222,8 +228,10 @@ public void setTextAndValueAndCheck(String text, String value, boolean checked, textView.setText(text); valueTextView.setText(value); if (checkBox != null) { + checkBox.setVisibility(View.VISIBLE); checkBox.setChecked(checked, false); } else { + checkBoxSquare.setVisibility(View.VISIBLE); checkBoxSquare.setChecked(checked,false); } needDivider = divider; @@ -249,6 +257,33 @@ public void setTextAndValueAndCheck(String text, String value, boolean checked, setWillNotDraw(!divider); } + public void setTextAndValue(String text, String value, boolean multiline, boolean divider) { + textView.setText(text); + valueTextView.setText(value); + checkBox.setVisibility(View.GONE); + needDivider = divider; + valueTextView.setVisibility(VISIBLE); + isMultiline = multiline; + if (multiline) { + valueTextView.setLines(0); + valueTextView.setMaxLines(0); + valueTextView.setSingleLine(false); + valueTextView.setEllipsize(null); + valueTextView.setPadding(0, 0, 0, AndroidUtilities.dp(11)); + } else { + valueTextView.setLines(1); + valueTextView.setMaxLines(1); + valueTextView.setSingleLine(true); + valueTextView.setEllipsize(TextUtils.TruncateAt.END); + valueTextView.setPadding(0, 0, 0, 0); + } + LayoutParams layoutParams = (LayoutParams) textView.getLayoutParams(); + layoutParams.height = LayoutParams.WRAP_CONTENT; + layoutParams.topMargin = AndroidUtilities.dp(10); + textView.setLayoutParams(layoutParams); + setWillNotDraw(!divider); + } + public void setEnabled(boolean value, ArrayList animators) { super.setEnabled(value); if (animators != null) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckbox2Cell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckbox2Cell.java index 2302691275..f1d34c5693 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckbox2Cell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckbox2Cell.java @@ -127,7 +127,7 @@ public void setTextAndCheck(String text, boolean checked, boolean divider) { setWillNotDraw(!divider); } - public void setColors(String key, String switchKey, String switchKeyChecked, String switchThumb, String switchThumbChecked) { + public void setColors(int key, int switchKey, int switchKeyChecked, int switchThumb, int switchThumbChecked) { textView.setTextColor(Theme.getColor(key)); // checkbox.setColors(switchKey, switchKeyChecked, switchThumb, switchThumbChecked); textView.setTag(key); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextInfoPrivacyCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextInfoPrivacyCell.java index 0d559f9975..bf0c9d6476 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextInfoPrivacyCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextInfoPrivacyCell.java @@ -35,7 +35,7 @@ public class TextInfoPrivacyCell extends FrameLayout { private TextView textView; private LinkSpanDrawable.LinkCollector links; - private String linkTextColorKey = Theme.key_windowBackgroundWhiteLinkText; + private int linkTextColorKey = Theme.key_windowBackgroundWhiteLinkText; private int topPadding = 10; private int bottomPadding = 17; private int fixedSize; @@ -115,7 +115,7 @@ protected void afterTextDraw() { } - public void setLinkTextColorKey(String key) { + public void setLinkTextColorKey(int key) { linkTextColorKey = key; } @@ -167,7 +167,7 @@ public void setTextColor(int color) { textView.setTextColor(color); } - public void setTextColor(String key) { + public void setTextColorByKey(int key) { textView.setTextColor(getThemedColor(key)); textView.setTag(key); } @@ -195,8 +195,7 @@ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { info.setText(text); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextRadioCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextRadioCell.java index e5933a3945..b38b18beaa 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextRadioCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextRadioCell.java @@ -152,7 +152,7 @@ public void setTextAndCheck(String text, boolean checked, boolean divider) { setWillNotDraw(!divider); } - public void setColors(String key, String switchKey, String switchKeyChecked, String switchThumb, String switchThumbChecked) { + public void setColors(int key, int switchKey, int switchKeyChecked, int switchThumb, int switchThumbChecked) { textView.setTextColor(Theme.getColor(key)); // radioButton.setColors(switchKey, switchKeyChecked, switchThumb, switchThumbChecked); textView.setTag(key); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSelectionHelper.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSelectionHelper.java index 07f5537a2c..31014ff1ce 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSelectionHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSelectionHelper.java @@ -39,6 +39,7 @@ import android.widget.TextView; import androidx.annotation.NonNull; +import androidx.core.widget.NestedScrollView; import androidx.recyclerview.widget.LinearLayoutManager; import org.jetbrains.annotations.NotNull; @@ -296,6 +297,9 @@ public void run() { } }; + protected Theme.ResourcesProvider resourcesProvider; + public boolean useMovingOffset = true; + private boolean invalidateParent; public TextSelectionHelper() { longpressDelay = ViewConfiguration.getLongPressTimeout(); @@ -303,6 +307,11 @@ public TextSelectionHelper() { selectionPaint.setPathEffect(new CornerPathEffect(cornerRadius = AndroidUtilities.dp(6))); } + public void setInvalidateParent() { + invalidateParent = true; + } + + public interface OnTranslateListener { public void run(CharSequence text, String fromLang, String toLang, Runnable onAlertDismiss); } @@ -396,7 +405,7 @@ private void showMagnifier(int x) { int offset = movingHandleStart ? selectionStart : selectionEnd; fillLayoutForOffset(offset, layoutBlock); - StaticLayout layout = layoutBlock.layout; + Layout layout = layoutBlock.layout; if (layout == null) { return; } @@ -404,17 +413,18 @@ private void showMagnifier(int x) { int line = layout.getLineForOffset(offset); int lineHeight = layout.getLineBottom(line) - layout.getLineTop(line); - int newY = (int) (layout.getLineTop(line) + textY + selectedView.getY()) - lineHeight - AndroidUtilities.dp(8); + int[] coordsInParent = getCoordsInParent(); + int newY = (int) (layout.getLineTop(line) + textY + coordsInParent[1]) - lineHeight - AndroidUtilities.dp(8); newY += layoutBlock.yOffset; int startLine; int endLine; if (selectedView instanceof ArticleViewer.BlockTableCell) { - startLine = (int) selectedView.getX(); - endLine = (int) selectedView.getX() + selectedView.getMeasuredWidth(); + startLine = (int) coordsInParent[0]; + endLine = (int) coordsInParent[0] + selectedView.getMeasuredWidth(); } else { - startLine = (int) (selectedView.getX() + textX + layout.getLineLeft(line)); - endLine = (int) (selectedView.getX() + textX + layout.getLineRight(line)); + startLine = (int) (coordsInParent[0] + textX + layout.getLineLeft(line)); + endLine = (int) (coordsInParent[0] + textX + layout.getLineRight(line)); } if (x < startLine) { x = startLine; @@ -482,7 +492,7 @@ protected void showHandleViews() { handleViewAnimator.start(); } - public boolean isSelectionMode() { + public boolean isInSelectionMode() { return selectionStart >= 0 && selectionEnd >= 0; } @@ -496,7 +506,7 @@ private void showActions() { return; } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (!movingHandle && isSelectionMode() && canShowActions()) { + if (!movingHandle && isInSelectionMode() && canShowActions()) { if (!actionsIsShowing) { if (actionMode == null) { FloatingToolbar floatingToolbar = new FloatingToolbar(textSelectionOverlay.getContext(), textSelectionOverlay, STYLE_THEME, getResourcesProvider()); @@ -511,11 +521,11 @@ private void showActions() { } } else { if (!showActionsAsPopupAlways) { - if (actionMode == null && isSelectionMode()) { + if (actionMode == null && isInSelectionMode()) { actionMode = textSelectionOverlay.startActionMode(textSelectActionCallback); } } else { - if (!movingHandle && isSelectionMode() && canShowActions()) { + if (!movingHandle && isInSelectionMode() && canShowActions()) { if (popupLayout == null) { popupRect = new android.graphics.Rect(); popupLayout = new ActionBarPopupWindow.ActionBarPopupWindowLayout(textSelectionOverlay.getContext()); @@ -559,7 +569,8 @@ private void showActions() { if (selectedView != null) { int lineHeight = -getLineHeight(); int[] coords = offsetToCord(selectionStart); - y = (int) (coords[1] + textY + selectedView.getY()) + lineHeight / 2 - AndroidUtilities.dp(4); + int[] coordsInParent = getCoordsInParent(); + y = (int) (coords[1] + textY + coordsInParent[1]) + lineHeight / 2 - AndroidUtilities.dp(4); if (y < 0) y = 0; } @@ -595,7 +606,7 @@ private void hideActions() { } actionsIsShowing = false; } - if (!isSelectionMode() && actionMode != null) { + if (!isInSelectionMode() && actionMode != null) { actionMode.finish(); actionMode = null; } @@ -673,7 +684,7 @@ public boolean isTryingSelect() { } public void onParentScrolled() { - if (isSelectionMode() && textSelectionOverlay != null) { + if (isInSelectionMode() && textSelectionOverlay != null) { parentIsScrolling = true; textSelectionOverlay.invalidate(); hideActions(); @@ -710,7 +721,7 @@ public TextSelectionOverlay(Context context) { public boolean checkOnTap(MotionEvent event) { - if (!isSelectionMode() || movingHandle) return false; + if (!isInSelectionMode() || movingHandle) return false; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: pressedX = event.getX(); @@ -732,7 +743,7 @@ public boolean checkOnTap(MotionEvent event) { @Override public boolean onTouchEvent(MotionEvent event) { - if (!isSelectionMode()) return false; + if (!isInSelectionMode()) return false; if (event.getPointerCount() > 1) { return movingHandle; } @@ -762,8 +773,13 @@ public boolean onTouchEvent(MotionEvent event) { float textSizeHalf = getLineHeight() / 2; - movingOffsetX = cords[0] + textX + selectedView.getX() - x; - movingOffsetY = cords[1] + textY + selectedView.getTop() - y - textSizeHalf; + int[] coordsInParent = getCoordsInParent(); + if (useMovingOffset) { + movingOffsetX = cords[0] + textX + coordsInParent[0] - x; + } else { + movingOffsetX = 0; + } + movingOffsetY = cords[1] + textY + coordsInParent[1] - y - textSizeHalf; hideActions(); return true; } @@ -778,8 +794,9 @@ public boolean onTouchEvent(MotionEvent event) { int[] cords = offsetToCord(selectionEnd); float textSizeHalf = getLineHeight() / 2; - movingOffsetX = cords[0] + textX + selectedView.getX() - x; - movingOffsetY = cords[1] + textY + selectedView.getTop() - y - textSizeHalf; + int[] coordsInParent = getCoordsInParent(); + movingOffsetX = cords[0] + textX + coordsInParent[0] - x; + movingOffsetY = cords[1] + textY + coordsInParent[1] - y - textSizeHalf; showMagnifier(lastX); hideActions(); return true; @@ -814,15 +831,16 @@ public boolean onTouchEvent(MotionEvent event) { fillLayoutForOffset(selectionEnd, layoutBlock); } - StaticLayout oldTextLayout = layoutBlock.layout; + Layout oldTextLayout = layoutBlock.layout; if (oldTextLayout == null) { return true; } float oldYoffset = layoutBlock.yOffset; Cell oldSelectedView = selectedView; - y -= selectedView.getTop(); - x -= selectedView.getX(); + int[] coordsInParent = getCoordsInParent(); + y -= coordsInParent[1]; + x -= coordsInParent[0]; boolean canScrollDown = event.getY() - touchSlop > parentView.getMeasuredHeight() - getParentBottomPadding() && (multiselect || selectedView.getBottom() > parentView.getMeasuredHeight() - getParentBottomPadding()); boolean canScrollUp = event.getY() < ((View) parentView.getParent()).getTop() + getParentTopPadding() && (multiselect || selectedView.getTop() < getParentTopPadding()); @@ -867,10 +885,10 @@ public boolean onTouchEvent(MotionEvent event) { CharSequence text = getText(selectedView, false); fillLayoutForOffset(newSelection, layoutBlock); - StaticLayout layoutOld = layoutBlock.layout; + Layout layoutOld = layoutBlock.layout; fillLayoutForOffset(selectionStart, layoutBlock); - StaticLayout layoutNew = layoutBlock.layout; + Layout layoutNew = layoutBlock.layout; if (layoutOld == null || layoutNew == null) { return true; @@ -969,10 +987,10 @@ public boolean onTouchEvent(MotionEvent event) { fillLayoutForOffset(newSelection, layoutBlock); - StaticLayout layoutOld = layoutBlock.layout; + Layout layoutOld = layoutBlock.layout; fillLayoutForOffset(selectionEnd, layoutBlock); - StaticLayout layoutNew = layoutBlock.layout; + Layout layoutNew = layoutBlock.layout; if (layoutOld == null || layoutNew == null) { return true; @@ -1052,7 +1070,7 @@ public boolean onTouchEvent(MotionEvent event) { movingHandle = false; movingDirectionSettling = false; isOneTouch = false; - if (isSelectionMode()) { + if (isInSelectionMode()) { showActions(); showHandleViews(); } @@ -1068,7 +1086,7 @@ public boolean onTouchEvent(MotionEvent event) { @Override protected void onDraw(Canvas canvas) { - if (!isSelectionMode()) return; + if (!isInSelectionMode()) return; int handleViewSize = AndroidUtilities.dp(22); int count = 0; @@ -1076,8 +1094,10 @@ protected void onDraw(Canvas canvas) { pickEndView(); if (selectedView != null) { canvas.save(); - float yOffset = selectedView.getY() + textY; - float xOffset = selectedView.getX() + textX; + int[] coordsInParent = getCoordsInParent(); + float yOffset = coordsInParent[1] + textY; + float xOffset = coordsInParent[0] + textX; + canvas.translate(xOffset, yOffset); MessageObject msg = selectedView instanceof ChatMessageCell ? ((ChatMessageCell) selectedView).getMessageObject() : null; @@ -1091,7 +1111,7 @@ protected void onDraw(Canvas canvas) { if (selectionEnd >= 0 && selectionEnd <= len) { fillLayoutForOffset(selectionEnd, layoutBlock); - StaticLayout layout = layoutBlock.layout; + Layout layout = layoutBlock.layout; if (layout != null) { int end = selectionEnd - layoutBlock.charOffset; int textLen = layout.getText().length(); @@ -1148,15 +1168,16 @@ protected void onDraw(Canvas canvas) { pickStartView(); if (selectedView != null) { canvas.save(); - float yOffset = selectedView.getY() + textY; - float xOffset = selectedView.getX() + textX; + int[] coordsInParent = getCoordsInParent(); + float yOffset = coordsInParent[1] + textY; + float xOffset = coordsInParent[0] + textX; canvas.translate(xOffset, yOffset); int len = getText(selectedView, false).length(); if (selectionStart >= 0 && selectionStart <= len) { fillLayoutForOffset(selectionStart, layoutBlock); - StaticLayout layout = layoutBlock.layout; + Layout layout = layoutBlock.layout; if (layout != null) { int start = selectionStart - layoutBlock.charOffset; int line = layout.getLineForOffset(start); @@ -1237,6 +1258,67 @@ protected void onDraw(Canvas canvas) { invalidate(); } } + + public boolean isTouched() { + return movingHandle; + } + + public void checkCancel(float lastMotionX, float lastMotionY, boolean inParent) { + if (!inParent) { + int[] coordsInParent = getCoordsInParent(); + lastMotionY += coordsInParent[1] + textY; + } + if (!movingHandle && (lastMotionY < startArea.top - AndroidUtilities.dp(8) || lastMotionY > endArea.bottom + AndroidUtilities.dp(8))) { + clear(); + } + } + + float cancelPressedX, cancelPressedY; + public void checkCancelAction(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + cancelPressedX = ev.getX(); + cancelPressedY = ev.getY(); + } else if (Math.abs(ev.getX() - cancelPressedX) < AndroidUtilities.touchSlop && Math.abs(ev.getY() - cancelPressedY) < AndroidUtilities.touchSlop && (ev.getAction() == MotionEvent.ACTION_CANCEL || ev.getAction() == MotionEvent.ACTION_UP)) { + checkCancel(ev.getX(), ev.getY(), true); + } + } + + @Override + public void invalidate() { + super.invalidate(); + if (invalidateParent && parentView != null) { + parentView.invalidate(); + } + } + } + + private int[] getCoordsInParent() { + View child = (View) selectedView; + int yOffset = 0; + int xOffset = 0; + if (child != null && parentView != null) { + while (child != parentView) { + if (child == null) { + xOffset = 0; + yOffset = 0; + break; + } + yOffset += child.getY(); + xOffset += child.getX(); + if (child instanceof NestedScrollView) { + yOffset -= child.getScrollY(); + xOffset -= child.getScrollX(); + } + if (child.getParent() instanceof View) { + child = (View) child.getParent(); + } else { + xOffset = 0; + yOffset = 0; + break; + } + } + } + return new int[] {xOffset, yOffset}; } protected void jumpToLine(int newSelection, int nextWhitespace, boolean viewChanged, float newYoffset, float oldYoffset, Cell oldSelectedView) { @@ -1338,7 +1420,7 @@ public boolean onPrepareActionMode(ActionMode mode, Menu menu) { @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - if (!isSelectionMode()) { + if (!isInSelectionMode()) { return true; } switch (item.getItemId()) { @@ -1425,17 +1507,18 @@ public void onDestroyActionMode(ActionMode mode) { @Override public void onGetContentRect(ActionMode mode, View view, Rect outRect) { - if (!isSelectionMode()) { + if (!isInSelectionMode()) { return; } pickStartView(); int x1 = 0; int y1 = 1; + int[] coordsInParent = getCoordsInParent(); if (selectedView != null) { int lineHeight = -getLineHeight(); int[] coords = offsetToCord(selectionStart); x1 = coords[0] + textX; - y1 = (int) (coords[1] + textY + selectedView.getY()) + lineHeight / 2 - AndroidUtilities.dp(4); + y1 = (int) (coords[1] + textY + coordsInParent[1]) + lineHeight / 2 - AndroidUtilities.dp(4); if (y1 < 1) y1 = 1; } @@ -1457,7 +1540,7 @@ public void onGetContentRect(ActionMode mode, View view, Rect outRect) { } private void copyText() { - if (!isSelectionMode()) { + if (!isInSelectionMode()) { return; } CharSequence str = getSelectedText(); @@ -1473,7 +1556,7 @@ private void copyText() { } private void translateText() { - if (!isSelectionMode()) { + if (!isInSelectionMode()) { return; } CharSequence str = getSelectedText(); @@ -1493,7 +1576,7 @@ protected CharSequence getSelectedText() { protected int[] offsetToCord(int offset) { fillLayoutForOffset(offset, layoutBlock); - StaticLayout layout = layoutBlock.layout; + Layout layout = layoutBlock.layout; int blockOffset = offset - layoutBlock.charOffset; if (layout == null || blockOffset < 0 || blockOffset > layout.getText().length()) { return tmpCoord; @@ -1506,7 +1589,7 @@ protected int[] offsetToCord(int offset) { return tmpCoord; } - protected void drawSelection(Canvas canvas, StaticLayout layout, int selectionStart, int selectionEnd, boolean hasStart, boolean hasEnd) { + protected void drawSelection(Canvas canvas, Layout layout, int selectionStart, int selectionEnd, boolean hasStart, boolean hasEnd) { selectionPath.reset(); selectionHandlePath.reset(); final float R = cornerRadius * 1.65f; @@ -1603,7 +1686,7 @@ protected void drawSelection(Canvas canvas, StaticLayout layout, int selectionSt } private final ScalablePath tempPath2 = new ScalablePath(); - private void drawLine(StaticLayout layout, int line, int start, int end, boolean padAtStart, boolean padAtEnd) { + private void drawLine(Layout layout, int line, int start, int end, boolean padAtStart, boolean padAtEnd) { tempPath2.reset(); layout.getSelectionPath(start, end, tempPath2); @@ -1636,11 +1719,11 @@ private void drawLine(StaticLayout layout, int line, int start, int end, boolean } } - private static class LayoutBlock { + public static class LayoutBlock { public int charOffset; - StaticLayout layout; - float yOffset; - float xOffset; + public Layout layout; + public float yOffset; + public float xOffset; } @@ -1670,6 +1753,109 @@ protected void fillLayoutForOffset(int offset, LayoutBlock layoutBlock) { protected abstract void onTextSelected(Cell newView, Cell oldView); + public static class SimpleTextSelectionHelper extends TextSelectionHelper { + + SimpleSelectabeleView selectabeleView; + + public SimpleTextSelectionHelper(SimpleSelectabeleView selectabeleView, Theme.ResourcesProvider resourcesProvider) { + this.selectabeleView = selectabeleView; + this.resourcesProvider = resourcesProvider; + } + + public void setSelectabeleView(SimpleSelectabeleView selectabeleView) { + this.selectabeleView = selectabeleView; + } + + @Override + protected CharSequence getText(SimpleSelectabeleView view, boolean maybe) { + return view.getText(); + } + + @Override + protected int getCharOffsetFromCord(int x, int y, int offsetX, int offsetY, SimpleSelectabeleView view, boolean maybe) { + if (y < 0) { + y = 1; + } + float yOffset = 0; + Layout lastLayout = view.getStaticTextLayout(); + if (y > yOffset + lastLayout.getLineBottom(lastLayout.getLineCount() - 1)) { + y = (int) (yOffset + lastLayout.getLineBottom(lastLayout.getLineCount() - 1) - 1); + } + + if (layoutBlock.layout == null) { + return -1; + } + + Layout layout = layoutBlock.layout; + x -= layoutBlock.xOffset; + + int line = -1; + for (int i = 0; i < layout.getLineCount(); i++) { + if (y > layoutBlock.yOffset + layout.getLineTop(i) && y < layoutBlock.yOffset + layout.getLineBottom(i)) { + line = i; + break; + } + } + if (line >= 0) { + int k = layoutBlock.charOffset + layout.getOffsetForHorizontal(line, x);; + return k; + } + + return -1; + } + + @Override + protected void fillLayoutForOffset(int offset, LayoutBlock layoutBlock, boolean maybe) { + layoutBlock.layout = selectabeleView.getStaticTextLayout(); + layoutBlock.xOffset = layoutBlock.yOffset = 0; + layoutBlock.charOffset = 0; + } + + @Override + protected int getLineHeight() { + Layout layout = selectabeleView.getStaticTextLayout(); + int lineHeight = layout.getLineBottom(0) - layout.getLineTop(0); + return lineHeight; + } + + @Override + protected void onTextSelected(SimpleSelectabeleView newView, SimpleSelectabeleView oldView) { + + } + + public void update(float textX, float textY) { + Layout layout = selectabeleView.getStaticTextLayout(); + if (layout == null) { + textArea.setEmpty(); + maybeSelectedView = null; + } else { + maybeSelectedView = selectabeleView; + maybeTextX = (int) textX; + maybeTextY = (int) textY; + layoutBlock.layout = layout; + layoutBlock.xOffset = textX; + layoutBlock.yOffset = textY; + layoutBlock.charOffset = 0; + textArea.set( + (int) textX, (int) textY, + (int) (textX + layout.getWidth()), (int) (textY + layout.getHeight()) + ); + } + } + + public void draw(Canvas canvas) { + Layout layout = selectabeleView.getStaticTextLayout(); + int color = Theme.getColor(Theme.key_chat_textSelectBackground, resourcesProvider); + selectionPaint.setColor(color); + selectionHandlePaint.setColor(color); + drawSelection(canvas, layout, selectionStart, selectionEnd, true, true); + } + + public boolean isCurrent(SimpleSelectabeleView view) { + return this.selectabeleView == view; + } + } + public static class ChatListTextSelectionHelper extends TextSelectionHelper { SparseArray animatorSparseArray = new SparseArray<>(); @@ -1853,7 +2039,7 @@ protected int getCharOffsetFromCord(int x, int y, int offsetX, int offsetY, Chat return -1; } - StaticLayout layout = layoutBlock.layout; + Layout layout = layoutBlock.layout; x -= layoutBlock.xOffset; @@ -2512,7 +2698,7 @@ protected void onNewViewSelected(ArticleSelectableView oldView, ArticleSelectabl boolean startPeek; protected void pickEndView() { - if (!isSelectionMode()) { + if (!isInSelectionMode()) { return; } startPeek = false; @@ -2554,7 +2740,7 @@ protected void pickEndView() { } protected void pickStartView() { - if (!isSelectionMode()) { + if (!isInSelectionMode()) { return; } startPeek = true; @@ -2805,6 +2991,13 @@ public interface ArticleSelectableView extends SelectableView { void fillTextLayoutBlocks(ArrayList blocks); } + public interface SimpleSelectabeleView extends SelectableView { + + CharSequence getText(); + + Layout getStaticTextLayout(); + } + public interface SelectableView { int getBottom(); @@ -2921,11 +3114,11 @@ public int getParentBottomPadding() { return 0; } - protected int getThemedColor(String key) { - return Theme.getColor(key); + protected int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } protected Theme.ResourcesProvider getResourcesProvider() { - return null; + return resourcesProvider; } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSettingsCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSettingsCell.java index 06daba2707..e57ff41e32 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSettingsCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSettingsCell.java @@ -46,7 +46,7 @@ public class TextSettingsCell extends FrameLayout { private boolean imageViewIsColorful; private BackupImageView valueBackupImageView; private ImageView valueImageView; - private boolean needDivider; + public boolean needDivider; private boolean canDisable; private boolean drawLoading; private int padding; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ThemePreviewMessagesCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ThemePreviewMessagesCell.java index a226d95402..b514d4109a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ThemePreviewMessagesCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ThemePreviewMessagesCell.java @@ -63,7 +63,7 @@ public ThemePreviewMessagesCell(Context context, INavigationLayout layout, int t setOrientation(LinearLayout.VERTICAL); setPadding(0, AndroidUtilities.dp(11), 0, AndroidUtilities.dp(11)); - shadowDrawable = Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow); + shadowDrawable = Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow); int date = (int) (System.currentTimeMillis() / 1000) - 60 * 60; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ThemesHorizontalListCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ThemesHorizontalListCell.java index 8a3ba2b704..5977e5ce4b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ThemesHorizontalListCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ThemesHorizontalListCell.java @@ -50,6 +50,7 @@ import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.EmojiThemes; import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.ActionBar.ThemeColors; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.MotionBackgroundDrawable; import org.telegram.ui.Components.RadioButton; @@ -293,8 +294,8 @@ private boolean parseTheme() { break; } else { if ((idx = line.indexOf('=')) != -1) { - String key = line.substring(0, idx); - if (key.equals(Theme.key_chat_inBubble) || key.equals(Theme.key_chat_outBubble) || key.equals(Theme.key_chat_wallpaper) || key.equals(Theme.key_chat_wallpaper_gradient_to1) || key.equals(Theme.key_chat_wallpaper_gradient_to2) || key.equals(Theme.key_chat_wallpaper_gradient_to3)) { + int key = ThemeColors.stringKeyToInt(line.substring(0, idx)); + if (key == Theme.key_chat_inBubble || key == Theme.key_chat_outBubble || key == Theme.key_chat_wallpaper || key == Theme.key_chat_wallpaper_gradient_to1 || key == Theme.key_chat_wallpaper_gradient_to2 || key == Theme.key_chat_wallpaper_gradient_to3) { String param = line.substring(idx + 1); int value; if (param.length() > 0 && param.charAt(0) == '#') { @@ -306,25 +307,18 @@ private boolean parseTheme() { } else { value = Utilities.parseInt(param); } - switch (key) { - case Theme.key_chat_inBubble: - themeInfo.setPreviewInColor(value); - break; - case Theme.key_chat_outBubble: - themeInfo.setPreviewOutColor(value); - break; - case Theme.key_chat_wallpaper: - themeInfo.setPreviewBackgroundColor(value); - break; - case Theme.key_chat_wallpaper_gradient_to1: - themeInfo.previewBackgroundGradientColor1 = value; - break; - case Theme.key_chat_wallpaper_gradient_to2: - themeInfo.previewBackgroundGradientColor2 = value; - break; - case Theme.key_chat_wallpaper_gradient_to3: - themeInfo.previewBackgroundGradientColor3 = value; - break; + if (key == Theme.key_chat_inBubble) { + themeInfo.setPreviewInColor(value); + } else if (key == Theme.key_chat_outBubble){ + themeInfo.setPreviewOutColor(value); + } else if (key == Theme.key_chat_wallpaper) { + themeInfo.setPreviewBackgroundColor(value); + } else if (key == Theme.key_chat_wallpaper_gradient_to1) { + themeInfo.previewBackgroundGradientColor1 = value; + } else if (key == Theme.key_chat_wallpaper_gradient_to2) { + themeInfo.previewBackgroundGradientColor2 = value; + } else if (key == Theme.key_chat_wallpaper_gradient_to3) { + themeInfo.previewBackgroundGradientColor3 = value; } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TooManyCommunitiesHintCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TooManyCommunitiesHintCell.java index 00abce7f25..a0c0690c9f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TooManyCommunitiesHintCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TooManyCommunitiesHintCell.java @@ -61,7 +61,7 @@ public TooManyCommunitiesHintCell(Context context) { @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); - paint.setColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText)); + paint.setColor(Theme.getColor(Theme.key_text_RedRegular)); canvas.save(); canvas.translate(getMeasuredWidth() - textPaint.measureText(s) - AndroidUtilities.dp(8), AndroidUtilities.dpf2(7f)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/UserCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/UserCell.java index 9eb522060e..b2cd332132 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/UserCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/UserCell.java @@ -16,6 +16,7 @@ import android.graphics.drawable.Drawable; import android.util.TypedValue; import android.view.Gravity; +import android.view.MotionEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; import android.widget.ImageView; @@ -35,6 +36,7 @@ import org.telegram.messenger.UserObject; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.SimpleTextView; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Components.AnimatedEmojiDrawable; @@ -44,11 +46,15 @@ import org.telegram.ui.Components.CheckBox; import org.telegram.ui.Components.CheckBoxSquare; import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.LaunchActivity; import org.telegram.ui.NotificationsSettingsActivity; +import org.telegram.ui.Stories.StoriesListPlaceProvider; +import org.telegram.ui.Stories.StoriesUtilities; public class UserCell extends FrameLayout implements NotificationCenter.NotificationCenterDelegate { - private BackupImageView avatarImageView; + public BackupImageView avatarImageView; private SimpleTextView nameTextView; private SimpleTextView statusTextView; private ImageView imageView; @@ -62,6 +68,7 @@ public class UserCell extends FrameLayout implements NotificationCenter.Notifica private ImageView mutualView; private AvatarDrawable avatarDrawable; + private boolean storiable; private Object currentObject; private TLRPC.EncryptedChat encryptedChat; @@ -81,7 +88,23 @@ public class UserCell extends FrameLayout implements NotificationCenter.Notifica private int statusColor; private int statusOnlineColor; - private boolean needDivider; + public boolean needDivider; + public StoriesUtilities.AvatarStoryParams storyParams = new StoriesUtilities.AvatarStoryParams(false) { + @Override + public void openStory(long dialogId, Runnable onDone) { + UserCell.this.openStory(dialogId, onDone); + } + }; + + public void openStory(long dialogId, Runnable runnable) { + BaseFragment fragment = LaunchActivity.getLastFragment(); + if (fragment != null) { + fragment.getOrCreateStoryViewer().doOnAnimationReady(runnable); + fragment.getOrCreateStoryViewer().open(getContext(), dialogId, StoriesListPlaceProvider.of((RecyclerListView) getParent())); + } + } + + protected long dialogId; public UserCell(Context context, int padding, int checkbox, boolean admin) { this(context, padding, checkbox, admin, false, null, false); @@ -109,7 +132,7 @@ public UserCell(Context context, int padding, int checkbox, boolean admin, boole addButton.setTextColor(Theme.getColor(Theme.key_featuredStickers_buttonText, resourcesProvider)); addButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); addButton.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - addButton.setBackgroundDrawable(Theme.AdaptiveRipple.filledRect(Theme.key_featuredStickers_addButton, 4)); + addButton.setBackgroundDrawable(Theme.AdaptiveRipple.filledRectByKey(Theme.key_featuredStickers_addButton, 4)); addButton.setText(LocaleController.getString("Add", R.string.Add)); addButton.setPadding(AndroidUtilities.dp(17), 0, AndroidUtilities.dp(17), 0); addView(addButton, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 28, Gravity.TOP | (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT), LocaleController.isRTL ? 14 : 0, 15, LocaleController.isRTL ? 0 : 14, 0)); @@ -123,9 +146,28 @@ public UserCell(Context context, int padding, int checkbox, boolean admin, boole avatarDrawable = new AvatarDrawable(); - avatarImageView = new BackupImageView(context); + avatarImageView = new BackupImageView(context) { + @Override + protected void onDraw(Canvas canvas) { + if (storiable) { + storyParams.originalAvatarRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight()); + StoriesUtilities.drawAvatarWithStory(dialogId, canvas, imageReceiver, storyParams); + } else { + super.onDraw(canvas); + } + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (storyParams.checkOnTouchEvent(event, this)) { + return true; + } + return super.onTouchEvent(event); + } + }; avatarImageView.setRoundRadius(AndroidUtilities.dp(24)); addView(avatarImageView, LayoutHelper.createFrame(46, 46, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 0 : 7 + padding, 6, LocaleController.isRTL ? 7 + padding : 0, 0)); + setClipChildren(false); nameTextView = new SimpleTextView(context); nameTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); @@ -238,6 +280,7 @@ public void setData(Object object, TLRPC.EncryptedChat ec, CharSequence name, Ch if (object == null && name == null && status == null) { currentStatus = null; currentName = null; + storiable = false; currentObject = null; nameTextView.setText(""); statusTextView.setText(""); @@ -254,6 +297,7 @@ public void setData(Object object, TLRPC.EncryptedChat ec, CharSequence name, Ch Exception ignore) { } currentName = name; + storiable = !(object instanceof String); currentObject = object; currentDrawable = resId; needDivider = divider; @@ -267,45 +311,58 @@ public Object getCurrentObject() { public void setException(NotificationsSettingsActivity.NotificationException exception, CharSequence name, boolean divider) { String text; - boolean enabled; - boolean custom = exception.hasCustom; - int value = exception.notify; - int delta = exception.muteUntil; - if (value == 3 && delta != Integer.MAX_VALUE) { - delta -= ConnectionsManager.getInstance(currentAccount).getCurrentTime(); - if (delta <= 0) { - if (custom) { - text = LocaleController.getString("NotificationsCustom", R.string.NotificationsCustom); - } else { - text = LocaleController.getString("NotificationsUnmuted", R.string.NotificationsUnmuted); - } - } else if (delta < 60 * 60) { - text = LocaleController.formatString("WillUnmuteIn", R.string.WillUnmuteIn, LocaleController.formatPluralString("Minutes", delta / 60)); - } else if (delta < 60 * 60 * 24) { - text = LocaleController.formatString("WillUnmuteIn", R.string.WillUnmuteIn, LocaleController.formatPluralString("Hours", (int) Math.ceil(delta / 60.0f / 60))); - } else if (delta < 60 * 60 * 24 * 365) { - text = LocaleController.formatString("WillUnmuteIn", R.string.WillUnmuteIn, LocaleController.formatPluralString("Days", (int) Math.ceil(delta / 60.0f / 60 / 24))); + if (exception.story) { + if (exception.notify <= 0 && exception.auto) { + text = LocaleController.getString("NotificationEnabledAutomatically"); + } else if (exception.notify <= 0) { + text = LocaleController.getString("NotificationEnabled"); } else { - text = null; + text = LocaleController.getString("NotificationDisabled"); } } else { - if (value == 0) { - enabled = true; - } else if (value == 1) { - enabled = true; - } else if (value == 2) { - enabled = false; + boolean enabled; + boolean custom = exception.hasCustom; + int value = exception.notify; + int delta = exception.muteUntil; + if (value == 3 && delta != Integer.MAX_VALUE) { + delta -= ConnectionsManager.getInstance(currentAccount).getCurrentTime(); + if (delta <= 0) { + if (custom) { + text = LocaleController.getString("NotificationsCustom", R.string.NotificationsCustom); + } else { + text = LocaleController.getString("NotificationsUnmuted", R.string.NotificationsUnmuted); + } + } else if (delta < 60 * 60) { + text = LocaleController.formatString("WillUnmuteIn", R.string.WillUnmuteIn, LocaleController.formatPluralString("Minutes", delta / 60)); + } else if (delta < 60 * 60 * 24) { + text = LocaleController.formatString("WillUnmuteIn", R.string.WillUnmuteIn, LocaleController.formatPluralString("Hours", (int) Math.ceil(delta / 60.0f / 60))); + } else if (delta < 60 * 60 * 24 * 365) { + text = LocaleController.formatString("WillUnmuteIn", R.string.WillUnmuteIn, LocaleController.formatPluralString("Days", (int) Math.ceil(delta / 60.0f / 60 / 24))); + } else { + text = null; + } } else { - enabled = false; + if (value == 0) { + enabled = true; + } else if (value == 1) { + enabled = true; + } else if (value == 2) { + enabled = false; + } else { + enabled = false; + } + if (enabled && custom) { + text = LocaleController.getString("NotificationsCustom", R.string.NotificationsCustom); + } else { + text = enabled ? LocaleController.getString("NotificationsUnmuted", R.string.NotificationsUnmuted) : LocaleController.getString("NotificationsMuted", R.string.NotificationsMuted); + } } - if (enabled && custom) { - text = LocaleController.getString("NotificationsCustom", R.string.NotificationsCustom); - } else { - text = enabled ? LocaleController.getString("NotificationsUnmuted", R.string.NotificationsUnmuted) : LocaleController.getString("NotificationsMuted", R.string.NotificationsMuted); + if (text == null) { + text = LocaleController.getString("NotificationsOff", R.string.NotificationsOff); + } + if (exception.auto) { + text += ", Auto"; } - } - if (text == null) { - text = LocaleController.getString("NotificationsOff", R.string.NotificationsOff); } if (DialogObject.isEncryptedDialog(exception.did)) { @@ -380,16 +437,19 @@ public void update(int mask) { String newName = null; TLRPC.User currentUser = null; TLRPC.Chat currentChat = null; + dialogId = 0; if (currentObject instanceof TLRPC.User) { currentUser = (TLRPC.User) currentObject; if (currentUser.photo != null) { photo = currentUser.photo.photo_small; } + dialogId = currentUser.id; } else if (currentObject instanceof TLRPC.Chat) { currentChat = (TLRPC.Chat) currentObject; if (currentChat.photo != null) { photo = currentChat.photo.photo_small; } + dialogId = currentChat.id; } if (mask != 0) { @@ -628,5 +688,10 @@ protected void onDetachedFromWindow() { super.onDetachedFromWindow(); NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.emojiLoaded); emojiStatus.detach(); + storyParams.onDetachFromWindow(); + } + + public long getDialogId() { + return dialogId; } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/WallpaperCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/WallpaperCell.java index 8d61e086ac..6b39900c31 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/WallpaperCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/WallpaperCell.java @@ -44,7 +44,10 @@ public class WallpaperCell extends FrameLayout { - private class WallpaperView extends FrameLayout { + int size; + public boolean drawStubBackground = true; + + public class WallpaperView extends FrameLayout { private BackupImageView imageView; private ImageView imageView2; @@ -290,7 +293,7 @@ public void clearAnimation() { @Override protected void onDraw(Canvas canvas) { - if (checkBox.isChecked() || !imageView.getImageReceiver().hasBitmapImage() || imageView.getImageReceiver().getCurrentAlpha() != 1.0f) { + if (drawStubBackground && checkBox.isChecked() || !imageView.getImageReceiver().hasBitmapImage() || imageView.getImageReceiver().getCurrentAlpha() != 1.0f) { canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), backgroundPaint); } } @@ -307,9 +310,13 @@ protected void onDraw(Canvas canvas) { private Drawable checkDrawable; public WallpaperCell(Context context) { + this(context, 5); + } + + public WallpaperCell(Context context, int size) { super(context); - wallpaperViews = new WallpaperView[5]; + wallpaperViews = new WallpaperView[size]; for (int a = 0; a < wallpaperViews.length; a++) { WallpaperView wallpaperView = wallpaperViews[a] = new WallpaperView(context); int num = a; @@ -339,6 +346,11 @@ protected boolean onWallpaperLongClick(Object wallPaper, int index) { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (spanCount == 1) { + super.onMeasure(MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(size + AndroidUtilities.dp(6), MeasureSpec.EXACTLY)); + setPadding(0, 0, 0, AndroidUtilities.dp(6)); + return; + } int width = MeasureSpec.getSize(widthMeasureSpec); int availableWidth = width - AndroidUtilities.dp(14 * 2 + 6 * (spanCount - 1)); int itemWidth = availableWidth / spanCount; @@ -353,6 +365,10 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + if (spanCount == 1) { + super.onLayout(changed, left, top, right, bottom); + return; + } int l = AndroidUtilities.dp(14); int t = isTop ? AndroidUtilities.dp(14) : 0; for (int a = 0; a < spanCount; a++) { @@ -394,4 +410,11 @@ public void invalidate() { wallpaperViews[a].invalidate(); } } + + public void setSize(int itemSize) { + if (size != itemSize) { + this.size = itemSize; + requestLayout(); + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChangeBioActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChangeBioActivity.java index 33bfbbde6d..b40727c6d6 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChangeBioActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChangeBioActivity.java @@ -100,7 +100,7 @@ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { firstNameField.setHintTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteHintText)); firstNameField.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); firstNameField.setBackgroundDrawable(null); - firstNameField.setLineColors(getThemedColor(Theme.key_windowBackgroundWhiteInputField), getThemedColor(Theme.key_windowBackgroundWhiteInputFieldActivated), getThemedColor(Theme.key_windowBackgroundWhiteRedText3)); + firstNameField.setLineColors(getThemedColor(Theme.key_windowBackgroundWhiteInputField), getThemedColor(Theme.key_windowBackgroundWhiteInputFieldActivated), getThemedColor(Theme.key_text_RedRegular)); firstNameField.setMaxLines(4); firstNameField.setPadding(AndroidUtilities.dp(LocaleController.isRTL ? 24 : 0), 0, AndroidUtilities.dp(LocaleController.isRTL ? 0 : 24), AndroidUtilities.dp(6)); firstNameField.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChangeNameActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChangeNameActivity.java index 4576c44c1a..b549485c43 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChangeNameActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChangeNameActivity.java @@ -96,7 +96,7 @@ protected Theme.ResourcesProvider getResourcesProvider() { firstNameField.setHintTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteHintText, resourcesProvider)); firstNameField.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); firstNameField.setBackgroundDrawable(null); - firstNameField.setLineColors(getThemedColor(Theme.key_windowBackgroundWhiteInputField), getThemedColor(Theme.key_windowBackgroundWhiteInputFieldActivated), getThemedColor(Theme.key_windowBackgroundWhiteRedText3)); + firstNameField.setLineColors(getThemedColor(Theme.key_windowBackgroundWhiteInputField), getThemedColor(Theme.key_windowBackgroundWhiteInputFieldActivated), getThemedColor(Theme.key_text_RedRegular)); firstNameField.setMaxLines(1); firstNameField.setLines(1); firstNameField.setSingleLine(true); @@ -127,7 +127,7 @@ protected Theme.ResourcesProvider getResourcesProvider() { lastNameField.setHintTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteHintText, resourcesProvider)); lastNameField.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); lastNameField.setBackgroundDrawable(null); - lastNameField.setLineColors(getThemedColor(Theme.key_windowBackgroundWhiteInputField), getThemedColor(Theme.key_windowBackgroundWhiteInputFieldActivated), getThemedColor(Theme.key_windowBackgroundWhiteRedText3)); + lastNameField.setLineColors(getThemedColor(Theme.key_windowBackgroundWhiteInputField), getThemedColor(Theme.key_windowBackgroundWhiteInputFieldActivated), getThemedColor(Theme.key_text_RedRegular)); lastNameField.setMaxLines(1); lastNameField.setLines(1); lastNameField.setSingleLine(true); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChangeUsernameActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChangeUsernameActivity.java index 26686310dd..a1dc9a99ec 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChangeUsernameActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChangeUsernameActivity.java @@ -19,6 +19,7 @@ import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.drawable.Drawable; +import android.os.Bundle; import android.text.Editable; import android.text.InputType; import android.text.Selection; @@ -66,6 +67,7 @@ import org.telegram.messenger.browser.Browser; import org.telegram.messenger.UserConfig; import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; @@ -87,6 +89,7 @@ import org.telegram.ui.Components.LinkSpanDrawable; import org.telegram.ui.Components.RecyclerListView; import org.telegram.ui.Components.TypefaceSpan; +import org.telegram.ui.Components.URLSpanNoUnderline; import java.util.ArrayList; import java.util.List; @@ -114,6 +117,8 @@ public class ChangeUsernameActivity extends BaseFragment { private ArrayList usernames = new ArrayList<>(); private ArrayList loadingUsernames = new ArrayList<>(); + private long botId; + private final static int done_button = 1; public class LinkSpan extends ClickableSpan { @@ -160,6 +165,26 @@ public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event } } + public ChangeUsernameActivity() { + this(null); + } + + public ChangeUsernameActivity(Bundle args) { + super(args); + + if (args != null) { + botId = args.getLong("bot_id"); + } + } + + private long getUserId() { + return botId != 0 ? botId : UserConfig.getInstance(currentAccount).getClientUserId(); + } + + private TLRPC.User getUser() { + return botId != 0 ? MessagesController.getInstance(currentAccount).getUser(botId) : UserConfig.getInstance(currentAccount).getCurrentUser(); + } + @Override public View createView(Context context) { actionBar.setBackButtonImage(R.drawable.ic_ab_back); @@ -180,9 +205,9 @@ public void onItemClick(int id) { ActionBarMenu menu = actionBar.createMenu(); doneButton = menu.addItemWithWidth(done_button, R.drawable.ic_ab_done, AndroidUtilities.dp(56), LocaleController.getString("Done", R.string.Done)); - TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(UserConfig.getInstance(currentAccount).getClientUserId()); + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(getUserId()); if (user == null) { - user = UserConfig.getInstance(currentAccount).getCurrentUser(); + user = getUser(); } if (user != null) { @@ -272,6 +297,10 @@ public void onItemClick(View view, int position) { return; } if (username.editable) { + if (botId != 0) { + return; + } + listView.smoothScrollToPosition(0); focusUsernameField(true); return; @@ -280,30 +309,40 @@ public void onItemClick(View view, int position) { .setTitle(username.active ? LocaleController.getString("UsernameDeactivateLink", R.string.UsernameDeactivateLink) : LocaleController.getString("UsernameActivateLink", R.string.UsernameActivateLink)) .setMessage(username.active ? LocaleController.getString("UsernameDeactivateLinkProfileMessage", R.string.UsernameDeactivateLinkProfileMessage) : LocaleController.getString("UsernameActivateLinkProfileMessage", R.string.UsernameActivateLinkProfileMessage)) .setPositiveButton(username.active ? LocaleController.getString("Hide", R.string.Hide) : LocaleController.getString("Show", R.string.Show), (di, e) -> { + boolean wasActive = username.active; + String reqUsername = username.username; + boolean reqActive = !username.active; + TLObject req; + if (botId == 0) { + TLRPC.TL_account_toggleUsername toggle = new TLRPC.TL_account_toggleUsername(); + toggle.username = reqUsername; + toggle.active = reqActive; + req = toggle; + } else { + TLRPC.TL_bots_toggleUsername toggle = new TLRPC.TL_bots_toggleUsername(); + toggle.bot = MessagesController.getInstance(currentAccount).getInputUser(botId); + toggle.username = reqUsername; + toggle.active = reqActive; + req = toggle; + } - TLRPC.TL_account_toggleUsername req = new TLRPC.TL_account_toggleUsername(); - req.username = username.username; - final boolean wasActive = username.active; - req.active = !username.active; getConnectionsManager().sendRequest(req, (res, err) -> { AndroidUtilities.runOnUIThread(() -> { - loadingUsernames.remove(req.username); + loadingUsernames.remove(reqUsername); if (res instanceof TLRPC.TL_boolTrue) { - toggleUsername(position, req.active); + toggleUsername(position, reqActive); } else if (err != null && "USERNAMES_ACTIVE_TOO_MUCH".equals(err.text)) { - username.active = req.active; + username.active = reqActive; toggleUsername(position, username.active); new AlertDialog.Builder(getContext(), getResourceProvider()) .setTitle(LocaleController.getString("UsernameActivateErrorTitle", R.string.UsernameActivateErrorTitle)) .setMessage(LocaleController.getString("UsernameActivateErrorMessage", R.string.UsernameActivateErrorMessage)) - .setPositiveButton(LocaleController.getString("OK", R.string.OK), (d, v) -> { - toggleUsername(username, wasActive, true); - }) + .setPositiveButton(LocaleController.getString("OK", R.string.OK), (d, v) -> toggleUsername(username, wasActive, true)) .show(); } else { toggleUsername(username, wasActive, true); } - TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(UserConfig.getInstance(currentAccount).getClientUserId()); + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(getUserId()); getMessagesController().updateUsernameActiveness(user, username.username, username.active); }); }); @@ -465,7 +504,7 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi ignoreCheck = false; break; case VIEW_TYPE_HEADER: - ((HeaderCell) holder.itemView).setText(position == 0 ? LocaleController.getString("SetUsernameHeader", R.string.SetUsernameHeader) : LocaleController.getString("UsernamesProfileHeader", R.string.UsernamesProfileHeader)); + ((HeaderCell) holder.itemView).setText(position == 0 ? LocaleController.getString(botId != 0 ? R.string.BotSetPublicLinkHeader : R.string.SetUsernameHeader) : LocaleController.getString("UsernamesProfileHeader", R.string.UsernamesProfileHeader)); break; case VIEW_TYPE_USERNAME: TLRPC.TL_username username = usernames.get(position - 4); @@ -475,13 +514,13 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi } else if (editableUsernameCell == cell) { editableUsernameCell = null; } - cell.set(username, position < getItemCount() - 2, false); + cell.set(username, position < getItemCount() - 2, false, botId); break; case VIEW_TYPE_HELP1: break; case VIEW_TYPE_HELP2: - ((TextInfoPrivacyCell) holder.itemView).setText(LocaleController.getString("UsernamesProfileHelp", R.string.UsernamesProfileHelp)); - ((TextInfoPrivacyCell) holder.itemView).setBackgroundDrawable(Theme.getThemedDrawable(getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + ((TextInfoPrivacyCell) holder.itemView).setText(LocaleController.getString(botId != 0 ? R.string.BotUsernamesHelp : R.string.UsernamesProfileHelp)); + ((TextInfoPrivacyCell) holder.itemView).setBackgroundDrawable(Theme.getThemedDrawableByKey(getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); break; } } @@ -563,7 +602,6 @@ private void sendReorder() { return; } needReorder = false; - TLRPC.TL_account_reorderUsernames req = new TLRPC.TL_account_reorderUsernames(); ArrayList usernames = new ArrayList<>(); for (int i = 0; i < notEditableUsernames.size(); ++i) { if (notEditableUsernames.get(i).active) @@ -573,7 +611,18 @@ private void sendReorder() { if (this.usernames.get(i).active) usernames.add(this.usernames.get(i).username); } - req.order = usernames; + + TLObject req; + if (botId == 0) { + TLRPC.TL_account_reorderUsernames reorder = new TLRPC.TL_account_reorderUsernames(); + reorder.order = usernames; + req = reorder; + } else { + TLRPC.TL_bots_reorderUsernames reorder = new TLRPC.TL_bots_reorderUsernames(); + reorder.bot = MessagesController.getInstance(currentAccount).getInputUser(botId); + reorder.order = usernames; + req = reorder; + } getConnectionsManager().sendRequest(req, (res, err) -> { if (res instanceof TLRPC.TL_boolTrue) {} }); @@ -585,7 +634,7 @@ private void updateUser() { newUsernames.addAll(notEditableUsernames); newUsernames.addAll(usernames); - TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(UserConfig.getInstance(currentAccount).getClientUserId()); + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(getUserId()); user.usernames = newUsernames; MessagesController.getInstance(currentAccount).putUser(user, false, true); } @@ -595,7 +644,7 @@ private void updateUser() { private class UsernameHelpCell extends FrameLayout { - private TextView text1View; + private LinkSpanDrawable.LinksTextView text1View; private LinkSpanDrawable.LinksTextView text2View; public UsernameHelpCell(Context context) { @@ -604,10 +653,10 @@ public UsernameHelpCell(Context context) { helpCell = this; setPadding(AndroidUtilities.dp(18), AndroidUtilities.dp(10), AndroidUtilities.dp(18), AndroidUtilities.dp(17)); - setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); setClipChildren(false); - text1View = new TextView(context); + text1View = new LinkSpanDrawable.LinksTextView(context); text1View.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); text1View.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText8)); text1View.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); @@ -623,7 +672,7 @@ public void setText(CharSequence text, BufferType type) { int index = tagsString.toString().indexOf('\n'); if (index >= 0) { tagsString.replace(index, index + 1, " "); - tagsString.setSpan(new ForegroundColorSpan(getThemedColor(Theme.key_windowBackgroundWhiteRedText4)), 0, index, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + tagsString.setSpan(new ForegroundColorSpan(getThemedColor(Theme.key_text_RedRegular)), 0, index, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } TypefaceSpan[] spans = tagsString.getSpans(0, tagsString.length(), TypefaceSpan.class); for (int i = 0; i < spans.length; ++i) { @@ -661,7 +710,20 @@ public void updateDrawState(@NonNull TextPaint ds) { addView(text1View, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP)); addView(text2View, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP)); - text1View.setText(AndroidUtilities.replaceTags(LocaleController.getString("UsernameHelp", R.string.UsernameHelp))); + if (botId != 0) { + String str = LocaleController.getString(R.string.BotUsernameHelp); + SpannableStringBuilder text = new SpannableStringBuilder(str); + int index1 = str.indexOf('*'); + int index2 = str.lastIndexOf('*'); + if (index1 != -1 && index2 != -1 && index1 != index2) { + text.replace(index2, index2 + 1, ""); + text.replace(index1, index1 + 1, ""); + text.setSpan(new URLSpanNoUnderline("https://fragment.com"), index1, index2 - 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + text1View.setText(text); + } else { + text1View.setText(AndroidUtilities.replaceTags(LocaleController.getString(R.string.UsernameHelp))); + } } private Integer height; @@ -713,7 +775,7 @@ public InputCell(Context context) { field.setHintTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteHintText)); field.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); field.setBackgroundDrawable(null); -// field.setLineColors(getThemedColor(Theme.key_windowBackgroundWhiteInputField), getThemedColor(Theme.key_windowBackgroundWhiteInputFieldActivated), getThemedColor(Theme.key_windowBackgroundWhiteRedText3)); +// field.setLineColors(getThemedColor(Theme.key_windowBackgroundWhiteInputField), getThemedColor(Theme.key_windowBackgroundWhiteInputFieldActivated), getThemedColor(Theme.key_text_RedRegular)); field.setMaxLines(1); field.setLines(1); field.setPadding(0, 0, 0, 0); @@ -777,6 +839,9 @@ private void updateUsernameCell(String was) { } } }); + if (botId != 0) { + field.setEnabled(false); + } tme = new TextView(getContext()); tme.setMaxLines(1); tme.setLines(1); @@ -791,6 +856,11 @@ private void updateUsernameCell(String was) { content.addView(field, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, 1, Gravity.CENTER_VERTICAL, 0, 15, 21, 15)); addView(content, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP)); setBackgroundColor(getThemedColor(Theme.key_windowBackgroundWhite)); + + if (botId != 0) { + field.setAlpha(0.6f); + tme.setAlpha(0.6f); + } } @Override @@ -886,6 +956,7 @@ public void onAnimationEnd(Animator animation) { public TLRPC.TL_username currentUsername; private boolean useDivider; + private long botId; private AnimatedFloat useDividerAlpha = new AnimatedFloat(this, 300, CubicBezierInterpolator.DEFAULT); private float activeViewTextColorT; @@ -896,8 +967,13 @@ public void onAnimationEnd(Animator animation) { private AnimatedFloat activeFloat = new AnimatedFloat(this, 400, CubicBezierInterpolator.EASE_OUT_QUINT); public void set(TLRPC.TL_username username, boolean useDivider, boolean animated) { + set(username, useDivider, animated, 0); + } + + public void set(TLRPC.TL_username username, boolean useDivider, boolean animated, long botId) { currentUsername = username; this.useDivider = useDivider; + this.botId = botId; invalidate(); if (currentUsername == null) { active = false; @@ -906,7 +982,7 @@ public void set(TLRPC.TL_username username, boolean useDivider, boolean animated } active = username.active; - editable = username.editable; + editable = botId == 0 && username.editable; updateUsername(username.username); if (isProfile) { activeView.setText(editable ? LocaleController.getString("UsernameProfileLinkEditable", R.string.UsernameProfileLinkEditable) : (active ? LocaleController.getString("UsernameProfileLinkActive", R.string.UsernameProfileLinkActive) : LocaleController.getString("UsernameProfileLinkInactive", R.string.UsernameProfileLinkInactive)), animated, !active); @@ -971,7 +1047,7 @@ private void animateValueTextColor(boolean active, boolean animated) { public void update() { if (currentUsername != null) { - set(currentUsername, useDivider, true); + set(currentUsername, useDivider, true, botId); } } @@ -1121,8 +1197,8 @@ private boolean checkUserName(String name, boolean alert) { if (name.startsWith("_") || name.endsWith("_")) { if (statusTextView != null) { statusTextView.setText(LocaleController.getString("UsernameInvalid", R.string.UsernameInvalid)); - statusTextView.setTag(Theme.key_windowBackgroundWhiteRedText4); - statusTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText4)); + statusTextView.setTag(Theme.key_text_RedRegular); + statusTextView.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); if (helpCell != null) { helpCell.update(); } @@ -1137,8 +1213,8 @@ private boolean checkUserName(String name, boolean alert) { } else { if (statusTextView != null) { statusTextView.setText(LocaleController.getString("UsernameInvalidStartNumber", R.string.UsernameInvalidStartNumber)); - statusTextView.setTag(Theme.key_windowBackgroundWhiteRedText4); - statusTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText4)); + statusTextView.setTag(Theme.key_text_RedRegular); + statusTextView.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); if (helpCell != null) { helpCell.update(); } @@ -1152,8 +1228,8 @@ private boolean checkUserName(String name, boolean alert) { } else { if (statusTextView != null) { statusTextView.setText(LocaleController.getString("UsernameInvalid", R.string.UsernameInvalid)); - statusTextView.setTag(Theme.key_windowBackgroundWhiteRedText4); - statusTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText4)); + statusTextView.setTag(Theme.key_text_RedRegular); + statusTextView.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); if (helpCell != null) { helpCell.update(); } @@ -1169,8 +1245,8 @@ private boolean checkUserName(String name, boolean alert) { } else { if (statusTextView != null) { statusTextView.setText(LocaleController.getString("UsernameInvalidShort", R.string.UsernameInvalidShort)); - statusTextView.setTag(Theme.key_windowBackgroundWhiteRedText4); - statusTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText4)); + statusTextView.setTag(Theme.key_text_RedRegular); + statusTextView.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); if (helpCell != null) { helpCell.update(); } @@ -1184,8 +1260,8 @@ private boolean checkUserName(String name, boolean alert) { } else { if (statusTextView != null) { statusTextView.setText(LocaleController.getString("UsernameInvalidLong", R.string.UsernameInvalidLong)); - statusTextView.setTag(Theme.key_windowBackgroundWhiteRedText4); - statusTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText4)); + statusTextView.setTag(Theme.key_text_RedRegular); + statusTextView.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); if (helpCell != null) { helpCell.update(); } @@ -1195,7 +1271,7 @@ private boolean checkUserName(String name, boolean alert) { } if (!alert) { - String currentName = UserConfig.getInstance(currentAccount).getCurrentUser().username; + String currentName = getUser().username; if (currentName == null) { currentName = ""; } @@ -1241,8 +1317,8 @@ private boolean checkUserName(String name, boolean alert) { if (statusTextView != null) { if (error != null && "USERNAME_INVALID".equals(error.text) && req.username.length() == 4) { statusTextView.setText(LocaleController.getString("UsernameInvalidShort", R.string.UsernameInvalidShort)); - statusTextView.setTag(Theme.key_windowBackgroundWhiteRedText4); - statusTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText4)); + statusTextView.setTag(Theme.key_text_RedRegular); + statusTextView.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); } else if (error != null && "USERNAME_PURCHASE_AVAILABLE".equals(error.text)) { if (req.username.length() == 4) { statusTextView.setText(LocaleController.getString("UsernameInvalidShortPurchase", R.string.UsernameInvalidShortPurchase)); @@ -1253,8 +1329,8 @@ private boolean checkUserName(String name, boolean alert) { statusTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText8)); } else { statusTextView.setText(LocaleController.getString("UsernameInUse", R.string.UsernameInUse)); - statusTextView.setTag(Theme.key_windowBackgroundWhiteRedText4); - statusTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText4)); + statusTextView.setTag(Theme.key_text_RedRegular); + statusTextView.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); } if (helpCell != null) { helpCell.update(); @@ -1271,6 +1347,10 @@ private boolean checkUserName(String name, boolean alert) { } private void saveName() { + if (botId != 0) { + finishFragment(); + return; + } if (username.startsWith("@")) { username = username.substring(1); } @@ -1278,7 +1358,7 @@ private void saveName() { shakeIfOff(); return; } - TLRPC.User user = UserConfig.getInstance(currentAccount).getCurrentUser(); + TLRPC.User user = getUser(); if (getParentActivity() == null || user == null) { return; } @@ -1392,7 +1472,7 @@ public ArrayList getThemeDescriptions() { // themeDescriptions.add(new ThemeDescription(helpTextView, ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteGrayText8)); // -// themeDescriptions.add(new ThemeDescription(checkTextView, ThemeDescription.FLAG_TEXTCOLOR | ThemeDescription.FLAG_CHECKTAG, null, null, null, null, Theme.key_windowBackgroundWhiteRedText4)); +// themeDescriptions.add(new ThemeDescription(checkTextView, ThemeDescription.FLAG_TEXTCOLOR | ThemeDescription.FLAG_CHECKTAG, null, null, null, null, Theme.key_text_RedRegular)); // themeDescriptions.add(new ThemeDescription(checkTextView, ThemeDescription.FLAG_TEXTCOLOR | ThemeDescription.FLAG_CHECKTAG, null, null, null, null, Theme.key_windowBackgroundWhiteGreenText)); // themeDescriptions.add(new ThemeDescription(checkTextView, ThemeDescription.FLAG_TEXTCOLOR | ThemeDescription.FLAG_CHECKTAG, null, null, null, null, Theme.key_windowBackgroundWhiteGrayText8)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChannelAdminLogActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChannelAdminLogActivity.java index 3f38104f25..cc4a0161d0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChannelAdminLogActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChannelAdminLogActivity.java @@ -68,6 +68,7 @@ import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.BuildVars; import org.telegram.messenger.ChatObject; @@ -208,7 +209,14 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio private MessageObject scrollToMessage; - private int allowAnimationIndex; + private AnimationNotificationsLocker notificationsLocker = new AnimationNotificationsLocker(new int[]{ + NotificationCenter.chatInfoDidLoad, + NotificationCenter.dialogsNeedReload, + NotificationCenter.closeChats, + NotificationCenter.messagesDidLoad, + NotificationCenter.botKeyboardDidLoad + /*, NotificationCenter.botInfoDidLoad*/ + }); private HashMap invitesCache = new HashMap<>(); private HashMap usersMap; @@ -305,7 +313,7 @@ public void onFragmentDestroy() { NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.messagePlayingDidReset); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.messagePlayingProgressDidChanged); NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.didSetNewWallpapper); - getNotificationCenter().onAnimationFinish(allowAnimationIndex); + notificationsLocker.unlock(); } private void updateEmptyPlaceholder() { @@ -2163,8 +2171,7 @@ private void updateMessagesVisiblePart() { @Override public void onTransitionAnimationStart(boolean isOpen, boolean backward) { if (isOpen) { - allowAnimationIndex = getNotificationCenter().setAnimationInProgress(allowAnimationIndex, new int[]{NotificationCenter.chatInfoDidLoad, NotificationCenter.dialogsNeedReload, - NotificationCenter.closeChats, NotificationCenter.messagesDidLoad, NotificationCenter.botKeyboardDidLoad/*, NotificationCenter.botInfoDidLoad*/}); + notificationsLocker.lock(); openAnimationEnded = false; } } @@ -2172,7 +2179,7 @@ public void onTransitionAnimationStart(boolean isOpen, boolean backward) { @Override public void onTransitionAnimationEnd(boolean isOpen, boolean backward) { if (isOpen) { - getNotificationCenter().onAnimationFinish(allowAnimationIndex); + notificationsLocker.unlock(); openAnimationEnded = true; } } @@ -3123,7 +3130,7 @@ public ArrayList getThemeDescriptions() { themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, Theme.chat_msgInCallDrawable, null, Theme.key_chat_inInstant)); themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, Theme.chat_msgInCallSelectedDrawable, null, Theme.key_chat_inInstantSelected)); themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgCallUpGreenDrawable}, null, Theme.key_chat_outGreenCall)); - themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgCallDownRedDrawable}, null, Theme.key_chat_inRedCall)); + themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgCallDownRedDrawable}, null, Theme.key_fill_RedNormal)); themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgCallDownGreenDrawable}, null, Theme.key_chat_inGreenCall)); themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, Theme.chat_msgErrorPaint, null, null, Theme.key_chat_sentError)); themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgErrorDrawable}, null, Theme.key_chat_sentErrorIcon)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChannelCreateActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChannelCreateActivity.java index 16838af0f1..59206c4a6e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChannelCreateActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChannelCreateActivity.java @@ -667,7 +667,7 @@ public void setAlpha(float alpha) { descriptionTextView.setHintTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteHintText)); descriptionTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); descriptionTextView.setBackgroundDrawable(null); - descriptionTextView.setLineColors(getThemedColor(Theme.key_windowBackgroundWhiteInputField), getThemedColor(Theme.key_windowBackgroundWhiteInputFieldActivated), getThemedColor(Theme.key_windowBackgroundWhiteRedText3)); + descriptionTextView.setLineColors(getThemedColor(Theme.key_windowBackgroundWhiteInputField), getThemedColor(Theme.key_windowBackgroundWhiteInputFieldActivated), getThemedColor(Theme.key_text_RedRegular)); descriptionTextView.setPadding(0, 0, 0, AndroidUtilities.dp(6)); descriptionTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); descriptionTextView.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES | InputType.TYPE_TEXT_FLAG_MULTI_LINE | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT); @@ -862,7 +862,7 @@ public void setText(CharSequence text, BufferType type) { int index = tagsString.toString().indexOf('\n'); if (index >= 0) { tagsString.replace(index, index + 1, " "); - tagsString.setSpan(new ForegroundColorSpan(getThemedColor(Theme.key_windowBackgroundWhiteRedText4)), 0, index, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + tagsString.setSpan(new ForegroundColorSpan(getThemedColor(Theme.key_text_RedRegular)), 0, index, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } TypefaceSpan[] spans = tagsString.getSpans(0, tagsString.length(), TypefaceSpan.class); final String username = descriptionTextView == null || descriptionTextView.getText() == null ? "" : descriptionTextView.getText().toString(); @@ -900,7 +900,7 @@ public void updateDrawState(@NonNull TextPaint ds) { linkContainer.addView(checkTextView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT, 18, 3, 18, 7)); typeInfoCell = new TextInfoPrivacyCell(context); - typeInfoCell.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + typeInfoCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); linearLayout.addView(typeInfoCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); loadingAdminedCell = new LoadingCell(context); @@ -912,7 +912,7 @@ public void updateDrawState(@NonNull TextPaint ds) { linearLayout.addView(adminnedChannelsLayout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); adminedInfoCell = new TextInfoPrivacyCell(context); - adminedInfoCell.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + adminedInfoCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); linearLayout.addView(adminedInfoCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); updatePrivatePublic(); @@ -954,17 +954,17 @@ private void updatePrivatePublic() { } if (!isPrivate && !canCreatePublic) { typeInfoCell.setText(LocaleController.getString("ChangePublicLimitReached", R.string.ChangePublicLimitReached)); - typeInfoCell.setTag(Theme.key_windowBackgroundWhiteRedText4); - typeInfoCell.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText4)); + typeInfoCell.setTag(Theme.key_text_RedRegular); + typeInfoCell.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); linkContainer.setVisibility(View.GONE); sectionCell.setVisibility(View.GONE); if (loadingAdminedChannels) { loadingAdminedCell.setVisibility(View.VISIBLE); adminnedChannelsLayout.setVisibility(View.GONE); - typeInfoCell.setBackgroundDrawable(Theme.getThemedDrawable(typeInfoCell.getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + typeInfoCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(typeInfoCell.getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); adminedInfoCell.setVisibility(View.GONE); } else { - typeInfoCell.setBackgroundDrawable(Theme.getThemedDrawable(typeInfoCell.getContext(), R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + typeInfoCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(typeInfoCell.getContext(), R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); loadingAdminedCell.setVisibility(View.GONE); adminnedChannelsLayout.setVisibility(View.VISIBLE); adminedInfoCell.setVisibility(View.VISIBLE); @@ -975,7 +975,7 @@ private void updatePrivatePublic() { sectionCell.setVisibility(View.VISIBLE); adminedInfoCell.setVisibility(View.GONE); adminnedChannelsLayout.setVisibility(View.GONE); - typeInfoCell.setBackgroundDrawable(Theme.getThemedDrawable(typeInfoCell.getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + typeInfoCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(typeInfoCell.getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); linkContainer.setVisibility(View.VISIBLE); loadingAdminedCell.setVisibility(View.GONE); if (isGroup) { @@ -1274,36 +1274,36 @@ private boolean checkUserName(final String name) { if (name != null) { if (name.startsWith("_") || name.endsWith("_")) { checkTextView.setText(LocaleController.getString("LinkInvalid", R.string.LinkInvalid)); - checkTextView.setTag(Theme.key_windowBackgroundWhiteRedText4); - checkTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText4)); + checkTextView.setTag(Theme.key_text_RedRegular); + checkTextView.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); return false; } for (int a = 0; a < name.length(); a++) { char ch = name.charAt(a); if (a == 0 && ch >= '0' && ch <= '9') { checkTextView.setText(LocaleController.getString("LinkInvalidStartNumber", R.string.LinkInvalidStartNumber)); - checkTextView.setTag(Theme.key_windowBackgroundWhiteRedText4); - checkTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText4)); + checkTextView.setTag(Theme.key_text_RedRegular); + checkTextView.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); return false; } if (!(ch >= '0' && ch <= '9' || ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch == '_')) { checkTextView.setText(LocaleController.getString("LinkInvalid", R.string.LinkInvalid)); - checkTextView.setTag(Theme.key_windowBackgroundWhiteRedText4); - checkTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText4)); + checkTextView.setTag(Theme.key_text_RedRegular); + checkTextView.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); return false; } } } if (name == null || name.length() < 4) { checkTextView.setText(LocaleController.getString("LinkInvalidShort", R.string.LinkInvalidShort)); - checkTextView.setTag(Theme.key_windowBackgroundWhiteRedText4); - checkTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText4)); + checkTextView.setTag(Theme.key_text_RedRegular); + checkTextView.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); return false; } if (name.length() > 32) { checkTextView.setText(LocaleController.getString("LinkInvalidLong", R.string.LinkInvalidLong)); - checkTextView.setTag(Theme.key_windowBackgroundWhiteRedText4); - checkTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText4)); + checkTextView.setTag(Theme.key_text_RedRegular); + checkTextView.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); return false; } @@ -1326,7 +1326,7 @@ private boolean checkUserName(final String name) { } else { if (error != null && "USERNAME_INVALID".equals(error.text) && req.username.length() == 4) { checkTextView.setText(LocaleController.getString("UsernameInvalidShort", R.string.UsernameInvalidShort)); - checkTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText4)); + checkTextView.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); } else if (error != null && "USERNAME_PURCHASE_AVAILABLE".equals(error.text)) { if (req.username.length() == 4) { checkTextView.setText(LocaleController.getString("UsernameInvalidShortPurchase", R.string.UsernameInvalidShortPurchase)); @@ -1335,11 +1335,11 @@ private boolean checkUserName(final String name) { } checkTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText8)); } else if (error != null && "CHANNELS_ADMIN_PUBLIC_TOO_MUCH".equals(error.text)) { - checkTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText4)); + checkTextView.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); canCreatePublic = false; showPremiumIncreaseLimitDialog(); } else { - checkTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText4)); + checkTextView.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); checkTextView.setText(LocaleController.getString("LinkInUse", R.string.LinkInUse)); } lastNameAvailable = false; @@ -1426,13 +1426,13 @@ public ArrayList getThemeDescriptions() { themeDescriptions.add(new ThemeDescription(headerCell2, 0, new Class[]{HeaderCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueHeader)); themeDescriptions.add(new ThemeDescription(editText, ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); themeDescriptions.add(new ThemeDescription(editText, ThemeDescription.FLAG_HINTTEXTCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteHintText)); - themeDescriptions.add(new ThemeDescription(checkTextView, ThemeDescription.FLAG_TEXTCOLOR | ThemeDescription.FLAG_CHECKTAG, null, null, null, null, Theme.key_windowBackgroundWhiteRedText4)); + themeDescriptions.add(new ThemeDescription(checkTextView, ThemeDescription.FLAG_TEXTCOLOR | ThemeDescription.FLAG_CHECKTAG, null, null, null, null, Theme.key_text_RedRegular)); themeDescriptions.add(new ThemeDescription(checkTextView, ThemeDescription.FLAG_TEXTCOLOR | ThemeDescription.FLAG_CHECKTAG, null, null, null, null, Theme.key_windowBackgroundWhiteGrayText8)); themeDescriptions.add(new ThemeDescription(checkTextView, ThemeDescription.FLAG_TEXTCOLOR | ThemeDescription.FLAG_CHECKTAG, null, null, null, null, Theme.key_windowBackgroundWhiteGreenText)); themeDescriptions.add(new ThemeDescription(typeInfoCell, ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{TextInfoPrivacyCell.class}, null, null, null, Theme.key_windowBackgroundGrayShadow)); themeDescriptions.add(new ThemeDescription(typeInfoCell, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText4)); - themeDescriptions.add(new ThemeDescription(typeInfoCell, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteRedText4)); + themeDescriptions.add(new ThemeDescription(typeInfoCell, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_text_RedRegular)); themeDescriptions.add(new ThemeDescription(adminedInfoCell, ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{TextInfoPrivacyCell.class}, null, null, null, Theme.key_windowBackgroundGrayShadow)); themeDescriptions.add(new ThemeDescription(adminnedChannelsLayout, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_windowBackgroundWhite)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Charts/data/ChartData.java b/TMessagesProj/src/main/java/org/telegram/ui/Charts/data/ChartData.java index 2f2d72d7d7..c660dbb822 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Charts/data/ChartData.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Charts/data/ChartData.java @@ -5,11 +5,11 @@ import androidx.core.graphics.ColorUtils; -import org.telegram.messenger.SegmentTree; - import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import org.telegram.messenger.SegmentTree; +import org.telegram.ui.ActionBar.ThemeColors; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -79,7 +79,7 @@ public ChartData(JSONObject jsonObject) throws JSONException { if (matcher.matches()) { String key = matcher.group(1); if (!TextUtils.isEmpty(key)) { - line.colorKey = "statisticChartLine_" + matcher.group(1).toLowerCase(); + line.colorKey = ThemeColors.stringKeyToInt("statisticChartLine_" + matcher.group(1).toLowerCase()); } line.color = Color.parseColor(matcher.group(2)); @@ -233,7 +233,7 @@ public class Line { public String name; public int maxValue = 0; public int minValue = Integer.MAX_VALUE; - public String colorKey; + public int colorKey; public int color = Color.BLACK; public int colorDark = Color.WHITE; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Charts/view_data/LegendSignatureView.java b/TMessagesProj/src/main/java/org/telegram/ui/Charts/view_data/LegendSignatureView.java index c82918bfb2..c1091e04c6 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Charts/view_data/LegendSignatureView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Charts/view_data/LegendSignatureView.java @@ -168,7 +168,7 @@ public void setData(int index, long date, ArrayList lines, boolean h.root.setVisibility(View.VISIBLE); h.value.setText(formatWholeNumber(l.y[index])); h.signature.setText(l.name); - if (l.colorKey != null && Theme.hasThemeKey(l.colorKey)) { + if (l.colorKey >= 0 && Theme.hasThemeKey(l.colorKey)) { h.value.setTextColor(Theme.getColor(l.colorKey)); } else { h.value.setTextColor(Theme.getCurrentTheme().isDark() ? l.colorDark : l.color); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Charts/view_data/LineViewData.java b/TMessagesProj/src/main/java/org/telegram/ui/Charts/view_data/LineViewData.java index 92852d5481..7c81692e8b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Charts/view_data/LineViewData.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Charts/view_data/LineViewData.java @@ -60,7 +60,7 @@ public LineViewData(ChartData.Line line) { } public void updateColors() { - if (line.colorKey != null && Theme.hasThemeKey(line.colorKey)) { + if (line.colorKey >= 0 && Theme.hasThemeKey(line.colorKey)) { lineColor = Theme.getColor(line.colorKey); } else { int color = Theme.getColor(Theme.key_windowBackgroundWhite); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java index d9bd36e7c6..33af5b0438 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java @@ -48,6 +48,7 @@ import android.graphics.Region; import android.graphics.Shader; import android.graphics.Typeface; +import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; @@ -110,7 +111,6 @@ import androidx.dynamicanimation.animation.FloatValueHolder; import androidx.dynamicanimation.animation.SpringAnimation; import androidx.dynamicanimation.animation.SpringForce; -import androidx.exifinterface.media.ExifInterface; import androidx.recyclerview.widget.ChatListItemAnimator; import androidx.recyclerview.widget.GridLayoutManagerFixed; import androidx.recyclerview.widget.LinearLayoutManager; @@ -131,6 +131,7 @@ import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.BotWebViewVibrationEffect; import org.telegram.messenger.BuildVars; +import org.telegram.messenger.ChatMessageSharedResources; import org.telegram.messenger.ChatMessagesMetadataController; import org.telegram.messenger.ChatObject; import org.telegram.messenger.ChatThemeController; @@ -309,6 +310,8 @@ import org.telegram.ui.Components.voip.CellFlickerDrawable; import org.telegram.ui.Components.voip.VoIPHelper; import org.telegram.ui.Delegates.ChatActivityMemberRequestsDelegate; +import org.telegram.ui.Stories.StoriesListPlaceProvider; +import org.telegram.ui.Stories.StoriesUtilities; import java.io.BufferedWriter; import java.io.ByteArrayOutputStream; @@ -316,6 +319,7 @@ import java.io.FileOutputStream; import java.io.FileWriter; import java.io.InputStream; +import java.net.IDN; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Arrays; @@ -357,7 +361,7 @@ import xyz.nextalone.nagram.helper.MessageHelper; @SuppressWarnings("unchecked") -public class ChatActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate, DialogsActivity.DialogsActivityDelegate, LocationActivity.LocationActivityDelegate, ChatAttachAlertDocumentLayout.DocumentSelectActivityDelegate, ChatActivityInterface, FloatingDebugProvider { +public class ChatActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate, DialogsActivity.DialogsActivityDelegate, LocationActivity.LocationActivityDelegate, ChatAttachAlertDocumentLayout.DocumentSelectActivityDelegate, ChatActivityInterface, FloatingDebugProvider, InstantCameraView.Delegate { private final static boolean PULL_DOWN_BACK_FRAGMENT = false; private final static boolean DISABLE_PROGRESS_VIEW = true; private final static int SKELETON_DISAPPEAR_MS = 200; @@ -809,7 +813,12 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private Paint skeletonServicePaint = new Paint(Paint.ANTI_ALIAS_FLAG); private ColorMatrix skeletonColorMatrix = new ColorMatrix(); private Theme.MessageDrawable.PathDrawParams skeletonBackgroundCacheParams = new Theme.MessageDrawable.PathDrawParams(); - private Theme.MessageDrawable skeletonBackgroundDrawable = new Theme.MessageDrawable(Theme.MessageDrawable.TYPE_TEXT, false, false, this::getThemedColor); + private Theme.MessageDrawable skeletonBackgroundDrawable = new Theme.MessageDrawable(Theme.MessageDrawable.TYPE_TEXT, false, false, new Theme.ResourcesProvider() { + @Override + public int getColor(int key) { + return getThemedColor(key); + } + }); private long skeletonLastUpdateTime; private int skeletonGradientWidth; private int skeletonTotalTranslation; @@ -822,6 +831,15 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private Matrix skeletonOutlineMatrix = new Matrix(); private LinearGradient skeletonOutlineGradient; private boolean chatListViewAttached; + public boolean forceDisallowApplyWallpeper; + public boolean forceDisallowRedrawThemeDescriptions; + private boolean waitingForGetDifference; + private int initialMessagesSize; + private boolean loadInfo; + private boolean historyPreloaded; + private int migrated_to; + private boolean firstMessagesLoaded; + private Runnable closeInstantCameraAnimation; { skeletonOutlinePaint.setStyle(Paint.Style.STROKE); @@ -944,6 +962,7 @@ public void run() { private TextSelectionHint textSelectionHint; private boolean textSelectionHintWasShowed; private float lastTouchY; + ContentPreviewViewer.ContentPreviewViewerDelegate contentPreviewViewerDelegate; private ChatMessageCell dummyMessageCell; private FireworksOverlay fireworksOverlay; @@ -1114,10 +1133,21 @@ public boolean needPostpone(int id, int currentAccount, Object[] args) { public float drawingChatLisViewYoffset; public int blurredViewTopOffset; public int blurredViewBottomOffset; + public ChatMessageSharedResources sharedResources; private ValueAnimator searchExpandAnimator; private float searchExpandProgress; + public static ChatActivity of(long dialogId) { + Bundle bundle = new Bundle(); + if (dialogId >= 0) { + bundle.putLong("user_id", dialogId); + } else { + bundle.putLong("chat_id", -dialogId); + } + return new ChatActivity(bundle); + } + public void deleteHistory(int dateSelectedStart, int dateSelectedEnd, boolean forAll) { chatAdapter.frozenMessages.clear(); for (int i = 0; i < messages.size(); i++) { @@ -1249,6 +1279,16 @@ public boolean allowSendPhotos() { } } + public ThemeDelegate createThemeDelegate() { + return new ThemeDelegate(); + } + + public void updateMessages(ArrayList messageObjects, boolean replace) { + for (int i = 0; i < messageObjects.size(); i++) { + chatAdapter.updateRowWithMessageObject(messageObjects.get(i), false, replace); + } + } + private interface ChatActivityDelegate { default void openReplyMessage(int mid) { @@ -1392,14 +1432,19 @@ public void sendButtonPressed(int index, VideoEditedInfo videoEditedInfo, boolea RecyclerListView.OnItemLongClickListenerExtended onItemLongClickListener = new RecyclerListView.OnItemLongClickListenerExtended() { @Override public boolean onItemClick(View view, int position, float x, float y) { - if (textSelectionHelper.isTryingSelect() || textSelectionHelper.isSelectionMode() || inPreviewMode) { + if (textSelectionHelper.isTryingSelect() || textSelectionHelper.isInSelectionMode() || inPreviewMode) { return false; } if((scrimPopupWindow != null && NaConfig.INSTANCE.getDoubleTapAction().Int()==DoubleTap.DOUBLE_TAP_ACTION_SHOW_REACTIONS)) return false; wasManualScroll = true; boolean result = true; - if (!actionBar.isActionModeShowed() && (reportType < 0 || (view instanceof ChatActionCell && (((ChatActionCell) view).getMessageObject().messageOwner.action instanceof TLRPC.TL_messageActionSetMessagesTTL) || ((view instanceof ChatActionCell) && ((ChatActionCell) view).getMessageObject().type == MessageObject.TYPE_SUGGEST_PHOTO)))) { + boolean showMenu = true; + if (view instanceof ChatActionCell) { + ChatActionCell actionCell = (ChatActionCell) view; + showMenu = actionCell.getMessageObject().messageOwner.action instanceof TLRPC.TL_messageActionSetMessagesTTL || actionCell.getMessageObject().type == MessageObject.TYPE_SUGGEST_PHOTO || actionCell.getMessageObject().isWallpaperAction(); + } + if (!actionBar.isActionModeShowed() && (reportType < 0 || showMenu)) { result = createMenu(view, false, true, x, y); } else { boolean outside = false; @@ -1537,6 +1582,13 @@ public void onItemClick(View view, int position, float x, float y) { presentFragment(calendarActivity); return; } + if (view instanceof ChatActionCell && ((ChatActionCell) view).getMessageObject() != null && ((ChatActionCell) view).getMessageObject().messageOwner.action instanceof TLRPC.TL_messageActionSetSameChatWallPaper) { + int messageId = ((ChatActionCell) view).getMessageObject().getReplyMsgId(); + AndroidUtilities.runOnUIThread(() -> { + scrollToMessageId(messageId, 0, true, 0, true, 0); + }, 16); + return; + } if (actionBar.isActionModeShowed() || reportType >= 0) { boolean outside = false; if (view instanceof ChatMessageCell) { @@ -1632,8 +1684,23 @@ public void onDoubleTap(View view, int position, float x, float y) { if (isSecretChat() || isInScheduleMode()) { return; } - createMenu(view, true, false, x, y, true, true); - return; + ChatMessageCell cell = (ChatMessageCell) view; + MessageObject primaryMessage = cell.getPrimaryMessageObject(); + if (primaryMessage.isSecretMedia() || primaryMessage.isExpiredStory()) { + return; + } + ReactionsEffectOverlay.removeCurrent(false); + String reactionString = getMediaDataController().getDoubleTapReaction(); + if (reactionString.startsWith("animated_")) { + boolean available = dialog_id >= 0; + if (!available && chatInfo != null) { + available = ChatObject.reactionIsAvailable(chatInfo, reactionString); + } + if (!available) { + return; + } + selectReaction(primaryMessage, null, null, x, y, ReactionsLayoutInBubble.VisibleReaction.fromEmojicon(reactionString), true, false, false); + } } else if (NaConfig.INSTANCE.getDoubleTapAction().Int() == DoubleTap.DOUBLE_TAP_ACTION_SEND_REACTIONS) { if (isSecretChat() || isInScheduleMode()) { return; @@ -2258,11 +2325,11 @@ public boolean onFragmentCreate() { } reportType = arguments.getInt("report", -1); pulled = arguments.getBoolean("pulled", false); - boolean historyPreloaded = arguments.getBoolean("historyPreloaded", false); + historyPreloaded = arguments.getBoolean("historyPreloaded", false); if (highlightMessageId != 0 && highlightMessageId != Integer.MAX_VALUE) { startLoadFromMessageId = highlightMessageId; } - int migrated_to = arguments.getInt("migrated_to", 0); + migrated_to = arguments.getInt("migrated_to", 0); scrollToTopOnResume = arguments.getBoolean("scrollToTopOnResume", false); needRemovePreviousSameChatActivity = arguments.getBoolean("need_remove_previous_same_chat_activity", true); noForwardQuote = arguments.getBoolean("forward_noquote", false); @@ -2290,7 +2357,17 @@ public boolean onFragmentCreate() { } dialog_id = -chatId; if (ChatObject.isChannel(currentChat)) { - getMessagesController().startShortPoll(currentChat, classGuid, false); + if (ChatObject.isNotInChat(currentChat)) { + waitingForGetDifference = true; + getMessagesController().startShortPoll(currentChat, classGuid, false, isGettingDifference -> { + waitingForGetDifference = isGettingDifference; + if (!waitingForGetDifference) { + firstLoadMessages(); + } + }); + } else { + getMessagesController().startShortPoll(currentChat, classGuid, false); + } } } else if (userId != 0) { currentUser = getMessagesController().getUser(userId); @@ -2470,6 +2547,7 @@ public boolean onFragmentCreate() { getNotificationCenter().addObserver(this, NotificationCenter.dialogIsTranslatable); getNotificationCenter().addObserver(this, NotificationCenter.messageTranslated); getNotificationCenter().addObserver(this, NotificationCenter.messageTranslating); + getNotificationCenter().addObserver(this, NotificationCenter.onReceivedChannelDifference); super.onFragmentCreate(); @@ -2520,7 +2598,7 @@ public boolean onFragmentCreate() { } } - boolean loadInfo = false; + loadInfo = false; if (currentChat != null) { chatInfo = getMessagesController().getChatFull(currentChat.id); groupCall = getMessagesController().getGroupCall(currentChat.id, true); @@ -2551,39 +2629,19 @@ public boolean onFragmentCreate() { checkDispatchHideSkeletons(false); } if (chatMode != MODE_PINNED && !forceHistoryEmpty) { - waitingForLoad.add(lastLoadIndex); - int initialMessagesSize; if (SharedConfig.deviceIsHigh()) { initialMessagesSize = (isThreadChat() && !isTopic) ? 30 : 25; } else { initialMessagesSize = (isThreadChat() && !isTopic) ? 20 : 15; } - if (startLoadFromDate != 0) { - getMessagesController().loadMessages(dialog_id, mergeDialogId, false, 30, 0, startLoadFromDate, true, 0, classGuid, 4, 0, chatMode, threadMessageId, replyMaxReadId, lastLoadIndex++, isTopic); - } else if (startLoadFromMessageId != 0 && (!isThreadChat() || startLoadFromMessageId == highlightMessageId || isTopic)) { - startLoadFromMessageIdSaved = startLoadFromMessageId; - if (migrated_to != 0) { - mergeDialogId = migrated_to; - getMessagesController().loadMessages(mergeDialogId, 0, loadInfo, initialMessagesSize, startLoadFromMessageId, 0, true, 0, classGuid, 3, 0, chatMode, threadMessageId, replyMaxReadId, lastLoadIndex++, isTopic); - } else { - getMessagesController().loadMessages(dialog_id, mergeDialogId, loadInfo, initialMessagesSize, startLoadFromMessageId, 0, true, 0, classGuid, 3, 0, chatMode, threadMessageId, replyMaxReadId, lastLoadIndex++, isTopic); - } - } else { - if (historyPreloaded) { - lastLoadIndex++; - } else { - getMessagesController().loadMessages(dialog_id, mergeDialogId, loadInfo, initialMessagesSize, startLoadFromMessageId, 0, true, 0, classGuid, 2, 0, chatMode, threadMessageId, replyMaxReadId, lastLoadIndex++, isTopic); - } + if (!waitingForGetDifference) { + firstLoadMessages(); } } - if (chatMode == 0 && !isThreadChat()) { - waitingForLoad.add(lastLoadIndex); - getMessagesController().loadMessages(dialog_id, mergeDialogId, false, 1, 0, 0, true, 0, classGuid, 2, 0, MODE_SCHEDULED, threadMessageId, replyMaxReadId, lastLoadIndex++, isTopic); - } if (chatMode == 0) { if (userId != 0 && currentUser.bot) { - getMediaDataController().loadBotInfo(userId, userId, true, classGuid); + AndroidUtilities.runOnUIThread(()-> getMediaDataController().loadBotInfo(userId, userId, true, classGuid)); } else if (chatInfo instanceof TLRPC.TL_chatFull) { for (int a = 0; a < chatInfo.participants.participants.size(); a++) { TLRPC.ChatParticipant participant = chatInfo.participants.participants.get(a); @@ -2608,7 +2666,7 @@ public boolean onFragmentCreate() { if (chatInfo != null && chatInfo.linked_chat_id != 0) { TLRPC.Chat chat = getMessagesController().getChat(chatInfo.linked_chat_id); if (chat != null && chat.megagroup) { - getMessagesController().startShortPoll(chat, classGuid, false); + getMessagesController().startShortPoll(chat, classGuid, false, null); } } @@ -2652,6 +2710,35 @@ public boolean onFragmentCreate() { return true; } + private void firstLoadMessages() { + if (firstMessagesLoaded) { + return; + } + firstMessagesLoaded = true; + waitingForLoad.add(lastLoadIndex); + if (startLoadFromDate != 0) { + getMessagesController().loadMessages(dialog_id, mergeDialogId, false, 30, 0, startLoadFromDate, true, 0, classGuid, 4, 0, chatMode, threadMessageId, replyMaxReadId, lastLoadIndex++, isTopic); + } else if (startLoadFromMessageId != 0 && (!isThreadChat() || startLoadFromMessageId == highlightMessageId || isTopic)) { + startLoadFromMessageIdSaved = startLoadFromMessageId; + if (migrated_to != 0) { + mergeDialogId = migrated_to; + getMessagesController().loadMessages(mergeDialogId, 0, loadInfo, initialMessagesSize, startLoadFromMessageId, 0, true, 0, classGuid, 3, 0, chatMode, threadMessageId, replyMaxReadId, lastLoadIndex++, isTopic); + } else { + getMessagesController().loadMessages(dialog_id, mergeDialogId, loadInfo, initialMessagesSize, startLoadFromMessageId, 0, true, 0, classGuid, 3, 0, chatMode, threadMessageId, replyMaxReadId, lastLoadIndex++, isTopic); + } + } else { + if (historyPreloaded) { + lastLoadIndex++; + } else { + getMessagesController().loadMessages(dialog_id, mergeDialogId, loadInfo, initialMessagesSize, startLoadFromMessageId, 0, true, 0, classGuid, 2, 0, chatMode, threadMessageId, replyMaxReadId, lastLoadIndex++, isTopic); + } + } + if (chatMode == 0 && !isThreadChat()) { + waitingForLoad.add(lastLoadIndex); + getMessagesController().loadMessages(dialog_id, mergeDialogId, false, 1, 0, 0, true, 0, classGuid, 2, 0, MODE_SCHEDULED, threadMessageId, replyMaxReadId, lastLoadIndex++, isTopic); + } + } + private void fillInviterId(boolean load) { if (currentChat == null || chatInfo == null || ChatObject.isNotInChat(currentChat) || currentChat.creator) { return; @@ -2737,6 +2824,7 @@ public void onFragmentDestroy() { if (chatAttachAlert != null) { chatAttachAlert.dismissInternal(); } + ContentPreviewViewer.getInstance().clearDelegate(contentPreviewViewerDelegate); getNotificationCenter().onAnimationFinish(transitionAnimationIndex); NotificationCenter.getGlobalInstance().onAnimationFinish(transitionAnimationGlobalIndex); getNotificationCenter().onAnimationFinish(scrollAnimationIndex); @@ -2825,6 +2913,7 @@ public void onFragmentDestroy() { getNotificationCenter().removeObserver(this, NotificationCenter.dialogIsTranslatable); getNotificationCenter().removeObserver(this, NotificationCenter.messageTranslated); getNotificationCenter().removeObserver(this, NotificationCenter.messageTranslating); + getNotificationCenter().removeObserver(this, NotificationCenter.onReceivedChannelDifference); if (currentEncryptedChat != null) { getNotificationCenter().removeObserver(this, NotificationCenter.didVerifyMessagesStickers); } @@ -2937,9 +3026,8 @@ public int getParentBottomPadding() { } @Override - protected int getThemedColor(String key) { - Integer color = chatActivity == null ? null : chatActivity.themeDelegate.getColor(key); - return color != null ? color : super.getThemedColor(key); + protected int getThemedColor(int key) { + return Theme.getColor(key, chatActivity.themeDelegate); } @Override @@ -2970,10 +3058,11 @@ public View createView(Context context) { actionBar.setSubtitleColor(getThemedColor(Theme.key_actionBarActionModeDefaultIcon)); } actionBarBackgroundPaint.setColor(getThemedColor(Theme.key_actionBarDefault)); + sharedResources = new ChatMessageSharedResources(context); if (chatMessageCellsCache.isEmpty()) { for (int a = 0; a < 15; a++) { - chatMessageCellsCache.add(new ChatMessageCell(context, true, themeDelegate)); + chatMessageCellsCache.add(new ChatMessageCell(context, true, sharedResources, themeDelegate)); } } for (int a = 1; a >= 0; a--) { @@ -3212,7 +3301,7 @@ public void run(boolean revoke) { } else if (id == to_the_message){ setScrollToMessage(); } else if (id == report) { - AlertsCreator.createReportAlert(getParentActivity(), dialog_id, 0, ChatActivity.this, themeDelegate, null); + AlertsCreator.createReportAlert(getParentActivity(), dialog_id, 0, 0, ChatActivity.this, themeDelegate, null); } else if (id == star) { for (int a = 0; a < 2; a++) { for (int b = 0; b < selectedMessagesCanStarIds[a].size(); b++) { @@ -3251,9 +3340,9 @@ public void run(boolean revoke) { }); headerItem.toggleSubMenu(attach, attachItem.createView()); } else if (id == bot_help) { - getSendMessagesHelper().sendMessage("/help", dialog_id, null, null, null, false, null, null, null, true, 0, null, false); + getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of("/help", dialog_id, null, null, null, false, null, null, null, true, 0, null, false)); } else if (id == bot_settings) { - getSendMessagesHelper().sendMessage("/settings", dialog_id, null, null, null, false, null, null, null, true, 0, null, false); + getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of("/settings", dialog_id, null, null, null, false, null, null, null, true, 0, null, false)); } else if (id == search) { openSearchWithText(null); } else if (id == translate) { @@ -3654,12 +3743,12 @@ public void toggleMute() { viewAsTopics = headerItem.lazilyAddSubItem(view_as_topics, R.drawable.msg_topics, LocaleController.getString("TopicViewAsTopics", R.string.TopicViewAsTopics)); } + if (themeDelegate.isThemeChangeAvailable()) { + headerItem.lazilyAddSubItem(change_colors, R.drawable.msg_colors, LocaleController.getString("SetWallpapers", R.string.SetWallpapers)); + } if (!isTopic) { clearHistoryItem = headerItem.lazilyAddSubItem(clear_history, R.drawable.msg_clear, LocaleController.getString("ClearHistory", R.string.ClearHistory)); } - if (themeDelegate.isThemeChangeAvailable()) { - headerItem.lazilyAddSubItem(change_colors, R.drawable.msg_colors, LocaleController.getString("ChangeColors", R.string.ChangeColors)); - } boolean addedSettings = false; if (!isTopic) { toTheBeginning = headerItem.lazilyAddSubItem(to_the_beginning, R.drawable.ic_upward, LocaleController.getString("ToTheBeginning", R.string.ToTheBeginning)); @@ -3687,7 +3776,7 @@ public void toggleMute() { } else if (currentUser != null && currentUser.bot) { headerItem.lazilyAddSubItem(bot_settings, R.drawable.msg_settings_old, LocaleController.getString("BotSettings", R.string.BotSettings)); addedSettings = true; - headerItem.lazilyAddSubItem(delete_chat, R.drawable.msg_block2, LocaleController.getString(R.string.DeleteAndBlock)).setColors(getThemedColor(Theme.key_dialogTextRed), getThemedColor(Theme.key_dialogTextRed)); + headerItem.lazilyAddSubItem(delete_chat, R.drawable.msg_block2, LocaleController.getString(R.string.DeleteAndBlock)).setColors(getThemedColor(Theme.key_text_RedRegular), getThemedColor(Theme.key_text_RedRegular)); } else { headerItem.lazilyAddSubItem(delete_chat, R.drawable.msg_delete, LocaleController.getString("DeleteChatUser", R.string.DeleteChatUser)); } @@ -3759,7 +3848,7 @@ public void toggleMute() { contentView.setOccupyStatusBar(false); } - contentView.setBackgroundImage(Theme.getCachedWallpaper(), Theme.isWallpaperMotion()); + updateBackground(); emptyViewContainer = null; @@ -3778,7 +3867,7 @@ public void toggleMute() { mentionContainer.getAdapter().onDestroy(); } - chatListView = new RecyclerListView(context, themeDelegate) { + chatListView = new RecyclerListViewInternal(context, themeDelegate) { private int lastWidth; @@ -3892,7 +3981,7 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) { } forceScrollToTop = false; - if (textSelectionHelper != null && textSelectionHelper.isSelectionMode()) { + if (textSelectionHelper != null && textSelectionHelper.isInSelectionMode()) { textSelectionHelper.invalidate(); } isSkeletonVisible(); @@ -4515,7 +4604,7 @@ public void draw(Canvas canvas) { int wasOutlineAlpha = skeletonOutlinePaint.getAlpha(); skeletonServicePaint.setAlpha((int) (0xFF * topSkeletonAlpha)); skeletonPaint.setAlpha((int) (topSkeletonAlpha * alpha)); - skeletonOutlinePaint.setAlpha((int) (wasOutlineAlpha * alpha)); + skeletonOutlinePaint.setAlpha((int) (topSkeletonAlpha * alpha)); while (lastTop > blurredViewTopOffset) { lastTop -= AndroidUtilities.dp(3f); @@ -5366,19 +5455,6 @@ public AccessibilityNodeInfo createAccessibilityNodeInfo() { return super.createAccessibilityNodeInfo(); } - @Override - public void invalidate() { - if (invalidated && slidingView == null) { - return; - } - invalidated = true; - super.invalidate(); - contentView.invalidateBlur(); - if (selectionReactionsOverlay != null && selectionReactionsOverlay.isVisible()) { - selectionReactionsOverlay.invalidatePosition(); - } - } - @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); @@ -6104,7 +6180,7 @@ public void onClick(View view) { reactionsMentiondownButton = new FrameLayout(context); contentView.addView(reactionsMentiondownButton, LayoutHelper.createFrame(46, 61, Gravity.RIGHT | Gravity.BOTTOM, 0, 0, 7, 5)); - mentionContainer = new MentionsContainerView(context, dialog_id, threadMessageId, ChatActivity.this, themeDelegate) { + mentionContainer = new MentionsContainerView(context, dialog_id, threadMessageId, ChatActivity.this, contentView, themeDelegate) { @Override protected boolean canOpen() { @@ -6166,7 +6242,7 @@ protected void onScrolled(boolean atTop, boolean atBottom) { }; contentView.addView(mentionContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 110, Gravity.LEFT | Gravity.BOTTOM)); - final ContentPreviewViewer.ContentPreviewViewerDelegate contentPreviewViewerDelegate = new ContentPreviewViewer.ContentPreviewViewerDelegate() { + contentPreviewViewerDelegate = new ContentPreviewViewer.ContentPreviewViewerDelegate() { @Override public void sendSticker(TLRPC.Document sticker, String query, Object parent, boolean notify, int scheduleDate) { chatActivityEnterView.onStickerSelected(sticker, query, parent, null, true, notify, scheduleDate); @@ -6235,9 +6311,9 @@ public long getDialogId() { Object parent = mentionContainer.getAdapter().getItemParent(position); String query = MessageObject.findAnimatedEmojiEmoticon(document); if (chatMode == MODE_SCHEDULED) { - AlertsCreator.createScheduleDatePickerDialog(getParentActivity(), dialog_id, (notify, scheduleDate) -> SendMessagesHelper.getInstance(currentAccount).sendSticker(document, query, dialog_id, replyingMessageObject, getThreadMessage(), parent, null, notify, scheduleDate, false), themeDelegate); + AlertsCreator.createScheduleDatePickerDialog(getParentActivity(), dialog_id, (notify, scheduleDate) -> SendMessagesHelper.getInstance(currentAccount).sendSticker(document, query, dialog_id, replyingMessageObject, getThreadMessage(), null, null, notify, scheduleDate, false, parent), themeDelegate); } else { - getSendMessagesHelper().sendSticker(document, query, dialog_id, replyingMessageObject, getThreadMessage(), parent, sendAnimationData, true, 0, false); + getSendMessagesHelper().sendSticker(document, query, dialog_id, replyingMessageObject, getThreadMessage(), null, sendAnimationData, true, 0, false, parent); } hideFieldPanel(false); chatActivityEnterView.addStickerToRecent(document); @@ -6270,7 +6346,7 @@ public long getDialogId() { if (mentionContainer.getAdapter().isBotCommands()) { if (chatMode == MODE_SCHEDULED) { AlertsCreator.createScheduleDatePickerDialog(getParentActivity(), dialog_id, (notify, scheduleDate) -> { - getSendMessagesHelper().sendMessage((String) object, dialog_id, replyingMessageObject, getThreadMessage(), null, false, null, null, null, notify, scheduleDate, null, false); + getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of((String) object, dialog_id, replyingMessageObject, getThreadMessage(), null, false, null, null, null, notify, scheduleDate, null, false)); chatActivityEnterView.setFieldText(""); hideFieldPanel(false); }, themeDelegate); @@ -6278,7 +6354,7 @@ public long getDialogId() { if (checkSlowMode(view)) { return; } - getSendMessagesHelper().sendMessage((String) object, dialog_id, replyingMessageObject, getThreadMessage(), null, false, null, null, null, true, 0, null, false); + getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of((String) object, dialog_id, replyingMessageObject, getThreadMessage(), null, false, null, null, null, true, 0, null, false)); chatActivityEnterView.setFieldText(""); hideFieldPanel(false); } @@ -7111,6 +7187,11 @@ public void setVisibility(int visibility) { bottomOverlayStartButton.setOnClickListener(v -> bottomOverlayChatText.callOnClick()); bottomOverlayChat.addView(bottomOverlayStartButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.CENTER, 8, 8, 8, 8)); + if (currentUser != null && currentUser.bot && !UserObject.isReplyUser(currentUser) && !isInScheduleMode() && chatMode != MODE_PINNED) { + bottomOverlayStartButton.setVisibility(View.VISIBLE); + bottomOverlayChat.setVisibility(View.VISIBLE); + } + bottomOverlayChatText = new UnreadCounterTextView(context) { @Override protected void updateCounter() { @@ -7145,7 +7226,7 @@ protected float getTopOffset() { return; } if (reportType >= 0) { - showDialog(new ReportAlert(getParentActivity(), reportType) { + showDialog(new ReportAlert(getParentActivity(), reportType, getResourceProvider()) { @Override protected void onSend(int type, String message) { ArrayList ids = new ArrayList<>(); @@ -7153,7 +7234,7 @@ protected void onSend(int type, String message) { ids.add(selectedMessagesIds[0].keyAt(b)); } TLRPC.InputPeer peer = currentUser != null ? MessagesController.getInputPeer(currentUser) : MessagesController.getInputPeer(currentChat); - AlertsCreator.sendReport(peer, reportType, message, ids); + AlertsCreator.sendReport(peer, reportType, message, ids, 0); finishFragment(); chatActivityDelegate.onReport(); } @@ -7169,7 +7250,7 @@ protected void onSend(int type, String message) { if (botUserLast != null && botUserLast.length() != 0) { getMessagesController().sendBotStart(currentUser, botUserLast); } else { - getSendMessagesHelper().sendMessage("/start", dialog_id, null, null, null, false, null, null, null, true, 0, null, false); + getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of("/start", dialog_id, null, null, null, false, null, null, null, true, 0, null, false)); } }); } else { @@ -7186,7 +7267,7 @@ protected void onSend(int type, String message) { if (botUser.length() != 0) { getMessagesController().sendBotStart(currentUser, botUser); } else { - getSendMessagesHelper().sendMessage("/start", dialog_id, null, null, null, false, null, null, null, true, 0, null, false); + getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of("/start", dialog_id, null, null, null, false, null, null, null, true, 0, null, false)); } botUser = null; updateBottomOverlay(); @@ -7301,7 +7382,7 @@ protected void onSend(int type, String message) { chatScrollHelper.setScrollListener(this::invalidateMessagesVisiblePart); chatScrollHelper.setAnimationCallback(chatScrollHelperCallback); - flagSecure = new FlagSecureReason(getParentActivity().getWindow(), () -> currentEncryptedChat != null || SharedConfig.passcodeHash.length() > 0 && !SharedConfig.allowScreenCapture || getMessagesController().isChatNoForwards(currentChat)); + flagSecure = new FlagSecureReason(getParentActivity().getWindow(), () -> currentEncryptedChat != null || getMessagesController().isChatNoForwards(currentChat)); if (oldMessage != null) { chatActivityEnterView.setFieldText(oldMessage); @@ -7625,11 +7706,11 @@ public void requestLayout() { contentView.addView(topChatPanelView, index, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 50, Gravity.TOP | Gravity.LEFT)); reportSpamButton = new TextView(getContext()); - reportSpamButton.setTextColor(getThemedColor(Theme.key_chat_reportSpam)); + reportSpamButton.setTextColor(getThemedColor(Theme.key_text_RedBold)); if (Build.VERSION.SDK_INT >= 21) { - reportSpamButton.setBackground(Theme.createSelectorDrawable(getThemedColor(Theme.key_chat_reportSpam) & 0x19ffffff, 3)); + reportSpamButton.setBackground(Theme.createSelectorDrawable(getThemedColor(Theme.key_text_RedBold) & 0x19ffffff, 3)); } - reportSpamButton.setTag(Theme.key_chat_reportSpam); + reportSpamButton.setTag(Theme.key_text_RedBold); reportSpamButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); reportSpamButton.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); reportSpamButton.setSingleLine(true); @@ -7796,6 +7877,9 @@ private void createTranslateButton() { } createTopPanel(); + if (topChatPanelView == null) { + return; + } translateButton = new TranslateButton(getContext(), this, themeDelegate) { @Override protected void onButtonClick() { @@ -8964,7 +9048,7 @@ private void openAnotherForward() { showDialog(dialog); TextView button = (TextView) dialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(getThemedColor(Theme.key_dialogTextRed)); + button.setTextColor(getThemedColor(Theme.key_text_RedBold)); } } @@ -9138,11 +9222,19 @@ public int getPreviewHeight() { boolean animateProgressViewTo; + private Boolean liteModeChat; + private boolean getLiteModeChat() { + if (liteModeChat == null) { + liteModeChat = LiteMode.isEnabled(LiteMode.FLAGS_CHAT); + } + return liteModeChat; + } + private void showProgressView(boolean show) { if (progressView == null) { return; } - if (DISABLE_PROGRESS_VIEW && !AndroidUtilities.isTablet() && !isComments && currentUser == null && LiteMode.isEnabled(LiteMode.FLAGS_CHAT)) { + if (DISABLE_PROGRESS_VIEW && !AndroidUtilities.isTablet() && !isComments && currentUser == null && getLiteModeChat()) { animateProgressViewTo = show; return; } @@ -9317,7 +9409,7 @@ private void updateChatListViewTopPadding() { } } - if (!isThreadChat() && !wasManualScroll && unreadMessageObject != null && chatListView != null) { + if (!isThreadChat() && !wasManualScroll && unreadMessageObject != null && chatListView != null && !(translateButton != null && translateButton.getVisibility() == View.VISIBLE)) { chatListView.scrollBy(0, (int) (oldPadding - chatListViewPaddingTop)); } } @@ -9725,7 +9817,7 @@ private void sendBotInlineResult(TLRPC.BotInlineResult result, boolean notify, i params.put("query_id", "" + result.query_id); params.put("bot", "" + uid); params.put("bot_name", mentionContainer.getAdapter().getContextBotName()); - SendMessagesHelper.prepareSendingBotContextResult(this, getAccountInstance(), result, params, dialog_id, replyingMessageObject, getThreadMessage(), notify, scheduleDate); + SendMessagesHelper.prepareSendingBotContextResult(this, getAccountInstance(), result, params, dialog_id, replyingMessageObject, getThreadMessage(), null, notify, scheduleDate); chatActivityEnterView.setFieldText(""); hideFieldPanel(false); getMediaDataController().increaseInlineRaiting(uid); @@ -9846,7 +9938,7 @@ private void createChatAttachView() { return; } if (chatAttachAlert == null) { - chatAttachAlert = new ChatAttachAlert(getParentActivity(), this, false, false, themeDelegate) { + chatAttachAlert = new ChatAttachAlert(getParentActivity(), this, false, false, true, themeDelegate) { @Override public void dismissInternal() { if (chatAttachAlert != null && chatAttachAlert.isShowing()) { @@ -9920,7 +10012,7 @@ public void didPressedButton(int button, boolean arg, boolean notify, int schedu fillEditingMediaWithCaption(photos.get(0).caption, photos.get(0).entities); updateStickersOrder = photos.get(0).updateStickersOrder; } - SendMessagesHelper.prepareSendingMedia(getAccountInstance(), photos, dialog_id, replyingMessageObject, getThreadMessage(), null, button == 4 || forceDocument, arg, editingMessageObject, notify, scheduleDate, updateStickersOrder); + SendMessagesHelper.prepareSendingMedia(getAccountInstance(), photos, dialog_id, replyingMessageObject, getThreadMessage(), null, button == 4 || forceDocument, arg, editingMessageObject, notify, scheduleDate, updateStickersOrder, null); } afterMessageSend(); chatActivityEnterView.setFieldText(""); @@ -10194,7 +10286,7 @@ public void shareMyContact(int type, MessageObject messageObject) { getMessagesController().processUpdates((TLRPC.Updates) response, false); }); } else { - SendMessagesHelper.getInstance(currentAccount).sendMessage(getUserConfig().getCurrentUser(), dialog_id, messageObject, getThreadMessage(), null, null, true, 0); + SendMessagesHelper.getInstance(currentAccount).sendMessage(SendMessagesHelper.SendMessageParams.of(getUserConfig().getCurrentUser(), dialog_id, messageObject, getThreadMessage(), null, null, true, 0)); if (chatMode == 0) { moveScrollToLastMessage(false); } @@ -10905,7 +10997,7 @@ public boolean isKeyboardVisible() { } private void checkScrollForLoad(boolean scroll) { - if (chatLayoutManager == null || paused || chatAdapter.isFrozen) { + if (chatLayoutManager == null || paused || chatAdapter.isFrozen || waitingForGetDifference) { return; } int firstVisibleItem = RecyclerListView.NO_POSITION; @@ -11070,7 +11162,7 @@ public void openPollCreate(Boolean quiz) { PollCreateActivity pollCreateActivity = new PollCreateActivity(ChatActivity.this, quiz); pollCreateActivity.setDelegate((poll, params, notify, scheduleDate) -> { if (checkSlowModeAlert()) { - getSendMessagesHelper().sendMessage(poll, dialog_id, replyingMessageObject, getThreadMessage(), null, params, notify, scheduleDate); + getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of(poll, dialog_id, replyingMessageObject, getThreadMessage(), null, params, notify, scheduleDate)); afterMessageSend(); } }); @@ -11082,11 +11174,11 @@ public void didSelectFiles(ArrayList files, String caption, ArrayList files, String caption, ArrayList photos, boolean notify, int scheduleDate) { fillEditingMediaWithCaption(photos.get(0).caption, photos.get(0).entities); - SendMessagesHelper.prepareSendingMedia(getAccountInstance(), photos, dialog_id, replyingMessageObject, getThreadMessage(), null, true, false, editingMessageObject, notify, scheduleDate, photos.get(0).updateStickersOrder); + SendMessagesHelper.prepareSendingMedia(getAccountInstance(), photos, dialog_id, replyingMessageObject, getThreadMessage(), null, true, false, editingMessageObject, notify, scheduleDate, photos.get(0).updateStickersOrder, null); afterMessageSend(); if (scheduleDate != 0) { if (scheduledMessagesCount == -1) { @@ -11121,12 +11213,12 @@ public void didSelectSearchPhotos(ArrayList } } if (!hasNoGifs && !TextUtils.isEmpty(photos.get(0).caption)) { - SendMessagesHelper.getInstance(currentAccount).sendMessage(photos.get(0).caption, dialog_id, replyingMessageObject, getThreadMessage(), null, false, photos.get(0).entities, null, null, notify, scheduleDate, null, false); + SendMessagesHelper.getInstance(currentAccount).sendMessage(SendMessagesHelper.SendMessageParams.of(photos.get(0).caption, dialog_id, replyingMessageObject, getThreadMessage(), null, false, photos.get(0).entities, null, null, notify, scheduleDate, null, false)); } for (int a = 0; a < photos.size(); a++) { SendMessagesHelper.SendingMediaInfo info = photos.get(a); if (info.inlineResult != null && info.videoEditedInfo == null) { - SendMessagesHelper.prepareSendingBotContextResult(this, getAccountInstance(), info.inlineResult, info.params, dialog_id, replyingMessageObject, getThreadMessage(), notify, scheduleDate); + SendMessagesHelper.prepareSendingBotContextResult(this, getAccountInstance(), info.inlineResult, info.params, dialog_id, replyingMessageObject, getThreadMessage(), null, notify, scheduleDate); photos.remove(a); a--; } @@ -11135,7 +11227,7 @@ public void didSelectSearchPhotos(ArrayList return; } fillEditingMediaWithCaption(photos.get(0).caption, photos.get(0).entities); - SendMessagesHelper.prepareSendingMedia(getAccountInstance(), photos, dialog_id, replyingMessageObject, getThreadMessage(), null, false, true, editingMessageObject, notify, scheduleDate, photos.get(0).updateStickersOrder); + SendMessagesHelper.prepareSendingMedia(getAccountInstance(), photos, dialog_id, replyingMessageObject, getThreadMessage(), null, false, true, editingMessageObject, notify, scheduleDate, photos.get(0).updateStickersOrder, null); afterMessageSend(); if (scheduleDate != 0) { if (scheduledMessagesCount == -1) { @@ -11162,7 +11254,7 @@ public void startDocumentSelectActivity() { @Override public boolean dismissDialogOnPause(Dialog dialog) { - return dialog != chatAttachAlert && super.dismissDialogOnPause(dialog); + return dialog != chatAttachAlert && dialog != chatThemeBottomSheet && super.dismissDialogOnPause(dialog); } private boolean disableLinkPreview = NekoConfig.disableLinkPreviewByDefault.Bool(); @@ -11197,9 +11289,11 @@ private void searchLinks(final CharSequence charSequence, final boolean force) { } final MessagesController messagesController = getMessagesController(); Utilities.searchQueue.postRunnable(() -> { + boolean requestAnyway = false; if (linkSearchRequestId != 0) { getConnectionsManager().cancelRequest(linkSearchRequestId, true); linkSearchRequestId = 0; + requestAnyway = true; } ArrayList urls = null; CharSequence textToCheck; @@ -11234,7 +11328,7 @@ private void searchLinks(final CharSequence charSequence, final boolean force) { clear = false; } } - if (clear) { + if (clear && !requestAnyway) { return; } } @@ -11731,6 +11825,12 @@ public void showFieldPanel(boolean show, MessageObject messageObjectToReply, Mes } else { replyNameTextView.setText(LocaleController.formatPluralString("PreviewForwardFile", messageObjectsToForward.size())); } + } else if (type == MessageObject.TYPE_STORY) { + if (messageObjectsToForward.size() == 1) { + replyNameTextView.setText(LocaleController.getString("Story", R.string.Story)); + } else { + replyNameTextView.setText(LocaleController.formatPluralString("Stories", messageObjectsToForward.size())); + } } if (forwardingMessages.hideForwardSendersName) { @@ -11814,7 +11914,6 @@ public void showFieldPanel(boolean show, MessageObject messageObjectToReply, Mes if (photoSize != null) { size = photoSize.size; } - cacheType = 0; } else { photoSize = FileLoader.getClosestPhotoSizeWithSize(thumbMediaMessageObject.photoThumbs, 320); } @@ -12644,13 +12743,13 @@ public void updateMessagesVisiblePart(boolean inLayout) { inlineUpdate2(); } getMessagesController().markDialogAsRead(dialog_id, minMessageId[0], minMessageId[0], maxDate[0], false, threadId, 0, true, scheduledRead); - if (isTopic) { + if (isTopic && replyOriginalChat != null) { getMessagesStorage().updateRepliesMaxReadId(replyOriginalChat.id, replyOriginalMessageId, Math.max(maxPositiveUnreadId, replyMaxReadId), 0, true); } firstUnreadSent = true; } } - if (threadId != 0 && maxPositiveUnreadId > 0 && replyMaxReadId != maxPositiveUnreadId) { + if (threadId != 0 && maxPositiveUnreadId > 0 && replyMaxReadId != maxPositiveUnreadId && replyOriginalChat != null) { replyMaxReadId = maxPositiveUnreadId; getMessagesStorage().updateRepliesMaxReadId(replyOriginalChat.id, replyOriginalMessageId, replyMaxReadId, newUnreadMessageCount, true); if (!isTopic) { @@ -12704,7 +12803,7 @@ private int getHeightForMessage(MessageObject object) { return 0; } if (dummyMessageCell == null) { - dummyMessageCell = new ChatMessageCell(getParentActivity(), true, themeDelegate); + dummyMessageCell = new ChatMessageCell(getParentActivity(), true, sharedResources, themeDelegate); } dummyMessageCell.isChat = currentChat != null || UserObject.isUserSelf(currentUser); dummyMessageCell.isBot = currentUser != null && currentUser.bot; @@ -12769,6 +12868,9 @@ public void scrollToMessageId(int id, int fromMessageId, boolean select, int loa } public void scrollToMessageId(int id, int fromMessageId, boolean select, int loadIndex, boolean forceScroll, int forcePinnedMessageId, Runnable inCaseLoading) { + if (waitingForGetDifference) { + return; + } if (id == 0 || NotificationCenter.getInstance(currentAccount).isAnimationInProgress() || getParentActivity() == null) { if (NotificationCenter.getInstance(currentAccount).isAnimationInProgress()) { nextScrollToMessageId = id; @@ -12808,7 +12910,7 @@ public void scrollToMessageId(int id, int fromMessageId, boolean select, int loa for (int i = chatLayoutManager.findFirstVisibleItemPosition(); i <= end; i++) { if (i >= chatAdapter.messagesStartRow && i < chatAdapter.messagesEndRow) { MessageObject messageObject = messages.get(i - chatAdapter.messagesStartRow); - if (messageObject.getId() == 0) { + if (messageObject.getId() == 0 || messageObject.isSponsored()) { continue; } scrollFromIndex = i - chatAdapter.messagesStartRow; @@ -12976,7 +13078,7 @@ private void updatePagedownButtonVisibility(boolean animated) { if (pagedownButton == null) { return; } - boolean show = canShowPagedownButton && !textSelectionHelper.isSelectionMode() && !chatActivityEnterView.isRecordingAudioVideo(); + boolean show = canShowPagedownButton && !textSelectionHelper.isInSelectionMode() && !chatActivityEnterView.isRecordingAudioVideo(); if (show) { if (animated && (openAnimationStartTime == 0 || SystemClock.elapsedRealtime() < openAnimationStartTime + 150)) { animated = false; @@ -13337,7 +13439,7 @@ public boolean dispatchTouchEvent(MotionEvent ev) { lastTouchY = ev.getY(); TextSelectionHelper.TextSelectionOverlay selectionOverlay = textSelectionHelper.getOverlayView(getContext()); ev.offsetLocation(-selectionOverlay.getX(), -selectionOverlay.getY()); - if (textSelectionHelper.isSelectionMode() && textSelectionHelper.getOverlayView(getContext()).onTouchEvent(ev)) { + if (textSelectionHelper.isInSelectionMode() && textSelectionHelper.getOverlayView(getContext()).onTouchEvent(ev)) { return true; } else { ev.offsetLocation(selectionOverlay.getX(), selectionOverlay.getY()); @@ -13347,7 +13449,7 @@ public boolean dispatchTouchEvent(MotionEvent ev) { ev.setAction(MotionEvent.ACTION_CANCEL); } - if (ev.getAction() == MotionEvent.ACTION_DOWN && textSelectionHelper.isSelectionMode() && (ev.getY() < chatListView.getTop() || ev.getY() > chatListView.getBottom())) { + if (ev.getAction() == MotionEvent.ACTION_DOWN && textSelectionHelper.isInSelectionMode() && (ev.getY() < chatListView.getTop() || ev.getY() > chatListView.getBottom())) { ev.offsetLocation(-selectionOverlay.getX(), -selectionOverlay.getY()); if (textSelectionHelper.getOverlayView(getContext()).onTouchEvent(ev)) { ev.offsetLocation(selectionOverlay.getX(), selectionOverlay.getY()); @@ -14075,14 +14177,19 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int contentHeightSpec = View.MeasureSpec.makeMeasureSpec(allHeight - actionBarHeight - AndroidUtilities.dp(48), View.MeasureSpec.EXACTLY); child.measure(contentWidthSpec, contentHeightSpec); } else if (chatActivityEnterView.isPopupView(child)) { + int height = chatActivityEnterView.getPopupViewHeight(child); if (inBubbleMode) { - child.measure(View.MeasureSpec.makeMeasureSpec(widthSize, View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(heightSize - inputFieldHeight + actionBarHeight + getPaddingTop(), View.MeasureSpec.EXACTLY)); + int maxHeight = (heightSize - inputFieldHeight + actionBarHeight + getPaddingTop()); + if (height < 0) { + height = Math.max(Math.min(maxHeight, AndroidUtilities.dp(350)), maxHeight / 2); + } + child.measure(View.MeasureSpec.makeMeasureSpec(widthSize, View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)); } else if (AndroidUtilities.isInMultiwindow) { - if (AndroidUtilities.isTablet()) { - child.measure(View.MeasureSpec.makeMeasureSpec(widthSize, View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(Math.min(AndroidUtilities.dp(320), heightSize - inputFieldHeight + actionBarHeight - AndroidUtilities.statusBarHeight + getPaddingTop()), View.MeasureSpec.EXACTLY)); - } else { - child.measure(View.MeasureSpec.makeMeasureSpec(widthSize, View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(heightSize - inputFieldHeight + actionBarHeight - AndroidUtilities.statusBarHeight + getPaddingTop(), View.MeasureSpec.EXACTLY)); + int maxHeight = (heightSize - inputFieldHeight + actionBarHeight - AndroidUtilities.statusBarHeight + getPaddingTop()); + if (height < 0) { + height = Math.max(Math.min(maxHeight, AndroidUtilities.dp(350)), maxHeight / 2); } + child.measure(View.MeasureSpec.makeMeasureSpec(widthSize, View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)); } else { child.measure(View.MeasureSpec.makeMeasureSpec(widthSize, View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(child.getLayoutParams().height, View.MeasureSpec.EXACTLY)); } @@ -14345,6 +14452,13 @@ protected Drawable getNewDrawable() { Drawable drawable = themeDelegate.getWallpaperDrawable(); return drawable != null ? drawable : super.getNewDrawable(); } + + protected boolean getNewDrawableMotion() { + if (themeDelegate.wallpaper == null) { + return super.getNewDrawableMotion(); + } + return themeDelegate.wallpaper.settings != null && themeDelegate.wallpaper.settings.motion; + } }; private void updateSecretStatus() { @@ -14426,8 +14540,13 @@ public void onRequestPermissionsResultFragment(int requestCode, String[] permiss if (mentionContainer != null && mentionContainer.getAdapter() != null) { mentionContainer.getAdapter().onRequestPermissionsResultFragment(requestCode, permissions, grantResults); } - if (requestCode == BasePermissionsActivity.REQUEST_CODE_EXTERNAL_STORAGE && chatAttachAlert != null) { - chatAttachAlert.getPhotoLayout().checkStorage(); + if (requestCode == BasePermissionsActivity.REQUEST_CODE_EXTERNAL_STORAGE) { + if (chatAttachAlert != null) { + chatAttachAlert.getPhotoLayout().checkStorage(); + } + if (chatThemeBottomSheet != null && chatThemeBottomSheet.chatAttachAlert != null) { + chatThemeBottomSheet.chatAttachAlert.getPhotoLayout().checkStorage(); + } } else if ((requestCode == BasePermissionsActivity.REQUEST_CODE_ATTACH_CONTACT || requestCode == 30) && chatAttachAlert != null) { chatAttachAlert.onRequestPermissionsResultFragment(requestCode, permissions, grantResults); } else if ((requestCode == 17 || requestCode == 18) && chatAttachAlert != null) { @@ -14536,7 +14655,7 @@ private int getMessageType(MessageObject messageObject) { return 2; } else if (messageObject.type == 6) { return -1; - } else if (messageObject.type == 10 || messageObject.type == MessageObject.TYPE_ACTION_PHOTO || messageObject.type == MessageObject.TYPE_SUGGEST_PHOTO) { + } else if (messageObject.type == 10 || messageObject.type == MessageObject.TYPE_ACTION_PHOTO || messageObject.type == MessageObject.TYPE_SUGGEST_PHOTO || messageObject.isWallpaperAction()) { if (messageObject.getId() == 0) { return -1; } @@ -15134,7 +15253,7 @@ private void processRowSelect(View view, boolean outside, float touchX, float to int type = getMessageType(message); - if (type < 2 || type == 20 || type == MessageObject.TYPE_SUGGEST_PHOTO) { + if (type < 2 || type == 20 || type == MessageObject.TYPE_SUGGEST_PHOTO || (message != null && message.isWallpaperAction())) { return; } addToSelectedMessages(message, outside); @@ -15345,7 +15464,7 @@ public boolean canScrollAway() { }, this); } else { fillEditingMediaWithCaption(caption, null); - SendMessagesHelper.prepareSendingVideo(getAccountInstance(), videoPath, null, dialog_id, replyingMessageObject, getThreadMessage(), null, null, 0, editingMessageObject, true, 0, false, false); + SendMessagesHelper.prepareSendingVideo(getAccountInstance(), videoPath, null, dialog_id, replyingMessageObject, getThreadMessage(), null, null, 0, editingMessageObject, true, 0, false, false, null); afterMessageSend(); } } @@ -15382,25 +15501,8 @@ public boolean openPhotosEditor(ArrayList p if (path == null) { continue; } - int orientation = 0; - try { - ExifInterface ei = new ExifInterface(path); - int exif = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); - switch (exif) { - case ExifInterface.ORIENTATION_ROTATE_90: - orientation = 90; - break; - case ExifInterface.ORIENTATION_ROTATE_180: - orientation = 180; - break; - case ExifInterface.ORIENTATION_ROTATE_270: - orientation = 270; - break; - } - } catch (Exception e) { - FileLog.e(e); - } - MediaController.PhotoEntry entry = new MediaController.PhotoEntry(0, 0, 0, path, orientation, photoInfo.isVideo, 0, 0, 0); + Pair orientation = AndroidUtilities.getImageOrientation(path); + MediaController.PhotoEntry entry = new MediaController.PhotoEntry(0, 0, 0, path, orientation.first, photoInfo.isVideo, 0, 0, 0).setOrientation(orientation); if (a == photoPathes.size() - 1 && caption != null) { entry.caption = caption; } @@ -15474,7 +15576,7 @@ private void sendPhotosGroup(ArrayList entries, bool entry.reset(); } fillEditingMediaWithCaption(photos.get(0).caption, photos.get(0).entities); - SendMessagesHelper.prepareSendingMedia(getAccountInstance(), photos, dialog_id, replyingMessageObject, getThreadMessage(), null, forceDocument, true, null, notify, scheduleDate, photos.get(0).updateStickersOrder); + SendMessagesHelper.prepareSendingMedia(getAccountInstance(), photos, dialog_id, replyingMessageObject, getThreadMessage(), null, forceDocument, true, null, notify, scheduleDate, photos.get(0).updateStickersOrder, null); afterMessageSend(); if (chatActivityEnterView != null) { chatActivityEnterView.setFieldText(""); @@ -15689,9 +15791,9 @@ private void sendUriAsDocument(Uri uri) { } fillEditingMediaWithCaption(null, null); if (sendAsUri) { - SendMessagesHelper.prepareSendingDocument(getAccountInstance(), null, null, uri, null, null, dialog_id, replyingMessageObject, getThreadMessage(), null, editingMessageObject, true, 0); + SendMessagesHelper.prepareSendingDocument(getAccountInstance(), null, null, uri, null, null, dialog_id, replyingMessageObject, getThreadMessage(), null, editingMessageObject, true, 0, null); } else { - SendMessagesHelper.prepareSendingDocument(getAccountInstance(), tempPath, originalPath, null, null, null, dialog_id, replyingMessageObject, getThreadMessage(), null, editingMessageObject, true, 0); + SendMessagesHelper.prepareSendingDocument(getAccountInstance(), tempPath, originalPath, null, null, null, dialog_id, replyingMessageObject, getThreadMessage(), null, editingMessageObject, true, 0, null); } hideFieldPanel(false); } @@ -15805,7 +15907,7 @@ public void restoreSelfArgs(Bundle args) { } private boolean isSkeletonVisible() { - if (justCreatedTopic || justCreatedChat || currentUser != null || chatListView == null || !SharedConfig.animationsEnabled() || !LiteMode.isEnabled(LiteMode.FLAGS_CHAT)) { + if (justCreatedTopic || justCreatedChat || currentUser != null || chatListView == null || !SharedConfig.animationsEnabled() || !getLiteModeChat()) { return false; } int childHeight = 0; @@ -16502,7 +16604,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { newRowsCount++; dayArray.add(obj); if (oldMessage != null) { - obj.stableId = oldMessage.stableId; + obj.stableId = oldMessage.stableId; } else { obj.stableId = lastStableId++; } @@ -16513,6 +16615,16 @@ public void didReceivedNotification(int id, int account, final Object... args) { messages.get(messages.size() - 1).stableId = lastStableId++; messages.add(messages.size() - 1, obj); } +// if (obj.isStoryMention()) { +// MessageObject msg = obj.makeStoryMentionAction(lastStableId++); +// newRowsCount++; +// if (load_type == 1) { +// messages.add(0, msg); +// } else { +// messages.get(messages.size() - 1).stableId = lastStableId++; +// messages.add(messages.size() - 1, msg); +// } +// } MessageObject prevObj; if (currentEncryptedChat == null) { @@ -16672,7 +16784,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { if (!postponedScroll) { chatAdapter.notifyDataSetChanged(true); } - if (isTopic && startLoadFromMessageId == getTopicId() && messArr.size() > 0 && messages.size() > 0) { + if (isTopic && startLoadFromMessageId == getTopicId() && messArr.size() > 0 && messages.size() > 0 && messArr.size() - 1 < messages.size()) { scrollToMessage = messages.get(messArr.size() - 1); } if (scrollToMessage != null) { @@ -16950,6 +17062,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { bottomOverlayChat.setVisibility(View.GONE); chatActivityEnterView.setVisibility(View.VISIBLE); + chatActivityEnterView.setBotInfo(botInfo); } } } else if (id == NotificationCenter.invalidateMotionBackground) { @@ -17205,7 +17318,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { } obj.setIsRead(); if (chatAdapter != null) { - chatAdapter.updateRowWithMessageObject(obj, false); + chatAdapter.updateRowWithMessageObject(obj, false, false); } } } @@ -17456,7 +17569,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { getMediaDataController().loadReplyMessagesForMessages(messArr, dialog_id, chatMode == MODE_SCHEDULED, 0, null); } if (chatAdapter != null) { - chatAdapter.updateRowWithMessageObject(obj, false); + chatAdapter.updateRowWithMessageObject(obj, false, false); } if (chatLayoutManager != null) { if (mediaUpdated && chatLayoutManager.findFirstVisibleItemPosition() == 0) { @@ -17473,7 +17586,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { if (obj != null) { obj.messageOwner.send_state = MessageObject.MESSAGE_SEND_STATE_SENT; if (chatAdapter != null) { - chatAdapter.updateRowWithMessageObject(obj, false); + chatAdapter.updateRowWithMessageObject(obj, false, false); } } } else if (id == NotificationCenter.messageSendError) { @@ -17631,7 +17744,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { } } checkGroupCallJoin((Boolean) args[3]); - checkThemeEmoticon(); + checkThemeEmoticonOrWallpaper(); if (pendingRequestsDelegate != null) { pendingRequestsDelegate.setChatInfo(chatInfo, true); } @@ -18005,28 +18118,24 @@ public void didReceivedNotification(int id, int account, final Object... args) { } MessageObject.updatePollResults(media, results); if (chatAdapter != null) { - pollView = chatAdapter.updateRowWithMessageObject(object, true); + pollView = chatAdapter.updateRowWithMessageObject(object, true, false); } if (isVoted != object.isVoted()) { isVotedChanged = true; } } + createUndoView(); if (isVotedChanged && isQuiz && undoView != null && pollView instanceof ChatMessageCell) { ChatMessageCell cell = (ChatMessageCell) pollView; if (cell.isAnimatingPollAnswer()) { for (int a = 0, N = results.results.size(); a < N; a++) { TLRPC.TL_pollAnswerVoters voters = results.results.get(a); if (voters.chosen) { + pollView.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); if (voters.correct) { fireworksOverlay.start(); - if (!NekoConfig.disableVibration.Bool()) { - pollView.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); - } } else { ((ChatMessageCell) pollView).shakeView(); - if (!NekoConfig.disableVibration.Bool()) { - pollView.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); - } showPollSolution(cell.getMessageObject(), results); cell.showHintButton(false, true, 0); } @@ -18072,7 +18181,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { existMessageObject.messageOwner.stickerVerified = message.stickerVerified; existMessageObject.setType(); if (chatAdapter != null) { - chatAdapter.updateRowWithMessageObject(existMessageObject, false); + chatAdapter.updateRowWithMessageObject(existMessageObject, false, false); } } } @@ -18123,7 +18232,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { if (message.media.ttl_seconds != 0 && (message.media.photo instanceof TLRPC.TL_photoEmpty || message.media.document instanceof TLRPC.TL_documentEmpty)) { existMessageObject.setType(); if (chatAdapter != null) { - chatAdapter.updateRowWithMessageObject(existMessageObject, false); + chatAdapter.updateRowWithMessageObject(existMessageObject, false, false); } } else { updateVisibleRows(); @@ -18316,7 +18425,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { } } } else { - chatAdapter.updateRowWithMessageObject(obj, false); + chatAdapter.updateRowWithMessageObject(obj, false, false); } } } @@ -18552,7 +18661,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { } } if (chatAdapter != null) { - chatAdapter.updateRowWithMessageObject(messageObject, false); + chatAdapter.updateRowWithMessageObject(messageObject, false, false); } } } @@ -18635,10 +18744,12 @@ public void didReceivedNotification(int id, int account, final Object... args) { if (newGroups != null) { for (int b = 0, N = newGroups.size(); b < N; b++) { MessageObject.GroupedMessages groupedMessages = newGroups.valueAt(b); - MessageObject messageObject = groupedMessages.messages.get(groupedMessages.messages.size() - 1); - int index = messages.indexOf(messageObject); - if (index >= 0) { - chatAdapter.notifyItemRangeChanged(index + chatAdapter.messagesStartRow, groupedMessages.messages.size()); + if (!groupedMessages.messages.isEmpty()) { + MessageObject messageObject = groupedMessages.messages.get(groupedMessages.messages.size() - 1); + int index = messages.indexOf(messageObject); + if (index >= 0) { + chatAdapter.notifyItemRangeChanged(index + chatAdapter.messagesStartRow, groupedMessages.messages.size()); + } } } } @@ -18706,7 +18817,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { Long uid = (Long) args[0]; if (currentUser != null && currentUser.id == uid) { userInfo = (TLRPC.UserFull) args[1]; - checkThemeEmoticon(); + checkThemeEmoticonOrWallpaper(); if (chatActivityEnterView != null) { chatActivityEnterView.checkChannelRights(); chatActivityEnterView.updateGiftButton(true); @@ -18751,7 +18862,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { } } else if (id == NotificationCenter.didSetNewWallpapper) { if (fragmentView != null) { - contentView.setBackgroundImage(Theme.getCachedWallpaper(), Theme.isWallpaperMotion()); + updateBackground(); progressView2.invalidate(); if (emptyView != null) { emptyView.invalidate(); @@ -18896,7 +19007,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { if (chatThemeBottomSheet != null) { chatThemeBottomSheet.setupLightDarkTheme(theme.isDark()); } else { - themeDelegate.setCurrentTheme(themeDelegate.chatTheme, true, theme.isDark()); + themeDelegate.setCurrentTheme(themeDelegate.chatTheme, themeDelegate.wallpaper, true, null); } } } else if (id == NotificationCenter.chatAvailableReactionsUpdated) { @@ -19062,6 +19173,12 @@ public void didReceivedNotification(int id, int account, final Object... args) { updateTopPanel(true); updateTranslateItemVisibility(); + } else if (id == NotificationCenter.onReceivedChannelDifference) { + final long channelId = (long) args[0]; + if (-channelId == dialog_id) { + waitingForGetDifference = false; + firstLoadMessages(); + } } } @@ -19596,7 +19713,7 @@ private void updateReplyMessageOwners(int id, MessageObject update) { object.replyMessageObject = update; } if (chatAdapter != null) { - chatAdapter.updateRowWithMessageObject(object, true); + chatAdapter.updateRowWithMessageObject(object, true, false); } } } @@ -19606,10 +19723,16 @@ private void updateReplyMessageOwners(int id, MessageObject update) { } private void rotateMotionBackgroundDrawable() { + if (themeDelegate == null) { + return; + } Drawable wallpaper = themeDelegate.getWallpaperDrawable(); if (fragmentView != null) { wallpaper = ((SizeNotifierFrameLayout) fragmentView).getBackgroundImage(); } + if (wallpaper instanceof ChatBackgroundDrawable) { + wallpaper = ((ChatBackgroundDrawable) wallpaper).getDrawable(); + } if (wallpaper instanceof MotionBackgroundDrawable) { ((MotionBackgroundDrawable) wallpaper).switchToNextPosition(); } @@ -19640,15 +19763,19 @@ private void processNewMessages(ArrayList arr) { if (!isAd) { isAd = messageObject.isSponsored(); } - if (messageObject.getId() > 0 && messageObject.type == MessageObject.TYPE_SUGGEST_PHOTO) { + if (messageObject.getId() > 0 && (messageObject.type == MessageObject.TYPE_SUGGEST_PHOTO || messageObject.type == MessageObject.TYPE_ACTION_WALLPAPER)) { for (int i = 0; i < messages.size(); i++) { - if (messages.get(i).type == MessageObject.TYPE_SUGGEST_PHOTO && messages.get(i).getId() < 0) { + int type = messageObject.type; + if (messages.get(i).type == type && messages.get(i).getId() < 0) { messagesDict[0].remove(messages.get(i).getId()); messagesDict[0].put(messageObject.getId(), messageObject); messageObject.stableId = messages.get(i).stableId; - PhotoUtilities.replacePhotoImagesInCache(currentAccount, messages.get(i).messageOwner.action.photo, messageObject.messageOwner.action.photo); + if (type == MessageObject.TYPE_ACTION_WALLPAPER) { + messageObject.messageOwner.action.wallpaper = messages.get(i).messageOwner.action.wallpaper; + } else if (type == MessageObject.TYPE_SUGGEST_PHOTO) { + PhotoUtilities.replacePhotoImagesInCache(currentAccount, messages.get(i).messageOwner.action.photo, messageObject.messageOwner.action.photo); + } messages.set(i, messageObject); - chatAdapter.notifyItemChanged(chatAdapter.messagesStartRow + i); break; } @@ -19739,6 +19866,7 @@ private void processNewMessages(ArrayList arr) { if (messageObject.replyMessageObject == null && messageObject.getDialogId() != mergeDialogId) { messageObject.replyMessageObject = repliesMessagesDict.get(messageObject.getReplyMsgId()); } + messageObject.applyTimestampsHighlightForReplyMsg(); if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionPinMessage) { messageObject.generatePinMessageText(null, null); } else if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionGameScore) { @@ -20608,6 +20736,32 @@ private void replaceMessageObjects(ArrayList messageObjects, int messageObject.generatePaymentSentMessageText(null); } } + if (old.isWebpage() && messageObject.isWebpage()) { + TLRPC.TL_messageMediaWebPage media = (TLRPC.TL_messageMediaWebPage) MessageObject.getMedia(old.messageOwner); + if (media.webpage != null && "telegram_story".equals(media.webpage.type)) { + TLRPC.StoryItem storyItem = null; + for (int i = 0; i < media.webpage.attributes.size(); ++i) { + TLRPC.WebPageAttribute attr = media.webpage.attributes.get(i); + if (attr instanceof TLRPC.TL_webPageAttributeStory) { + storyItem = ((TLRPC.TL_webPageAttributeStory) attr).storyItem; + break; + } + } + if (storyItem != null) { + TLRPC.TL_messageMediaWebPage newMedia = (TLRPC.TL_messageMediaWebPage) MessageObject.getMedia(messageObject.messageOwner); + for (int i = 0; i < newMedia.webpage.attributes.size(); ++i) { + TLRPC.WebPageAttribute attr = newMedia.webpage.attributes.get(i); + if (attr instanceof TLRPC.TL_webPageAttributeStory) { + TLRPC.TL_webPageAttributeStory storyAttr = (TLRPC.TL_webPageAttributeStory) attr; + if (!(storyAttr.storyItem instanceof TLRPC.TL_storyItem)) { + storyAttr.storyItem = storyItem; + } + break; + } + } + } + } + } if (!old.isEditing()) { if (old.getFileName().equals(messageObject.getFileName())) { messageObject.messageOwner.attachPath = old.messageOwner.attachPath; @@ -20795,11 +20949,8 @@ private void addToPolls(MessageObject obj, MessageObject old) { } private void showInfoHint(MessageObject messageObject, CharSequence text, int type) { - checkTopUndoView(); - if (topUndoView == null) { - return; - } - Runnable runnable = () -> { + int duration = Math.max(4000, Math.min((text == null ? 0 : text.length()) / 50 * 1600, 10000)); + BulletinFactory.of(this).createSimpleBulletin(R.raw.chats_infotip, text, 9999).setDuration(duration).setOnHideListener(() -> { if (chatListView != null) { int count = chatListView.getChildCount(); for (int a = 0; a < count; a++) { @@ -20815,8 +20966,7 @@ private void showInfoHint(MessageObject messageObject, CharSequence text, int ty } } hintMessageObject = null; - }; - topUndoView.showWithAction(0, UndoView.ACTION_TEXT_INFO, text, runnable, runnable); + }).show(true); hintMessageObject = messageObject; hintMessageType = type; } @@ -21092,92 +21242,96 @@ public void onTransitionAnimationEnd(boolean isOpen, boolean backward) { contentView.invalidate(); if (!TextUtils.isEmpty(attachMenuBotToOpen)) { - TLRPC.TL_contacts_resolveUsername req = new TLRPC.TL_contacts_resolveUsername(); - req.username = attachMenuBotToOpen; - getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(()->{ - if (response != null) { - TLRPC.TL_contacts_resolvedPeer resolvedPeer = (TLRPC.TL_contacts_resolvedPeer) response; - if (!resolvedPeer.users.isEmpty()) { - TLRPC.User user = resolvedPeer.users.get(0); - if (user.bot && user.bot_attach_menu) { - TLRPC.TL_messages_getAttachMenuBot getAttachMenuBot = new TLRPC.TL_messages_getAttachMenuBot(); - getAttachMenuBot.bot = MessagesController.getInstance(currentAccount).getInputUser(user.id); - ConnectionsManager.getInstance(currentAccount).sendRequest(getAttachMenuBot, (response1, error1) -> AndroidUtilities.runOnUIThread(()-> { - if (response1 instanceof TLRPC.TL_attachMenuBotsBot) { - TLRPC.TL_attachMenuBotsBot attachMenuBotsBot = (TLRPC.TL_attachMenuBotsBot) response1; - MessagesController.getInstance(currentAccount).putUsers(attachMenuBotsBot.users, false); - TLRPC.TL_attachMenuBot attachMenuBot = attachMenuBotsBot.bot; - - if (!MediaDataController.canShowAttachMenuBot(attachMenuBot, getCurrentUser() != null ? getCurrentUser() : getCurrentChat())) { - if (currentUser != null && currentUser.bot && user.id == attachMenuBot.bot_id) { - BulletinFactory.of(this).createErrorBulletin(LocaleController.getString(R.string.BotCantOpenAttachMenuSameBot)).show(); - } else if (currentUser != null && currentUser.bot && user.id != attachMenuBot.bot_id) { - BulletinFactory.of(this).createErrorBulletin(LocaleController.getString(R.string.BotCantOpenAttachMenuBot)).show(); - } else if (currentUser != null && !currentUser.bot) { - BulletinFactory.of(this).createErrorBulletin(LocaleController.getString(R.string.BotCantOpenAttachMenuUser)).show(); - } else if (currentChat != null && !ChatObject.isChannelAndNotMegaGroup(currentChat)) { - BulletinFactory.of(this).createErrorBulletin(LocaleController.getString(R.string.BotCantOpenAttachMenuGroup)).show(); - } else if (currentChat != null && ChatObject.isChannelAndNotMegaGroup(currentChat)) { - BulletinFactory.of(this).createErrorBulletin(LocaleController.getString(R.string.BotCantOpenAttachMenuChannel)).show(); - } - return; + openAttachBotLayout(attachMenuBotToOpen); + attachMenuBotToOpen = null; + } + } + + public void openAttachBotLayout(String botUsername) { + TLRPC.TL_contacts_resolveUsername req = new TLRPC.TL_contacts_resolveUsername(); + req.username = botUsername; + getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(()->{ + if (response != null) { + TLRPC.TL_contacts_resolvedPeer resolvedPeer = (TLRPC.TL_contacts_resolvedPeer) response; + if (!resolvedPeer.users.isEmpty()) { + TLRPC.User user = resolvedPeer.users.get(0); + if (user.bot && user.bot_attach_menu) { + TLRPC.TL_messages_getAttachMenuBot getAttachMenuBot = new TLRPC.TL_messages_getAttachMenuBot(); + getAttachMenuBot.bot = MessagesController.getInstance(currentAccount).getInputUser(user.id); + ConnectionsManager.getInstance(currentAccount).sendRequest(getAttachMenuBot, (response1, error1) -> AndroidUtilities.runOnUIThread(()-> { + if (response1 instanceof TLRPC.TL_attachMenuBotsBot) { + TLRPC.TL_attachMenuBotsBot attachMenuBotsBot = (TLRPC.TL_attachMenuBotsBot) response1; + MessagesController.getInstance(currentAccount).putUsers(attachMenuBotsBot.users, false); + TLRPC.TL_attachMenuBot attachMenuBot = attachMenuBotsBot.bot; + + if (!MediaDataController.canShowAttachMenuBot(attachMenuBot, getCurrentUser() != null ? getCurrentUser() : getCurrentChat())) { + if (currentUser != null && currentUser.bot && user.id == attachMenuBot.bot_id) { + BulletinFactory.of(this).createErrorBulletin(LocaleController.getString(R.string.BotCantOpenAttachMenuSameBot)).show(); + } else if (currentUser != null && currentUser.bot && user.id != attachMenuBot.bot_id) { + BulletinFactory.of(this).createErrorBulletin(LocaleController.getString(R.string.BotCantOpenAttachMenuBot)).show(); + } else if (currentUser != null && !currentUser.bot) { + BulletinFactory.of(this).createErrorBulletin(LocaleController.getString(R.string.BotCantOpenAttachMenuUser)).show(); + } else if (currentChat != null && !ChatObject.isChannelAndNotMegaGroup(currentChat)) { + BulletinFactory.of(this).createErrorBulletin(LocaleController.getString(R.string.BotCantOpenAttachMenuGroup)).show(); + } else if (currentChat != null && ChatObject.isChannelAndNotMegaGroup(currentChat)) { + BulletinFactory.of(this).createErrorBulletin(LocaleController.getString(R.string.BotCantOpenAttachMenuChannel)).show(); } + return; + } - if (!attachMenuBot.inactive) { - openAttachBotLayout(user.id, attachMenuBotStartCommand); - } else { - AttachBotIntroTopView introTopView = new AttachBotIntroTopView(getParentActivity()); - introTopView.setColor(Theme.getColor(Theme.key_chat_attachIcon)); - introTopView.setBackgroundColor(Theme.getColor(Theme.key_dialogTopBackground)); - introTopView.setAttachBot(attachMenuBot); - - AtomicBoolean allowWrite = new AtomicBoolean(); - AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()) - .setTopView(introTopView) - .setMessage(AndroidUtilities.replaceTags(LocaleController.formatString("BotRequestAttachPermission", R.string.BotRequestAttachPermission, UserObject.getUserName(user)))) - .setPositiveButton(LocaleController.getString(R.string.BotAddToMenu), (dialog, which) -> { - TLRPC.TL_messages_toggleBotInAttachMenu botRequest = new TLRPC.TL_messages_toggleBotInAttachMenu(); - botRequest.bot = MessagesController.getInstance(currentAccount).getInputUser(user.id); - botRequest.enabled = true; - botRequest.write_allowed = allowWrite.get(); - ConnectionsManager.getInstance(currentAccount).sendRequest(botRequest, (response2, error2) -> AndroidUtilities.runOnUIThread(() -> { - if (error2 == null) { - MediaDataController.getInstance(currentAccount).loadAttachMenuBots(false, true); - - openAttachBotLayout(user.id, attachMenuBotStartCommand); - } - }), ConnectionsManager.RequestFlagInvokeAfter | ConnectionsManager.RequestFlagFailOnServerErrors); - }) - .setNegativeButton(LocaleController.getString(R.string.Cancel), null); - - if (attachMenuBot.request_write_access) { - allowWrite.set(true); - - CheckBoxCell cell = new CheckBoxCell(getParentActivity(), 5, getResourceProvider()); - cell.setPadding(0, AndroidUtilities.dp(8), 0, AndroidUtilities.dp(8)); - cell.setBackground(Theme.getSelectorDrawable(false)); - cell.setMultiline(true); - cell.setText(AndroidUtilities.replaceTags(LocaleController.formatString("OpenUrlOption2", R.string.OpenUrlOption2, UserObject.getUserName(user))), "", true, false); - cell.setPadding(LocaleController.isRTL ? AndroidUtilities.dp(16) : AndroidUtilities.dp(8), 0, LocaleController.isRTL ? AndroidUtilities.dp(8) : AndroidUtilities.dp(16), 0); - cell.setOnClickListener(v -> { - boolean allow = !cell.isChecked(); - cell.setChecked(allow, true); - allowWrite.set(allow); - }); + if (!attachMenuBot.inactive) { + openAttachBotLayout(user.id, attachMenuBotStartCommand); + } else { + AttachBotIntroTopView introTopView = new AttachBotIntroTopView(getParentActivity()); + introTopView.setColor(Theme.getColor(Theme.key_chat_attachIcon)); + introTopView.setBackgroundColor(Theme.getColor(Theme.key_dialogTopBackground)); + introTopView.setAttachBot(attachMenuBot); + + AtomicBoolean allowWrite = new AtomicBoolean(); + AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()) + .setTopView(introTopView) + .setMessage(AndroidUtilities.replaceTags(LocaleController.formatString("BotRequestAttachPermission", R.string.BotRequestAttachPermission, UserObject.getUserName(user)))) + .setPositiveButton(LocaleController.getString(R.string.BotAddToMenu), (dialog, which) -> { + TLRPC.TL_messages_toggleBotInAttachMenu botRequest = new TLRPC.TL_messages_toggleBotInAttachMenu(); + botRequest.bot = MessagesController.getInstance(currentAccount).getInputUser(user.id); + botRequest.enabled = true; + botRequest.write_allowed = allowWrite.get(); + ConnectionsManager.getInstance(currentAccount).sendRequest(botRequest, (response2, error2) -> AndroidUtilities.runOnUIThread(() -> { + if (error2 == null) { + MediaDataController.getInstance(currentAccount).loadAttachMenuBots(false, true); + + openAttachBotLayout(user.id, attachMenuBotStartCommand); + } + }), ConnectionsManager.RequestFlagInvokeAfter | ConnectionsManager.RequestFlagFailOnServerErrors); + }) + .setNegativeButton(LocaleController.getString(R.string.Cancel), null); + + if (attachMenuBot.request_write_access) { + allowWrite.set(true); + + CheckBoxCell cell = new CheckBoxCell(getParentActivity(), 5, getResourceProvider()); + cell.setPadding(0, AndroidUtilities.dp(8), 0, AndroidUtilities.dp(8)); + cell.setBackground(Theme.getSelectorDrawable(false)); + cell.setMultiline(true); + cell.setText(AndroidUtilities.replaceTags(LocaleController.formatString("OpenUrlOption2", R.string.OpenUrlOption2, UserObject.getUserName(user))), "", true, false); + cell.setPadding(LocaleController.isRTL ? AndroidUtilities.dp(16) : AndroidUtilities.dp(8), 0, LocaleController.isRTL ? AndroidUtilities.dp(8) : AndroidUtilities.dp(16), 0); + cell.setOnClickListener(v -> { + boolean allow = !cell.isChecked(); + cell.setChecked(allow, true); + allowWrite.set(allow); + }); - builder.setCustomViewOffset(6); - builder.setView(cell); - } - builder.show(); + builder.setCustomViewOffset(6); + builder.setView(cell); } + builder.show(); } - })); - } + } + })); } } - })); - attachMenuBotToOpen = null; - } + } + })); } public void openAttachBotLayout(long botId, String startCommand) { @@ -21211,12 +21365,11 @@ public boolean extendActionMode(Menu menu) { return true; } } - fillActionModeMenu(menu); + fillActionModeMenu(menu, currentEncryptedChat); return true; } - public void fillActionModeMenu(Menu menu) { - + public static void fillActionModeMenu(Menu menu, TLRPC.EncryptedChat encryptedChat) { if (menu.findItem(R.id.menu_bold) != null) { return; } @@ -21247,7 +21400,7 @@ public void fillActionModeMenu(Menu menu) { stringBuilder.setSpan(new TypefaceSpan(Typeface.MONOSPACE), 0, stringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); menu.add(R.id.menu_groupbolditalic, R.id.menu_mono, order++, stringBuilder); } - if (currentEncryptedChat == null || AndroidUtilities.getPeerLayerVersion(currentEncryptedChat.layer) >= 101) { + if (encryptedChat == null || AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 101) { TextStyleSpan.TextStyleRun run; if (NaConfig.INSTANCE.getShowTextStrikethrough().Bool()) { stringBuilder = new SpannableStringBuilder(LocaleController.getString("Strike", R.string.Strike)); @@ -21373,6 +21526,9 @@ private void updateBottomOverlay() { } else { showBottomOverlayProgress(false, true); if (userBlocked) { + if (bottomOverlayStartButton != null) { + bottomOverlayStartButton.setVisibility(View.GONE); + } if (currentUser.bot) { bottomOverlayChatText.setText(LocaleController.getString("BotUnblock", R.string.BotUnblock)); } else { @@ -21397,17 +21553,17 @@ private void updateBottomOverlay() { showBottomOverlayProgress(false, true); } else if (botUser != null && currentUser.bot) { // bottomOverlayStartButton.setText(LocaleController.getString("BotStart", R.string.BotStart)); - bottomOverlayStartButton.setVisibility(View.VISIBLE); + if (bottomOverlayStartButton != null) { + bottomOverlayStartButton.setVisibility(View.VISIBLE); + } bottomOverlayChatText.setVisibility(View.GONE); chatActivityEnterView.hidePopup(false); if (getParentActivity() != null) { AndroidUtilities.hideKeyboard(getParentActivity().getCurrentFocus()); } - if (dialog_id == dialog_id && !messages.isEmpty() && currentUser != null) { - if (botUser.length() != 0 && !sentBotStart) { - sentBotStart = true; - } + if (!messages.isEmpty() && currentUser != null && botUser.length() != 0) { + sentBotStart = true; } } else { bottomOverlayChatText.setText(LocaleController.getString("DeleteThisChat", R.string.DeleteThisChat)); @@ -21597,6 +21753,7 @@ public void onAnimationEnd(Animator animation) { bottomOverlayChat.setVisibility(View.GONE); chatActivityEnterView.setVisibility(View.VISIBLE); + chatActivityEnterView.setBotInfo(botInfo); } checkRaiseSensors(); } @@ -21630,7 +21787,7 @@ public void updateReplyMessageHeader(boolean notify) { } replyMessageHeaderObject.messageText = replyMessageHeaderObject.messageOwner.message = text; if (notify) { - chatAdapter.updateRowWithMessageObject(replyMessageHeaderObject, true); + chatAdapter.updateRowWithMessageObject(replyMessageHeaderObject, true, false); } } @@ -21651,7 +21808,14 @@ private void createAlertView() { alertView.setVisibility(View.GONE); alertView.setBackgroundResource(R.drawable.blockpanel); alertView.getBackground().setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_chat_topPanelBackground), PorterDuff.Mode.MULTIPLY)); - contentView.addView(alertView, 9, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 50, Gravity.TOP | Gravity.LEFT)); + int index = 9; + if (topChatPanelView != null && topChatPanelView.getParent() == contentView) { + index = Math.max(index, contentView.indexOfChild(topChatPanelView) + 1); + } + if (pinnedMessageView != null && pinnedMessageView.getParent() == contentView) { + index = Math.max(index, contentView.indexOfChild(pinnedMessageView) + 1); + } + contentView.addView(alertView, index, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 50, Gravity.TOP | Gravity.LEFT)); alertNameTextView = new TextView(getContext()); alertNameTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); @@ -22099,7 +22263,6 @@ public void onAnimationCancel(Animator animation) { if (photoSize != null) { size = photoSize.size; } - cacheType = 0; } else { photoSize = FileLoader.getClosestPhotoSizeWithSize(pinnedMessageObject.photoThumbs, AndroidUtilities.dp(320)); } @@ -22847,11 +23010,11 @@ public void onClick(View view) { } if (reportSpamButton != null) { reportSpamButton.setTag(R.id.object_tag, null); - reportSpamButton.setTextColor(getThemedColor(Theme.key_chat_reportSpam)); + reportSpamButton.setTextColor(getThemedColor(Theme.key_text_RedBold)); if (Build.VERSION.SDK_INT >= 21) { - Theme.setSelectorDrawableColor(reportSpamButton.getBackground(), getThemedColor(Theme.key_chat_reportSpam) & 0x19ffffff, true); + Theme.setSelectorDrawableColor(reportSpamButton.getBackground(), getThemedColor(Theme.key_text_RedBold) & 0x19ffffff, true); } - reportSpamButton.setTag(Theme.key_chat_reportSpam); + reportSpamButton.setTag(Theme.key_text_RedBold); } } @@ -23230,6 +23393,11 @@ public int getBottomOffset(int tag) { return height - AndroidUtilities.dp(1.5f); } + @Override + public int getTopOffset(int tag) { + return (actionBar != null ? actionBar.getMeasuredHeight() + (int) actionBar.getTranslationY() : 0) + Math.max(0, contentPaddingTop); + } + @Override public boolean allowLayoutChanges() { return false; @@ -23351,7 +23519,7 @@ public void onPause() { } int replyId = threadMessageId; getMessagesController().markDialogAsReadNow(dialog_id, replyId); - MediaController.getInstance().stopRaiseToEarSensors(this, true); + MediaController.getInstance().stopRaiseToEarSensors(this, true, true); paused = true; wasPaused = true; if (chatMode == 0) { @@ -24010,7 +24178,7 @@ private boolean createMenu(View v, boolean single, boolean listView, float x, fl selectedMessagesIds[a].clear(); } hideActionMode(); - updatePinnedMessageView(true); +// updatePinnedMessageView(true); MessageObject.GroupedMessages groupedMessages; if (searchGroup) { @@ -24036,11 +24204,11 @@ private boolean createMenu(View v, boolean single, boolean listView, float x, fl } else { allowPin = false; } - allowPin = allowPin && message.getId() > 0 && (message.messageOwner.action == null || message.messageOwner.action instanceof TLRPC.TL_messageActionEmpty); + allowPin = allowPin && message.getId() > 0 && (message.messageOwner.action == null || message.messageOwner.action instanceof TLRPC.TL_messageActionEmpty) && !message.isExpiredStory() && message.type != MessageObject.TYPE_STORY_MENTION; boolean noforwards = getMessagesController().isChatNoForwards(currentChat) || message.messageOwner.noforwards; boolean noforwardsOverride = noforwards && !NekoXConfig.disableFlagSecure && !NaConfig.INSTANCE.getForceCopy().Bool(); - boolean allowUnpin = message.getDialogId() != mergeDialogId && allowPin && (pinnedMessageObjects.containsKey(message.getId()) || groupedMessages != null && !groupedMessages.messages.isEmpty() && pinnedMessageObjects.containsKey(groupedMessages.messages.get(0).getId())); - boolean allowEdit = message.canEditMessage(currentChat) && !chatActivityEnterView.hasAudioToSend() && message.getDialogId() != mergeDialogId; + boolean allowUnpin = message.getDialogId() != mergeDialogId && allowPin && (pinnedMessageObjects.containsKey(message.getId()) || groupedMessages != null && !groupedMessages.messages.isEmpty() && pinnedMessageObjects.containsKey(groupedMessages.messages.get(0).getId())) && !message.isExpiredStory(); + boolean allowEdit = message.canEditMessage(currentChat) && !chatActivityEnterView.hasAudioToSend() && message.getDialogId() != mergeDialogId && message.type != MessageObject.TYPE_STORY; if (allowEdit && groupedMessages != null) { int captionsCount = 0; for (int a = 0, N = groupedMessages.messages.size(); a < N; a++) { @@ -24054,7 +24222,7 @@ private boolean createMenu(View v, boolean single, boolean listView, float x, fl } allowEdit = captionsCount < 2; } - if (chatMode == MODE_SCHEDULED || threadMessageObjects != null && threadMessageObjects.contains(message) || + if (message.isExpiredStory() || chatMode == MODE_SCHEDULED || threadMessageObjects != null && threadMessageObjects.contains(message) || message.isSponsored() || type == 1 && message.getDialogId() == mergeDialogId || message.messageOwner.action instanceof TLRPC.TL_messageActionSecureValuesSent || currentEncryptedChat == null && message.getId() < 0 || @@ -24159,7 +24327,7 @@ public void setAutoDeleteHistory(int time, int action) { icons.add(R.drawable.msg_block2); } if (type == -1) { - if ((selectedObject.type == MessageObject.TYPE_TEXT || selectedObject.isAnimatedEmoji() || selectedObject.isAnimatedEmojiStickers() || getMessageCaption(selectedObject, selectedObjectGroup) != null) && !noforwardsOverride) { + if ((selectedObject.type == MessageObject.TYPE_TEXT || selectedObject.isAnimatedEmoji() || selectedObject.isAnimatedEmojiStickers() || getMessageCaption(selectedObject, selectedObjectGroup) != null) && !noforwardsOverride && !message.isExpiredStory()) { items.add(LocaleController.getString("Copy", R.string.Copy)); options.add(OPTION_COPY); icons.add(R.drawable.msg_copy); @@ -24181,8 +24349,8 @@ public void setAutoDeleteHistory(int time, int action) { options.add(OPTION_REPLY); icons.add(R.drawable.msg_reply); } - if (!isThreadChat() && chatMode != MODE_SCHEDULED && message.hasReplies() && currentChat.megagroup && message.canViewThread()) { - items.add(LocaleController.formatPluralString("ViewReplies", message.getRepliesCount())); + if (!isThreadChat() && chatMode != MODE_SCHEDULED && primaryMessage != null && primaryMessage.hasReplies() && currentChat.megagroup && primaryMessage.canViewThread()) { + items.add(LocaleController.formatPluralString("ViewReplies", primaryMessage.getRepliesCount())); options.add(OPTION_VIEW_REPLIES_OR_THREAD); icons.add(R.drawable.msg_viewreplies); } @@ -24274,9 +24442,9 @@ public void setAutoDeleteHistory(int time, int action) { options.add(OPTION_COPY); icons.add(R.drawable.msg_copy); } - if (!isThreadChat() && chatMode != MODE_SCHEDULED && currentChat != null && (currentChat.has_link || message.hasReplies()) && currentChat.megagroup && message.canViewThread()) { - if (message.hasReplies()) { - items.add(LocaleController.formatPluralString("ViewReplies", message.getRepliesCount())); + if (!isThreadChat() && chatMode != MODE_SCHEDULED && currentChat != null && primaryMessage != null && (currentChat.has_link || primaryMessage.hasReplies()) && currentChat.megagroup && primaryMessage.canViewThread()) { + if (primaryMessage.hasReplies()) { + items.add(LocaleController.formatPluralString("ViewReplies", primaryMessage.getRepliesCount())); } else { items.add(LocaleController.getString("ViewThread", R.string.ViewThread)); } @@ -24505,7 +24673,8 @@ public void setAutoDeleteHistory(int time, int action) { boolean noforwardOverride = noforward && !NekoXConfig.disableFlagSecure && !NaConfig.INSTANCE.getForceCopy().Bool(); if (!selectedObject.isSponsored() && chatMode != MODE_SCHEDULED && (!selectedObject.needDrawBluredPreview() || selectedObject.hasExtendedMediaPreview()) && !selectedObject.isLiveLocation() && selectedObject.type != MessageObject.TYPE_PHONE_CALL && !noforwards && - selectedObject.type != MessageObject.TYPE_GIFT_PREMIUM && selectedObject.type != MessageObject.TYPE_SUGGEST_PHOTO) { + selectedObject.type != MessageObject.TYPE_GIFT_PREMIUM && selectedObject.type != MessageObject.TYPE_SUGGEST_PHOTO && !selectedObject.isWallpaperAction() + && !message.isExpiredStory() && message.type != MessageObject.TYPE_STORY_MENTION) { items.add(LocaleController.getString("Forward", R.string.Forward)); options.add(OPTION_FORWARD); icons.add(R.drawable.msg_forward); @@ -24693,9 +24862,9 @@ public void setAutoDeleteHistory(int time, int action) { options.add(OPTION_COPY); icons.add(R.drawable.msg_copy); } - if (!isThreadChat() && chatMode != MODE_SCHEDULED && currentChat != null && (currentChat.has_link || message.hasReplies()) && currentChat.megagroup && message.canViewThread()) { - if (message.hasReplies()) { - items.add(LocaleController.formatPluralString("ViewReplies", message.getRepliesCount())); + if (!isThreadChat() && chatMode != MODE_SCHEDULED && currentChat != null && primaryMessage != null && (currentChat.has_link || primaryMessage.hasReplies()) && currentChat.megagroup && primaryMessage.canViewThread()) { + if (primaryMessage.hasReplies()) { + items.add(LocaleController.formatPluralString("ViewReplies", primaryMessage.getRepliesCount())); } else { items.add(LocaleController.getString("ViewThread", R.string.ViewThread)); } @@ -24906,8 +25075,8 @@ public void setAutoDeleteHistory(int time, int action) { } else { isReactionsAvailable = nekoXShowReactionsView && !message.isSecretMedia() && !isSecretChat() && !isInScheduleMode() && message.isReactionsAvailable() && (chatInfo != null && !(chatInfo.available_reactions instanceof TLRPC.TL_chatReactionsNone) || (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)) && !ChatObject.isForum(currentChat) && chatInfo != null && chatInfo.participants_count <= getMessagesController().chatReadMarkSizeThreshold && !(message.messageOwner.action instanceof TLRPC.TL_messageActionChatJoinedByRequest) && (v instanceof ChatMessageCell); - boolean showSponsorInfo = selectedObject != null && selectedObject.isSponsored() && (selectedObject.sponsoredInfo != null || selectedObject.sponsoredAdditionalInfo != null); + 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 showSponsorInfo = selectedObject != null && selectedObject.isSponsored() && (selectedObject.sponsoredInfo != null || selectedObject.sponsoredAdditionalInfo != null || selectedObject.sponsoredWebPage != null || selectedObject.sponsoredWebPage != null); int flags = 0; if (isReactionsViewAvailable || showMessageSeen || showSponsorInfo) { @@ -25048,7 +25217,11 @@ public void dismiss() { }) .setOnProfileSelectedListener((view, userId, messagePeerReaction) -> { Bundle args = new Bundle(); - args.putLong("user_id", userId); + if (userId > 0) { + args.putLong("user_id", userId); + } else { + args.putLong("chat_id", -userId); + } args.putInt("report_reaction_message_id", message.getId()); args.putLong("report_reaction_from_dialog_id", dialog_id); ProfileActivity fragment = new ProfileActivity(args); @@ -25133,7 +25306,11 @@ public void dismiss() { }) .setOnProfileSelectedListener((view, userId, messagePeerReaction) -> { Bundle args = new Bundle(); - args.putLong("user_id", userId); + if (userId > 0) { + args.putLong("user_id", userId); + } else { + args.putLong("chat_id", -userId); + } args.putInt("report_reaction_message_id", message.getId()); args.putLong("report_reaction_from_dialog_id", dialog_id); ProfileActivity fragment = new ProfileActivity(args); @@ -25192,12 +25369,16 @@ public void onClick(View view) { return; } if (finalMessageSeenView.users.size() == 1 && (finalMessageSeenView.dates.size() <= 0 || finalMessageSeenView.dates.get(0) <= 0)) { - TLRPC.User user = finalMessageSeenView.users.get(0); - if (user == null) { + TLObject object = finalMessageSeenView.users.get(0); + if (object == null) { return; } Bundle args = new Bundle(); - args.putLong("user_id", user.id); + if (object instanceof TLRPC.User) { + args.putLong("user_id", ((TLRPC.User) object).id); + } else if (object instanceof TLRPC.Chat) { + args.putLong("chat_id", ((TLRPC.Chat) object).id); + } ProfileActivity fragment = new ProfileActivity(args); presentFragment(fragment); closeMenu(); @@ -25220,13 +25401,17 @@ public void onClick(View view) { linearLayout.addView(listView2, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); listView2.setOnItemClickListener((view1, position) -> { - TLRPC.User user = finalMessageSeenView.users.get(position); - if (user == null) { + TLObject object = finalMessageSeenView.users.get(position); + if (object == null) { return; } closeMenu(true); Bundle args = new Bundle(); - args.putLong("user_id", user.id); + if (object instanceof TLRPC.User) { + args.putLong("user_id", ((TLRPC.User) object).id); + } else if (object instanceof TLRPC.Chat) { + args.putLong("chat_id", ((TLRPC.Chat) object).id); + } ProfileActivity fragment = new ProfileActivity(args); presentFragment(fragment); }); @@ -25364,9 +25549,8 @@ public void run() { popupLayout.addView(rateTranscriptionLayout, rateTranscriptionLayoutParams); } - final boolean translateButtonEnabled = MessagesController.getInstance(currentAccount).getTranslateController().isContextTranslateEnabled(); if (selectedObject != null && selectedObject.isSponsored()) { - if (selectedObject.sponsoredInfo != null || selectedObject.sponsoredAdditionalInfo != null) { + if (selectedObject.sponsoredInfo != null || selectedObject.sponsoredAdditionalInfo != null || selectedObject.sponsoredWebPage != null) { LinearLayout linearLayout = new LinearLayout(getParentActivity()); linearLayout.setOrientation(LinearLayout.VERTICAL); @@ -25379,6 +25563,36 @@ public void run() { linearLayout.addView(new ActionBarPopupWindow.GapView(contentView.getContext(), themeDelegate), LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 8)); + ArrayList sections = new ArrayList<>(); + + if (selectedObject.sponsoredWebPage != null) { + TextView textView = new TextView(getParentActivity()); + textView.setTextColor(getThemedColor(Theme.key_chat_messageLinkIn)); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + textView.setPadding(AndroidUtilities.dp(18), AndroidUtilities.dp(10), AndroidUtilities.dp(18), AndroidUtilities.dp(10)); + textView.setMaxWidth(AndroidUtilities.dp(300)); + Uri uri = Uri.parse(selectedObject.sponsoredWebPage.url); + textView.setText(Browser.replaceHostname(uri, IDN.toUnicode(uri.getHost(), IDN.ALLOW_UNASSIGNED))); + textView.setBackground(Theme.createRadSelectorDrawable(getThemedColor(Theme.key_dialogButtonSelector), 0, selectedObject.sponsoredAdditionalInfo == null ? 6 : 0)); + textView.setOnClickListener(e -> { + if (selectedObject == null) { + return; + } + logSponsoredClicked(selectedObject); + Browser.openUrl(getContext(), selectedObject.sponsoredWebPage.url, true, false); + }); + textView.setOnLongClickListener(e -> { + if (selectedObject == null) { + return false; + } + if (AndroidUtilities.addToClipboard(selectedObject.sponsoredWebPage.url)) { + BulletinFactory.of(Bulletin.BulletinWindow.make(getParentActivity()), themeDelegate).createCopyLinkBulletin().show(); + } + return true; + }); + sections.add(textView); + } + if (selectedObject.sponsoredInfo != null) { TextView textView = new TextView(getParentActivity()); textView.setTextColor(getThemedColor(Theme.key_actionBarDefaultSubmenuItem)); @@ -25386,21 +25600,13 @@ public void run() { textView.setPadding(AndroidUtilities.dp(18), AndroidUtilities.dp(10), AndroidUtilities.dp(18), AndroidUtilities.dp(10)); textView.setMaxWidth(AndroidUtilities.dp(300)); textView.setText(selectedObject.sponsoredInfo); - textView.setBackground(Theme.createRadSelectorDrawable(getThemedColor(Theme.key_listSelector), 0, selectedObject.sponsoredAdditionalInfo == null ? 6 : 0)); + textView.setBackground(Theme.createRadSelectorDrawable(getThemedColor(Theme.key_dialogButtonSelector), 0, selectedObject.sponsoredAdditionalInfo == null ? 6 : 0)); textView.setOnClickListener(e -> { if (AndroidUtilities.addToClipboard(selectedObject.sponsoredInfo)) { BulletinFactory.of(Bulletin.BulletinWindow.make(getParentActivity()), themeDelegate).createCopyBulletin(LocaleController.getString("TextCopied", R.string.TextCopied)).show(); } }); - linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - } - - if (selectedObject.sponsoredInfo != null && selectedObject.sponsoredAdditionalInfo != null) { - FrameLayout separator = new FrameLayout(getParentActivity()); - separator.setBackgroundColor(getThemedColor(Theme.key_divider)); - LinearLayout.LayoutParams params = LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 1); - params.height = 1; - linearLayout.addView(separator, params); + sections.add(textView); } if (selectedObject.sponsoredAdditionalInfo != null) { @@ -25410,13 +25616,25 @@ public void run() { textView.setPadding(AndroidUtilities.dp(18), AndroidUtilities.dp(10), AndroidUtilities.dp(18), AndroidUtilities.dp(10)); textView.setMaxWidth(AndroidUtilities.dp(300)); textView.setText(selectedObject.sponsoredAdditionalInfo); - textView.setBackground(Theme.createRadSelectorDrawable(getThemedColor(Theme.key_listSelector), 0, 6)); + textView.setBackground(Theme.createRadSelectorDrawable(getThemedColor(Theme.key_dialogButtonSelector), 0, 6)); textView.setOnClickListener(e -> { if (AndroidUtilities.addToClipboard(selectedObject.sponsoredAdditionalInfo)) { BulletinFactory.of(Bulletin.BulletinWindow.make(getParentActivity()), themeDelegate).createCopyBulletin(LocaleController.getString("TextCopied", R.string.TextCopied)).show(); } }); - linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + sections.add(textView); + } + + for (int i = 0; i < sections.size(); ++i) { + View section = sections.get(i); + if (i > 0) { + FrameLayout separator = new FrameLayout(getParentActivity()); + separator.setBackgroundColor(getThemedColor(Theme.key_divider)); + LinearLayout.LayoutParams params = LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 1); + params.height = 1; + linearLayout.addView(separator, params); + } + linearLayout.addView(section, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); } final int foregroundIndex = popupLayout.addViewToSwipeBack(linearLayout); @@ -25569,7 +25787,7 @@ public boolean onTouch(View v, MotionEvent event) { if (optionsView != null) { scrimPopupContainerLayout.addView(optionsView); } else { - reactionsLayout = new ReactionsContainerLayout(ChatActivity.this, contentView.getContext(), currentAccount, getResourceProvider()); + reactionsLayout = new ReactionsContainerLayout(ReactionsContainerLayout.TYPE_DEFAULT, ChatActivity.this, contentView.getContext(), currentAccount, getResourceProvider()); if (isReactionsAvailable) { int pad = 22; int sPad = 24; @@ -25787,7 +26005,7 @@ public void dismiss(boolean animated) { scrimPopupContainerLayout.setMaxHeight(totalHeight - popupY); ReactionsContainerLayout finalReactionsLayout = reactionsLayout; Runnable showMenu = () -> { - if (scrimPopupWindow == null || fragmentView == null || scrimPopupWindow.isShowing()) { + if (scrimPopupWindow == null || fragmentView == null || scrimPopupWindow.isShowing() || !AndroidUtilities.isActivityRunning(getParentActivity())) { return; } scrimPopupWindow.showAtLocation(chatListView, Gravity.LEFT | Gravity.TOP, finalPopupX, finalPopupY); @@ -25922,7 +26140,7 @@ private void createEmptyView() { greetingsViewContainer = new ChatGreetingsView(getContext(), currentUser, distance, currentAccount, preloadedGreetingsSticker, themeDelegate); greetingsViewContainer.setListener((sticker) -> { animatingDocuments.put(sticker, 0); - SendMessagesHelper.getInstance(currentAccount).sendSticker(sticker, null, dialog_id, null, null, null, null, true, 0, false); + SendMessagesHelper.getInstance(currentAccount).sendSticker(sticker, null, dialog_id, null, null, null, null, true, 0, false, null); }); 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)); @@ -25955,7 +26173,7 @@ private void createEmptyView() { greetingsViewContainer = new ChatGreetingsView(getContext(), currentUser, distance, currentAccount, preloadedGreetingsSticker, themeDelegate); greetingsViewContainer.setListener((sticker) -> { animatingDocuments.put(sticker, 0); - SendMessagesHelper.getInstance(currentAccount).sendSticker(sticker, null, dialog_id, null, null, null, null, true, 0, false); + SendMessagesHelper.getInstance(currentAccount).sendSticker(sticker, null, dialog_id, null, null, null, null, true, 0, false, null); }); 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)); @@ -26697,7 +26915,7 @@ private void processSelectedOption(int option) { if (path == null || path.length() == 0) { path = getFileLoader().getPathToMessage(selectedObject.messageOwner).toString(); } - MediaController.saveFile(path, getParentActivity(), 2, fileName, selectedObject.getDocument() != null ? selectedObject.getDocument().mime_type : "", () -> { + MediaController.saveFile(path, getParentActivity(), 2, fileName, selectedObject.getDocument() != null ? selectedObject.getDocument().mime_type : "", uri -> { if (getParentActivity() == null || fragmentView == null) { return; } @@ -26912,7 +27130,7 @@ private void processSelectedOption(int option) { } } else { preserveDim = true; - AlertsCreator.createReportAlert(getParentActivity(), dialog_id, selectedObject.getId(), ChatActivity.this, themeDelegate, () -> dimBehindView(false)); + AlertsCreator.createReportAlert(getParentActivity(), dialog_id, selectedObject.getId(), 0, ChatActivity.this, themeDelegate, () -> dimBehindView(false)); } break; } @@ -27002,7 +27220,10 @@ private void processSelectedOption(int option) { break; } case OPTION_VIEW_REPLIES_OR_THREAD: { - openDiscussionMessageChat(currentChat.id, null, selectedObject.getId(), 0, -1, 0, null); + MessageObject message = selectedObjectGroup != null ? selectedObjectGroup.findPrimaryMessageObject() : selectedObject; + if (message != null) { + openDiscussionMessageChat(currentChat.id, null, message.getId(), 0, -1, 0, null); + } break; } case OPTION_VIEW_IN_TOPIC: { @@ -27212,11 +27433,11 @@ public boolean didSelectDialogs(DialogsActivity fragment, ArrayList audios, CharSequence caption, boolean notify, int scheduleDate) { if (checkSlowModeAlert()) { fillEditingMediaWithCaption(caption, null); - SendMessagesHelper.prepareSendingAudioDocuments(getAccountInstance(), audios, caption != null ? caption : null, dialog_id, replyingMessageObject, getThreadMessage(), editingMessageObject, notify, scheduleDate); + SendMessagesHelper.prepareSendingAudioDocuments(getAccountInstance(), audios, caption != null ? caption : null, dialog_id, replyingMessageObject, getThreadMessage(), null, notify, scheduleDate, editingMessageObject); afterMessageSend(); } } public void sendContact(TLRPC.User user, boolean notify, int scheduleDate) { if (checkSlowModeAlert()) { - getSendMessagesHelper().sendMessage(user, dialog_id, replyingMessageObject, getThreadMessage(), null, null, notify, scheduleDate); + getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of(user, dialog_id, replyingMessageObject, getThreadMessage(), null, null, notify, scheduleDate)); afterMessageSend(); } } public void sendPoll(TLRPC.TL_messageMediaPoll poll, HashMap params, boolean notify, int scheduleDate) { if (checkSlowModeAlert()) { - getSendMessagesHelper().sendMessage(poll, dialog_id, replyingMessageObject, getThreadMessage(), null, params, notify, scheduleDate); + getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of(poll, dialog_id, replyingMessageObject, getThreadMessage(), null, params, notify, scheduleDate)); afterMessageSend(); } } @@ -27713,24 +27936,58 @@ public void sendMedia(MediaController.PhotoEntry photoEntry, VideoEditedInfo vid if (photoEntry == null) { return; } + beforeMessageSend(notify, scheduleDate, true); + + if (videoEditedInfo != null && videoEditedInfo.roundVideo) { + AndroidUtilities.runOnUIThread(closeInstantCameraAnimation = () -> { + closeInstantCameraAnimation = null; + runCloseInstantCameraAnimation(); + }, 3000); + } fillEditingMediaWithCaption(photoEntry.caption, photoEntry.entities); if (photoEntry.isVideo) { if (videoEditedInfo != null) { - SendMessagesHelper.prepareSendingVideo(getAccountInstance(), photoEntry.path, videoEditedInfo, dialog_id, replyingMessageObject, getThreadMessage(), photoEntry.caption, photoEntry.entities, photoEntry.ttl, editingMessageObject, notify, scheduleDate, forceDocument, photoEntry.hasSpoiler); + SendMessagesHelper.prepareSendingVideo(getAccountInstance(), photoEntry.path, videoEditedInfo, dialog_id, replyingMessageObject, getThreadMessage(), null, photoEntry.entities, photoEntry.ttl, editingMessageObject, notify, scheduleDate, forceDocument, photoEntry.hasSpoiler, photoEntry.caption); } else { - SendMessagesHelper.prepareSendingVideo(getAccountInstance(), photoEntry.path, null, dialog_id, replyingMessageObject, getThreadMessage(), photoEntry.caption, photoEntry.entities, photoEntry.ttl, editingMessageObject, notify, scheduleDate, forceDocument, photoEntry.hasSpoiler); + SendMessagesHelper.prepareSendingVideo(getAccountInstance(), photoEntry.path, null, dialog_id, replyingMessageObject, getThreadMessage(), null, photoEntry.entities, photoEntry.ttl, editingMessageObject, notify, scheduleDate, forceDocument, photoEntry.hasSpoiler, photoEntry.caption); } } else { if (photoEntry.imagePath != null) { - SendMessagesHelper.prepareSendingPhoto(getAccountInstance(), photoEntry.imagePath, photoEntry.thumbPath, null, dialog_id, replyingMessageObject, getThreadMessage(), photoEntry.caption, photoEntry.entities, photoEntry.stickers, null, photoEntry.ttl, editingMessageObject, videoEditedInfo, notify, scheduleDate, forceDocument); + SendMessagesHelper.prepareSendingPhoto(getAccountInstance(), photoEntry.imagePath, photoEntry.thumbPath, null, dialog_id, replyingMessageObject, getThreadMessage(), null, photoEntry.entities, photoEntry.stickers, null, photoEntry.ttl, editingMessageObject, videoEditedInfo, notify, scheduleDate, forceDocument, photoEntry.caption); } else if (photoEntry.path != null) { - SendMessagesHelper.prepareSendingPhoto(getAccountInstance(), photoEntry.path, photoEntry.thumbPath, null, dialog_id, replyingMessageObject, getThreadMessage(), photoEntry.caption, photoEntry.entities, photoEntry.stickers, null, photoEntry.ttl, editingMessageObject, videoEditedInfo, notify, scheduleDate, forceDocument); + SendMessagesHelper.prepareSendingPhoto(getAccountInstance(), photoEntry.path, photoEntry.thumbPath, null, dialog_id, replyingMessageObject, getThreadMessage(), null, photoEntry.entities, photoEntry.stickers, null, photoEntry.ttl, editingMessageObject, videoEditedInfo, notify, scheduleDate, forceDocument, photoEntry.caption); } } afterMessageSend(); } + private void runCloseInstantCameraAnimation() { + instantCameraView.cancelBlur(); + + final InstantCameraView.InstantViewCameraContainer cameraContainer = instantCameraView.getCameraContainer(); + AnimatorSet allAnimators = new AnimatorSet(); + allAnimators.playTogether( + ObjectAnimator.ofFloat(cameraContainer, View.SCALE_X, 0.5f), + ObjectAnimator.ofFloat(cameraContainer, View.SCALE_Y, 0.5f), + ObjectAnimator.ofFloat(cameraContainer, View.ALPHA, 0.0f), + ObjectAnimator.ofFloat(instantCameraView.getSwitchButtonView(), View.ALPHA, 0.0f), + ObjectAnimator.ofInt(instantCameraView.getPaint(), AnimationProperties.PAINT_ALPHA, 0), + ObjectAnimator.ofFloat(instantCameraView.getMuteImageView(), View.ALPHA, 0.0f) + ); + allAnimators.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (instantCameraView != null) { + instantCameraView.setIsMessageTransition(false); + instantCameraView.hideCamera(true); + instantCameraView.setVisibility(View.INVISIBLE); + } + } + }); + allAnimators.start(); + } + public void sendAnimatedEmoji(TLRPC.Document emoji, boolean notify, int scheduleDate) { if (emoji == null) { return; @@ -27746,7 +28003,7 @@ public void sendAnimatedEmoji(TLRPC.Document emoji, boolean notify, int schedule entity.offset = 0; entity.length = message.length(); entities.add(entity); - SendMessagesHelper.getInstance(currentAccount).sendMessage(message, dialog_id, replyingMessageObject, getThreadMessage(), null, false, entities, null, null, notify, scheduleDate, null, false); + SendMessagesHelper.getInstance(currentAccount).sendMessage(SendMessagesHelper.SendMessageParams.of(message, dialog_id, replyingMessageObject, getThreadMessage(), null, false, entities, null, null, notify, scheduleDate, null, false)); afterMessageSend(); } @@ -27882,7 +28139,7 @@ public void didReceivedNotification(int id, int account, Object... args) { }; NotificationCenter.getInstance(currentAccount).addObserver(observer, NotificationCenter.messagesDidLoad); Utilities.stageQueue.postRunnable(() -> { - getMessagesController().processLoadedMessages(historyFinal, historyFinal.messages.size(), dialogId, 0, 30, (highlightMsgId > 0 ? highlightMsgId : maxReadId), 0, false, commentsClassGuid, fnidFinal, 0, 0, 0, (highlightMsgId > 0 ? 3 : 2), true, 0, arrayList.get(arrayList.size() - 1).getId(), 1, false, 0, true, isTopic); + getMessagesController().processLoadedMessages(historyFinal, historyFinal.messages.size(), dialogId, 0, 30, (highlightMsgId > 0 ? highlightMsgId : maxReadId), 0, false, commentsClassGuid, fnidFinal, 0, 0, 0, (highlightMsgId > 0 ? 3 : 2), true, 0, arrayList.get(arrayList.size() - 1).getId(), 1, false, 0, true, isTopic, null); }); } else { openCommentsChat.run(); @@ -28196,7 +28453,13 @@ private void openClickableLink(CharacterStyle url, String str, boolean longPress } else { String formattedUrl = str; try { - formattedUrl = URLDecoder.decode(str.replaceAll("\\+", "%2b"), "UTF-8"); + try { + Uri uri = Uri.parse(formattedUrl); + formattedUrl = Browser.replaceHostname(uri, IDN.toUnicode(uri.getHost(), IDN.ALLOW_UNASSIGNED)); + } catch (Exception e) { + FileLog.e(e, false); + } + formattedUrl = URLDecoder.decode(formattedUrl.replaceAll("\\+", "%2b"), "UTF-8"); } catch (Exception e) { FileLog.e(e); } @@ -28214,6 +28477,7 @@ private void openClickableLink(CharacterStyle url, String str, boolean longPress if (str.startsWith("video?")) { didPressMessageUrl(url, false, messageObject, cell); } else { + logSponsoredClicked(messageObject); openClickableLink(url, str, false, cell, messageObject); } } else if (which == 1) { @@ -28278,6 +28542,7 @@ private void openClickableLink(CharacterStyle url, String str, boolean longPress }); showDialog(builder.create()); } else { + logSponsoredClicked(messageObject); String username = Browser.extractUsername(str); if (username != null) { username = username.toLowerCase(); @@ -28384,7 +28649,6 @@ public void end(boolean replaced) { private void processExternalUrl(int type, String url, CharacterStyle span, ChatMessageCell cell, boolean forceAlert) { try { - String host = AndroidUtilities.getHostAuthority(url); if ((currentEncryptedChat == null || getMessagesController().secretWebpagePreview == 1) && getMessagesController().authDomains.contains(host)) { getSendMessagesHelper().requestUrlAuth(url, this, type == 0 || type == 2); @@ -28395,7 +28659,11 @@ private void processExternalUrl(int type, String url, CharacterStyle span, ChatM } if (forceAlert || AndroidUtilities.shouldShowUrlInAlert(url)) { if (type == 0 || type == 2) { - AlertsCreator.showOpenUrlAlert(ChatActivity.this, url, true, true, true, makeProgressForLink(cell, span), themeDelegate); + boolean forceNotInternalForApps = false; + if (span instanceof URLSpanReplacement && (((URLSpanReplacement) span).getTextStyleRun().flags & TextStyleSpan.FLAG_STYLE_TEXT_URL) != 0) { + forceNotInternalForApps = true; + } + AlertsCreator.showOpenUrlAlert(ChatActivity.this, url, true, true, true, forceNotInternalForApps, makeProgressForLink(cell, span), themeDelegate); } else if (type == 1) { AlertsCreator.showOpenUrlAlert(ChatActivity.this, url, true, true, false, makeProgressForLink(cell, span), themeDelegate); } @@ -28410,6 +28678,16 @@ private void processExternalUrl(int type, String url, CharacterStyle span, ChatM } } + private void logSponsoredClicked(MessageObject messageObject) { + if (messageObject == null || !messageObject.isSponsored()) { + return; + } + TLRPC.TL_channels_clickSponsoredMessage req = new TLRPC.TL_channels_clickSponsoredMessage(); + req.random_id = messageObject.sponsoredId; + req.channel = getMessagesController().getInputChannel(-getDialogId()); + getConnectionsManager().sendRequest(req, null); + } + private void didPressMessageUrl(CharacterStyle url, boolean longPress, MessageObject messageObject, ChatMessageCell cell) { if (url == null || getParentActivity() == null) { return; @@ -28435,6 +28713,7 @@ private void didPressMessageUrl(CharacterStyle url, boolean longPress, MessageOb if (longPress && cell != null) { cell.resetPressedLink(-1); } + logSponsoredClicked(messageObject); } else if (url instanceof URLSpanNoUnderline) { String str = ((URLSpanNoUnderline) url).getURL(); if (messageObject != null && str.startsWith("/")) { @@ -28559,7 +28838,13 @@ private void didPressMessageUrl(CharacterStyle url, boolean longPress, MessageOb BottomBuilder builder = new BottomBuilder(getParentActivity(), false); String formattedUrl = urlFinal; try { - formattedUrl = URLDecoder.decode(urlFinal.replaceAll("\\+", "%2b"), "UTF-8"); + try { + Uri uri = Uri.parse(formattedUrl); + formattedUrl = Browser.replaceHostname(uri, IDN.toUnicode(uri.getHost(), IDN.ALLOW_UNASSIGNED)); + } catch (Exception e) { + FileLog.e(e, false); + } + formattedUrl = URLDecoder.decode(formattedUrl.replaceAll("\\+", "%2b"), "UTF-8"); } catch (Exception e) { FileLog.e(e); } @@ -28579,6 +28864,7 @@ private void didPressMessageUrl(CharacterStyle url, boolean longPress, MessageOb }, (which, text, __) -> { if (which == 0) { + logSponsoredClicked(finalMessageObject); processExternalUrl(1, urlFinal, url, finalCell, false); } else if (which == 1 || which == 3) { // Copy / ShareMessage @@ -28622,6 +28908,7 @@ private void didPressMessageUrl(CharacterStyle url, boolean longPress, MessageOb }); showDialog(builder.create()); } else { + logSponsoredClicked(messageObject); boolean forceAlert = url instanceof URLSpanReplacement; if (url instanceof URLSpanReplacement && (urlFinal == null || !urlFinal.startsWith("mailto:")) || AndroidUtilities.shouldShowUrlInAlert(urlFinal)) { if (openLinkInternally(urlFinal, cell, url, messageObject != null ? messageObject.getId() : 0)) { @@ -28854,7 +29141,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType view = chatMessageCellsCache.get(0); chatMessageCellsCache.remove(0); } else { - view = new ChatMessageCell(mContext, true, themeDelegate); + view = new ChatMessageCell(mContext, true, sharedResources, themeDelegate); } ChatMessageCell chatMessageCell = (ChatMessageCell) view; chatMessageCell.setResourcesProvider(themeDelegate); @@ -28894,6 +29181,25 @@ public void didClickImage(ChatActionCell cell) { TLRPC.PhotoSize photoSize = FileLoader.getClosestPhotoSizeWithSize(message.photoThumbs, 640); TLRPC.VideoSize videoSize = null; TLRPC.VideoSize emojiMarkup = null; + if (cell.getMessageObject().type == MessageObject.TYPE_STORY_MENTION) { + getOrCreateStoryViewer().openFor(ChatActivity.this, chatListView, cell); + return; + } + if (cell.getMessageObject().type == MessageObject.TYPE_ACTION_WALLPAPER) { + + MessagesController messagesController = MessagesController.getInstance(currentAccount); + if (message.getId() < 0 && messagesController.uploadingWallpaper != null && TextUtils.equals(message.messageOwner.action.wallpaper.uploadingImage, messagesController.uploadingWallpaper)) { + messagesController.cancelUploadWallpaper(); + removeMessageObject(message); + return; + } + if (cell.hasButton()) { + ThemePreviewActivity.showFor(ChatActivity.this, message); + } else { + showChatThemeBottomSheet(); + } + return; + } if (message.messageOwner.action.photo.video_sizes != null && !message.messageOwner.action.photo.video_sizes.isEmpty()) { videoSize = FileLoader.getClosestVideoSizeWithSize(message.messageOwner.action.photo.video_sizes, 1000); emojiMarkup = FileLoader.getEmojiMarkup(message.messageOwner.action.photo.video_sizes); @@ -28959,7 +29265,7 @@ public void sendButtonPressed(int index, VideoEditedInfo videoEditedInfo, boolea args.putLong("user_id", UserConfig.getInstance(currentAccount).clientUserId); presentFragment(new ProfileActivity(args)); }); - BulletinFactory.of(ChatActivity.this).createUsersBulletin(Collections.singletonList(user), title, subtitle).show(); + BulletinFactory.of(ChatActivity.this).createUsersBulletin(Collections.singletonList(user), title, subtitle, null).show(); } } } @@ -29238,6 +29544,10 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { boolean applyAnimation = false; if (message.type == MessageObject.TYPE_ROUND_VIDEO && instantCameraView != null && instantCameraView.getTextureView() != null) { applyAnimation = true; + if (closeInstantCameraAnimation != null) { + AndroidUtilities.cancelRunOnUIThread(closeInstantCameraAnimation); + closeInstantCameraAnimation = null; + } messageCell.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { @@ -29491,39 +29801,7 @@ public int getItemViewType(int position) { @Override public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) { if (holder.itemView instanceof ChatMessageCell || holder.itemView instanceof ChatActionCell) { - View view = holder.itemView; - holder.itemView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { - @Override - public boolean onPreDraw() { - view.getViewTreeObserver().removeOnPreDrawListener(this); - - int height = chatListView.getMeasuredHeight(); - int top = view.getTop(); - int bottom = view.getBottom(); - int viewTop = top >= 0 ? 0 : -top; - int viewBottom = view.getMeasuredHeight(); - if (viewBottom > height) { - viewBottom = viewTop + height; - } - int recyclerChatViewHeight = (contentView.getHeightWithKeyboard() - (inPreviewMode ? 0 : AndroidUtilities.dp(48)) - chatListView.getTop()); - int keyboardOffset = contentView.getKeyboardHeight(); - int parentHeight = viewBottom - viewTop; - if (keyboardOffset < AndroidUtilities.dp(20) && chatActivityEnterView.isPopupShowing() || chatActivityEnterView.panelAnimationInProgress()) { - keyboardOffset = chatActivityEnterView.getEmojiPadding(); - } - if (holder.itemView instanceof ChatMessageCell) { - ChatMessageCell chatMessageCell = (ChatMessageCell) view; - chatMessageCell.setVisiblePart(viewTop, viewBottom - viewTop, recyclerChatViewHeight, keyboardOffset, view.getY() + (isKeyboardVisible() ? chatListView.getTop() : actionBar.getMeasuredHeight()) - contentView.getBackgroundTranslationY(), contentView.getMeasuredWidth(), contentView.getBackgroundSizeY(), blurredViewTopOffset, blurredViewBottomOffset); - markSponsoredAsRead(chatMessageCell.getMessageObject()); - } else if (holder.itemView instanceof ChatActionCell) { - if (actionBar != null && contentView != null) { - ((ChatActionCell) view).setVisiblePart(view.getY() + (isKeyboardVisible() ? chatListView.getTop() : actionBar.getMeasuredHeight()) - contentView.getBackgroundTranslationY(), contentView.getBackgroundSizeY()); - } - } - - return true; - } - }); + invalidateMessagesVisiblePart(); } if (holder.itemView instanceof ChatMessageCell) { final ChatMessageCell messageCell = (ChatMessageCell) holder.itemView; @@ -29678,7 +29956,7 @@ public void invalidateRowWithMessageObject(MessageObject messageObject) { } } - public View updateRowWithMessageObject(MessageObject messageObject, boolean allowInPlace) { + public View updateRowWithMessageObject(MessageObject messageObject, boolean allowInPlace, boolean replace) { if (allowInPlace) { int count = chatListView.getChildCount(); for (int a = 0; a < count; a++) { @@ -29698,7 +29976,12 @@ public View updateRowWithMessageObject(MessageObject messageObject, boolean allo if (index == -1) { return null; } - updateRowAtPosition(index + messagesStartRow); + if (replace) { + messageObject.stableId = lastStableId++; + notifyDataSetChanged(true); + } else { + updateRowAtPosition(index + messagesStartRow); + } return null; } @@ -30537,7 +30820,11 @@ public void dismiss() { }) .setOnProfileSelectedListener((view1, userId, messagePeerReaction) -> { Bundle args = new Bundle(); - args.putLong("user_id", userId); + if (userId > 0) { + args.putLong("user_id", userId); + } else { + args.putLong("chat_id", -userId); + } args.putInt("report_reaction_message_id", cell.getMessageObject().getId()); args.putLong("report_reaction_from_dialog_id", dialog_id); ProfileActivity fragment = new ProfileActivity(args); @@ -30701,7 +30988,7 @@ public boolean didPressAnimatedEmoji(ChatMessageCell cell, AnimatedEmojiSpan spa if (document == null) { return false; } - Bulletin bulletin = BulletinFactory.of(ChatActivity.this).createContainsEmojiBulletin(document, false, set -> { + Bulletin bulletin = BulletinFactory.of(ChatActivity.this).createContainsEmojiBulletin(document, BulletinFactory.CONTAINS_EMOJI_IN_MESSAGE, set -> { ArrayList inputSets = new ArrayList<>(1); inputSets.add(set); EmojiPacksAlert alert = new EmojiPacksAlert(ChatActivity.this, getParentActivity(), themeDelegate, inputSets); @@ -30755,7 +31042,18 @@ public void didPressReplyMessage(ChatMessageCell cell, int id) { return; } MessageObject messageObject = cell.getMessageObject(); - if (chatMode == MODE_PINNED || chatMode == MODE_SCHEDULED) { + if (messageObject.isReplyToStory() && messageObject.messageOwner.replyStory != null) { + if (messageObject.messageOwner.replyStory instanceof TLRPC.TL_storyItemDeleted) { + BulletinFactory.of(ChatActivity.this).createSimpleBulletin(R.raw.story_bomb1, LocaleController.getString("StoryNotFound", R.string.StoryNotFound)).show(); + } else { + TLRPC.StoryItem storyItem = messageObject.messageOwner.replyStory; + storyItem.dialogId = messageObject.messageOwner.reply_to.user_id; + storyItem.messageId = messageObject.getId(); + storyItem.messageType = 3; + StoriesUtilities.applyViewedUser(storyItem, currentUser); + getOrCreateStoryViewer().open(getContext(), storyItem, StoriesListPlaceProvider.of(chatListView)); + } + } else if (chatMode == MODE_PINNED || chatMode == MODE_SCHEDULED) { chatActivityDelegate.openReplyMessage(id); finishFragment(); } else { @@ -30822,7 +31120,17 @@ public void needReloadPolls() { @Override public void didPressImage(ChatMessageCell cell, float x, float y) { MessageObject message = cell.getMessageObject(); - if (message.isVideo()) { + if (message.type == MessageObject.TYPE_STORY) { + if (message.messageOwner.media.storyItem != null && !(message.messageOwner.media.storyItem instanceof TLRPC.TL_storyItemDeleted)) { + TLRPC.StoryItem storyItem = message.messageOwner.media.storyItem; + storyItem.dialogId = message.messageOwner.media.user_id; + storyItem.messageId = message.getId(); + storyItem.messageType = 2; + StoriesUtilities.applyViewedUser(storyItem, currentUser); + getOrCreateStoryViewer().open(getContext(), message.messageOwner.media.storyItem, StoriesListPlaceProvider.of(chatListView)); + } + return; + } else if (message.isVideo()) { if (DownloadController.getInstance(currentAccount).canDownloadMedia(message.messageOwner) == 1) { message.putInDownloadsStore = true; } @@ -30842,7 +31150,7 @@ public void didPressImage(ChatMessageCell cell, float x, float y) { } undoView.showWithAction(0, chatActivityEnterView.getVisibility() == View.VISIBLE && bottomOverlay.getVisibility() != View.VISIBLE ? UndoView.ACTION_DICE_INFO : UndoView.ACTION_DICE_NO_SEND_INFO, message.getDiceEmoji(), null, () -> { if (checkSlowModeAlert()) { - getSendMessagesHelper().sendMessage(message.getDiceEmoji(), dialog_id, replyingMessageObject, getThreadMessage(), null, false, null, null, null, true, 0, null, false); + getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of(message.getDiceEmoji(), dialog_id, replyingMessageObject, getThreadMessage(), null, false, null, null, null, true, 0, null, false)); } }); } else if (message.isAnimatedEmoji() && (!message.isAnimatedAnimatedEmoji() || emojiAnimationsOverlay.supports(MessageObject.findAnimatedEmojiEmoticon(message.getDocument())) && currentUser != null) || message.isPremiumSticker()) { @@ -31009,50 +31317,77 @@ public void didPressImage(ChatMessageCell cell, float x, float y) { } } } - } - - @Override - public void didPressInstantButton(ChatMessageCell cell, int type) { - MessageObject messageObject = cell.getMessageObject(); - if (type == 8) { - PollVotesAlert.showForPoll(ChatActivity.this, messageObject); - } else if (type == 0) { - if (messageObject.messageOwner.media != null && messageObject.messageOwner.media.webpage != null && messageObject.messageOwner.media.webpage.cached_page != null) { - ArticleViewer.getInstance().setParentActivity(getParentActivity(), ChatActivity.this); - ArticleViewer.getInstance().open(messageObject); - } - } else if (type == 5) { - long uid = messageObject.messageOwner.media.user_id; - TLRPC.User user = null; - if (uid != 0) { - user = MessagesController.getInstance(currentAccount).getUser(uid); - } - openVCard(user, messageObject.messageOwner.media.phone_number, messageObject.messageOwner.media.vcard, messageObject.messageOwner.media.first_name, messageObject.messageOwner.media.last_name); - } else { - if (messageObject.isSponsored()) { - Bundle args = new Bundle();if (messageObject.sponsoredChatInvite != null) { - showDialog(new JoinGroupAlert(getContext(), messageObject.sponsoredChatInvite, messageObject.sponsoredChatInviteHash, ChatActivity.this, themeDelegate)); + @Override + public void didPressInstantButton(ChatMessageCell cell, int type) { + MessageObject messageObject = cell.getMessageObject(); + if (type == 8) { + PollVotesAlert.showForPoll(ChatActivity.this, messageObject); + } else if (type == 0) { + if (messageObject.messageOwner.media != null && messageObject.messageOwner.media.webpage != null && messageObject.messageOwner.media.webpage.cached_page != null) { + ArticleViewer.getInstance().setParentActivity(getParentActivity(), ChatActivity.this); + ArticleViewer.getInstance().open(messageObject); + } + } else if (type == 5) { + long uid = messageObject.messageOwner.media.user_id; + TLRPC.User user = null; + if (uid != 0) { + user = MessagesController.getInstance(currentAccount).getUser(uid); + } + openVCard(user, messageObject.messageOwner.media.phone_number, messageObject.messageOwner.media.vcard, messageObject.messageOwner.media.first_name, messageObject.messageOwner.media.last_name); + } else { + if (messageObject.isSponsored()) { + logSponsoredClicked(messageObject); + Bundle args = new Bundle(); + if (messageObject.sponsoredWebPage != null) { + Browser.openUrl(getContext(), messageObject.sponsoredWebPage.url, true, false); + } else if (messageObject.sponsoredChatInvite != null) { + showDialog(new JoinGroupAlert(getContext(), messageObject.sponsoredChatInvite, messageObject.sponsoredChatInviteHash, ChatActivity.this, themeDelegate)); } else { long peerId = MessageObject.getPeerId(messageObject.messageOwner.from_id); - if (peerId < 0) { - args.putLong("chat_id", -peerId); + if (peerId == getDialogId() && messageObject.sponsoredChannelPost != 0) { + scrollToMessageId(messageObject.sponsoredChannelPost, 0, true, 0, false, 0); } else { - args.putLong("user_id", peerId); - } - if (messageObject.sponsoredChannelPost != 0) { - args.putInt("message_id", messageObject.sponsoredChannelPost); - } - if (messageObject.botStartParam != null) { - args.putString("inline_query", messageObject.botStartParam); + if (peerId < 0) { + args.putLong("chat_id", -peerId); + } else { + args.putLong("user_id", peerId); + } + if (messageObject.sponsoredChannelPost != 0) { + args.putInt("message_id", messageObject.sponsoredChannelPost); + } + if (messageObject.botStartParam != null) { + args.putString("inline_query", messageObject.botStartParam); + } + if (getMessagesController().checkCanOpenChat(args, ChatActivity.this)) { + presentFragment(new ChatActivity(args)); + } } - if (getMessagesController().checkCanOpenChat(args, ChatActivity.this)) { - presentFragment(new ChatActivity(args)); + } + } else { + TLRPC.WebPage webPage = messageObject.getStoryMentionWebpage(); + if (webPage == null && messageObject.messageOwner != null && messageObject.messageOwner.media != null) { + webPage = messageObject.messageOwner.media.webpage; + } + if (webPage == null) { + return; + } + if (webPage.attributes != null) { + for (int i = 0; i < webPage.attributes.size(); ++i) { + if (webPage.attributes.get(i) instanceof TLRPC.TL_webPageAttributeStory) { + TLRPC.TL_webPageAttributeStory story = (TLRPC.TL_webPageAttributeStory) webPage.attributes.get(i); + if (story.storyItem != null) { + story.storyItem.dialogId = story.user_id; + story.storyItem.messageId = messageObject.getId(); + story.storyItem.messageType = 1; + getOrCreateStoryViewer().open(getContext(), story.storyItem, StoriesListPlaceProvider.of(chatListView)); + return; + } + } } } - } else if (messageObject.messageOwner.media != null && messageObject.messageOwner.media.webpage != null) { - if (!openLinkInternally(messageObject.messageOwner.media.webpage.url, cell, null, messageObject.getId(), PROGRESS_INSTANT)) { + if (!openLinkInternally(webPage.url, cell, null, messageObject.getId(), PROGRESS_INSTANT)) { if (progressDialogCurrent != null) { progressDialogCurrent.cancel(true); } @@ -31072,7 +31407,7 @@ public void end(boolean replaced) { } } }; - Browser.openUrl(getParentActivity(), Uri.parse(messageObject.messageOwner.media.webpage.url), true, true, progressDialogCurrent); + Browser.openUrl(getParentActivity(), Uri.parse(webPage.url), true, true, progressDialogCurrent); } } } @@ -31433,6 +31768,9 @@ public SimpleTextView getReplyObjectTextView() { @Override public ArrayList getThemeDescriptions() { + if (forceDisallowRedrawThemeDescriptions) { + return null; + } if (isPauseOnThemePreview) { isPauseOnThemePreview = false; return null; @@ -31505,6 +31843,7 @@ public ArrayList getThemeDescriptions() { } if (contentView != null) { contentView.invalidateBlurredViews(); + contentView.invalidateBackground(); } if (parentLayout != null && parentLayout.getDrawerLayoutContainer() != null) { parentLayout.getDrawerLayoutContainer().setBehindKeyboardColor(getThemedColor(Theme.key_windowBackgroundWhite)); @@ -31512,6 +31851,12 @@ public ArrayList getThemeDescriptions() { if (suggestEmojiPanel != null) { suggestEmojiPanel.updateColors(); } + if (avatarContainer != null && avatarContainer.getTimeItem() != null) { + avatarContainer.getTimeItem().invalidate(); + } + if (translateButton != null) { + translateButton.updateColors(); + } }; ArrayList themeDescriptions = new ArrayList<>(); @@ -31640,7 +31985,7 @@ public ArrayList getThemeDescriptions() { themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, Theme.chat_msgInCallDrawable, null, Theme.key_chat_inInstant)); themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, Theme.chat_msgInCallSelectedDrawable, null, Theme.key_chat_inInstantSelected)); themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgCallUpGreenDrawable}, null, Theme.key_chat_outGreenCall)); - themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgCallDownRedDrawable}, null, Theme.key_chat_inRedCall)); + themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgCallDownRedDrawable}, null, Theme.key_fill_RedNormal)); themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgCallDownGreenDrawable}, null, Theme.key_chat_inGreenCall)); themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, Theme.chat_msgErrorPaint, null, null, Theme.key_chat_sentError)); themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgErrorDrawable}, null, Theme.key_chat_sentErrorIcon)); @@ -31904,7 +32249,7 @@ public ArrayList getThemeDescriptions() { themeDescriptions.add(new ThemeDescription(alertView, ThemeDescription.FLAG_BACKGROUNDFILTER, null, null, null, null, Theme.key_chat_topPanelBackground)); themeDescriptions.add(new ThemeDescription(pinnedMessageView, ThemeDescription.FLAG_BACKGROUNDFILTER, null, null, null, null, Theme.key_chat_topPanelBackground)); themeDescriptions.add(new ThemeDescription(addToContactsButton, ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_chat_addContact)); - themeDescriptions.add(new ThemeDescription(reportSpamButton, ThemeDescription.FLAG_TEXTCOLOR | ThemeDescription.FLAG_CHECKTAG, null, null, null, null, Theme.key_chat_reportSpam)); + themeDescriptions.add(new ThemeDescription(reportSpamButton, ThemeDescription.FLAG_TEXTCOLOR | ThemeDescription.FLAG_CHECKTAG, null, null, null, null, Theme.key_text_RedBold)); themeDescriptions.add(new ThemeDescription(reportSpamButton, ThemeDescription.FLAG_TEXTCOLOR | ThemeDescription.FLAG_CHECKTAG, null, null, null, null, Theme.key_chat_addContact)); themeDescriptions.add(new ThemeDescription(replyLineView, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_chat_replyPanelLine)); @@ -32402,40 +32747,37 @@ private void setChildrenEnabled(View view, boolean isEnabled) { } } - private void checkThemeEmoticon() { - if (!fragmentOpened) { - return; - } - String emoticon = null; - if (userInfo != null) { - emoticon = userInfo.theme_emoticon; - } - if (emoticon == null && chatInfo != null) { - emoticon = chatInfo.theme_emoticon; - } - setChatThemeEmoticon(emoticon); + private void checkThemeEmoticonOrWallpaper() { + getNotificationCenter().doOnIdle(() -> { + String emoticon = null; + if (userInfo != null) { + emoticon = userInfo.theme_emoticon; + } + if (emoticon == null && chatInfo != null) { + emoticon = chatInfo.theme_emoticon; + } + setChatThemeEmoticon(emoticon); + }); } private void setChatThemeEmoticon(final String emoticon) { + if (themeDelegate == null) { + return; + } ChatThemeController.getInstance(currentAccount).setDialogTheme(dialog_id, emoticon, false); if (!TextUtils.isEmpty(emoticon)) { ChatThemeController.requestChatTheme(emoticon, result -> { - themeDelegate.setCurrentTheme(result, openAnimationStartTime != 0, null); + themeDelegate.setCurrentTheme(result, themeDelegate.wallpaper,openAnimationStartTime != 0, null); }); - } else { - themeDelegate.setCurrentTheme(null, openAnimationStartTime != 0, null); } - } - - @Override - public int getNavigationBarColor() { - return getThemedColor(Theme.key_windowBackgroundGray); - } - - @Override - public int getThemedColor(String key) { - Integer color = themeDelegate != null ? themeDelegate.getColor(key) : null; - return color != null ? color : super.getThemedColor(key); + TLRPC.WallPaper wallPaper = null; + if (dialog_id >= 0) { + TLRPC.UserFull userFull = getMessagesController().getUserFull(dialog_id); + if (userFull != null) { + wallPaper = userFull.wallpaper; + } + } + themeDelegate.setCurrentTheme(themeDelegate.chatTheme, wallPaper, openAnimationStartTime != 0, null); } @Override @@ -32444,7 +32786,8 @@ public Drawable getThemedDrawable(String drawableKey) { return drawable != null ? drawable : super.getThemedDrawable(drawableKey); } - private Paint getThemedPaint(String paintKey) { + @Override + public Paint getThemedPaint(String paintKey) { Paint paint = themeDelegate.getPaint(paintKey); return paint != null ? paint : Theme.getThemePaint(paintKey); } @@ -32469,9 +32812,10 @@ public class ThemeDelegate implements Theme.ResourcesProvider, ChatActionCell.Th private final HashMap currentPaints = new HashMap<>(); private final Matrix actionMatrix = new Matrix(); - private HashMap currentColors = new HashMap<>(); - private HashMap animatingColors; + private SparseIntArray currentColors = new SparseIntArray(); + private SparseIntArray animatingColors; private EmojiThemes chatTheme; + private TLRPC.WallPaper wallpaper; private Drawable backgroundDrawable; private ValueAnimator patternIntensityAnimator; private Bitmap serviceBitmap; @@ -32494,9 +32838,10 @@ public class ThemeDelegate implements Theme.ResourcesProvider, ChatActionCell.Th boolean setup = false; if (isThemeChangeAvailable()) { chatTheme = ChatThemeController.getInstance(currentAccount).getDialogTheme(dialog_id); - if (chatTheme != null) { + wallpaper = ChatThemeController.getInstance(currentAccount).getDialogWallpaper(dialog_id); + if (chatTheme != null || wallpaper != null) { setup = true; - setupChatTheme(chatTheme, false, true); + setupChatTheme(chatTheme, wallpaper, false, true); } } if (!setup && ThemeEditorView.getInstance() == null) { @@ -32515,63 +32860,58 @@ public void setCachedThemes(List cachedThemes) { } @Override - public Integer getColor(String key) { - if (chatTheme == null) { - return Theme.getColor(key); - } + public int getColor(int key) { if (animatingColors != null) { - Integer color = animatingColors.get(key); - if (color != null) { - return color; + int index = animatingColors.indexOfKey(key); + if (index >= 0) { + return animatingColors.valueAt(index); } } - Integer color = currentColors.get(key); - if (color == null) { - if (Theme.key_chat_outBubbleGradient1.equals(key) || Theme.key_chat_outBubbleGradient2.equals(key) || Theme.key_chat_outBubbleGradient3.equals(key)) { - color = currentColors.get(Theme.key_chat_outBubble); - if (color == null) { - color = Theme.getColorOrNull(key); - } - if (color == null) { - color = Theme.getColor(Theme.key_chat_outBubble); - } - } - if (color == null) { - String fallbackKey = Theme.getFallbackKey(key); - if (fallbackKey != null) { - color = currentColors.get(fallbackKey); - } - } + if (chatTheme == null) { + return Theme.getColor(key); } - if (color == null) { - if (chatTheme != null) { - color = Theme.getDefaultColor(key); + int index = currentColors.indexOfKey(key); + if (index >= 0) { + return currentColors.valueAt(index); + } + + int fallbackKey = Theme.getFallbackKey(key); + if (fallbackKey >= 0) { + index = currentColors.indexOfKey(fallbackKey); + if (index >= 0) { + return currentColors.valueAt(index); } } - return color; + return Theme.getColor(key); } @Override - public Integer getCurrentColor(String key) { + public int getCurrentColor(int key) { return getCurrentColor(key, false); } - public Integer getCurrentColor(String key, boolean ignoreAnimation) { - if (chatTheme == null) { - return Theme.getColorOrNull(key); + public int getCurrentColor(int key, boolean ignoreAnimation) { + if (chatTheme == null && backgroundDrawable == null) { + return Theme.getColor(key); } - Integer color = null; + if (!ignoreAnimation && animatingColors != null) { - color = animatingColors.get(key); + int index = animatingColors.indexOfKey(key); + if (index >= 0) { + return animatingColors.valueAt(index); + } } - if (color == null) { - color = currentColors.get(key); + if (currentColors != null) { + int index = currentColors.indexOfKey(key); + if (index >= 0) { + return currentColors.valueAt(index); + } } - return color; + return Theme.getColor(key); } @Override - public void setAnimatedColor(String key, int color) { + public void setAnimatedColor(int key, int color) { if (animatingColors != null) { animatingColors.put(key, color); } @@ -32579,7 +32919,7 @@ public void setAnimatedColor(String key, int color) { @Override public void applyServiceShaderMatrix(int w, int h, float translationX, float translationY) { - if (chatTheme == null || serviceBitmap == null || serviceShader == null) { + if (backgroundDrawable == null || serviceBitmap == null || serviceShader == null) { ChatActionCell.ThemeDelegate.super.applyServiceShaderMatrix(w, h, translationX, translationY); } else { if (useSourceShader) { @@ -32592,12 +32932,12 @@ public void applyServiceShaderMatrix(int w, int h, float translationX, float tra @Override public int getCurrentColor() { - return chatTheme != null ? currentColor : Theme.currentColor; + return backgroundDrawable != null ? currentColor : Theme.currentColor; } @Override public boolean hasGradientService() { - return chatTheme != null ? serviceShader != null : Theme.hasGradientService(); + return backgroundDrawable != null ? serviceShader != null : Theme.hasGradientService(); } @Override @@ -32607,7 +32947,7 @@ public Drawable getDrawable(String drawableKey) { @Override public Paint getPaint(String paintKey) { - return chatTheme != null ? currentPaints.get(paintKey) : null; + return chatTheme != null || backgroundDrawable != null ? currentPaints.get(paintKey) : null; } public boolean isThemeChangeAvailable() { @@ -32628,23 +32968,28 @@ public boolean isWallpaperMotion() { return chatTheme != null ? false : Theme.isWallpaperMotion(); } - public void setCurrentTheme(final EmojiThemes chatTheme, boolean animated, Boolean forceDark) { + public void setCurrentTheme(final EmojiThemes chatTheme, TLRPC.WallPaper newWallpaper, boolean animated, Boolean forceDark) { + setCurrentTheme(chatTheme, newWallpaper, animated, forceDark, false); + } + public void setCurrentTheme(final EmojiThemes chatTheme, TLRPC.WallPaper newWallpaper, boolean animated, Boolean forceDark, boolean force) { if (parentLayout == null) { return; } final EmojiThemes prevTheme = this.chatTheme; - boolean newIsDark = forceDark != null ? forceDark : Theme.getActiveTheme().isDark(); + boolean newIsDark = forceDark != null ? forceDark : this.isDark;//Theme.getActiveTheme().isDark(); String newEmoticon = chatTheme != null ? chatTheme.getEmoticon() : null; String oldEmoticon = this.chatTheme != null ? this.chatTheme.getEmoticon() : null; - if (!isThemeChangeAvailable() || (TextUtils.equals(oldEmoticon, newEmoticon) && this.isDark == newIsDark)) { + TLRPC.WallPaper oldWallpaper = this.wallpaper; + if (!force && (!isThemeChangeAvailable() || (TextUtils.equals(oldEmoticon, newEmoticon) && this.isDark == newIsDark && ChatThemeController.equals(newWallpaper, oldWallpaper)))) { return; } + this.isDark = newIsDark; Theme.ThemeInfo currentTheme = newIsDark ? Theme.getCurrentNightTheme() : Theme.getCurrentTheme(); ActionBarLayout.ThemeAnimationSettings animationSettings = new ActionBarLayout.ThemeAnimationSettings(currentTheme, currentTheme.currentAccentId, currentTheme.isDark(), !animated); - if (this.chatTheme == null) { + if (this.chatTheme == null && wallpaper == null) { Drawable background = Theme.getCachedWallpaperNonBlocking(); drawServiceGradient = background instanceof MotionBackgroundDrawable; initServiceMessageColors(background); @@ -32652,16 +32997,18 @@ public void setCurrentTheme(final EmojiThemes chatTheme, boolean animated, Boole startServiceLinkColor = drawServiceGradient ? 0xffffffff : Theme.getColor(Theme.key_chat_serviceLink); startServiceButtonColor = drawServiceGradient ? 0xffffffff : Theme.getColor(Theme.key_chat_serviceLink); startServiceIconColor = drawServiceGradient ? 0xffffffff : Theme.getColor(Theme.key_chat_serviceIcon); - } else if (drawServiceGradient) { + } else if (drawServiceGradient && backgroundDrawable instanceof MotionBackgroundDrawable) { startServiceBitmap = ((MotionBackgroundDrawable) backgroundDrawable).getBitmap(); + } else if (backgroundDrawable != null){ + initServiceMessageColors(backgroundDrawable); } startServiceColor = currentServiceColor; - startServiceTextColor = drawServiceGradient ? 0xffffffff : getCurrentColorOrDefault(Theme.key_chat_serviceText, true); - startServiceLinkColor = drawServiceGradient ? 0xffffffff : getCurrentColorOrDefault(Theme.key_chat_serviceLink, true); - startServiceButtonColor = drawServiceGradient ? 0xffffffff : getCurrentColorOrDefault(Theme.key_chat_serviceLink, true); - startServiceIconColor = drawServiceGradient ? 0xffffffff : getCurrentColorOrDefault(Theme.key_chat_serviceIcon, true); + startServiceTextColor = drawServiceGradient ? 0xffffffff : getCurrentColor(Theme.key_chat_serviceText, true); + startServiceLinkColor = drawServiceGradient ? 0xffffffff : getCurrentColor(Theme.key_chat_serviceLink, true); + startServiceButtonColor = drawServiceGradient ? 0xffffffff : getCurrentColor(Theme.key_chat_serviceLink, true); + startServiceIconColor = drawServiceGradient ? 0xffffffff : getCurrentColor(Theme.key_chat_serviceIcon, true); - if (chatTheme != null) { + if (chatTheme != null || newWallpaper != null) { int[] colors = AndroidUtilities.calcDrawableColor(backgroundDrawable); currentColor = colors[0]; initDrawables(); @@ -32670,8 +33017,10 @@ public void setCurrentTheme(final EmojiThemes chatTheme, boolean animated, Boole animationSettings.applyTheme = false; animationSettings.afterStartDescriptionsAddedRunnable = () -> { - setupChatTheme(chatTheme, animated, false); + setupChatTheme(chatTheme, newWallpaper, animated, true); initServiceMessageColors(backgroundDrawable); + //updateBackground(); + contentView.invalidateBackground(); }; if (animated) { animationSettings.animationProgress = new INavigationLayout.ThemeAnimationSettings.onAnimationProgress() { @@ -32684,7 +33033,7 @@ public void setProgress(float p) { } }; animationSettings.beforeAnimationRunnable = () -> { - animatingColors = new HashMap<>(); + animatingColors = new SparseIntArray(); animatingMessageDrawable = (Theme.MessageDrawable) getThemedDrawable(Theme.key_drawable_msgOut); animatingMessageDrawable.crossfadeFromDrawable = parentLayout.getMessageDrawableOutStart(); animatingMessageMediaDrawable = (Theme.MessageDrawable) getThemedDrawable(Theme.key_drawable_msgOutMedia); @@ -32702,8 +33051,9 @@ public void setProgress(float p) { }; } else { if (contentView != null) { - contentView.setBackgroundImage(Theme.getCachedWallpaper(), Theme.isWallpaperMotion()); + updateBackground(); } + animationSettings.afterStartDescriptionsAddedRunnable.run(); } animationSettings.onlyTopFragment = true; animationSettings.resourcesProvider = this; @@ -32714,8 +33064,9 @@ public void setProgress(float p) { } } - private void setupChatTheme(EmojiThemes chatTheme, boolean withAnimation, boolean createNewResources) { + private void setupChatTheme(EmojiThemes chatTheme, TLRPC.WallPaper wallPaper, boolean withAnimation, boolean createNewResources) { this.chatTheme = chatTheme; + this.wallpaper = wallPaper; Drawable prevDrawable = null; if (fragmentView != null) { @@ -32724,18 +33075,18 @@ private void setupChatTheme(EmojiThemes chatTheme, boolean withAnimation, boolea final MotionBackgroundDrawable prevMotionDrawable = (prevDrawable instanceof MotionBackgroundDrawable) ? (MotionBackgroundDrawable) prevDrawable : null; final int prevPhase = prevMotionDrawable != null ? prevMotionDrawable.getPhase() : 0; - if (chatTheme == null || chatTheme.showAsDefaultStub) { + if ((chatTheme == null || chatTheme.showAsDefaultStub) && wallPaper == null) { currentColor = Theme.getServiceMessageColor(); } - if (chatTheme == null) { - currentColors = new HashMap<>(); + if (chatTheme == null && wallPaper == null) { + currentColors = new SparseIntArray(); currentPaints.clear(); currentDrawables.clear(); - Drawable wallpaper = Theme.getCachedWallpaperNonBlocking(); - if (wallpaper instanceof MotionBackgroundDrawable) { - ((MotionBackgroundDrawable) wallpaper).setPhase(prevPhase); + Drawable wallpaperDrawable = Theme.getCachedWallpaperNonBlocking(); + if (wallpaperDrawable instanceof MotionBackgroundDrawable) { + ((MotionBackgroundDrawable) wallpaperDrawable).setPhase(prevPhase); } - backgroundDrawable = null; + backgroundDrawable = null;//wallpaperDrawable; Theme.ThemeInfo activeTheme; if (Theme.getActiveTheme().isDark() == isDark) { @@ -32754,12 +33105,21 @@ private void setupChatTheme(EmojiThemes chatTheme, boolean withAnimation, boolea } Theme.applyTheme(activeTheme, false, isDark); + initServiceMessageColors(backgroundDrawable); } else { if (ApplicationLoader.applicationContext != null) { Theme.createChatResources(ApplicationLoader.applicationContext, false); } - currentColors = chatTheme.createColors(currentAccount, isDark ? 1 : 0); - backgroundDrawable = getBackgroundDrawableFromTheme(chatTheme, prevPhase); + if (chatTheme == null) { + currentColors = new SparseIntArray(); + } else { + currentColors = chatTheme.createColors(currentAccount, isDark ? 1 : 0); + } + if (wallPaper != null) { + backgroundDrawable = ChatBackgroundDrawable.getOrCreate(backgroundDrawable, wallPaper, isDark); + } else { + backgroundDrawable = getBackgroundDrawableFromTheme(chatTheme, prevPhase); + } if (patternAlphaAnimator != null) { patternAlphaAnimator.cancel(); @@ -32797,6 +33157,25 @@ public void onAnimationEnd(Animator animation) { patternAlphaAnimator.start(); } + if (chatTheme == null) { + Theme.ThemeInfo activeTheme; + if (Theme.getActiveTheme().isDark() == isDark) { + activeTheme = Theme.getActiveTheme(); + } else { + SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("themeconfig", Activity.MODE_PRIVATE); + String dayThemeName = preferences.getString("lastDayTheme", "Blue"); + if (Theme.getTheme(dayThemeName) == null || Theme.getTheme(dayThemeName).isDark()) { + dayThemeName = "Blue"; + } + String nightThemeName = preferences.getString("lastDarkTheme", "Dark Blue"); + if (Theme.getTheme(nightThemeName) == null || !Theme.getTheme(nightThemeName).isDark()) { + nightThemeName = "Dark Blue"; + } + activeTheme = isDark ? Theme.getTheme(nightThemeName) : Theme.getTheme(dayThemeName); + } + + Theme.applyTheme(activeTheme, false, isDark); + } if (createNewResources) { int[] colors = AndroidUtilities.calcDrawableColor(backgroundDrawable); currentColor = colors[0]; @@ -32846,13 +33225,9 @@ private void initDrawables() { drawable = null; } if (drawable != null) { - String colorKey = Theme.getThemeDrawableColorKey(entry.getKey()); - if (colorKey != null) { - Integer color = getColor(colorKey); - if (color == null) { - color = Theme.getColor(colorKey); - } - Theme.setDrawableColor(drawable, color); + int colorKey = Theme.getThemeDrawableColorKey(entry.getKey()); + if (colorKey >= 0) { + Theme.setDrawableColor(drawable, getColor(colorKey)); } } } @@ -32878,13 +33253,9 @@ private void initPaints() { newPaint.setFlags(Paint.ANTI_ALIAS_FLAG); } - String colorKey = Theme.getThemePaintColorKey(entry.getKey()); - if (colorKey != null) { - Integer color = getColor(colorKey); - if (color == null) { - color = Theme.getColor(colorKey); - } - newPaint.setColor(color); + int colorKey = Theme.getThemePaintColorKey(entry.getKey()); + if (colorKey >= 0) { + newPaint.setColor(getColor(colorKey)); } currentPaints.put(entry.getKey(), newPaint); } @@ -32906,14 +33277,17 @@ private void initServiceMessageColors(Drawable backgroundDrawable) { int[] result = AndroidUtilities.calcDrawableColor(backgroundDrawable); int currentServiceMessageColor = result[0]; - Integer serviceColor = getCurrentColor(Theme.key_chat_serviceBackground); - Integer selectedBackgroundColor = getCurrentColor(Theme.key_chat_selectedBackground); - Integer serviceColor2 = serviceColor; - if (serviceColor == null) { + int serviceColor = getCurrentColor(Theme.key_chat_serviceBackground); + int selectedBackgroundColor = getCurrentColor(Theme.key_chat_selectedBackground); + int serviceColor2 = serviceColor; + if (serviceColor == 0 || wallpaper != null) { serviceColor = currentServiceMessageColor; } currentServiceColor = serviceColor; + if (backgroundDrawable instanceof ChatBackgroundDrawable) { + backgroundDrawable = ((ChatBackgroundDrawable) backgroundDrawable).getDrawable(); + } drawServiceGradient = backgroundDrawable instanceof MotionBackgroundDrawable && SharedConfig.getDevicePerformanceClass() != SharedConfig.PERFORMANCE_CLASS_LOW; drawSelectedGradient = drawServiceGradient; @@ -32928,6 +33302,9 @@ private void initServiceMessageColors(Drawable backgroundDrawable) { } else { serviceBitmap = null; serviceShader = null; + serviceBitmapSource = null; + serviceCanvas = null; + useSourceShader = false; } Paint actionBackgroundPaint = getPaint(Theme.key_paint_chatActionBackground); @@ -32966,7 +33343,7 @@ private void initServiceMessageColors(Drawable backgroundDrawable) { msgBackgroundSelectedPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix2)); msgBackgroundSelectedPaint.setShader(serviceShaderSource); } else { - if (selectedBackgroundColor == null) { + if (selectedBackgroundColor == 0) { selectedBackgroundColor = getColor(Theme.key_chat_selectedBackground); } msgBackgroundSelectedPaint.setColor(selectedBackgroundColor); @@ -32984,10 +33361,10 @@ private void updateServiceMessageColor(float progress) { Paint msgBackgroundSelectedPaint = getPaint(Theme.key_paint_chatMessageBackgroundSelected); int serviceColor = currentServiceColor; - int serviceTextColor = drawServiceGradient ? 0xffffffff : getCurrentColorOrDefault(Theme.key_chat_serviceText, true); - int serviceLinkColor = drawServiceGradient ? 0xffffffff : getCurrentColorOrDefault(Theme.key_chat_serviceLink, true); - int serviceButtonColor = drawServiceGradient ? 0xffffffff : getCurrentColorOrDefault(Theme.key_chat_serviceLink, true); - int serviceIconColor = drawServiceGradient ? 0xffffffff : getCurrentColorOrDefault(Theme.key_chat_serviceIcon, true); + int serviceTextColor = drawServiceGradient ? 0xffffffff : getCurrentColor(Theme.key_chat_serviceText, true); + int serviceLinkColor = drawServiceGradient ? 0xffffffff : getCurrentColor(Theme.key_chat_serviceLink, true); + int serviceButtonColor = drawServiceGradient ? 0xffffffff : getCurrentColor(Theme.key_chat_serviceLink, true); + int serviceIconColor = drawServiceGradient ? 0xffffffff : getCurrentColor(Theme.key_chat_serviceIcon, true); if (progress != 1f) { serviceColor = ColorUtils.blendARGB(startServiceColor, serviceColor, progress); serviceTextColor = ColorUtils.blendARGB(startServiceTextColor, serviceTextColor, progress); @@ -33055,18 +33432,16 @@ private Drawable getBackgroundDrawableFromTheme(EmojiThemes chatTheme, int prevP Drawable drawable; if (chatTheme.showAsDefaultStub) { Theme.ThemeInfo themeInfo = EmojiThemes.getDefaultThemeInfo(isDark); - HashMap currentColors = chatTheme.getPreviewColors(currentAccount, isDark ? 1 : 0); + SparseIntArray currentColors = chatTheme.getPreviewColors(currentAccount, isDark ? 1 : 0); String wallpaperLink = chatTheme.getWallpaperLink(isDark ? 1 : 0); Theme.BackgroundDrawableSettings settings = Theme.createBackgroundDrawable(themeInfo, currentColors, wallpaperLink, prevPhase); drawable = settings.wallpaper; + drawable = new ColorDrawable(Color.BLACK); } else { - Integer backgroundColor = getColor(Theme.key_chat_wallpaper); - Integer gradientColor1 = getColor(Theme.key_chat_wallpaper_gradient_to1); - Integer gradientColor2 = getColor(Theme.key_chat_wallpaper_gradient_to2); - Integer gradientColor3 = getColor(Theme.key_chat_wallpaper_gradient_to3); - if (gradientColor3 == null) { - gradientColor3 = 0; - } + int backgroundColor = getColor(Theme.key_chat_wallpaper); + int gradientColor1 = getColor(Theme.key_chat_wallpaper_gradient_to1); + int gradientColor2 = getColor(Theme.key_chat_wallpaper_gradient_to2); + int gradientColor3 = getColor(Theme.key_chat_wallpaper_gradient_to3); MotionBackgroundDrawable motionDrawable = new MotionBackgroundDrawable(); motionDrawable.setPatternBitmap(chatTheme.getWallpaper(isDark ? 1 : 0).settings.intensity); @@ -33101,12 +33476,20 @@ private Drawable getBackgroundDrawableFromTheme(EmojiThemes chatTheme, int prevP return drawable; } - private int getCurrentColorOrDefault(String key, boolean ignoreAnimation) { - Integer color = getCurrentColor(key, ignoreAnimation); - if (color == null) { - color = Theme.getColor(key, null, ignoreAnimation); - } - return color; + public TLRPC.WallPaper getCurrentWallpaper() { + return wallpaper; + } + } + + private void updateBackground() { + if (contentView == null) { + return; + } + if (themeDelegate.backgroundDrawable != null && contentView.getBackgroundImage() != null) { + return; + } + if (contentView.getBackgroundImage() == null) { + contentView.setBackgroundImage(Theme.getCachedWallpaper(), Theme.isWallpaperMotion()); } } @@ -33913,4 +34296,16 @@ private boolean isLanguageRestricted(String lang) { } return restricted; } + + private class RecyclerListViewInternal extends RecyclerListView implements StoriesListPlaceProvider.ClippedView { + public RecyclerListViewInternal(Context context, ThemeDelegate themeDelegate) { + super(context, themeDelegate); + } + + @Override + public void updateClip(int[] clip) { + clip[0] = (int) chatListViewPaddingTop - AndroidUtilities.dp(4); + clip[1] = chatListView.getMeasuredHeight() - (chatListView.getPaddingBottom() - AndroidUtilities.dp(3)); + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatBackgroundDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatBackgroundDrawable.java new file mode 100644 index 0000000000..204eee1c0e --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatBackgroundDrawable.java @@ -0,0 +1,261 @@ +package org.telegram.ui; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; +import android.text.TextUtils; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.graphics.ColorUtils; + +import com.google.android.exoplayer2.util.Log; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ImageLoader; +import org.telegram.messenger.ImageLocation; +import org.telegram.messenger.ImageReceiver; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.EmojiThemes; +import org.telegram.ui.Components.BackgroundGradientDrawable; +import org.telegram.ui.Components.MotionBackgroundDrawable; + +import java.util.Objects; + +public class ChatBackgroundDrawable extends Drawable { + + private final boolean themeIsDark; + boolean isPattern; + View parent; + int alpha = 255; + float dimAmount; + ImageReceiver imageReceiver = new ImageReceiver() { + @Override + public void invalidate() { + if (parent != null) { + parent.invalidate(); + } + } + }; + + MotionBackgroundDrawable motionBackgroundDrawable; + final TLRPC.WallPaper wallpaper; + private boolean colorFilterSetted; + + public static Drawable getOrCreate(Drawable backgroundDrawable, TLRPC.WallPaper wallpaper, boolean themeIsDark) { + if (backgroundDrawable instanceof ChatBackgroundDrawable) { + ChatBackgroundDrawable chatBackgroundDrawable = (ChatBackgroundDrawable) backgroundDrawable; + if (wallpaper.uploadingImage != null) { + if (wallpaper.uploadingImage.equals(chatBackgroundDrawable.wallpaper.uploadingImage)) { + if (wallpaper.settings != null && chatBackgroundDrawable.wallpaper.settings != null && wallpaper.settings.intensity > 0) { + if (chatBackgroundDrawable.themeIsDark == themeIsDark) { + return chatBackgroundDrawable; + } + } else { + return chatBackgroundDrawable; + } + } + } else if (wallpaper.id == chatBackgroundDrawable.wallpaper.id && TextUtils.equals(hash(wallpaper.settings), hash(chatBackgroundDrawable.wallpaper.settings))) { + if (wallpaper.document != null && !wallpaper.pattern && wallpaper.settings != null && wallpaper.settings.intensity > 0) { + if (chatBackgroundDrawable.themeIsDark == themeIsDark) { + return chatBackgroundDrawable; + } + } else { + return chatBackgroundDrawable; + } + } + } + return new ChatBackgroundDrawable(wallpaper, themeIsDark, false); + } + + public void setParent(View parent) { + this.parent = parent; + if (motionBackgroundDrawable != null) { + motionBackgroundDrawable.setParentView(parent); + } + } + + public ChatBackgroundDrawable(TLRPC.WallPaper wallPaper) { + this(wallPaper, false, false); + } + + public ChatBackgroundDrawable(TLRPC.WallPaper wallPaper, boolean themeIsDark, boolean preview) { + imageReceiver.setInvalidateAll(true); + isPattern = wallPaper.pattern; + this.wallpaper = wallPaper; + this.themeIsDark = themeIsDark; + if (themeIsDark && (wallpaper.document != null || wallpaper.uploadingImage != null) && !wallpaper.pattern && wallpaper.settings != null) { + dimAmount = wallpaper.settings.intensity / 100f; + // imageReceiver.setColorFilter(new PorterDuffColorFilter(ColorUtils.setAlphaComponent(Color.BLACK, (int) (dimAmount * 255)), PorterDuff.Mode.DARKEN)); + } + if ((isPattern || wallPaper.document == null) && wallPaper.settings != null && wallPaper.settings.second_background_color != 0 && wallPaper.settings.third_background_color != 0) { + motionBackgroundDrawable = new MotionBackgroundDrawable(); + motionBackgroundDrawable.setColors( + wallPaper.settings.background_color, + wallPaper.settings.second_background_color, + wallPaper.settings.third_background_color, + wallPaper.settings.fourth_background_color + ); + EmojiThemes.loadWallpaperImage(wallPaper.id, wallPaper, result -> { + motionBackgroundDrawable.setPatternBitmap(wallPaper.settings.intensity, result.second); + if (parent != null) { + parent.invalidate(); + } + }); + } else { + String imageFilter; + int w = Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y); + int h = Math.max(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y); + if (preview) { + imageFilter = "150_150_wallpaper"; + } else { + imageFilter = (int) (w / AndroidUtilities.density) + "_" + (int) (h / AndroidUtilities.density) + "_wallpaper"; + } + imageFilter += wallPaper.id; + imageFilter += hash(wallPaper.settings); + + Drawable thumb = createThumb(wallPaper); + if (wallPaper.uploadingImage != null) { + imageReceiver.setImage(ImageLocation.getForPath(wallPaper.uploadingImage), imageFilter, thumb, null, wallPaper, 1); + } else if (wallPaper.document != null) { + imageReceiver.setImage(ImageLocation.getForDocument(wallPaper.document), imageFilter, thumb, null, wallPaper, 1); + } else { + imageReceiver.setImageBitmap(thumb); + } + } + } + + public static Drawable createThumb(TLRPC.WallPaper wallPaper) { + Drawable thumb = null; + if (wallPaper.stripedThumb != null) { + return new BitmapDrawable(wallPaper.stripedThumb); + } + if (wallPaper.pattern && wallPaper.settings == null) { + return new ColorDrawable(Color.BLACK); + } + if (wallPaper.document != null) { + for (int i = 0; i < wallPaper.document.thumbs.size(); i++) { + if (wallPaper.document.thumbs.get(i) instanceof TLRPC.TL_photoStrippedSize) { + thumb = new BitmapDrawable(ImageLoader.getStrippedPhotoBitmap(wallPaper.document.thumbs.get(i).bytes, "b")); + } + } + } else { + if (wallPaper.settings.intensity < 0) { + thumb = bitmapDrawableOf(new ColorDrawable(Color.BLACK)); + } else { + if (wallPaper.settings.second_background_color == 0) { //one color + thumb = bitmapDrawableOf(new ColorDrawable(ColorUtils.setAlphaComponent(wallPaper.settings.background_color, 255))); + } else if (wallPaper.settings.third_background_color == 0) { //two color + int color1 = ColorUtils.setAlphaComponent(wallPaper.settings.background_color, 255); + int color2 = ColorUtils.setAlphaComponent(wallPaper.settings.second_background_color, 255); + thumb = bitmapDrawableOf(new GradientDrawable(BackgroundGradientDrawable.getGradientOrientation(wallPaper.settings.rotation), new int[]{color1, color2})); + } else { + int color1 = ColorUtils.setAlphaComponent(wallPaper.settings.background_color, 255); + int color2 = ColorUtils.setAlphaComponent(wallPaper.settings.second_background_color, 255); + int color3 = ColorUtils.setAlphaComponent(wallPaper.settings.third_background_color, 255); + int color4 = wallPaper.settings.fourth_background_color == 0 ? 0 : ColorUtils.setAlphaComponent(wallPaper.settings.fourth_background_color, 255); + MotionBackgroundDrawable motionBackgroundDrawable = new MotionBackgroundDrawable(); + motionBackgroundDrawable.setColors(color1, color2, color3, color4); + thumb = new BitmapDrawable(motionBackgroundDrawable.getBitmap()); + } + } + } + return thumb; + } + + private static Drawable bitmapDrawableOf(Drawable drawable) { + Bitmap bitmap = Bitmap.createBitmap(20, 20, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, 20, 20); + drawable.draw(canvas); + return new BitmapDrawable(bitmap); + } + + @Override + public void draw(@NonNull Canvas canvas) { + if (motionBackgroundDrawable != null) { + motionBackgroundDrawable.setBounds(getBounds()); + motionBackgroundDrawable.setAlpha(alpha); + motionBackgroundDrawable.draw(canvas); + } else { + boolean drawDim = false; + if (!imageReceiver.hasImageLoaded() || imageReceiver.getCurrentAlpha() != 1f) { + drawDim = true; + } else if (!colorFilterSetted) { + colorFilterSetted = true; + imageReceiver.setColorFilter(new PorterDuffColorFilter(ColorUtils.setAlphaComponent(Color.BLACK, (int) (dimAmount * 255)), PorterDuff.Mode.DARKEN)); + } + imageReceiver.setImageCoords(getBounds()); + imageReceiver.setAlpha(alpha / 255f); + imageReceiver.draw(canvas); + if (drawDim && dimAmount != 0) { + canvas.drawColor(ColorUtils.setAlphaComponent(Color.BLACK, (int) (dimAmount * 255))); + } + } + } + + @Override + public void setAlpha(int alpha) { + if (this.alpha != alpha) { + this.alpha = alpha; + invalidateSelf(); + } + } + + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) { + + } + + @Override + public int getOpacity() { + return 0; + } + + boolean attached; + + public void onAttachedToWindow() { + if (attached) { + return; + } + attached = true; + imageReceiver.onAttachedToWindow(); + } + + public void onDetachedFromWindow() { + if (!attached) { + return; + } + attached = false; + imageReceiver.onDetachedFromWindow(); + } + + public Drawable getDrawable() { + if (motionBackgroundDrawable != null) { + return motionBackgroundDrawable; + } + if (imageReceiver.getStaticThumb() != null) { + return imageReceiver.getStaticThumb(); + } else if (imageReceiver.getThumb() != null) { + return imageReceiver.getThumb(); + } else { + return imageReceiver.getDrawable(); + } + } + + public static String hash(TLRPC.WallPaperSettings settings) { + if (settings == null) { + return ""; + } + return String.valueOf(Objects.hash(settings.blur, settings.motion, settings.intensity, settings.background_color, settings.second_background_color, settings.third_background_color, settings.fourth_background_color)); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatEditActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatEditActivity.java index 69c7229394..ef5b7e88b2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatEditActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatEditActivity.java @@ -25,8 +25,12 @@ import android.text.Editable; import android.text.InputFilter; import android.text.InputType; +import android.text.SpannableString; +import android.text.Spanned; +import android.text.TextPaint; import android.text.TextUtils; import android.text.TextWatcher; +import android.text.style.ClickableSpan; import android.util.TypedValue; import android.view.Gravity; import android.view.HapticFeedbackConstants; @@ -37,8 +41,11 @@ import android.widget.LinearLayout; import android.widget.ScrollView; +import androidx.annotation.NonNull; + import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ChatObject; +import org.telegram.messenger.ContactsController; import org.telegram.messenger.Emoji; import org.telegram.messenger.FileLog; import org.telegram.messenger.ImageLocation; @@ -48,6 +55,8 @@ import org.telegram.messenger.MessagesStorage; import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; +import org.telegram.messenger.browser.Browser; +import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; @@ -80,6 +89,7 @@ import org.telegram.ui.Components.UndoView; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.concurrent.CountDownLatch; @@ -137,10 +147,22 @@ public class ChatEditActivity extends BaseFragment implements ImageUpdater.Image private TextSettingsCell deleteCell; private ShadowSectionCell deleteInfoCell; + private TextCell publicLinkCell; + private TextCell editIntroCell; + private TextCell editCommandsCell; + private TextCell changeBotSettingsCell; + private TextInfoPrivacyCell botInfoCell; + private TLRPC.FileLocation avatar; + + private long chatId; private TLRPC.Chat currentChat; private TLRPC.ChatFull info; - private long chatId; + + private long userId; + private TLRPC.User currentUser; + private TLRPC.UserFull userInfo; + private boolean signMessages; private boolean forum, canForum; @@ -156,6 +178,8 @@ public class ChatEditActivity extends BaseFragment implements ImageUpdater.Image private int realAdminCount = 0; + private boolean hasUploadedPhoto; + private PhotoViewer.PhotoViewerProvider provider = new PhotoViewer.EmptyPhotoViewerProvider() { @Override @@ -165,9 +189,16 @@ public PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageObj } TLRPC.FileLocation photoBig = null; - TLRPC.Chat chat = getMessagesController().getChat(chatId); - if (chat != null && chat.photo != null && chat.photo.photo_big != null) { - photoBig = chat.photo.photo_big; + if (currentUser != null) { + TLRPC.User user = userId == 0 ? null : getMessagesController().getUser(userId); + if (user != null && user.photo != null && user.photo.photo_big != null) { + photoBig = user.photo.photo_big; + } + } else { + TLRPC.Chat chat = getMessagesController().getChat(chatId); + if (chat != null && chat.photo != null && chat.photo.photo_big != null) { + photoBig = chat.photo.photo_big; + } } if (photoBig != null && photoBig.local_id == fileLocation.local_id && photoBig.volume_id == fileLocation.volume_id && photoBig.dc_id == fileLocation.dc_id) { @@ -178,7 +209,7 @@ public PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageObj object.viewY = coords[1] - (Build.VERSION.SDK_INT >= 21 ? 0 : AndroidUtilities.statusBarHeight); object.parentView = avatarImage; object.imageReceiver = avatarImage.getImageReceiver(); - object.dialogId = -chatId; + object.dialogId = userId != 0 ? userId : -chatId; object.thumb = object.imageReceiver.getBitmapSafe(); object.size = -1; object.radius = avatarImage.getImageReceiver().getRoundRadius(); @@ -198,44 +229,116 @@ public void willHidePhotoViewer() { public void openPhotoForEdit(String file, String thumb, boolean isVideo) { imageUpdater.openPhotoForEdit(file, thumb, 0, isVideo); } + + @Override + public boolean onDeletePhoto(int index) { + TLRPC.TL_photos_updateProfilePhoto req = new TLRPC.TL_photos_updateProfilePhoto(); + req.bot = getMessagesController().getInputUser(userId); + req.flags |= 2; + req.id = new TLRPC.TL_inputPhotoEmpty(); + getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + avatarImage.setImageDrawable(avatarDrawable); + setAvatarCell.setTextAndIcon(LocaleController.getString("ChatSetPhotoOrVideo", R.string.ChatSetPhotoOrVideo), R.drawable.msg_addphoto, true); + + if (currentUser != null) { + currentUser.photo = null; + getMessagesController().putUser(currentUser, true); + } + hasUploadedPhoto = true; + + if (cameraDrawable == null) { + cameraDrawable = new RLottieDrawable(R.raw.camera_outline, "" + R.raw.camera_outline, AndroidUtilities.dp(50), AndroidUtilities.dp(50), false, null); + } + setAvatarCell.imageView.setTranslationX(-AndroidUtilities.dp(8)); + setAvatarCell.imageView.setAnimation(cameraDrawable); + })); + return false; + } + + @Override + public int getTotalImageCount() { + return 1; + } + + @Override + public boolean canLoadMoreAvatars() { + return false; + } }; public ChatEditActivity(Bundle args) { super(args); avatarDrawable = new AvatarDrawable(); chatId = args.getLong("chat_id", 0); - TLRPC.Chat chat = getMessagesController().getChat(chatId); - imageUpdater = new ImageUpdater(true, chat != null && ChatObject.isChannelAndNotMegaGroup(chat) ? ImageUpdater.FOR_TYPE_CHANNEL : ImageUpdater.FOR_TYPE_GROUP, true); + userId = args.getLong("user_id", 0); + + if (chatId != 0) { + TLRPC.Chat chat = getMessagesController().getChat(chatId); + imageUpdater = new ImageUpdater(true, chat != null && ChatObject.isChannelAndNotMegaGroup(chat) ? ImageUpdater.FOR_TYPE_CHANNEL : ImageUpdater.FOR_TYPE_GROUP, true); + } else { + imageUpdater = new ImageUpdater(false, ImageUpdater.FOR_TYPE_USER, false); + } } @Override public boolean onFragmentCreate() { - currentChat = getMessagesController().getChat(chatId); - if (currentChat == null) { - currentChat = MessagesStorage.getInstance(currentAccount).getChatSync(chatId); - if (currentChat != null) { - getMessagesController().putChat(currentChat, true); - } else { - return false; - } - if (info == null) { - info = MessagesStorage.getInstance(currentAccount).loadChatInfo(chatId, ChatObject.isChannel(currentChat), new CountDownLatch(1), false, false); + if (chatId != 0) { + currentChat = getMessagesController().getChat(chatId); + if (currentChat == null) { + currentChat = MessagesStorage.getInstance(currentAccount).getChatSync(chatId); + if (currentChat != null) { + getMessagesController().putChat(currentChat, true); + } else { + return false; + } if (info == null) { + info = MessagesStorage.getInstance(currentAccount).loadChatInfo(chatId, ChatObject.isChannel(currentChat), new CountDownLatch(1), false, false); + if (info == null) { + return false; + } + } + } + } else { + currentUser = userId == 0 ? null : getMessagesController().getUser(userId); + if (currentUser == null) { + currentUser = MessagesStorage.getInstance(currentAccount).getUserSync(userId); + if (currentUser != null) { + getMessagesController().putUser(currentUser, true); + } else { return false; } + if (userInfo == null) { + HashSet set = new HashSet<>(); + set.add(userId); + List fulls = MessagesStorage.getInstance(currentAccount).loadUserInfos(set); + if (!fulls.isEmpty()) { + userInfo = fulls.get(0); + } else { + return false; + } + } } } - avatarDrawable.setInfo(5, currentChat.title, null); - isChannel = ChatObject.isChannel(currentChat) && !currentChat.megagroup; + if (currentChat != null) { + avatarDrawable.setInfo(5, currentChat.title, null); + isChannel = ChatObject.isChannel(currentChat) && !currentChat.megagroup; + signMessages = currentChat.signatures; + forum = currentChat.forum; + canForum = userId == 0 && (forum || Math.max(info == null ? 0 : info.participants_count, currentChat.participants_count) >= getMessagesController().forumUpgradeParticipantsMin) && (info == null || info.linked_chat_id == 0); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.chatInfoDidLoad); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.chatAvailableReactionsUpdated); + } else { + avatarDrawable.setInfo(5, currentUser.first_name, null); + isChannel = false; + signMessages = false; + forum = false; + canForum = false; + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.userInfoDidLoad); + } imageUpdater.parentFragment = this; imageUpdater.setDelegate(this); - signMessages = currentChat.signatures; - forum = currentChat.forum; - canForum = (forum || Math.max(info == null ? 0 : info.participants_count, currentChat.participants_count) >= getMessagesController().forumUpgradeParticipantsMin) && (info == null || info.linked_chat_id == 0); - NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.chatInfoDidLoad); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.updateInterfaces); - NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.chatAvailableReactionsUpdated); if (info != null) { loadLinksCount(); @@ -264,9 +367,13 @@ public void onFragmentDestroy() { if (imageUpdater != null) { imageUpdater.clear(); } - NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.chatInfoDidLoad); + if (currentChat != null) { + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.chatInfoDidLoad); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.chatAvailableReactionsUpdated); + } else { + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.userInfoDidLoad); + } NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.updateInterfaces); - NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.chatAvailableReactionsUpdated); if (nameTextView != null) { nameTextView.onDestroy(); } @@ -513,7 +620,7 @@ public void invalidate(int l, int t, int r, int b) { }; avatarImage.setRoundRadius(forum ? AndroidUtilities.dp(16) : AndroidUtilities.dp(32)); - if (ChatObject.canChangeChatInfo(currentChat)) { + if (currentUser != null || ChatObject.canChangeChatInfo(currentChat)) { frameLayout.addView(avatarImage, LayoutHelper.createFrame(64, 64, Gravity.TOP | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), LocaleController.isRTL ? 0 : 16, 12, LocaleController.isRTL ? 16 : 0, 8)); Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); @@ -542,6 +649,18 @@ protected void onDraw(Canvas canvas) { if (imageUpdater.isUploadingImage()) { return; } + TLRPC.User user = userId == 0 ? null : getMessagesController().getUser(userId); + if (user != null) { + if (user.photo != null && user.photo.photo_big != null) { + PhotoViewer.getInstance().setParentActivity(ChatEditActivity.this); + if (user.photo.dc_id != 0) { + user.photo.photo_big.dc_id = user.photo.dc_id; + } + PhotoViewer.getInstance().openPhoto(user.photo.photo_big, provider); + } + return; + } + TLRPC.Chat chat = getMessagesController().getChat(chatId); if (chat.photo != null && chat.photo.photo_big != null) { PhotoViewer.getInstance().setParentActivity(ChatEditActivity.this); @@ -562,12 +681,14 @@ protected void onDraw(Canvas canvas) { } nameTextView = new EditTextEmoji(context, sizeNotifierFrameLayout, this, EditTextEmoji.STYLE_FRAGMENT, false); - if (isChannel) { + if (userId != 0) { + nameTextView.setHint(LocaleController.getString(R.string.BotName)); + } else if (isChannel) { nameTextView.setHint(LocaleController.getString("EnterChannelName", R.string.EnterChannelName)); } else { nameTextView.setHint(LocaleController.getString("GroupName", R.string.GroupName)); } - nameTextView.setEnabled(ChatObject.canChangeChatInfo(currentChat)); + nameTextView.setEnabled(currentChat != null || ChatObject.canChangeChatInfo(currentChat)); nameTextView.setFocusable(nameTextView.isEnabled()); nameTextView.getEditText().addTextChangedListener(new TextWatcher() { @Override @@ -598,7 +719,7 @@ public void afterTextChanged(Editable s) { settingsContainer.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); linearLayout1.addView(settingsContainer, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - if (ChatObject.canChangeChatInfo(currentChat)) { + if (currentUser != null || ChatObject.canChangeChatInfo(currentChat)) { setAvatarCell = new TextCell(context) { @Override protected void onDraw(Canvas canvas) { @@ -610,9 +731,32 @@ protected void onDraw(Canvas canvas) { setAvatarCell.setOnClickListener(v -> { imageUpdater.openMenu(avatar != null, () -> { avatar = null; - MessagesController.getInstance(currentAccount).changeChatAvatar(chatId, null, null, null, null, 0, null, null, null, null); + if (userId == 0) { + MessagesController.getInstance(currentAccount).changeChatAvatar(chatId, null, null, null, null, 0, null, null, null, null); + } else { + TLRPC.TL_photos_updateProfilePhoto req = new TLRPC.TL_photos_updateProfilePhoto(); + req.bot = getMessagesController().getInputUser(userId); + req.flags |= 2; + req.id = new TLRPC.TL_inputPhotoEmpty(); + getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + avatarImage.setImageDrawable(avatarDrawable); + setAvatarCell.setTextAndIcon(LocaleController.getString("ChatSetPhotoOrVideo", R.string.ChatSetPhotoOrVideo), R.drawable.msg_addphoto, true); + + if (currentUser != null) { + currentUser.photo = null; + getMessagesController().putUser(currentUser, true); + } + hasUploadedPhoto = true; + + if (cameraDrawable == null) { + cameraDrawable = new RLottieDrawable(R.raw.camera_outline, "" + R.raw.camera_outline, AndroidUtilities.dp(50), AndroidUtilities.dp(50), false, null); + } + setAvatarCell.imageView.setTranslationX(-AndroidUtilities.dp(8)); + setAvatarCell.imageView.setAnimation(cameraDrawable); + })); + } showAvatarProgress(false, true); - avatarImage.setImage(null, null, avatarDrawable, currentChat); + avatarImage.setImage(null, null, avatarDrawable, currentUser != null ? currentUser : currentChat); cameraDrawable.setCurrentFrame(0); setAvatarCell.imageView.playAnimation(); }, dialogInterface -> { @@ -640,7 +784,7 @@ protected void onDraw(Canvas canvas) { descriptionTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); descriptionTextView.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES | InputType.TYPE_TEXT_FLAG_MULTI_LINE | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT); descriptionTextView.setImeOptions(EditorInfo.IME_ACTION_DONE); - descriptionTextView.setEnabled(ChatObject.canChangeChatInfo(currentChat)); + descriptionTextView.setEnabled(currentUser != null || ChatObject.canChangeChatInfo(currentChat)); descriptionTextView.setFocusable(descriptionTextView.isEnabled()); inputFilters = new InputFilter[1]; inputFilters[0] = new InputFilter.LengthFilter(255); @@ -685,142 +829,144 @@ public void afterTextChanged(Editable editable) { typeEditContainer.setOrientation(LinearLayout.VERTICAL); linearLayout1.addView(typeEditContainer, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - if (currentChat.megagroup && (info == null || info.can_set_location)) { - locationCell = new TextCell(context); - locationCell.setBackgroundDrawable(Theme.getSelectorDrawable(true)); - typeEditContainer.addView(locationCell, LayoutHelper.createLinear(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); - locationCell.setOnClickListener(v -> { - if (!AndroidUtilities.isMapsInstalled(ChatEditActivity.this)) { - return; - } - LocationActivity fragment = new LocationActivity(LocationActivity.LOCATION_TYPE_GROUP); - fragment.setDialogId(-chatId); - if (info != null && info.location instanceof TLRPC.TL_channelLocation) { - fragment.setInitialLocation((TLRPC.TL_channelLocation) info.location); - } - fragment.setDelegate((location, live, notify, scheduleDate) -> { - TLRPC.TL_channelLocation channelLocation = new TLRPC.TL_channelLocation(); - channelLocation.address = location.address; - channelLocation.geo_point = location.geo; - - info.location = channelLocation; - info.flags |= 32768; - updateFields(false, true); - getMessagesController().loadFullChat(chatId, 0, true); + if (currentChat != null) { + if (currentChat.megagroup && (info == null || info.can_set_location)) { + locationCell = new TextCell(context); + locationCell.setBackgroundDrawable(Theme.getSelectorDrawable(true)); + typeEditContainer.addView(locationCell, LayoutHelper.createLinear(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + locationCell.setOnClickListener(v -> { + if (!AndroidUtilities.isMapsInstalled(ChatEditActivity.this)) { + return; + } + LocationActivity fragment = new LocationActivity(LocationActivity.LOCATION_TYPE_GROUP); + fragment.setDialogId(-chatId); + if (info != null && info.location instanceof TLRPC.TL_channelLocation) { + fragment.setInitialLocation((TLRPC.TL_channelLocation) info.location); + } + fragment.setDelegate((location, live, notify, scheduleDate) -> { + TLRPC.TL_channelLocation channelLocation = new TLRPC.TL_channelLocation(); + channelLocation.address = location.address; + channelLocation.geo_point = location.geo; + + info.location = channelLocation; + info.flags |= 32768; + updateFields(false, true); + getMessagesController().loadFullChat(chatId, 0, true); + }); + presentFragment(fragment); }); - presentFragment(fragment); - }); - } - - if (currentChat.creator && (info == null || info.can_set_username)) { - typeCell = new TextCell(context); - typeCell.setBackgroundDrawable(Theme.getSelectorDrawable(true)); - typeEditContainer.addView(typeCell, LayoutHelper.createLinear(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); - typeCell.setOnClickListener(v -> { - ChatEditTypeActivity fragment = new ChatEditTypeActivity(chatId, locationCell != null && locationCell.getVisibility() == View.VISIBLE); - fragment.setInfo(info); - presentFragment(fragment); - }); - } - - if (ChatObject.isChannel(currentChat) && (isChannel && ChatObject.canUserDoAdminAction(currentChat, ChatObject.ACTION_CHANGE_INFO) || !isChannel && ChatObject.canUserDoAdminAction(currentChat, ChatObject.ACTION_PIN))) { - linkedCell = new TextCell(context); - linkedCell.setBackgroundDrawable(Theme.getSelectorDrawable(true)); - typeEditContainer.addView(linkedCell, LayoutHelper.createLinear(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); - linkedCell.setOnClickListener(v -> { - ChatLinkActivity fragment = new ChatLinkActivity(chatId); - fragment.setInfo(info); - presentFragment(fragment); - }); - } - - if (!isChannel && ChatObject.canBlockUsers(currentChat) && (ChatObject.isChannel(currentChat) || currentChat.creator)) { - historyCell = new TextCell(context); - historyCell.setBackgroundDrawable(Theme.getSelectorDrawable(true)); - typeEditContainer.addView(historyCell, LayoutHelper.createLinear(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); - historyCell.setOnClickListener(v -> { - BottomSheet.Builder builder = new BottomSheet.Builder(context); - builder.setApplyTopPadding(false); - - LinearLayout linearLayout = new LinearLayout(context); - linearLayout.setOrientation(LinearLayout.VERTICAL); - - HeaderCell headerCell = new HeaderCell(context, Theme.key_dialogTextBlue2, 23, 15, false); - headerCell.setHeight(47); - headerCell.setText(LocaleController.getString("ChatHistory", R.string.ChatHistory)); - linearLayout.addView(headerCell); + } - LinearLayout linearLayoutInviteContainer = new LinearLayout(context); - linearLayoutInviteContainer.setOrientation(LinearLayout.VERTICAL); - linearLayout.addView(linearLayoutInviteContainer, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + if (currentChat.creator && (info == null || info.can_set_username)) { + typeCell = new TextCell(context); + typeCell.setBackgroundDrawable(Theme.getSelectorDrawable(true)); + typeEditContainer.addView(typeCell, LayoutHelper.createLinear(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + typeCell.setOnClickListener(v -> { + ChatEditTypeActivity fragment = new ChatEditTypeActivity(chatId, locationCell != null && locationCell.getVisibility() == View.VISIBLE); + fragment.setInfo(info); + presentFragment(fragment); + }); + } - RadioButtonCell[] buttons = new RadioButtonCell[2]; + if (ChatObject.isChannel(currentChat) && (isChannel && ChatObject.canUserDoAdminAction(currentChat, ChatObject.ACTION_CHANGE_INFO) || !isChannel && ChatObject.canUserDoAdminAction(currentChat, ChatObject.ACTION_PIN))) { + linkedCell = new TextCell(context); + linkedCell.setBackgroundDrawable(Theme.getSelectorDrawable(true)); + typeEditContainer.addView(linkedCell, LayoutHelper.createLinear(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + linkedCell.setOnClickListener(v -> { + ChatLinkActivity fragment = new ChatLinkActivity(chatId); + fragment.setInfo(info); + presentFragment(fragment); + }); + } - for (int a = 0; a < 2; a++) { - buttons[a] = new RadioButtonCell(context, true); - buttons[a].setTag(a); - buttons[a].setBackgroundDrawable(Theme.getSelectorDrawable(false)); - if (a == 0) { - buttons[a].setTextAndValueAndCheck(LocaleController.getString("ChatHistoryVisible", R.string.ChatHistoryVisible), LocaleController.getString("ChatHistoryVisibleInfo", R.string.ChatHistoryVisibleInfo), true, !historyHidden); - } else { - if (ChatObject.isChannel(currentChat)) { - buttons[a].setTextAndValueAndCheck(LocaleController.getString("ChatHistoryHidden", R.string.ChatHistoryHidden), LocaleController.getString("ChatHistoryHiddenInfo", R.string.ChatHistoryHiddenInfo), false, historyHidden); + if (!isChannel && ChatObject.canBlockUsers(currentChat) && (ChatObject.isChannel(currentChat) || currentChat.creator)) { + historyCell = new TextCell(context); + historyCell.setBackgroundDrawable(Theme.getSelectorDrawable(true)); + typeEditContainer.addView(historyCell, LayoutHelper.createLinear(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + historyCell.setOnClickListener(v -> { + BottomSheet.Builder builder = new BottomSheet.Builder(context); + builder.setApplyTopPadding(false); + + LinearLayout linearLayout = new LinearLayout(context); + linearLayout.setOrientation(LinearLayout.VERTICAL); + + HeaderCell headerCell = new HeaderCell(context, Theme.key_dialogTextBlue2, 23, 15, false); + headerCell.setHeight(47); + headerCell.setText(LocaleController.getString("ChatHistory", R.string.ChatHistory)); + linearLayout.addView(headerCell); + + LinearLayout linearLayoutInviteContainer = new LinearLayout(context); + linearLayoutInviteContainer.setOrientation(LinearLayout.VERTICAL); + linearLayout.addView(linearLayoutInviteContainer, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + + RadioButtonCell[] buttons = new RadioButtonCell[2]; + + for (int a = 0; a < 2; a++) { + buttons[a] = new RadioButtonCell(context, true); + buttons[a].setTag(a); + buttons[a].setBackgroundDrawable(Theme.getSelectorDrawable(false)); + if (a == 0) { + buttons[a].setTextAndValueAndCheck(LocaleController.getString("ChatHistoryVisible", R.string.ChatHistoryVisible), LocaleController.getString("ChatHistoryVisibleInfo", R.string.ChatHistoryVisibleInfo), true, !historyHidden); } else { - buttons[a].setTextAndValueAndCheck(LocaleController.getString("ChatHistoryHidden", R.string.ChatHistoryHidden), LocaleController.getString("ChatHistoryHiddenInfo2", R.string.ChatHistoryHiddenInfo2), false, historyHidden); + if (ChatObject.isChannel(currentChat)) { + buttons[a].setTextAndValueAndCheck(LocaleController.getString("ChatHistoryHidden", R.string.ChatHistoryHidden), LocaleController.getString("ChatHistoryHiddenInfo", R.string.ChatHistoryHiddenInfo), false, historyHidden); + } else { + buttons[a].setTextAndValueAndCheck(LocaleController.getString("ChatHistoryHidden", R.string.ChatHistoryHidden), LocaleController.getString("ChatHistoryHiddenInfo2", R.string.ChatHistoryHiddenInfo2), false, historyHidden); + } } + linearLayoutInviteContainer.addView(buttons[a], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + buttons[a].setOnClickListener(v2 -> { + Integer tag = (Integer) v2.getTag(); + buttons[0].setChecked(tag == 0, true); + buttons[1].setChecked(tag == 1, true); + historyHidden = tag == 1; + builder.getDismissRunnable().run(); + updateFields(true, true); + }); } - linearLayoutInviteContainer.addView(buttons[a], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - buttons[a].setOnClickListener(v2 -> { - Integer tag = (Integer) v2.getTag(); - buttons[0].setChecked(tag == 0, true); - buttons[1].setChecked(tag == 1, true); - historyHidden = tag == 1; - builder.getDismissRunnable().run(); - updateFields(true, true); - }); - } - builder.setCustomView(linearLayout); - showDialog(builder.create()); - }); - } + builder.setCustomView(linearLayout); + showDialog(builder.create()); + }); + } - if (isChannel) { - signCell = new TextCell(context, 23, false, true, null); - signCell.setBackgroundDrawable(Theme.getSelectorDrawable(true)); - signCell.setTextAndCheckAndIcon(LocaleController.getString("ChannelSignMessages", R.string.ChannelSignMessages), signMessages, R.drawable.msg_signed, false); - typeEditContainer.addView(signCell, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - signCell.setOnClickListener(v -> { - signMessages = !signMessages; - ((TextCell) v).setChecked(signMessages); - }); - } else if (currentChat.creator) { - forumsCell = new TextCell(context, 23, false, true, null); - forumsCell.setBackgroundDrawable(Theme.getSelectorDrawable(true)); - forumsCell.setTextAndCheckAndIcon(LocaleController.getString("ChannelTopics", R.string.ChannelTopics), forum, R.drawable.msg_topics, false); - forumsCell.getCheckBox().setIcon(canForum ? 0 : R.drawable.permission_locked); - typeEditContainer.addView(forumsCell, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - forumsCell.setOnClickListener(v -> { - if (!canForum) { - CharSequence text; - if (!(info == null || info.linked_chat_id == 0)) { - text = AndroidUtilities.replaceTags(LocaleController.getString("ChannelTopicsDiscussionForbidden", R.string.ChannelTopicsDiscussionForbidden)); - } else { - text = AndroidUtilities.replaceTags(LocaleController.formatPluralString("ChannelTopicsForbidden", getMessagesController().forumUpgradeParticipantsMin)); + if (isChannel) { + signCell = new TextCell(context, 23, false, true, null); + signCell.setBackgroundDrawable(Theme.getSelectorDrawable(true)); + signCell.setTextAndCheckAndIcon(LocaleController.getString("ChannelSignMessages", R.string.ChannelSignMessages), signMessages, R.drawable.msg_signed, false); + typeEditContainer.addView(signCell, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + signCell.setOnClickListener(v -> { + signMessages = !signMessages; + ((TextCell) v).setChecked(signMessages); + }); + } else if (currentChat.creator) { + forumsCell = new TextCell(context, 23, false, true, null); + forumsCell.setBackgroundDrawable(Theme.getSelectorDrawable(true)); + forumsCell.setTextAndCheckAndIcon(LocaleController.getString("ChannelTopics", R.string.ChannelTopics), forum, R.drawable.msg_topics, false); + forumsCell.getCheckBox().setIcon(canForum ? 0 : R.drawable.permission_locked); + typeEditContainer.addView(forumsCell, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + forumsCell.setOnClickListener(v -> { + if (!canForum) { + CharSequence text; + if (!(info == null || info.linked_chat_id == 0)) { + text = AndroidUtilities.replaceTags(LocaleController.getString("ChannelTopicsDiscussionForbidden", R.string.ChannelTopicsDiscussionForbidden)); + } else { + text = AndroidUtilities.replaceTags(LocaleController.formatPluralString("ChannelTopicsForbidden", getMessagesController().forumUpgradeParticipantsMin)); + } + BulletinFactory.of(this).createSimpleBulletin(R.raw.topics, text).show(); + frameLayout.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + return; } - BulletinFactory.of(this).createSimpleBulletin(R.raw.topics, text).show(); - frameLayout.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); - return; - } - forum = !forum; - avatarImage.animateToRoundRadius(forum ? AndroidUtilities.dp(16) : AndroidUtilities.dp(32)); - ((TextCell) v).setChecked(forum); - updateFields(false, true); - }); + forum = !forum; + avatarImage.animateToRoundRadius(forum ? AndroidUtilities.dp(16) : AndroidUtilities.dp(32)); + ((TextCell) v).setChecked(forum); + updateFields(false, true); + }); + } } ActionBarMenu menu = actionBar.createMenu(); - if (ChatObject.canChangeChatInfo(currentChat) || signCell != null || historyCell != null) { + if (currentUser != null || ChatObject.canChangeChatInfo(currentChat) || signCell != null || historyCell != null) { doneButton = menu.addItemWithWidth(done_button, R.drawable.ic_ab_done, AndroidUtilities.dp(56)); doneButton.setContentDescription(LocaleController.getString("Done", R.string.Done)); } @@ -845,139 +991,197 @@ public void afterTextChanged(Editable editable) { infoContainer.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); linearLayout1.addView(infoContainer, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - blockCell = new TextCell(context); - blockCell.setBackground(Theme.getSelectorDrawable(false)); - blockCell.setVisibility(ChatObject.isChannel(currentChat) || currentChat.creator || ChatObject.hasAdminRights(currentChat) && ChatObject.canChangeChatInfo(currentChat) ? View.VISIBLE : View.GONE); - blockCell.setOnClickListener(v -> { - Bundle args = new Bundle(); - args.putLong("chat_id", chatId); - args.putInt("type", !isChannel && !currentChat.gigagroup ? ChatUsersActivity.TYPE_KICKED : ChatUsersActivity.TYPE_BANNED); - ChatUsersActivity fragment = new ChatUsersActivity(args); - fragment.setInfo(info); - presentFragment(fragment); - }); - - inviteLinksCell = new TextCell(context); - inviteLinksCell.setBackground(Theme.getSelectorDrawable(false)); - inviteLinksCell.setOnClickListener(v -> { - ManageLinksActivity fragment = new ManageLinksActivity(chatId, 0, 0); - fragment.setInfo(info, info.exported_invite); - presentFragment(fragment); - }); + if (currentChat != null) { + blockCell = new TextCell(context); + blockCell.setBackground(Theme.getSelectorDrawable(false)); + blockCell.setVisibility(ChatObject.isChannel(currentChat) || currentChat.creator || ChatObject.hasAdminRights(currentChat) && ChatObject.canChangeChatInfo(currentChat) ? View.VISIBLE : View.GONE); + blockCell.setOnClickListener(v -> { + Bundle args = new Bundle(); + args.putLong("chat_id", chatId); + args.putInt("type", !isChannel && !currentChat.gigagroup ? ChatUsersActivity.TYPE_KICKED : ChatUsersActivity.TYPE_BANNED); + ChatUsersActivity fragment = new ChatUsersActivity(args); + fragment.setInfo(info); + presentFragment(fragment); + }); - reactionsCell = new TextCell(context); - reactionsCell.setBackground(Theme.getSelectorDrawable(false)); - reactionsCell.setOnClickListener(v -> { - Bundle args = new Bundle(); - args.putLong(ChatReactionsEditActivity.KEY_CHAT_ID, chatId); - ChatReactionsEditActivity reactionsEditActivity = new ChatReactionsEditActivity(args); - reactionsEditActivity.setInfo(info); - presentFragment(reactionsEditActivity); - }); + inviteLinksCell = new TextCell(context); + inviteLinksCell.setBackground(Theme.getSelectorDrawable(false)); + inviteLinksCell.setOnClickListener(v -> { + ManageLinksActivity fragment = new ManageLinksActivity(chatId, 0, 0); + fragment.setInfo(info, info.exported_invite); + presentFragment(fragment); + }); - adminCell = new TextCell(context); - adminCell.setBackground(Theme.getSelectorDrawable(false)); - adminCell.setOnClickListener(v -> { - Bundle args = new Bundle(); - args.putLong("chat_id", chatId); - args.putInt("type", ChatUsersActivity.TYPE_ADMIN); - ChatUsersActivity fragment = new ChatUsersActivity(args); - fragment.setInfo(info); - presentFragment(fragment); - }); + reactionsCell = new TextCell(context); + reactionsCell.setBackground(Theme.getSelectorDrawable(false)); + reactionsCell.setOnClickListener(v -> { + Bundle args = new Bundle(); + args.putLong(ChatReactionsEditActivity.KEY_CHAT_ID, chatId); + ChatReactionsEditActivity reactionsEditActivity = new ChatReactionsEditActivity(args); + reactionsEditActivity.setInfo(info); + presentFragment(reactionsEditActivity); + }); - membersCell = new TextCell(context); - membersCell.setBackgroundDrawable(Theme.getSelectorDrawable(false)); - membersCell.setOnClickListener(v -> { - Bundle args = new Bundle(); - args.putLong("chat_id", chatId); - args.putInt("type", ChatUsersActivity.TYPE_USERS); - ChatUsersActivity fragment = new ChatUsersActivity(args); - fragment.setInfo(info); - presentFragment(fragment); - }); + adminCell = new TextCell(context); + adminCell.setBackground(Theme.getSelectorDrawable(false)); + adminCell.setOnClickListener(v -> { + Bundle args = new Bundle(); + args.putLong("chat_id", chatId); + args.putInt("type", ChatUsersActivity.TYPE_ADMIN); + ChatUsersActivity fragment = new ChatUsersActivity(args); + fragment.setInfo(info); + presentFragment(fragment); + }); - if (!ChatObject.isChannelAndNotMegaGroup(currentChat)) { - memberRequestsCell = new TextCell(context); - memberRequestsCell.setBackground(Theme.getSelectorDrawable(false)); - memberRequestsCell.setOnClickListener(v -> { - MemberRequestsActivity activity = new MemberRequestsActivity(chatId); - presentFragment(activity); + membersCell = new TextCell(context); + membersCell.setBackgroundDrawable(Theme.getSelectorDrawable(false)); + membersCell.setOnClickListener(v -> { + Bundle args = new Bundle(); + args.putLong("chat_id", chatId); + args.putInt("type", ChatUsersActivity.TYPE_USERS); + ChatUsersActivity fragment = new ChatUsersActivity(args); + fragment.setInfo(info); + presentFragment(fragment); }); - } - if ((ChatObject.isChannel(currentChat) || currentChat.gigagroup) && ChatObject.hasAdminRights(currentChat)) { - logCell = new TextCell(context); - logCell.setTextAndIcon(LocaleController.getString("EventLog", R.string.EventLog), R.drawable.msg_log, false); - logCell.setBackground(Theme.getSelectorDrawable(false)); - logCell.setOnClickListener(v -> presentFragment(new ChannelAdminLogActivity(currentChat))); - } + if (!ChatObject.isChannelAndNotMegaGroup(currentChat)) { + memberRequestsCell = new TextCell(context); + memberRequestsCell.setBackground(Theme.getSelectorDrawable(false)); + memberRequestsCell.setOnClickListener(v -> { + MemberRequestsActivity activity = new MemberRequestsActivity(chatId); + presentFragment(activity); + }); + } - infoContainer.addView(reactionsCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + if (ChatObject.isChannel(currentChat) || currentChat.gigagroup) { + logCell = new TextCell(context); + logCell.setTextAndIcon(LocaleController.getString("EventLog", R.string.EventLog), R.drawable.msg_log, false); + logCell.setBackground(Theme.getSelectorDrawable(false)); + logCell.setOnClickListener(v -> presentFragment(new ChannelAdminLogActivity(currentChat))); + } - if (!isChannel && !currentChat.gigagroup) { - infoContainer.addView(blockCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - } - if (!isChannel) { - infoContainer.addView(inviteLinksCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - } - infoContainer.addView(adminCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - infoContainer.addView(membersCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - if (memberRequestsCell != null && info != null && info.requests_pending > 0) { - infoContainer.addView(memberRequestsCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - } - if (isChannel) { - infoContainer.addView(inviteLinksCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - } - if (isChannel || currentChat.gigagroup) { - infoContainer.addView(blockCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - } - if (logCell != null) { - infoContainer.addView(logCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - } + infoContainer.addView(reactionsCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - if (false && !ChatObject.hasAdminRights(currentChat)) { - infoContainer.setVisibility(View.GONE); - settingsTopSectionCell.setVisibility(View.GONE); + if (!isChannel && !currentChat.gigagroup) { + infoContainer.addView(blockCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + } + if (!isChannel) { + infoContainer.addView(inviteLinksCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + } + infoContainer.addView(adminCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + infoContainer.addView(membersCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + if (memberRequestsCell != null && info != null && info.requests_pending > 0) { + infoContainer.addView(memberRequestsCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + } + if (isChannel) { + infoContainer.addView(inviteLinksCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + } + if (isChannel || currentChat.gigagroup) { + infoContainer.addView(blockCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + } + if (!isChannel && info != null && info.can_set_stickers) { + stickersContainer = new FrameLayout(context); + stickersContainer.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + linearLayout1.addView(stickersContainer, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + + stickersCell = new TextCell(context); + stickersCell.setBackground(Theme.getSelectorDrawable(false)); + stickersCell.setOnClickListener(v -> presentFragment(new ChannelAdminLogActivity(currentChat))); + stickersCell.setPrioritizeTitleOverValue(true); + stickersContainer.addView(stickersCell, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + stickersCell.setOnClickListener(v -> { + GroupStickersActivity groupStickersActivity = new GroupStickersActivity(currentChat.id); + groupStickersActivity.setInfo(info); + presentFragment(groupStickersActivity); + }); + } else if (logCell != null) { + infoContainer.addView(logCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + } } - if (!isChannel && info != null && info.can_set_stickers) { - stickersContainer = new FrameLayout(context); - stickersContainer.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); - linearLayout1.addView(stickersContainer, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - - stickersCell = new TextCell(context); - stickersCell.setBackground(Theme.getSelectorDrawable(false)); - stickersCell.setOnClickListener(v -> presentFragment(new ChannelAdminLogActivity(currentChat))); - stickersCell.setPrioritizeTitleOverValue(true); - stickersContainer.addView(stickersCell, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - stickersCell.setOnClickListener(v -> { - GroupStickersActivity groupStickersActivity = new GroupStickersActivity(currentChat.id); - groupStickersActivity.setInfo(info); - presentFragment(groupStickersActivity); + if (currentUser != null) { + publicLinkCell = new TextCell(context); + publicLinkCell.setBackground(Theme.getSelectorDrawable(false)); + publicLinkCell.setPrioritizeTitleOverValue(true); + infoContainer.addView(publicLinkCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + publicLinkCell.setOnClickListener(v -> { + Bundle args = new Bundle(); + args.putLong("bot_id", userId); + presentFragment(new ChangeUsernameActivity(args)); }); - } - if (stickersCell == null) { - infoSectionCell = new ShadowSectionCell(context); - linearLayout1.addView(infoSectionCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + updatePublicLinksCount(); + + editIntroCell = new TextCell(context); + editIntroCell.setBackground(Theme.getSelectorDrawable(false)); + editIntroCell.setTextAndIcon(LocaleController.getString(R.string.BotEditIntro), R.drawable.msg_log, true); + infoContainer.addView(editIntroCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + editIntroCell.setOnClickListener(v -> Browser.openUrl(v.getContext(), "https://t.me/BotFather?start=" + getActiveUsername(currentUser) + "-intro")); + + editCommandsCell = new TextCell(context); + editCommandsCell.setBackground(Theme.getSelectorDrawable(false)); + editCommandsCell.setTextAndIcon(LocaleController.getString(R.string.BotEditCommands), R.drawable.msg_media, true); + infoContainer.addView(editCommandsCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + editCommandsCell.setOnClickListener(v -> Browser.openUrl(v.getContext(), "https://t.me/BotFather?start=" + getActiveUsername(currentUser) + "-commands")); + + changeBotSettingsCell = new TextCell(context); + changeBotSettingsCell.setBackground(Theme.getSelectorDrawable(false)); + changeBotSettingsCell.setTextAndIcon(LocaleController.getString(R.string.BotChangeSettings), R.drawable.msg_bot, true); + infoContainer.addView(changeBotSettingsCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + changeBotSettingsCell.setOnClickListener(v -> Browser.openUrl(v.getContext(), "https://t.me/BotFather?start=" + getActiveUsername(currentUser))); } - if (!isChannel && info != null && info.can_set_stickers) { - stickersInfoCell = new TextInfoPrivacyCell(context); - stickersInfoCell.setText(LocaleController.getString(R.string.GroupStickersInfo)); - linearLayout1.addView(stickersInfoCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + if (currentChat != null) { + if (!ChatObject.hasAdminRights(currentChat)) { + infoContainer.setVisibility(View.GONE); + settingsTopSectionCell.setVisibility(View.GONE); + } + + if (stickersCell == null) { + infoSectionCell = new ShadowSectionCell(context); + linearLayout1.addView(infoSectionCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + } + + if (!isChannel && info != null && info.can_set_stickers) { + stickersInfoCell = new TextInfoPrivacyCell(context); + stickersInfoCell.setText(LocaleController.getString(R.string.GroupStickersInfo)); + linearLayout1.addView(stickersInfoCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + } + } else { + botInfoCell = new TextInfoPrivacyCell(context); + String str = LocaleController.getString(R.string.BotManageInfo); + SpannableString span = SpannableString.valueOf(str); + int index = str.indexOf("@BotFather"); + if (index != -1) { + span.setSpan(new ClickableSpan() { + @Override + public void onClick(@NonNull View widget) { + Browser.openUrl(widget.getContext(), "https://t.me/BotFather"); + } + + @Override + public void updateDrawState(TextPaint ds) { + super.updateDrawState(ds); + ds.setUnderlineText(false); + } + }, index, index + 10, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + botInfoCell.setBackground(Theme.getThemedDrawableByKey(getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + botInfoCell.setText(span); + linearLayout1.addView(botInfoCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); } - if (currentChat.creator) { + if (currentUser == null && currentChat.creator) { deleteContainer = new FrameLayout(context); deleteContainer.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); linearLayout1.addView(deleteContainer, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); deleteCell = new TextSettingsCell(context); - deleteCell.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText5)); + deleteCell.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); deleteCell.setBackgroundDrawable(Theme.getSelectorDrawable(false)); - if (isChannel) { + if (currentUser != null) { + deleteCell.setText(LocaleController.getString(R.string.DeleteBot), false); + } else if (isChannel) { deleteCell.setText(LocaleController.getString("ChannelDelete", R.string.ChannelDelete), false); } else { deleteCell.setText(LocaleController.getString("DeleteAndExitButton", R.string.DeleteAndExitButton), false); @@ -994,25 +1198,27 @@ public void afterTextChanged(Editable editable) { }, null)); deleteInfoCell = new ShadowSectionCell(context); - deleteInfoCell.setBackground(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + deleteInfoCell.setBackground(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); linearLayout1.addView(deleteInfoCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); } if (stickersInfoCell != null) { if (deleteInfoCell == null) { - stickersInfoCell.setBackground(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + stickersInfoCell.setBackground(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } else { - stickersInfoCell.setBackground(Theme.getThemedDrawable(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + stickersInfoCell.setBackground(Theme.getThemedDrawableByKey(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); } } undoView = new UndoView(context); sizeNotifierFrameLayout.addView(undoView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM | Gravity.LEFT, 8, 0, 8, 8)); - nameTextView.setText(Emoji.replaceEmoji(currentChat.title, nameTextView.getEditText().getPaint().getFontMetricsInt(), AndroidUtilities.dp(16), true)); + nameTextView.setText(Emoji.replaceEmoji(currentUser != null ? ContactsController.formatName(currentUser) : currentChat.title, nameTextView.getEditText().getPaint().getFontMetricsInt(), AndroidUtilities.dp(16), true)); nameTextView.setSelection(nameTextView.length()); if (info != null) { descriptionTextView.setText(info.about); + } else if (userInfo != null) { + descriptionTextView.setText(userInfo.about); } setAvatar(); updateFields(true, false); @@ -1020,22 +1226,57 @@ public void afterTextChanged(Editable editable) { return fragmentView; } + private void updatePublicLinksCount() { + if (publicLinkCell == null) { + return; + } + if (currentUser.usernames.size() > 1) { + int usernamesActive = 0; + for (TLRPC.TL_username username : currentUser.usernames) { + if (username.active) { + usernamesActive++; + } + } + + publicLinkCell.setTextAndValueAndIcon(LocaleController.getString(R.string.BotPublicLinks), LocaleController.formatString(R.string.BotPublicLinksCount, usernamesActive, currentUser.usernames.size()), R.drawable.msg_link2, true); + } else { + publicLinkCell.setTextAndValueAndIcon(LocaleController.getString(R.string.BotPublicLink), "t.me/" + currentUser.username, R.drawable.msg_link2, true); + } + } + + private String getActiveUsername(TLRPC.User user) { + if (user.username != null) { + return user.username; + } + + for (TLRPC.TL_username username : user.usernames) { + if (username.active) { + return username.username; + } + } + + return null; + } + RLottieDrawable cameraDrawable; private void setAvatar() { - if (avatarImage == null) { + if (avatarImage == null || hasUploadedPhoto) { return; } TLRPC.Chat chat = getMessagesController().getChat(chatId); - if (chat == null) { + TLRPC.User user = userId == 0 ? null : getMessagesController().getUser(userId); + if (chat == null && user == null) { return; } + currentUser = user; currentChat = chat; boolean hasPhoto; - if (currentChat.photo != null) { - avatar = currentChat.photo.photo_small; - ImageLocation location = ImageLocation.getForUserOrChat(currentChat, ImageLocation.TYPE_SMALL); - avatarImage.setForUserOrChat(currentChat, avatarDrawable); + if (currentUser != null ? currentUser.photo != null : currentChat.photo != null) { + TLObject obj = currentUser != null ? currentUser : currentChat; + avatar = currentUser != null ? currentUser.photo.photo_small : currentChat.photo.photo_small; + ImageLocation location = ImageLocation.getForUserOrChat(obj, ImageLocation.TYPE_SMALL); + avatarImage.setForUserOrChat(obj, avatarDrawable); hasPhoto = location != null; } else { avatarImage.setImageDrawable(avatarDrawable); @@ -1050,7 +1291,6 @@ private void setAvatar() { if (cameraDrawable == null) { cameraDrawable = new RLottieDrawable(R.raw.camera_outline, "" + R.raw.camera_outline, AndroidUtilities.dp(50), AndroidUtilities.dp(50), false, null); } - setAvatarCell.imageView.setTranslationY(-AndroidUtilities.dp(9)); setAvatarCell.imageView.setTranslationX(-AndroidUtilities.dp(8)); setAvatarCell.imageView.setAnimation(cameraDrawable); } @@ -1060,6 +1300,10 @@ private void setAvatar() { } private void updateCanForum() { + if (userId != 0) { + canForum = false; + return; + } canForum = (forum || Math.max(info == null ? 0 : info.participants_count, currentChat.participants_count) >= getMessagesController().forumUpgradeParticipantsMin) && (info == null || info.linked_chat_id == 0); if (forumsCell != null) { forumsCell.getCheckBox().setIcon(canForum ? 0 : R.drawable.permission_locked); @@ -1088,6 +1332,9 @@ public void didReceivedNotification(int id, int account, Object... args) { if ((mask & MessagesController.UPDATE_MASK_AVATAR) != 0) { setAvatar(); } + if ((mask & MessagesController.UPDATE_MASK_NAME) != 0) { + updatePublicLinksCount(); + } } else if (id == NotificationCenter.chatAvailableReactionsUpdated) { long chatId = (long) args[0]; if (chatId == this.chatId) { @@ -1121,7 +1368,42 @@ public void didUploadPhoto(final TLRPC.InputFile photo, final TLRPC.InputFile vi AndroidUtilities.runOnUIThread(() -> { avatar = smallSize.location; if (photo != null || video != null || emojiMarkup != null) { - getMessagesController().changeChatAvatar(chatId, null, photo, video, emojiMarkup, videoStartTimestamp, videoPath, smallSize.location, bigSize.location, null); + if (userId != 0) { + if (currentUser != null) { + currentUser.photo = new TLRPC.TL_userProfilePhoto(); + currentUser.photo.photo_id = photo != null ? photo.id : video != null ? video.id : 0; + currentUser.photo.photo_big = bigSize.location; + currentUser.photo.photo_small = smallSize.location; + getMessagesController().putUser(currentUser, true); + } + + TLRPC.TL_photos_uploadProfilePhoto req = new TLRPC.TL_photos_uploadProfilePhoto(); + if (photo != null) { + req.file = photo; + req.flags |= 1; + } + if (video != null) { + req.video = video; + req.flags |= 2; + + req.video_start_ts = videoStartTimestamp; + req.flags |= 4; + } + if (emojiMarkup != null) { + req.video_emoji_markup = emojiMarkup; + req.flags |= 16; + } + req.bot = getMessagesController().getInputUser(currentUser); + req.flags |= 32; + + getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + hasUploadedPhoto = true; + + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.updateInterfaces, MessagesController.UPDATE_MASK_AVATAR); + })); + } else { + getMessagesController().changeChatAvatar(chatId, null, photo, video, emojiMarkup, videoStartTimestamp, videoPath, smallSize.location, bigSize.location, null); + } if (createAfterUpload) { try { if (progressDialog != null && progressDialog.isShowing()) { @@ -1136,12 +1418,11 @@ public void didUploadPhoto(final TLRPC.InputFile photo, final TLRPC.InputFile vi } showAvatarProgress(false, true); } else { - avatarImage.setImage(ImageLocation.getForLocal(avatar), "50_50", avatarDrawable, currentChat); + avatarImage.setImage(ImageLocation.getForLocal(avatar), "50_50", avatarDrawable, currentUser != null ? currentUser : currentChat); setAvatarCell.setTextAndIcon(LocaleController.getString("ChatSetNewPhoto", R.string.ChatSetNewPhoto), R.drawable.msg_addphoto, true); if (cameraDrawable == null) { cameraDrawable = new RLottieDrawable(R.raw.camera_outline, "" + R.raw.camera_outline, AndroidUtilities.dp(50), AndroidUtilities.dp(50), false, null); } - setAvatarCell.imageView.setTranslationY(-AndroidUtilities.dp(9)); setAvatarCell.imageView.setTranslationX(-AndroidUtilities.dp(8)); setAvatarCell.imageView.setAnimation(cameraDrawable); showAvatarProgress(true, false); @@ -1159,6 +1440,21 @@ public void showConvertTooltip() { } private boolean checkDiscard() { + if (userId != 0) { + String about = userInfo != null && userInfo.about != null ? userInfo.about : ""; + if (nameTextView != null && !currentUser.first_name.equals(nameTextView.getText().toString()) || + descriptionTextView != null && !about.equals(descriptionTextView.getText().toString())) { + showDialog(new AlertDialog.Builder(getParentActivity()) + .setTitle(LocaleController.getString("UserRestrictionsApplyChanges", R.string.UserRestrictionsApplyChanges)) + .setMessage(LocaleController.getString(R.string.BotSettingsChangedAlert)) + .setPositiveButton(LocaleController.getString("ApplyTheme", R.string.ApplyTheme), (dialogInterface, i) -> processDone()) + .setNegativeButton(LocaleController.getString("PassportDiscard", R.string.PassportDiscard), (dialog, which) -> finishFragment()) + .create()); + return false; + } + return true; + } + String about = info != null && info.about != null ? info.about : ""; if (info != null && ChatObject.isChannel(currentChat) && info.hidden_prehistory != historyHidden || nameTextView != null && !currentChat.title.equals(nameTextView.getText().toString()) || @@ -1239,6 +1535,43 @@ private void processDone() { return; } donePressed = true; + if (currentUser != null) { + TLRPC.TL_bots_setBotInfo req = new TLRPC.TL_bots_setBotInfo(); + req.bot = getMessagesController().getInputUser(currentUser); + req.flags |= 4; + req.lang_code = ""; + + if (!currentUser.first_name.equals(nameTextView.getText().toString())) { + req.name = nameTextView.getText().toString(); + req.flags |= 8; + } + + String about = userInfo != null && userInfo.about != null ? userInfo.about : ""; + if (descriptionTextView != null && !about.equals(descriptionTextView.getText().toString())) { + req.about = descriptionTextView.getText().toString(); + req.flags |= 1; + } + + progressDialog = new AlertDialog(getParentActivity(), AlertDialog.ALERT_TYPE_SPINNER); + int reqId = getConnectionsManager().sendRequest(req, (response, error) -> { + if (userInfo != null) { + userInfo.about = req.about; + getMessagesStorage().updateUserInfo(userInfo, false); + } + + AndroidUtilities.runOnUIThread(() -> { + progressDialog.dismiss(); + finishFragment(); + }); + }); + progressDialog.setOnCancelListener(dialog -> { + donePressed = false; + progressDialog = null; + getConnectionsManager().cancelRequest(reqId, true); + }); + progressDialog.show(); + return; + } if (!ChatObject.isChannel(currentChat) && (!historyHidden || forum)) { getMessagesController().convertToMegaGroup(getParentActivity(), chatId, this, param -> { if (param == 0) { @@ -1384,6 +1717,15 @@ public void restoreSelfArgs(Bundle args) { } } + public void setInfo(TLRPC.UserFull userFull) { + userInfo = userFull; + if (userFull != null) { + if (currentUser == null) { + currentUser = userId == 0 ? null : getMessagesController().getUser(userId); + } + } + } + public void setInfo(TLRPC.ChatFull chatFull) { info = chatFull; if (chatFull != null) { @@ -1762,7 +2104,7 @@ public ArrayList getThemeDescriptions() { themeDescriptions.add(new ThemeDescription(signCell, 0, new Class[]{TextCheckCell.class}, new String[]{"checkBox"}, null, null, null, Theme.key_switchTrackChecked)); themeDescriptions.add(new ThemeDescription(deleteCell, ThemeDescription.FLAG_SELECTOR, null, null, null, null, Theme.key_listSelector)); - themeDescriptions.add(new ThemeDescription(deleteCell, ThemeDescription.FLAG_TEXTCOLOR, new Class[]{TextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteRedText5)); + themeDescriptions.add(new ThemeDescription(deleteCell, ThemeDescription.FLAG_TEXTCOLOR, new Class[]{TextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_text_RedRegular)); themeDescriptions.add(new ThemeDescription(stickersCell, ThemeDescription.FLAG_SELECTOR, null, null, null, null, Theme.key_listSelector)); themeDescriptions.add(new ThemeDescription(stickersCell, ThemeDescription.FLAG_TEXTCOLOR, new Class[]{TextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatEditTypeActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatEditTypeActivity.java index e62a6c7ddb..668652132a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatEditTypeActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatEditTypeActivity.java @@ -471,7 +471,7 @@ public void showUsersForPermanentLink() { inviteLinkBottomSheet.show(); } }); - permanentLinkView.setUsers(0, null); + permanentLinkView.setUsers(0, null, false); privateContainer.addView(permanentLinkView); checkTextView = new TextInfoPrivacyCell(context) { @@ -482,7 +482,7 @@ public void setText(CharSequence text) { int index = tagsString.toString().indexOf('\n'); if (index >= 0) { tagsString.replace(index, index + 1, " "); - tagsString.setSpan(new ForegroundColorSpan(getThemedColor(Theme.key_windowBackgroundWhiteRedText4)), 0, index, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + tagsString.setSpan(new ForegroundColorSpan(getThemedColor(Theme.key_text_RedRegular)), 0, index, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } TypefaceSpan[] spans = tagsString.getSpans(0, tagsString.length(), TypefaceSpan.class); final String username = usernameTextView == null || usernameTextView.getText() == null ? "" : usernameTextView.getText().toString(); @@ -550,7 +550,7 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto prevHeight = getHeight(); } }; - checkTextView.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + checkTextView.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); checkTextView.setBottomPadding(6); linearLayout.addView(checkTextView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT)); @@ -1064,7 +1064,7 @@ public void onBindViewHolder(@NonNull ViewHolder holder, int position) { break; case VIEW_TYPE_HELP: ((TextInfoPrivacyCell) holder.itemView).setText(LocaleController.getString("UsernamesChannelHelp", R.string.UsernamesChannelHelp)); - ((TextInfoPrivacyCell) holder.itemView).setBackgroundDrawable(Theme.getThemedDrawable(getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + ((TextInfoPrivacyCell) holder.itemView).setBackgroundDrawable(Theme.getThemedDrawableByKey(getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); break; } } @@ -1296,8 +1296,8 @@ private void updatePrivatePublic() { } if (!isPrivate && !canCreatePublic && getUserConfig().isPremium()) { typeInfoCell.setText(LocaleController.getString("ChangePublicLimitReached", R.string.ChangePublicLimitReached)); - typeInfoCell.setTag(Theme.key_windowBackgroundWhiteRedText4); - typeInfoCell.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText4)); + typeInfoCell.setTag(Theme.key_text_RedRegular); + typeInfoCell.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); linkContainer.setVisibility(View.GONE); checkTextView.setVisibility(View.GONE); sectionCell2.setVisibility(View.GONE); @@ -1305,11 +1305,11 @@ private void updatePrivatePublic() { if (loadingAdminedChannels) { loadingAdminedCell.setVisibility(View.VISIBLE); adminnedChannelsLayout.setVisibility(View.GONE); - typeInfoCell.setBackgroundDrawable(checkTextView.getVisibility() == View.VISIBLE ? null : Theme.getThemedDrawable(typeInfoCell.getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + typeInfoCell.setBackgroundDrawable(checkTextView.getVisibility() == View.VISIBLE ? null : Theme.getThemedDrawableByKey(typeInfoCell.getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); adminedInfoCell.setBackgroundDrawable(null); } else { - adminedInfoCell.setBackgroundDrawable(Theme.getThemedDrawable(adminedInfoCell.getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); - typeInfoCell.setBackgroundDrawable(Theme.getThemedDrawable(typeInfoCell.getContext(), R.drawable.greydivider_top, Theme.key_windowBackgroundGrayShadow)); + adminedInfoCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(adminedInfoCell.getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + typeInfoCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(typeInfoCell.getContext(), R.drawable.greydivider_top, Theme.key_windowBackgroundGrayShadow)); loadingAdminedCell.setVisibility(View.GONE); adminnedChannelsLayout.setVisibility(View.VISIBLE); } @@ -1322,7 +1322,7 @@ private void updatePrivatePublic() { sectionCell2.setVisibility(View.VISIBLE); } adminedInfoCell.setVisibility(View.GONE); - typeInfoCell.setBackgroundDrawable(Theme.getThemedDrawable(typeInfoCell.getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + typeInfoCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(typeInfoCell.getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); adminnedChannelsLayout.setVisibility(View.GONE); linkContainer.setVisibility(View.VISIBLE); loadingAdminedCell.setVisibility(View.GONE); @@ -1344,10 +1344,10 @@ private void updatePrivatePublic() { checkTextView.setVisibility(!isPrivate && checkTextView.length() != 0 ? View.VISIBLE : View.GONE); manageLinksInfoCell.setText(LocaleController.getString("ManageLinksInfoHelp", R.string.ManageLinksInfoHelp)); if (isPrivate) { - typeInfoCell.setBackgroundDrawable(Theme.getThemedDrawable(typeInfoCell.getContext(), R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); - manageLinksInfoCell.setBackground(Theme.getThemedDrawable(typeInfoCell.getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + typeInfoCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(typeInfoCell.getContext(), R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + manageLinksInfoCell.setBackground(Theme.getThemedDrawableByKey(typeInfoCell.getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } else { - typeInfoCell.setBackgroundDrawable(checkTextView.getVisibility() == View.VISIBLE ? null : Theme.getThemedDrawable(typeInfoCell.getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + typeInfoCell.setBackgroundDrawable(checkTextView.getVisibility() == View.VISIBLE ? null : Theme.getThemedDrawableByKey(typeInfoCell.getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } } radioButtonCell1.setChecked(!isPrivate, true); @@ -1392,7 +1392,7 @@ private boolean checkUserName(final String name) { } else { checkTextView.setVisibility(View.GONE); } - typeInfoCell.setBackgroundDrawable(checkTextView.getVisibility() == View.VISIBLE ? null : Theme.getThemedDrawable(typeInfoCell.getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + typeInfoCell.setBackgroundDrawable(checkTextView.getVisibility() == View.VISIBLE ? null : Theme.getThemedDrawableByKey(typeInfoCell.getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); if (checkRunnable != null) { AndroidUtilities.cancelRunOnUIThread(checkRunnable); checkRunnable = null; @@ -1405,7 +1405,7 @@ private boolean checkUserName(final String name) { if (name != null) { if (name.startsWith("_") || name.endsWith("_")) { checkTextView.setText(LocaleController.getString("LinkInvalid", R.string.LinkInvalid)); - checkTextView.setTextColor(Theme.key_windowBackgroundWhiteRedText4); + checkTextView.setTextColorByKey(Theme.key_text_RedRegular); return false; } for (int a = 0; a < name.length(); a++) { @@ -1416,12 +1416,12 @@ private boolean checkUserName(final String name) { } else { checkTextView.setText(LocaleController.getString("LinkInvalidStartNumberMega", R.string.LinkInvalidStartNumberMega)); } - checkTextView.setTextColor(Theme.key_windowBackgroundWhiteRedText4); + checkTextView.setTextColorByKey(Theme.key_text_RedRegular); return false; } if (!(ch >= '0' && ch <= '9' || ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch == '_')) { checkTextView.setText(LocaleController.getString("LinkInvalid", R.string.LinkInvalid)); - checkTextView.setTextColor(Theme.key_windowBackgroundWhiteRedText4); + checkTextView.setTextColorByKey(Theme.key_text_RedRegular); return false; } } @@ -1432,17 +1432,17 @@ private boolean checkUserName(final String name) { } else { checkTextView.setText(LocaleController.getString("LinkInvalidShortMega", R.string.LinkInvalidShortMega)); } - checkTextView.setTextColor(Theme.key_windowBackgroundWhiteRedText4); + checkTextView.setTextColorByKey(Theme.key_text_RedRegular); return false; } if (name.length() > 32) { checkTextView.setText(LocaleController.getString("LinkInvalidLong", R.string.LinkInvalidLong)); - checkTextView.setTextColor(Theme.key_windowBackgroundWhiteRedText4); + checkTextView.setTextColorByKey(Theme.key_text_RedRegular); return false; } checkTextView.setText(LocaleController.getString("LinkChecking", R.string.LinkChecking)); - checkTextView.setTextColor(Theme.key_windowBackgroundWhiteGrayText8); + checkTextView.setTextColorByKey(Theme.key_windowBackgroundWhiteGrayText8); lastCheckName = name; checkRunnable = () -> { TLRPC.TL_channels_checkUsername req = new TLRPC.TL_channels_checkUsername(); @@ -1453,12 +1453,12 @@ private boolean checkUserName(final String name) { if (lastCheckName != null && lastCheckName.equals(name)) { if (error == null && response instanceof TLRPC.TL_boolTrue) { checkTextView.setText(LocaleController.formatString("LinkAvailable", R.string.LinkAvailable, name)); - checkTextView.setTextColor(Theme.key_windowBackgroundWhiteGreenText); + checkTextView.setTextColorByKey(Theme.key_windowBackgroundWhiteGreenText); lastNameAvailable = true; } else { if (error != null && "USERNAME_INVALID".equals(error.text) && req.username.length() == 4) { checkTextView.setText(LocaleController.getString("UsernameInvalidShort", R.string.UsernameInvalidShort)); - checkTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText4)); + checkTextView.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); } else if (error != null && "USERNAME_PURCHASE_AVAILABLE".equals(error.text)) { if (req.username.length() == 4) { checkTextView.setText(LocaleController.getString("UsernameInvalidShortPurchase", R.string.UsernameInvalidShortPurchase)); @@ -1471,7 +1471,7 @@ private boolean checkUserName(final String name) { showPremiumIncreaseLimitDialog(); } else { checkTextView.setText(LocaleController.getString("LinkInUse", R.string.LinkInUse)); - checkTextView.setTextColor(Theme.key_windowBackgroundWhiteRedText4); + checkTextView.setTextColorByKey(Theme.key_text_RedRegular); } lastNameAvailable = false; } @@ -1547,7 +1547,7 @@ public ArrayList getThemeDescriptions() { themeDescriptions.add(new ThemeDescription(infoCell, 0, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText4)); themeDescriptions.add(new ThemeDescription(textCell, ThemeDescription.FLAG_SELECTOR, null, null, null, null, Theme.key_listSelector)); - themeDescriptions.add(new ThemeDescription(textCell, ThemeDescription.FLAG_TEXTCOLOR, new Class[]{TextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteRedText5)); + themeDescriptions.add(new ThemeDescription(textCell, ThemeDescription.FLAG_TEXTCOLOR, new Class[]{TextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_text_RedRegular)); themeDescriptions.add(new ThemeDescription(textCell2, ThemeDescription.FLAG_SELECTOR, null, null, null, null, Theme.key_listSelector)); themeDescriptions.add(new ThemeDescription(textCell2, ThemeDescription.FLAG_TEXTCOLOR, new Class[]{TextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); @@ -1567,21 +1567,21 @@ public ArrayList getThemeDescriptions() { themeDescriptions.add(new ThemeDescription(saveRestrictCell, 0, new Class[]{TextCheckCell.class}, new String[]{"checkBox"}, null, null, null, Theme.key_switchTrack)); themeDescriptions.add(new ThemeDescription(saveRestrictCell, 0, new Class[]{TextCheckCell.class}, new String[]{"checkBox"}, null, null, null, Theme.key_switchTrackChecked)); - themeDescriptions.add(new ThemeDescription(checkTextView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteRedText4)); + themeDescriptions.add(new ThemeDescription(checkTextView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_text_RedRegular)); themeDescriptions.add(new ThemeDescription(checkTextView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText8)); themeDescriptions.add(new ThemeDescription(checkTextView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteGreenText)); themeDescriptions.add(new ThemeDescription(typeInfoCell, ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{TextInfoPrivacyCell.class}, null, null, null, Theme.key_windowBackgroundGrayShadow)); themeDescriptions.add(new ThemeDescription(typeInfoCell, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText4)); - themeDescriptions.add(new ThemeDescription(typeInfoCell, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteRedText4)); + themeDescriptions.add(new ThemeDescription(typeInfoCell, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_text_RedRegular)); themeDescriptions.add(new ThemeDescription(manageLinksInfoCell, ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{TextInfoPrivacyCell.class}, null, null, null, Theme.key_windowBackgroundGrayShadow)); themeDescriptions.add(new ThemeDescription(manageLinksInfoCell, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText4)); - themeDescriptions.add(new ThemeDescription(manageLinksInfoCell, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteRedText4)); + themeDescriptions.add(new ThemeDescription(manageLinksInfoCell, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_text_RedRegular)); themeDescriptions.add(new ThemeDescription(saveRestrictInfoCell, ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{TextInfoPrivacyCell.class}, null, null, null, Theme.key_windowBackgroundGrayShadow)); themeDescriptions.add(new ThemeDescription(saveRestrictInfoCell, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText4)); - themeDescriptions.add(new ThemeDescription(saveRestrictInfoCell, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteRedText4)); + themeDescriptions.add(new ThemeDescription(saveRestrictInfoCell, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_text_RedRegular)); themeDescriptions.add(new ThemeDescription(adminedInfoCell, ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{TextInfoPrivacyCell.class}, null, null, null, Theme.key_windowBackgroundGrayShadow)); themeDescriptions.add(new ThemeDescription(adminnedChannelsLayout, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_windowBackgroundWhite)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatLinkActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatLinkActivity.java index 85bb50bca5..00b0f6bd58 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatLinkActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatLinkActivity.java @@ -465,7 +465,7 @@ public void didFailChatCreation() { showDialog(dialog); TextView button = (TextView) dialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } } @@ -871,7 +871,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType break; case 1: view = new TextInfoPrivacyCell(mContext); - view.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + view.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); break; case 2: view = new ManageChatTextCell(mContext); @@ -986,14 +986,14 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { ManageChatTextCell actionCell = (ManageChatTextCell) holder.itemView; if (isChannel) { if (info.linked_chat_id != 0) { - actionCell.setColors(Theme.key_windowBackgroundWhiteRedText5, Theme.key_windowBackgroundWhiteRedText5); + actionCell.setColors(Theme.key_text_RedRegular, Theme.key_text_RedRegular); actionCell.setText(LocaleController.getString("DiscussionUnlinkGroup", R.string.DiscussionUnlinkGroup), null, R.drawable.actions_remove_user, false); } else { actionCell.setColors(Theme.key_windowBackgroundWhiteBlueIcon, Theme.key_windowBackgroundWhiteBlueButton); actionCell.setText(LocaleController.getString("DiscussionCreateGroup", R.string.DiscussionCreateGroup), null, R.drawable.menu_groups, true); } } else { - actionCell.setColors(Theme.key_windowBackgroundWhiteRedText5, Theme.key_windowBackgroundWhiteRedText5); + actionCell.setColors(Theme.key_text_RedRegular, Theme.key_text_RedRegular); actionCell.setText(LocaleController.getString("DiscussionUnlinkChannel", R.string.DiscussionUnlinkChannel), null, R.drawable.actions_remove_user, false); } break; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatPullingDownDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatPullingDownDrawable.java index d529993170..44c1e1a180 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatPullingDownDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatPullingDownDrawable.java @@ -691,9 +691,8 @@ public void reset() { animateCheck = false; } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } private Paint getThemedPaint(String paintKey) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatReactionsEditActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatReactionsEditActivity.java index fa7da3b927..867903c315 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatReactionsEditActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatReactionsEditActivity.java @@ -365,7 +365,7 @@ public ArrayList getThemeDescriptions() { Theme.key_listSelector, Theme.key_windowBackgroundGray, Theme.key_windowBackgroundWhiteGrayText4, - Theme.key_windowBackgroundWhiteRedText4, + Theme.key_text_RedRegular, Theme.key_windowBackgroundChecked, Theme.key_windowBackgroundCheckText, Theme.key_switchTrackBlue, diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatRightsEditActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatRightsEditActivity.java index ef8d0aa940..db1c1a9335 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatRightsEditActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatRightsEditActivity.java @@ -1395,7 +1395,7 @@ private void setTextLeft(View cell) { if (left <= MAX_RANK_LENGTH - MAX_RANK_LENGTH * 0.7f) { headerCell.setText2(String.format("%d", left)); TextView textView = headerCell.getTextView2(); - String key = left < 0 ? Theme.key_windowBackgroundWhiteRedText5 : Theme.key_windowBackgroundWhiteGrayText3; + int key = left < 0 ? Theme.key_text_RedRegular : Theme.key_windowBackgroundWhiteGrayText3; textView.setTextColor(Theme.getColor(key)); textView.setTag(key); } else { @@ -1537,7 +1537,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType break; case VIEW_TYPE_INFO_CELL: view = new TextInfoPrivacyCell(mContext); - view.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + view.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); break; case VIEW_TYPE_TRANSFER_CELL: default: @@ -1571,7 +1571,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType addBotButtonText.setGravity(Gravity.CENTER); addBotButtonText.setText(LocaleController.getString("AddBotButton", R.string.AddBotButton) + " " + (asAdmin ? LocaleController.getString("AddBotButtonAsAdmin", R.string.AddBotButtonAsAdmin) : LocaleController.getString("AddBotButtonAsMember", R.string.AddBotButtonAsMember))); addBotButton.addView(addBotButtonText, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); - addBotButton.setBackground(Theme.AdaptiveRipple.filledRect(Theme.key_featuredStickers_addButton, 4)); + addBotButton.setBackground(Theme.AdaptiveRipple.filledRectByKey(Theme.key_featuredStickers_addButton, 4)); addBotButton.setOnClickListener(e -> onDonePressed()); addBotButtonContainer.addView(addBotButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.FILL, 14, 28, 14, 14)); addBotButtonContainer.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); @@ -1683,8 +1683,8 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { case VIEW_TYPE_TRANSFER_CELL: TextSettingsCell actionCell = (TextSettingsCell) holder.itemView; if (position == removeAdminRow) { - actionCell.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText5)); - actionCell.setTag(Theme.key_windowBackgroundWhiteRedText5); + actionCell.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); + actionCell.setTag(Theme.key_text_RedRegular); if (currentType == TYPE_ADMIN) { actionCell.setText(LocaleController.getString("EditAdminRemoveAdmin", R.string.EditAdminRemoveAdmin), false); } else if (currentType == TYPE_BANNED) { @@ -1851,13 +1851,13 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { shadowCell.setAlpha(1); } if (position == rightsShadowRow) { - shadowCell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, removeAdminRow == -1 && rankRow == -1 ? R.drawable.greydivider_bottom : R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + shadowCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, removeAdminRow == -1 && rankRow == -1 ? R.drawable.greydivider_bottom : R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); } else if (position == removeAdminShadowRow) { - shadowCell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + shadowCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } else if (position == rankInfoRow) { - shadowCell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, canEdit ? R.drawable.greydivider : R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + shadowCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, canEdit ? R.drawable.greydivider : R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } else { - shadowCell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + shadowCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); } break; case VIEW_TYPE_UNTIL_DATE_CELL: @@ -2126,7 +2126,7 @@ public ArrayList getThemeDescriptions() { themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{TextInfoPrivacyCell.class}, null, null, null, Theme.key_windowBackgroundGrayShadow)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText4)); - themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteRedText5)); + themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_text_RedRegular)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{TextSettingsCell.class}, new String[]{"valueTextView"}, null, null, null, Theme.key_windowBackgroundWhiteValueText)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{TextSettingsCell.class}, new String[]{"valueImageView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayIcon)); @@ -2142,7 +2142,7 @@ public ArrayList getThemeDescriptions() { themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{ShadowSectionCell.class}, null, null, null, Theme.key_windowBackgroundGrayShadow)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{HeaderCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueHeader)); - themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{HeaderCell.class}, new String[]{"textView2"}, null, null, null, Theme.key_windowBackgroundWhiteRedText5)); + themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{HeaderCell.class}, new String[]{"textView2"}, null, null, null, Theme.key_text_RedRegular)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{HeaderCell.class}, new String[]{"textView2"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText3)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_TEXTCOLOR, new Class[]{PollEditTextCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatUsersActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatUsersActivity.java index 5505149728..e314d52c19 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatUsersActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatUsersActivity.java @@ -14,6 +14,7 @@ import android.animation.ObjectAnimator; import android.content.Context; import android.graphics.Canvas; +import android.graphics.drawable.ColorDrawable; import android.os.Bundle; import android.text.SpannableStringBuilder; import android.text.Spanned; @@ -34,6 +35,7 @@ import androidx.recyclerview.widget.RecyclerView; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.BotWebViewVibrationEffect; import org.telegram.messenger.ChatObject; import org.telegram.messenger.FileLog; @@ -69,6 +71,7 @@ import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.FlickerLoadingView; import org.telegram.ui.Components.GigagroupConvertAlert; +import org.telegram.ui.Components.ItemOptions; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.RadialProgressView; import org.telegram.ui.Components.RecyclerListView; @@ -447,9 +450,11 @@ private void updateRows() { loadingUserCellRow = rowCount++; } } else if (type == TYPE_USERS) { - if (!ChatObject.isChannelAndNotMegaGroup(currentChat) && !needOpenSearch) { - hideMembersRow = rowCount++; - hideMembersInfoRow = rowCount++; + if (ChatObject.isChannel(currentChat)) { + if (!ChatObject.isChannelAndNotMegaGroup(currentChat) && !needOpenSearch) { + hideMembersRow = rowCount++; + hideMembersInfoRow = rowCount++; + } } if (selectType == SELECT_TYPE_MEMBERS && ChatObject.canAddUsers(currentChat)) { addNewRow = rowCount++; @@ -670,12 +675,12 @@ public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerVi }); DefaultItemAnimator itemAnimator = new DefaultItemAnimator() { - int animationIndex = -1; + AnimationNotificationsLocker notificationsLocker = new AnimationNotificationsLocker();; @Override protected void onAllAnimationsDone() { super.onAllAnimationsDone(); - getNotificationCenter().onAnimationFinish(animationIndex); + notificationsLocker.unlock(); } @Override @@ -685,7 +690,7 @@ public void runPendingAnimations() { boolean changesPending = !mPendingChanges.isEmpty(); boolean additionsPending = !mPendingAdditions.isEmpty(); if (removalsPending || movesPending || additionsPending || changesPending) { - animationIndex = getNotificationCenter().setAnimationInProgress(animationIndex, null); + notificationsLocker.lock(); } super.runPendingAnimations(); } @@ -904,7 +909,7 @@ public void needAddBot(TLRPC.User user) { } else if (position == antiSpamRow) { final TextCell textCell = (TextCell) view; // nagram: no this limit -// if (info != null && !info.antispam && info.participants_count < getMessagesController().telegramAntispamGroupSizeMin) { +// if (info != null && !info.antispam && getParticipantsCount() < getMessagesController().telegramAntispamGroupSizeMin) { if (false) { BulletinFactory.of(this).createSimpleBulletin(R.raw.msg_antispam, AndroidUtilities.replaceTags(LocaleController.formatPluralString("ChannelAntiSpamForbidden", getMessagesController().telegramAntispamGroupSizeMin))).show(); } else if (info != null && ChatObject.canUserDoAdminAction(currentChat, ChatObject.ACTION_DELETE_MESSAGES) && !antiSpamToggleLoading) { @@ -913,7 +918,7 @@ public void needAddBot(TLRPC.User user) { TLRPC.TL_channels_toggleAntiSpam req = new TLRPC.TL_channels_toggleAntiSpam(); req.channel = getMessagesController().getInputChannel(chatId); textCell.setChecked(req.enabled = (info.antispam = !info.antispam)); - textCell.getCheckBox().setIcon(ChatObject.canUserDoAdminAction(currentChat, ChatObject.ACTION_DELETE_MESSAGES) && (info == null || info.antispam || info.participants_count >= getMessagesController().telegramAntispamGroupSizeMin) ? 0 : R.drawable.permission_locked); + textCell.getCheckBox().setIcon(ChatObject.canUserDoAdminAction(currentChat, ChatObject.ACTION_DELETE_MESSAGES) && (info == null || info.antispam || getParticipantsCount() >= getMessagesController().telegramAntispamGroupSizeMin) ? 0 : R.drawable.permission_locked); getConnectionsManager().sendRequest(req, (res, err) -> { if (res != null) { getMessagesController().processUpdates((TLRPC.Updates) res, false); @@ -925,7 +930,7 @@ public void needAddBot(TLRPC.User user) { return; } textCell.setChecked(info.antispam = wasAntispam); - textCell.getCheckBox().setIcon(ChatObject.canUserDoAdminAction(currentChat, ChatObject.ACTION_DELETE_MESSAGES) && (info == null || !info.antispam || info.participants_count >= getMessagesController().telegramAntispamGroupSizeMin) ? 0 : R.drawable.permission_locked); + textCell.getCheckBox().setIcon(ChatObject.canUserDoAdminAction(currentChat, ChatObject.ACTION_DELETE_MESSAGES) && (info == null || !info.antispam || getParticipantsCount() >= getMessagesController().telegramAntispamGroupSizeMin) ? 0 : R.drawable.permission_locked); BulletinFactory.of(ChatUsersActivity.this).createSimpleBulletin(R.raw.error, LocaleController.getString("UnknownError", R.string.UnknownError)).show(); }); } @@ -935,7 +940,7 @@ public void needAddBot(TLRPC.User user) { return; } else if (position == hideMembersRow) { final TextCell textCell = (TextCell) view; - if (info != null && !info.participants_hidden && info.participants_count < getMessagesController().hiddenMembersGroupSizeMin) { + if (getParticipantsCount() < getMessagesController().hiddenMembersGroupSizeMin) { BulletinFactory.of(this).createSimpleBulletin(R.raw.contacts_sync_off, AndroidUtilities.replaceTags(LocaleController.formatPluralString("ChannelHiddenMembersForbidden", getMessagesController().hiddenMembersGroupSizeMin))).show(); } else if (info != null && ChatObject.canUserDoAdminAction(currentChat, ChatObject.ACTION_BLOCK_USERS) && !hideMembersToggleLoading) { hideMembersToggleLoading = true; @@ -943,7 +948,7 @@ public void needAddBot(TLRPC.User user) { TLRPC.TL_channels_toggleParticipantsHidden req = new TLRPC.TL_channels_toggleParticipantsHidden(); req.channel = getMessagesController().getInputChannel(chatId); textCell.setChecked(req.enabled = (info.participants_hidden = !info.participants_hidden)); - textCell.getCheckBox().setIcon(ChatObject.canUserDoAdminAction(currentChat, ChatObject.ACTION_BLOCK_USERS) && (info == null || info.participants_hidden || info.participants_count >= getMessagesController().hiddenMembersGroupSizeMin) ? 0 : R.drawable.permission_locked); + textCell.getCheckBox().setIcon(ChatObject.canUserDoAdminAction(currentChat, ChatObject.ACTION_BLOCK_USERS) && (info == null || info.participants_hidden || getParticipantsCount() >= getMessagesController().hiddenMembersGroupSizeMin) ? 0 : R.drawable.permission_locked); getConnectionsManager().sendRequest(req, (res, err) -> { if (res != null) { getMessagesController().processUpdates((TLRPC.Updates) res, false); @@ -955,7 +960,7 @@ public void needAddBot(TLRPC.User user) { return; } textCell.setChecked(info.participants_hidden = wasParticipantsHidden); - textCell.getCheckBox().setIcon(ChatObject.canUserDoAdminAction(currentChat, ChatObject.ACTION_BLOCK_USERS) && (info == null || !info.participants_hidden || info.participants_count >= getMessagesController().hiddenMembersGroupSizeMin) ? 0 : R.drawable.permission_locked); + textCell.getCheckBox().setIcon(ChatObject.canUserDoAdminAction(currentChat, ChatObject.ACTION_BLOCK_USERS) && (info == null || !info.participants_hidden || getParticipantsCount() >= getMessagesController().hiddenMembersGroupSizeMin) ? 0 : R.drawable.permission_locked); BulletinFactory.of(ChatUsersActivity.this).createSimpleBulletin(R.raw.error, LocaleController.getString("UnknownError", R.string.UnknownError)).show(); }); } @@ -1228,7 +1233,12 @@ public void didChangeOwner(TLRPC.User user) { } }); - listView.setOnItemLongClickListener((view, position) -> !(getParentActivity() == null || listView.getAdapter() != listViewAdapter) && createMenuForParticipant(listViewAdapter.getItem(position), false)); + listView.setOnItemLongClickListener((view, position) -> { + if (getParentActivity() != null && listView.getAdapter() == listViewAdapter) { + return createMenuForParticipant(listViewAdapter.getItem(position), false, view); + } + return false; + }); if (searchItem != null) { listView.setOnScrollListener(new RecyclerView.OnScrollListener() { @Override @@ -1260,6 +1270,17 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { return fragmentView; } + private int getParticipantsCount() { + if (info == null) { + return 0; + } + int count = info.participants_count; + if (info.participants != null && info.participants.participants != null) { + count = Math.max(count, info.participants.participants.size()); + } + return count; + } + private void setBannedRights(TLRPC.TL_chatBannedRights defaultBannedRights) { if (defaultBannedRights != null) { this.defaultBannedRights = defaultBannedRights; @@ -1655,7 +1676,7 @@ private void updateParticipantWithRights(TLRPC.ChannelParticipant channelPartici } } - private boolean createMenuForParticipant(final TLObject participant, boolean resultOnly) { + private boolean createMenuForParticipant(final TLObject participant, boolean resultOnly, View view) { if (participant == null || selectType != SELECT_TYPE_MEMBERS) { return false; } @@ -1697,132 +1718,84 @@ private boolean createMenuForParticipant(final TLObject participant, boolean res boolean allowSetAdmin = ChatObject.canAddAdmins(currentChat) && (participant instanceof TLRPC.TL_channelParticipant || participant instanceof TLRPC.TL_channelParticipantBanned || participant instanceof TLRPC.TL_chatParticipant || canEdit); boolean canEditAdmin = !(participant instanceof TLRPC.TL_channelParticipantAdmin || participant instanceof TLRPC.TL_channelParticipantCreator || participant instanceof TLRPC.TL_chatParticipantCreator || participant instanceof TLRPC.TL_chatParticipantAdmin) || canEdit; boolean editingAdmin = participant instanceof TLRPC.TL_channelParticipantAdmin || participant instanceof TLRPC.TL_chatParticipantAdmin; + boolean canChangePermission = ChatObject.canBlockUsers(currentChat) && canEditAdmin && !isChannel && ChatObject.isChannel(currentChat) && !currentChat.gigagroup; if (selectType == SELECT_TYPE_MEMBERS) { allowSetAdmin &= !UserObject.isDeleted(user); } - final ArrayList items; - final ArrayList actions; - final ArrayList icons; - if (!resultOnly) { - items = new ArrayList<>(); - actions = new ArrayList<>(); - icons = new ArrayList<>(); - } else { - items = null; - actions = null; - icons = null; + boolean result = allowSetAdmin || (ChatObject.canBlockUsers(currentChat) && canEditAdmin); + if (resultOnly || !result) { + return result; } - if (allowSetAdmin) { - if (resultOnly) { - return true; - } - items.add(editingAdmin ? LocaleController.getString("EditAdminRights", R.string.EditAdminRights) : LocaleController.getString("SetAsAdmin", R.string.SetAsAdmin)); - icons.add(R.drawable.actions_addadmin); - actions.add(0); - } - boolean hasRemove = false; - if (ChatObject.canBlockUsers(currentChat) && canEditAdmin) { - if (resultOnly) { - return true; - } - if (!isChannel) { - if (ChatObject.isChannel(currentChat) && !currentChat.gigagroup) { - items.add(LocaleController.getString("ChangePermissions", R.string.ChangePermissions)); - icons.add(R.drawable.actions_permissions); - actions.add(1); - } - items.add(LocaleController.getString("KickFromGroup", R.string.KickFromGroup)); - } else { - items.add(LocaleController.getString("ChannelRemoveUser", R.string.ChannelRemoveUser)); - } - icons.add(R.drawable.actions_remove_user); - actions.add(2); - hasRemove = true; - } - if (actions == null || actions.isEmpty()) { - return false; - } + Utilities.Callback openRightsFor = action -> + openRightsEdit2(peerId, date, participant, adminRights, bannedRights, rank, canEditAdmin, action, false); - AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); - builder.setItems(items.toArray(new CharSequence[actions.size()]), AndroidUtilities.toIntArray(icons), (dialogInterface, i) -> { - if (actions.get(i) == 2) { + ItemOptions.makeOptions(this, view) + .setScrimViewBackground(new ColorDrawable(Theme.getColor(Theme.key_windowBackgroundWhite))) + .addIf(allowSetAdmin, R.drawable.msg_admins, editingAdmin ? LocaleController.getString("EditAdminRights", R.string.EditAdminRights) : LocaleController.getString("SetAsAdmin", R.string.SetAsAdmin), () -> openRightsFor.run(0)) + .addIf(canChangePermission, R.drawable.msg_permissions, LocaleController.getString("ChangePermissions", R.string.ChangePermissions), () -> { + if (participant instanceof TLRPC.TL_channelParticipantAdmin || participant instanceof TLRPC.TL_chatParticipantAdmin) { + showDialog( + new AlertDialog.Builder(getParentActivity()) + .setTitle(LocaleController.getString("AppName", R.string.AppName)) + .setMessage(LocaleController.formatString("AdminWillBeRemoved", R.string.AdminWillBeRemoved, UserObject.getUserName(user))) + .setPositiveButton(LocaleController.getString("OK", R.string.OK), (dialog, which) -> openRightsFor.run(1)) + .setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null) + .create() + ); + } else { + openRightsFor.run(1); + } + }) + .addIf(ChatObject.canBlockUsers(currentChat) && canEditAdmin, R.drawable.msg_remove, isChannel ? LocaleController.getString("ChannelRemoveUser", R.string.ChannelRemoveUser) : LocaleController.getString("KickFromGroup", R.string.KickFromGroup), true, () -> { getMessagesController().deleteParticipantFromChat(chatId, user); removeParticipants(peerId); if (currentChat != null && user != null && BulletinFactory.canShowBulletin(this)) { BulletinFactory.createRemoveFromChatBulletin(this, user, currentChat.title).show(); } - } else { - if (actions.get(i) == 1 && canEditAdmin && (participant instanceof TLRPC.TL_channelParticipantAdmin || participant instanceof TLRPC.TL_chatParticipantAdmin)) { - AlertDialog.Builder builder2 = new AlertDialog.Builder(getParentActivity()); - builder2.setTitle(LocaleController.getString("AppName", R.string.AppName)); - builder2.setMessage(LocaleController.formatString("AdminWillBeRemoved", R.string.AdminWillBeRemoved, UserObject.getUserName(user))); - builder2.setPositiveButton(LocaleController.getString("OK", R.string.OK), (dialog, which) -> openRightsEdit2(peerId, date, participant, adminRights, bannedRights, rank, canEditAdmin, actions.get(i), false)); - builder2.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showDialog(builder2.create()); - } else { - openRightsEdit2(peerId, date, participant, adminRights, bannedRights, rank, canEditAdmin, actions.get(i), false); - } - } - }); - AlertDialog alertDialog = builder.create(); - showDialog(alertDialog); - if (hasRemove) { - alertDialog.setItemColor(items.size() - 1, Theme.getColor(Theme.key_dialogTextRed), Theme.getColor(Theme.key_dialogRedIcon)); - } + }) + .setMinWidth(190) + .show(); } else { - CharSequence[] items; - int[] icons; + + ItemOptions options = ItemOptions.makeOptions(this, view); + if (type == TYPE_KICKED && ChatObject.canBlockUsers(currentChat)) { - if (resultOnly) { - return true; - } - items = new CharSequence[]{ - LocaleController.getString("ChannelEditPermissions", R.string.ChannelEditPermissions), - LocaleController.getString("ChannelDeleteFromList", R.string.ChannelDeleteFromList)}; - icons = new int[]{ - R.drawable.actions_permissions, - R.drawable.chats_delete}; + options.add(R.drawable.msg_permissions, LocaleController.getString("ChannelEditPermissions", R.string.ChannelEditPermissions), () -> { + ChatRightsEditActivity fragment = new ChatRightsEditActivity(peerId, chatId, null, defaultBannedRights, bannedRights, rank, ChatRightsEditActivity.TYPE_BANNED, true, false, null); + fragment.setDelegate(new ChatRightsEditActivity.ChatRightsEditActivityDelegate() { + @Override + public void didSetRights(int rights, TLRPC.TL_chatAdminRights rightsAdmin, TLRPC.TL_chatBannedRights rightsBanned, String rank) { + if (participant instanceof TLRPC.ChannelParticipant) { + TLRPC.ChannelParticipant channelParticipant = (TLRPC.ChannelParticipant) participant; + channelParticipant.admin_rights = rightsAdmin; + channelParticipant.banned_rights = rightsBanned; + channelParticipant.rank = rank; + updateParticipantWithRights(channelParticipant, rightsAdmin, rightsBanned, 0, false); + } + } + + @Override + public void didChangeOwner(TLRPC.User user) { + onOwnerChaged(user); + } + }); + presentFragment(fragment); + }); + options.add(R.drawable.msg_delete, LocaleController.getString("ChannelDeleteFromList", R.string.ChannelDeleteFromList), true, () -> deletePeer(peerId)); } else if (type == TYPE_BANNED && ChatObject.canBlockUsers(currentChat)) { - if (resultOnly) { - return true; - } - items = new CharSequence[]{ - ChatObject.canAddUsers(currentChat) && peerId > 0 ? (isChannel ? LocaleController.getString("ChannelAddToChannel", R.string.ChannelAddToChannel) : LocaleController.getString("ChannelAddToGroup", R.string.ChannelAddToGroup)) : null, - LocaleController.getString("ChannelDeleteFromList", R.string.ChannelDeleteFromList)}; - icons = new int[]{ - R.drawable.actions_addmember2, - R.drawable.chats_delete}; - } else if (type == TYPE_ADMIN && ChatObject.canAddAdmins(currentChat) && canEdit) { - if (resultOnly) { - return true; - } - if (currentChat.creator || !(participant instanceof TLRPC.TL_channelParticipantCreator) && canEdit) { - items = new CharSequence[]{ - LocaleController.getString("EditAdminRights", R.string.EditAdminRights), - LocaleController.getString("ChannelRemoveUserAdmin", R.string.ChannelRemoveUserAdmin)}; - icons = new int[]{ - R.drawable.actions_addadmin, - R.drawable.actions_remove_user}; - } else { - items = new CharSequence[]{ - LocaleController.getString("ChannelRemoveUserAdmin", R.string.ChannelRemoveUserAdmin)}; - icons = new int[]{ - R.drawable.actions_remove_user}; + if (ChatObject.canAddUsers(currentChat) && peerId > 0) { + options.add(R.drawable.msg_contact_add, isChannel ? LocaleController.getString("ChannelAddToChannel", R.string.ChannelAddToChannel) : LocaleController.getString("ChannelAddToGroup", R.string.ChannelAddToGroup), () -> { + TLRPC.User user = getMessagesController().getUser(peerId); + getMessagesController().addUserToChat(chatId, user, 0, null, ChatUsersActivity.this, null); + }); } - } else { - items = null; - icons = null; - } - if (items == null) { - return false; - } - AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); - builder.setItems(items, icons, (dialogInterface, i) -> { - if (type == TYPE_ADMIN) { - if (i == 0 && items.length == 2) { + options.add(R.drawable.msg_delete, LocaleController.getString("ChannelDeleteFromList", R.string.ChannelDeleteFromList), true, () -> deletePeer(peerId)); + } else if (type == TYPE_ADMIN && ChatObject.canAddAdmins(currentChat) && canEdit) { + if (currentChat.creator || !(participant instanceof TLRPC.TL_channelParticipantCreator)) { + options.add(R.drawable.msg_admins, LocaleController.getString("EditAdminRights", R.string.EditAdminRights), () -> { ChatRightsEditActivity fragment = new ChatRightsEditActivity(peerId, chatId, adminRights, null, null, rank, ChatRightsEditActivity.TYPE_ADMIN, true, false, null); fragment.setDelegate(new ChatRightsEditActivity.ChatRightsEditActivityDelegate() { @Override @@ -1842,83 +1815,46 @@ public void didChangeOwner(TLRPC.User user) { } }); presentFragment(fragment); - } else { - getMessagesController().setUserAdminRole(chatId, getMessagesController().getUser(peerId), new TLRPC.TL_chatAdminRights(), "", !isChannel, ChatUsersActivity.this, false, false, null, null); - removeParticipants(peerId); - } - } else if (type == TYPE_BANNED || type == TYPE_KICKED) { - if (i == 0) { - if (type == TYPE_KICKED) { - ChatRightsEditActivity fragment = new ChatRightsEditActivity(peerId, chatId, null, defaultBannedRights, bannedRights, rank, ChatRightsEditActivity.TYPE_BANNED, true, false, null); - fragment.setDelegate(new ChatRightsEditActivity.ChatRightsEditActivityDelegate() { - @Override - public void didSetRights(int rights, TLRPC.TL_chatAdminRights rightsAdmin, TLRPC.TL_chatBannedRights rightsBanned, String rank) { - if (participant instanceof TLRPC.ChannelParticipant) { - TLRPC.ChannelParticipant channelParticipant = (TLRPC.ChannelParticipant) participant; - channelParticipant.admin_rights = rightsAdmin; - channelParticipant.banned_rights = rightsBanned; - channelParticipant.rank = rank; - updateParticipantWithRights(channelParticipant, rightsAdmin, rightsBanned, 0, false); - } - } - - @Override - public void didChangeOwner(TLRPC.User user) { - onOwnerChaged(user); - } - }); - presentFragment(fragment); - } else if (type == TYPE_BANNED) { - if (peerId > 0) { - TLRPC.User user = getMessagesController().getUser(peerId); - getMessagesController().addUserToChat(chatId, user, 0, null, ChatUsersActivity.this, null); - } - } - } else if (i == 1) { - TLRPC.TL_channels_editBanned req = new TLRPC.TL_channels_editBanned(); - req.participant = getMessagesController().getInputPeer(peerId); - req.channel = getMessagesController().getInputChannel(chatId); - req.banned_rights = new TLRPC.TL_chatBannedRights(); - getConnectionsManager().sendRequest(req, (response, error) -> { - if (response != null) { - final TLRPC.Updates updates = (TLRPC.Updates) response; - getMessagesController().processUpdates(updates, false); - if (!updates.chats.isEmpty()) { - AndroidUtilities.runOnUIThread(() -> { - TLRPC.Chat chat = updates.chats.get(0); - getMessagesController().loadFullChat(chat.id, 0, true); - }, 1000); - } - } - }); - } - if (i == 0 && type == TYPE_BANNED || i == 1) { - removeParticipants(participant); - } - } else { - if (i == 0) { - TLRPC.User user; - TLRPC.Chat chat; - if (peerId > 0) { - user = getMessagesController().getUser(peerId); - chat = null; - } else { - user = null; - chat = getMessagesController().getChat(-peerId); - } - getMessagesController().deleteParticipantFromChat(chatId, user, chat, false, false); - } + }); } - }); - AlertDialog alertDialog = builder.create(); - showDialog(alertDialog); - if (type == TYPE_ADMIN) { - alertDialog.setItemColor(items.length - 1, Theme.getColor(Theme.key_dialogTextRed), Theme.getColor(Theme.key_dialogRedIcon)); + options.add(R.drawable.msg_remove, LocaleController.getString("ChannelRemoveUserAdmin", R.string.ChannelRemoveUserAdmin), true, () -> { + getMessagesController().setUserAdminRole(chatId, getMessagesController().getUser(peerId), new TLRPC.TL_chatAdminRights(), "", !isChannel, ChatUsersActivity.this, false, false, null, null); + removeParticipants(peerId); + }); + } + + options.setScrimViewBackground(new ColorDrawable(Theme.getColor(Theme.key_windowBackgroundWhite))); + options.setMinWidth(190); + + boolean result = options.getItemsCount() > 0; + if (resultOnly || !result) { + return result; } + + options.show(); } return true; } + private void deletePeer(long peerId) { + TLRPC.TL_channels_editBanned req = new TLRPC.TL_channels_editBanned(); + req.participant = getMessagesController().getInputPeer(peerId); + req.channel = getMessagesController().getInputChannel(chatId); + req.banned_rights = new TLRPC.TL_chatBannedRights(); + getConnectionsManager().sendRequest(req, (response, error) -> { + if (response != null) { + final TLRPC.Updates updates = (TLRPC.Updates) response; + getMessagesController().processUpdates(updates, false); + if (!updates.chats.isEmpty()) { + AndroidUtilities.runOnUIThread(() -> { + TLRPC.Chat chat = updates.chats.get(0); + getMessagesController().loadFullChat(chat.id, 0, true); + }, 1000); + } + } + }); + } + @Override public void didReceivedNotification(int id, int account, Object... args) { if (id == NotificationCenter.chatInfoDidLoad) { @@ -2837,7 +2773,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType TLObject object = getItem((Integer) cell.getTag()); if (object instanceof TLRPC.ChannelParticipant) { TLRPC.ChannelParticipant participant = (TLRPC.ChannelParticipant) object; - return createMenuForParticipant(participant, !click); + return createMenuForParticipant(participant, !click, cell); } else { return false; } @@ -3056,7 +2992,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType manageChatUserCell.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); manageChatUserCell.setDelegate((cell, click) -> { TLObject participant = listViewAdapter.getItem((Integer) cell.getTag()); - return createMenuForParticipant(participant, !click); + return createMenuForParticipant(participant, !click, cell); }); view = manageChatUserCell; break; @@ -3078,7 +3014,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType } else { privacyCell.setText(LocaleController.getString(R.string.NoBlockedGroup2)); } - privacyCell.setBackground(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + privacyCell.setBackground(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); break; case 5: HeaderCell headerCell = new HeaderCell(mContext, Theme.key_windowBackgroundWhiteBlueHeader, 21, 11, false); @@ -3252,7 +3188,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { TextInfoPrivacyCell privacyCell = (TextInfoPrivacyCell) holder.itemView; if (position == antiSpamInfoRow) { privacyCell.setText(LocaleController.getString("ChannelAntiSpamInfo", R.string.ChannelAntiSpamInfo)); - privacyCell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + privacyCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); } else if (position == participantsInfoRow) { if (type == TYPE_BANNED || type == TYPE_KICKED) { if (isChannel) { @@ -3260,7 +3196,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } else { privacyCell.setText(LocaleController.getString("NoBlockedGroup2", R.string.NoBlockedGroup2)); } - privacyCell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + privacyCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } else if (type == TYPE_ADMIN) { if (addNewRow != -1) { if (isChannel) { @@ -3271,17 +3207,17 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } else { privacyCell.setText(""); } - privacyCell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + privacyCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } else if (type == TYPE_USERS) { if (!isChannel || selectType != SELECT_TYPE_MEMBERS) { privacyCell.setText(""); } else { privacyCell.setText(LocaleController.getString("ChannelMembersInfo", R.string.ChannelMembersInfo)); } - privacyCell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + privacyCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } } else if (position == slowmodeInfoRow) { - privacyCell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + privacyCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); int seconds = getSecondsForIndex(selectedSlowmode); if (info == null || seconds == 0) { privacyCell.setText(LocaleController.getString("SlowmodeInfoOff", R.string.SlowmodeInfoOff)); @@ -3289,7 +3225,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { privacyCell.setText(LocaleController.formatString("SlowmodeInfoSelected", R.string.SlowmodeInfoSelected, formatSeconds(seconds))); } } else if (position == hideMembersInfoRow) { - privacyCell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + privacyCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); privacyCell.setText(LocaleController.getString("ChannelHideMembersInfo", R.string.ChannelHideMembersInfo)); } else if (position == gigaInfoRow) { privacyCell.setText(LocaleController.getString("BroadcastGroupConvertInfo", R.string.BroadcastGroupConvertInfo)); @@ -3330,9 +3266,9 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { break; case 3: if (position == addNewSectionRow || type == TYPE_KICKED && position == participantsDividerRow && addNewRow == -1 && participantsStartRow == -1) { - holder.itemView.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + holder.itemView.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } else { - holder.itemView.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + holder.itemView.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); } break; case 5: @@ -3440,10 +3376,10 @@ public void run() { case 12: TextCell textCell = (TextCell) holder.itemView; if (position == antiSpamRow) { - textCell.getCheckBox().setIcon(ChatObject.canUserDoAdminAction(currentChat, ChatObject.ACTION_DELETE_MESSAGES) && (info == null || info.antispam || info.participants_count >= getMessagesController().telegramAntispamGroupSizeMin) ? 0 : R.drawable.permission_locked); + textCell.getCheckBox().setIcon(ChatObject.canUserDoAdminAction(currentChat, ChatObject.ACTION_DELETE_MESSAGES) && (info == null || info.antispam || getParticipantsCount() >= getMessagesController().telegramAntispamGroupSizeMin) ? 0 : R.drawable.permission_locked); textCell.setTextAndCheckAndIcon(LocaleController.getString("ChannelAntiSpam", R.string.ChannelAntiSpam), info != null && info.antispam, R.drawable.msg_policy, false); } else if (position == hideMembersRow) { - textCell.getCheckBox().setIcon(ChatObject.canUserDoAdminAction(currentChat, ChatObject.ACTION_BLOCK_USERS) && (info == null || info.participants_hidden || info.participants_count >= getMessagesController().hiddenMembersGroupSizeMin) ? 0 : R.drawable.permission_locked); + textCell.getCheckBox().setIcon(ChatObject.canUserDoAdminAction(currentChat, ChatObject.ACTION_BLOCK_USERS) && (info == null || info.participants_hidden || getParticipantsCount() >= getMessagesController().hiddenMembersGroupSizeMin) ? 0 : R.drawable.permission_locked); textCell.setTextAndCheck(LocaleController.getString("ChannelHideMembers", R.string.ChannelHideMembers), info != null && info.participants_hidden, false); } break; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/CodeFieldContainer.java b/TMessagesProj/src/main/java/org/telegram/ui/CodeFieldContainer.java index 3271e21bd2..ae32c4e6fe 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/CodeFieldContainer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/CodeFieldContainer.java @@ -58,7 +58,7 @@ protected void dispatchDraw(Canvas canvas) { } float successProgress = codeField.getSuccessProgress(); int focusClr = ColorUtils.blendARGB(Theme.getColor(Theme.key_windowBackgroundWhiteInputField), Theme.getColor(Theme.key_windowBackgroundWhiteInputFieldActivated), codeField.getFocusedProgress()); - int errorClr = ColorUtils.blendARGB(focusClr, Theme.getColor(Theme.key_dialogTextRed), codeField.getErrorProgress()); + int errorClr = ColorUtils.blendARGB(focusClr, Theme.getColor(Theme.key_text_RedBold), codeField.getErrorProgress()); paint.setColor(ColorUtils.blendARGB(errorClr, Theme.getColor(Theme.key_checkbox), successProgress)); AndroidUtilities.rectTmp.set(child.getLeft(), child.getTop(), child.getRight(), child.getBottom()); AndroidUtilities.rectTmp.inset(strokeWidth, strokeWidth); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/AlertsCreator.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/AlertsCreator.java index 2b329b823a..d3bb890534 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AlertsCreator.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AlertsCreator.java @@ -552,6 +552,8 @@ public static Dialog processError(int currentAccount, TLRPC.TL_error error, Base } else { showSimpleToast(fragment, error.text); } + } else if (request instanceof TLRPC.TL_payments_assignPlayMarketTransaction) { + showSimpleAlert(fragment, LocaleController.getString("PaymentConfirmationError", R.string.PaymentConfirmationError) + "\n" + error.text); } return null; @@ -847,7 +849,7 @@ public static void showBlockReportSpamReplyAlert(ChatActivity fragment, MessageO fragment.showDialog(dialog); TextView button = (TextView) dialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } @@ -936,15 +938,15 @@ public static void showBlockReportSpamAlert(BaseFragment fragment, long dialog_i fragment.showDialog(dialog); TextView button = (TextView) dialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } - public static void showCustomNotificationsDialog(BaseFragment parentFragment, long did, int topicId, int globalType, ArrayList exceptions, int currentAccount, MessagesStorage.IntCallback callback) { - showCustomNotificationsDialog(parentFragment, did, topicId, globalType, exceptions, currentAccount, callback, null); + public static void showCustomNotificationsDialog(BaseFragment parentFragment, long did, int topicId, int globalType, ArrayList exceptions, ArrayList autoExceptions, int currentAccount, MessagesStorage.IntCallback callback) { + showCustomNotificationsDialog(parentFragment, did, topicId, globalType, exceptions, autoExceptions, currentAccount, callback, null); } - public static void showCustomNotificationsDialog(BaseFragment parentFragment, long did, int topicId, int globalType, ArrayList exceptions, int currentAccount, MessagesStorage.IntCallback callback, MessagesStorage.IntCallback resultCallback) { + public static void showCustomNotificationsDialog(BaseFragment parentFragment, long did, int topicId, int globalType, ArrayList exceptions, ArrayList autoExceptions, int currentAccount, MessagesStorage.IntCallback callback, MessagesStorage.IntCallback resultCallback) { if (parentFragment == null || parentFragment.getParentActivity() == null) { return; } @@ -978,8 +980,8 @@ public static void showCustomNotificationsDialog(BaseFragment parentFragment, lo TextView textView = new TextView(parentFragment.getParentActivity()); Drawable drawable = parentFragment.getParentActivity().getResources().getDrawable(icons[a]); if (a == descriptions.length - 1) { - textView.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); - drawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_dialogRedIcon), PorterDuff.Mode.SRC_IN)); + textView.setTextColor(Theme.getColor(Theme.key_text_RedBold)); + drawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_text_RedRegular), PorterDuff.Mode.MULTIPLY)); } else { textView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); drawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_dialogIcon), PorterDuff.Mode.SRC_IN)); @@ -1030,7 +1032,7 @@ public static void showCustomNotificationsDialog(BaseFragment parentFragment, lo args.putLong("dialog_id", did); parentFragment.presentFragment(new ProfileNotificationsActivity(args)); } else { - parentFragment.presentFragment(new NotificationsCustomSettingsActivity(globalType, exceptions)); + parentFragment.presentFragment(new NotificationsCustomSettingsActivity(globalType, exceptions, autoExceptions)); } } else { int untilTime = ConnectionsManager.getInstance(currentAccount).getCurrentTime(); @@ -1160,31 +1162,34 @@ private static void checkPickerDate(NumberPicker dayPicker, NumberPicker monthPi } public static void showOpenUrlAlert(BaseFragment fragment, String url, boolean punycode, boolean ask) { - showOpenUrlAlert(fragment, url, punycode, true, ask, null, null); + showOpenUrlAlert(fragment, url, punycode, true, ask, false, null, null); } public static void showOpenUrlAlert(BaseFragment fragment, String url, boolean punycode, boolean tryTelegraph, boolean ask) { showOpenUrlAlert(fragment, url, punycode, tryTelegraph, ask, null, null); } - public static void showOpenUrlAlert(BaseFragment fragment, String url, boolean punycode, boolean ask, Theme.ResourcesProvider resourcesProvider) { - showOpenUrlAlert(fragment, url, punycode, true, ask, null, resourcesProvider); + public static void showOpenUrlAlert(BaseFragment fragment, String url, boolean punycode, boolean ask, Theme.ResourcesProvider resourcesProvider) { + showOpenUrlAlert(fragment, url, punycode, true, ask, false, null, resourcesProvider); } public static void showOpenUrlAlert(BaseFragment fragment, String url, boolean punycode, boolean tryTelegraph, boolean ask, Browser.Progress progress, Theme.ResourcesProvider resourcesProvider) { + showOpenUrlAlert(fragment, url, punycode, tryTelegraph, ask, false, progress, resourcesProvider); + } + + public static void showOpenUrlAlert(BaseFragment fragment, String url, boolean punycode, boolean tryTelegraph, boolean ask, boolean forceNotInternalForApps, Browser.Progress progress, Theme.ResourcesProvider resourcesProvider) { if (fragment == null || fragment.getParentActivity() == null) { return; } long inlineReturn = (fragment instanceof ChatActivity) ? ((ChatActivity) fragment).getInlineReturn() : 0; if (Browser.isInternalUrl(url, null) || !ask || NekoConfig.skipOpenLinkConfirm.Bool()) { - Browser.openUrl(fragment.getParentActivity(), Uri.parse(url), inlineReturn == 0, tryTelegraph, progress); + Browser.openUrl(fragment.getParentActivity(), Uri.parse(url), inlineReturn == 0, tryTelegraph, forceNotInternalForApps && checkInternalBotApp(url), progress); } else { String urlFinal = url; // if (punycode) { // try { // Uri uri = Uri.parse(url); -// String host = IDN.toASCII(uri.getHost(), IDN.ALLOW_UNASSIGNED); -// urlFinal = uri.getScheme() + "://" + host + uri.getPath(); +// urlFinal = Browser.replaceHostname(uri, IDN.toUnicode(uri.getHost(), IDN.ALLOW_UNASSIGNED)); // } catch (Exception e) { // FileLog.e(e, false); // urlFinal = url; @@ -1192,17 +1197,28 @@ public static void showOpenUrlAlert(BaseFragment fragment, String url, boolean p // } else { // urlFinal = url; // } + Runnable open = () -> Browser.openUrl(fragment.getParentActivity(), Uri.parse(url), inlineReturn == 0, tryTelegraph, progress); AlertDialog.Builder builder = new AlertDialog.Builder(fragment.getParentActivity(), resourcesProvider); builder.setTitle(LocaleController.getString("OpenUrlTitle", R.string.OpenUrlTitle)); - String format = LocaleController.getString("OpenUrlAlert2", R.string.OpenUrlAlert2); - int index = format.indexOf("%"); - SpannableStringBuilder stringBuilder = new SpannableStringBuilder(String.format(format, urlFinal)); + AlertDialog[] dialog = new AlertDialog[1]; + SpannableString link = new SpannableString(urlFinal); + link.setSpan(new URLSpan(urlFinal) { + @Override + public void onClick(View widget) { + open.run(); + if (dialog[0] != null) { + dialog[0].dismiss(); + } + } + }, 0, link.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + SpannableStringBuilder stringBuilder = new SpannableStringBuilder(LocaleController.getString("OpenUrlAlert2", R.string.OpenUrlAlert2)); + int index = stringBuilder.toString().indexOf("%1$s"); if (index >= 0) { - stringBuilder.setSpan(new URLSpan(urlFinal), index, index + urlFinal.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + stringBuilder.replace(index, index + 4, link); } builder.setMessage(stringBuilder); builder.setMessageTextViewClickable(false); - builder.setPositiveButton(LocaleController.getString("Open", R.string.Open), (dialogInterface, i) -> Browser.openUrl(fragment.getParentActivity(), Uri.parse(url), inlineReturn == 0, tryTelegraph, progress)); + builder.setPositiveButton(LocaleController.getString("Open", R.string.Open), (dialogInterface, i) -> open.run()); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); builder.setNeutralButton(LocaleController.getString("Copy", R.string.Copy), (dialogInterface, i) -> { try { @@ -1212,10 +1228,15 @@ public static void showOpenUrlAlert(BaseFragment fragment, String url, boolean p FileLog.e(e); } }); - fragment.showDialog(builder.create()); + fragment.showDialog(dialog[0] = builder.create()); } } + private static boolean checkInternalBotApp(String url) { + String path = Uri.parse(url).getPath(); + return path.matches("^/\\w*/[^\\d]*(?:\\?startapp=.*?|)$"); + } + public static AlertDialog createSupportAlert(BaseFragment fragment, Theme.ResourcesProvider resourcesProvider) { if (fragment == null || fragment.getParentActivity() == null) { return null; @@ -1410,6 +1431,181 @@ public static void createImportDialogAlert(BaseFragment fragment, String title, fragment.showDialog(alertDialog); } + public static void createBotLaunchAlert(BaseFragment fragment, TLRPC.User user, Runnable onConfirm, Runnable onDismiss) { + Context context = fragment.getContext(); + AlertDialog.Builder builder = new AlertDialog.Builder(context); + + TextView messageTextView = new TextView(context) { + @Override + public void setText(CharSequence text, BufferType type) { + text = Emoji.replaceEmoji(text, getPaint().getFontMetricsInt(), false); + super.setText(text, type); + } + }; + NotificationCenter.listenEmojiLoading(messageTextView); + messageTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); + messageTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + messageTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); + messageTextView.setText(LocaleController.getString(R.string.BotWebViewStartPermission)); + + FrameLayout frameLayout = new FrameLayout(context); + builder.setCustomViewOffset(6); + builder.setView(frameLayout); + + AvatarDrawable avatarDrawable = new AvatarDrawable(); + avatarDrawable.setTextSize(AndroidUtilities.dp(18)); + + BackupImageView imageView = new BackupImageView(context); + imageView.setRoundRadius(AndroidUtilities.dp(20)); + frameLayout.addView(imageView, LayoutHelper.createFrame(40, 40, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 22, 5, 22, 0)); + + TextView textView = new TextView(context); + textView.setTextColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuItem)); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); + textView.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + textView.setLines(1); + textView.setMaxLines(1); + textView.setSingleLine(true); + textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); + textView.setEllipsize(TextUtils.TruncateAt.END); + textView.setText(user.first_name); + frameLayout.addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, (LocaleController.isRTL ? 21 : 76), 11, (LocaleController.isRTL ? 76 : 21), 0)); + frameLayout.addView(messageTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 24, 57, 24, 1)); + + if (user != null) { + if (UserObject.isReplyUser(user)) { + avatarDrawable.setScaleSize(.8f); + avatarDrawable.setAvatarType(AvatarDrawable.AVATAR_TYPE_REPLIES); + imageView.setImage(null, null, avatarDrawable, user); + } else { + avatarDrawable.setScaleSize(1f); + avatarDrawable.setInfo(user); + imageView.setForUserOrChat(user, avatarDrawable); + } + } + + builder.setPositiveButton(LocaleController.getString(R.string.Start), (dialogInterface, i) -> { + if (onConfirm != null) { + onConfirm.run(); + } + }); + builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + fragment.showDialog(builder.create(), false, dialog -> { + if (onDismiss != null) { + onDismiss.run(); + } + }); + } + + public static void createBotLaunchAlert(BaseFragment fragment, TLRPC.TL_messages_botApp botApp, TLRPC.User user, AtomicBoolean allowWrite, Runnable loadBotSheet) { + Context context = fragment.getContext(); + CheckBoxCell[] cell = new CheckBoxCell[1]; + AlertDialog.Builder builder = new AlertDialog.Builder(context); + + TextView messageTextView = new TextView(context) { + @Override + public void setText(CharSequence text, BufferType type) { + text = Emoji.replaceEmoji(text, getPaint().getFontMetricsInt(), false); + super.setText(text, type); + } + }; + NotificationCenter.listenEmojiLoading(messageTextView); + messageTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); + messageTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + messageTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); + messageTextView.setText(LocaleController.getString(R.string.BotWebViewStartPermission)); + + FrameLayout frameLayout = new FrameLayout(context) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + if (cell[0] != null) { + setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight() + cell[0].getMeasuredHeight() + AndroidUtilities.dp(7)); + } + } + }; + builder.setCustomViewOffset(6); + builder.setView(frameLayout); + + AvatarDrawable avatarDrawable = new AvatarDrawable(); + avatarDrawable.setTextSize(AndroidUtilities.dp(18)); + + BackupImageView imageView = new BackupImageView(context); + imageView.setRoundRadius(AndroidUtilities.dp(20)); + frameLayout.addView(imageView, LayoutHelper.createFrame(40, 40, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 22, 5, 22, 0)); + + TextView titleView = new TextView(context); + titleView.setTextColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuItem)); + titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); + titleView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + titleView.setLines(1); + titleView.setMaxLines(1); + titleView.setSingleLine(true); + titleView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); + titleView.setEllipsize(TextUtils.TruncateAt.END); + titleView.setText(user.first_name); + + TextView subtitleView = new TextView(context); + subtitleView.setTextColor(Theme.getColor(Theme.key_dialogTextBlue)); + subtitleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + subtitleView.setLines(1); + subtitleView.setMaxLines(1); + subtitleView.setSingleLine(true); + subtitleView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); + subtitleView.setEllipsize(TextUtils.TruncateAt.END); + subtitleView.setOnClickListener(v -> { + Bundle args = new Bundle(); + args.putLong("user_id", user.id); + if (fragment.getMessagesController().checkCanOpenChat(args, fragment)) { + fragment.presentFragment(new ChatActivity(args)); + } + + builder.getDismissRunnable().run(); + }); + SpannableString ssb = SpannableString.valueOf(LocaleController.getString(R.string.MoreAboutThisBot) + " "); + ColoredImageSpan img = new ColoredImageSpan(R.drawable.attach_arrow_right); + img.setTopOffset(1); + img.setSize(AndroidUtilities.dp(10)); + ssb.setSpan(img, ssb.length() - 1, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + subtitleView.setText(ssb); + + frameLayout.addView(titleView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, (LocaleController.isRTL ? 21 : 76), 0, (LocaleController.isRTL ? 76 : 21), 0)); + frameLayout.addView(subtitleView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, (LocaleController.isRTL ? 21 : 76), 28, (LocaleController.isRTL ? 76 : 21), 0)); + frameLayout.addView(messageTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 24, 57, 24, 1)); + + if (botApp.request_write_access) { + allowWrite.set(true); + + cell[0] = new CheckBoxCell(context, 1, fragment.getResourceProvider()); + cell[0].setBackgroundDrawable(Theme.getSelectorDrawable(false)); + cell[0].setText(AndroidUtilities.replaceTags(LocaleController.formatString("OpenUrlOption2", R.string.OpenUrlOption2, UserObject.getUserName(user))), "", true, false); + cell[0].setPadding(LocaleController.isRTL ? AndroidUtilities.dp(16) : AndroidUtilities.dp(8), 0, LocaleController.isRTL ? AndroidUtilities.dp(8) : AndroidUtilities.dp(16), 0); + cell[0].setChecked(true, false); + frameLayout.addView(cell[0], LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.BOTTOM | Gravity.LEFT, 0, 0, 0, 0)); + cell[0].setOnClickListener(v -> { + CheckBoxCell cell1 = (CheckBoxCell) v; + allowWrite.set(!allowWrite.get()); + cell1.setChecked(allowWrite.get(), true); + }); + } + + if (user != null) { + if (UserObject.isReplyUser(user)) { + avatarDrawable.setScaleSize(.8f); + avatarDrawable.setAvatarType(AvatarDrawable.AVATAR_TYPE_REPLIES); + imageView.setImage(null, null, avatarDrawable, user); + } else { + avatarDrawable.setScaleSize(1f); + avatarDrawable.setInfo(user); + imageView.setForUserOrChat(user, avatarDrawable); + } + } + + builder.setPositiveButton(LocaleController.getString(R.string.Start), (dialogInterface, i) -> loadBotSheet.run()); + builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + fragment.showDialog(builder.create()); + } + public static void createClearOrDeleteDialogAlert(BaseFragment fragment, boolean clear, TLRPC.Chat chat, TLRPC.User user, boolean secret, boolean canDeleteHistory, MessagesStorage.BooleanCallback onProcessRunnable) { createClearOrDeleteDialogAlert(fragment, clear, false, false, chat, user, secret, false, canDeleteHistory, onProcessRunnable, null); } @@ -1716,7 +1912,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { fragment.showDialog(alertDialog); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } @@ -1827,7 +2023,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { fragment.showDialog(alertDialog); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } @@ -2191,7 +2387,7 @@ public static void showChatWithAdmin(BaseFragment fragment, TLRPC.User user, Str LinearLayout linearLayout = new LinearLayout(fragment.getParentActivity()); linearLayout.setOrientation(LinearLayout.VERTICAL); TextView messageTextView = new TextView(fragment.getParentActivity()); - linearLayout.addView(messageTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, 0, 24, 16, 24, 24)); + linearLayout.addView(messageTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, 0, 21, 0, 21, 8)); messageTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); messageTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); @@ -2208,8 +2404,7 @@ public static void showChatWithAdmin(BaseFragment fragment, TLRPC.User user, Str buttonTextView.setTextColor(Theme.getColor(Theme.key_featuredStickers_buttonText)); buttonTextView.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(6), Theme.getColor(Theme.key_featuredStickers_addButton), Theme.getColor(Theme.key_featuredStickers_addButtonPressed))); - linearLayout.addView(buttonTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48, 0, 24, 15, 16, 24)); - + linearLayout.addView(buttonTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48, 0, 16, 12, 16, 8)); builder.setCustomView(linearLayout); BottomSheet bottomSheet = builder.show(); @@ -2376,7 +2571,7 @@ public static void createBlockDialogAlert(BaseFragment fragment, int count, bool fragment.showDialog(alertDialog); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } @@ -2790,7 +2985,11 @@ public CharSequence getAccessibilityClassName() { final String week = loc.formatterWeek.format(date) + ", "; if (year == currentYear) { - return week + loc.formatterScheduleDay.format(date); + return ( + LocaleController.getInstance().formatterWeek.format(date) + + ", " + + LocaleController.getInstance().formatterScheduleDay.format(date) + ); } else { return week + loc.formatterScheduleYear.format(date); } @@ -2845,7 +3044,7 @@ public CharSequence getAccessibilityClassName() { buttonTextView.setTextColor(datePickerColors.buttonTextColor); buttonTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); buttonTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - buttonTextView.setBackground(Theme.AdaptiveRipple.filledRect(datePickerColors.buttonBackgroundColor, 4)); + buttonTextView.setBackground(Theme.AdaptiveRipple.filledRect(datePickerColors.buttonBackgroundColor, 8)); container.addView(buttonTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT | Gravity.BOTTOM, 16, 15, 16, 16)); buttonTextView.setOnClickListener(v -> { canceled[0] = false; @@ -4000,35 +4199,51 @@ public static BottomSheet createMuteAlert(BaseFragment fragment, ArrayList return builder.create(); } - public static void sendReport(TLRPC.InputPeer peer, int type, String message, ArrayList messages) { - TLRPC.TL_messages_report request = new TLRPC.TL_messages_report(); - request.peer = peer; - request.id.addAll(messages); - request.message = message; + public static void sendReport(TLRPC.InputPeer peer, int type, String message, ArrayList messages, int storyId) { + TLRPC.ReportReason reason = null; if (type == AlertsCreator.REPORT_TYPE_SPAM) { - request.reason = new TLRPC.TL_inputReportReasonSpam(); + reason = new TLRPC.TL_inputReportReasonSpam(); } else if (type == AlertsCreator.REPORT_TYPE_FAKE_ACCOUNT) { - request.reason = new TLRPC.TL_inputReportReasonFake(); + reason = new TLRPC.TL_inputReportReasonFake(); } else if (type == AlertsCreator.REPORT_TYPE_VIOLENCE) { - request.reason = new TLRPC.TL_inputReportReasonViolence(); + reason = new TLRPC.TL_inputReportReasonViolence(); } else if (type == AlertsCreator.REPORT_TYPE_CHILD_ABUSE) { - request.reason = new TLRPC.TL_inputReportReasonChildAbuse(); + reason = new TLRPC.TL_inputReportReasonChildAbuse(); } else if (type == AlertsCreator.REPORT_TYPE_PORNOGRAPHY) { - request.reason = new TLRPC.TL_inputReportReasonPornography(); + reason = new TLRPC.TL_inputReportReasonPornography(); } else if (type == AlertsCreator.REPORT_TYPE_ILLEGAL_DRUGS) { - request.reason = new TLRPC.TL_inputReportReasonIllegalDrugs(); + reason = new TLRPC.TL_inputReportReasonIllegalDrugs(); } else if (type == AlertsCreator.REPORT_TYPE_PERSONAL_DETAILS) { - request.reason = new TLRPC.TL_inputReportReasonPersonalDetails(); + reason = new TLRPC.TL_inputReportReasonPersonalDetails(); } else if (type == AlertsCreator.REPORT_TYPE_OTHER) { - request.reason = new TLRPC.TL_inputReportReasonOther(); + reason = new TLRPC.TL_inputReportReasonOther(); } - ConnectionsManager.getInstance(UserConfig.selectedAccount).sendRequest(request, (response, error) -> { + if (reason == null) { + return; + } + if (storyId != 0) { + TLRPC.TL_stories_report request = new TLRPC.TL_stories_report(); + request.user_id = MessagesController.getInstance(UserConfig.selectedAccount).getInputUser(peer.user_id); + request.id.add(storyId); + request.message = message; + request.reason = reason; + ConnectionsManager.getInstance(UserConfig.selectedAccount).sendRequest(request, (response, error) -> { - }); + }); + } else { + TLRPC.TL_messages_report request = new TLRPC.TL_messages_report(); + request.peer = peer; + request.id.addAll(messages); + request.message = message; + request.reason = reason; + ConnectionsManager.getInstance(UserConfig.selectedAccount).sendRequest(request, (response, error) -> { + + }); + } } public static void createReportAlert(final Context context, final long dialog_id, final int messageId, final BaseFragment parentFragment, Runnable hideDim) { - createReportAlert(context, dialog_id, messageId, parentFragment, null, hideDim); + createReportAlert(context, dialog_id, messageId, 0, parentFragment, null, hideDim); } public final static int REPORT_TYPE_SPAM = 0; @@ -4040,7 +4255,7 @@ public static void createReportAlert(final Context context, final long dialog_id public final static int REPORT_TYPE_FAKE_ACCOUNT = 6; public final static int REPORT_TYPE_OTHER = 100; - public static void createReportAlert(final Context context, final long dialog_id, final int messageId, final BaseFragment parentFragment, Theme.ResourcesProvider resourcesProvider, Runnable hideDim) { + public static void createReportAlert(final Context context, final long dialog_id, final int messageId, final int storyId, final BaseFragment parentFragment, Theme.ResourcesProvider resourcesProvider, Runnable hideDim) { if (context == null || parentFragment == null) { return; } @@ -4126,7 +4341,7 @@ public static void createReportAlert(final Context context, final long dialog_id if (parentFragment instanceof ChatActivity) { AndroidUtilities.requestAdjustNothing(parentFragment.getParentActivity(), parentFragment.getClassGuid()); } - parentFragment.showDialog(new ReportAlert(context, type) { + parentFragment.showDialog(new ReportAlert(context, type, resourcesProvider) { @Override public void dismissInternal() { @@ -4143,12 +4358,19 @@ protected void onSend(int type, String message) { ids.add(messageId); } TLRPC.InputPeer peer = MessagesController.getInstance(UserConfig.selectedAccount).getInputPeer(dialog_id); - sendReport(peer, type, message, ids); + sendReport(peer, type, message, ids, storyId); if (parentFragment instanceof ChatActivity) { UndoView undoView = ((ChatActivity) parentFragment).getUndoView(); if (undoView != null) { undoView.showWithAction(0, UndoView.ACTION_REPORT_SENT, null); } + } else { + AndroidUtilities.runOnUIThread(() -> { + BulletinFactory bulletinFactory = BulletinFactory.global(); + if (bulletinFactory != null) { + bulletinFactory.createReportSent(resourcesProvider).show(); + } + }); } } }); @@ -4156,7 +4378,28 @@ protected void onSend(int type, String message) { } TLObject req; TLRPC.InputPeer peer = MessagesController.getInstance(UserConfig.selectedAccount).getInputPeer(dialog_id); - if (messageId != 0) { + if (storyId != 0) { + TLRPC.TL_stories_report request = new TLRPC.TL_stories_report(); + request.id.add(storyId); + request.user_id = MessagesController.getInstance(UserConfig.selectedAccount).getInputUser(dialog_id); + request.message = ""; + if (type == REPORT_TYPE_SPAM) { + request.reason = new TLRPC.TL_inputReportReasonSpam(); + } else if (type == REPORT_TYPE_FAKE_ACCOUNT) { + request.reason = new TLRPC.TL_inputReportReasonFake(); + } else if (type == REPORT_TYPE_VIOLENCE) { + request.reason = new TLRPC.TL_inputReportReasonViolence(); + } else if (type == REPORT_TYPE_CHILD_ABUSE) { + request.reason = new TLRPC.TL_inputReportReasonChildAbuse(); + } else if (type == REPORT_TYPE_PORNOGRAPHY) { + request.reason = new TLRPC.TL_inputReportReasonPornography(); + } else if (type == REPORT_TYPE_ILLEGAL_DRUGS) { + request.reason = new TLRPC.TL_inputReportReasonIllegalDrugs(); + } else if (type == REPORT_TYPE_PERSONAL_DETAILS) { + request.reason = new TLRPC.TL_inputReportReasonPersonalDetails(); + } + req = request; + } else if (messageId != 0) { TLRPC.TL_messages_report request = new TLRPC.TL_messages_report(); request.peer = peer; request.id.add(messageId); @@ -4430,6 +4673,8 @@ public static Dialog createColorSelectDialog(Activity parentActivity, final long currentColor = preferences.getInt("MessagesLed", 0xff0000ff); } else if (globalType == NotificationsController.TYPE_GROUP) { currentColor = preferences.getInt("GroupLed", 0xff0000ff); + } else if (globalType == NotificationsController.TYPE_STORIES) { + currentColor = preferences.getInt("StoriesLed", 0xff0000ff); } else { currentColor = preferences.getInt("ChannelLed", 0xff0000ff); } @@ -4475,6 +4720,8 @@ public static Dialog createColorSelectDialog(Activity parentActivity, final long editor.putInt("MessagesLed", selectedColor[0]); } else if (globalType == NotificationsController.TYPE_GROUP) { editor.putInt("GroupLed", selectedColor[0]); + } else if (globalType == NotificationsController.TYPE_STORIES) { + editor.putInt("StoriesLed", selectedColor[0]); } else { editor.putInt("ChannelLed", selectedColor[0]); } @@ -4494,6 +4741,8 @@ public static Dialog createColorSelectDialog(Activity parentActivity, final long editor.putInt("MessagesLed", 0); } else if (globalType == NotificationsController.TYPE_GROUP) { editor.putInt("GroupLed", 0); + } else if (globalType == NotificationsController.TYPE_STORIES) { + editor.putInt("StoriesLed", 0); } else { editor.putInt("ChannelLed", 0); } @@ -4914,6 +5163,8 @@ public static Dialog createPrioritySelectDialog(Activity parentActivity, final l selected[0] = preferences.getInt("priority_group", 1); } else if (globalType == NotificationsController.TYPE_CHANNEL) { selected[0] = preferences.getInt("priority_channel", 1); + } else if (globalType == NotificationsController.TYPE_STORIES) { + selected[0] = preferences.getInt("priority_stories", 1); } if (selected[0] == 4) { selected[0] = 0; @@ -4983,6 +5234,9 @@ public static Dialog createPrioritySelectDialog(Activity parentActivity, final l } else if (globalType == NotificationsController.TYPE_CHANNEL) { editor.putInt("priority_channel", option); selected[0] = preferences.getInt("priority_channel", 1); + } else if (globalType == NotificationsController.TYPE_STORIES) { + editor.putInt("priority_stories", option); + selected[0] = preferences.getInt("priority_stories", 1); } NotificationsController.getInstance(UserConfig.selectedAccount).deleteNotificationChannelGlobal(globalType); } @@ -5495,13 +5749,6 @@ public static void createDeleteMessagesAlert(BaseFragment fragment, TLRPC.User u ids.add(selectedMessages[a].keyAt(b)); } ArrayList random_ids = null; - long channelId = 0; - if (!ids.isEmpty()) { - MessageObject msg = selectedMessages[a].get(ids.get(0)); - if (msg.messageOwner.peer_id.channel_id != 0) { - channelId = msg.messageOwner.peer_id.channel_id; - } - } if (encryptedChat != null) { random_ids = new ArrayList<>(); for (int b = 0; b < selectedMessages[a].size(); b++) { @@ -5600,7 +5847,7 @@ public static void createDeleteMessagesAlert(BaseFragment fragment, TLRPC.User u fragment.showDialog(dialog); TextView button = (TextView) dialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } @@ -5611,7 +5858,7 @@ public static void createThemeCreateDialog(BaseFragment fragment, int type, Them Context context = fragment.getParentActivity(); final EditTextBoldCursor editText = new EditTextBoldCursor(context); editText.setBackground(null); - editText.setLineColors(Theme.getColor(Theme.key_dialogInputField), Theme.getColor(Theme.key_dialogInputFieldActivated), Theme.getColor(Theme.key_dialogTextRed)); + editText.setLineColors(Theme.getColor(Theme.key_dialogInputField), Theme.getColor(Theme.key_dialogInputFieldActivated), Theme.getColor(Theme.key_text_RedBold)); AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle(LocaleController.getString("NewTheme", R.string.NewTheme)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedEmojiDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedEmojiDrawable.java index 49023dfdd3..bb080cba86 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedEmojiDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedEmojiDrawable.java @@ -34,6 +34,7 @@ import org.telegram.messenger.LiteMode; import org.telegram.messenger.MessageObject; import org.telegram.messenger.MessagesStorage; +import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.SharedConfig; import org.telegram.messenger.SvgHelper; import org.telegram.messenger.UserConfig; @@ -41,7 +42,7 @@ import org.telegram.tgnet.NativeByteBuffer; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.Theme; -import org.telegram.ui.Components.Premium.PremiumLockIconView; +import org.telegram.ui.Components.Reactions.HwEmojis; import org.telegram.ui.SelectAnimatedEmojiDialog; import java.io.File; @@ -146,6 +147,7 @@ public interface ReceivedDocument { } private static HashMap fetchers; + public static EmojiDocumentFetcher getDocumentFetcher(int account) { if (fetchers == null) { fetchers = new HashMap<>(); @@ -156,17 +158,23 @@ public static EmojiDocumentFetcher getDocumentFetcher(int account) { } return fetcher; } + public static class EmojiDocumentFetcher { private HashMap emojiDocumentsCache; private HashMap> loadingDocuments; private HashSet toFetchDocuments; private Runnable fetchRunnable; + private Runnable uiDbCallback; private final int currentAccount; public EmojiDocumentFetcher(int account) { currentAccount = account; } + public void setUiDbCallback(Runnable uiDbCallback) { + this.uiDbCallback = uiDbCallback; + } + public void fetchDocument(long id, ReceivedDocument onDone) { synchronized (this) { if (emojiDocumentsCache != null) { @@ -204,7 +212,7 @@ public void fetchDocument(long id, ReceivedDocument onDone) { AndroidUtilities.runOnUIThread(fetchRunnable = () -> { ArrayList emojiToLoad = new ArrayList<>(toFetchDocuments); toFetchDocuments.clear(); - loadFromDatabase(emojiToLoad); + loadFromDatabase(emojiToLoad, uiDbCallback == null); fetchRunnable = null; }); } @@ -219,51 +227,73 @@ private boolean checkThread() { return true; } + private void loadFromDatabase(ArrayList emojiToLoad, boolean async) { + if (async) { + MessagesStorage messagesStorage = MessagesStorage.getInstance(currentAccount); + messagesStorage.getStorageQueue().postRunnable(() -> loadFromDatabase(emojiToLoad)); + } else { + loadFromDatabase(emojiToLoad); + } + } + private void loadFromDatabase(ArrayList emojiToLoad) { MessagesStorage messagesStorage = MessagesStorage.getInstance(currentAccount); - messagesStorage.getStorageQueue().postRunnable(() -> { - SQLiteDatabase database = messagesStorage.getDatabase(); - if (database == null) { - return; - } - try { - String idsStr = TextUtils.join(",", emojiToLoad); - SQLiteCursor cursor = database.queryFinalized(String.format(Locale.US, "SELECT data FROM animated_emoji WHERE document_id IN (%s)", idsStr)); - ArrayList documents = new ArrayList<>(); - HashSet loadFromServerIds = new HashSet<>(emojiToLoad); - while (cursor.next()) { - NativeByteBuffer byteBuffer = cursor.byteBufferValue(0); - try { - TLRPC.Document document = TLRPC.Document.TLdeserialize(byteBuffer, byteBuffer.readInt32(true), true); - if (document != null && document.id != 0) { - documents.add(document); - loadFromServerIds.remove(document.id); - } - } catch (Exception e) { - FileLog.e(e); - } - if (byteBuffer != null) { - byteBuffer.reuse(); + SQLiteDatabase database = messagesStorage.getDatabase(); + if (database == null) { + return; + } + try { + String idsStr = TextUtils.join(",", emojiToLoad); + SQLiteCursor cursor = database.queryFinalized(String.format(Locale.US, "SELECT data FROM animated_emoji WHERE document_id IN (%s)", idsStr)); + ArrayList documents = new ArrayList<>(); + HashSet loadFromServerIds = new HashSet<>(emojiToLoad); + while (cursor.next()) { + NativeByteBuffer byteBuffer = cursor.byteBufferValue(0); + try { + TLRPC.Document document = TLRPC.Document.TLdeserialize(byteBuffer, byteBuffer.readInt32(true), true); + if (document != null && document.id != 0) { + documents.add(document); + loadFromServerIds.remove(document.id); } + } catch (Exception e) { + FileLog.e(e); } + if (byteBuffer != null) { + byteBuffer.reuse(); + } + } - AndroidUtilities.runOnUIThread(() -> { - processDocuments(documents); - if (!loadFromServerIds.isEmpty()) { - loadFromServer(new ArrayList<>(loadFromServerIds)); - } - }); - cursor.dispose(); - } catch (SQLiteException e) { - messagesStorage.checkSQLException(e); + processDatabaseResult(documents, loadFromServerIds); + cursor.dispose(); + + if (uiDbCallback != null) { + uiDbCallback.run(); + uiDbCallback = null; } - }); + } catch (SQLiteException e) { + messagesStorage.checkSQLException(e); + } + } + + private void processDocumentsAndLoadMore(ArrayList documents, HashSet loadFromServerIds) { + processDocuments(documents); + if (!loadFromServerIds.isEmpty()) { + loadFromServer(new ArrayList<>(loadFromServerIds)); + } + } + + private void processDatabaseResult(ArrayList documents, HashSet loadFromServerIds) { + if (Thread.currentThread() == Looper.getMainLooper().getThread()) { + processDocumentsAndLoadMore(documents, loadFromServerIds); + } else { + NotificationCenter.getInstance(currentAccount).doOnIdle(() -> AndroidUtilities.runOnUIThread(() -> processDocumentsAndLoadMore(documents, loadFromServerIds))); + } } private void loadFromServer(ArrayList loadFromServerIds) { final TLRPC.TL_messages_getCustomEmojiDocuments req = new TLRPC.TL_messages_getCustomEmojiDocuments(); req.document_id = loadFromServerIds; - ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> NotificationCenter.getInstance(currentAccount).doOnIdle(() -> AndroidUtilities.runOnUIThread(() -> { HashSet loadedFromServer = new HashSet<>(loadFromServerIds); if (res instanceof TLRPC.Vector) { ArrayList objects = ((TLRPC.Vector) res).objects; @@ -280,7 +310,7 @@ private void loadFromServer(ArrayList loadFromServerIds) { loadFromServer(new ArrayList<>(loadedFromServer)); } } - })); + }))); } private void putToStorage(ArrayList objects) { @@ -318,6 +348,7 @@ public void processDocuments(ArrayList documents) { if (!checkThread()) { return; } + updateLiteModeValues(); for (int i = 0; i < documents.size(); ++i) { if (documents.get(i) instanceof TLRPC.Document) { TLRPC.Document document = (TLRPC.Document) documents.get(i); @@ -420,6 +451,7 @@ public AnimatedEmojiDrawable(int cacheType, int currentAccount, @NonNull TLRPC.D this.currentAccount = currentAccount; this.document = document; updateSize(); + updateLiteModeValues(); this.initDocument(false); } @@ -436,10 +468,18 @@ private void updateSize() { sizedp = 34; } } + public long getDocumentId() { return this.document != null ? this.document.id : this.documentId; } + private static boolean liteModeKeyboard, liteModeReactions; + + private static void updateLiteModeValues() { + liteModeKeyboard = LiteMode.isEnabled(LiteMode.FLAG_ANIMATED_EMOJI_KEYBOARD); + liteModeReactions = LiteMode.isEnabled(LiteMode.FLAG_ANIMATED_EMOJI_REACTIONS); + } + public TLRPC.Document getDocument() { return this.document; } @@ -463,10 +503,11 @@ protected boolean setImageBitmapByKey(Drawable drawable, String key, int type, b } }; imageReceiver.setAllowLoadingOnAttachedOnly(true); + if (cacheType == CACHE_TYPE_RENDERING_VIDEO) { + imageReceiver.ignoreNotifications = true; + } }; - if (cacheType == CACHE_TYPE_RENDERING_VIDEO) { - imageReceiver.ignoreNotifications = true; - } + if (colorFilterToSet != null && canOverrideColor()) { imageReceiver.setColorFilter(colorFilterToSet); } @@ -478,7 +519,7 @@ protected boolean setImageBitmapByKey(Drawable drawable, String key, int type, b imageReceiver.setUniqKeyPrefix(cacheType + "_"); } imageReceiver.setVideoThumbIsSame(true); - boolean onlyStaticPreview = SharedConfig.getDevicePerformanceClass() == SharedConfig.PERFORMANCE_CLASS_LOW && cacheType == CACHE_TYPE_ALERT_PREVIEW_TAB_STRIP || cacheType == CACHE_TYPE_KEYBOARD && !LiteMode.isEnabled(LiteMode.FLAG_ANIMATED_EMOJI_KEYBOARD) || cacheType == CACHE_TYPE_ALERT_PREVIEW && !LiteMode.isEnabled(LiteMode.FLAG_ANIMATED_EMOJI_REACTIONS); + boolean onlyStaticPreview = SharedConfig.getDevicePerformanceClass() == SharedConfig.PERFORMANCE_CLASS_LOW && cacheType == CACHE_TYPE_ALERT_PREVIEW_TAB_STRIP || cacheType == CACHE_TYPE_KEYBOARD && !liteModeKeyboard || cacheType == CACHE_TYPE_ALERT_PREVIEW && !liteModeReactions; if (cacheType == CACHE_TYPE_ALERT_PREVIEW_STATIC) { onlyStaticPreview = true; } @@ -530,10 +571,10 @@ protected boolean setImageBitmapByKey(Drawable drawable, String key, int type, b } else if (cacheType == STANDARD_LOTTIE_FRAME) { imageReceiver.setImage(null, null, mediaLocation, mediaFilter, null, null, thumbDrawable, document.size, null, document, 1); } else { - if (onlyStaticPreview || (!LiteMode.isEnabled(LiteMode.FLAG_ANIMATED_EMOJI_KEYBOARD) && cacheType != CACHE_TYPE_AVATAR_CONSTRUCTOR_PREVIEW)) { + if (onlyStaticPreview || (!liteModeKeyboard && cacheType != CACHE_TYPE_AVATAR_CONSTRUCTOR_PREVIEW)) { if ("video/webm".equals(document.mime_type)) { imageReceiver.setImage(null, null, ImageLocation.getForDocument(thumb, document), sizedp + "_" + sizedp, null, null, thumbDrawable, document.size, null, document, 1); - } else if (MessageObject.isAnimatedStickerDocument(document, true)){ + } else if (MessageObject.isAnimatedStickerDocument(document, true)) { imageReceiver.setImage(mediaLocation, mediaFilter + "_firstframe", null, null, thumbDrawable, document.size, null, document, 1); } else { imageReceiver.setImage(ImageLocation.getForDocument(thumb, document), sizedp + "_" + sizedp, null, null, thumbDrawable, document.size, null, document, 1); @@ -604,6 +645,7 @@ public String toString() { } private static Paint placeholderPaint; + public static void updatePlaceholderPaintColor() { if (placeholderPaint != null) { placeholderPaint.setColor(Theme.isCurrentThemeDark() ? 0x0fffffff : 0x0f000000); @@ -698,6 +740,7 @@ public void removeView(View view) { public static int attachedCount = 0; public static ArrayList attachedDrawable; + private void updateAttachState() { if (imageReceiver == null) { return; @@ -734,6 +777,7 @@ private void updateAttachState() { } private Boolean canOverrideColorCached = null; + public boolean canOverrideColor() { if (canOverrideColorCached != null) { return canOverrideColorCached; @@ -745,6 +789,7 @@ public boolean canOverrideColor() { } private Boolean isDefaultStatusEmojiCached = null; + public boolean isDefaultStatusEmoji() { if (isDefaultStatusEmojiCached != null) { return isDefaultStatusEmojiCached; @@ -752,8 +797,8 @@ public boolean isDefaultStatusEmoji() { if (document != null) { TLRPC.InputStickerSet set = MessageObject.getInputStickerSet(document); return isDefaultStatusEmojiCached = ( - set instanceof TLRPC.TL_inputStickerSetEmojiDefaultStatuses || - set instanceof TLRPC.TL_inputStickerSetID && (set.id == 773947703670341676L || set.id == 2964141614563343L) + set instanceof TLRPC.TL_inputStickerSetEmojiDefaultStatuses || + set instanceof TLRPC.TL_inputStickerSetID && (set.id == 773947703670341676L || set.id == 2964141614563343L) ); } return false; @@ -804,6 +849,7 @@ public ImageReceiver getImageReceiver() { } private static HashMap dominantColors; + public static int getDominantColor(AnimatedEmojiDrawable yourDrawable) { if (yourDrawable == null) { return 0; @@ -818,7 +864,7 @@ public static int getDominantColor(AnimatedEmojiDrawable yourDrawable) { Integer color = dominantColors.get(documentId); if (color == null) { if (yourDrawable.getImageReceiver() != null && yourDrawable.getImageReceiver().getBitmap() != null) { - dominantColors.put(documentId, color = PremiumLockIconView.getDominantColor(yourDrawable.getImageReceiver().getBitmap())); + dominantColors.put(documentId, color = AndroidUtilities.getDominantColor(yourDrawable.getImageReceiver().getBitmap())); } } return color == null ? 0 : color; @@ -860,6 +906,7 @@ public int getIntrinsicHeight() { } private int alpha = 255; + @Override public void setAlpha(int alpha) { this.alpha = alpha; @@ -898,6 +945,7 @@ public static class SwapAnimatedEmojiDrawable extends Drawable implements Animat private int size; private int alpha = 255; boolean attached; + private Theme.ResourcesProvider resourcesProvider; public SwapAnimatedEmojiDrawable(View parentView, int size) { this(parentView, false, size, CACHE_TYPE_EMOJI_STATUS); @@ -936,6 +984,7 @@ public void play() { private Integer lastColor; private ColorFilter colorFilter; + public void setColor(Integer color) { if (lastColor == null && color == null || lastColor != null && lastColor.equals(color)) { return; @@ -958,17 +1007,17 @@ public void draw(@NonNull Canvas canvas) { drawables[1].setBounds(bounds); } else if (center) { drawables[1].setBounds( - bounds.centerX() - drawables[1].getIntrinsicWidth() / 2, - bounds.centerY() - drawables[1].getIntrinsicHeight() / 2, - bounds.centerX() + drawables[1].getIntrinsicWidth() / 2, - bounds.centerY() + drawables[1].getIntrinsicHeight() / 2 + bounds.centerX() - drawables[1].getIntrinsicWidth() / 2, + bounds.centerY() - drawables[1].getIntrinsicHeight() / 2, + bounds.centerX() + drawables[1].getIntrinsicWidth() / 2, + bounds.centerY() + drawables[1].getIntrinsicHeight() / 2 ); } else { // left drawables[1].setBounds( - bounds.left, - bounds.centerY() - drawables[1].getIntrinsicHeight() / 2, - bounds.left + drawables[1].getIntrinsicWidth(), - bounds.centerY() + drawables[1].getIntrinsicHeight() / 2 + bounds.left, + bounds.centerY() - drawables[1].getIntrinsicHeight() / 2, + bounds.left + drawables[1].getIntrinsicWidth(), + bounds.centerY() + drawables[1].getIntrinsicHeight() / 2 ); } drawables[1].setColorFilter(colorFilter); @@ -992,10 +1041,10 @@ public void draw(@NonNull Canvas canvas) { canvas.scale(scale, scale, bounds.centerX(), bounds.centerY()); } drawables[0].setBounds( - bounds.centerX() - drawables[0].getIntrinsicWidth() / 2, - bounds.centerY() - drawables[0].getIntrinsicHeight() / 2, - bounds.centerX() + drawables[0].getIntrinsicWidth() / 2, - bounds.centerY() + drawables[0].getIntrinsicHeight() / 2 + bounds.centerX() - drawables[0].getIntrinsicWidth() / 2, + bounds.centerY() - drawables[0].getIntrinsicHeight() / 2, + bounds.centerX() + drawables[0].getIntrinsicWidth() / 2, + bounds.centerY() + drawables[0].getIntrinsicHeight() / 2 ); } else { // left if (progress < 1) { @@ -1003,10 +1052,10 @@ public void draw(@NonNull Canvas canvas) { canvas.scale(scale, scale, bounds.left + drawables[0].getIntrinsicWidth() / 2f, bounds.centerY()); } drawables[0].setBounds( - bounds.left, - bounds.centerY() - drawables[0].getIntrinsicHeight() / 2, - bounds.left + drawables[0].getIntrinsicWidth(), - bounds.centerY() + drawables[0].getIntrinsicHeight() / 2 + bounds.left, + bounds.centerY() - drawables[0].getIntrinsicHeight() / 2, + bounds.left + drawables[0].getIntrinsicWidth(), + bounds.centerY() + drawables[0].getIntrinsicHeight() / 2 ); } drawables[0].setAlpha(alpha); @@ -1053,7 +1102,7 @@ public void set(long documentId, int cacheType, boolean animated) { attach(); } } - lastColor = 0xffffffff; + lastColor = null; colorFilter = null; play(); invalidate(); @@ -1099,7 +1148,7 @@ public void set(TLRPC.Document document, int cacheType, boolean animated) { attach(); } } - lastColor = 0xffffffff; + lastColor = null; colorFilter = null; play(); invalidate(); @@ -1130,7 +1179,7 @@ public void set(Drawable drawable, boolean animated) { attach(); } } - lastColor = 0xffffffff; + lastColor = null; colorFilter = null; play(); invalidate(); @@ -1176,8 +1225,10 @@ public int getIntrinsicHeight() { public void setAlpha(int i) { alpha = i; } + @Override public void setColorFilter(@Nullable ColorFilter colorFilter) {} + @Override public int getOpacity() { return PixelFormat.TRANSPARENT; @@ -1201,12 +1252,17 @@ public void invalidate() { public void setSecondParent(View secondParent) { this.secondParent = secondParent; } + + public void setResourcesProvider(Theme.ResourcesProvider resourcesProvider) { + this.resourcesProvider = resourcesProvider; + } } public static void updateAll() { if (globalEmojiCache == null) { return; } + updateLiteModeValues(); for (int i = 0; i < globalEmojiCache.size(); i++) { LongSparseArray map = globalEmojiCache.valueAt(i); for (int j = 0; j < map.size(); j++) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedEmojiSpan.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedEmojiSpan.java index 84f71f12d5..a59c351904 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedEmojiSpan.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedEmojiSpan.java @@ -44,7 +44,7 @@ public class AnimatedEmojiSpan extends ReplacementSpan { private float size = AndroidUtilities.dp(20); public int cacheType = -1; public String documentAbsolutePath; - int measuredSize; + protected int measuredSize; boolean spanDrawn; boolean positionChanged; @@ -58,6 +58,11 @@ public AnimatedEmojiSpan(@NonNull TLRPC.Document document, Paint.FontMetricsInt this.document = document; } + public AnimatedEmojiSpan(@NonNull TLRPC.Document document, float scale, Paint.FontMetricsInt fontMetrics) { + this(document.id, scale, fontMetrics); + this.document = document; + } + public AnimatedEmojiSpan(long documentId, Paint.FontMetricsInt fontMetrics) { this(documentId, 1.2f, fontMetrics); } @@ -318,7 +323,7 @@ public void releaseDrawInBackground(int threadIndex) { } } - public void draw(Canvas canvas, long time, float boundTop, float boundBottom, float alpha) { + public void draw(Canvas canvas, long time, float boundTop, float boundBottom, float alpha, ColorFilter colorFilter) { if ((boundTop != 0 || boundBottom != 0) && outOfBounds(boundTop, boundBottom)) { skipDraw = true; return; @@ -327,10 +332,9 @@ public void draw(Canvas canvas, long time, float boundTop, float boundBottom, fl } if (drawable.getImageReceiver() != null) { - drawable.setColorFilter(Theme.chat_animatedEmojiTextColorFilter); + drawable.setColorFilter(colorFilter == null ? Theme.chat_animatedEmojiTextColorFilter : colorFilter); drawable.setTime(time); drawable.draw(canvas, drawableBounds, alpha * this.alpha); -// drawable.setTime(0); } } @@ -753,7 +757,7 @@ public void drawInUiThread(Canvas canvas, float alpha) { if (!holder.span.spanDrawn) { continue; } - holder.draw(canvas, time, 0, 0, alpha); + holder.draw(canvas, time, 0, 0, alpha, null); } } @@ -839,7 +843,7 @@ public void draw(Canvas canvas, List spoilers, long time, float b holder.drawingYOffset = drawingYOffset; holder.alpha = spoilerAlpha; if (backgroundThreadDrawable == null) { - holder.draw(canvas, time, boundTop, boundBottom, alpha); + holder.draw(canvas, time, boundTop, boundBottom, alpha, colorFilter); } } if (backgroundThreadDrawable != null) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedFileDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedFileDrawable.java index bd9b285d5a..2ee416349b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedFileDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedFileDrawable.java @@ -27,12 +27,14 @@ import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.AnimatedFileDrawableStream; +import org.telegram.messenger.BuildVars; import org.telegram.messenger.DispatchQueue; import org.telegram.messenger.DispatchQueuePoolBackground; import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; import org.telegram.messenger.ImageLocation; import org.telegram.messenger.ImageReceiver; +import org.telegram.messenger.SharedConfig; import org.telegram.messenger.utils.BitmapsCache; import org.telegram.tgnet.TLRPC; @@ -47,6 +49,11 @@ public class AnimatedFileDrawable extends BitmapDrawable implements Animatable, public boolean skipFrameUpdate; public long currentTime; + // canvas.drawPath lead to glitches + // clipPath not use antialias + private final boolean USE_BITMAP_SHADER = Build.VERSION.SDK_INT < 29; + private boolean PRERENDER_FRAME = true; + private static native long createDecoder(String src, int[] params, int account, long streamFileSize, Object readCallback, boolean preview); private static native void destroyDecoder(long ptr); @@ -79,12 +86,14 @@ public class AnimatedFileDrawable extends BitmapDrawable implements Animatable, private long lastFrameTime; private int lastTimeStamp; private int invalidateAfter = 50; - private final int[] metaData = new int[5]; + private final int[] metaData = new int[6]; private Runnable loadFrameTask; private Bitmap renderingBitmap; private int renderingBitmapTime; private Bitmap nextRenderingBitmap; + private Bitmap nextRenderingBitmap2; private int nextRenderingBitmapTime; + private int nextRenderingBitmapTime2; private Bitmap backgroundBitmap; private int backgroundBitmapTime; private boolean destroyWhenDone; @@ -113,7 +122,9 @@ public class AnimatedFileDrawable extends BitmapDrawable implements Animatable, private BitmapShader[] renderingShader = new BitmapShader[1 + DrawingInBackgroundThreadDrawable.THREAD_COUNT]; private BitmapShader[] nextRenderingShader = new BitmapShader[1 + DrawingInBackgroundThreadDrawable.THREAD_COUNT]; + private BitmapShader[] nextRenderingShader2 = new BitmapShader[1 + DrawingInBackgroundThreadDrawable.THREAD_COUNT]; private BitmapShader[] backgroundShader = new BitmapShader[1 + DrawingInBackgroundThreadDrawable.THREAD_COUNT]; + ArrayList unusedBitmaps = new ArrayList<>(); private BitmapShader renderingShaderBackgroundDraw; @@ -195,6 +206,7 @@ public void run() { cacheGenRunnable = null; } generatingCache = false; + chekDestroyDecoder(); scheduleNextGetFrame(); }); }); @@ -220,6 +232,10 @@ private void chekDestroyDecoder() { decodeQueue.recycle(); decodeQueue = null; } + for (int i = 0; i < unusedBitmaps.size(); i++) { + unusedBitmaps.get(i).recycle(); + } + unusedBitmaps.clear(); invalidateInternal(); } } @@ -248,10 +264,40 @@ public void run() { forceDecodeAfterNextFrame = false; } loadFrameTask = null; - nextRenderingBitmap = backgroundBitmap; - nextRenderingBitmapTime = backgroundBitmapTime; + + if (!PRERENDER_FRAME) { + nextRenderingBitmap = backgroundBitmap; + nextRenderingBitmapTime = backgroundBitmapTime; + for (int i = 0; i < backgroundShader.length; i++) { + nextRenderingShader[i] = backgroundShader[i]; + } + } else { + if (nextRenderingBitmap == null && nextRenderingBitmap2 == null) { + nextRenderingBitmap = backgroundBitmap; + nextRenderingBitmapTime = backgroundBitmapTime; + for (int i = 0; i < backgroundShader.length; i++) { + nextRenderingShader[i] = backgroundShader[i]; + } + } else if (nextRenderingBitmap == null) { + nextRenderingBitmap = nextRenderingBitmap2; + nextRenderingBitmapTime = nextRenderingBitmapTime2; + nextRenderingBitmap2 = backgroundBitmap; + nextRenderingBitmapTime2 = backgroundBitmapTime; + for (int i = 0; i < backgroundShader.length; i++) { + nextRenderingShader[i] = nextRenderingShader2[i]; + nextRenderingShader2[i] = backgroundShader[i]; + } + } else { + nextRenderingBitmap2 = backgroundBitmap; + nextRenderingBitmapTime2 = backgroundBitmapTime; + for (int i = 0; i < backgroundShader.length; i++) { + nextRenderingShader2[i] = backgroundShader[i]; + } + } + } + backgroundBitmap = null; for (int i = 0; i < backgroundShader.length; i++) { - nextRenderingShader[i] = backgroundShader[i]; + backgroundShader[i] = null; } if (isRestarted) { @@ -323,7 +369,11 @@ public void run() { try { if (bitmapsCache != null) { if (backgroundBitmap == null) { - backgroundBitmap = Bitmap.createBitmap(renderingWidth, renderingHeight, Bitmap.Config.ARGB_8888); + if (!unusedBitmaps.isEmpty()) { + backgroundBitmap = unusedBitmaps.remove(0); + } else { + backgroundBitmap = Bitmap.createBitmap(renderingWidth, renderingHeight, Bitmap.Config.ARGB_8888); + } } if (cacheMetadata == null) { cacheMetadata = new BitmapsCache.Metadata(); @@ -350,11 +400,15 @@ public void run() { if (nativePtr != 0 || metaData[0] == 0 || metaData[1] == 0) { if (backgroundBitmap == null && metaData[0] > 0 && metaData[1] > 0) { try { - backgroundBitmap = Bitmap.createBitmap((int) (metaData[0] * scaleFactor), (int) (metaData[1] * scaleFactor), Bitmap.Config.ARGB_8888); + if (!unusedBitmaps.isEmpty()) { + backgroundBitmap = unusedBitmaps.remove(0); + } else { + backgroundBitmap = Bitmap.createBitmap((int) (metaData[0] * scaleFactor), (int) (metaData[1] * scaleFactor), Bitmap.Config.ARGB_8888); + } } catch (Throwable e) { FileLog.e(e); } - if (backgroundShader[0] == null && backgroundBitmap != null && hasRoundRadius()) { + if (USE_BITMAP_SHADER && backgroundShader[0] == null && backgroundBitmap != null && hasRoundRadius()) { backgroundShader[0] = new BitmapShader(backgroundBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); } } @@ -427,6 +481,7 @@ public AnimatedFileDrawable(File file, boolean createDecoder, long streamSize, i public AnimatedFileDrawable(File file, boolean createDecoder, long streamSize, int streamLoadingPriority, TLRPC.Document document, ImageLocation location, Object parentObject, long seekTo, int account, boolean preview, int w, int h, BitmapsCache.CacheOptions cacheOptions) { path = file; + PRERENDER_FRAME = SharedConfig.deviceIsAboveAverage() && limitFps; streamFileSize = streamSize; this.streamLoadingPriority = streamLoadingPriority; currentAccount = account; @@ -463,6 +518,7 @@ public AnimatedFileDrawable(File file, boolean createDecoder, long streamSize, i public void setIsWebmSticker(boolean b) { isWebmSticker = b; + PRERENDER_FRAME = false; if (isWebmSticker) { useSharedQueue = true; } @@ -616,12 +672,18 @@ public void recycle() { ArrayList bitmapToRecycle = new ArrayList<>(); bitmapToRecycle.add(renderingBitmap); bitmapToRecycle.add(nextRenderingBitmap); + bitmapToRecycle.add(nextRenderingBitmap2); bitmapToRecycle.add(backgroundBitmap); + bitmapToRecycle.addAll(unusedBitmaps); + // unusedBitmaps.remove(backgroundBitmap); + unusedBitmaps.clear(); renderingBitmap = null; nextRenderingBitmap = null; + nextRenderingBitmap2 = null; backgroundBitmap = null; + if (decodeQueue != null) { decodeQueue.recycle(); decodeQueue = null; @@ -704,7 +766,7 @@ public int getDurationMs() { } private void scheduleNextGetFrame() { - if (loadFrameTask != null || nextRenderingBitmap != null || !canLoadFrames() || destroyWhenDone || !isRunning && (!decodeSingleFrame || decodeSingleFrame && singleFrameDecoded) || parents.size() == 0 && !ignoreNoParent || generatingCache) { + if (loadFrameTask != null || ((!PRERENDER_FRAME || nextRenderingBitmap2 != null) && nextRenderingBitmap != null) || !canLoadFrames() || destroyWhenDone || !isRunning && (!decodeSingleFrame || decodeSingleFrame && singleFrameDecoded) || parents.size() == 0 && !ignoreNoParent || generatingCache) { return; } long ms = 0; @@ -828,33 +890,36 @@ public void drawInternal(Canvas canvas, boolean drawInBackground, long currentTi } if (hasRoundRadius()) { int index = drawInBackground ? threadIndex + 1 : 0; - if (renderingShader[index] == null) { - renderingShader[index] = new BitmapShader(renderingBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); - } - paint.setShader(renderingShader[index]); - Matrix matrix = shaderMatrix[index]; - if (matrix == null) { - matrix = shaderMatrix[index] = new Matrix(); + if (USE_BITMAP_SHADER) { + if (renderingShader[index] == null) { + renderingShader[index] = new BitmapShader(renderingBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); + } + paint.setShader(renderingShader[index]); + Matrix matrix = shaderMatrix[index]; + if (matrix == null) { + matrix = shaderMatrix[index] = new Matrix(); + } + matrix.reset(); + matrix.setTranslate(rect.left, rect.top); + if (metaData[2] == 90) { + matrix.preRotate(90); + matrix.preTranslate(0, -rect.width()); + } else if (metaData[2] == 180) { + matrix.preRotate(180); + matrix.preTranslate(-rect.width(), -rect.height()); + } else if (metaData[2] == 270) { + matrix.preRotate(270); + matrix.preTranslate(-rect.height(), 0); + } + matrix.preScale(scaleX, scaleY); + + renderingShader[index].setLocalMatrix(matrix); } + Path path = roundPath[index]; if (path == null) { path = roundPath[index] = new Path(); } - matrix.reset(); - matrix.setTranslate(rect.left, rect.top); - if (metaData[2] == 90) { - matrix.preRotate(90); - matrix.preTranslate(0, -rect.width()); - } else if (metaData[2] == 180) { - matrix.preRotate(180); - matrix.preTranslate(-rect.width(), -rect.height()); - } else if (metaData[2] == 270) { - matrix.preRotate(270); - matrix.preTranslate(-rect.height(), 0); - } - matrix.preScale(scaleX, scaleY); - - renderingShader[index].setLocalMatrix(matrix); if (invalidatePath || drawInBackground) { if (!drawInBackground) { invalidatePath = false; @@ -863,29 +928,39 @@ public void drawInternal(Canvas canvas, boolean drawInBackground, long currentTi radii[a * 2] = roundRadius[a]; radii[a * 2 + 1] = roundRadius[a]; } - path.reset(); + path.rewind(); path.addRoundRect(drawInBackground ? rect : actualDrawRect, radii, Path.Direction.CW); - path.close(); } - canvas.drawPath(path, paint); - } else { - canvas.translate(rect.left, rect.top); - if (metaData[2] == 90) { - canvas.rotate(90); - canvas.translate(0, -rect.width()); - } else if (metaData[2] == 180) { - canvas.rotate(180); - canvas.translate(-rect.width(), -rect.height()); - } else if (metaData[2] == 270) { - canvas.rotate(270); - canvas.translate(-rect.height(), 0); + if (USE_BITMAP_SHADER) { + canvas.drawPath(path, paint); + } else { + canvas.save(); + canvas.clipPath(path); + drawBitmap(rect, paint, canvas, scaleX, scaleY); + canvas.restore(); } - canvas.scale(scaleX, scaleY); - canvas.drawBitmap(renderingBitmap, 0, 0, paint); + } else { + drawBitmap(rect, paint, canvas, scaleX, scaleY); } } } + private void drawBitmap(RectF rect, Paint paint, Canvas canvas, float sx, float sy) { + canvas.translate(rect.left, rect.top); + if (metaData[2] == 90) { + canvas.rotate(90); + canvas.translate(0, -rect.width()); + } else if (metaData[2] == 180) { + canvas.rotate(180); + canvas.translate(-rect.width(), -rect.height()); + } else if (metaData[2] == 270) { + canvas.rotate(270); + canvas.translate(-rect.height(), 0); + } + canvas.scale(sx, sy); + canvas.drawBitmap(renderingBitmap, 0, 0, paint); + } + public long getLastFrameTimestamp() { return lastTimeStamp; } @@ -925,6 +1000,8 @@ public Bitmap getAnimatedBitmap() { return renderingBitmap; } else if (nextRenderingBitmap != null) { return nextRenderingBitmap; + } else if (nextRenderingBitmap2 != null) { + return nextRenderingBitmap2; } return null; } @@ -1007,7 +1084,11 @@ public Bitmap getNextFrame() { return backgroundBitmap; } if (backgroundBitmap == null) { - backgroundBitmap = Bitmap.createBitmap((int) (metaData[0] * scaleFactor), (int) (metaData[1] * scaleFactor), Bitmap.Config.ARGB_8888); + if (!unusedBitmaps.isEmpty()) { + backgroundBitmap = unusedBitmaps.remove(0); + } else { + backgroundBitmap = Bitmap.createBitmap((int) (metaData[0] * scaleFactor), (int) (metaData[1] * scaleFactor), Bitmap.Config.ARGB_8888); + } } getVideoFrame(nativePtr, backgroundBitmap, metaData, backgroundBitmap.getRowBytes(), false, startTime, endTime); return backgroundBitmap; @@ -1015,6 +1096,9 @@ public Bitmap getNextFrame() { public void setLimitFps(boolean limitFps) { this.limitFps = limitFps; + if (limitFps) { + PRERENDER_FRAME = false; + } } public ArrayList getParents() { @@ -1133,14 +1217,18 @@ public void updateCurrentFrame(long now, boolean b) { if (renderingBitmap == null && nextRenderingBitmap == null) { scheduleNextGetFrame(); } else if (nextRenderingBitmap != null && (renderingBitmap == null || (Math.abs(now - lastFrameTime) >= invalidateAfter && !skipFrameUpdate))) { + unusedBitmaps.add(renderingBitmap); renderingBitmap = nextRenderingBitmap; renderingBitmapTime = nextRenderingBitmapTime; for (int i = 0; i < backgroundShader.length; i++) { renderingShader[i] = nextRenderingShader[i]; - nextRenderingShader[i] = null; + nextRenderingShader[i] = nextRenderingShader2[i]; + nextRenderingShader2[i] = null; } - nextRenderingBitmap = null; - nextRenderingBitmapTime = 0; + nextRenderingBitmap = nextRenderingBitmap2; + nextRenderingBitmapTime = nextRenderingBitmapTime2; + nextRenderingBitmap2 = null; + nextRenderingBitmapTime2 = 0; lastFrameTime = now; scheduleNextGetFrame(); @@ -1148,22 +1236,33 @@ public void updateCurrentFrame(long now, boolean b) { invalidateInternal(); } } else if (!isRunning && decodeSingleFrame && Math.abs(now - lastFrameTime) >= invalidateAfter && nextRenderingBitmap != null) { - // if (precache) { - backgroundBitmap = renderingBitmap; - // } + unusedBitmaps.add(renderingBitmap); renderingBitmap = nextRenderingBitmap; renderingBitmapTime = nextRenderingBitmapTime; for (int i = 0; i < backgroundShader.length; i++) { - // if (precache) { - backgroundShader[i] = renderingShader[i]; - // } renderingShader[i] = nextRenderingShader[i]; - nextRenderingShader[i] = null; + nextRenderingShader[i] = nextRenderingShader2[i]; + nextRenderingShader2[i] = null; } - nextRenderingBitmap = null; - nextRenderingBitmapTime = 0; + nextRenderingBitmap = nextRenderingBitmap2; + nextRenderingBitmapTime = nextRenderingBitmapTime2; + nextRenderingBitmap2 = null; + nextRenderingBitmapTime2 = 0; + lastFrameTime = now; scheduleNextGetFrame(); } } + + public int getFps() { + return metaData[5]; + } + + public int getRenderingWidth() { + return renderingWidth; + } + + public int getRenderingHeight() { + return renderingHeight; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedFloat.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedFloat.java index 40f6bfdb3f..f70b563b1e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedFloat.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedFloat.java @@ -126,6 +126,14 @@ public float set(float mustBe) { return this.set(mustBe, false); } + public float set(boolean mustBe) { + return this.set(mustBe ? 1 : 0, false); + } + + public float set(boolean mustBe, boolean force) { + return this.set(mustBe ? 1 : 0, force); + } + public float set(float mustBe, boolean force) { if (force || transitionDuration <= 0 || firstSet) { value = targetValue = mustBe; @@ -161,6 +169,14 @@ public float set(float mustBe, boolean force) { return value; } + public void setDuration(long duration) { + transitionDuration = duration; + } + + public long getDuration() { + return transitionDuration; + } + public boolean isInProgress() { return transition; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedTextView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedTextView.java index 12a6a7a9d4..b3e1b6f7d4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedTextView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedTextView.java @@ -8,9 +8,14 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; +import android.graphics.LinearGradient; +import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PixelFormat; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; import android.graphics.Rect; +import android.graphics.Shader; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.os.Build; @@ -18,6 +23,8 @@ import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; +import android.util.Log; +import android.util.Pair; import android.view.Gravity; import android.view.View; import android.view.accessibility.AccessibilityNodeInfo; @@ -26,16 +33,19 @@ import androidx.annotation.Nullable; import org.telegram.messenger.AndroidUtilities; +import org.telegram.ui.ActionBar.Theme; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.stream.IntStream; public class AnimatedTextView extends View { public static class AnimatedTextDrawable extends Drawable { - private TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + private final TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); private int gravity = 0; private boolean isRTL = false; @@ -48,7 +58,7 @@ public static class AnimatedTextDrawable extends Drawable { private Part[] oldParts; private CharSequence oldText; - private class Part { + private static class Part { StaticLayout layout; float offset; int toOppositeIndex; @@ -75,15 +85,32 @@ public Part(StaticLayout layout, float offset, int toOppositeIndex) { private float moveAmplitude = 1f; private int alpha = 255; - private Rect bounds = new Rect(); + private final Rect bounds = new Rect(); private boolean splitByWords; private boolean preserveIndex; private boolean startFromEnd; + public void setHacks(boolean splitByWords, boolean preserveIndex, boolean startFromEnd) { + this.splitByWords = splitByWords; + this.preserveIndex = preserveIndex; + this.startFromEnd = startFromEnd; + } private Runnable onAnimationFinishListener; private boolean allowCancel; public boolean ignoreRTL; + public boolean updateAll; + + private int overrideFullWidth; + public void setOverrideFullWidth(int value) { + overrideFullWidth = value; + } + + private float rightPadding; + private boolean ellipsizeByGradient; + private LinearGradient ellipsizeGradient; + private Matrix ellipsizeGradientMatrix; + private Paint ellipsizePaint; public AnimatedTextDrawable() { this(false, false, false); @@ -99,12 +126,29 @@ public void setAllowCancel(boolean allowCancel) { this.allowCancel = allowCancel; } + public void setEllipsizeByGradient(boolean enabled) { + ellipsizeByGradient = enabled; + invalidateSelf(); + } + public void setOnAnimationFinishListener(Runnable listener) { onAnimationFinishListener = listener; } + private void applyAlphaInternal(float t) { + textPaint.setAlpha((int) (alpha * t)); + if (shadowed) { + textPaint.setShadowLayer(shadowRadius, shadowDx, shadowDy, Theme.multAlpha(shadowColor, t)); + } + } + @Override public void draw(@NonNull Canvas canvas) { + if (ellipsizeByGradient) { + AndroidUtilities.rectTmp.set(bounds); + AndroidUtilities.rectTmp.right -= rightPadding; + canvas.saveLayerAlpha(AndroidUtilities.rectTmp, 255, Canvas.ALL_SAVE_FLAG); + } canvas.save(); canvas.translate(bounds.left, bounds.top); int fullWidth = bounds.width(); @@ -117,24 +161,21 @@ public void draw(@NonNull Canvas canvas) { Part current = currentParts[i]; int j = current.toOppositeIndex; float x = current.offset, y = 0; + if (isRTL && !ignoreRTL) { + x = currentWidth - (x + current.width); + } if (j >= 0) { - if (isRTL && !ignoreRTL) { - x = currentWidth - (x + current.width); - } Part old = oldParts[j]; float oldX = old.offset; if (isRTL && !ignoreRTL) { oldX = oldWidth - (oldX + old.width); } x = AndroidUtilities.lerp(oldX - old.left, x - current.left, t); - textPaint.setAlpha(alpha); + applyAlphaInternal(1f); } else { - if (isRTL && !ignoreRTL) { - x = currentWidth - (x + current.width); - } x -= current.left; y = -textPaint.getTextSize() * moveAmplitude * (1f - t) * (moveDown ? 1f : -1f); - textPaint.setAlpha((int) (alpha * t)); + applyAlphaInternal(t); } canvas.save(); float lwidth = j >= 0 ? width : currentWidth; @@ -159,7 +200,7 @@ public void draw(@NonNull Canvas canvas) { } float x = old.offset; float y = textPaint.getTextSize() * moveAmplitude * t * (moveDown ? 1f : -1f); - textPaint.setAlpha((int) (alpha * (1f - t))); + applyAlphaInternal(1f - t); canvas.save(); if (isRTL && !ignoreRTL) { x = oldWidth - (x + old.width); @@ -181,7 +222,7 @@ public void draw(@NonNull Canvas canvas) { } else { canvas.translate(0, (fullHeight - currentHeight) / 2f); if (currentParts != null) { - textPaint.setAlpha(alpha); + applyAlphaInternal(1f); for (int i = 0; i < currentParts.length; ++i) { canvas.save(); Part current = currentParts[i]; @@ -207,6 +248,28 @@ public void draw(@NonNull Canvas canvas) { } } canvas.restore(); + if (ellipsizeByGradient) { + final float w = AndroidUtilities.dp(16); + if (ellipsizeGradient == null) { + ellipsizeGradient = new LinearGradient(0, 0, w, 0, new int[] {0x00ff0000, 0xffff0000}, new float[] {0, 1}, Shader.TileMode.CLAMP); + ellipsizeGradientMatrix = new Matrix(); + ellipsizePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + ellipsizePaint.setShader(ellipsizeGradient); + ellipsizePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); + } + ellipsizeGradientMatrix.reset(); + ellipsizeGradientMatrix.postTranslate(bounds.right - rightPadding - w, 0); + ellipsizeGradient.setLocalMatrix(ellipsizeGradientMatrix); + canvas.save(); + canvas.drawRect(bounds.right - rightPadding - w, bounds.top, bounds.right - rightPadding, bounds.bottom, ellipsizePaint); + canvas.restore(); + canvas.restore(); + } + } + + public void setRightPadding(float rightPadding) { + this.rightPadding = rightPadding; + invalidateSelf(); } public void cancelAnimation() { @@ -234,6 +297,7 @@ public void setText(CharSequence text, boolean animated, boolean moveDown) { if (text == null) { text = ""; } + final int width = overrideFullWidth > 0 ? overrideFullWidth : bounds.width(); if (animated) { if (allowCancel) { if (animator != null) { @@ -253,12 +317,6 @@ public void setText(CharSequence text, boolean animated, boolean moveDown) { oldText = currentText; currentText = text; -// ArrayList currentLayoutOffsets = new ArrayList<>(); -// ArrayList currentLayoutToOldIndex = new ArrayList<>(); -// ArrayList currentLayoutList = new ArrayList<>(); -// ArrayList oldLayoutOffsets = new ArrayList<>(); -// ArrayList oldLayoutToCurrentIndex = new ArrayList<>(); -// ArrayList oldLayoutList = new ArrayList<>(); ArrayList currentParts = new ArrayList<>(); ArrayList oldParts = new ArrayList<>(); @@ -268,7 +326,7 @@ public void setText(CharSequence text, boolean animated, boolean moveDown) { // order execution matters RegionCallback onEqualRegion = (part, from, to) -> { - StaticLayout layout = makeLayout(part, bounds.width() - (int) Math.ceil(Math.min(currentWidth, oldWidth))); + StaticLayout layout = makeLayout(part, width - (int) Math.ceil(Math.min(currentWidth, oldWidth))); final Part currentPart = new Part(layout, currentWidth, oldParts.size()); final Part oldPart = new Part(layout, oldWidth, oldParts.size()); currentParts.add(currentPart); @@ -280,14 +338,14 @@ public void setText(CharSequence text, boolean animated, boolean moveDown) { oldHeight = Math.max(oldHeight, layout.getHeight()); }; RegionCallback onNewPart = (part, from, to) -> { - StaticLayout layout = makeLayout(part, bounds.width() - (int) Math.ceil(currentWidth)); + StaticLayout layout = makeLayout(part, width - (int) Math.ceil(currentWidth)); final Part currentPart = new Part(layout, currentWidth, -1); currentParts.add(currentPart); currentWidth += currentPart.width; currentHeight = Math.max(currentHeight, layout.getHeight()); }; RegionCallback onOldPart = (part, from, to) -> { - StaticLayout layout = makeLayout(part, bounds.width() - (int) Math.ceil(oldWidth)); + StaticLayout layout = makeLayout(part, width - (int) Math.ceil(oldWidth)); final Part oldPart = new Part(layout, oldWidth, -1); oldParts.add(oldPart); oldWidth += oldPart.width; @@ -298,6 +356,7 @@ public void setText(CharSequence text, boolean animated, boolean moveDown) { CharSequence to = splitByWords ? new WordSequence(currentText) : currentText; diff(from, to, onEqualRegion, onNewPart, onOldPart); +// betterDiff(from, to, onEqualRegion, onNewPart, onOldPart); if (this.currentParts == null || this.currentParts.length != currentParts.size()) { this.currentParts = new Part[currentParts.size()]; @@ -352,7 +411,7 @@ public void onAnimationEnd(Animator animation) { if (!text.equals(currentText)) { currentParts = new Part[1]; - currentParts[0] = new Part(makeLayout(currentText = text, bounds.width()), 0, -1); + currentParts[0] = new Part(makeLayout(currentText = text, width), 0, -1); currentWidth = currentParts[0].width; currentHeight = currentParts[0].layout.getHeight(); isRTL = AndroidUtilities.isRTL(currentText); @@ -386,6 +445,17 @@ public float getAnimateToWidth() { return currentWidth; } + public float getMaxWidth(AnimatedTextDrawable otherTextDrawable) { + if (oldParts == null || otherTextDrawable.oldParts == null) { + return Math.max(getCurrentWidth(), otherTextDrawable.getCurrentWidth()); + } + return AndroidUtilities.lerp( + Math.max(oldWidth, otherTextDrawable.oldWidth), + Math.max(currentWidth, otherTextDrawable.currentWidth), + Math.max(t, otherTextDrawable.t) + ); + } + public float getHeight() { return currentHeight; } @@ -402,8 +472,8 @@ private StaticLayout makeLayout(CharSequence textPart, int width) { .setEllipsize(TextUtils.TruncateAt.END) .setEllipsizedWidth(width) .build(); - } - return new StaticLayout( + } else { + return new StaticLayout( textPart, 0, textPart.length(), textPaint, @@ -414,13 +484,14 @@ private StaticLayout makeLayout(CharSequence textPart, int width) { false, TextUtils.TruncateAt.END, width - ); + ); + } } private static class WordSequence implements CharSequence { private static final char SPACE = ' '; - private CharSequence words[]; + private final CharSequence[] words; private final int length; public WordSequence(CharSequence text) { @@ -492,6 +563,7 @@ public CharSequence subSequence(int from, int to) { } @Override + @NonNull public String toString() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < words.length; ++i) { @@ -530,7 +602,87 @@ public static boolean partEquals(CharSequence a, CharSequence b, int aIndex, int return (a == null && b == null || a != null && b != null && a.charAt(aIndex) == b.charAt(bIndex)); } + private void betterDiff(final CharSequence oldText, final CharSequence newText, + RegionCallback onEqualPart, RegionCallback onNewPart, RegionCallback onOldPart) { + int m = oldText.length(); + int n = newText.length(); + + int[][] dp = new int[m+1][n+1]; + for (int i = 0; i <= m; i++) { + for (int j = 0; j <= n; j++) { + if (i == 0 || j == 0) + dp[i][j] = 0; + else if (partEquals(oldText, newText, i - 1, j - 1)) + dp[i][j] = dp[i - 1][j - 1] + 1; + else + dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); + } + } + + List parts = new ArrayList<>(); + int i = m, j = n; + while (i > 0 && j > 0) { + if (partEquals(oldText, newText, i - 1, j - 1)) { + int start = i-1; + while (i > 1 && j > 1 && partEquals(oldText, newText, i - 2, j - 2)) { + i--; + j--; + } + final int end = i - 1; + parts.add(() -> onEqualPart.run(oldText.subSequence(end, start + 1), end, start + 1)); + i--; + j--; + } else if (dp[i - 1][j] > dp[i][j - 1]) { + int start = i-1; + while (i > 1 && dp[i - 2][j] > dp[i - 1][j - 1]) { + i--; + } + final int end = i - 1; + parts.add(() -> onOldPart.run(oldText.subSequence(end, start + 1), end, start + 1)); + i--; + } else { + int start = j - 1; + while (j > 1 && dp[i][j - 2] > dp[i - 1][j - 1]) { + j--; + } + final int end = j - 1; + parts.add(() -> onNewPart.run(newText.subSequence(end, start + 1), end, start + 1)); + j--; + } + } + + while (i > 0) { + final int start = i - 1; + while (i > 1 && dp[i - 2][j] >= dp[i - 1][j]) { + i--; + } + final int end = i - 1; + parts.add(() -> onOldPart.run(oldText.subSequence(end, start + 1), end, start + 1)); + i--; + } + while (j > 0) { + final int start = j - 1; + while (j > 1 && dp[i][j - 2] >= dp[i][j - 1]) { + j--; + } + final int end = j - 1; + parts.add(() -> onNewPart.run(newText.subSequence(end, start + 1), end, start + 1)); + j--; + } + + Collections.reverse(parts); + for (Runnable part : parts) { + part.run(); + } + } + + private void diff(final CharSequence oldText, final CharSequence newText, RegionCallback onEqualPart, RegionCallback onNewPart, RegionCallback onOldPart) { + if (updateAll) { + onOldPart.run(oldText, 0, oldText.length()); + onNewPart.run(newText, 0, newText.length()); + return; + } if (preserveIndex) { boolean equal = true; int start = 0; @@ -563,7 +715,7 @@ private void diff(final CharSequence oldText, final CharSequence newText, Region } for (int i = indexes.size() - 1; i >= 0; --i) { int count = indexes.get(i); - if (i % 2 == 0 ? eq : !eq) { + if ((i % 2 == 0) == eq) { if (newText.length() > oldText.length()) { onEqualPart.run(newText.subSequence(a, a + count), a, a + count); } else { @@ -651,6 +803,14 @@ public void setTextColor(int color) { alpha = Color.alpha(color); } + private boolean shadowed = false; + private float shadowRadius, shadowDx, shadowDy; + private int shadowColor; + public void setShadowLayer(float radius, float dx, float dy, int shadowColor) { + shadowed = true; + textPaint.setShadowLayer(shadowRadius = radius, shadowDx = dx, shadowDy = dy, this.shadowColor = shadowColor); + } + public int getTextColor() { return textPaint.getColor(); } @@ -681,7 +841,7 @@ public TextPaint getPaint() { } private interface RegionCallback { - public void run(CharSequence part, int start, int end); + void run(CharSequence part, int start, int end); } @Override @@ -718,7 +878,7 @@ public Rect getDirtyBounds() { } } - private AnimatedTextDrawable drawable; + private final AnimatedTextDrawable drawable; private int lastMaxWidth, maxWidth; private CharSequence toSetText; @@ -873,4 +1033,12 @@ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { info.setClassName("android.widget.TextView"); info.setText(getText()); } + + public void setEllipsizeByGradient(boolean enabled) { + drawable.setEllipsizeByGradient(enabled); + } + + public void setRightPadding(float rightPadding) { + drawable.setRightPadding(rightPadding); + } } \ No newline at end of file diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ArchiveHelp.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ArchiveHelp.java new file mode 100644 index 0000000000..674ebb6769 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ArchiveHelp.java @@ -0,0 +1,162 @@ +package org.telegram.ui.Components; + +import static org.telegram.messenger.AndroidUtilities.dp; + +import android.content.Context; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.drawable.Drawable; +import android.text.SpannableString; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.style.DynamicDrawableSpan; +import android.text.style.ImageSpan; +import android.util.TypedValue; +import android.view.Gravity; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ContactsController; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.R; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Stories.recorder.ButtonWithCounterView; + +public class ArchiveHelp extends FrameLayout implements NotificationCenter.NotificationCenterDelegate { + + private int currentAccount; + private LinkSpanDrawable.LinksTextView subtitleTextView; + private Runnable linkCallback; + + public ArchiveHelp(Context context, int currentAccount, Theme.ResourcesProvider resourcesProvider, @NonNull Runnable linkCallback, Runnable buttonCallback) { + super(context); + + this.currentAccount = currentAccount; + this.linkCallback = linkCallback; + ContactsController.getInstance(currentAccount).loadGlobalPrivacySetting(); + + LinearLayout layout = new LinearLayout(context); + layout.setOrientation(LinearLayout.VERTICAL); + addView(layout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); + + ImageView archiveIcon = new ImageView(context); + archiveIcon.setBackground(Theme.createCircleDrawable(dp(80), Theme.getColor(Theme.key_avatar_backgroundSaved, resourcesProvider))); + archiveIcon.setImageResource(R.drawable.large_archive); + archiveIcon.setScaleType(ImageView.ScaleType.CENTER); + layout.addView(archiveIcon, LayoutHelper.createLinear(80, 80, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, (buttonCallback != null ? 14 : 0), 0, 14)); + + TextView titleTextView = new TextView(context); + titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); + titleTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider)); + titleTextView.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + titleTextView.setGravity(Gravity.CENTER_HORIZONTAL); + titleTextView.setText(LocaleController.getString("ArchiveHintHeader1", R.string.ArchiveHintHeader1)); + layout.addView(titleTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 32, 0, 32, 9)); + + subtitleTextView = new LinkSpanDrawable.LinksTextView(context); + subtitleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + subtitleTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2, resourcesProvider)); + subtitleTextView.setGravity(Gravity.CENTER_HORIZONTAL); + updateText(); + layout.addView(subtitleTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 32, 0, 32, 25)); + + layout.addView( + makeHint(R.drawable.msg_archive_archive, LocaleController.getString("ArchiveHintSection1"), LocaleController.getString("ArchiveHintSection1Info"), resourcesProvider), + LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.FILL_HORIZONTAL, 32, 0, 32, 16) + ); + layout.addView( + makeHint(R.drawable.msg_archive_hide, LocaleController.getString("ArchiveHintSection2"), LocaleController.getString("ArchiveHintSection2Info"), resourcesProvider), + LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.FILL_HORIZONTAL, 32, 0, 32, 16) + ); + layout.addView( + makeHint(R.drawable.msg_archive_stories, LocaleController.getString("ArchiveHintSection3"), LocaleController.getString("ArchiveHintSection3Info"), resourcesProvider), + LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.FILL_HORIZONTAL, 32, 0, 32, 16) + ); + + if (buttonCallback != null) { + ButtonWithCounterView button = new ButtonWithCounterView(context, resourcesProvider); + button.setText(LocaleController.getString("GotIt"), false); + button.setOnClickListener(e -> buttonCallback.run()); + layout.addView(button, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48, 14, 18, 14, 0)); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(MeasureSpec.makeMeasureSpec(Math.min(AndroidUtilities.dp(400), MeasureSpec.getSize(widthMeasureSpec)), MeasureSpec.EXACTLY), heightMeasureSpec); + } + + private void updateText() { + boolean keep = true; + TLRPC.TL_globalPrivacySettings settings = ContactsController.getInstance(currentAccount).getGlobalPrivacySettings(); + if (settings != null) { + keep = settings.keep_archived_unmuted; + } + String subtitle = LocaleController.getString(keep ? "ArchiveHintSubtitle" : "ArchiveHintSubtitleUnmutedMove"); + SpannableStringBuilder linkedSubtitle = AndroidUtilities.replaceSingleTag(subtitle, Theme.key_chat_messageLinkIn, 0, linkCallback); + SpannableString arrow = new SpannableString(">"); + Drawable imageDrawable = getContext().getResources().getDrawable(R.drawable.msg_arrowright).mutate(); + imageDrawable.setColorFilter(new PorterDuffColorFilter(Theme.key_chat_messageLinkIn, PorterDuff.Mode.SRC_IN)); + ColoredImageSpan span = new ColoredImageSpan(imageDrawable); + span.setColorKey(Theme.key_chat_messageLinkIn); + span.setSize(dp(18)); + span.setWidth(dp(11)); + span.setTranslateX(-dp(5)); + arrow.setSpan(span, 0, arrow.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + subtitleTextView.setText(AndroidUtilities.replaceCharSequence(">", linkedSubtitle, arrow)); + } + + private FrameLayout makeHint(int resId, CharSequence title, CharSequence subtitle, Theme.ResourcesProvider resourcesProvider) { + FrameLayout hint = new FrameLayout(getContext()); + + ImageView imageView = new ImageView(getContext()); + imageView.setColorFilter(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider)); + imageView.setImageResource(resId); + hint.addView(imageView, LayoutHelper.createFrame(24, 24, Gravity.LEFT | Gravity.TOP, 0, 8, 0, 0)); + + LinearLayout textLayout = new LinearLayout(getContext()); + textLayout.setOrientation(LinearLayout.VERTICAL); + TextView textView1 = new TextView(getContext()); + textView1.setTextColor(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider)); + textView1.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + textView1.setTextSize(TypedValue.COMPLEX_UNIT_PX, dp(14)); + textView1.setText(title); + textLayout.addView(textView1, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 2.6f, 0, 0)); + + TextView textView2 = new TextView(getContext()); + textView2.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2, resourcesProvider)); + textView2.setTextSize(TypedValue.COMPLEX_UNIT_PX, dp(14)); + textView2.setText(subtitle); + textLayout.addView(textView2, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 2.6f, 0, 0)); + hint.addView(textLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.FILL_HORIZONTAL, 41, 0, 0, 0)); + + return hint; + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.privacyRulesUpdated); + updateText(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.privacyRulesUpdated); + } + + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.privacyRulesUpdated) { + updateText(); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/AudioPlayerAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/AudioPlayerAlert.java index d13e6917fc..1b58508f47 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AudioPlayerAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AudioPlayerAlert.java @@ -74,6 +74,7 @@ import org.telegram.messenger.MediaController; import org.telegram.messenger.MessageObject; import org.telegram.messenger.MessagesController; +import org.telegram.messenger.MessagesStorage; import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; import org.telegram.messenger.SendMessagesHelper; @@ -96,6 +97,7 @@ import org.telegram.ui.Adapters.FiltersView; import org.telegram.ui.Cells.AudioPlayerCell; import org.telegram.ui.ChatActivity; +import org.telegram.ui.Components.Forum.ForumUtilities; import org.telegram.ui.DialogsActivity; import org.telegram.ui.LaunchActivity; @@ -103,6 +105,8 @@ import java.util.ArrayList; import java.util.List; +import xyz.nextalone.nagram.NaConfig; + public class AudioPlayerAlert extends BottomSheet implements NotificationCenter.NotificationCenterDelegate, DownloadController.FileDownloadProgressListener { private ActionBar actionBar; @@ -1465,21 +1469,23 @@ private void onSubItemClick(int id) { Bundle args = new Bundle(); args.putBoolean("onlySelect", true); args.putInt("dialogsType", DialogsActivity.DIALOGS_TYPE_FORWARD); + args.putBoolean("canSelectTopics", true); DialogsActivity fragment = new DialogsActivity(args); final ArrayList fmessages = new ArrayList<>(); fmessages.add(messageObject); fragment.setDelegate((fragment1, dids, message, param, topicsFragment) -> { - if (dids.size() > 1 || dids.get(0) .dialogId== UserConfig.getInstance(currentAccount).getClientUserId() || message != null) { + if (dids.size() > 1 || dids.get(0).dialogId == UserConfig.getInstance(currentAccount).getClientUserId() || message != null) { for (int a = 0; a < dids.size(); a++) { long did = dids.get(a).dialogId; if (message != null) { - SendMessagesHelper.getInstance(currentAccount).sendMessage(message.toString(), did, null, null, null, true, null, null, null, true, 0, null, false); + SendMessagesHelper.getInstance(currentAccount).sendMessage(SendMessagesHelper.SendMessageParams.of(message.toString(), did, null, null, null, true, null, null, null, true, 0, null, false)); } SendMessagesHelper.getInstance(currentAccount).sendMessage(fmessages, did, false, false, true, 0); } fragment1.finishFragment(); } else { - long did = dids.get(0).dialogId; + MessagesStorage.TopicKey topicKey = dids.get(0); + long did = topicKey.dialogId; Bundle args1 = new Bundle(); args1.putBoolean("scrollToTopOnResume", true); if (DialogObject.isEncryptedDialog(did)) { @@ -1489,10 +1495,15 @@ private void onSubItemClick(int id) { } else { args1.putLong("chat_id", -did); } - NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.closeChats); ChatActivity chatActivity = new ChatActivity(args1); + if (topicKey.topicId != 0) { + ForumUtilities.applyTopic(chatActivity, topicKey); + } if (parentActivity.presentFragment(chatActivity, true, false)) { chatActivity.showFieldPanelForForward(true, fmessages); + if (topicKey.topicId != 0) { + fragment1.removeSelfFromStack(); + } } else { fragment1.finishFragment(); } @@ -1583,7 +1594,7 @@ private void onSubItemClick(int id) { if (path == null || path.length() == 0) { path = FileLoader.getInstance(currentAccount).getPathToMessage(messageObject.messageOwner).toString(); } - MediaController.saveFile(path, parentActivity, 3, fileName, messageObject.getDocument() != null ? messageObject.getDocument().mime_type : "", () -> BulletinFactory.of((FrameLayout) containerView, resourcesProvider).createDownloadBulletin(BulletinFactory.FileType.AUDIO).show()); + MediaController.saveFile(path, parentActivity, 3, fileName, messageObject.getDocument() != null ? messageObject.getDocument().mime_type : "", uri -> BulletinFactory.of((FrameLayout) containerView, resourcesProvider).createDownloadBulletin(BulletinFactory.FileType.AUDIO).show()); } } @@ -2006,7 +2017,13 @@ private void updateTitle(boolean shutdown) { } else { optionsButton.setVisibility(View.VISIBLE); } - if (MessagesController.getInstance(currentAccount).isChatNoForwardsWithOverride(messageObject.getChatId())) { + final long dialogId = messageObject.getDialogId(); + final boolean noforwards = ( + dialogId < 0 && MessagesController.getInstance(currentAccount).isChatNoForwards(-dialogId) || + MessagesController.getInstance(currentAccount).isChatNoForwards(messageObject.getChatId()) || + messageObject.messageOwner.noforwards + ) && !NaConfig.INSTANCE.getForceCopy().Bool(); + if (noforwards) { optionsButton.hideSubItem(1); optionsButton.hideSubItem(2); optionsButton.hideSubItem(5); @@ -2034,7 +2051,7 @@ private void updateTitle(boolean shutdown) { titleTextView.setText(title); authorTextView.setText(author); - int duration = lastDuration = messageObject.getDuration(); + int duration = lastDuration = (int) messageObject.getDuration(); if (durationTextView != null) { durationTextView.setText(duration != 0 ? AndroidUtilities.formatShortDuration(duration) : "-:--"); @@ -2299,7 +2316,7 @@ public ArrayList getThemeDescriptions() { EditTextBoldCursor editText = searchItem.getSearchField(); editText.setCursorColor(getThemedColor(Theme.key_player_actionBarTitle)); - repeatButton.setIconColor(getThemedColor((String) repeatButton.getTag())); + repeatButton.setIconColor(getThemedColor((Integer) repeatButton.getTag())); Theme.setSelectorDrawableColor(repeatButton.getBackground(), getThemedColor(Theme.key_listSelector), true); optionsButton.setIconColor(getThemedColor(Theme.key_player_button)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/AutoDeletePopupWrapper.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/AutoDeletePopupWrapper.java index 0409725fa8..a5d2028c00 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AutoDeletePopupWrapper.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AutoDeletePopupWrapper.java @@ -72,14 +72,14 @@ public AutoDeletePopupWrapper(Context context, PopupSwipeBackLayout swipeBackLay callback.setAutoDeleteHistory(0, UndoView.ACTION_AUTO_DELETE_OFF); }); if (type != TYPE_GROUP_CREATE) { - disableItem.setColors(Theme.getColor(Theme.key_dialogTextRed), Theme.getColor(Theme.key_dialogTextRed)); + disableItem.setColors(Theme.getColor(Theme.key_text_RedBold), Theme.getColor(Theme.key_text_RedBold)); } if (type != TYPE_GROUP_CREATE) { FrameLayout gap = new FrameLayout(context); gap.setBackgroundColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuSeparator, resourcesProvider)); View gapShadow = new View(context); - gapShadow.setBackground(Theme.getThemedDrawable(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow, resourcesProvider)); + gapShadow.setBackground(Theme.getThemedDrawableByKey(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow, resourcesProvider)); gap.addView(gapShadow, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); gap.setTag(R.id.fit_width_tag, 1); windowLayout.addView(gap, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 8)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarConstructorFragment.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarConstructorFragment.java index 56737fea88..45006fe53f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarConstructorFragment.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarConstructorFragment.java @@ -295,7 +295,7 @@ protected void dispatchDraw(Canvas canvas) { @Override public boolean onInterceptTouchEvent(MotionEvent ev) { - if (keyboardVisibleProgress == 0 && AndroidUtilities.findClickableView(this, ev.getX(), ev.getY())) { + if (keyboardVisibleProgress == 0) { return false; } return onTouchEvent(ev); @@ -432,7 +432,7 @@ protected void onEmojiSelected(View view, Long documentId, TLRPC.Document docume button = new FrameLayout(getContext()); - button.setBackground(Theme.AdaptiveRipple.filledRect(Theme.key_featuredStickers_addButton, 8)); + button.setBackground(Theme.AdaptiveRipple.filledRectByKey(Theme.key_featuredStickers_addButton, 8)); TextView textView = new TextView(getContext()); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); @@ -1106,7 +1106,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { colorPickerContainer.addView(colorPicker); FrameLayout button = new FrameLayout(getContext()); - button.setBackground(Theme.AdaptiveRipple.filledRect(Theme.key_featuredStickers_addButton, 8)); + button.setBackground(Theme.AdaptiveRipple.filledRectByKey(Theme.key_featuredStickers_addButton, 8)); TextView textView = new TextView(getContext()); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarDrawable.java index 2b168534c7..566a2d84a7 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarDrawable.java @@ -77,6 +77,7 @@ public class AvatarDrawable extends Drawable { public static final int AVATAR_TYPE_FILTER_ARCHIVED = 11; public static final int AVATAR_TYPE_REGISTER = 13; public static final int AVATAR_TYPE_OTHER_CHATS = 14; + public static final int AVATAR_TYPE_CLOSE_FRIENDS = 15; private int alpha = 255; private Theme.ResourcesProvider resourcesProvider; @@ -154,7 +155,7 @@ public static int getProfileBackColorForId(long id, Theme.ResourcesProvider reso return Theme.getColor(Theme.key_avatar_backgroundActionBarBlue, resourcesProvider); } - public static String getNameColorNameForId(long id) { + public static int getNameColorNameForId(long id) { return Theme.keys_avatar_nameInMessage[getColorIndex(id)]; } @@ -283,7 +284,7 @@ public int getColor2() { return needApplyColorAccent ? Theme.changeColorAccent(color2) : color2; } - private String takeFirstCharacter(String text) { + private static String takeFirstCharacter(String text) { ArrayList ranges = Emoji.parseEmojis(text); if (ranges != null && !ranges.isEmpty() && ranges.get(0).start == 0) { return text.substring(0, ranges.get(0).end); @@ -306,12 +307,16 @@ public void setInfo(long id, String firstName, String lastName, String custom) { lastName = null; } - stringBuilder.setLength(0); + getAvatarSymbols(firstName, lastName, custom, stringBuilder); + } + + public static void getAvatarSymbols(String firstName, String lastName, String custom, StringBuilder result) { + result.setLength(0); if (custom != null) { - stringBuilder.append(custom); + result.append(custom); } else { if (firstName != null && firstName.length() > 0) { - stringBuilder.append(takeFirstCharacter(firstName)); + result.append(takeFirstCharacter(firstName)); } if (lastName != null && lastName.length() > 0) { String lastNameLastWord = lastName; @@ -320,18 +325,18 @@ public void setInfo(long id, String firstName, String lastName, String custom) { lastNameLastWord = lastNameLastWord.substring(index + 1); } if (Build.VERSION.SDK_INT > 17) { - stringBuilder.append("\u200C"); + result.append("\u200C"); } - stringBuilder.append(takeFirstCharacter(lastNameLastWord)); + result.append(takeFirstCharacter(lastNameLastWord)); } else if (firstName != null && firstName.length() > 0) { for (int a = firstName.length() - 1; a >= 0; a--) { if (firstName.charAt(a) == ' ') { if (a != firstName.length() - 1 && firstName.charAt(a + 1) != ' ') { - int index = stringBuilder.length(); + int index = result.length(); if (Build.VERSION.SDK_INT > 17) { - stringBuilder.append("\u200C"); + result.append("\u200C"); } - stringBuilder.append(takeFirstCharacter(firstName.substring(index))); + result.append(takeFirstCharacter(firstName.substring(index))); break; } } @@ -421,6 +426,8 @@ public void draw(Canvas canvas) { drawable = Theme.avatarDrawables[11]; } else if (avatarType == AVATAR_TYPE_OTHER_CHATS) { drawable = Theme.avatarDrawables[12]; + } else if (avatarType == AVATAR_TYPE_CLOSE_FRIENDS) { + drawable = Theme.avatarDrawables[13]; } else { drawable = Theme.avatarDrawables[9]; } @@ -512,9 +519,8 @@ public int getIntrinsicHeight() { return 0; } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } public void setRoundRadius(int roundRadius) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarsDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarsDrawable.java index 22cc04190b..8c21a30ddb 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarsDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarsDrawable.java @@ -27,6 +27,7 @@ import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Cells.GroupCallUserCell; +import org.telegram.ui.Stories.StoriesGradientTools; import java.util.Random; @@ -58,10 +59,13 @@ public class AvatarsDrawable { View parent; private int overrideSize; + private float overrideSizeStepFactor = 0.8f; private float overrideAlpha = 1f; public long transitionDuration = 220; public Interpolator transitionInterpolator = CubicBezierInterpolator.DEFAULT; private boolean transitionInProgress; + public boolean drawStoriesCircle; + StoriesGradientTools storiesTools; public void commitTransition(boolean animated) { commitTransition(animated, true); @@ -129,6 +133,7 @@ public void commitTransition(boolean animated, boolean createAnimator) { } } if (transitionProgressAnimator != null) { + transitionProgressAnimator.removeAllListeners(); transitionProgressAnimator.cancel(); if (transitionInProgress) { swapStates(); @@ -199,6 +204,10 @@ public void setSize(int size) { overrideSize = size; } + public void setStepFactor(float factor) { + overrideSizeStepFactor = factor; + } + public void animateFromState(AvatarsDrawable avatarsDrawable, int currentAccount, boolean createAnimator) { if (avatarsDrawable.transitionProgressAnimator != null) { avatarsDrawable.transitionProgressAnimator.cancel(); @@ -342,9 +351,8 @@ public void setObject(int index, int account, TLObject object) { } else { animatingStates[index].imageReceiver.setForUserOrChat(currentChat, animatingStates[index].avatarDrawable); } - boolean bigAvatars = currentStyle == 4 || currentStyle == STYLE_GROUP_CALL_TOOLTIP; - animatingStates[index].imageReceiver.setRoundRadius(AndroidUtilities.dp(bigAvatars ? 16 : 12)); int size = getSize(); + animatingStates[index].imageReceiver.setRoundRadius(size / 2); animatingStates[index].imageReceiver.setImageCoords(0, 0, size, size); invalidate(); } @@ -357,7 +365,7 @@ public void onDraw(Canvas canvas) { if (currentStyle == STYLE_MESSAGE_SEEN) { toAdd = AndroidUtilities.dp(12); } else if (overrideSize != 0) { - toAdd = (int) (overrideSize * 0.8f); + toAdd = (int) (overrideSize * overrideSizeStepFactor); } else { toAdd = AndroidUtilities.dp(bigAvatars ? 24 : 20); } @@ -385,8 +393,78 @@ public void onDraw(Canvas canvas) { boolean useAlphaLayer = currentStyle == 0 || currentStyle == 1 || currentStyle == 3 || currentStyle == 4 || currentStyle == 5 || currentStyle == STYLE_GROUP_CALL_TOOLTIP || currentStyle == STYLE_MESSAGE_SEEN; if (useAlphaLayer) { float padding = currentStyle == STYLE_GROUP_CALL_TOOLTIP ? AndroidUtilities.dp(16) : 0; + if (drawStoriesCircle) { + padding += AndroidUtilities.dp(20); + } canvas.saveLayerAlpha(-padding, -padding, width + padding, height + padding, 255, Canvas.ALL_SAVE_FLAG); } + if (drawStoriesCircle) { + for (int a = 2; a >= 0; a--) { + for (int k = 0; k < 2; k++) { + if (k == 0 && transitionProgress == 1f) { + continue; + } + DrawingState[] states = k == 0 ? animatingStates : currentStates; + + if (k == 1 && transitionProgress != 1f && states[a].animationType != DrawingState.ANIMATION_TYPE_OUT) { + continue; + } + ImageReceiver imageReceiver = states[a].imageReceiver; + if (!imageReceiver.hasImageSet()) { + continue; + } + if (k == 0) { + int toAx = centered ? (width - animateToDrawCount * toAdd - AndroidUtilities.dp(bigAvatars ? 8 : 4)) / 2 : startPadding; + imageReceiver.setImageX(toAx + toAdd * a); + } else { + imageReceiver.setImageX(ax + toAdd * a); + } + + if (currentStyle == 0 || currentStyle == STYLE_GROUP_CALL_TOOLTIP || currentStyle == STYLE_MESSAGE_SEEN) { + imageReceiver.setImageY((height - size) / 2f); + } else { + imageReceiver.setImageY(AndroidUtilities.dp(currentStyle == 4 ? 8 : 6)); + } + + boolean needRestore = false; + float alpha = 1f; + if (transitionProgress != 1f) { + if (states[a].animationType == DrawingState.ANIMATION_TYPE_OUT) { + canvas.save(); + canvas.scale(1f - transitionProgress, 1f - transitionProgress, imageReceiver.getCenterX(), imageReceiver.getCenterY()); + needRestore = true; + alpha = 1f - transitionProgress; + } else if (states[a].animationType == DrawingState.ANIMATION_TYPE_IN) { + canvas.save(); + canvas.scale(transitionProgress, transitionProgress, imageReceiver.getCenterX(), imageReceiver.getCenterY()); + alpha = transitionProgress; + needRestore = true; + } else if (states[a].animationType == DrawingState.ANIMATION_TYPE_MOVE) { + int toAx = centered ? (width - animateToDrawCount * toAdd - AndroidUtilities.dp(bigAvatars ? 8 : 4)) / 2 : startPadding; + int toX = toAx + toAdd * a; + int fromX = ax + toAdd * states[a].moveFromIndex; + imageReceiver.setImageX((int) (toX * transitionProgress + fromX * (1f - transitionProgress))); + } else if (states[a].animationType == DrawingState.ANIMATION_TYPE_NONE && centered) { + int toAx = (width - animateToDrawCount * toAdd - AndroidUtilities.dp(bigAvatars ? 8 : 4)) / 2; + int toX = toAx + toAdd * a; + int fromX = ax + toAdd * a; + imageReceiver.setImageX((int) (toX * transitionProgress + fromX * (1f - transitionProgress))); + } + } + alpha *= overrideAlpha; + float rad = getSize() / 2f + AndroidUtilities.dp(4); + if (storiesTools == null) { + storiesTools = new StoriesGradientTools(); + } + storiesTools.setBounds(0, 0, parent.getMeasuredHeight(), AndroidUtilities.dp(40)); + storiesTools.paint.setAlpha((int) (255 * alpha)); + canvas.drawCircle(imageReceiver.getCenterX(), imageReceiver.getCenterY(), rad, storiesTools.paint); + if (needRestore) { + canvas.restore(); + } + } + } + } for (int a = 2; a >= 0; a--) { for (int k = 0; k < 2; k++) { if (k == 0 && transitionProgress == 1f) { @@ -443,7 +521,7 @@ public void onDraw(Canvas canvas) { alpha *= overrideAlpha; float avatarScale = 1f; - if (a != states.length - 1) { + if (a != states.length - 1 || drawStoriesCircle) { if (currentStyle == 1 || currentStyle == 3 || currentStyle == 5) { canvas.drawCircle(imageReceiver.getCenterX(), imageReceiver.getCenterY(), AndroidUtilities.dp(13), xRefP); if (states[a].wavesDrawable == null) { @@ -541,7 +619,7 @@ public void onDraw(Canvas canvas) { } } - private int getSize() { + public int getSize() { if (overrideSize != 0) { return overrideSize; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarsImageView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarsImageView.java index 962b4b86bb..443b276b15 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarsImageView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarsImageView.java @@ -59,6 +59,14 @@ public void setAvatarsTextSize(int size) { avatarsDrawable.setAvatarsTextSize(size); } + public void setSize(int size) { + avatarsDrawable.setSize(size); + } + + public void setStepFactor(float factor) { + avatarsDrawable.setStepFactor(factor); + } + public void reset() { avatarsDrawable.reset(); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/BackupImageView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/BackupImageView.java index 1c0750ac82..17f5965114 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BackupImageView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BackupImageView.java @@ -38,11 +38,12 @@ public class BackupImageView extends View { protected boolean hasBlur; protected boolean blurAllowed; + public boolean drawFromStart; public BackupImageView(Context context) { super(context); - imageReceiver = new ImageReceiver(this); - + imageReceiver = createImageReciever(); + imageReceiver.setAllowLoadingOnAttachedOnly(true); imageReceiver.setDelegate((imageReceiver1, set, thumb, memCache) -> { if (set && !thumb) { checkCreateBlurredImage(); @@ -50,6 +51,10 @@ public BackupImageView(Context context) { }); } + protected ImageReceiver createImageReciever() { + return new ImageReceiver(this); + } + public void setBlurAllowed(boolean blurAllowed) { if (attached) { throw new IllegalStateException("You should call setBlurAllowed(...) only when detached!"); @@ -74,7 +79,7 @@ public void setHasBlur(boolean hasBlur) { checkCreateBlurredImage(); } - private void onNewImageSet() { + public void onNewImageSet() { if (hasBlur) { if (blurImageReceiver.getBitmap() != null && !blurImageReceiver.getBitmap().isRecycled()) { blurImageReceiver.getBitmap().recycle(); @@ -98,6 +103,10 @@ public void setOrientation(int angle, boolean center) { imageReceiver.setOrientation(angle, center); } + public void setOrientation(int angle, int invert, boolean center) { + imageReceiver.setOrientation(angle, invert, center); + } + public void setImage(SecureDocument secureDocument, String filter) { setImage(ImageLocation.getForSecureDocument(secureDocument), filter, null, null, null, null, null, 0, null); } @@ -132,6 +141,10 @@ public void setImage(ImageLocation imageLocation, String imageFilter, Bitmap thu onNewImageSet(); } + public void clearImage() { + imageReceiver.clearImage(); + } + public void setForUserOrChat(TLObject object, AvatarDrawable avatarDrawable) { imageReceiver.setForUserOrChat(object, avatarDrawable); onNewImageSet(); @@ -294,9 +307,16 @@ protected void onDraw(Canvas canvas) { return; } if (width != -1 && height != -1) { - imageReceiver.setImageCoords((getWidth() - width) / 2, (getHeight() - height) / 2, width, height); - if (blurAllowed) { - blurImageReceiver.setImageCoords((getWidth() - width) / 2, (getHeight() - height) / 2, width, height); + if (drawFromStart) { + imageReceiver.setImageCoords(0, 0, width, height); + if (blurAllowed) { + blurImageReceiver.setImageCoords(0, 0, width, height); + } + } else { + imageReceiver.setImageCoords((getWidth() - width) / 2, (getHeight() - height) / 2, width, height); + if (blurAllowed) { + blurImageReceiver.setImageCoords((getWidth() - width) / 2, (getHeight() - height) / 2, width, height); + } } } else { imageReceiver.setImageCoords(0, 0, getWidth(), getHeight()); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/BitmapShaderTools.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/BitmapShaderTools.java new file mode 100644 index 0000000000..4ee2471caf --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BitmapShaderTools.java @@ -0,0 +1,67 @@ +package org.telegram.ui.Components; + +import android.graphics.Bitmap; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.RectF; +import android.graphics.Shader; + +import org.telegram.messenger.AndroidUtilities; + +public class BitmapShaderTools { + + final public Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + final Canvas canvas; + final Bitmap bitmap; + + private final static int INTERNAL_WIDTH = 30; + private final static int INTERNAL_HEIGHT = 40; + + final RectF bounds = new RectF(); + final Shader shader; + final Matrix matrix = new Matrix(); + + public BitmapShaderTools() { + bitmap = Bitmap.createBitmap(INTERNAL_WIDTH, INTERNAL_HEIGHT, Bitmap.Config.ARGB_8888); + canvas = new Canvas(bitmap); + paint.setShader(shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)); + updateBounds(); + } + + public Bitmap getBitmap() { + return bitmap; + } + + public Canvas getCanvas() { + return canvas; + } + + public void setBounds(RectF bounds) { + if (this.bounds.top == bounds.top && this.bounds.bottom == bounds.bottom && this.bounds.left == bounds.left && this.bounds.right == bounds.right) { + return; + } + this.bounds.set(bounds); + updateBounds(); + } + + private void updateBounds() { + if (shader == null) { + return; + } + float sx = bounds.width() / (float) bitmap.getWidth(); + float sy = bounds.height() / (float) bitmap.getHeight(); + + matrix.reset(); + matrix.postTranslate(bounds.left, bounds.top); + matrix.preScale(sx, sy); + + shader.setLocalMatrix(matrix); + } + + public void setBounds(float left, float top, float right, float bottom) { + AndroidUtilities.rectTmp.set(left, top, right, bottom); + setBounds(AndroidUtilities.rectTmp); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/BlockingUpdateView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/BlockingUpdateView.java index 4b778ce8f0..ab5e279da6 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BlockingUpdateView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BlockingUpdateView.java @@ -141,7 +141,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } }; acceptButton.setPadding(AndroidUtilities.dp(34), 0, AndroidUtilities.dp(34), 0); - acceptButton.setBackgroundDrawable(Theme.AdaptiveRipple.filledRect(Theme.key_featuredStickers_addButton, 4)); + acceptButton.setBackgroundDrawable(Theme.AdaptiveRipple.filledRectByKey(Theme.key_featuredStickers_addButton, 4)); acceptButton.setPadding(AndroidUtilities.dp(34), 0, AndroidUtilities.dp(34), 0); addView(acceptButton, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 46, Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM, 0, 0, 0, 45)); acceptButton.setOnClickListener(view1 -> { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/BlurBehindDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/BlurBehindDrawable.java index ec9f805560..838b372492 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BlurBehindDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BlurBehindDrawable.java @@ -1,5 +1,7 @@ package org.telegram.ui.Components; +import static android.graphics.Canvas.ALL_SAVE_FLAG; + import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; @@ -15,8 +17,6 @@ import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.ChatActivity; -import static android.graphics.Canvas.ALL_SAVE_FLAG; - public class BlurBehindDrawable { DispatchQueue queue; @@ -418,8 +418,7 @@ private Drawable getBackgroundDrawable() { : Theme.getCachedWallpaperNonBlocking(); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/BotCommandsMenuView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/BotCommandsMenuView.java index 68de90c07b..ee5d3ca7fe 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BotCommandsMenuView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BotCommandsMenuView.java @@ -26,7 +26,9 @@ import androidx.recyclerview.widget.RecyclerView; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.Emoji; import org.telegram.messenger.LocaleController; +import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.MenuDrawable; @@ -334,7 +336,14 @@ public BotCommandView(@NonNull Context context) { setOrientation(HORIZONTAL); setPadding(AndroidUtilities.dp(16), 0, AndroidUtilities.dp(16), 0); - description = new TextView(context); + description = new TextView(context) { + @Override + public void setText(CharSequence text, BufferType type) { + text = Emoji.replaceEmoji(text, getPaint().getFontMetricsInt(), AndroidUtilities.dp(14), false); + super.setText(text, type); + } + }; + NotificationCenter.listenEmojiLoading(description); description.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); description.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); description.setTag(Theme.key_windowBackgroundWhiteBlackText); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/BotKeyboardView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/BotKeyboardView.java index b6037b7e49..ad9c2d76f4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BotKeyboardView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BotKeyboardView.java @@ -167,8 +167,7 @@ public int getKeyboardHeight() { return isFullSize ? panelHeight : botButtons.rows.size() * AndroidUtilities.dp(buttonHeight) + AndroidUtilities.dp(30) + (botButtons.rows.size() - 1) * AndroidUtilities.dp(10); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/BotWebViewContainer.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/BotWebViewContainer.java index d6a54c0c2d..ff5e748460 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BotWebViewContainer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BotWebViewContainer.java @@ -1136,21 +1136,21 @@ public void didReceivedNotification(int id, int account, Object... args) { currentDialog = builder.show(); if (buttonsList.size() >= 1) { PopupButton btn = buttonsList.get(0); - if (btn.textColorKey != null) { + if (btn.textColorKey >= 0) { TextView textView = (TextView) currentDialog.getButton(AlertDialog.BUTTON_POSITIVE); textView.setTextColor(getColor(btn.textColorKey)); } } if (buttonsList.size() >= 2) { PopupButton btn = buttonsList.get(1); - if (btn.textColorKey != null) { + if (btn.textColorKey >= 0) { TextView textView = (TextView) currentDialog.getButton(AlertDialog.BUTTON_NEGATIVE); textView.setTextColor(getColor(btn.textColorKey)); } } if (buttonsList.size() == 3) { PopupButton btn = buttonsList.get(2); - if (btn.textColorKey != null) { + if (btn.textColorKey >= 0) { TextView textView = (TextView) currentDialog.getButton(AlertDialog.BUTTON_NEUTRAL); textView.setTextColor(getColor(btn.textColorKey)); } @@ -1182,7 +1182,7 @@ public void didReceivedNotification(int id, int account, Object... args) { try { JSONObject jsonObject = new JSONObject(eventData); String key = jsonObject.getString("color_key"); - String themeKey = null; + int themeKey = -1; switch (key) { case "bg_color": { themeKey = Theme.key_windowBackgroundWhite; @@ -1193,7 +1193,7 @@ public void didReceivedNotification(int id, int account, Object... args) { break; } } - if (themeKey != null) { + if (themeKey >= 0) { delegate.onWebAppSetActionBarColor(themeKey); } } catch (JSONException e) { @@ -1426,15 +1426,14 @@ private JSONObject buildThemeParams() { } } - private int getColor(String colorKey) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(colorKey) : Theme.getColor(colorKey); - if (color == null) { - color = Theme.getColor(colorKey); + private int getColor(int colorKey) { + if (resourcesProvider != null) { + return resourcesProvider.getColor(colorKey); } - return color; + return Theme.getColor(colorKey); } - private String formatColor(String colorKey) { + private String formatColor(int colorKey) { int color = getColor(colorKey); return "#" + hexFixed(Color.red(color)) + hexFixed(Color.green(color)) + hexFixed(Color.blue(color)); } @@ -1490,7 +1489,7 @@ default void onSendWebViewData(String data) {} * * @param colorKey Color theme key */ - void onWebAppSetActionBarColor(String colorKey); + void onWebAppSetActionBarColor(int colorKey); /** * Called when WebView requests to set background color @@ -1547,8 +1546,7 @@ default boolean isClipboardAvailable() { public final static class PopupButton { public String id; public String text; - @Nullable - public String textColorKey; + public int textColorKey = -1; public PopupButton(JSONObject obj) throws JSONException { id = obj.getString("id"); @@ -1574,7 +1572,7 @@ public PopupButton(JSONObject obj) throws JSONException { } case "destructive": { textRequired = true; - textColorKey = Theme.key_dialogTextRed; + textColorKey = Theme.key_text_RedBold; break; } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/BotWebViewMenuContainer.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/BotWebViewMenuContainer.java index fc5649f213..fb9009158c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BotWebViewMenuContainer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BotWebViewMenuContainer.java @@ -159,7 +159,7 @@ public void onWebAppSetupClosingBehavior(boolean needConfirmation) { } @Override - public void onWebAppSetActionBarColor(String colorKey) { + public void onWebAppSetActionBarColor(int colorKey) { int from = overrideActionBarBackground; int to = getColor(colorKey); @@ -376,7 +376,7 @@ private void invalidateActionBar() { } ChatAvatarContainer avatarContainer = chatActivity.getAvatarContainer(); - String subtitleDefaultColorKey = avatarContainer.getLastSubtitleColorKey() == null ? Theme.key_actionBarDefaultSubtitle : avatarContainer.getLastSubtitleColorKey(); + int subtitleDefaultColorKey = avatarContainer.getLastSubtitleColorKey() < 0 ? Theme.key_actionBarDefaultSubtitle : avatarContainer.getLastSubtitleColorKey(); int subtitleColor = ColorUtils.blendARGB(getColor(subtitleDefaultColorKey), getColor(Theme.key_windowBackgroundWhiteGrayText), actionBarTransitionProgress); ActionBar actionBar = chatActivity.getActionBar(); int backgroundColor = ColorUtils.blendARGB(getColor(Theme.key_actionBarDefault), getColor(Theme.key_windowBackgroundWhite), actionBarTransitionProgress); @@ -421,7 +421,7 @@ public boolean onCheckDismissByUser() { .create(); dialog.show(); TextView textView = (TextView) dialog.getButton(AlertDialog.BUTTON_POSITIVE); - textView.setTextColor(getColor(Theme.key_dialogTextRed)); + textView.setTextColor(getColor(Theme.key_text_RedBold)); return false; } else { dismiss(); @@ -762,15 +762,9 @@ private void loadWebView() { })); } - private int getColor(String key) { - Integer color; + private int getColor(int key) { Theme.ResourcesProvider resourcesProvider = parentEnterView.getParentFragment().getResourceProvider(); - if (resourcesProvider != null) { - color = resourcesProvider.getColor(key); - } else { - color = Theme.getColor(key); - } - return color != null ? color : Theme.getColor(key); + return Theme.getColor(key, resourcesProvider); } /** diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/BotWebViewSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/BotWebViewSheet.java index 6e7bae60a5..9829a9837f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BotWebViewSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BotWebViewSheet.java @@ -42,6 +42,7 @@ import org.telegram.messenger.MessagesController; import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; +import org.telegram.messenger.SendMessagesHelper; import org.telegram.messenger.UserObject; import org.telegram.messenger.Utilities; import org.telegram.tgnet.ConnectionsManager; @@ -146,7 +147,7 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi prolongWebView.query_id = queryId; prolongWebView.silent = silent; if (replyToMsgId != 0) { - prolongWebView.reply_to_msg_id = replyToMsgId; + prolongWebView.reply_to = SendMessagesHelper.creteReplyInput(replyToMsgId); prolongWebView.flags |= 1; } ConnectionsManager.getInstance(currentAccount).sendRequest(prolongWebView, (response, error) -> AndroidUtilities.runOnUIThread(() -> { @@ -236,7 +237,7 @@ public void onSendWebViewData(String data) { } @Override - public void onWebAppSetActionBarColor(String colorKey) { + public void onWebAppSetActionBarColor(int colorKey) { int from = actionBarColor; int to = getColor(colorKey); @@ -297,6 +298,8 @@ public void onWebAppSwitchInlineQuery(TLRPC.User botUser, String query, List= 0) { paint.setColor((Theme.getColor(colorKey) & 0x00ffffff) | 0xb4000000); } else { paint.setColor(Theme.getCurrentTheme().isDark() ? 0xff555555 : 0xffbbbbbb); @@ -67,7 +67,7 @@ protected void onDraw(Canvas canvas) { rect.set(x, 0, x + AndroidUtilities.dp(5), AndroidUtilities.dp(5)); canvas.drawRoundRect(rect, AndroidUtilities.dp(2.5f), AndroidUtilities.dp(2.5f), paint); } - if (selectedColorKey != null) { + if (selectedColorKey >= 0) { paint.setColor(Theme.getColor(selectedColorKey)); } else { paint.setColor(0xff2ca5e0); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/BottomSheetWithRecyclerListView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/BottomSheetWithRecyclerListView.java index 6daeb3cd46..5de35c21b1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BottomSheetWithRecyclerListView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BottomSheetWithRecyclerListView.java @@ -245,7 +245,7 @@ private void postDrawInternal(Canvas canvas, View parentView) { } shadowAlpha = Utilities.clamp(shadowAlpha, 1f, 0f); if (actionBar != null && actionBar.getVisibility() == View.VISIBLE && actionBar.getAlpha() != 0 && shadowAlpha != 0) { - headerShadowDrawable.setBounds(0, actionBar.getBottom(), parentView.getMeasuredWidth(), actionBar.getBottom() + headerShadowDrawable.getIntrinsicHeight()); + headerShadowDrawable.setBounds(backgroundPaddingLeft, actionBar.getBottom(), parentView.getMeasuredWidth() - backgroundPaddingLeft, actionBar.getBottom() + headerShadowDrawable.getIntrinsicHeight()); headerShadowDrawable.setAlpha((int) (255 * actionBar.getAlpha() * shadowAlpha)); headerShadowDrawable.draw(canvas); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Bulletin.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Bulletin.java index 2b6f924be5..d2c7cfaa33 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Bulletin.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Bulletin.java @@ -10,11 +10,15 @@ import android.content.Context; import android.content.res.Configuration; import android.graphics.Canvas; +import android.graphics.LinearGradient; +import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; +import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; +import android.graphics.Shader; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.os.Build; @@ -141,6 +145,7 @@ public static void hide(@NonNull FrameLayout containerLayout, boolean animated) public int lastBottomOffset; private Delegate currentDelegate; private Layout.Transition layoutTransition; + public boolean hideAfterBottomSheet = true; private Bulletin() { layout = null; @@ -186,6 +191,11 @@ public Bulletin setDuration(int duration) { return this; } + public Bulletin hideAfterBottomSheet(boolean hide) { + this.hideAfterBottomSheet = hide; + return this; + } + public Bulletin show() { return show(false); } @@ -309,6 +319,13 @@ public void setCanHide(boolean canHide) { } } + private Runnable onHideListener; + + public Bulletin setOnHideListener(Runnable listener) { + this.onHideListener = listener; + return this; + } + private void ensureLayoutTransitionCreated() { if (layout != null && layoutTransition == null) { layoutTransition = layout.createTransition(); @@ -361,6 +378,10 @@ public void hide(boolean animated, long duration) { containerLayout.removeView(parentLayout); containerLayout.removeOnLayoutChangeListener(containerLayoutListener); layout.onDetach(); + + if (onHideListener != null) { + onHideListener.run(); + } }, offset -> { if (currentDelegate != null && !layout.top) { currentDelegate.onBottomOffsetChange(layout.getHeight() - offset); @@ -384,6 +405,10 @@ public void hide(boolean animated, long duration) { }); } layout.onDetach(); + + if (onHideListener != null) { + onHideListener.run(); + } } } @@ -416,7 +441,7 @@ public void updatePosition() { private @interface GravityDef { } - private static abstract class ParentLayout extends FrameLayout { + public static abstract class ParentLayout extends FrameLayout { private final Layout layout; private final Rect rect = new Rect(); @@ -577,6 +602,10 @@ default int getTopOffset(int tag) { return 0; } + default boolean clipWithGradient(int tag) { + return false; + } + default void onBottomOffsetChange(float offset) { } @@ -967,6 +996,10 @@ private void setInOutOffset(float offset) { updatePosition(); } + private LinearGradient clipGradient; + private Matrix clipMatrix; + private Paint clipPaint; + @Override protected void dispatchDraw(Canvas canvas) { if (bulletin == null) { @@ -974,15 +1007,37 @@ protected void dispatchDraw(Canvas canvas) { } background.setBounds(AndroidUtilities.dp(8), AndroidUtilities.dp(8), getMeasuredWidth() - AndroidUtilities.dp(8), getMeasuredHeight() - AndroidUtilities.dp(8)); if (isTransitionRunning() && delegate != null) { + final float top = delegate.getTopOffset(bulletin.tag) - getY(); + final float bottom = ((View) getParent()).getMeasuredHeight() - getBottomOffset() - getY(); + final boolean clip = delegate.clipWithGradient(bulletin.tag); canvas.save(); - canvas.clipRect( - 0, - delegate.getTopOffset(bulletin.tag) - getY(), - getMeasuredWidth(), - ((View) getParent()).getMeasuredHeight() - getBottomOffset() - getY() - ); + canvas.clipRect(0, top, getMeasuredWidth(), bottom); + if (clip) { + canvas.saveLayerAlpha(0, 0, getWidth(), getHeight(), 0xFF, Canvas.ALL_SAVE_FLAG); + } background.draw(canvas); super.dispatchDraw(canvas); + if (clip) { + if (clipPaint == null) { + clipPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + clipPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); + clipGradient = new LinearGradient(0, 0, 0, AndroidUtilities.dp(8), this.top ? new int[] {0xff000000, 0} : new int[] {0, 0xff000000}, new float[] { 0, 1 }, Shader.TileMode.CLAMP); + clipMatrix = new Matrix(); + clipGradient.setLocalMatrix(clipMatrix); + clipPaint.setShader(clipGradient); + } + canvas.save(); + clipMatrix.reset(); + clipMatrix.postTranslate(0, this.top ? top : bottom - AndroidUtilities.dp(8)); + clipGradient.setLocalMatrix(clipMatrix); + if (this.top) { + canvas.drawRect(0, top, getWidth(), top + AndroidUtilities.dp(8), clipPaint); + } else { + canvas.drawRect(0, bottom - AndroidUtilities.dp(8), getWidth(), bottom, clipPaint); + } + canvas.restore(); + canvas.restore(); + } canvas.restore(); invalidate(); } else { @@ -991,9 +1046,8 @@ protected void dispatchDraw(Canvas canvas) { } } - protected int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + protected int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } //endregion } @@ -1059,7 +1113,7 @@ public void setTimer() { public static class SimpleLayout extends ButtonLayout { public final ImageView imageView; - public final TextView textView; + public final LinkSpanDrawable.LinksTextView textView; public SimpleLayout(@NonNull Context context, Theme.ResourcesProvider resourcesProvider) { super(context, resourcesProvider); @@ -1070,7 +1124,8 @@ public SimpleLayout(@NonNull Context context, Theme.ResourcesProvider resourcesP imageView.setColorFilter(new PorterDuffColorFilter(undoInfoColor, PorterDuff.Mode.SRC_IN)); addView(imageView, LayoutHelper.createFrameRelatively(24, 24, Gravity.START | Gravity.CENTER_VERTICAL, 16, 12, 16, 12)); - textView = new TextView(context); + textView = new LinkSpanDrawable.LinksTextView(context); + textView.setDisablePaddingsOffsetY(true); textView.setSingleLine(); textView.setTextColor(undoInfoColor); textView.setTypeface(Typeface.SANS_SERIF); @@ -1392,11 +1447,12 @@ public void setText(CharSequence text, BufferType type) { textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); textView.setEllipsize(TextUtils.TruncateAt.END); textView.setPadding(0, AndroidUtilities.dp(8), 0, AndroidUtilities.dp(8)); - addView(textView, LayoutHelper.createFrameRelatively(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.START | Gravity.CENTER_VERTICAL, 12 + 56 + 2, 0, 8, 0)); + textView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); + addView(textView, LayoutHelper.createFrameRelatively(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.START | Gravity.CENTER_VERTICAL, 12 + 56 + 2, 0, 12, 0)); } else { linearLayout = new LinearLayout(getContext()); linearLayout.setOrientation(LinearLayout.VERTICAL); - addView(linearLayout, LayoutHelper.createFrameRelatively(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.START | Gravity.CENTER_VERTICAL, 18 + 56 + 2, 0, 8, 0)); + addView(linearLayout, LayoutHelper.createFrameRelatively(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.START | Gravity.CENTER_VERTICAL, 18 + 56 + 2, 0, 12, 0)); textView = new LinkSpanDrawable.LinksTextView(context) { @Override @@ -1547,7 +1603,9 @@ public void undo() { if (undoAction != null) { undoAction.run(); } - bulletin.hide(); + if (bulletin != null) { + bulletin.hide(); + } } } @@ -1574,9 +1632,11 @@ public UndoButton setDelayedAction(Runnable delayedAction) { return this; } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + protected int getThemedColor(int key) { + if (resourcesProvider != null) { + return resourcesProvider.getColor(key); + } + return Theme.getColor(key); } } @@ -1643,7 +1703,7 @@ protected void onDraw(Canvas canvas) { rect.set(AndroidUtilities.dp(1), AndroidUtilities.dp(1), getMeasuredWidth() - AndroidUtilities.dp(1), getMeasuredHeight() - AndroidUtilities.dp(1)); if (prevSeconds != newSeconds) { prevSeconds = newSeconds; - timeLeftString = String.format("%d", Math.max(0, newSeconds)); + timeLeftString = String.valueOf(Math.max(0, newSeconds)); if (timeLayout != null) { timeLayoutOut = timeLayout; timeReplaceProgress = 0; @@ -1802,4 +1862,9 @@ private void applyInsets(WindowInsets insets) { } } } + + public Bulletin setTag(int tag) { + this.tag = tag; + return this; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/BulletinFactory.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/BulletinFactory.java index adb07b60a8..e2e055c496 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BulletinFactory.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BulletinFactory.java @@ -35,6 +35,7 @@ import org.telegram.messenger.UserConfig; import org.telegram.messenger.UserObject; import org.telegram.messenger.Utilities; +import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.Theme; @@ -148,9 +149,15 @@ private enum Icon { private final Theme.ResourcesProvider resourcesProvider; private BulletinFactory(BaseFragment fragment) { - this.fragment = fragment; - this.containerLayout = null; - this.resourcesProvider = fragment == null ? null : fragment.getResourceProvider(); + if (fragment != null && fragment.storyViewer != null && fragment.storyViewer.attachedToParent()) { + this.fragment = null; + this.containerLayout = fragment.storyViewer.getContainerForBulletin(); + this.resourcesProvider = fragment.storyViewer.getResourceProvider(); + } else { + this.fragment = fragment; + this.containerLayout = null; + this.resourcesProvider = fragment == null ? null : fragment.getResourceProvider(); + } } private BulletinFactory(FrameLayout containerLayout, Theme.ResourcesProvider resourcesProvider) { @@ -171,6 +178,16 @@ public Bulletin createSimpleBulletin(int iconRawId, CharSequence text) { public Bulletin createSimpleBulletin(int iconRawId, CharSequence text, int maxLines) { final Bulletin.LottieLayout layout = new Bulletin.LottieLayout(getContext(), resourcesProvider); layout.setAnimation(iconRawId, 36, 36); + if (text != null) { + String string = text.toString(); + SpannableStringBuilder ssb = text instanceof SpannableStringBuilder ? (SpannableStringBuilder) text : new SpannableStringBuilder(text); + for (int index = string.indexOf('\n'), l = 0; index >= 0 && index < text.length(); l++, index = string.indexOf('\n', index + 1)) { + if (l >= maxLines) { + ssb.replace(index, index + 1, " "); + } + } + text = ssb; + } layout.textView.setText(text); layout.textView.setSingleLine(false); layout.textView.setMaxLines(maxLines); @@ -200,7 +217,12 @@ public Bulletin createSimpleBulletin(int iconRawId, CharSequence text, CharSeque public Bulletin createSimpleBulletin(int iconRawId, CharSequence text, CharSequence button, int duration, Runnable onButtonClick) { final Bulletin.LottieLayout layout = new Bulletin.LottieLayout(getContext(), resourcesProvider); - layout.setAnimation(iconRawId, 36, 36); + if (iconRawId != 0) { + layout.setAnimation(iconRawId, 36, 36); + } else { + layout.imageView.setVisibility(View.INVISIBLE); + ((ViewGroup.MarginLayoutParams) layout.textView.getLayoutParams()).leftMargin = AndroidUtilities.dp(16); + } layout.textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); layout.textView.setTextDirection(View.TEXT_DIRECTION_LOCALE); layout.textView.setSingleLine(false); @@ -274,10 +296,10 @@ public Bulletin createUndoBulletin(CharSequence text, Runnable onUndo, Runnable } public Bulletin createUsersBulletin(List users, CharSequence text) { - return createUsersBulletin(users, text, null); + return createUsersBulletin(users, text, null, null); } - public Bulletin createUsersBulletin(List users, CharSequence text, CharSequence subtitle) { + public Bulletin createUsersBulletin(List users, CharSequence text, CharSequence subtitle, UndoObject undoObject) { final Bulletin.UsersLayout layout = new Bulletin.UsersLayout(getContext(), subtitle != null, resourcesProvider); int count = 0; if (users != null) { @@ -311,6 +333,9 @@ public Bulletin createUsersBulletin(List users, CharSequence text, C layout.subtitleView.setMaxLines(1); if (layout.linearLayout.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { int margin = AndroidUtilities.dp(12 + 56 + 2 - (3 - count) * 12); + if (count == 1) { + margin += AndroidUtilities.dp(4); + } if (LocaleController.isRTL) { ((ViewGroup.MarginLayoutParams) layout.linearLayout.getLayoutParams()).rightMargin = margin; } else { @@ -323,6 +348,10 @@ public Bulletin createUsersBulletin(List users, CharSequence text, C layout.textView.setText(text); if (layout.textView.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { int margin = AndroidUtilities.dp(12 + 56 + 2 - (3 - count) * 12); + if (count == 1) { + layout.textView.setTranslationY(-AndroidUtilities.dp(1)); + margin += AndroidUtilities.dp(4); + } if (LocaleController.isRTL) { ((ViewGroup.MarginLayoutParams) layout.textView.getLayoutParams()).rightMargin = margin; } else { @@ -331,6 +360,69 @@ public Bulletin createUsersBulletin(List users, CharSequence text, C } } + if (undoObject != null) { + layout.setButton(new Bulletin.UndoButton(getContext(), true, resourcesProvider).setText(LocaleController.getString("Undo", R.string.Undo)).setUndoAction(undoObject.onUndo).setDelayedAction(undoObject.onAction)); + } + + return create(layout, Bulletin.DURATION_PROLONG); + } + + public Bulletin createChatsBulletin(List objects, CharSequence text, CharSequence subtitle) { + final Bulletin.UsersLayout layout = new Bulletin.UsersLayout(getContext(), subtitle != null, resourcesProvider); + int count = 0; + if (objects != null) { + for (int i = 0; i < objects.size(); ++i) { + if (count >= 3) + break; + TLObject object = objects.get(i); + if (object != null) { + layout.avatarsImageView.setCount(++count); + layout.avatarsImageView.setObject(count - 1, UserConfig.selectedAccount, object); + } + } + if (objects.size() == 1) { + layout.avatarsImageView.setTranslationX(AndroidUtilities.dp(4)); + layout.avatarsImageView.setScaleX(1.2f); + layout.avatarsImageView.setScaleY(1.2f); + } else { + layout.avatarsImageView.setScaleX(1f); + layout.avatarsImageView.setScaleY(1f); + } + } + layout.avatarsImageView.commitTransition(false); + + if (subtitle != null) { + layout.textView.setSingleLine(true); + layout.textView.setMaxLines(1); + layout.textView.setText(text); + layout.subtitleView.setText(subtitle); + layout.subtitleView.setSingleLine(true); + layout.subtitleView.setMaxLines(1); + if (layout.linearLayout.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { + int margin = AndroidUtilities.dp(12 + 56 + 6 - (3 - count) * 12); + if (LocaleController.isRTL) { + ((ViewGroup.MarginLayoutParams) layout.linearLayout.getLayoutParams()).rightMargin = margin; + } else { + ((ViewGroup.MarginLayoutParams) layout.linearLayout.getLayoutParams()).leftMargin = margin; + } + } + } else { + layout.textView.setSingleLine(false); + layout.textView.setMaxLines(2); + layout.textView.setText(text); + if (layout.textView.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { + int margin = AndroidUtilities.dp(12 + 56 + 6 - (3 - count) * 12); + if (LocaleController.isRTL) { + ((ViewGroup.MarginLayoutParams) layout.textView.getLayoutParams()).rightMargin = margin; + } else { + ((ViewGroup.MarginLayoutParams) layout.textView.getLayoutParams()).leftMargin = margin; + } + } + } + if (LocaleController.isRTL) { + layout.avatarsImageView.setTranslationX(AndroidUtilities.dp(32 - (count - 1) * 12)); + } + return create(layout, Bulletin.DURATION_PROLONG); } @@ -414,7 +506,11 @@ public Bulletin createEmojiLoadingBulletin(TLRPC.Document document, CharSequence return create(layout, Bulletin.DURATION_LONG); } - public Bulletin createContainsEmojiBulletin(TLRPC.Document document, boolean inTopic, Utilities.Callback openSet) { + public static final int CONTAINS_EMOJI_IN_MESSAGE = 0; + public static final int CONTAINS_EMOJI_IN_TOPIC = 1; + public static final int CONTAINS_EMOJI_IN_STORY = 2; + + public Bulletin createContainsEmojiBulletin(TLRPC.Document document, int type, Utilities.Callback openSet) { TLRPC.InputStickerSet inputStickerSet = MessageObject.getInputStickerSet(document); if (inputStickerSet == null) { return null; @@ -423,15 +519,17 @@ public Bulletin createContainsEmojiBulletin(TLRPC.Document document, boolean inT if (cachedSet == null || cachedSet.set == null) { final String loadingPlaceholder = "<{LOADING}>"; SpannableStringBuilder stringBuilder; - if (inTopic) { + if (type == CONTAINS_EMOJI_IN_TOPIC) { stringBuilder = new SpannableStringBuilder(AndroidUtilities.replaceTags(LocaleController.formatString("TopicContainsEmojiPackSingle", R.string.TopicContainsEmojiPackSingle, loadingPlaceholder))); + } else if (type == CONTAINS_EMOJI_IN_STORY) { + stringBuilder = new SpannableStringBuilder(AndroidUtilities.replaceTags(LocaleController.formatString("StoryContainsEmojiPackSingle", R.string.StoryContainsEmojiPackSingle, loadingPlaceholder))); } else { stringBuilder = new SpannableStringBuilder(AndroidUtilities.replaceTags(LocaleController.formatString("MessageContainsEmojiPackSingle", R.string.MessageContainsEmojiPackSingle, loadingPlaceholder))); } LoadingSpan loadingSpan = null; int index; if ((index = stringBuilder.toString().indexOf(loadingPlaceholder)) >= 0) { - stringBuilder.setSpan(loadingSpan = new LoadingSpan(null, AndroidUtilities.dp(100)), index, index + loadingPlaceholder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + stringBuilder.setSpan(loadingSpan = new LoadingSpan(null, AndroidUtilities.dp(100), AndroidUtilities.dp(2), resourcesProvider), index, index + loadingPlaceholder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); loadingSpan.setColors( ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_undo_infoColor, resourcesProvider), 0x20), ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_undo_infoColor, resourcesProvider), 0x48) @@ -439,15 +537,17 @@ public Bulletin createContainsEmojiBulletin(TLRPC.Document document, boolean inT } final long startTime = System.currentTimeMillis(); final long minDuration = 750; - Bulletin bulletin = createEmojiLoadingBulletin(document, stringBuilder, LocaleController.getString("ViewAction", R.string.ViewAction), () -> openSet.run(inputStickerSet)).show(); + Bulletin bulletin = createEmojiLoadingBulletin(document, stringBuilder, LocaleController.getString("ViewAction", R.string.ViewAction), () -> openSet.run(inputStickerSet)); if (loadingSpan != null && bulletin.getLayout() instanceof Bulletin.LoadingLottieLayout) { loadingSpan.setView(((Bulletin.LoadingLottieLayout) bulletin.getLayout()).textLoadingView); } MediaDataController.getInstance(UserConfig.selectedAccount).getStickerSet(inputStickerSet, null, false, set -> { CharSequence message; if (set != null && set.set != null) { - if (inTopic) { + if (type == CONTAINS_EMOJI_IN_TOPIC) { message = AndroidUtilities.replaceTags(LocaleController.formatString("TopicContainsEmojiPackSingle", R.string.TopicContainsEmojiPackSingle, set.set.title)); + } else if (type == CONTAINS_EMOJI_IN_STORY) { + message = AndroidUtilities.replaceTags(LocaleController.formatString("StoryContainsEmojiPackSingle", R.string.StoryContainsEmojiPackSingle, set.set.title)); } else { message = AndroidUtilities.replaceTags(LocaleController.formatString("MessageContainsEmojiPackSingle", R.string.MessageContainsEmojiPackSingle, set.set.title)); } @@ -461,8 +561,10 @@ public Bulletin createContainsEmojiBulletin(TLRPC.Document document, boolean inT return bulletin; } else { CharSequence message; - if (inTopic) { + if (type == CONTAINS_EMOJI_IN_TOPIC) { message = AndroidUtilities.replaceTags(LocaleController.formatString("TopicContainsEmojiPackSingle", R.string.TopicContainsEmojiPackSingle, cachedSet.set.title)); + } else if (type == CONTAINS_EMOJI_IN_STORY) { + message = AndroidUtilities.replaceTags(LocaleController.formatString("StoryContainsEmojiPackSingle", R.string.StoryContainsEmojiPackSingle, cachedSet.set.title)); } else { message = AndroidUtilities.replaceTags(LocaleController.formatString("MessageContainsEmojiPackSingle", R.string.MessageContainsEmojiPackSingle, cachedSet.set.title)); } @@ -731,6 +833,22 @@ public static Bulletin createMuteBulletin(BaseFragment fragment, int setting, in return Bulletin.make(fragment, layout, Bulletin.DURATION_SHORT); } + @CheckResult + public static Bulletin createMuteBulletin(BaseFragment fragment, boolean mute, int chatsCount, Theme.ResourcesProvider resourcesProvider) { + final Bulletin.LottieLayout layout = new Bulletin.LottieLayout(fragment.getParentActivity(), resourcesProvider); + layout.textView.setText( + mute ? + LocaleController.formatPluralString("NotificationsMutedHintChats", chatsCount) : + LocaleController.formatPluralString("NotificationsUnmutedHintChats", chatsCount) + ); + if (mute) { + layout.setAnimation(R.raw.ic_mute, "Body Main", "Body Top", "Line", "Curve Big", "Curve Small"); + } else { + layout.setAnimation(R.raw.ic_unmute, "BODY", "Wibe Big", "Wibe Big 3", "Wibe Small"); + } + return Bulletin.make(fragment, layout, Bulletin.DURATION_SHORT); + } + @CheckResult public static Bulletin createMuteBulletin(BaseFragment fragment, boolean muted, Theme.ResourcesProvider resourcesProvider) { return createMuteBulletin(fragment, muted ? NotificationsController.SETTING_MUTE_FOREVER : NotificationsController.SETTING_MUTE_UNMUTE, 0, resourcesProvider); @@ -774,6 +892,11 @@ public static Bulletin createSaveToGalleryBulletin(BaseFragment fragment, boolea return of(fragment).createDownloadBulletin(video ? FileType.VIDEO : FileType.PHOTO, resourcesProvider); } + @CheckResult + public static Bulletin createSaveToGalleryBulletin(FrameLayout containerLayout, boolean video, Theme.ResourcesProvider resourcesProvider) { + return of(containerLayout, resourcesProvider).createDownloadBulletin(video ? FileType.VIDEO : FileType.PHOTO, resourcesProvider); + } + @CheckResult public static Bulletin createSaveToGalleryBulletin(FrameLayout containerLayout, boolean video, int backgroundColor, int textColor) { return of(containerLayout, null).createDownloadBulletin(video ? FileType.VIDEO : FileType.PHOTO, 1, backgroundColor, textColor); @@ -978,4 +1101,10 @@ public static Bulletin createSoundEnabledBulletin(BaseFragment fragment, int set return Bulletin.make(fragment, layout, Bulletin.DURATION_SHORT); } //endregion + + public static class UndoObject { + public CharSequence undoText; + public Runnable onUndo; + public Runnable onAction; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ButtonBounce.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ButtonBounce.java new file mode 100644 index 0000000000..3726e8b5f8 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ButtonBounce.java @@ -0,0 +1,81 @@ +package org.telegram.ui.Components; + + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.view.View; +import android.view.animation.OvershootInterpolator; + +public class ButtonBounce { + + private View view; + private final float durationMultiplier; + + public ButtonBounce(View viewToInvalidate) { + view = viewToInvalidate; + durationMultiplier = 1f; + } + + public ButtonBounce(View viewToInvalidate, float durationMultiplier) { + view = viewToInvalidate; + this.durationMultiplier = durationMultiplier; + } + + public void setView(View view) { + this.view = view; + } + + private ValueAnimator animator; + private boolean isPressed; + private float pressedT; + + public void setPressed(boolean pressed) { + if (isPressed != pressed) { + isPressed = pressed; + if (animator != null) { + animator.cancel(); + } + animator = ValueAnimator.ofFloat(pressedT, pressed ? 1 : 0); + animator.addUpdateListener(anm -> { + pressedT = (float) anm.getAnimatedValue(); + invalidate(); + }); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + animator = null; + pressedT = pressed ? 1 : 0; + invalidate(); + } + }); + if (isPressed) { + animator.setInterpolator(CubicBezierInterpolator.DEFAULT); + animator.setDuration((long) (60 * durationMultiplier)); + } else { + animator.setInterpolator(new OvershootInterpolator(5.0f)); + animator.setDuration((long) (350 * durationMultiplier)); + } + animator.start(); + } + } + + public float isPressedProgress() { + return pressedT; + } + + public float getScale(float diff) { + return (1f - diff) + diff * (1f - pressedT); + } + + public boolean isPressed() { + return isPressed; + } + + private void invalidate() { + if (view != null) { + view.invalidate(); + } + } + +} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/CacheChart.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/CacheChart.java index b5e132054c..9e61dfa0a0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/CacheChart.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/CacheChart.java @@ -45,13 +45,14 @@ public class CacheChart extends View { private RectF chartBounds = new RectF(); private RectF chartInnerBounds = new RectF(); - private static final int DEFAULT_SECTIONS_COUNT = 9; - private static final String[] DEFAULT_COLORS = new String[] { + private static final int DEFAULT_SECTIONS_COUNT = 10; + private static final int[] DEFAULT_COLORS = new int[] { Theme.key_statisticChartLine_lightblue, Theme.key_statisticChartLine_blue, Theme.key_statisticChartLine_green, - Theme.key_statisticChartLine_red, + Theme.key_statisticChartLine_purple, Theme.key_statisticChartLine_lightgreen, + Theme.key_statisticChartLine_red, Theme.key_statisticChartLine_orange, Theme.key_statisticChartLine_cyan, Theme.key_statisticChartLine_purple, @@ -64,6 +65,7 @@ public class CacheChart extends View { R.raw.cache_documents, R.raw.cache_music, R.raw.cache_videos, + R.raw.cache_music, R.raw.cache_stickers, R.raw.cache_profile_photos, R.raw.cache_other, @@ -71,7 +73,7 @@ public class CacheChart extends View { }; private final int sectionsCount; - private final String[] colorKeys; + private final int[] colorKeys; private final int type; private final boolean svgParticles; private final int[] particles; @@ -356,7 +358,7 @@ public CacheChart(Context context) { this(context, DEFAULT_SECTIONS_COUNT, DEFAULT_COLORS, TYPE_CACHE, DEFAULT_PARTICLES); } - public CacheChart(Context context, int count, String[] colorKeys, int type, int[] particles) { + public CacheChart(Context context, int count, int[] colorKeys, int type, int[] particles) { super(context); setLayerType(LAYER_TYPE_HARDWARE, null); @@ -703,7 +705,7 @@ public void setSegments(long totalSize, boolean animated, SegmentSize ...segment k++; } - String[] fileSize = AndroidUtilities.formatFileSize(segmentsSum).split(" "); + String[] fileSize = AndroidUtilities.formatFileSize(segmentsSum, true, true).split(" "); String top = fileSize.length > 0 ? fileSize[0] : ""; if (top.length() >= 4 && segmentsSum < 1024L * 1024L * 1024L) { top = top.split("\\.")[0]; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityBotWebViewButton.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityBotWebViewButton.java index d4613ba2a5..4b52b1fb9d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityBotWebViewButton.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityBotWebViewButton.java @@ -71,6 +71,7 @@ public void setupButtonParams(boolean isActive, String text, int color, int text buttonColor = color; rippleView.setBackground(Theme.createSelectorDrawable(BotWebViewContainer.getMainButtonRippleColor(buttonColor), 2)); + invalidate(); progressView.setProgressColor(textColor); if (progressWasVisible != isProgressVisible) { @@ -136,4 +137,14 @@ public void draw(Canvas canvas) { super.draw(canvas); canvas.restore(); } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int height = MeasureSpec.getSize(heightMeasureSpec); + int parentHeight = getParent() instanceof View ? ((View) getParent()).getHeight() : 0; + if (parentHeight > 0) { + height = Math.min(height, parentHeight); + } + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.getMode(heightMeasureSpec))); + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java index 6ac58b2b55..b7b8157396 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java @@ -49,6 +49,7 @@ import android.os.Bundle; import android.os.PowerManager; import android.os.SystemClock; +import android.provider.Settings; import android.text.Editable; import android.text.Layout; import android.text.Spannable; @@ -113,6 +114,7 @@ import org.openintents.openpgp.util.OpenPgpApi; import org.telegram.messenger.AccountInstance; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.BuildVars; import org.telegram.messenger.ChatObject; @@ -200,6 +202,35 @@ public class ChatActivityEnterView extends BlurredFrameLayout implements Notific private int commonInputType; private boolean stickersEnabled; + private ActionBarMenuSubItem sendWhenOnlineButton; + private LinearLayout recordTimeContainer; + private CharSequence overrideHint; + float emojiButtonScale = 1f; + float emojiButtonAlpha = 1f; + float emojiButtonPaddingScale = 1f; + float emojiButtonPaddingAlpha = 1f; + private boolean overrideKeyboardAnimation; + private Runnable animationEndRunnable; + private float attachLayoutTranslationX; + private float attachLayoutPaddingTranslationX; + private float messageTextTranslationX; + private float messageTextPaddingTranslationX; + private float horizontalPadding = 0; + private boolean sendButtonEnabled = true; + private TLRPC.UserFull userInfo; + + public void drawRecordedPannel(Canvas canvas) { + if (getAlpha() == 0 || recordedAudioPanel == null || recordedAudioPanel.getParent() == null || recordedAudioPanel.getVisibility() != View.VISIBLE) { + return; + } + int restoreCount = canvas.save(); + canvas.translate(getX() + textFieldContainer.getX() + messageEditTextContainer.getX() + recordedAudioPanel.getX(), getY() + textFieldContainer.getY() + messageEditTextContainer.getY() + recordedAudioPanel.getY()); + if (getAlpha() != 1f) { + canvas.saveLayerAlpha(0, 0, getMeasuredWidth(), getMeasuredHeight(), (int) (255 * getAlpha()), Canvas.ALL_SAVE_FLAG); + } + recordedAudioPanel.draw(canvas); + canvas.restoreToCount(restoreCount); + } public interface ChatActivityEnterViewDelegate { default void beforeMessageSend(CharSequence message, boolean notify, int scheduleDate) { @@ -318,14 +349,18 @@ default int measureKeyboardHeight() { default TLRPC.TL_channels_sendAsPeers getSendAsPeers() { return null; } + + default TLRPC.StoryItem getReplyToStory() { + return null; + } } - private final static int RECORD_STATE_ENTER = 0; - private final static int RECORD_STATE_SENDING = 1; - private final static int RECORD_STATE_CANCEL = 2; - private final static int RECORD_STATE_PREPARING = 3; - private final static int RECORD_STATE_CANCEL_BY_TIME = 4; - private final static int RECORD_STATE_CANCEL_BY_GESTURE = 5; + public final static int RECORD_STATE_ENTER = 0; + public final static int RECORD_STATE_SENDING = 1; + public final static int RECORD_STATE_CANCEL = 2; + public final static int RECORD_STATE_PREPARING = 3; + public final static int RECORD_STATE_CANCEL_BY_TIME = 4; + public final static int RECORD_STATE_CANCEL_BY_GESTURE = 5; private final static int POPUP_CONTENT_BOT_KEYBOARD = 1; @@ -425,7 +460,7 @@ public boolean onTouchEvent(MotionEvent event) { @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - seekBarWaveform.setSize(right - left, bottom - top); + seekBarWaveform.setSize((int) (right - left - horizontalPadding * 2), bottom - top); } @Override @@ -456,6 +491,7 @@ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo i private Drawable sendButtonDrawable; private Drawable inactinveSendButtonDrawable; private Drawable sendButtonInverseDrawable; + private int sendButtonBackgroundColor; private ActionBarPopupWindow sendPopupWindow; private ActionBarPopupWindow.ActionBarPopupWindowLayout sendPopupLayout; private ImageView cancelBotButton; @@ -473,7 +509,7 @@ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo i @Nullable private FrameLayout recordPanel; @Nullable - private FrameLayout recordedAudioPanel; + protected FrameLayout recordedAudioPanel; @Nullable private VideoTimelineView videoTimelineView; @SuppressWarnings("FieldCanBeLocal") @@ -496,6 +532,8 @@ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo i private ImageView attachButton; @Nullable private ImageView botButton; + @Nullable + private ImageView reactionsButton; private FrameLayout messageEditTextContainer; private FrameLayout textFieldContainer; private FrameLayout sendButtonContainer; @@ -576,7 +614,7 @@ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo i private float startedDraggingX = -1; private float distCanMove = AndroidUtilities.dp(80); private boolean recordingAudioVideo; - private int recordingGuid; + public int recordingGuid; public boolean forceShowSendButton; private boolean allowAnimatedEmoji; private boolean allowStickers; @@ -782,7 +820,8 @@ public void run() { delegate.needStartRecordAudio(1); startedDraggingX = -1; - MediaController.getInstance().startRecording(currentAccount, dialog_id, replyingMessageObject, getThreadMessage(), recordingGuid, true); + TLRPC.StoryItem storyItem = delegate != null ? delegate.getReplyToStory() : null; + MediaController.getInstance().startRecording(currentAccount, dialog_id, replyingMessageObject, getThreadMessage(), storyItem, recordingGuid, true); recordingAudioVideo = true; updateRecordInterface(RECORD_STATE_ENTER); if (recordTimerView != null) { @@ -799,7 +838,7 @@ public void run() { } }; - private int notificationsIndex; + private AnimationNotificationsLocker notificationsLocker = new AnimationNotificationsLocker(); private class RecordDot extends View { @@ -840,7 +879,7 @@ public RecordDot(Context context) { public void updateColors() { int dotColor = getThemedColor(Theme.key_chat_recordedVoiceDot); - int background = getThemedColor(Theme.key_chat_messagePanelBackground); + int background = ColorUtils.blendARGB(dotColor, getThemedColor(Theme.key_chat_messagePanelBackground), 0.5f); redDotPaint.setColor(dotColor); drawable.beginApplyLayerColors(); drawable.setLayerColor("Cup Red.**", dotColor); @@ -937,6 +976,58 @@ public void run() { } }; + private final Property EMOJI_BUTTON_SCALE = new Property(Float.class, "emoji_button_scale") { + @Override + public Float get(View object) { + return emojiButtonScale; + } + + @Override + public void set(View object, Float value) { + emojiButtonScale = value; + updateEmojiButtonParams(); + } + }; + + private final Property EMOJI_BUTTON_ALPHA = new Property(Float.class, "emoji_button_alpha") { + @Override + public Float get(View object) { + return emojiButtonAlpha; + } + + @Override + public void set(View object, Float value) { + emojiButtonAlpha = value; + updateEmojiButtonParams(); + } + }; + + private final Property ATTACH_LAYOUT_TRANSLATION_X = new Property(Float.class, "attach_layout_translation_x") { + @Override + public Float get(View object) { + return attachLayoutTranslationX; + } + + @Override + public void set(View object, Float value) { + attachLayoutTranslationX = value; + updateAttachLayoutParams(); + } + }; + + private final Property MESSAGE_TEXT_TRANSLATION_X = new Property(Float.class, "message_text_translation_x") { + @Override + public Float get(View object) { + return messageTextTranslationX; + } + + @Override + public void set(View object, Float value) { + messageTextTranslationX = value; + updateMessageTextParams(); + } + }; + public class RecordCircle extends View { private float scale; @@ -1405,15 +1496,15 @@ protected void onDraw(Canvas canvas) { View transformToView = recordedAudioBackground; View v = (View) transformToView.getParent(); while (v != getParent()) { - topOffset += v.getTop(); - leftOffset += v.getLeft(); + topOffset += v.getY(); + leftOffset += v.getX(); v = (View) v.getParent(); } - int seekbarT = transformToView.getTop() + topOffset - getTop(); - int seekbarB = transformToView.getBottom() + topOffset - getTop(); - int seekbarR = transformToView.getRight() + leftOffset - getLeft(); - int seekbarL = transformToView.getLeft() + leftOffset - getLeft(); + float seekbarT = transformToView.getY() + topOffset - getY(); + float seekbarB = transformToView.getY() + transformToView.getMeasuredHeight() + topOffset - getY(); + float seekbarR = transformToView.getX() + transformToView.getMeasuredWidth() + leftOffset - getX() - horizontalPadding; + float seekbarL = transformToView.getX() + leftOffset - getX() + horizontalPadding; float toRadius = isInVideoMode() ? 0 : transformToView.getMeasuredHeight() / 2f; float top = seekbarT + (circleT - seekbarT) * (1f - progressToSeekbarStep3); @@ -1933,6 +2024,9 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { canvas.restore(); return rez; } + if (shouldDrawRecordedAudioPanelInParent && child == recordedAudioPanel) { + return true; + } return super.drawChild(canvas, child, drawingTime); } }; @@ -2044,7 +2138,7 @@ public void onClick(View v) { attachButton = new ImageView(context); attachButton.setScaleType(ImageView.ScaleType.CENTER); - attachButton.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_chat_messagePanelIcons), PorterDuff.Mode.SRC_IN)); + attachButton.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_chat_messagePanelIcons), PorterDuff.Mode.MULTIPLY)); attachButton.setImageResource(R.drawable.input_attach); if (Build.VERSION.SDK_INT >= 21) { attachButton.setBackgroundDrawable(Theme.createSelectorDrawable(getThemedColor(Theme.key_listSelector))); @@ -2071,6 +2165,22 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { } return super.drawChild(canvas, child, drawingTime); } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + if (!sendButtonEnabled) { + return false; + } + return super.dispatchTouchEvent(ev); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (!sendButtonEnabled) { + return false; + } + return super.onTouchEvent(event); + } }; sendButtonContainer.setClipChildren(false); sendButtonContainer.setClipToPadding(false); @@ -2111,14 +2221,14 @@ public boolean onTouchEvent(MotionEvent motionEvent) { getParent().requestDisallowInterceptTouchEvent(true); return true; } - if (parentFragment != null) { - TLRPC.Chat chat = parentFragment.getCurrentChat(); - TLRPC.UserFull userFull = parentFragment.getCurrentUserInfo(); - if (chat != null && !(ChatObject.canSendVoice(chat) || (ChatObject.canSendRoundVideo(chat) && hasRecordVideo)) || userFull != null && userFull.voice_messages_forbidden) { - delegate.needShowMediaBanHint(); - return true; - } + + TLRPC.Chat chat = parentFragment == null ? null : parentFragment.getCurrentChat(); + TLRPC.UserFull userFull = parentFragment == null ? userInfo : parentFragment.getCurrentUserInfo(); + if (chat != null && !(ChatObject.canSendVoice(chat) || (ChatObject.canSendRoundVideo(chat) && hasRecordVideo)) || userFull != null && userFull.voice_messages_forbidden) { + delegate.needShowMediaBanHint(); + return true; } + if (hasRecordVideo) { calledRecordRunnable = false; recordAudioVideoRunnableStarted = true; @@ -2203,6 +2313,12 @@ public boolean onTouchEvent(MotionEvent motionEvent) { updateRecordInterface(RECORD_STATE_SENDING); }, 500); } + recordingAudioVideo = false; + messageTransitionIsRunning = false; + AndroidUtilities.runOnUIThread(moveToSendStateRunnable = () -> { + moveToSendStateRunnable = null; + updateRecordInterface(RECORD_STATE_SENDING); + }, shouldDrawBackground ? 500 : 0); } } return true; @@ -2515,9 +2631,12 @@ protected int getCurrentColor() { private long lastAnimationTime; private float animationDuration; private int prevColorType; + private Paint backgroundPaint; @Override protected void onDraw(Canvas canvas) { + int rad = AndroidUtilities.dp(20); + int center = getMeasuredWidth() >> 1; int x = (getMeasuredWidth() - sendButtonDrawable.getIntrinsicWidth()) / 2; int y = (getMeasuredHeight() - sendButtonDrawable.getIntrinsicHeight()) / 2; if (isInScheduleMode()) { @@ -2575,8 +2694,11 @@ protected void onDraw(Canvas canvas) { } } if (showingPopup || animationProgress != 1.0f) { - Theme.dialogs_onlineCirclePaint.setColor(getThemedColor(Theme.key_chat_messagePanelSend)); - int rad = AndroidUtilities.dp(20); + if (shouldDrawBackground) { + Theme.dialogs_onlineCirclePaint.setColor(getThemedColor(Theme.key_chat_messagePanelSend)); + } else { + Theme.dialogs_onlineCirclePaint.setColor(ColorUtils.setAlphaComponent(Color.WHITE, 75)); + } if (showingPopup) { sendButtonInverseDrawable.setAlpha(255); float p = animationProgress; @@ -2599,7 +2721,7 @@ protected void onDraw(Canvas canvas) { Theme.dialogs_onlineCirclePaint.setAlpha(alpha); sendButtonInverseDrawable.setAlpha(alpha); } - canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, rad, Theme.dialogs_onlineCirclePaint); + canvas.drawCircle(center, center, rad, Theme.dialogs_onlineCirclePaint); sendButtonInverseDrawable.setBounds(x, y, x + sendButtonDrawable.getIntrinsicWidth(), y + sendButtonDrawable.getIntrinsicHeight()); sendButtonInverseDrawable.draw(canvas); } @@ -2621,7 +2743,7 @@ public boolean onTouchEvent(MotionEvent event) { sendButton.setScaleY(0.1f); sendButton.setAlpha(0.0f); if (Build.VERSION.SDK_INT >= 21) { - sendButton.setBackgroundDrawable(Theme.createSelectorDrawable(Color.argb(24, Color.red(color), Color.green(color), Color.blue(color)), 1)); + sendButton.setBackgroundDrawable(Theme.createSelectorDrawable(ColorUtils.setAlphaComponent(color, 24), 1)); } sendButtonContainer.addView(sendButton, LayoutHelper.createFrame(48, 48)); sendButton.setOnClickListener(view -> { @@ -2802,6 +2924,22 @@ private void createDoneButton() { doneButtonContainer.addView(doneButtonProgress, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); } + public void createReactionsButton(View.OnClickListener onClickListener) { + if (reactionsButton != null) { + return; + } + reactionsButton = new ImageView(getContext()); + reactionsButton.setImageDrawable(ContextCompat.getDrawable(getContext(), R.drawable.msg_input_like)); + reactionsButton.setScaleType(ImageView.ScaleType.CENTER); + if (Build.VERSION.SDK_INT >= 21) { + reactionsButton.setBackgroundDrawable(Theme.createSelectorDrawable(getThemedColor(Theme.key_listSelector))); + } + //reactionsButton.setVisibility(GONE); + AndroidUtilities.updateViewVisibilityAnimated(reactionsButton, true, 0.1f, false); + attachLayout.addView(reactionsButton, 0, LayoutHelper.createLinear(48, 48)); + reactionsButton.setOnClickListener(onClickListener); + } + private void createExpandStickersButton() { if (expandStickersButton != null) { return; @@ -2884,26 +3022,11 @@ public void setVisibility(int visibility) { if (runningAnimationAudio != null && runningAnimationAudio.isRunning()) { return; } - if (videoToSendMessageObject != null) { - CameraController.getInstance().cancelOnInitRunnable(onFinishInitCameraRunnable); - delegate.needStartRecordVideo(2, true, 0); - } else { - MessageObject playing = MediaController.getInstance().getPlayingMessageObject(); - if (playing != null && playing == audioToSendMessageObject) { - MediaController.getInstance().cleanupPlayer(true, true); - } - } - if (audioToSendPath != null) { - if (BuildVars.LOGS_ENABLED) { - FileLog.d("delete file " + audioToSendPath); - } - new File(audioToSendPath).delete(); - } - hideRecordedAudioPanel(false); - checkSendButton(true); + resetRecordedState(); }); videoTimelineView = new VideoTimelineView(getContext()); + videoTimelineView.useClip = !shouldDrawBackground; videoTimelineView.setRoundFrames(true); videoTimelineView.setDelegate(new VideoTimelineView.VideoTimelineViewDelegate() { @Override @@ -2940,7 +3063,13 @@ public void didStopDragging() { videoTimelineView.setTimeHintView(videoTimeHintView); sizeNotifierLayout.addView(videoTimeHintView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM, 0, 0, 0, 52)); - recordedAudioBackground = new View(getContext()); + recordedAudioBackground = new View(getContext()) { + @Override + protected void dispatchDraw(Canvas canvas) { + getBackground().setBounds((int) horizontalPadding, 0, (int) (getMeasuredWidth() - horizontalPadding), getMeasuredHeight()); + getBackground().draw(canvas); + } + }; recordedAudioBackground.setBackgroundDrawable(Theme.createRoundRectDrawable(AndroidUtilities.dp(18), getThemedColor(Theme.key_chat_recordedVoiceBackground))); recordedAudioPanel.addView(recordedAudioBackground, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 36, Gravity.CENTER_VERTICAL | Gravity.LEFT, 48, 0, 0, 0)); @@ -2980,6 +3109,26 @@ public void didStopDragging() { waveFormTimerLayout.addView(recordedAudioTimeTextView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, 0f, Gravity.CENTER_VERTICAL)); } + private void resetRecordedState() { + if (videoToSendMessageObject != null) { + CameraController.getInstance().cancelOnInitRunnable(onFinishInitCameraRunnable); + delegate.needStartRecordVideo(2, true, 0); + } else { + MessageObject playing = MediaController.getInstance().getPlayingMessageObject(); + if (playing != null && playing == audioToSendMessageObject) { + MediaController.getInstance().cleanupPlayer(true, true); + } + } + if (audioToSendPath != null) { + if (BuildVars.LOGS_ENABLED) { + FileLog.d("delete file " + audioToSendPath); + } + new File(audioToSendPath).delete(); + } + hideRecordedAudioPanel(false); + checkSendButton(true); + } + private void createSenderSelectView() { if (senderSelectView != null) { return; @@ -3343,7 +3492,7 @@ private void createRecordCircle() { } recordCircle = new RecordCircle(getContext()); recordCircle.setVisibility(GONE); - sizeNotifierLayout.addView(recordCircle, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM, 0, 0, 0, 0)); + sizeNotifierLayout.addView(recordCircle, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM)); } private void showRestrictedHint() { @@ -3374,27 +3523,22 @@ private void openWebViewMenu() { if (SharedPrefsHelper.isWebViewConfirmShown(currentAccount, dialog_id)) { onRequestWebView.run(); } else { - new AlertDialog.Builder(parentFragment.getParentActivity()) - .setTitle(LocaleController.getString(R.string.BotOpenPageTitle)) - .setMessage(AndroidUtilities.replaceTags(LocaleController.formatString(R.string.BotOpenPageMessage, UserObject.getUserName(MessagesController.getInstance(currentAccount).getUser(dialog_id))))) - .setPositiveButton(LocaleController.getString(R.string.OK), (dialog, which) -> { - onRequestWebView.run(); - SharedPrefsHelper.setWebViewConfirmShown(currentAccount, dialog_id, true); - }) - .setNegativeButton(LocaleController.getString(R.string.Cancel), null) - .setOnDismissListener(dialog -> { - if (botCommandsMenuButton != null && !SharedPrefsHelper.isWebViewConfirmShown(currentAccount, dialog_id)) { - botCommandsMenuButton.setOpened(false); - } - }) - .show(); + AlertsCreator.createBotLaunchAlert(parentFragment, MessagesController.getInstance(currentAccount).getUser(dialog_id), () -> { + onRequestWebView.run(); + SharedPrefsHelper.setWebViewConfirmShown(currentAccount, dialog_id, true); + }, () -> { + if (botCommandsMenuButton != null && !SharedPrefsHelper.isWebViewConfirmShown(currentAccount, dialog_id)) { + botCommandsMenuButton.setOpened(false); + } + }); } } public void setBotWebViewButtonOffsetX(float offset) { emojiButton.setTranslationX(offset); if (messageEditText != null) { - messageEditText.setTranslationX(offset); + messageTextTranslationX = offset; + updateMessageTextParams(); } attachButton.setTranslationX(offset); audioVideoSendButton.setTranslationX(offset); @@ -3501,32 +3645,37 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { } public boolean allowBlur = true; + public boolean shouldDrawBackground = true; + public boolean shouldDrawRecordedAudioPanelInParent; Paint backgroundPaint = new Paint(); private float composeShadowAlpha = 1f; @Override protected void onDraw(Canvas canvas) { - int top = animatedTop; - top += Theme.chat_composeShadowDrawable.getIntrinsicHeight() * (1f - composeShadowAlpha); - if (topView != null && topView.getVisibility() == View.VISIBLE) { - top += (1f - topViewEnterProgress) * topView.getLayoutParams().height; - } - int bottom = top + Theme.chat_composeShadowDrawable.getIntrinsicHeight(); - - Theme.chat_composeShadowDrawable.setAlpha((int) (composeShadowAlpha * 0xFF)); - Theme.chat_composeShadowDrawable.setBounds(0, top, getMeasuredWidth(), bottom); - Theme.chat_composeShadowDrawable.draw(canvas); - bottom += chatSearchExpandOffset; - if (allowBlur) { - backgroundPaint.setColor(getThemedColor(Theme.key_chat_messagePanelBackground)); - if (SharedConfig.chatBlurEnabled() && sizeNotifierLayout != null) { - AndroidUtilities.rectTmp2.set(0, bottom, getWidth(), getHeight()); - sizeNotifierLayout.drawBlurRect(canvas, getTop(), AndroidUtilities.rectTmp2, backgroundPaint, false); + if (shouldDrawBackground) { + int top = animatedTop; + top += Theme.chat_composeShadowDrawable.getIntrinsicHeight() * (1f - composeShadowAlpha); + if (topView != null && topView.getVisibility() == View.VISIBLE) { + top += (1f - topViewEnterProgress) * topView.getLayoutParams().height; + } + int bottom = top + Theme.chat_composeShadowDrawable.getIntrinsicHeight(); + + Theme.chat_composeShadowDrawable.setAlpha((int) (composeShadowAlpha * 0xFF)); + Theme.chat_composeShadowDrawable.setBounds(0, top, getMeasuredWidth(), bottom); + Theme.chat_composeShadowDrawable.draw(canvas); + bottom += chatSearchExpandOffset; + + if (allowBlur) { + backgroundPaint.setColor(getThemedColor(Theme.key_chat_messagePanelBackground)); + if (SharedConfig.chatBlurEnabled() && sizeNotifierLayout != null) { + AndroidUtilities.rectTmp2.set(0, bottom, getWidth(), getHeight()); + sizeNotifierLayout.drawBlurRect(canvas, getTop(), AndroidUtilities.rectTmp2, backgroundPaint, false); + } else { + canvas.drawRect(0, bottom, getWidth(), getHeight(), backgroundPaint); + } } else { - canvas.drawRect(0, bottom, getWidth(), getHeight(), backgroundPaint); + canvas.drawRect(0, bottom, getWidth(), getHeight(), getThemedPaint(Theme.key_paint_chatComposeBackground)); } - } else { - canvas.drawRect(0, bottom, getWidth(), getHeight(), getThemedPaint(Theme.key_paint_chatComposeBackground)); } } @@ -3887,6 +4036,18 @@ public boolean onTouch(View v, MotionEvent event) { AlertsCreator.createScheduleDatePickerDialog(parentActivity, parentFragment.getDialogId(), this::sendMessageInternal, resourcesProvider); }); sendPopupLayout.addView(scheduleButton, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48)); + if (!self && dialog_id > 0) { + sendWhenOnlineButton = new ActionBarMenuSubItem(getContext(), true, !sendWithoutSoundButtonValue, resourcesProvider); + sendWhenOnlineButton.setTextAndIcon(LocaleController.getString("SendWhenOnline", R.string.SendWhenOnline), R.drawable.msg_online); + sendWhenOnlineButton.setMinimumWidth(AndroidUtilities.dp(196)); + sendWhenOnlineButton.setOnClickListener(v -> { + if (sendPopupWindow != null && sendPopupWindow.isShowing()) { + sendPopupWindow.dismiss(); + } + sendMessageInternal(true, 0x7FFFFFFE); + }); + sendPopupLayout.addView(sendWhenOnlineButton, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48)); + } } if (sendWithoutSoundButtonValue) { ActionBarMenuSubItem sendWithoutSoundButton = new ActionBarMenuSubItem(getContext(), !scheduleButtonValue, true, resourcesProvider); @@ -3949,6 +4110,14 @@ public void dismiss() { } } + if (sendWhenOnlineButton != null) { + TLRPC.User user = parentFragment.getCurrentUser(); + if (user != null && !user.bot && !(user.status instanceof TLRPC.TL_userStatusEmpty) && !(user.status instanceof TLRPC.TL_userStatusOnline) && !(user.status instanceof TLRPC.TL_userStatusRecently)) { + sendWhenOnlineButton.setVisibility(VISIBLE); + } else { + sendWhenOnlineButton.setVisibility(GONE); + } + } sendPopupLayout.measure(MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(1000), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(1000), MeasureSpec.AT_MOST)); sendPopupWindow.setFocusable(true); view.getLocationInWindow(location); @@ -3996,7 +4165,7 @@ public void onItemClick(View view, int position) { } if (isInScheduleMode()) { AlertsCreator.createScheduleDatePickerDialog(parentActivity, dialog_id, (notify, scheduleDate) -> { - SendMessagesHelper.getInstance(currentAccount).sendMessage(command, dialog_id, replyingMessageObject, getThreadMessage(), null, false, null, null, null, notify, scheduleDate, null, false); + SendMessagesHelper.getInstance(currentAccount).sendMessage(SendMessagesHelper.SendMessageParams.of(command, dialog_id, replyingMessageObject, getThreadMessage(), null, false, null, null, null, notify, scheduleDate, null, false)); setFieldText(""); botCommandsMenuContainer.dismiss(); }, resourcesProvider); @@ -4004,7 +4173,7 @@ public void onItemClick(View view, int position) { if (parentFragment != null && parentFragment.checkSlowMode(view)) { return; } - SendMessagesHelper.getInstance(currentAccount).sendMessage(command, dialog_id, replyingMessageObject, getThreadMessage(), null, false, null, null, null, true, 0, null, false); + SendMessagesHelper.getInstance(currentAccount).sendMessage(SendMessagesHelper.SendMessageParams.of(command, dialog_id, replyingMessageObject, getThreadMessage(), null, false, null, null, null, true, 0, null, false)); setFieldText(""); botCommandsMenuContainer.dismiss(); } @@ -4121,7 +4290,7 @@ private void send(InputContentInfoCompat inputContentInfo, boolean notify, int s } ClipDescription description = inputContentInfo.getDescription(); if (description.hasMimeType("image/gif")) { - SendMessagesHelper.prepareSendingDocument(accountInstance, null, null, inputContentInfo.getContentUri(), null, "image/gif", dialog_id, replyingMessageObject, getThreadMessage(), inputContentInfo, null, notify, 0); + SendMessagesHelper.prepareSendingDocument(accountInstance, null, null, inputContentInfo.getContentUri(), null, "image/gif", dialog_id, replyingMessageObject, getThreadMessage(), null, null, notify, 0, inputContentInfo); } else { SendMessagesHelper.prepareSendingPhoto(accountInstance, null, inputContentInfo.getContentUri(), dialog_id, replyingMessageObject, getThreadMessage(), null, null, null, inputContentInfo, 0, null, notify, 0); } @@ -4226,6 +4395,8 @@ protected void onSelectionChanged(int selStart, int selEnd) { protected void extendActionMode(ActionMode actionMode, Menu menu) { if (parentFragment != null) { parentFragment.extendActionMode(menu); + } else { + ChatActivityEnterView.this.extendActionMode(menu); } } @@ -4328,7 +4499,7 @@ public void sendButtonPressed(int index, VideoEditedInfo videoEditedInfo, boolea photoEntry.reset(); sending = true; boolean updateStickersOrder = SendMessagesHelper.checkUpdateStickersOrder(info.caption); - SendMessagesHelper.prepareSendingMedia(accountInstance, photos, dialog_id, replyingMessageObject, getThreadMessage(), null, false, false, editingMessageObject, notify, scheduleDate, updateStickersOrder); + SendMessagesHelper.prepareSendingMedia(accountInstance, photos, dialog_id, replyingMessageObject, getThreadMessage(), null, false, false, editingMessageObject, notify, scheduleDate, updateStickersOrder, null); if (delegate != null) { delegate.onMessageSend(null, true, scheduleDate); } @@ -4374,6 +4545,19 @@ public void setOffsetY(float offset) { } } + public void extendActionMode(Menu menu) { + + } + + /** + * Samsung keyboard does not support incognito mode. + * Also on samsung keyboard when the EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING flag is set, the keyboard starts to lag. + */ + private boolean isKeyboardSupportIncognitoMode() { + String keyboardName = Settings.Secure.getString(getContext().getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD); + return keyboardName == null || !keyboardName.startsWith("com.samsung"); + } + private void createMessageEditText() { if (messageEditText != null) { return; @@ -4413,8 +4597,8 @@ public long getCurrentChat() { TLRPC.EncryptedChat encryptedChat = parentFragment != null ? parentFragment.getCurrentEncryptedChat() : null; messageEditText.setAllowTextEntitiesIntersection(supportsSendingNewEntities()); int flags = EditorInfo.IME_FLAG_NO_EXTRACT_UI; - if (encryptedChat != null) { - flags |= 0x01000000; // EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING; + if (isKeyboardSupportIncognitoMode() && encryptedChat != null) { + flags |= EditorInfoCompat.IME_FLAG_NO_PERSONALIZED_LEARNING; } messageEditText.setIncludeFontPadding(false); messageEditText.setImeOptions(flags); @@ -4624,7 +4808,7 @@ public void afterTextChanged(Editable editable) { captionLimitView.animate().alpha(1f).scaleX(1f).scaleY(1f).setDuration(100).start(); if (beforeLimit < 0) { doneButtonEnabledLocal = false; - captionLimitView.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteRedText)); + captionLimitView.setTextColor(getThemedColor(Theme.key_text_RedRegular)); } else { captionLimitView.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteGrayText)); } @@ -5135,7 +5319,7 @@ public void onAnimationEnd(Animator animation) { if (currentTopViewAnimation != null && currentTopViewAnimation.equals(animation)) { currentTopViewAnimation = null; } - NotificationCenter.getInstance(currentAccount).onAnimationFinish(notificationsIndex); + notificationsLocker.unlock(); if (parentFragment != null && parentFragment.mentionContainer != null) { parentFragment.mentionContainer.setTranslationY(0); } @@ -5144,7 +5328,7 @@ public void onAnimationEnd(Animator animation) { currentTopViewAnimation.setDuration(ChatListItemAnimator.DEFAULT_DURATION + 20); currentTopViewAnimation.setInterpolator(ChatListItemAnimator.DEFAULT_INTERPOLATOR); currentTopViewAnimation.start(); - notificationsIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(notificationsIndex, null); + notificationsLocker.lock(); } else { topViewEnterProgress = 1f; topView.setTranslationY(0); @@ -5433,13 +5617,16 @@ public void checkChannelRights() { if (parentFragment == null) { return; } - TLRPC.Chat chat = parentFragment.getCurrentChat(); - TLRPC.UserFull userFull = parentFragment.getCurrentUserInfo(); + updateRecordButton(parentFragment.getCurrentChat(), parentFragment.getCurrentUserInfo()); + } + + public void updateRecordButton(TLRPC.Chat chat, TLRPC.UserFull userFull) { emojiButtonRestricted = false; stickersEnabled = true; sendPlainEnabled = true; sendRoundEnabled = true; sendVoiceEnabled = true; + if (chat != null) { audioVideoButtonContainer.setAlpha(ChatObject.canSendVoice(chat) || (ChatObject.canSendRoundVideo(chat) && hasRecordVideo)? 1.0f : 0.5f); @@ -5447,7 +5634,8 @@ public void checkChannelRights() { sendPlainEnabled = ChatObject.canSendPlain(chat); sendPlainEnabled = ChatObject.canSendPlain(chat); emojiButtonRestricted = !stickersEnabled && !sendPlainEnabled; - emojiButton.setAlpha(emojiButtonRestricted ? 0.5f : 1.0f); + emojiButtonAlpha = emojiButtonRestricted ? 0.5f : 1.0f; + updateEmojiButtonParams(); if (!emojiButtonRestricted) { if (emojiView != null) { emojiView.setStickersBanned(!ChatObject.canSendPlain(chat), !ChatObject.canSendStickers(chat), chat.id); @@ -5456,6 +5644,7 @@ public void checkChannelRights() { sendRoundEnabled = ChatObject.canSendRoundVideo(chat); sendVoiceEnabled = ChatObject.canSendVoice(chat); } else if (userFull != null) { + userInfo = userFull; audioVideoButtonContainer.setAlpha(userFull.voice_messages_forbidden ? 0.5f : 1.0f); } updateFieldHint(false); @@ -5546,7 +5735,7 @@ public void setVisibility(int visibility) { public void setDialogId(long id, int account) { dialog_id = id; if (currentAccount != account) { - NotificationCenter.getInstance(currentAccount).onAnimationFinish(notificationsIndex); + notificationsLocker.unlock(); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.recordStarted); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.recordStartError); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.recordStopped); @@ -5664,6 +5853,10 @@ public void updateFieldHint(boolean animated) { if (messageEditText == null) { return; } + if (overrideHint != null) { + messageEditText.setHintText(overrideHint, animated); + return; + } if (!sendPlainEnabled && !isEditingMessage()) { SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(" d " + LocaleController.getString("PlainTextRestrictedHint", R.string.PlainTextRestrictedHint)); spannableStringBuilder.setSpan(new ColoredImageSpan(R.drawable.msg_mini_lock3), 1, 2, 0); @@ -5674,7 +5867,11 @@ public void updateFieldHint(boolean animated) { return; } else { messageEditText.setEnabled(true); - messageEditText.setInputType(commonInputType); + if (messageEditText.getInputType() != commonInputType) { + messageEditText.setInputType(commonInputType); + } else { + + } } if (replyingMessageObject != null && replyingMessageObject.messageOwner.reply_markup != null && !TextUtils.isEmpty(replyingMessageObject.messageOwner.reply_markup.placeholder)) { messageEditText.setHintText(replyingMessageObject.messageOwner.reply_markup.placeholder, animated); @@ -5765,7 +5962,8 @@ public void setReplyingMessageObject(MessageObject messageObject) { } else { replyingMessageObject = null; } - MediaController.getInstance().setReplyingMessage(messageObject, getThreadMessage()); + TLRPC.StoryItem storyItem = delegate != null ? delegate.getReplyToStory() : null; + MediaController.getInstance().setReplyingMessage(messageObject, getThreadMessage(), storyItem); updateFieldHint(false); } @@ -5868,19 +6066,17 @@ private void hideRecordedAudioPanel(boolean wasSent) { if (wasSent) { attachButton.setAlpha(0f); - emojiButton.setAlpha(0f); - attachButton.setScaleX(0); - emojiButton.setScaleX(0); - attachButton.setScaleY(0); - emojiButton.setScaleY(0); + + emojiButtonAlpha = 0; + emojiButtonScale = 0; + updateEmojiButtonParams(); recordPannelAnimation = new AnimatorSet(); recordPannelAnimation.playTogether( - ObjectAnimator.ofFloat(emojiButton, View.ALPHA, emojiButtonRestricted ? 0.5f : 1.0f), - ObjectAnimator.ofFloat(emojiButton, View.SCALE_X, 1.0f), - ObjectAnimator.ofFloat(emojiButton, View.SCALE_Y, 1.0f), + ObjectAnimator.ofFloat(emojiButton, EMOJI_BUTTON_ALPHA, emojiButtonRestricted ? 0.5f : 1.0f), + ObjectAnimator.ofFloat(emojiButton, EMOJI_BUTTON_SCALE, 1.0f), ObjectAnimator.ofFloat(recordDeleteImageView, View.ALPHA, 0.0f), ObjectAnimator.ofFloat(recordDeleteImageView, View.SCALE_X, 0.0f), ObjectAnimator.ofFloat(recordDeleteImageView, View.SCALE_Y, 0.0f), @@ -5890,7 +6086,7 @@ private void hideRecordedAudioPanel(boolean wasSent) { ObjectAnimator.ofFloat(attachButton, View.SCALE_X, 1.0f), ObjectAnimator.ofFloat(attachButton, View.SCALE_Y, 1.0f), ObjectAnimator.ofFloat(messageEditText, View.ALPHA, 1f), - ObjectAnimator.ofFloat(messageEditText, View.TRANSLATION_X, 0) + ObjectAnimator.ofFloat(messageEditText, MESSAGE_TEXT_TRANSLATION_X, 0) ); if (botCommandsMenuButton != null) { @@ -5914,6 +6110,7 @@ public void onAnimationEnd(Animator animation) { if (messageEditText != null) { messageEditText.requestFocus(); } + isRecordingStateChanged(); } }); @@ -5927,13 +6124,28 @@ public void onAnimationEnd(Animator animation) { exitAnimation.playTogether( ObjectAnimator.ofFloat(videoTimelineView, View.ALPHA, 0.0f), ObjectAnimator.ofFloat(videoTimelineView, View.TRANSLATION_X, -AndroidUtilities.dp(20)), - ObjectAnimator.ofFloat(messageEditText, View.ALPHA, 1f), - ObjectAnimator.ofFloat(messageEditText, View.TRANSLATION_X, 0) + ObjectAnimator.ofFloat(messageEditText, MESSAGE_TEXT_TRANSLATION_X, 0) ); + if (emojiButtonPaddingAlpha == 1f) { + exitAnimation.playTogether(ObjectAnimator.ofFloat(messageEditText, View.ALPHA, 1f)); + } else { + ObjectAnimator messageEditTextAniamtor = ObjectAnimator.ofFloat(messageEditText, View.ALPHA, 1); + messageEditTextAniamtor.setStartDelay(750); + messageEditTextAniamtor.setDuration(200); + exitAnimation.playTogether(messageEditTextAniamtor); + } } else { - if (messageEditText != null) { + if (messageEditText != null && emojiButtonPaddingAlpha == 1f) { messageEditText.setAlpha(1f); - messageEditText.setTranslationX(0); + messageTextTranslationX = 0; + updateMessageTextParams(); + } else { + messageTextTranslationX = 0; + updateMessageTextParams(); + ObjectAnimator messageEditTextAniamtor = ObjectAnimator.ofFloat(messageEditText, View.ALPHA, 1); + messageEditTextAniamtor.setStartDelay(750); + messageEditTextAniamtor.setDuration(200); + exitAnimation.playTogether(messageEditTextAniamtor); } exitAnimation.playTogether( ObjectAnimator.ofFloat(recordedAudioSeekBar, View.ALPHA, 0.0f), @@ -5980,9 +6192,8 @@ public void onAnimationEnd(Animator animation) { } } - emojiButton.setAlpha(0f); - emojiButton.setScaleX(0); - emojiButton.setScaleY(0); + emojiButtonAlpha = emojiButtonScale = 0; + updateEmojiButtonParams(); AnimatorSet iconsEndAnimator = new AnimatorSet(); @@ -5992,9 +6203,8 @@ public void onAnimationEnd(Animator animation) { ObjectAnimator.ofFloat(recordDeleteImageView, View.SCALE_Y, 0.0f), ObjectAnimator.ofFloat(recordDeleteImageView, View.ALPHA, 0.0f), - ObjectAnimator.ofFloat(emojiButton, View.ALPHA, emojiButtonRestricted ? 0.5f : 1.0f), - ObjectAnimator.ofFloat(emojiButton, View.SCALE_X, 1.0f), - ObjectAnimator.ofFloat(emojiButton, View.SCALE_Y, 1.0f) + ObjectAnimator.ofFloat(emojiButton, EMOJI_BUTTON_ALPHA, emojiButtonRestricted ? 0.5f : 1.0f), + ObjectAnimator.ofFloat(emojiButton, EMOJI_BUTTON_SCALE, 1.0f) ); if (botCommandsMenuButton != null) { @@ -6038,38 +6248,53 @@ public void onAnimationEnd(Animator animation) { recordPannelAnimation.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - if (recordedAudioSeekBar != null) { - recordedAudioSeekBar.setAlpha(1f); - recordedAudioSeekBar.setTranslationX(0); - } - if (recordedAudioPlayButton != null) { - recordedAudioPlayButton.setAlpha(1f); - recordedAudioPlayButton.setTranslationX(0); - } - if (recordedAudioBackground != null) { - recordedAudioBackground.setAlpha(1f); - recordedAudioBackground.setTranslationX(0); - } - if (recordedAudioTimeTextView != null) { - recordedAudioTimeTextView.setAlpha(1f); - recordedAudioTimeTextView.setTranslationX(0); - } - if (videoTimelineView != null) { - videoTimelineView.setAlpha(1f); - videoTimelineView.setTranslationX(0); - } - if (messageEditText != null) { - messageEditText.setAlpha(1f); - messageEditText.setTranslationX(0); - messageEditText.requestFocus(); - } - if (recordedAudioPanel != null) { - recordedAudioPanel.setVisibility(GONE); - } + hideRecordedAudioPanelInternal(); } }); } - recordPannelAnimation.start(); + if (recordPannelAnimation != null) { + recordPannelAnimation.start(); + } + } + + private void hideRecordedAudioPanelInternal() { + audioToSendPath = null; + audioToSend = null; + audioToSendMessageObject = null; + videoToSendMessageObject = null; + if (videoTimelineView != null) { + videoTimelineView.destroy(); + } + if (recordedAudioSeekBar != null) { + recordedAudioSeekBar.setAlpha(1f); + recordedAudioSeekBar.setTranslationX(0); + } + if (recordedAudioPlayButton != null) { + recordedAudioPlayButton.setAlpha(1f); + recordedAudioPlayButton.setTranslationX(0); + } + if (recordedAudioBackground != null) { + recordedAudioBackground.setAlpha(1f); + recordedAudioBackground.setTranslationX(0); + } + if (recordedAudioTimeTextView != null) { + recordedAudioTimeTextView.setAlpha(1f); + recordedAudioTimeTextView.setTranslationX(0); + } + if (videoTimelineView != null) { + videoTimelineView.setAlpha(1f); + videoTimelineView.setTranslationX(0); + } + if (messageEditText != null) { + messageEditText.setAlpha(1f); + messageTextTranslationX = 0; + updateMessageTextParams(); + messageEditText.requestFocus(); + } + if (recordedAudioPanel != null) { + recordedAudioPanel.setVisibility(GONE); + } + isRecordingStateChanged(); } private void sendMessage() { @@ -6120,8 +6345,10 @@ private void sendMessageInternal(boolean notify, int scheduleDate, boolean withM if (playing != null && playing == audioToSendMessageObject) { MediaController.getInstance().cleanupPlayer(true, true); } - SendMessagesHelper.getInstance(currentAccount).sendMessage(audioToSend, null, audioToSendPath, dialog_id, replyingMessageObject, getThreadMessage(), voiceCaption, null, null, null, notify, scheduleDate, 0, null, null, false); + SendMessagesHelper.SendMessageParams params = SendMessagesHelper.SendMessageParams.of(audioToSend, null, audioToSendPath, dialog_id, replyingMessageObject, getThreadMessage(), voiceCaption, null, null, null, notify, scheduleDate, 0, null, null, false); voiceCaption = null; + applyStoryToSendMessageParams(params); + SendMessagesHelper.getInstance(currentAccount).sendMessage(params); if (delegate != null) { delegate.onMessageSend(null, notify, scheduleDate); } @@ -6412,7 +6639,10 @@ public boolean processSendingText(CharSequence text, boolean notify, int schedul updateStickersOrder = SendMessagesHelper.checkUpdateStickersOrder(text); - SendMessagesHelper.getInstance(currentAccount).sendMessage(message[0].toString(), dialog_id, replyingMessageObject, getThreadMessage(), messageWebPage, messageWebPageSearch, entities, null, null, notify, scheduleDate, sendAnimationData, updateStickersOrder, withGame); + SendMessagesHelper.SendMessageParams params = SendMessagesHelper.SendMessageParams.of(message[0].toString(), dialog_id, replyingMessageObject, getThreadMessage(), messageWebPage, messageWebPageSearch, entities, null, null, notify, scheduleDate, sendAnimationData, updateStickersOrder); + params.canSendGames = withGame; + applyStoryToSendMessageParams(params); + SendMessagesHelper.getInstance(currentAccount).sendMessage(params); start = end + 1; } while (end != text.length()); return true; @@ -6420,6 +6650,12 @@ public boolean processSendingText(CharSequence text, boolean notify, int schedul return false; } + private void applyStoryToSendMessageParams(SendMessagesHelper.SendMessageParams params) { + if (delegate != null) { + params.replyToStoryItem = delegate.getReplyToStory(); + } + } + private boolean supportsSendingNewEntities() { TLRPC.EncryptedChat encryptedChat = parentFragment != null ? parentFragment.getCurrentEncryptedChat() : null; return encryptedChat == null || AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 101; @@ -6628,7 +6864,12 @@ public void onAnimationCancel(Animator animation) { } else { color = getThemedColor(Theme.key_chat_messagePanelSend); } - Theme.setSelectorDrawableColor(sendButton.getBackground(), Color.argb(24, Color.red(color), Color.green(color), Color.blue(color)), true); + + if (color != sendButtonBackgroundColor) { + sendButtonBackgroundColor = color; + Theme.setSelectorDrawableColor(sendButton.getBackground(), Color.argb(24, Color.red(color), Color.green(color), Color.blue(color)), true); + } + if (audioVideoButtonContainer.getVisibility() == VISIBLE || slowModeButton.getVisibility() == VISIBLE || showBotButton || showSendButton) { if (animated) { if (runningAnimationType == 1 && caption == null || runningAnimationType == 3 && caption != null) { @@ -7089,8 +7330,8 @@ public void onAnimationCancel(Animator animation) { animators.add(ObjectAnimator.ofFloat(audioVideoButtonContainer, View.SCALE_Y, 1.0f)); float alpha = 1.0f; - TLRPC.Chat chat = parentFragment.getCurrentChat(); - TLRPC.UserFull userFull = parentFragment.getCurrentUserInfo(); + TLRPC.Chat chat = parentFragment == null ? null : parentFragment.getCurrentChat(); + TLRPC.UserFull userFull = parentFragment == null ? userInfo : parentFragment.getCurrentUserInfo(); if (chat != null) { alpha = (ChatObject.canSendVoice(chat) || ChatObject.canSendRoundVideo(chat)) ? 1.0f : 0.5f; } else if (userFull != null) { @@ -7249,7 +7490,7 @@ public boolean canShowMessageTransition() { return moveToSendStateRunnable != null; } - private void updateRecordInterface(int recordState) { + protected void updateRecordInterface(int recordState) { if (moveToSendStateRunnable != null) { AndroidUtilities.cancelRunOnUIThread(moveToSendStateRunnable); moveToSendStateRunnable = null; @@ -7322,9 +7563,8 @@ private void updateRecordInterface(int recordState) { //ENTER TRANSITION AnimatorSet iconChanges = new AnimatorSet(); iconChanges.playTogether( - ObjectAnimator.ofFloat(emojiButton, View.SCALE_Y, 0), - ObjectAnimator.ofFloat(emojiButton, View.SCALE_X, 0), - ObjectAnimator.ofFloat(emojiButton, View.ALPHA, 0), + ObjectAnimator.ofFloat(emojiButton, EMOJI_BUTTON_SCALE, 0), + ObjectAnimator.ofFloat(emojiButton, EMOJI_BUTTON_ALPHA, 0), ObjectAnimator.ofFloat(recordDot, View.SCALE_Y, 1), ObjectAnimator.ofFloat(recordDot, View.SCALE_X, 1), ObjectAnimator.ofFloat(recordTimerView, View.TRANSLATION_X, 0), @@ -7346,7 +7586,7 @@ private void updateRecordInterface(int recordState) { AnimatorSet viewTransition = new AnimatorSet(); viewTransition.playTogether( - ObjectAnimator.ofFloat(messageEditText, View.TRANSLATION_X, AndroidUtilities.dp(20)), + ObjectAnimator.ofFloat(messageEditText, MESSAGE_TEXT_TRANSLATION_X, AndroidUtilities.dp(20)), ObjectAnimator.ofFloat(messageEditText, View.ALPHA, 0), ObjectAnimator.ofFloat(recordedAudioPanel, View.ALPHA, 1f) ); @@ -7358,7 +7598,7 @@ private void updateRecordInterface(int recordState) { } if (attachLayout != null) { viewTransition.playTogether( - ObjectAnimator.ofFloat(attachLayout, View.TRANSLATION_X, AndroidUtilities.dp(30)), + ObjectAnimator.ofFloat(attachLayout, ATTACH_LAYOUT_TRANSLATION_X, AndroidUtilities.dp(30)), ObjectAnimator.ofFloat(attachLayout, View.ALPHA, 0f) ); } @@ -7374,6 +7614,7 @@ public void onAnimationEnd(Animator animator) { if (animator.equals(runningAnimationAudio)) { runningAnimationAudio = null; } + isRecordingStateChanged(); slideText.setAlpha(1f); slideText.setTranslationX(0); @@ -7435,9 +7676,8 @@ public void onAnimationEnd(Animator animator) { audioVideoSendButton.setVisibility(View.VISIBLE); } runningAnimationAudio.playTogether( - ObjectAnimator.ofFloat(emojiButton, View.SCALE_Y, 1), - ObjectAnimator.ofFloat(emojiButton, View.SCALE_X, 1), - ObjectAnimator.ofFloat(emojiButton, View.ALPHA, emojiButtonRestricted ? 0.5f : 1.0f), + ObjectAnimator.ofFloat(emojiButton, EMOJI_BUTTON_SCALE, 1), + ObjectAnimator.ofFloat(emojiButton, EMOJI_BUTTON_ALPHA, emojiButtonRestricted ? 0.5f : 1.0f), ObjectAnimator.ofFloat(recordDot, View.SCALE_Y, 0), ObjectAnimator.ofFloat(recordDot, View.SCALE_X, 0), ObjectAnimator.ofFloat(recordCircle, recordCircleScale, 0.0f), @@ -7446,7 +7686,7 @@ public void onAnimationEnd(Animator animator) { ObjectAnimator.ofFloat(recordCircle, recordCircleScale, 0.0f), ObjectAnimator.ofFloat(audioVideoButtonContainer, View.ALPHA, 1.0f), ObjectAnimator.ofFloat(messageEditText, View.ALPHA, 1), - ObjectAnimator.ofFloat(messageEditText, View.TRANSLATION_X, 0), + ObjectAnimator.ofFloat(messageEditText, MESSAGE_TEXT_TRANSLATION_X, 0), ObjectAnimator.ofFloat(recordCircle, "slideToCancelProgress", 1f) ); if (botCommandsMenuButton != null) { @@ -7471,7 +7711,7 @@ public void onAnimationEnd(Animator animator) { } if (attachLayout != null) { runningAnimationAudio.playTogether( - ObjectAnimator.ofFloat(attachLayout, View.TRANSLATION_X, 0), + ObjectAnimator.ofFloat(attachLayout, ATTACH_LAYOUT_TRANSLATION_X, 0), ObjectAnimator.ofFloat(attachLayout, View.ALPHA, 1f) ); } @@ -7557,7 +7797,7 @@ public void onAnimationEnd(Animator animator) { ViewGroup.LayoutParams oldLayoutParams = null; ViewGroup parent = null; - if (!isInVideoMode()) { + if (!isInVideoMode() && !shouldDrawRecordedAudioPanelInParent) { parent = (ViewGroup) recordedAudioPanel.getParent(); oldLayoutParams = recordedAudioPanel.getLayoutParams(); parent.removeView(recordedAudioPanel); @@ -7587,9 +7827,8 @@ public void onAnimationEnd(Animator animator) { ObjectAnimator.ofFloat(recordDeleteImageView, View.ALPHA, 1), ObjectAnimator.ofFloat(recordDeleteImageView, View.SCALE_Y, 1f), ObjectAnimator.ofFloat(recordDeleteImageView, View.SCALE_X, 1f), - ObjectAnimator.ofFloat(emojiButton, View.SCALE_Y, 0), - ObjectAnimator.ofFloat(emojiButton, View.SCALE_X, 0), - ObjectAnimator.ofFloat(emojiButton, View.ALPHA, 0), + ObjectAnimator.ofFloat(emojiButton, EMOJI_BUTTON_SCALE, 0), + ObjectAnimator.ofFloat(emojiButton, EMOJI_BUTTON_ALPHA, 0), ObjectAnimator.ofFloat(messageEditText, View.ALPHA, 0) ); if (audioVideoSendButton != null) { @@ -7660,9 +7899,9 @@ public void onAnimationEnd(Animator animation) { recordedAudioPlayButton.setScaleX(1f); recordedAudioSeekBar.setAlpha(1f); - emojiButton.setScaleY(0f); - emojiButton.setScaleX(0f); - emojiButton.setAlpha(0f); + emojiButtonAlpha = emojiButtonScale = 0f; + updateEmojiButtonParams(); + if (botCommandsMenuButton != null) { botCommandsMenuButton.setAlpha(0f); botCommandsMenuButton.setScaleX(0f); @@ -7678,9 +7917,8 @@ public void onAnimationEnd(Animator animation) { recordIsCanceled = true; AnimatorSet iconsAnimator = new AnimatorSet(); iconsAnimator.playTogether( - ObjectAnimator.ofFloat(emojiButton, View.SCALE_Y, 1), - ObjectAnimator.ofFloat(emojiButton, View.SCALE_X, 1), - ObjectAnimator.ofFloat(emojiButton, View.ALPHA, emojiButtonRestricted ? 0.5f : 1.0f), + ObjectAnimator.ofFloat(emojiButton, EMOJI_BUTTON_SCALE, 1), + ObjectAnimator.ofFloat(emojiButton, EMOJI_BUTTON_ALPHA, emojiButtonRestricted ? 0.5f : 1.0f), ObjectAnimator.ofFloat(recordDot, View.SCALE_Y, 0), ObjectAnimator.ofFloat(recordDot, View.SCALE_X, 0) ); @@ -7726,7 +7964,11 @@ public void onAnimationEnd(Animator animation) { if (attachLayout != null) { iconsAnimator.playTogether( ObjectAnimator.ofFloat(attachLayout, View.ALPHA, 1f), - ObjectAnimator.ofFloat(attachLayout, View.TRANSLATION_X, 0), + ObjectAnimator.ofFloat(attachLayout, ATTACH_LAYOUT_TRANSLATION_X, 0) + ); + } + if (attachButton != null) { + iconsAnimator.playTogether( ObjectAnimator.ofFloat(attachButton, View.SCALE_X, 1f), ObjectAnimator.ofFloat(attachButton, View.SCALE_Y, 1f) ); @@ -7759,7 +8001,7 @@ public void onAnimationEnd(Animator animation) { ); if (attachLayout != null) { icons2.playTogether( - ObjectAnimator.ofFloat(attachLayout, View.TRANSLATION_X, 0), + ObjectAnimator.ofFloat(attachLayout, ATTACH_LAYOUT_TRANSLATION_X, 0), ObjectAnimator.ofFloat(attachLayout, View.ALPHA, 1f) ); } @@ -7790,11 +8032,11 @@ public void onAnimationEnd(Animator animation) { recordTimer.setDuration(200); recordTimer.setStartDelay(200); - if (messageEditText != null) { - messageEditText.setTranslationX(0f); - } + messageTextTranslationX = 0; + updateMessageTextParams(); + ObjectAnimator messageEditTextAniamtor = ObjectAnimator.ofFloat(messageEditText, View.ALPHA, 1); - messageEditTextAniamtor.setStartDelay(300); + messageEditTextAniamtor.setStartDelay(emojiButtonPaddingAlpha == 1f ? 300 : 700); messageEditTextAniamtor.setDuration(200); runningAnimationAudio.playTogether( @@ -7828,9 +8070,8 @@ public void onAnimationEnd(Animator animation) { AnimatorSet iconsAnimator = new AnimatorSet(); iconsAnimator.playTogether( - ObjectAnimator.ofFloat(emojiButton, View.SCALE_Y, 1), - ObjectAnimator.ofFloat(emojiButton, View.SCALE_X, 1), - ObjectAnimator.ofFloat(emojiButton, View.ALPHA, emojiButtonRestricted ? 0.5f : 1.0f), + ObjectAnimator.ofFloat(emojiButton, EMOJI_BUTTON_SCALE, 1), + ObjectAnimator.ofFloat(emojiButton, EMOJI_BUTTON_ALPHA, emojiButtonRestricted ? 0.5f : 1.0f), ObjectAnimator.ofFloat(recordDot, View.SCALE_Y, 0), ObjectAnimator.ofFloat(recordDot, View.SCALE_X, 0), ObjectAnimator.ofFloat(audioVideoButtonContainer, View.ALPHA, 1.0f) @@ -7849,7 +8090,8 @@ public void onAnimationEnd(Animator animation) { audioVideoSendButton.setState(isInVideoMode() ? ChatActivityEnterViewAnimatedIconView.State.VIDEO : ChatActivityEnterViewAnimatedIconView.State.VOICE, true); } if (attachLayout != null) { - attachLayout.setTranslationX(0); + attachLayoutTranslationX = 0; + updateAttachLayoutParams(); iconsAnimator.playTogether( ObjectAnimator.ofFloat(attachLayout, View.ALPHA, 1f) ); @@ -7877,11 +8119,11 @@ public void onAnimationEnd(Animator animation) { Animator recordCircleAnimator = ObjectAnimator.ofFloat(recordCircle, "exitTransition", 1.0f); recordCircleAnimator.setDuration(messageTransitionIsRunning ? 220 : 360); - if (messageEditText != null) { - messageEditText.setTranslationX(0f); - } + messageTextTranslationX = 0; + updateMessageTextParams(); + ObjectAnimator messageEditTextAniamtor = ObjectAnimator.ofFloat(messageEditText, View.ALPHA, 1); - messageEditTextAniamtor.setStartDelay(150); + messageEditTextAniamtor.setStartDelay(emojiButtonPaddingAlpha == 1f ? 150 : 450); messageEditTextAniamtor.setDuration(200); runningAnimationAudio.playTogether( @@ -7895,29 +8137,10 @@ public void onAnimationEnd(Animator animation) { @Override public void onAnimationEnd(Animator animator) { if (animator.equals(runningAnimationAudio)) { - if (recordPanel != null) { - recordPanel.setVisibility(GONE); - } - if (recordCircle != null) { - recordCircle.setVisibility(GONE); - recordCircle.setSendButtonInvisible(); - } - runningAnimationAudio = null; if (recordState != RECORD_STATE_PREPARING && messageEditText != null) { messageEditText.requestFocus(); } - if (recordedAudioBackground != null) { - recordedAudioBackground.setAlpha(1f); - } - if (attachLayout != null) { - attachLayout.setTranslationX(0); - } - if (slideText != null) { - slideText.setCancelToProgress(0f); - } - - delegate.onAudioVideoInterfaceUpdated(); - updateSendAsButton(); + cancelRecordIntefraceInternal(); } } }); @@ -7930,6 +8153,35 @@ public void onAnimationEnd(Animator animator) { updateSendAsButton(); } + private void cancelRecordIntefraceInternal() { + if (recordPanel != null) { + recordPanel.setVisibility(GONE); + } + if (recordCircle != null) { + recordCircle.setVisibility(GONE); + recordCircle.setSendButtonInvisible(); + } + runningAnimationAudio = null; + isRecordingStateChanged(); + if (recordedAudioBackground != null) { + recordedAudioBackground.setAlpha(1f); + } + if (attachLayout != null) { + attachLayoutTranslationX = 0; + updateAttachLayoutParams(); + } + if (slideText != null) { + slideText.setCancelToProgress(0f); + } + + delegate.onAudioVideoInterfaceUpdated(); + updateSendAsButton(); + } + + protected void isRecordingStateChanged() { + + } + private void createRecordPanel() { if (recordPanel != null || getContext() == null) { return; @@ -7942,7 +8194,7 @@ private void createRecordPanel() { recordPanel.setOnTouchListener((v, event) -> true); recordPanel.addView(slideText = new SlideTextView(getContext()), LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.NO_GRAVITY, 45, 0, 0, 0)); - LinearLayout recordTimeContainer = new LinearLayout(getContext()); + recordTimeContainer = new LinearLayout(getContext()); recordTimeContainer.setOrientation(LinearLayout.HORIZONTAL); recordTimeContainer.setPadding(AndroidUtilities.dp(13), 0, 0, 0); recordTimeContainer.setFocusable(false); @@ -7995,11 +8247,14 @@ public void setCommand(MessageObject messageObject, String command, boolean long return; } TLRPC.User user = messageObject != null && DialogObject.isChatDialog(dialog_id) ? accountInstance.getMessagesController().getUser(messageObject.messageOwner.from_id.user_id) : null; + SendMessagesHelper.SendMessageParams sendMessageParams; if ((botCount != 1 || username) && user != null && user.bot && !command.contains("@")) { - SendMessagesHelper.getInstance(currentAccount).sendMessage(String.format(Locale.US, "%s@%s", command, UserObject.getPublicUsername(user)), dialog_id, replyingMessageObject, getThreadMessage(), null, false, null, null, null, true, 0, null, false); + sendMessageParams = SendMessagesHelper.SendMessageParams.of(String.format(Locale.US, "%s@%s", command, UserObject.getPublicUsername(user)), dialog_id, replyingMessageObject, getThreadMessage(), null, false, null, null, null, true, 0, null, false); } else { - SendMessagesHelper.getInstance(currentAccount).sendMessage(command, dialog_id, replyingMessageObject, getThreadMessage(), null, false, null, null, null, true, 0, null, false); + sendMessageParams = SendMessagesHelper.SendMessageParams.of(command, dialog_id, replyingMessageObject, getThreadMessage(), null, false, null, null, null, true, 0, null, false); } + applyStoryToSendMessageParams(sendMessageParams); + SendMessagesHelper.getInstance(currentAccount).sendMessage(sendMessageParams); } } @@ -8296,7 +8551,7 @@ public void updateColors() { if (captionLimitView != null && messageEditText != null) { if (codePointCount - currentLimit < 0) { - captionLimitView.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteRedText)); + captionLimitView.setTextColor(getThemedColor(Theme.key_text_RedRegular)); } else { captionLimitView.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteGrayText)); } @@ -8322,7 +8577,7 @@ public void updateColors() { private void updateRecordedDeleteIconColors() { int dotColor = getThemedColor(Theme.key_chat_recordedVoiceDot); - int background = getThemedColor(Theme.key_chat_messagePanelBackground); + int background = ColorUtils.blendARGB(dotColor, getThemedColor(Theme.key_chat_messagePanelBackground), 0.5f); int greyColor = getThemedColor(Theme.key_chat_messagePanelVoiceDelete); if (recordDeleteImageView != null) { @@ -8641,7 +8896,8 @@ public void updateSendAsButton(boolean animated) { if (senderSelectView != null) { senderSelectView.setTranslationX(startX); } - messageEditText.setTranslationX(startX); + messageTextTranslationX = startX; + updateMessageTextParams(); anim.addUpdateListener(animation -> { final float val = (float) animation.getAnimatedValue(); final float tx = startX + (endX - startX) * val; @@ -8650,7 +8906,8 @@ public void updateSendAsButton(boolean animated) { senderSelectView.setTranslationX(tx); } emojiButton.setTranslationX(tx); - messageEditText.setTranslationX(tx); + messageTextTranslationX = tx; + updateMessageTextParams(); }); anim.addListener(new AnimatorListenerAdapter() { @Override @@ -8666,7 +8923,8 @@ public void onAnimationStart(Animator animation) { tx = senderSelectView.getTranslationX(); } emojiButton.setTranslationX(tx); - messageEditText.setTranslationX(tx); + messageTextTranslationX = tx; + updateMessageTextParams(); if (botCommandsMenuButton != null && botCommandsMenuButton.getTag() == null) { animationParamsX.clear(); @@ -8680,7 +8938,8 @@ public void onAnimationEnd(Animator animation) { senderSelectView.setVisibility(GONE); } emojiButton.setTranslationX(0); - messageEditText.setTranslationX(0); + messageTextTranslationX = 0; + updateMessageTextParams(); } } @@ -8697,7 +8956,8 @@ public void onAnimationCancel(Animator animation) { tx = senderSelectView.getTranslationX(); } emojiButton.setTranslationX(tx); - messageEditText.setTranslationX(tx); + messageTextTranslationX = tx; + updateMessageTextParams(); requestLayout(); } }); @@ -8715,7 +8975,8 @@ public void onAnimationCancel(Animator animation) { } float translationX = isVisible ? endX : 0; emojiButton.setTranslationX(translationX); - messageEditText.setTranslationX(translationX); + messageTextTranslationX = translationX; + updateMessageTextParams(); if (senderSelectView != null) { senderSelectView.setAlpha(endAlpha); senderSelectView.setTag(null); @@ -8736,7 +8997,7 @@ private void updateBotButton(boolean animated) { if (!isChat) { return; } - if (!parentFragment.openAnimationEnded) { + if (parentFragment != null && !parentFragment.openAnimationEnded) { animated = false; } boolean hasBotWebView = hasBotWebView(); @@ -8955,7 +9216,7 @@ public boolean didPressedBotButton(final TLRPC.KeyboardButton button, final Mess return false; } if (button instanceof TLRPC.TL_keyboardButton) { - SendMessagesHelper.getInstance(currentAccount).sendMessage(button.text, dialog_id, replyMessageObject, getThreadMessage(), null, false, null, null, null, true, 0, null, false); + SendMessagesHelper.getInstance(currentAccount).sendMessage(SendMessagesHelper.SendMessageParams.of(button.text, dialog_id, replyMessageObject, getThreadMessage(), null, false, null, null, null, true, 0, null, false)); } else if (button instanceof TLRPC.TL_keyboardButtonUrl) { if (Browser.urlMustNotHaveConfirmation(button.url)) { Browser.openUrl(parentActivity, Uri.parse(button.url), true, true, progress); @@ -8988,15 +9249,10 @@ public void run() { if (SharedPrefsHelper.isWebViewConfirmShown(currentAccount, botId)) { onRequestWebView.run(); } else { - new AlertDialog.Builder(parentFragment.getParentActivity()) - .setTitle(LocaleController.getString(R.string.BotOpenPageTitle)) - .setMessage(AndroidUtilities.replaceTags(LocaleController.formatString("BotOpenPageMessage", R.string.BotOpenPageMessage, UserObject.getUserName(user)))) - .setPositiveButton(LocaleController.getString(R.string.OK), (dialog, which) -> { - onRequestWebView.run(); - SharedPrefsHelper.setWebViewConfirmShown(currentAccount, botId, true); - }) - .setNegativeButton(LocaleController.getString(R.string.Cancel), null) - .show(); + AlertsCreator.createBotLaunchAlert(parentFragment, MessagesController.getInstance(currentAccount).getUser(dialog_id), () -> { + onRequestWebView.run(); + SharedPrefsHelper.setWebViewConfirmShown(currentAccount, botId, true); + }, null); } } else if (button instanceof TLRPC.TL_keyboardButtonRequestGeoLocation) { AlertDialog.Builder builder = new AlertDialog.Builder(parentActivity); @@ -9033,6 +9289,29 @@ public void run() { Bundle args = new Bundle(); args.putBoolean("onlySelect", true); args.putInt("dialogsType", DialogsActivity.DIALOGS_TYPE_BOT_SHARE); + + if ((button.flags & 2) != 0) { + args.putBoolean("allowGroups", false); + args.putBoolean("allowMegagroups", false); + args.putBoolean("allowLegacyGroups", false); + args.putBoolean("allowUsers", false); + args.putBoolean("allowChannels", false); + args.putBoolean("allowBots", false); + for (TLRPC.InlineQueryPeerType peerType : button.peer_types) { + if (peerType instanceof TLRPC.TL_inlineQueryPeerTypePM) { + args.putBoolean("allowUsers", true); + } else if (peerType instanceof TLRPC.TL_inlineQueryPeerTypeBotPM) { + args.putBoolean("allowBots", true); + } else if (peerType instanceof TLRPC.TL_inlineQueryPeerTypeBroadcast) { + args.putBoolean("allowChannels", true); + } else if (peerType instanceof TLRPC.TL_inlineQueryPeerTypeChat) { + args.putBoolean("allowLegacyGroups", true); + } else if (peerType instanceof TLRPC.TL_inlineQueryPeerTypeMegagroup) { + args.putBoolean("allowMegagroups", true); + } + } + } + DialogsActivity fragment = new DialogsActivity(args); fragment.setDelegate((fragment1, dids, message, param, topicsFragment) -> { long uid = messageObject.messageOwner.from_id.user_id; @@ -9128,6 +9407,13 @@ public boolean isPopupView(View view) { return view == botKeyboardView || view == emojiView; } + public int getPopupViewHeight(View view) { + if (view == botKeyboardView && botKeyboardView != null) { + return botKeyboardView.getKeyboardHeight(); + } + return -1; + } + public boolean isRecordCircle(View view) { return view == recordCircle; } @@ -9144,7 +9430,7 @@ private void createEmojiView() { if (emojiView != null) { return; } - emojiView = new EmojiView(parentFragment, allowAnimatedEmoji, true, true, getContext(), true, info, sizeNotifierLayout, resourcesProvider) { + emojiView = new EmojiView(parentFragment, allowAnimatedEmoji, true, true, getContext(), true, info, sizeNotifierLayout, shouldDrawBackground, resourcesProvider) { @Override public void setTranslationY(float translationY) { super.setTranslationY(translationY); @@ -9153,6 +9439,9 @@ public void setTranslationY(float translationY) { } } }; + if (!shouldDrawBackground) { + emojiView.updateColors(); + } emojiView.setAllow(allowStickers, allowGifs, true); emojiView.setVisibility(GONE); emojiView.setShowing(false); @@ -9229,9 +9518,13 @@ public void onCustomEmojiSelected(long documentId, TLRPC.Document document, Stri @Override public void onAnimatedEmojiUnlockClick() { - BottomSheet alert = new PremiumFeatureBottomSheet(parentFragment, PremiumPreviewFragment.PREMIUM_FEATURE_ANIMATED_EMOJI, false); - if (parentFragment != null) { - parentFragment.showDialog(alert); + BaseFragment fragment = parentFragment; + if (fragment == null) { + fragment = LaunchActivity.getLastFragment(); + } + BottomSheet alert = new PremiumFeatureBottomSheet(fragment, PremiumPreviewFragment.PREMIUM_FEATURE_ANIMATED_EMOJI, false); + if (fragment != null) { + fragment.showDialog(alert); } else { alert.show(); } @@ -9297,6 +9590,7 @@ public void onGifSelected(View view, Object gif, String query, Object parent, bo } setStickersExpanded(false, true, false); } + TLRPC.StoryItem storyItem = delegate != null ? delegate.getReplyToStory() : null; if (gif instanceof TLRPC.Document) { TLRPC.Document document = (TLRPC.Document) gif; if (NaConfig.INSTANCE.getAutoInsertGIFCaption().Bool()) { @@ -9305,10 +9599,10 @@ public void onGifSelected(View view, Object gif, String query, Object parent, bo if (caption.startsWith("@gif")) { caption = ""; } - SendMessagesHelper.getInstance(currentAccount).sendSticker(document, query, dialog_id, replyingMessageObject, getThreadMessage(), parent, null, notify, scheduleDate, false, caption, entities); + SendMessagesHelper.getInstance(currentAccount).sendSticker(document, query, dialog_id, replyingMessageObject, getThreadMessage(), storyItem, null, notify, scheduleDate, false, parent, caption, entities); messageEditText.setText(""); } else { - SendMessagesHelper.getInstance(currentAccount).sendSticker(document, query, dialog_id, replyingMessageObject, getThreadMessage(), parent, null, notify, scheduleDate, false); + SendMessagesHelper.getInstance(currentAccount).sendSticker(document, query, dialog_id, replyingMessageObject, getThreadMessage(), storyItem, null, notify, scheduleDate, false, parent); } MediaDataController.getInstance(currentAccount).addRecentGif(document, (int) (System.currentTimeMillis() / 1000), true); if (DialogObject.isEncryptedDialog(dialog_id)) { @@ -9331,8 +9625,11 @@ public void onGifSelected(View view, Object gif, String query, Object parent, bo params.put("query_id", "" + result.query_id); params.put("force_gif", "1"); - SendMessagesHelper.prepareSendingBotContextResult(parentFragment, accountInstance, result, params, dialog_id, replyingMessageObject, getThreadMessage(), notify, scheduleDate); - + if (storyItem == null) { + SendMessagesHelper.prepareSendingBotContextResult(parentFragment, accountInstance, result, params, dialog_id, replyingMessageObject, getThreadMessage(), storyItem, notify, scheduleDate); + } else { + SendMessagesHelper.getInstance(currentAccount).sendSticker(result.document, query, dialog_id, replyingMessageObject, getThreadMessage(), storyItem, null, notify, scheduleDate, false, parent); + } if (searchingType != 0) { setSearchingTypeInternal(0, true); emojiView.closeSearch(true); @@ -9370,7 +9667,11 @@ public void onShowStickerSet(TLRPC.StickerSet stickerSet, TLRPC.InputStickerSet trendingStickersAlert.getLayout().showStickerSet(stickerSet, inputStickerSet); return; } - if (parentFragment == null || parentActivity == null) { + BaseFragment fragment = parentFragment; + if (fragment == null) { + fragment = LaunchActivity.getLastFragment(); + } + if (fragment == null || parentActivity == null) { return; } if (stickerSet != null) { @@ -9378,7 +9679,7 @@ public void onShowStickerSet(TLRPC.StickerSet stickerSet, TLRPC.InputStickerSet inputStickerSet.access_hash = stickerSet.access_hash; inputStickerSet.id = stickerSet.id; } - parentFragment.showDialog(new StickersAlert(parentActivity, parentFragment, inputStickerSet, null, ChatActivityEnterView.this, resourcesProvider)); + fragment.showDialog(new StickersAlert(parentActivity, fragment, inputStickerSet, null, ChatActivityEnterView.this, resourcesProvider)); } @Override @@ -9446,8 +9747,12 @@ public int getThreadId() { @Override public void showTrendingStickersAlert(TrendingStickersLayout layout) { - if (parentActivity != null && parentFragment != null) { - trendingStickersAlert = new TrendingStickersAlert(parentActivity, parentFragment, layout, resourcesProvider) { + BaseFragment fragment = parentFragment; + if (fragment == null) { + fragment = LaunchActivity.getLastFragment(); + } + if (fragment != null) { + trendingStickersAlert = new TrendingStickersAlert(getContext(), fragment, layout, resourcesProvider) { @Override public void dismiss() { super.dismiss(); @@ -9462,7 +9767,7 @@ public void dismiss() { if (ChatActivityEnterView.this.delegate != null) { ChatActivityEnterView.this.delegate.onTrendingStickersShowed(true); } - trendingStickersAlert.show(); + fragment.showDialog(trendingStickersAlert); } } @@ -9500,7 +9805,9 @@ public void onDragStart() { emojiView.getLayoutParams().height = stickersExpandedHeight; emojiView.setLayerType(LAYER_TYPE_HARDWARE, null); sizeNotifierLayout.requestLayout(); - sizeNotifierLayout.setForeground(new ScrimDrawable()); + if (shouldDrawBackground) { + sizeNotifierLayout.setForeground(new ScrimDrawable()); + } initialOffset = (int) getTranslationY(); if (delegate != null) { delegate.onStickersExpandedChange(); @@ -9547,7 +9854,7 @@ private boolean allowDragging() { return stickersTabOpen && !(!stickersExpanded && messageEditText != null && messageEditText.length() > 0) && emojiView.areThereAnyStickers() && !waitingForKeyboardOpen; } }); - sizeNotifierLayout.addView(emojiView, sizeNotifierLayout.getChildCount() - 5); + attachEmojiView(); checkChannelRights(); } @@ -9571,7 +9878,8 @@ public void onStickerSelected(TLRPC.Document sticker, String query, Object paren emojiView.hideSearchKeyboard(); } setStickersExpanded(false, true, false); - SendMessagesHelper.getInstance(currentAccount).sendSticker(sticker, query, dialog_id, replyingMessageObject, getThreadMessage(), parent, sendAnimationData, notify, scheduleDate, parent instanceof TLRPC.TL_messages_stickerSet); + TLRPC.StoryItem storyItem = delegate != null ? delegate.getReplyToStory() : null; + SendMessagesHelper.getInstance(currentAccount).sendSticker(sticker, query, dialog_id, replyingMessageObject, getThreadMessage(), storyItem, sendAnimationData, notify, scheduleDate, parent instanceof TLRPC.TL_messages_stickerSet, parent); if (delegate != null) { delegate.onMessageSend(null, true, scheduleDate); } @@ -9630,9 +9938,7 @@ private void showPopup(int show, int contentType, boolean allowAnimation) { boolean samePannelWasVisible = false; int previousHeight = 0; if (contentType == 0) { - if (emojiView.getParent() == null) { - sizeNotifierLayout.addView(emojiView, sizeNotifierLayout.getChildCount() - 5); - } + attachEmojiView(); samePannelWasVisible = emojiViewVisible && emojiView.getVisibility() == View.VISIBLE; emojiView.setVisibility(VISIBLE); emojiViewVisible = true; @@ -9694,24 +10000,31 @@ private void showPopup(int show, int contentType, boolean allowAnimation) { updateBotButton(true); onWindowSizeChanged(); if (smoothKeyboard && !keyboardVisible && currentHeight != previousHeight && allowAnimation) { - panelAnimation = new AnimatorSet(); - currentView.setTranslationY(currentHeight - previousHeight); - panelAnimation.playTogether(ObjectAnimator.ofFloat(currentView, View.TRANSLATION_Y, currentHeight - previousHeight, 0)); - panelAnimation.setInterpolator(AdjustPanLayoutHelper.keyboardInterpolator); - panelAnimation.setDuration(AdjustPanLayoutHelper.keyboardDuration); - panelAnimation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - panelAnimation = null; - if (delegate != null) { - delegate.bottomPanelTranslationYChanged(0); - } - NotificationCenter.getInstance(currentAccount).onAnimationFinish(notificationsIndex); - requestLayout(); + Runnable onAnimationEndRunnuble = () -> { + if (delegate != null) { + delegate.bottomPanelTranslationYChanged(0); } - }); - AndroidUtilities.runOnUIThread(runEmojiPanelAnimation, 50); - notificationsIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(notificationsIndex, null); + requestLayout(); + }; + if (overrideKeyboardAnimation) { + this.animationEndRunnable = onAnimationEndRunnuble; + } else { + panelAnimation = new AnimatorSet(); + currentView.setTranslationY(currentHeight - previousHeight); + panelAnimation.playTogether(ObjectAnimator.ofFloat(currentView, View.TRANSLATION_Y, currentHeight - previousHeight, 0)); + panelAnimation.setInterpolator(AdjustPanLayoutHelper.keyboardInterpolator); + panelAnimation.setDuration(AdjustPanLayoutHelper.keyboardDuration); + panelAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + panelAnimation = null; + notificationsLocker.unlock(); + onAnimationEndRunnuble.run(); + } + }); + AndroidUtilities.runOnUIThread(runEmojiPanelAnimation, 50); + notificationsLocker.lock(); + } requestLayout(); } } @@ -9727,34 +10040,41 @@ public void onAnimationEnd(Animator animation) { animatingContentType = 0; } emojiView.setShowing(false); - panelAnimation = new AnimatorSet(); - panelAnimation.playTogether(ObjectAnimator.ofFloat(emojiView, View.TRANSLATION_Y, emojiView.getMeasuredHeight())); - panelAnimation.setInterpolator(AdjustPanLayoutHelper.keyboardInterpolator); - panelAnimation.setDuration(AdjustPanLayoutHelper.keyboardDuration); - panelAnimation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (show == 0) { - emojiPadding = 0; - } - panelAnimation = null; - if (emojiView != null) { - emojiView.setTranslationY(0); - emojiView.setVisibility(GONE); - sizeNotifierLayout.removeView(emojiView); - if (removeEmojiViewAfterAnimation) { - removeEmojiViewAfterAnimation = false; - emojiView = null; - } - } - if (delegate != null) { - delegate.bottomPanelTranslationYChanged(0); + Runnable animationEndRunnable = () -> { + if (show == 0) { + emojiPadding = 0; + } + panelAnimation = null; + if (emojiView != null) { + emojiView.setTranslationY(0); + emojiView.setVisibility(GONE); + sizeNotifierLayout.removeView(emojiView); + if (removeEmojiViewAfterAnimation) { + removeEmojiViewAfterAnimation = false; + emojiView = null; } - NotificationCenter.getInstance(currentAccount).onAnimationFinish(notificationsIndex); - requestLayout(); } - }); - notificationsIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(notificationsIndex, null); + if (delegate != null) { + delegate.bottomPanelTranslationYChanged(0); + } + requestLayout(); + }; + if (!overrideKeyboardAnimation) { + panelAnimation = new AnimatorSet(); + panelAnimation.playTogether(ObjectAnimator.ofFloat(emojiView, View.TRANSLATION_Y, emojiView.getMeasuredHeight())); + panelAnimation.setInterpolator(AdjustPanLayoutHelper.keyboardInterpolator); + panelAnimation.setDuration(AdjustPanLayoutHelper.keyboardDuration); + notificationsLocker.lock(); + panelAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + animationEndRunnable.run(); + notificationsLocker.unlock(); + } + }); + } else { + this.animationEndRunnable = animationEndRunnable; + } AndroidUtilities.runOnUIThread(runEmojiPanelAnimation, 50); requestLayout(); } else { @@ -9782,6 +10102,7 @@ public void onAnimationEnd(Animator animation) { if (botKeyboardViewVisible) { animatingContentType = 1; } + panelAnimation = new AnimatorSet(); panelAnimation.playTogether(ObjectAnimator.ofFloat(botKeyboardView, View.TRANSLATION_Y, botKeyboardView.getMeasuredHeight())); panelAnimation.setInterpolator(AdjustPanLayoutHelper.keyboardInterpolator); @@ -9795,14 +10116,14 @@ public void onAnimationEnd(Animator animation) { panelAnimation = null; botKeyboardView.setTranslationY(0); botKeyboardView.setVisibility(GONE); - NotificationCenter.getInstance(currentAccount).onAnimationFinish(notificationsIndex); + notificationsLocker.unlock(); if (delegate != null) { delegate.bottomPanelTranslationYChanged(0); } requestLayout(); } }); - notificationsIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(notificationsIndex, null); + notificationsLocker.lock(); AndroidUtilities.runOnUIThread(runEmojiPanelAnimation, 50); requestLayout(); } else { @@ -9829,6 +10150,16 @@ public void onAnimationEnd(Animator animation) { checkBotMenu(); } + private void attachEmojiView() { + if (emojiView.getParent() == null) { + int index = sizeNotifierLayout.getChildCount() - 5; + if (!shouldDrawBackground) { + index = sizeNotifierLayout.getChildCount(); + } + sizeNotifierLayout.addView(emojiView, index); + } + } + private String getTopicKeyString() { if (parentFragment != null && parentFragment.isTopic) { return dialog_id + "_" + parentFragment.getTopicId(); @@ -9842,9 +10173,8 @@ private void setEmojiButtonImage(boolean byOpen, boolean animated) { } boolean showingRecordInterface = recordInterfaceState == 1 || (recordedAudioPanel != null && recordedAudioPanel.getVisibility() == View.VISIBLE); if (showingRecordInterface) { - emojiButton.setScaleX(0); - emojiButton.setScaleY(0); - emojiButton.setAlpha(0f); + emojiButtonAlpha = emojiButtonScale = 0f; + updateEmojiButtonParams(); animated = false; } ChatActivityEnterViewAnimatedIconView.State nextIcon; @@ -9923,8 +10253,11 @@ public boolean hidePopup(boolean byBackButton, boolean forceAnimate) { if (messageEditText != null) { messageEditText.requestFocus(); } + } else if (stickersExpanded) { + setStickersExpanded(false, true, false); + } else { + showPopup(0, 0); } - showPopup(0, 0); } return true; } @@ -10029,6 +10362,10 @@ public boolean isKeyboardVisible() { return keyboardVisible; } + public boolean isWaitingForKeyboard() { + return waitingForKeyboardOpen; + } + public void addRecentGif(TLRPC.Document searchImage) { MediaDataController.getInstance(currentAccount).addRecentGif(searchImage, (int) (System.currentTimeMillis() / 1000), true); if (emojiView != null) { @@ -10118,11 +10455,11 @@ public void onAnimationEnd(Animator animation) { delegate.bottomPanelTranslationYChanged(0); } requestLayout(); - NotificationCenter.getInstance(currentAccount).onAnimationFinish(notificationsIndex); + notificationsLocker.unlock(); } }); AndroidUtilities.runOnUIThread(runEmojiPanelAnimation, 50); - notificationsIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(notificationsIndex, null); + notificationsLocker.lock(); requestLayout(); } } @@ -10166,6 +10503,10 @@ public void onAnimationEnd(Animator animation) { onWindowSizeChanged(); } + public void checkReactionsButton(boolean visible) { + AndroidUtilities.updateViewVisibilityAnimated(reactionsButton, visible, 0.1f, true); + } + public int getEmojiPadding() { return emojiPadding; } @@ -10319,7 +10660,7 @@ public void didReceivedNotification(int id, int account, Object... args) { recordDeleteImageView.setAlpha(0f); recordDeleteImageView.setScaleY(0f); recordDeleteImageView.setScaleX(0f); - int duration = 0; + double duration = 0; for (int a = 0; a < audioToSend.attributes.size(); a++) { TLRPC.DocumentAttribute attribute = audioToSend.attributes.get(a); if (attribute instanceof TLRPC.TL_documentAttributeAudio) { @@ -10338,7 +10679,7 @@ public void didReceivedNotification(int id, int account, Object... args) { break; } } - recordedAudioTimeTextView.setText(AndroidUtilities.formatShortDuration(duration)); + recordedAudioTimeTextView.setText(AndroidUtilities.formatShortDuration((int) duration)); checkSendButton(false); updateRecordInterface(RECORD_STATE_PREPARING); } else { @@ -10437,7 +10778,7 @@ public void onRequestPermissionsResultFragment(int requestCode, String[] permiss } } - private void checkStickresExpandHeight() { + public void checkStickresExpandHeight() { if (emojiView == null) { return; } @@ -10456,27 +10797,34 @@ private void checkStickresExpandHeight() { } stickersExpandedHeight = newHeight; if (currentHeight > newHeight) { - AnimatorSet anims = new AnimatorSet(); - anims.playTogether( - ObjectAnimator.ofInt(this, roundedTranslationYProperty, -(stickersExpandedHeight - origHeight)), - ObjectAnimator.ofInt(emojiView, roundedTranslationYProperty, -(stickersExpandedHeight - origHeight)) - ); - ((ObjectAnimator) anims.getChildAnimations().get(0)).addUpdateListener(animation -> sizeNotifierLayout.invalidate()); - anims.setDuration(300); - anims.setInterpolator(CubicBezierInterpolator.DEFAULT); - anims.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - stickersExpansionAnim = null; - if (emojiView != null) { - emojiView.getLayoutParams().height = stickersExpandedHeight; - emojiView.setLayerType(LAYER_TYPE_NONE, null); - } + Runnable animationEndRunnable = () -> { + if (emojiView != null) { + emojiView.getLayoutParams().height = stickersExpandedHeight; + emojiView.setLayerType(LAYER_TYPE_NONE, null); } - }); - stickersExpansionAnim = anims; + }; emojiView.setLayerType(LAYER_TYPE_HARDWARE, null); - anims.start(); + if (overrideKeyboardAnimation) { + this.animationEndRunnable = animationEndRunnable; + } else { + AnimatorSet anims = new AnimatorSet(); + anims.playTogether( + ObjectAnimator.ofInt(this, roundedTranslationYProperty, -(stickersExpandedHeight - origHeight)), + ObjectAnimator.ofInt(emojiView, roundedTranslationYProperty, -(stickersExpandedHeight - origHeight)) + ); + ((ObjectAnimator) anims.getChildAnimations().get(0)).addUpdateListener(animation -> sizeNotifierLayout.invalidate()); + anims.setDuration(300); + anims.setInterpolator(CubicBezierInterpolator.DEFAULT); + anims.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + stickersExpansionAnim = null; + animationEndRunnable.run(); + } + }); + stickersExpansionAnim = anims; + anims.start(); + } } else { emojiView.getLayoutParams().height = stickersExpandedHeight; sizeNotifierLayout.requestLayout(); @@ -10539,7 +10887,9 @@ public void setStickersExpanded(boolean expanded, boolean animated, boolean byDr } emojiView.getLayoutParams().height = stickersExpandedHeight; sizeNotifierLayout.requestLayout(); - sizeNotifierLayout.setForeground(new ScrimDrawable()); + if (shouldDrawBackground) { + sizeNotifierLayout.setForeground(new ScrimDrawable()); + } int start = 0, end = 0; if (messageEditText != null) { start = messageEditText.getSelectionStart(); @@ -10565,12 +10915,12 @@ public void setStickersExpanded(boolean expanded, boolean animated, boolean byDr public void onAnimationEnd(Animator animation) { stickersExpansionAnim = null; emojiView.setLayerType(LAYER_TYPE_NONE, null); - NotificationCenter.getInstance(currentAccount).onAnimationFinish(notificationsIndex); + notificationsLocker.unlock(); } }); stickersExpansionAnim = anims; emojiView.setLayerType(LAYER_TYPE_HARDWARE, null); - notificationsIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(notificationsIndex, null); + notificationsLocker.lock(); stickersExpansionProgress = 0f; sizeNotifierLayout.invalidate(); anims.start(); @@ -10619,14 +10969,14 @@ public void onAnimationEnd(Animator animation) { onEmojiSearchClosed.run(); onEmojiSearchClosed = null; } - NotificationCenter.getInstance(currentAccount).onAnimationFinish(notificationsIndex); + notificationsLocker.unlock(); } }); stickersExpansionProgress = 1f; sizeNotifierLayout.invalidate(); stickersExpansionAnim = anims; emojiView.setLayerType(LAYER_TYPE_HARDWARE, null); - notificationsIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(notificationsIndex, null); + notificationsLocker.lock(); anims.start(); } else { stickersExpansionProgress = 0; @@ -11072,12 +11422,7 @@ protected void onDraw(Canvas canvas) { MessagesController.getInstance(currentAccount).sendTyping(dialog_id, getThreadMessageId(), isInVideoMode() ? 7 : 1, 0); } - String newString; - if (time / 60 >= 60) { - newString = String.format(Locale.US, "%01d:%02d:%02d,%d", (time / 60) / 60, (time / 60) % 60, time % 60, ms / 10); - } else { - newString = String.format(Locale.US, "%01d:%02d,%d", time / 60, time % 60, ms / 10); - } + String newString = AndroidUtilities.formatTimerDurationFast((int) time, ms); if (newString.length() >= 3 && oldString != null && oldString.length() >= 3 && newString.length() == oldString.length() && newString.charAt(newString.length() - 3) != oldString.charAt(newString.length() - 3)) { int n = newString.length(); @@ -11371,7 +11716,7 @@ protected void dispatchDraw(Canvas canvas) { } } - int getThemedColor(String key) { + private int getThemedColor(int key) { Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; return color != null ? color : Theme.getColor(key); } @@ -11386,6 +11731,102 @@ public void setChatSearchExpandOffset(float chatSearchExpandOffset) { invalidate(); } + public void setHorizontalPadding(float padding, float progress, boolean allowShare) { + float v = -padding * (1f - progress); + float s = 0.5f + 0.5f * progress; + emojiButtonPaddingScale = s; + emojiButtonPaddingAlpha = progress; + updateEmojiButtonParams(); + emojiButton.setTranslationX(-v); + messageTextPaddingTranslationX = -v - AndroidUtilities.dp(31) * (1f - progress); + if (recordDeleteImageView != null) { + recordDeleteImageView.setTranslationX(-v); + } + if (recordTimeContainer != null) { + recordTimeContainer.setTranslationX(-v); + } + if (recordedAudioPlayButton != null) { + recordedAudioPlayButton.setTranslationX(-v); + } + if (recordedAudioTimeTextView != null) { + recordedAudioTimeTextView.setTranslationX(v); + } + sendButtonContainer.setTranslationX(v); + sendButtonContainer.setAlpha(allowShare ? progress : 1f); + sendButtonEnabled = allowShare ? progress == 1f : true; + attachLayoutPaddingTranslationX = v; + updateAttachLayoutParams(); + updateMessageTextParams(); + float newPadding = padding * (1f - progress); + + if (horizontalPadding != newPadding) { + horizontalPadding = newPadding; + if (seekBarWaveform != null && recordedAudioSeekBar != null) { + recordedAudioSeekBar.setTranslationX(horizontalPadding); + recordedAudioSeekBar.invalidate(); + seekBarWaveform.setSize((int) (recordedAudioSeekBar.getMeasuredWidth() - horizontalPadding * 2), recordedAudioSeekBar.getMeasuredHeight()); + } + if (recordedAudioBackground != null) { + recordedAudioBackground.invalidate(); + } + } + } + + private void updateMessageTextParams() { + if (messageEditText != null) { + messageEditText.setTranslationX(messageTextPaddingTranslationX + messageTextTranslationX); + } + } + + private void updateAttachLayoutParams() { + if (attachLayout != null) { + attachLayout.setTranslationX(attachLayoutPaddingTranslationX + attachLayoutTranslationX); + } + } + + private void updateEmojiButtonParams() { + emojiButton.setScaleX(emojiButtonPaddingScale * emojiButtonScale); + emojiButton.setScaleY(emojiButtonPaddingScale * emojiButtonScale); + emojiButton.setAlpha(emojiButtonPaddingAlpha * emojiButtonAlpha); + } + + public void setOverrideHint(CharSequence overrideHint) { + this.overrideHint = overrideHint; + updateFieldHint(false); + } + + public void setOverrideKeyboardAnimation(boolean overrideKeyboardAnimation) { + this.overrideKeyboardAnimation = overrideKeyboardAnimation; + } + + public void onOverrideAnimationEnd() { + if (animationEndRunnable != null) { + animationEndRunnable.run(); + animationEndRunnable = null; + } + } + + public int getStickersExpandedHeight() { + return stickersExpandedHeight; + } + + public void reset() { + setStickersExpanded(false, true, false); + showPopup(0, 0, false); + if (getEditField() != null) { + if (!TextUtils.isEmpty(getEditField().getText())) { + getEditField().setText(""); + } + } + recordingAudioVideo = false; + if (audioVideoSendButton != null) { + audioVideoSendButton.setVisibility(View.VISIBLE); + } + recordIsCanceled = true; + cancelRecordIntefraceInternal(); + hideRecordedAudioPanelInternal(); + } + public enum BotMenuButtonType { NO_BUTTON, COMMANDS, diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlert.java index ea27002557..5ba1db22ad 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlert.java @@ -60,6 +60,7 @@ import androidx.annotation.Keep; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.core.graphics.ColorUtils; import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.FloatValueHolder; @@ -132,6 +133,7 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N private final NumberTextView captionLimitView; public boolean forUser; + public boolean isPhotoPicker; private int currentLimit; private int codepointCount; @@ -139,10 +141,15 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N private boolean isSoundPicker = false; private boolean isEmojiPicker = false; private ImageUpdater.AvatarFor setAvatarFor; + public boolean pinnedToTop; private PasscodeView passcodeView; private ChatAttachRestrictedLayout restrictedLayout; public ImageUpdater parentImageUpdater; + public boolean destroyed; + public boolean allowEnterCaption; + private ChatAttachAlertDocumentLayout.DocumentSelectActivityDelegate documentsDelegate; + private long dialogId; public void setCanOpenPreview(boolean canOpenPreview) { this.canOpenPreview = canOpenPreview; @@ -188,7 +195,7 @@ public void onCloseRequested(Runnable callback) { } @Override - public void onWebAppSetActionBarColor(String colorKey) { + public void onWebAppSetActionBarColor(int colorKey) { int from = ((ColorDrawable) actionBar.getBackground()).getColor(); int to = getThemedColor(colorKey); @@ -232,6 +239,8 @@ public void onWebAppSwitchInlineQuery(TLRPC.User botUser, String query, List audios, CharSequence caption, boolean notify, int scheduleDate) { + + } } public float translationProgress = 0; @@ -455,6 +502,9 @@ public void setValue(AttachAlertLayout object, float value) { currentAttachLayout.onHideShowProgress(1.0f - Math.min(1.0f, value / 0.7f)); currentAttachLayout.onContainerTranslationUpdated(currentPanTranslationY); } + if (viewChangeAnimator != null) { + updateSelectedPosition(1); + } containerView.invalidate(); } @@ -627,9 +677,8 @@ boolean onBackPressed() { return false; } - protected int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + protected int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } boolean shouldHideBottomButtons() { @@ -643,7 +692,8 @@ public void onPanTransitionEnd() { } } - protected BaseFragment baseFragment; + @Nullable + final BaseFragment baseFragment; protected boolean inBubbleMode; private ActionBarPopupWindow sendPopupWindow; private ActionBarPopupWindow.ActionBarPopupWindowLayout sendPopupLayout; @@ -658,6 +708,7 @@ public void onPanTransitionEnd() { private ChatAttachAlertLocationLayout locationLayout; private ChatAttachAlertDocumentLayout documentLayout; private ChatAttachAlertPhotoLayoutPreview photoPreviewLayout; + public ChatAttachAlertColorsLayout colorsLayout; private AttachAlertLayout[] layouts = new AttachAlertLayout[7]; private LongSparseArray botAttachLayouts = new LongSparseArray<>(); private AttachAlertLayout currentAttachLayout; @@ -679,6 +730,8 @@ public void onPanTransitionEnd() { protected boolean avatarSearch; protected boolean typeButtonsAvailable; + private boolean stories; + boolean sendButtonEnabled = true; private float sendButtonEnabledProgress = 1f; private ValueAnimator sendButtonColorAnimator; @@ -692,6 +745,7 @@ public void onPanTransitionEnd() { private AnimatorSet actionBarAnimation; private AnimatorSet menuAnimator; protected ActionBarMenuItem selectedMenuItem; + @Nullable protected ActionBarMenuItem searchItem; protected ActionBarMenuItem doneItem; protected FrameLayout headerView; @@ -702,7 +756,7 @@ public void onPanTransitionEnd() { protected TextView mediaPreviewTextView; private float baseSelectedTextViewTranslationY; private boolean menuShowed; - protected SizeNotifierFrameLayout sizeNotifierFrameLayout; + public SizeNotifierFrameLayout sizeNotifierFrameLayout; private boolean openTransitionFinished; private Object viewChangeAnimator; @@ -765,8 +819,8 @@ private class AttachButton extends FrameLayout { private TextView textView; private RLottieImageView imageView; private boolean checked; - private String backgroundKey; - private String textKey; + private int backgroundKey; + private int textKey; private float checkedState; private Animator checkAnimator; private int currentId; @@ -853,7 +907,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(MeasureSpec.makeMeasureSpec(attachItemSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(84), MeasureSpec.EXACTLY)); } - public void setTextAndIcon(int id, CharSequence text, RLottieDrawable drawable, String background, String textColor) { + public void setTextAndIcon(int id, CharSequence text, RLottieDrawable drawable, int background, int textColor) { currentId = id; textView.setText(text); imageView.setAnimation(drawable); @@ -1121,12 +1175,15 @@ public void setAttachBot(TLRPC.User user, TLRPC.TL_attachMenuBot bot) { float currentPanTranslationY; public ChatAttachAlert(Context context, final BaseFragment parentFragment, boolean forceDarkTheme, boolean showingFromDialog) { - this(context, parentFragment, forceDarkTheme, showingFromDialog, null); + this(context, parentFragment, forceDarkTheme, showingFromDialog, true, null); } @SuppressLint("ClickableViewAccessibility") - public ChatAttachAlert(Context context, final BaseFragment parentFragment, boolean forceDarkTheme, boolean showingFromDialog, Theme.ResourcesProvider resourcesProvider) { + public ChatAttachAlert(Context context, final @Nullable BaseFragment parentFragment, boolean forceDarkTheme, boolean showingFromDialog, boolean needCamera, Theme.ResourcesProvider resourcesProvider) { super(context, false, resourcesProvider); + if (parentFragment instanceof ChatActivity) { + setImageReceiverNumLevel(0, 4); + } this.forceDarkTheme = forceDarkTheme; this.showingFromDialog = showingFromDialog; drawNavigationBar = true; @@ -1206,7 +1263,9 @@ protected void onPanTranslationUpdate(float y, float progress, boolean keyboardV } actionBar.setTranslationY(currentPanTranslationY); selectedMenuItem.setTranslationY(currentPanTranslationY); - searchItem.setTranslationY(currentPanTranslationY); + if (searchItem != null) { + searchItem.setTranslationY(currentPanTranslationY); + } doneItem.setTranslationY(currentPanTranslationY); actionBarShadow.setTranslationY(currentPanTranslationY); updateSelectedPosition(0); @@ -1810,82 +1869,84 @@ public void onItemClick(int id) { doneItem.setBackgroundDrawable(Theme.createSelectorDrawable(getThemedColor(Theme.key_dialogButtonSelector), 3)); doneItem.setOnClickListener(v -> currentAttachLayout.onMenuItemClick(40)); - searchItem = new ActionBarMenuItem(context, null, 0, getThemedColor(Theme.key_dialogTextBlack), false, resourcesProvider); - searchItem.setLongClickEnabled(false); - searchItem.setIcon(R.drawable.ic_ab_search); - searchItem.setContentDescription(LocaleController.getString("Search", R.string.Search)); - searchItem.setVisibility(View.INVISIBLE); - searchItem.setAlpha(0.0f); - searchItem.setTranslationX(-AndroidUtilities.dp(42)); - searchItem.setBackgroundDrawable(Theme.createSelectorDrawable(getThemedColor(Theme.key_dialogButtonSelector), 6)); - searchItem.setOnClickListener(v -> { - if (avatarPicker != 0) { - delegate.openAvatarsSearch(); - dismiss(); - return; - } - final HashMap photos = new HashMap<>(); - final ArrayList order = new ArrayList<>(); - PhotoPickerSearchActivity fragment = new PhotoPickerSearchActivity(photos, order, 0, true, (ChatActivity) baseFragment); - fragment.setDelegate(new PhotoPickerActivity.PhotoPickerActivityDelegate() { - - private boolean sendPressed; + if (baseFragment != null) { + searchItem = new ActionBarMenuItem(context, null, 0, getThemedColor(Theme.key_dialogTextBlack), false, resourcesProvider); + searchItem.setLongClickEnabled(false); + searchItem.setIcon(R.drawable.ic_ab_search); + searchItem.setContentDescription(LocaleController.getString("Search", R.string.Search)); + searchItem.setVisibility(View.INVISIBLE); + searchItem.setAlpha(0.0f); + searchItem.setTranslationX(-AndroidUtilities.dp(42)); + searchItem.setBackgroundDrawable(Theme.createSelectorDrawable(getThemedColor(Theme.key_dialogButtonSelector), 6)); + searchItem.setOnClickListener(v -> { + if (avatarPicker != 0) { + delegate.openAvatarsSearch(); + dismiss(); + return; + } + final HashMap photos = new HashMap<>(); + final ArrayList order = new ArrayList<>(); + PhotoPickerSearchActivity fragment = new PhotoPickerSearchActivity(photos, order, 0, true, (ChatActivity) baseFragment); + fragment.setDelegate(new PhotoPickerActivity.PhotoPickerActivityDelegate() { - @Override - public void selectedPhotosChanged() { + private boolean sendPressed; - } + @Override + public void selectedPhotosChanged() { - @Override - public void actionButtonPressed(boolean canceled, boolean notify, int scheduleDate) { - if (canceled) { - return; } - if (photos.isEmpty() || sendPressed) { - return; - } - sendPressed = true; - - ArrayList media = new ArrayList<>(); - for (int a = 0; a < order.size(); a++) { - Object object = photos.get(order.get(a)); - SendMessagesHelper.SendingMediaInfo info = new SendMessagesHelper.SendingMediaInfo(); - media.add(info); - MediaController.SearchImage searchImage = (MediaController.SearchImage) object; - if (searchImage.imagePath != null) { - info.path = searchImage.imagePath; - } else { - info.searchImage = searchImage; + + @Override + public void actionButtonPressed(boolean canceled, boolean notify, int scheduleDate) { + if (canceled) { + return; } - info.thumbPath = searchImage.thumbPath; - info.videoEditedInfo = searchImage.editedInfo; - info.caption = searchImage.caption != null ? searchImage.caption.toString() : null; - info.entities = searchImage.entities; - info.masks = searchImage.stickers; - info.ttl = searchImage.ttl; - if (searchImage.inlineResult != null && searchImage.type == 1) { - info.inlineResult = searchImage.inlineResult; - info.params = searchImage.params; + if (photos.isEmpty() || sendPressed) { + return; } + sendPressed = true; + + ArrayList media = new ArrayList<>(); + for (int a = 0; a < order.size(); a++) { + Object object = photos.get(order.get(a)); + SendMessagesHelper.SendingMediaInfo info = new SendMessagesHelper.SendingMediaInfo(); + media.add(info); + MediaController.SearchImage searchImage = (MediaController.SearchImage) object; + if (searchImage.imagePath != null) { + info.path = searchImage.imagePath; + } else { + info.searchImage = searchImage; + } + info.thumbPath = searchImage.thumbPath; + info.videoEditedInfo = searchImage.editedInfo; + info.caption = searchImage.caption != null ? searchImage.caption.toString() : null; + info.entities = searchImage.entities; + info.masks = searchImage.stickers; + info.ttl = searchImage.ttl; + if (searchImage.inlineResult != null && searchImage.type == 1) { + info.inlineResult = searchImage.inlineResult; + info.params = searchImage.params; + } - searchImage.date = (int) (System.currentTimeMillis() / 1000); + searchImage.date = (int) (System.currentTimeMillis() / 1000); + } + ((ChatActivity) baseFragment).didSelectSearchPhotos(media, notify, scheduleDate); } - ((ChatActivity) baseFragment).didSelectSearchPhotos(media, notify, scheduleDate); - } - @Override - public void onCaptionChanged(CharSequence text) { + @Override + public void onCaptionChanged(CharSequence text) { + } + }); + fragment.setMaxSelectedPhotos(maxSelectedPhotos, allowOrder); + if (showingFromDialog) { + baseFragment.showAsSheet(fragment); + } else { + baseFragment.presentFragment(fragment); } + dismiss(); }); - fragment.setMaxSelectedPhotos(maxSelectedPhotos, allowOrder); - if (showingFromDialog) { - baseFragment.showAsSheet(fragment); - } else { - baseFragment.presentFragment(fragment); - } - dismiss(); - }); + } headerView = new FrameLayout(context) { @Override @@ -1962,7 +2023,7 @@ public boolean onInterceptTouchEvent(MotionEvent event) { headerView.addView(mediaPreviewView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT)); - layouts[0] = photoLayout = new ChatAttachAlertPhotoLayout(this, context, forceDarkTheme, resourcesProvider); + layouts[0] = photoLayout = new ChatAttachAlertPhotoLayout(this, context, forceDarkTheme, needCamera, resourcesProvider); photoLayout.setTranslationX(0); currentAttachLayout = photoLayout; selectedId = 1; @@ -1972,7 +2033,9 @@ public boolean onInterceptTouchEvent(MotionEvent event) { containerView.addView(headerView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.LEFT, 23, 0, 48, 0)); containerView.addView(actionBar, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); containerView.addView(selectedMenuItem, LayoutHelper.createFrame(48, 48, Gravity.TOP | Gravity.RIGHT)); - containerView.addView(searchItem, LayoutHelper.createFrame(48, 48, Gravity.TOP | Gravity.RIGHT)); + if (searchItem != null) { + containerView.addView(searchItem, LayoutHelper.createFrame(48, 48, Gravity.TOP | Gravity.RIGHT)); + } containerView.addView(doneItem, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 48, Gravity.TOP | Gravity.RIGHT)); actionBarShadow = new View(context); @@ -2003,7 +2066,7 @@ public void setTranslationY(float translationY) { buttonsRecyclerView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); containerView.addView(buttonsRecyclerView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 84, Gravity.BOTTOM | Gravity.LEFT)); buttonsRecyclerView.setOnItemClickListener((view, position) -> { - if (baseFragment.getParentActivity() == null) { + if (baseFragment != null && baseFragment.getParentActivity() == null) { return; } if (view instanceof AttachButton) { @@ -2014,21 +2077,21 @@ public void setTranslationY(float translationY) { } showLayout(photoLayout); } else if (num == 3) { - if (Build.VERSION.SDK_INT >= 23 && baseFragment.getParentActivity().checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - baseFragment.getParentActivity().requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, BasePermissionsActivity.REQUEST_CODE_EXTERNAL_STORAGE); + if (Build.VERSION.SDK_INT >= 23 && getContext().checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + AndroidUtilities.findActivity(getContext()).requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, BasePermissionsActivity.REQUEST_CODE_EXTERNAL_STORAGE); return; } openAudioLayout(true); } else if (num == 4) { - if (Build.VERSION.SDK_INT >= 23 && baseFragment.getParentActivity().checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - baseFragment.getParentActivity().requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, BasePermissionsActivity.REQUEST_CODE_EXTERNAL_STORAGE); + if (Build.VERSION.SDK_INT >= 23 && getContext().checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + AndroidUtilities.findActivity(getContext()).requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, BasePermissionsActivity.REQUEST_CODE_EXTERNAL_STORAGE); return; } openDocumentsLayout(true); } else if (num == 5) { if (Build.VERSION.SDK_INT >= 23 && plainTextEnabled) { - if (baseFragment.getParentActivity().checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) { - baseFragment.getParentActivity().requestPermissions(new String[]{Manifest.permission.READ_CONTACTS}, BasePermissionsActivity.REQUEST_CODE_ATTACH_CONTACT); + if (getContext().checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) { + AndroidUtilities.findActivity(getContext()).requestPermissions(new String[]{Manifest.permission.READ_CONTACTS}, BasePermissionsActivity.REQUEST_CODE_ATTACH_CONTACT); return; } } @@ -2086,7 +2149,7 @@ public void setTranslationY(float translationY) { buttonsRecyclerView.setOnItemLongClickListener((view, position) -> { if (view instanceof AttachBotButton) { AttachBotButton button = (AttachBotButton) view; - if (baseFragment == null || button.currentUser == null) { + if (destroyed || button.currentUser == null) { return false; } onLongClickBotButton(button.attachMenuBot, button.currentUser); @@ -2319,7 +2382,7 @@ public void afterTextChanged(Editable editable) { captionLimitView.animate().alpha(1f).scaleX(1f).scaleY(1f).setDuration(100).start(); if (beforeLimit < 0) { sendButtonEnabledLocal = false; - captionLimitView.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteRedText)); + captionLimitView.setTextColor(getThemedColor(Theme.key_text_RedRegular)); } else { captionLimitView.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteGrayText)); } @@ -2440,14 +2503,22 @@ public void getOutline(View view, Outline outline) { } }); writeButton.setOnLongClickListener(view -> { - if (!(baseFragment instanceof ChatActivity) || editingMessageObject != null || currentLimit - codepointCount < 0) { + if ((dialogId == 0 && !(baseFragment instanceof ChatActivity)) || editingMessageObject != null || currentLimit - codepointCount < 0) { return false; } - ChatActivity chatActivity = (ChatActivity) baseFragment; - TLRPC.Chat chat = chatActivity.getCurrentChat(); - TLRPC.User user = chatActivity.getCurrentUser(); - if (chatActivity.isInScheduleMode()) { - return false; + ChatActivity chatActivity = null; + TLRPC.User user = null; + long dialogId = this.dialogId; + if (baseFragment instanceof ChatActivity) { + chatActivity = (ChatActivity) baseFragment; + TLRPC.Chat chat = chatActivity.getCurrentChat(); + user = chatActivity.getCurrentUser(); + if (chatActivity.isInScheduleMode()) { + return false; + } + dialogId = chatActivity.getDialogId(); + } else { + user = MessagesController.getInstance(currentAccount).getUser(dialogId); } sendPopupLayout = new ActionBarPopupWindow.ActionBarPopupWindowLayout(getContext(), resourcesProvider); @@ -2480,7 +2551,7 @@ public boolean onTouch(View v, MotionEvent event) { int i = 0; for (int a = 0; a < 3; a++) { if (a == 1) { - if (!chatActivity.canScheduleMessage() || !currentAttachLayout.canScheduleMessages()) { + if ((chatActivity != null && !chatActivity.canScheduleMessage()) || !currentAttachLayout.canScheduleMessages()) { continue; } } else if (a == 2 && UserObject.isUserSelf(user)) { @@ -2503,15 +2574,15 @@ public boolean onTouch(View v, MotionEvent event) { sendPopupLayout.addView(itemCells[a], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48)); - long chatId = chat == null ? -1 : chat.id; + long finalDialogId = dialogId; itemCells[a].setOnClickListener(v -> { if (sendPopupWindow != null && sendPopupWindow.isShowing()) { sendPopupWindow.dismiss(); } if (num == 0) { - translateComment(parentFragment.getParentActivity(), TranslateDb.getChatLanguage(chatId, TranslatorKt.getCode2Locale(NekoConfig.translateInputLang.String()))); + translateComment(parentFragment.getParentActivity(), TranslateDb.getChatLanguage(finalDialogId, TranslatorKt.getCode2Locale(NekoConfig.translateInputLang.String()))); } else if (num == 1) { - AlertsCreator.createScheduleDatePickerDialog(getContext(), chatActivity.getDialogId(), (notify, scheduleDate) -> { + AlertsCreator.createScheduleDatePickerDialog(getContext(), finalDialogId, (notify, scheduleDate) -> { if (currentAttachLayout == photoLayout || currentAttachLayout == photoPreviewLayout) { sendPressed(notify, scheduleDate); } else { @@ -2536,7 +2607,7 @@ public boolean onTouch(View v, MotionEvent event) { sendPopupWindow.dismiss(); } translateComment(parentFragment.getParentActivity(), locale); - TranslateDb.saveChatLanguage(chatId, locale); + TranslateDb.saveChatLanguage(finalDialogId, locale); return Unit.INSTANCE; }); return true; @@ -2600,7 +2671,7 @@ protected void onDraw(Canvas canvas) { if (forceDarkTheme) { checkColors(); - navBarColorKey = null; + navBarColorKey = -1; } passcodeView = new PasscodeView(context); @@ -2756,7 +2827,7 @@ public void show() { } openTransitionFinished = false; if (Build.VERSION.SDK_INT >= 30) { - navBarColorKey = null; + navBarColorKey = -1; navBarColor = ColorUtils.setAlphaComponent(getThemedColor(Theme.key_windowBackgroundGray), 0); AndroidUtilities.setNavigationBarColor(getWindow(), navBarColor, false); AndroidUtilities.setLightNavigationBar(getWindow(), AndroidUtilities.computePerceivedBrightness(navBarColor) > 0.721); @@ -2809,7 +2880,7 @@ private void sendPressed(boolean notify, int scheduleDate) { delegate.didPressedButton(7, true, notify, scheduleDate, false); } - private void showLayout(AttachAlertLayout layout) { + public void showLayout(AttachAlertLayout layout) { long newId = selectedId; if (layout == restrictedLayout) { newId = restrictedLayout.id; @@ -2825,6 +2896,8 @@ private void showLayout(AttachAlertLayout layout) { newId = 6; } else if (layout == pollLayout) { newId = 9; + } else if (layout == colorsLayout) { + newId = 10; } showLayout(layout, newId); } @@ -2925,7 +2998,7 @@ public void onAnimationEnd(Animator animation) { springAnimation.getSpring().setDampingRatio(0.75f); springAnimation.getSpring().setStiffness(500.0f); springAnimation.addUpdateListener((animation12, value, velocity) -> { - if (nextAttachLayout == pollLayout) { + if (nextAttachLayout == pollLayout || (isPhotoPicker && viewChangeAnimator != null)) { updateSelectedPosition(1); } nextAttachLayout.onContainerTranslationUpdated(currentPanTranslationY); @@ -2933,6 +3006,7 @@ public void onAnimationEnd(Animator animation) { }); springAnimation.addEndListener((animation1, canceled, value, velocity) -> { onEnd.run(); + updateSelectedPosition(0); }); viewChangeAnimator = springAnimation; springAnimation.start(); @@ -3015,7 +3089,7 @@ public void updatePhotoPreview(boolean show) { return; } if (photoPreviewLayout == null) { - photoPreviewLayout = new ChatAttachAlertPhotoLayoutPreview(this, getContext(), parentThemeDelegate); + photoPreviewLayout = new ChatAttachAlertPhotoLayoutPreview(this, getContext(), (parentThemeDelegate != null ? parentThemeDelegate : resourcesProvider)); photoPreviewLayout.bringToFront(); } showLayout(currentAttachLayout == photoPreviewLayout ? photoLayout : photoPreviewLayout); @@ -3053,7 +3127,13 @@ private void openAudioLayout(boolean show) { } if (audioLayout == null) { layouts[3] = audioLayout = new ChatAttachAlertAudioLayout(this, getContext(), resourcesProvider); - audioLayout.setDelegate((audios, caption, notify, scheduleDate) -> ((ChatActivity) baseFragment).sendAudio(audios, caption, notify, scheduleDate)); + audioLayout.setDelegate((audios, caption, notify, scheduleDate) -> { + if (baseFragment != null && baseFragment instanceof ChatActivity) { + ((ChatActivity) baseFragment).sendAudio(audios, caption, notify, scheduleDate); + } else if (delegate != null) { + delegate.sendAudio(audios, caption, notify, scheduleDate); + } + }); } if (baseFragment instanceof ChatActivity) { ChatActivity chatActivity = (ChatActivity) baseFragment; @@ -3065,6 +3145,18 @@ private void openAudioLayout(boolean show) { } } + public void openColorsLayout() { + if (colorsLayout == null) { + colorsLayout = new ChatAttachAlertColorsLayout(this, getContext(), resourcesProvider); + colorsLayout.setDelegate((wallpaper -> { + if (delegate != null) { + delegate.onWallpaperSelected(wallpaper); + } + })); + } + showLayout(colorsLayout); + } + private void openDocumentsLayout(boolean show) { if (!documentsEnabled) { if (show) { @@ -3078,7 +3170,9 @@ private void openDocumentsLayout(boolean show) { documentLayout.setDelegate(new ChatAttachAlertDocumentLayout.DocumentSelectActivityDelegate() { @Override public void didSelectFiles(ArrayList files, String caption, ArrayList fmessages, boolean notify, int scheduleDate) { - if (baseFragment instanceof ChatAttachAlertDocumentLayout.DocumentSelectActivityDelegate) { + if (documentsDelegate != null) { + documentsDelegate.didSelectFiles(files, caption, fmessages, notify, scheduleDate); + } else if (baseFragment instanceof ChatAttachAlertDocumentLayout.DocumentSelectActivityDelegate) { ((ChatAttachAlertDocumentLayout.DocumentSelectActivityDelegate) baseFragment).didSelectFiles(files, caption, fmessages, notify, scheduleDate); } else if (baseFragment instanceof PassportActivity) { ((PassportActivity) baseFragment).didSelectFiles(files, caption, notify, scheduleDate); @@ -3087,7 +3181,9 @@ public void didSelectFiles(ArrayList files, String caption, ArrayList photos, boolean notify, int scheduleDate) { - if (baseFragment instanceof ChatActivity) { + if (documentsDelegate != null) { + documentsDelegate.didSelectPhotos(photos, notify, scheduleDate); + } else if (baseFragment instanceof ChatActivity) { ((ChatActivity) baseFragment).didSelectPhotos(photos, notify, scheduleDate); } else if (baseFragment instanceof PassportActivity) { ((PassportActivity) baseFragment).didSelectPhotos(photos, notify, scheduleDate); @@ -3096,7 +3192,9 @@ public void didSelectPhotos(ArrayList photo @Override public void startDocumentSelectActivity() { - if (baseFragment instanceof ChatAttachAlertDocumentLayout.DocumentSelectActivityDelegate) { + if (documentsDelegate != null) { + documentsDelegate.startDocumentSelectActivity(); + } else if (baseFragment instanceof ChatAttachAlertDocumentLayout.DocumentSelectActivityDelegate) { ((ChatAttachAlertDocumentLayout.DocumentSelectActivityDelegate) baseFragment).startDocumentSelectActivity(); } else if (baseFragment instanceof PassportActivity) { ((PassportActivity) baseFragment).startDocumentSelectActivity(); @@ -3115,7 +3213,7 @@ public void startMusicSelectActivity() { documentLayout.setMaxSelectedFiles(currentChat != null && !ChatObject.hasAdminRights(currentChat) && currentChat.slowmode_enabled || editingMessageObject != null ? 1 : -1); } else { documentLayout.setMaxSelectedFiles(maxSelectedPhotos); - documentLayout.setCanSelectOnlyImageFiles(!isSoundPicker && !isEmojiPicker); + documentLayout.setCanSelectOnlyImageFiles(!isSoundPicker && !isEmojiPicker && !allowEnterCaption); } documentLayout.isSoundPicker = isSoundPicker; documentLayout.isEmojiPicker = isEmojiPicker; @@ -3483,8 +3581,10 @@ public void checkColors() { selectedMenuItem.setPopupItemsColor(getThemedColor(Theme.key_actionBarDefaultSubmenuItem), true); selectedMenuItem.redrawPopup(getThemedColor(Theme.key_actionBarDefaultSubmenuBackground)); - searchItem.setIconColor(forceDarkTheme ? getThemedColor(Theme.key_voipgroup_actionBarItems) : getThemedColor(Theme.key_dialogTextBlack)); - Theme.setDrawableColor(searchItem.getBackground(), forceDarkTheme ? getThemedColor(Theme.key_voipgroup_actionBarItemsSelector) : getThemedColor(Theme.key_dialogButtonSelector)); + if (searchItem != null) { + searchItem.setIconColor(forceDarkTheme ? getThemedColor(Theme.key_voipgroup_actionBarItems) : getThemedColor(Theme.key_dialogTextBlack)); + Theme.setDrawableColor(searchItem.getBackground(), forceDarkTheme ? getThemedColor(Theme.key_voipgroup_actionBarItemsSelector) : getThemedColor(Theme.key_dialogButtonSelector)); + } commentTextView.updateColors(); @@ -3528,6 +3628,15 @@ public void checkColors() { layouts[a].checkColors(); } } + + if (Build.VERSION.SDK_INT >= 30) { + navBarColorKey = -1; + navBarColor = getThemedColor(Theme.key_dialogBackgroundGray); + AndroidUtilities.setNavigationBarColor(getWindow(), getThemedColor(Theme.key_dialogBackground), false); + AndroidUtilities.setLightNavigationBar(getWindow(), AndroidUtilities.computePerceivedBrightness(navBarColor) > 0.721); + } else { + fixNavigationBar(getThemedColor(Theme.key_dialogBackground)); + } } @Override @@ -3593,6 +3702,9 @@ private int getScrollOffsetY(int idx) { private void updateSelectedPosition(int idx) { float moveProgress; AttachAlertLayout layout = idx == 0 ? currentAttachLayout : nextAttachLayout; + if (layout == null || layout.getVisibility() != View.VISIBLE) { + return; + } int scrollOffset = getScrollOffsetY(idx); int t = scrollOffset - backgroundPaddingTop; float toMove; @@ -3626,8 +3738,18 @@ private void updateSelectedPosition(int idx) { } else { selectedMenuItem.setTranslationY(ActionBar.getCurrentActionBarHeight() - AndroidUtilities.dp(4) - AndroidUtilities.dp(37 + finalMove) + currentPanTranslationY); } - searchItem.setTranslationY(ActionBar.getCurrentActionBarHeight() - AndroidUtilities.dp(4) - AndroidUtilities.dp(37 + finalMove) + currentPanTranslationY); - headerView.setTranslationY(baseSelectedTextViewTranslationY = scrollOffset - AndroidUtilities.dp(25 + finalMove * moveProgress) + offset + currentPanTranslationY); + float swapOffset = 0; + if (isPhotoPicker && openTransitionFinished) { + if (nextAttachLayout != null && currentAttachLayout != null) { + swapOffset = Math.min(nextAttachLayout.getTranslationY(), currentAttachLayout.getTranslationY()); + } else if (nextAttachLayout != null) { + swapOffset = nextAttachLayout.getTranslationY(); + } + } + if (searchItem != null) { + searchItem.setTranslationY(ActionBar.getCurrentActionBarHeight() - AndroidUtilities.dp(4) - AndroidUtilities.dp(37 + finalMove) + currentPanTranslationY); + } + headerView.setTranslationY(baseSelectedTextViewTranslationY = scrollOffset - AndroidUtilities.dp(25 + finalMove * moveProgress) + offset + currentPanTranslationY + swapOffset); if (pollLayout != null && layout == pollLayout) { if (AndroidUtilities.isTablet()) { finalMove = 63; @@ -3648,8 +3770,8 @@ private void updateActionBarVisibility(boolean show, boolean animated) { actionBarAnimation = null; } - boolean needsSearchItem = avatarSearch || currentAttachLayout == photoLayout && !menuShowed && baseFragment instanceof ChatActivity && ((ChatActivity) baseFragment).allowSendGifs() && ((ChatActivity) baseFragment).allowSendPhotos(); - boolean needMoreItem = avatarPicker != 0 || !menuShowed && currentAttachLayout == photoLayout && (photosEnabled || videosEnabled); + boolean needsSearchItem = searchItem != null && (avatarSearch || currentAttachLayout == photoLayout && !menuShowed && baseFragment instanceof ChatActivity && ((ChatActivity) baseFragment).allowSendGifs() && ((ChatActivity) baseFragment).allowSendPhotos()); + boolean needMoreItem = !isPhotoPicker && (avatarPicker != 0 || !menuShowed) && currentAttachLayout == photoLayout && (photosEnabled || videosEnabled); if (currentAttachLayout == restrictedLayout) { needsSearchItem = false; needMoreItem = false; @@ -3694,7 +3816,9 @@ public void onAnimationEnd(Animator animation) { buttonsRecyclerView.setVisibility(View.INVISIBLE); } } else { - searchItem.setVisibility(View.INVISIBLE); + if (searchItem != null) { + searchItem.setVisibility(View.INVISIBLE); + } if (avatarPicker != 0 || !menuShowed) { selectedMenuItem.setVisibility(View.INVISIBLE); } @@ -3723,7 +3847,9 @@ public void onAnimationCancel(Animator animation) { selectedMenuItem.setAlpha(show ? 1.0f : 0.0f); } if (!show) { - searchItem.setVisibility(View.INVISIBLE); + if (searchItem != null) { + searchItem.setVisibility(View.INVISIBLE); + } if (avatarPicker != 0 || !menuShowed) { selectedMenuItem.setVisibility(View.INVISIBLE); } @@ -3742,6 +3868,7 @@ protected void updateLayout(AttachAlertLayout layout, boolean animated, int dy) return; } boolean show = layout == currentAttachLayout && newOffset <= layout.getButtonsHideOffset(); + pinnedToTop = show; if (currentAttachLayout != photoPreviewLayout && keyboardVisible && animated && !(currentAttachLayout instanceof ChatAttachAlertBotWebViewLayout)) { animated = false; } @@ -3802,14 +3929,14 @@ public void updateCountButton(int animated) { menuAnimator.cancel(); menuAnimator = null; } - boolean needsSearchItem = actionBar.getTag() != null && baseFragment instanceof ChatActivity && ((ChatActivity) baseFragment).allowSendGifs(); + boolean needsSearchItem = searchItem != null && actionBar.getTag() != null && baseFragment instanceof ChatActivity && ((ChatActivity) baseFragment).allowSendGifs(); if (menuShowed) { if (avatarPicker == 0) { selectedMenuItem.setVisibility(View.VISIBLE); } headerView.setVisibility(View.VISIBLE); } else { - if (actionBar.getTag() != null) { + if (actionBar.getTag() != null && searchItem != null) { searchItem.setVisibility(View.VISIBLE); } } @@ -3821,7 +3948,7 @@ public void updateCountButton(int animated) { if (needsSearchItem) { searchItem.setAlpha(menuShowed ? 0.0f : 1.0f); } - if (menuShowed) { + if (menuShowed && searchItem != null) { searchItem.setVisibility(View.INVISIBLE); } } else { @@ -3844,7 +3971,7 @@ public void onAnimationEnd(Animator animation) { selectedMenuItem.setVisibility(View.INVISIBLE); } headerView.setVisibility(View.INVISIBLE); - } else { + } else if (searchItem != null) { searchItem.setVisibility(View.INVISIBLE); } } @@ -3860,10 +3987,6 @@ public void setDelegate(ChatAttachViewDelegate chatAttachViewDelegate) { } public void init() { - if (baseFragment == null) { - return; - } - botButtonWasVisible = false; botButtonProgressWasVisible = false; botMainButtonOffsetY = 0; @@ -3895,7 +4018,11 @@ public void init() { pollsEnabled = user != null && user.bot; } } else { - commentTextView.setVisibility(View.INVISIBLE); + if (allowEnterCaption) { + commentTextView.setVisibility(View.VISIBLE); + } else { + commentTextView.setVisibility(View.INVISIBLE); + } } photoLayout.onInit(videosEnabled, photosEnabled, documentsEnabled); commentTextView.hidePopup(true); @@ -3964,7 +4091,7 @@ public void onDestroy() { NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.reloadInlineHints); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.attachMenuBotsDidLoad); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.currentUserPremiumStatusChanged); - baseFragment = null; + destroyed = true; if (commentTextView != null) { commentTextView.onDestroy(); } @@ -4002,12 +4129,16 @@ public void setAllowDrawContent(boolean value) { currentAttachLayout.onContainerTranslationUpdated(currentPanTranslationY); } + public void setStories(boolean value) { + stories = value; + } + public void setAvatarPicker(int type, boolean search) { avatarPicker = type; avatarSearch = search; if (avatarPicker != 0) { typeButtonsAvailable = false; - if (currentAttachLayout == null) { + if (currentAttachLayout == null || currentAttachLayout == photoLayout) { buttonsRecyclerView.setVisibility(View.GONE); shadow.setVisibility(View.GONE); } @@ -4172,6 +4303,9 @@ public void notifyDataSetChanged() { if (!(baseFragment instanceof ChatActivity)) { galleryButton = buttonsCount++; documentButton = buttonsCount++; + if (allowEnterCaption) { + musicButton = buttonsCount++; + } } else if (editingMessageObject != null) { if ((editingMessageObject.isMusic() || editingMessageObject.isDocument()) && editingMessageObject.hasValidGroupId()) { if (editingMessageObject.isMusic()) { @@ -4331,13 +4465,17 @@ public void dismiss() { AndroidUtilities.hideKeyboard(commentTextView.getEditText()); } botAttachLayouts.clear(); - if (!allowPassConfirmationAlert && baseFragment != null && currentAttachLayout.getSelectedItemsCount() > 0) { + BaseFragment baseFragment = this.baseFragment; + if (baseFragment == null) { + baseFragment = LaunchActivity.getLastFragment(); + } + if (!allowPassConfirmationAlert && baseFragment != null && currentAttachLayout.getSelectedItemsCount() > 0 && !isPhotoPicker) { if (confirmationAlertShown) { return; } confirmationAlertShown = true; AlertDialog dialog = - new AlertDialog.Builder(baseFragment.getParentActivity(), parentThemeDelegate) + new AlertDialog.Builder(baseFragment.getParentActivity(), resourcesProvider) .setTitle(LocaleController.getString("DiscardSelectionAlertTitle", R.string.DiscardSelectionAlertTitle)) .setMessage(LocaleController.getString("DiscardSelectionAlertMessage", R.string.DiscardSelectionAlertMessage)) .setPositiveButton(LocaleController.getString("PassportDiscard", R.string.PassportDiscard), (dialogInterface, i) -> { @@ -4361,7 +4499,7 @@ public void dismiss() { dialog.show(); TextView button = (TextView) dialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(getThemedColor(Theme.key_dialogTextRed)); + button.setTextColor(getThemedColor(Theme.key_text_RedBold)); } return; } @@ -4408,7 +4546,7 @@ public void dismiss() { } } AndroidUtilities.setNavigationBarColor(getWindow(), ColorUtils.setAlphaComponent(getThemedColor(Theme.key_windowBackgroundGray), 0), true, tcolor -> { - navBarColorKey = null; + navBarColorKey = -1; navBarColor = tcolor; containerView.invalidate(); }); @@ -4444,4 +4582,12 @@ public EditTextEmoji getCommentTextView() { public ChatAttachAlertDocumentLayout getDocumentLayout() { return documentLayout; } + + public void setAllowEnterCaption(boolean allowEnterCaption) { + this.allowEnterCaption = allowEnterCaption; + } + + public void setDocumentsDelegate(ChatAttachAlertDocumentLayout.DocumentSelectActivityDelegate documentsDelegate) { + this.documentsDelegate = documentsDelegate; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertBotWebViewLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertBotWebViewLayout.java index 9e6a6dc58b..0994178191 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertBotWebViewLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertBotWebViewLayout.java @@ -37,6 +37,7 @@ import org.telegram.messenger.MessagesController; import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; +import org.telegram.messenger.SendMessagesHelper; import org.telegram.messenger.UserObject; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLRPC; @@ -92,7 +93,7 @@ public class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert prolongWebView.query_id = queryId; prolongWebView.silent = silent; if (replyToMsgId != 0) { - prolongWebView.reply_to_msg_id = replyToMsgId; + prolongWebView.reply_to = SendMessagesHelper.creteReplyInput(replyToMsgId); prolongWebView.flags |= 1; } @@ -248,7 +249,7 @@ public boolean onCheckDismissByUser() { .create(); dialog.show(); TextView textView = (TextView) dialog.getButton(AlertDialog.BUTTON_POSITIVE); - textView.setTextColor(getThemedColor(Theme.key_dialogTextRed)); + textView.setTextColor(getThemedColor(Theme.key_text_RedBold)); return false; } else { parentAlert.dismiss(); @@ -454,7 +455,7 @@ public void requestWebView(int currentAccount, long peerId, long botId, boolean } if (replyToMsgId != 0) { - req.reply_to_msg_id = replyToMsgId; + req.reply_to = SendMessagesHelper.creteReplyInput(replyToMsgId); req.flags |= 1; } @@ -986,9 +987,8 @@ public WebProgressView(Context context, Theme.ResourcesProvider resourcesProvide bluePaint.setStrokeCap(Paint.Cap.ROUND); } - protected int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + protected int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertColorsLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertColorsLayout.java new file mode 100644 index 0000000000..a9db7d254e --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertColorsLayout.java @@ -0,0 +1,283 @@ +package org.telegram.ui.Components; + +import android.content.Context; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; + +import androidx.core.util.Consumer; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.LinearSmoothScroller; +import androidx.recyclerview.widget.RecyclerView; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.R; +import org.telegram.ui.ActionBar.ActionBar; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Cells.WallpaperCell; +import org.telegram.ui.PhotoViewer; +import org.telegram.ui.WallpapersListActivity; + +import java.util.ArrayList; + +public class ChatAttachAlertColorsLayout extends ChatAttachAlert.AttachAlertLayout { + + public RecyclerListView gridView; + private int itemSize = AndroidUtilities.dp(80); + GridLayoutManager layoutManager; + Adapter adapter; + private int itemsPerRow = 3; + Consumer wallpaperConsumer; + + + public ChatAttachAlertColorsLayout(ChatAttachAlert alert, Context context, Theme.ResourcesProvider resourcesProvider) { + super(alert, context, resourcesProvider); + + gridView = new RecyclerListView(context, resourcesProvider) { + @Override + public boolean onTouchEvent(MotionEvent e) { + if (e.getAction() == MotionEvent.ACTION_DOWN && e.getY() < parentAlert.scrollOffsetY[0] - AndroidUtilities.dp(80)) { + return false; + } + return super.onTouchEvent(e); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent e) { + if (e.getAction() == MotionEvent.ACTION_DOWN && e.getY() < parentAlert.scrollOffsetY[0] - AndroidUtilities.dp(80)) { + return false; + } + return super.onInterceptTouchEvent(e); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + PhotoViewer.getInstance().checkCurrentImageVisibility(); + } + }; + gridView.setAdapter(adapter = new Adapter(context)); + gridView.setClipToPadding(false); + gridView.setItemAnimator(null); + gridView.setLayoutAnimation(null); + gridView.setVerticalScrollBarEnabled(false); + gridView.setGlowColor(getThemedColor(Theme.key_dialogScrollGlow)); + addView(gridView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + gridView.setOnScrollListener(new RecyclerView.OnScrollListener() { + + boolean parentPinnedToTop; + + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + if (gridView.getChildCount() <= 0) { + return; + } + parentAlert.updateLayout(ChatAttachAlertColorsLayout.this, true, dy); + } + + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + if (newState == RecyclerView.SCROLL_STATE_IDLE) { + int offset = AndroidUtilities.dp(13) + (parentAlert.selectedMenuItem != null ? AndroidUtilities.dp(parentAlert.selectedMenuItem.getAlpha() * 26) : 0); + int backgroundPaddingTop = parentAlert.getBackgroundPaddingTop(); + int top = parentAlert.scrollOffsetY[0] - backgroundPaddingTop - offset; + if (top + backgroundPaddingTop < ActionBar.getCurrentActionBarHeight()) { + RecyclerListView.Holder holder = (RecyclerListView.Holder) gridView.findViewHolderForAdapterPosition(0); + if (holder != null && holder.itemView.getTop() > AndroidUtilities.dp(7)) { + gridView.smoothScrollBy(0, holder.itemView.getTop() - AndroidUtilities.dp(7)); + } + } + } + } + }); + layoutManager = new GridLayoutManager(context, itemSize) { + @Override + public boolean supportsPredictiveItemAnimations() { + return false; + } + + @Override + public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) { + LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext()) { + @Override + public int calculateDyToMakeVisible(View view, int snapPreference) { + int dy = super.calculateDyToMakeVisible(view, snapPreference); + dy -= (gridView.getPaddingTop() - AndroidUtilities.dp(7)); + return dy; + } + + @Override + protected int calculateTimeForDeceleration(int dx) { + return super.calculateTimeForDeceleration(dx) * 2; + } + }; + linearSmoothScroller.setTargetPosition(position); + startSmoothScroll(linearSmoothScroller); + } + }; + layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { + @Override + public int getSpanSize(int position) { +// if (position == adapter.getItemCount() - 1) { +// return layoutManager.getSpanCount(); +// } + return itemSize + (position % itemsPerRow != itemsPerRow - 1 ? AndroidUtilities.dp(5) : 0); + } + }); + gridView.setLayoutManager(layoutManager); + } + + @Override + void scrollToTop() { + gridView.smoothScrollToPosition(0); + } + + @Override + int needsActionBar() { + return 1; + } + + @Override + int getListTopPadding() { + return gridView.getPaddingTop(); + } + + public int currentItemTop = 0; + + @Override + int getCurrentItemTop() { + if (gridView.getChildCount() <= 0) { + gridView.setTopGlowOffset(currentItemTop = gridView.getPaddingTop()); + return Integer.MAX_VALUE; + } + View child = gridView.getChildAt(0); + RecyclerListView.Holder holder = (RecyclerListView.Holder) gridView.findContainingViewHolder(child); + int top = child.getTop(); + int newOffset = AndroidUtilities.dp(7); + if (top >= AndroidUtilities.dp(7) && holder != null && holder.getAdapterPosition() == 0) { + newOffset = top; + } + gridView.setTopGlowOffset(newOffset); + return currentItemTop = newOffset; + } + + @Override + int getFirstOffset() { + return getListTopPadding() + AndroidUtilities.dp(56); + } + + @Override + public void setTranslationY(float translationY) { + super.setTranslationY(translationY); + parentAlert.getSheetContainer().invalidate(); + invalidate(); + } + + @Override + public void onPreMeasure(int availableWidth, int availableHeight) { + if (AndroidUtilities.isTablet()) { + itemsPerRow = 4; + } else if (AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y) { + itemsPerRow = 4; + } else { + itemsPerRow = 3; + } + LayoutParams layoutParams = (LayoutParams) getLayoutParams(); + layoutParams.topMargin = ActionBar.getCurrentActionBarHeight(); + + int itemSize = (availableWidth - AndroidUtilities.dp(6 * 2) - AndroidUtilities.dp(5 * 2)) / itemsPerRow; + if (this.itemSize != itemSize) { + this.itemSize = itemSize; + adapter.notifyDataSetChanged(); + } + layoutManager.setSpanCount(Math.max(1, itemSize * itemsPerRow + AndroidUtilities.dp(5) * (itemsPerRow - 1))); + int rows = (int) Math.ceil((adapter.getItemCount() - 1) / (float) itemsPerRow); + int contentSize = rows * itemSize + (rows - 1) * AndroidUtilities.dp(5); + int newSize = Math.max(0, availableHeight - contentSize - ActionBar.getCurrentActionBarHeight() - AndroidUtilities.dp(48 + 12)); + int paddingTop; + if (!AndroidUtilities.isTablet() && AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y) { + paddingTop = (int) (availableHeight / 3.5f); + } else { + paddingTop = (availableHeight / 5 * 2); + } + paddingTop -= AndroidUtilities.dp(52); + if (paddingTop < 0) { + paddingTop = 0; + } + if (gridView.getPaddingTop() != paddingTop) { + gridView.setPadding(AndroidUtilities.dp(6), paddingTop, AndroidUtilities.dp(6), AndroidUtilities.dp(48)); + } + } + + + public void setDelegate(Consumer wallpaperConsumer) { + this.wallpaperConsumer = wallpaperConsumer; + } + + public void updateColors(boolean isDark) { + adapter.wallpapers.clear(); + WallpapersListActivity.fillDefaultColors(adapter.wallpapers, isDark); + adapter.notifyDataSetChanged(); + } + + private class Adapter extends RecyclerListView.SelectionAdapter { + + + private Context mContext; + private final ArrayList wallpapers = new ArrayList<>(); + + public Adapter(Context context) { + mContext = context; + } + + @Override + public boolean isEnabled(RecyclerView.ViewHolder holder) { + return holder.getItemViewType() == 0; + } + + @Override + public int getItemCount() { + return wallpapers.size(); + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + WallpaperCell view = new WallpaperCell(mContext, 1) { + @Override + protected void onWallpaperClick(Object wallPaper, int index) { + if (wallpaperConsumer != null) { + wallpaperConsumer.accept(wallPaper); + } + } + }; + view.drawStubBackground = false; + return new RecyclerListView.Holder(view); + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + WallpaperCell wallpaperCell = (WallpaperCell) holder.itemView; + wallpaperCell.setParams(1, false, false); + wallpaperCell.setSize(itemSize); + + Object object = wallpapers.get(position); + wallpaperCell.setWallpaper(WallpapersListActivity.TYPE_COLOR, 0, object, null, null, false); + } + + @Override + public int getItemViewType(int position) { + return 0; + } + } + + @Override + void onShow(ChatAttachAlert.AttachAlertLayout previousLayout) { + try { + parentAlert.actionBar.getTitleTextView().setBuildFullLayout(true); + } catch (Exception ignore) {} + parentAlert.actionBar.setTitle(LocaleController.getString("SelectColor", R.string.SelectColor)); + layoutManager.scrollToPositionWithOffset(0, 0); + } +} + diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertContactsLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertContactsLayout.java index 093111d370..ae36a6060c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertContactsLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertContactsLayout.java @@ -271,9 +271,8 @@ protected void onDraw(Canvas canvas) { } } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + protected int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertDocumentLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertDocumentLayout.java index b31358841f..48e35498c6 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertDocumentLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertDocumentLayout.java @@ -46,6 +46,7 @@ import org.telegram.messenger.AccountInstance; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.BuildVars; import org.telegram.messenger.FileLoader; @@ -463,7 +464,7 @@ public void onOpenInPressed() { } }); fragment.setMaxSelectedPhotos(maxSelectedFiles, false); - parentAlert.baseFragment.presentFragment(fragment); + parentAlert.presentFragment(fragment); parentAlert.dismiss(true); } else if (item.icon == R.drawable.files_music) { if (delegate != null) { @@ -967,6 +968,7 @@ public void loadRecentFiles() { private void checkDirectory(File rootDir) { File[] files = rootDir.listFiles(); + File storiesDir = FileLoader.checkDirectory(FileLoader.MEDIA_DIR_STORIES); if (files != null) { for (int a = 0; a < files.length; a++) { File file = files[a]; @@ -974,6 +976,9 @@ private void checkDirectory(File rootDir) { checkDirectory(file); continue; } + if (file.equals(storiesDir)) { + continue; + } ListItem item = new ListItem(); item.title = file.getName(); item.file = file; @@ -1190,11 +1195,17 @@ private boolean listFiles(File dir) { } currentDir = dir; listAdapter.items.clear(); + + File storiesDir = FileLoader.checkDirectory(FileLoader.MEDIA_DIR_STORIES); for (int a = 0; a < files.length; a++) { File file = files[a]; if (file.getName().indexOf('.') == 0) { continue; } + + if (file.equals(storiesDir)) { + continue; + } ListItem item = new ListItem(); item.title = file.getName(); item.file = file; @@ -1462,7 +1473,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType break; case 2: view = new ShadowSectionCell(mContext); - Drawable drawable = Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow); + Drawable drawable = Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow); CombinedDrawable combinedDrawable = new CombinedDrawable(new ColorDrawable(getThemedColor(Theme.key_windowBackgroundGray)), drawable); combinedDrawable.setFullsize(true); view.setBackgroundDrawable(combinedDrawable); @@ -1546,7 +1557,7 @@ public class SearchAdapter extends RecyclerListView.SectionsAdapter { private boolean isLoading; private int requestIndex; private boolean firstLoading = true; - private int animationIndex = -1; + private AnimationNotificationsLocker notificationsLocker = new AnimationNotificationsLocker(); private boolean endReached; private Runnable clearCurrentResultsRunnable = new Runnable() { @@ -1999,10 +2010,10 @@ public boolean onPreDraw() { animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - accountInstance.getNotificationCenter().onAnimationFinish(animationIndex); + notificationsLocker.unlock(); } }); - animationIndex = accountInstance.getNotificationCenter().setAnimationInProgress(animationIndex, null); + notificationsLocker.lock(); animatorSet.start(); if (finalProgressView != null && finalProgressView.getParent() == null) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPhotoLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPhotoLayout.java index 3cf681f73e..62b0aec2bf 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPhotoLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPhotoLayout.java @@ -22,13 +22,11 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.Outline; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.hardware.Camera; import android.media.MediaMetadataRetriever; @@ -37,7 +35,7 @@ import android.provider.MediaStore; import android.provider.Settings; import android.text.TextUtils; -import android.util.Log; +import android.util.Pair; import android.util.TypedValue; import android.view.Gravity; import android.view.KeyEvent; @@ -70,6 +68,7 @@ import androidx.recyclerview.widget.RecyclerView; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.BuildVars; import org.telegram.messenger.ChatObject; @@ -96,13 +95,16 @@ import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.ActionBarMenuItem; import org.telegram.ui.ActionBar.ActionBarMenuSubItem; +import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.BasePermissionsActivity; import org.telegram.ui.Cells.PhotoAttachCameraCell; import org.telegram.ui.Cells.PhotoAttachPermissionCell; import org.telegram.ui.Cells.PhotoAttachPhotoCell; import org.telegram.ui.ChatActivity; +import org.telegram.ui.LaunchActivity; import org.telegram.ui.PhotoViewer; +import org.telegram.ui.Stories.recorder.AlbumButton; import java.io.ByteArrayOutputStream; import java.io.File; @@ -110,6 +112,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -122,6 +125,8 @@ public class ChatAttachAlertPhotoLayout extends ChatAttachAlert.AttachAlertLayout implements NotificationCenter.NotificationCenterDelegate { private static final int VIEW_TYPE_AVATAR_CONSTRUCTOR = 4; + private static final int SHOW_FAST_SCROLL_MIN_COUNT = 30; + private final boolean needCamera; private RecyclerListView cameraPhotoRecyclerView; private LinearLayoutManager cameraPhotoLayoutManager; @@ -182,7 +187,7 @@ public class ChatAttachAlertPhotoLayout extends ChatAttachAlert.AttachAlertLayou private static ArrayList cameraPhotos = new ArrayList<>(); private static HashMap selectedPhotos = new HashMap<>(); private static ArrayList selectedPhotosOrder = new ArrayList<>(); - private static int lastImageId = -1; + public static int lastImageId = -1; private boolean cancelTakingPhotos; private boolean checkCameraWhenShown; @@ -236,11 +241,11 @@ public class ChatAttachAlertPhotoLayout extends ChatAttachAlert.AttachAlertLayou protected ActionBarMenuSubItem previewItem; boolean forceDarkTheme; - private int animationIndex = -1; + private AnimationNotificationsLocker notificationsLocker = new AnimationNotificationsLocker(); private boolean showAvatarConstructor; public void updateAvatarPicker() { - showAvatarConstructor = parentAlert.avatarPicker != 0; + showAvatarConstructor = parentAlert.avatarPicker != 0 && !parentAlert.isPhotoPicker; } private class BasePhotoProvider extends PhotoViewer.EmptyPhotoViewerProvider { @@ -410,7 +415,7 @@ public void updatePhotoAtIndex(int index) { if (photoEntry.thumbPath != null) { cell.getImageView().setImage(photoEntry.thumbPath, null, Theme.chat_attachEmptyDrawable); } else if (photoEntry.path != null) { - cell.getImageView().setOrientation(photoEntry.orientation, true); + cell.getImageView().setOrientation(photoEntry.orientation, photoEntry.invert, true); if (photoEntry.isVideo) { cell.getImageView().setImage("vthumb://" + photoEntry.imageId + ":" + photoEntry.path, null, Theme.chat_attachEmptyDrawable); } else { @@ -507,6 +512,10 @@ public void sendButtonPressed(int index, VideoEditedInfo videoEditedInfo, boolea } } parentAlert.delegate.didPressedButton(7, true, notify, scheduleDate, forceDocument); + selectedPhotos.clear(); + cameraPhotos.clear(); + selectedPhotosOrder.clear(); + selectedPhotos.clear(); } }; @@ -547,7 +556,7 @@ private MediaController.PhotoEntry getPhotoEntryAtPosition(int position) { return (MediaController.PhotoEntry) cameraPhotos.get(position); } position -= cameraCount; - if (position < selectedAlbumEntry.photos.size()) { + if (selectedAlbumEntry != null && position < selectedAlbumEntry.photos.size()) { return selectedAlbumEntry.photos.get(position); } return null; @@ -572,9 +581,10 @@ protected ArrayList getAllPhotosArray() { return arrayList; } - public ChatAttachAlertPhotoLayout(ChatAttachAlert alert, Context context, boolean forceDarkTheme, Theme.ResourcesProvider resourcesProvider) { + public ChatAttachAlertPhotoLayout(ChatAttachAlert alert, Context context, boolean forceDarkTheme, boolean needCamera, Theme.ResourcesProvider resourcesProvider) { super(alert, context, resourcesProvider); this.forceDarkTheme = forceDarkTheme; + this.needCamera = needCamera; NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.albumsDidLoad); NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.cameraInitied); FrameLayout container = alert.getContainer(); @@ -643,7 +653,11 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) { PhotoViewer.getInstance().checkCurrentImageVisibility(); } }; - gridView.setAdapter(adapter = new PhotoAttachAdapter(context, true)); + gridView.setFastScrollEnabled(RecyclerListView.FastScroll.DATE_TYPE); + gridView.setFastScrollVisible(true); + gridView.getFastScroll().setAlpha(0f); + gridView.getFastScroll().usePadding = false; + gridView.setAdapter(adapter = new PhotoAttachAdapter(context, needCamera)); adapter.createCache(); gridView.setClipToPadding(false); gridView.setItemAnimator(null); @@ -652,13 +666,22 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) { gridView.setGlowColor(getThemedColor(Theme.key_dialogScrollGlow)); addView(gridView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); gridView.setOnScrollListener(new RecyclerView.OnScrollListener() { + + boolean parentPinnedToTop; @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { if (gridView.getChildCount() <= 0) { return; } parentAlert.updateLayout(ChatAttachAlertPhotoLayout.this, true, dy); - + if (adapter.getTotalItemsCount() > SHOW_FAST_SCROLL_MIN_COUNT) { + if (parentPinnedToTop != parentAlert.pinnedToTop) { + parentPinnedToTop = parentAlert.pinnedToTop; + gridView.getFastScroll().animate().alpha(parentPinnedToTop ? 1f : 0f).setDuration(100).start(); + } + } else { + gridView.getFastScroll().setAlpha(0); + } if (dy != 0) { checkCameraViewPosition(); } @@ -715,13 +738,20 @@ public int getSpanSize(int position) { }); gridView.setLayoutManager(layoutManager); gridView.setOnItemClickListener((view, position, x, y) -> { - if (!mediaEnabled || parentAlert.baseFragment == null || parentAlert.baseFragment.getParentActivity() == null) { + if (!mediaEnabled || parentAlert.destroyed) { + return; + } + BaseFragment fragment = parentAlert.baseFragment; + if (fragment == null) { + fragment = LaunchActivity.getLastFragment(); + } + if (fragment == null) { return; } if (Build.VERSION.SDK_INT >= 23) { if (adapter.needCamera && selectedAlbumEntry == galleryAlbumEntry && position == 0 && noCameraPermissions) { try { - parentAlert.baseFragment.getParentActivity().requestPermissions(new String[]{Manifest.permission.CAMERA}, 18); + fragment.getParentActivity().requestPermissions(new String[]{Manifest.permission.CAMERA}, 18); } catch (Exception ignore) { } @@ -739,8 +769,8 @@ public int getSpanSize(int position) { return; } } - if (position != 0 || selectedAlbumEntry != galleryAlbumEntry) { - if (selectedAlbumEntry == galleryAlbumEntry) { + if (position != 0 || !needCamera || selectedAlbumEntry != galleryAlbumEntry) { + if (selectedAlbumEntry == galleryAlbumEntry && needCamera) { position--; } if (showAvatarConstructor) { @@ -757,7 +787,7 @@ public int getSpanSize(int position) { if (position < 0 || position >= arrayList.size()) { return; } - PhotoViewer.getInstance().setParentActivity(parentAlert.baseFragment, resourcesProvider); + PhotoViewer.getInstance().setParentActivity(fragment, resourcesProvider); PhotoViewer.getInstance().setParentAlert(parentAlert); PhotoViewer.getInstance().setMaxSelectedPhotos(parentAlert.maxSelectedPhotos, parentAlert.allowOrder); ChatActivity chatActivity; @@ -768,12 +798,15 @@ public int getSpanSize(int position) { } else if (parentAlert.baseFragment instanceof ChatActivity) { chatActivity = (ChatActivity) parentAlert.baseFragment; type = 0; + } else if (parentAlert.allowEnterCaption) { + chatActivity = null; + type = 0; } else { chatActivity = null; type = 4; } if (!parentAlert.delegate.needEnterComment()) { - AndroidUtilities.hideKeyboard(parentAlert.baseFragment.getFragmentView().findFocus()); + AndroidUtilities.hideKeyboard(fragment.getFragmentView().findFocus()); AndroidUtilities.hideKeyboard(parentAlert.getContainer().findFocus()); } if (selectedPhotos.size() > 0 && selectedPhotosOrder.size() > 0) { @@ -807,10 +840,19 @@ public int getSpanSize(int position) { setCurrentSpoilerVisible(position, false); } int finalPosition = position; - AndroidUtilities.runOnUIThread(()->{ - PhotoViewer.getInstance().openPhotoForSelect(arrayList, finalPosition, type, false, photoViewerProvider, chatActivity); - + BaseFragment finalFragment = fragment; + AndroidUtilities.runOnUIThread(()-> { + int avatarType = type; + if (parentAlert.isPhotoPicker) { + PhotoViewer.getInstance().setParentActivity(finalFragment); + PhotoViewer.getInstance().setMaxSelectedPhotos(0, false); + avatarType = PhotoViewer.SELECT_TYPE_WALLPAPER;; + } + PhotoViewer.getInstance().openPhotoForSelect(arrayList, finalPosition, avatarType, false, photoViewerProvider, chatActivity); PhotoViewer.getInstance().setAvatarFor(parentAlert.getAvatarFor()); + if (parentAlert.isPhotoPicker) { + PhotoViewer.getInstance().closePhotoAfterSelect = false; + } if (captionForAllMedia()) { PhotoViewer.getInstance().setCaption(parentAlert.getCommentTextView().getText()); } @@ -1007,7 +1049,14 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto @Override public boolean shutterLongPressed() { - if (parentAlert.avatarPicker != 2 && !(parentAlert.baseFragment instanceof ChatActivity) || takingPhoto || parentAlert.baseFragment == null || parentAlert.baseFragment.getParentActivity() == null || cameraView == null) { + if (parentAlert.avatarPicker != 2 && !(parentAlert.baseFragment instanceof ChatActivity) || takingPhoto || parentAlert.destroyed || cameraView == null) { + return false; + } + BaseFragment baseFragment = parentAlert.baseFragment; + if (baseFragment == null) { + baseFragment = LaunchActivity.getLastFragment(); + } + if (baseFragment == null || baseFragment.getParentActivity() == null) { return false; } if (!videoEnabled) { @@ -1015,9 +1064,9 @@ public boolean shutterLongPressed() { return false; } if (Build.VERSION.SDK_INT >= 23) { - if (parentAlert.baseFragment.getParentActivity().checkSelfPermission(Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { + if (getContext().checkSelfPermission(Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { requestingPermissions = true; - parentAlert.baseFragment.getParentActivity().requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO}, 21); + baseFragment.getParentActivity().requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO}, 21); return false; } } @@ -1038,9 +1087,9 @@ public boolean shutterLongPressed() { recordTime.setText(AndroidUtilities.formatLongDuration(videoRecordTime)); AndroidUtilities.runOnUIThread(videoRecordRunnable, 1000); }; - AndroidUtilities.lockOrientation(parentAlert.baseFragment.getParentActivity()); + AndroidUtilities.lockOrientation(baseFragment.getParentActivity()); CameraController.getInstance().recordVideo(cameraView.getCameraSession(), outputFile, parentAlert.avatarPicker != 0, (thumbPath, duration) -> { - if (outputFile == null || parentAlert.baseFragment == null || cameraView == null) { + if (outputFile == null || parentAlert.destroyed || cameraView == null) { return; } mediaFromExternalCamera = false; @@ -1053,7 +1102,7 @@ public boolean shutterLongPressed() { height = options.outHeight; } catch (Exception ignore) {} MediaController.PhotoEntry photoEntry = new MediaController.PhotoEntry(0, lastImageId--, 0, outputFile.getAbsolutePath(), 0, true, width, height, 0); - photoEntry.duration = (int) duration; + photoEntry.duration = (int) (duration / 1000f); photoEntry.thumbPath = thumbPath; if (parentAlert.avatarPicker != 0 && cameraView.isFrontface()) { photoEntry.cropState = new MediaController.CropState(); @@ -1096,29 +1145,12 @@ public void shutterReleased() { final File cameraFile = AndroidUtilities.generatePicturePath(parentAlert.baseFragment instanceof ChatActivity && ((ChatActivity) parentAlert.baseFragment).isSecretChat(), null); final boolean sameTakePictureOrientation = cameraView.getCameraSession().isSameTakePictureOrientation(); cameraView.getCameraSession().setFlipFront(parentAlert.baseFragment instanceof ChatActivity || parentAlert.avatarPicker == 2); - takingPhoto = CameraController.getInstance().takePicture(cameraFile, cameraView.getCameraSession(), () -> { + takingPhoto = CameraController.getInstance().takePicture(cameraFile, false, cameraView.getCameraSession(), (orientation) -> { takingPhoto = false; - if (cameraFile == null || parentAlert.baseFragment == null) { + if (cameraFile == null || parentAlert.destroyed) { return; } - int orientation = 0; - try { - ExifInterface ei = new ExifInterface(cameraFile.getAbsolutePath()); - int exif = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); - switch (exif) { - case ExifInterface.ORIENTATION_ROTATE_90: - orientation = 90; - break; - case ExifInterface.ORIENTATION_ROTATE_180: - orientation = 180; - break; - case ExifInterface.ORIENTATION_ROTATE_270: - orientation = 270; - break; - } - } catch (Exception e) { - FileLog.e(e); - } +// Pair orientation = AndroidUtilities.getImageOrientation(cameraFile); mediaFromExternalCamera = false; int width = 0, height = 0; try { @@ -1128,7 +1160,7 @@ public void shutterReleased() { width = options.outWidth; height = options.outHeight; } catch (Exception ignore) {} - MediaController.PhotoEntry photoEntry = new MediaController.PhotoEntry(0, lastImageId--, 0, cameraFile.getAbsolutePath(), orientation, false, width, height, 0); + MediaController.PhotoEntry photoEntry = new MediaController.PhotoEntry(0, lastImageId--, 0, cameraFile.getAbsolutePath(), orientation == -1 ? 0 : orientation, false, width, height, 0); photoEntry.canDeleteAfter = true; openPhotoViewer(photoEntry, sameTakePictureOrientation, false); }); @@ -1171,7 +1203,6 @@ public boolean onTranslationChanged(float x, float y) { } canSaveCameraPreview = false; cameraView.switchCamera(); - cameraView.startSwitchingAnimation(); ObjectAnimator animator = ObjectAnimator.ofFloat(switchCameraButton, View.SCALE_X, 0.0f).setDuration(100); animator.addListener(new AnimatorListenerAdapter() { @Override @@ -1506,7 +1537,7 @@ private void updateAlbumsDropDown() { dropDownContainer.removeAllSubItems(); if (mediaEnabled) { ArrayList albums; - if (parentAlert.baseFragment instanceof ChatActivity || parentAlert.avatarPicker == 2) { + if (shouldLoadAllMedia()) { albums = MediaController.allMediaAlbums; } else { albums = MediaController.allPhotoAlbums; @@ -1537,7 +1568,14 @@ private void updateAlbumsDropDown() { } else { dropDown.setCompoundDrawablesWithIntrinsicBounds(null, null, dropDownDrawable, null); for (int a = 0, N = dropDownAlbums.size(); a < N; a++) { - dropDownContainer.addSubItem(10 + a, dropDownAlbums.get(a).bucketName); + MediaController.AlbumEntry album = dropDownAlbums.get(a); + AlbumButton btn = new AlbumButton(getContext(), album.coverPhoto, album.bucketName, album.photos.size(), resourcesProvider); + dropDownContainer.getPopupLayout().addView(btn); + final int i = a + 10; + btn.setOnClickListener(v -> { + parentAlert.actionBar.getActionBarMenuOnItemClick().onItemClick(i); + dropDownContainer.toggleSubMenu(); + }); } } } @@ -1659,7 +1697,7 @@ private boolean processTouchEvent(MotionEvent event) { } private void resetRecordState() { - if (parentAlert.baseFragment == null) { + if (parentAlert.destroyed) { return; } @@ -1672,7 +1710,7 @@ private void resetRecordState() { AndroidUtilities.cancelRunOnUIThread(videoRecordRunnable); videoRecordRunnable = null; - AndroidUtilities.unlockOrientation(parentAlert.baseFragment.getParentActivity()); + AndroidUtilities.unlockOrientation(AndroidUtilities.findActivity(getContext())); } protected void openPhotoViewer(MediaController.PhotoEntry entry, final boolean sameTakePictureOrientation, boolean external) { @@ -1698,7 +1736,15 @@ protected void openPhotoViewer(MediaController.PhotoEntry entry, final boolean s return; } cancelTakingPhotos = true; - PhotoViewer.getInstance().setParentActivity(parentAlert.baseFragment.getParentActivity(), resourcesProvider); + + BaseFragment fragment = parentAlert.baseFragment; + if (fragment == null) { + fragment = LaunchActivity.getLastFragment(); + } + if (fragment == null) { + return; + } + PhotoViewer.getInstance().setParentActivity(fragment.getParentActivity(), resourcesProvider); PhotoViewer.getInstance().setParentAlert(parentAlert); PhotoViewer.getInstance().setMaxSelectedPhotos(parentAlert.maxSelectedPhotos, parentAlert.allowOrder); @@ -1798,7 +1844,7 @@ public void needAddMorePhotos() { @Override public void sendButtonPressed(int index, VideoEditedInfo videoEditedInfo, boolean notify, int scheduleDate, boolean forceDocument) { - if (cameraPhotos.isEmpty() || parentAlert.baseFragment == null) { + if (cameraPhotos.isEmpty() || parentAlert.destroyed) { return; } if (videoEditedInfo != null && index >= 0 && index < cameraPhotos.size()) { @@ -1827,10 +1873,10 @@ public void sendButtonPressed(int index, VideoEditedInfo videoEditedInfo, boolea @Override public boolean scaleToFill() { - if (parentAlert.baseFragment == null || parentAlert.baseFragment.getParentActivity() == null) { + if (parentAlert.destroyed) { return false; } - int locked = Settings.System.getInt(parentAlert.baseFragment.getParentActivity().getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 0); + int locked = Settings.System.getInt(getContext().getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 0); return sameTakePictureOrientation || locked == 1; } @@ -1968,16 +2014,23 @@ private void setCameraFlashModeIcon(ImageView imageView, String mode) { } public void checkCamera(boolean request) { - if (parentAlert.baseFragment == null || parentAlert.baseFragment.getParentActivity() == null) { + if (parentAlert.destroyed || !needCamera) { return; } boolean old = deviceHasGoodCamera; boolean old2 = noCameraPermissions; + BaseFragment fragment = parentAlert.baseFragment; + if (fragment == null) { + fragment = LaunchActivity.getLastFragment(); + } + if (fragment == null || fragment.getParentActivity() == null) { + return; + } if (!SharedConfig.inappCamera) { deviceHasGoodCamera = false; } else { if (Build.VERSION.SDK_INT >= 23) { - if (noCameraPermissions = (parentAlert.baseFragment.getParentActivity().checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED)) { + if (noCameraPermissions = (fragment.getParentActivity().checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED)) { if (request) { try { parentAlert.baseFragment.getParentActivity().requestPermissions(new String[]{Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE}, 17); @@ -2007,7 +2060,7 @@ public void checkCamera(boolean request) { if ((old != deviceHasGoodCamera || old2 != noCameraPermissions) && adapter != null) { adapter.notifyDataSetChanged(); } - if (parentAlert.isShowing() && deviceHasGoodCamera && parentAlert.baseFragment != null && parentAlert.getBackDrawable().getAlpha() != 0 && !cameraOpened && !NekoConfig.disableInstantCamera.Bool()) { + if (!parentAlert.destroyed && parentAlert.isShowing() && deviceHasGoodCamera && parentAlert.getBackDrawable().getAlpha() != 0 && !cameraOpened && !NekoConfig.disableInstantCamera.Bool()) { showCamera(); } } @@ -2018,7 +2071,7 @@ private void openCamera(boolean animated) { return; } cameraView.initTexture(); - if (parentAlert.avatarPicker == 2 || parentAlert.baseFragment instanceof ChatActivity) { + if (shouldLoadAllMedia()) { tooltipTextView.setVisibility(VISIBLE); } else { tooltipTextView.setVisibility(GONE); @@ -2051,7 +2104,7 @@ private void openCamera(boolean animated) { if (animated) { setCameraOpenProgress(0); cameraAnimationInProgress = true; - animationIndex = NotificationCenter.getInstance(parentAlert.currentAccount).setAnimationInProgress(animationIndex, null); + notificationsLocker.lock(); ArrayList animators = new ArrayList<>(); animators.add(ObjectAnimator.ofFloat(this, "cameraOpenProgress", 0.0f, 1.0f)); animators.add(ObjectAnimator.ofFloat(cameraPanel, View.ALPHA, 1.0f)); @@ -2070,7 +2123,7 @@ private void openCamera(boolean animated) { animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animator) { - NotificationCenter.getInstance(parentAlert.currentAccount).onAnimationFinish(animationIndex); + notificationsLocker.unlock(); cameraAnimationInProgress = false; if (cameraView != null) { if (Build.VERSION.SDK_INT >= 21) { @@ -2119,7 +2172,7 @@ public void onAnimationEnd(Animator animator) { public void loadGalleryPhotos() { MediaController.AlbumEntry albumEntry; - if (parentAlert.baseFragment instanceof ChatActivity || parentAlert.avatarPicker == 2) { + if (shouldLoadAllMedia()) { albumEntry = MediaController.allMediaAlbumEntry; } else { albumEntry = MediaController.allPhotosAlbumEntry; @@ -2129,13 +2182,17 @@ public void loadGalleryPhotos() { } } + private boolean shouldLoadAllMedia() { + return !parentAlert.isPhotoPicker && (parentAlert.baseFragment instanceof ChatActivity || parentAlert.avatarPicker == 2); + } + public void showCamera() { if (parentAlert.paused || !mediaEnabled) { return; } if (cameraView == null) { final boolean lazy = !LiteMode.isEnabled(LiteMode.FLAGS_CHAT); - cameraView = new CameraView(parentAlert.baseFragment.getParentActivity(), parentAlert.openWithFrontFaceCamera, lazy) { + cameraView = new CameraView(getContext(), parentAlert.openWithFrontFaceCamera, lazy) { Bulletin.Delegate bulletinDelegate = new Bulletin.Delegate() { @Override @@ -2263,7 +2320,7 @@ public void onAnimationCancel(Animator animation) { }); if (cameraIcon == null) { - cameraIcon = new FrameLayout(parentAlert.baseFragment.getParentActivity()) { + cameraIcon = new FrameLayout(getContext()) { @Override protected void onDraw(Canvas canvas) { int maxY = (int) Math.min(parentAlert.getCommentTextViewTop() + currentPanTranslationY + parentAlert.getContainerView().getTranslationY() - cameraView.getTranslationY(), getMeasuredHeight()); @@ -2375,32 +2432,14 @@ private void saveLastCameraBitmap() { } public void onActivityResultFragment(int requestCode, Intent data, String currentPicturePath) { - if (parentAlert.baseFragment == null || parentAlert.baseFragment.getParentActivity() == null) { + if (parentAlert.destroyed) { return; } mediaFromExternalCamera = true; if (requestCode == 0) { PhotoViewer.getInstance().setParentActivity(parentAlert.baseFragment.getParentActivity(), resourcesProvider); PhotoViewer.getInstance().setMaxSelectedPhotos(parentAlert.maxSelectedPhotos, parentAlert.allowOrder); - final ArrayList arrayList = new ArrayList<>(); - int orientation = 0; - try { - ExifInterface ei = new ExifInterface(currentPicturePath); - int exif = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); - switch (exif) { - case ExifInterface.ORIENTATION_ROTATE_90: - orientation = 90; - break; - case ExifInterface.ORIENTATION_ROTATE_180: - orientation = 180; - break; - case ExifInterface.ORIENTATION_ROTATE_270: - orientation = 270; - break; - } - } catch (Exception e) { - FileLog.e(e); - } + Pair orientation = AndroidUtilities.getImageOrientation(currentPicturePath); int width = 0, height = 0; try { BitmapFactory.Options options = new BitmapFactory.Options(); @@ -2409,7 +2448,7 @@ public void onActivityResultFragment(int requestCode, Intent data, String curren width = options.outWidth; height = options.outHeight; } catch (Exception ignore) {} - MediaController.PhotoEntry photoEntry = new MediaController.PhotoEntry(0, lastImageId--, 0, currentPicturePath, orientation, false, width, height, 0); + MediaController.PhotoEntry photoEntry = new MediaController.PhotoEntry(0, lastImageId--, 0, currentPicturePath, orientation.first, false, width, height, 0).setOrientation(orientation); photoEntry.canDeleteAfter = true; openPhotoViewer(photoEntry, false, true); } else if (requestCode == 2) { @@ -2518,7 +2557,7 @@ public void closeCamera(boolean animated) { } } - animationIndex = NotificationCenter.getInstance(parentAlert.currentAccount).setAnimationInProgress(animationIndex, null); + notificationsLocker.lock(); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.playTogether(animators); animatorSet.setDuration(220); @@ -2526,7 +2565,7 @@ public void closeCamera(boolean animated) { animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animator) { - NotificationCenter.getInstance(parentAlert.currentAccount).onAnimationFinish(animationIndex); + notificationsLocker.unlock(); cameraExpanded = false; parentAlert.getWindow().clearFlags(FLAG_KEEP_SCREEN_ON); setCameraOpenProgress(0f); @@ -3193,7 +3232,7 @@ void checkColors() { if (cameraIcon != null) { cameraIcon.invalidate(); } - String textColor = forceDarkTheme ? Theme.key_voipgroup_actionBarItems : Theme.key_dialogTextBlack; + int textColor = forceDarkTheme ? Theme.key_voipgroup_actionBarItems : Theme.key_dialogTextBlack; Theme.setDrawableColor(cameraDrawable, getThemedColor(Theme.key_dialogCameraIcon)); progressView.setTextColor(getThemedColor(Theme.key_emptyListPlaceholder)); gridView.setGlowColor(getThemedColor(Theme.key_dialogScrollGlow)); @@ -3240,14 +3279,14 @@ void onInit(boolean hasVideo, boolean hasPhoto, boolean hasDocuments) { } } } else { - if (parentAlert.avatarPicker == 2) { + if (shouldLoadAllMedia()) { galleryAlbumEntry = MediaController.allMediaAlbumEntry; } else { galleryAlbumEntry = MediaController.allPhotosAlbumEntry; } } if (Build.VERSION.SDK_INT >= 23) { - noGalleryPermissions = parentAlert.baseFragment.getParentActivity().checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED; + noGalleryPermissions = parentAlert.getContext().checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED; } if (galleryAlbumEntry != null) { for (int a = 0; a < Math.min(100, galleryAlbumEntry.photos.size()); a++) { @@ -3731,14 +3770,14 @@ protected boolean onCustomLayout(View view, int left, int top, int right, int bo public void didReceivedNotification(int id, int account, Object... args) { if (id == NotificationCenter.albumsDidLoad) { if (adapter != null) { - if (parentAlert.baseFragment instanceof ChatActivity || parentAlert.avatarPicker == 2) { + if (shouldLoadAllMedia()) { galleryAlbumEntry = MediaController.allMediaAlbumEntry; } else { galleryAlbumEntry = MediaController.allPhotosAlbumEntry; } if (selectedAlbumEntry == null) { selectedAlbumEntry = galleryAlbumEntry; - } else { + } else if (shouldLoadAllMedia()) { for (int a = 0; a < MediaController.allMediaAlbums.size(); a++) { MediaController.AlbumEntry entry = MediaController.allMediaAlbums.get(a); if (entry.bucketId == selectedAlbumEntry.bucketId && entry.videoOnly == selectedAlbumEntry.videoOnly) { @@ -3772,17 +3811,18 @@ public void didReceivedNotification(int id, int account, Object... args) { } } - private class PhotoAttachAdapter extends RecyclerListView.SelectionAdapter { + private class PhotoAttachAdapter extends RecyclerListView.FastScrollAdapter { private Context mContext; private boolean needCamera; private ArrayList viewsCache = new ArrayList<>(8); private int itemsCount; + private int photosStartRow; + private int photosEndRow; public PhotoAttachAdapter(Context context, boolean camera) { mContext = context; needCamera = camera; - } public void createCache() { @@ -4007,7 +4047,7 @@ public int getItemCount() { return 1; } int count = 0; - if (needCamera && selectedAlbumEntry == galleryAlbumEntry) { + if (needCamera && selectedAlbumEntry != null && galleryAlbumEntry != null && selectedAlbumEntry.bucketId == galleryAlbumEntry.bucketId) { count++; } if (showAvatarConstructor) { @@ -4017,10 +4057,12 @@ public int getItemCount() { count++; count++; // NekoX: Additional Open In picker } + photosStartRow = count; count += cameraPhotos.size(); if (selectedAlbumEntry != null) { count += selectedAlbumEntry.photos.size(); } + photosEndRow = count; if (this == adapter) { count++; } @@ -4061,5 +4103,65 @@ public void notifyDataSetChanged() { progressView.setVisibility(getItemCount() == 1 && selectedAlbumEntry == null || !mediaEnabled ? View.VISIBLE : View.INVISIBLE); } } + + @Override + public float getScrollProgress(RecyclerListView listView) { + int parentCount = itemsPerRow; + int cellCount = (int) Math.ceil(itemsCount / (float) parentCount); + if (listView.getChildCount() == 0) { + return 0; + } + int cellHeight = listView.getChildAt(0).getMeasuredHeight(); + View firstChild = listView.getChildAt(0); + int firstPosition = listView.getChildAdapterPosition(firstChild); + if (firstPosition < 0) { + return 0; + } + float childTop = firstChild.getTop(); + float listH = listView.getMeasuredHeight(); + float scrollY = (firstPosition / parentCount) * cellHeight - childTop; + return Utilities.clamp(scrollY / (((float) cellCount) * cellHeight - listH), 1f, 0f); + } + + @Override + public String getLetter(int position) { + MediaController.PhotoEntry entry = getPhoto(position); + if (entry == null) { + if (position <= photosStartRow) { + if (!cameraPhotos.isEmpty()) { + entry = (MediaController.PhotoEntry) cameraPhotos.get(0); + } else if (selectedAlbumEntry != null && selectedAlbumEntry.photos != null) { + entry = selectedAlbumEntry.photos.get(0); + } + } else if (!selectedAlbumEntry.photos.isEmpty()){ + entry = selectedAlbumEntry.photos.get(selectedAlbumEntry.photos.size() - 1); + } + } + if (entry != null) { + long date = entry.dateTaken; + if (Build.VERSION.SDK_INT <= 28) { + date /= 1000; + } + return LocaleController.formatYearMont(date, true); + } + return ""; + } + + @Override + public boolean fastScrollIsVisible(RecyclerListView listView) { + return (!cameraPhotos.isEmpty() || selectedAlbumEntry != null && !selectedAlbumEntry.photos.isEmpty()) && parentAlert.pinnedToTop && getTotalItemsCount() > SHOW_FAST_SCROLL_MIN_COUNT; + } + + @Override + public void getPositionForScrollProgress(RecyclerListView listView, float progress, int[] position) { + int viewHeight = listView.getChildAt(0).getMeasuredHeight(); + int totalHeight = (int) (Math.ceil(getTotalItemsCount() / (float) itemsPerRow) * viewHeight); + int listHeight = listView.getMeasuredHeight(); + position[0] = (int) ((progress * (totalHeight - listHeight)) / viewHeight) * itemsPerRow; + position[1] = (int) ((progress * (totalHeight - listHeight)) % viewHeight) + listView.getPaddingTop(); + if (position[0] == 0 && position[1] < getListTopPadding()) { + position[1] = getListTopPadding(); + } + } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPhotoLayoutPreview.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPhotoLayoutPreview.java index 8fc31c7389..7c4be4ab77 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPhotoLayoutPreview.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPhotoLayoutPreview.java @@ -56,10 +56,12 @@ import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.ActionBarMenuItem; +import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Cells.ChatActionCell; import org.telegram.ui.ChatActivity; import org.telegram.ui.Components.spoilers.SpoilerEffect; +import org.telegram.ui.LaunchActivity; import org.telegram.ui.PhotoViewer; import java.util.ArrayList; @@ -79,7 +81,7 @@ public float getPreviewScale() { return AndroidUtilities.displaySize.y > AndroidUtilities.displaySize.x ? .8f : .45f; } - private ChatActivity.ThemeDelegate themeDelegate; + private Theme.ResourcesProvider themeDelegate; public RecyclerListView listView; private LinearLayoutManager layoutManager; @@ -98,7 +100,7 @@ public float getPreviewScale() { private Drawable videoPlayImage; - public ChatAttachAlertPhotoLayoutPreview(ChatAttachAlert alert, Context context, ChatActivity.ThemeDelegate themeDelegate) { + public ChatAttachAlertPhotoLayoutPreview(ChatAttachAlert alert, Context context, Theme.ResourcesProvider themeDelegate) { super(alert, context, themeDelegate); this.themeDelegate = themeDelegate; @@ -1825,11 +1827,15 @@ public boolean onTouchEvent(MotionEvent event) { chatActivity = null; type = 4; } + BaseFragment fragment = parentAlert.baseFragment; + if (fragment == null) { + fragment = LaunchActivity.getLastFragment(); + } if (!parentAlert.delegate.needEnterComment()) { - AndroidUtilities.hideKeyboard(parentAlert.baseFragment.getFragmentView().findFocus()); + AndroidUtilities.hideKeyboard(fragment.getFragmentView().findFocus()); AndroidUtilities.hideKeyboard(parentAlert.getContainer().findFocus()); } - PhotoViewer.getInstance().setParentActivity(parentAlert.baseFragment, resourcesProvider); + PhotoViewer.getInstance().setParentActivity(fragment, resourcesProvider); PhotoViewer.getInstance().setParentAlert(parentAlert); PhotoViewer.getInstance().setMaxSelectedPhotos(parentAlert.maxSelectedPhotos, parentAlert.allowOrder); photoViewerProvider.init(arrayList); @@ -2559,7 +2565,7 @@ public boolean draw(Canvas canvas) { } public Drawable getThemedDrawable(String drawableKey) { - Drawable drawable = themeDelegate.getDrawable(drawableKey); + Drawable drawable = themeDelegate != null ? themeDelegate.getDrawable(drawableKey) : null; return drawable != null ? drawable : Theme.getThemeDrawable(drawableKey); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPollLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPollLayout.java index 7b10305adf..5981134960 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPollLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPollLayout.java @@ -639,6 +639,9 @@ private void updateRows() { @Override void onShow(ChatAttachAlert.AttachAlertLayout previousLayout) { + try { + parentAlert.actionBar.getTitleTextView().setBuildFullLayout(true); + } catch (Exception ignore) {} if (quizOnly == 1) { parentAlert.actionBar.setTitle(LocaleController.getString("NewQuiz", R.string.NewQuiz)); } else { @@ -709,7 +712,7 @@ private void setTextLeft(View cell, int index) { if (left <= max - max * 0.7f) { textCell.setText2(String.format("%d", left)); SimpleTextView textView = textCell.getTextView2(); - String key = left < 0 ? Theme.key_windowBackgroundWhiteRedText5 : Theme.key_windowBackgroundWhiteGrayText3; + int key = left < 0 ? Theme.key_text_RedRegular : Theme.key_windowBackgroundWhiteGrayText3; textView.setTextColor(getThemedColor(key)); textView.setTag(key); } else { @@ -768,7 +771,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } case 2: { TextInfoPrivacyCell cell = (TextInfoPrivacyCell) holder.itemView; - Drawable drawable = Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow); + Drawable drawable = Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow); CombinedDrawable combinedDrawable = new CombinedDrawable(new ColorDrawable(getThemedColor(Theme.key_windowBackgroundGray)), drawable); combinedDrawable.setFullsize(true); cell.setBackgroundDrawable(combinedDrawable); @@ -789,7 +792,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } case 3: { TextCell textCell = (TextCell) holder.itemView; - textCell.setColors(null, Theme.key_windowBackgroundWhiteBlueText4); + textCell.setColors(-1, Theme.key_windowBackgroundWhiteBlueText4); Drawable drawable1 = mContext.getResources().getDrawable(R.drawable.poll_add_circle); Drawable drawable2 = mContext.getResources().getDrawable(R.drawable.poll_add_plus); drawable1.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_switchTrackChecked), PorterDuff.Mode.SRC_IN)); @@ -877,7 +880,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType break; case 1: view = new ShadowSectionCell(mContext); - Drawable drawable = Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow); + Drawable drawable = Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow); CombinedDrawable combinedDrawable = new CombinedDrawable(new ColorDrawable(getThemedColor(Theme.key_windowBackgroundGray)), drawable); combinedDrawable.setFullsize(true); view.setBackgroundDrawable(combinedDrawable); @@ -940,7 +943,7 @@ protected void onActionModeStart(EditTextBoldCursor editText, ActionMode actionM if (menu.findItem(android.R.id.copy) == null) { return; } - ((ChatActivity) parentAlert.baseFragment).fillActionModeMenu(menu); + ChatActivity.fillActionModeMenu(menu, ((ChatActivity) parentAlert.baseFragment).getCurrentEncryptedChat()); } } }; @@ -1212,7 +1215,7 @@ public ArrayList getThemeDescriptions() { themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText4)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{HeaderCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueHeader)); - themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{HeaderCell.class}, new String[]{"textView2"}, null, null, null, Theme.key_windowBackgroundWhiteRedText5)); + themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{HeaderCell.class}, new String[]{"textView2"}, null, null, null, Theme.key_text_RedRegular)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{HeaderCell.class}, new String[]{"textView2"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText3)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_TEXTCOLOR, new Class[]{PollEditTextCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); @@ -1220,7 +1223,7 @@ public ArrayList getThemeDescriptions() { themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_HINTTEXTCOLOR, new Class[]{PollEditTextCell.class}, new String[]{"deleteImageView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayIcon)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_HINTTEXTCOLOR, new Class[]{PollEditTextCell.class}, new String[]{"moveImageView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayIcon)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_USEBACKGROUNDDRAWABLE | ThemeDescription.FLAG_DRAWABLESELECTEDSTATE, new Class[]{PollEditTextCell.class}, new String[]{"deleteImageView"}, null, null, null, Theme.key_stickers_menuSelector)); - themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{PollEditTextCell.class}, new String[]{"textView2"}, null, null, null, Theme.key_windowBackgroundWhiteRedText5)); + themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{PollEditTextCell.class}, new String[]{"textView2"}, null, null, null, Theme.key_text_RedRegular)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{PollEditTextCell.class}, new String[]{"checkBox"}, null, null, null, Theme.key_windowBackgroundWhiteGrayIcon)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{PollEditTextCell.class}, new String[]{"checkBox"}, null, null, null, Theme.key_checkboxCheck)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAvatarContainer.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAvatarContainer.java index 8bc2b5817d..e595f08665 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAvatarContainer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAvatarContainer.java @@ -88,7 +88,7 @@ public class ChatAvatarContainer extends FrameLayout implements NotificationCent private int onlineCount = -1; private int currentConnectionState; private CharSequence lastSubtitle; - private String lastSubtitleColorKey; + private int lastSubtitleColorKey = -1; private Integer overrideSubtitleColor; private SharedMediaLayout.SharedMediaPreloader sharedMediaPreloader; @@ -768,62 +768,7 @@ public void onAnimationEnd(Animator animation) { } } else if (chat != null) { TLRPC.ChatFull info = parentFragment.getCurrentChatInfo(); - if (ChatObject.isChannel(chat)) { - if (info != null && info.participants_count != 0) { - if (chat.megagroup) { - if (onlineCount > 1) { - newSubtitle = String.format("%s, %s", LocaleController.formatPluralString("Members", info.participants_count), LocaleController.formatPluralString("OnlineCount", Math.min(onlineCount, info.participants_count))); - } else { - newSubtitle = LocaleController.formatPluralString("Members", info.participants_count); - } - } else { - int[] result = new int[1]; - boolean ignoreShort = AndroidUtilities.isAccessibilityScreenReaderEnabled(); - String shortNumber = ignoreShort ? String.valueOf(result[0] = info.participants_count) : LocaleController.formatShortNumber(info.participants_count, result); - if (chat.megagroup) { - newSubtitle = LocaleController.formatPluralString("Members", result[0]).replace(String.format("%d", result[0]), shortNumber); - } else { - newSubtitle = LocaleController.formatPluralString("Subscribers", result[0]).replace(String.format("%d", result[0]), shortNumber); - } - } - } else { - if (chat.megagroup) { - if (info == null) { - newSubtitle = LocaleController.getString("Loading", R.string.Loading).toLowerCase(); - } else { - if (chat.has_geo) { - newSubtitle = LocaleController.getString("MegaLocation", R.string.MegaLocation).toLowerCase(); - } else if (ChatObject.isPublic(chat)) { - newSubtitle = LocaleController.getString("MegaPublic", R.string.MegaPublic).toLowerCase(); - } else { - newSubtitle = LocaleController.getString("MegaPrivate", R.string.MegaPrivate).toLowerCase(); - } - } - } else { - if (ChatObject.isPublic(chat)) { - newSubtitle = LocaleController.getString("ChannelPublic", R.string.ChannelPublic).toLowerCase(); - } else { - newSubtitle = LocaleController.getString("ChannelPrivate", R.string.ChannelPrivate).toLowerCase(); - } - } - } - } else { - if (ChatObject.isKickedFromChat(chat)) { - newSubtitle = LocaleController.getString("YouWereKicked", R.string.YouWereKicked); - } else if (ChatObject.isLeftFromChat(chat)) { - newSubtitle = LocaleController.getString("YouLeft", R.string.YouLeft); - } else { - int count = chat.participants_count; - if (info != null && info.participants != null) { - count = info.participants.participants.size(); - } - if (onlineCount > 1 && count != 0) { - newSubtitle = String.format("%s, %s", LocaleController.formatPluralString("Members", count), LocaleController.formatPluralString("OnlineCount", onlineCount)); - } else { - newSubtitle = LocaleController.formatPluralString("Members", count); - } - } - } + newSubtitle = getChatSubtitle(chat, info, onlineCount); } else if (user != null) { TLRPC.User newUser = MessagesController.getInstance(currentAccount).getUser(user.id); if (newUser != null) { @@ -898,7 +843,68 @@ public void onAnimationEnd(Animator animation) { } } - public String getLastSubtitleColorKey() { + public static CharSequence getChatSubtitle(TLRPC.Chat chat, TLRPC.ChatFull info, int onlineCount) { + CharSequence newSubtitle = null; + if (ChatObject.isChannel(chat)) { + if (info != null && info.participants_count != 0) { + if (chat.megagroup) { + if (onlineCount > 1) { + newSubtitle = String.format("%s, %s", LocaleController.formatPluralString("Members", info.participants_count), LocaleController.formatPluralString("OnlineCount", Math.min(onlineCount, info.participants_count))); + } else { + newSubtitle = LocaleController.formatPluralString("Members", info.participants_count); + } + } else { + int[] result = new int[1]; + boolean ignoreShort = AndroidUtilities.isAccessibilityScreenReaderEnabled(); + String shortNumber = ignoreShort ? String.valueOf(result[0] = info.participants_count) : LocaleController.formatShortNumber(info.participants_count, result); + if (chat.megagroup) { + newSubtitle = LocaleController.formatPluralString("Members", result[0]).replace(String.format("%d", result[0]), shortNumber); + } else { + newSubtitle = LocaleController.formatPluralString("Subscribers", result[0]).replace(String.format("%d", result[0]), shortNumber); + } + } + } else { + if (chat.megagroup) { + if (info == null) { + newSubtitle = LocaleController.getString("Loading", R.string.Loading).toLowerCase(); + } else { + if (chat.has_geo) { + newSubtitle = LocaleController.getString("MegaLocation", R.string.MegaLocation).toLowerCase(); + } else if (ChatObject.isPublic(chat)) { + newSubtitle = LocaleController.getString("MegaPublic", R.string.MegaPublic).toLowerCase(); + } else { + newSubtitle = LocaleController.getString("MegaPrivate", R.string.MegaPrivate).toLowerCase(); + } + } + } else { + if (ChatObject.isPublic(chat)) { + newSubtitle = LocaleController.getString("ChannelPublic", R.string.ChannelPublic).toLowerCase(); + } else { + newSubtitle = LocaleController.getString("ChannelPrivate", R.string.ChannelPrivate).toLowerCase(); + } + } + } + } else { + if (ChatObject.isKickedFromChat(chat)) { + newSubtitle = LocaleController.getString("YouWereKicked", R.string.YouWereKicked); + } else if (ChatObject.isLeftFromChat(chat)) { + newSubtitle = LocaleController.getString("YouLeft", R.string.YouLeft); + } else { + int count = chat.participants_count; + if (info != null && info.participants != null) { + count = info.participants.participants.size(); + } + if (onlineCount > 1 && count != 0) { + newSubtitle = String.format("%s, %s", LocaleController.formatPluralString("Members", count), LocaleController.formatPluralString("OnlineCount", onlineCount)); + } else { + newSubtitle = LocaleController.formatPluralString("Members", count); + } + } + } + return newSubtitle; + } + + public int getLastSubtitleColorKey() { return lastSubtitleColorKey; } @@ -959,7 +965,7 @@ public void checkAndUpdateAvatar() { } else { avatarDrawable.setScaleSize(1f); if (avatarImageView != null) { - avatarImageView.imageReceiver.setForUserOrChat(user, avatarDrawable, null, true, VectorAvatarThumbDrawable.TYPE_STATIC); + avatarImageView.imageReceiver.setForUserOrChat(user, avatarDrawable, null, true, VectorAvatarThumbDrawable.TYPE_STATIC, false); } } } else if (chat != null) { @@ -1056,7 +1062,7 @@ private void updateCurrentConnectionState() { lastSubtitle = null; if (overrideSubtitleColor != null) { subtitleTextView.setTextColor(overrideSubtitleColor); - } else if (lastSubtitleColorKey != null) { + } else if (lastSubtitleColorKey >= 0) { subtitleTextView.setTextColor(getThemedColor(lastSubtitleColorKey)); subtitleTextView.setTag(lastSubtitleColorKey); } @@ -1100,9 +1106,8 @@ public BackupImageView getAvatarImageView() { return avatarImageView; } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } public void updateColors() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatBigEmptyView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatBigEmptyView.java index 5b12c392d7..ad94271d41 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatBigEmptyView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatBigEmptyView.java @@ -184,9 +184,8 @@ public void setStatusText(CharSequence text) { statusTextView.setText(text); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } private Paint getThemedPaint(String paintKey) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatGreetingsView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatGreetingsView.java index 4af0689535..acc2153fd4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatGreetingsView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatGreetingsView.java @@ -203,8 +203,7 @@ private void fetchSticker() { } } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatNotificationsPopupWrapper.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatNotificationsPopupWrapper.java index b8411b7eca..67f0190e75 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatNotificationsPopupWrapper.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatNotificationsPopupWrapper.java @@ -175,7 +175,7 @@ public void update(long dialogId, int topicId, HashSet topicExceptions) soundToggle.setVisibility(View.GONE); } else { muteUnmuteButton.setTextAndIcon(LocaleController.getString("MuteNotifications", R.string.MuteNotifications), R.drawable.msg_mute); - color = Theme.getColor(Theme.key_dialogTextRed); + color = Theme.getColor(Theme.key_text_RedBold); soundToggle.setVisibility(View.VISIBLE); boolean soundOn = MessagesController.getInstance(currentAccount).isDialogNotificationsSoundEnabled(dialogId, topicId); if (soundOn) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatScrimPopupContainerLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatScrimPopupContainerLayout.java index 839af67bb9..b74e5a08b1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatScrimPopupContainerLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatScrimPopupContainerLayout.java @@ -164,10 +164,10 @@ public void setExpandSize(float expandSize) { updateBottomViewPosition(); } - public void setPopupAlpha(float aplha) { - popupWindowLayout.setAlpha(aplha); + public void setPopupAlpha(float alpha) { + popupWindowLayout.setAlpha(alpha); if (bottomView != null) { - bottomView.setAlpha(aplha); + bottomView.setAlpha(alpha); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatThemeBottomSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatThemeBottomSheet.java index 5af5b2361f..1dcbe3cee9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatThemeBottomSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatThemeBottomSheet.java @@ -4,17 +4,21 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.SharedPreferences; import android.graphics.Bitmap; import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; +import android.graphics.Point; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.PorterDuffXfermode; import android.graphics.Shader; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.text.TextUtils; import android.util.TypedValue; @@ -29,6 +33,7 @@ import android.widget.Toast; import androidx.annotation.NonNull; +import androidx.core.graphics.ColorUtils; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearSmoothScroller; import androidx.recyclerview.widget.RecyclerView; @@ -38,7 +43,9 @@ import org.telegram.messenger.ChatThemeController; import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; +import org.telegram.messenger.ImageLoader; import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MediaController; import org.telegram.messenger.MediaDataController; import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; @@ -48,16 +55,23 @@ import org.telegram.tgnet.ResultCallback; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.AlertDialog; +import org.telegram.ui.ActionBar.BackDrawable; +import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.BottomSheet; import org.telegram.ui.ActionBar.EmojiThemes; import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.ActionBar.ThemeColors; import org.telegram.ui.ActionBar.ThemeDescription; import org.telegram.ui.Cells.DrawerProfileCell; import org.telegram.ui.Cells.ThemesHorizontalListCell; import org.telegram.ui.ChatActivity; +import org.telegram.ui.PhotoViewer; +import org.telegram.ui.ThemePreviewActivity; +import org.telegram.ui.WallpapersListActivity; import java.io.File; import java.io.FileInputStream; +import java.io.FileOutputStream; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; @@ -66,6 +80,11 @@ public class ChatThemeBottomSheet extends BottomSheet implements NotificationCenter.NotificationCenterDelegate { + private final ImageView backButtonView; + private final BackDrawable backButtonDrawable; + private TextView cancelOrResetTextView; + private TextView themeHintTextView; + private TLRPC.WallPaper currentWallpaper; private FrameLayout rootLayout; private final Adapter adapter; private final ChatActivity.ThemeDelegate themeDelegate; @@ -80,8 +99,8 @@ public class ChatThemeBottomSheet extends BottomSheet implements NotificationCen private final RLottieImageView darkThemeView; private final LinearSmoothScroller scroller; private final View applyButton; - private TextView applyTextView; - private TextView resetTextView; + private AnimatedTextView applyTextView; + private TextView chooseBackgroundTextView; private ChatThemeItem selectedItem; private boolean forceDark; private boolean isApplyClicked; @@ -91,20 +110,33 @@ public class ChatThemeBottomSheet extends BottomSheet implements NotificationCen private float changeDayNightViewProgress; private ValueAnimator changeDayNightViewAnimator; HintView hintView; + private boolean dataLoaded; + private EmojiThemes currentTheme; + ThemePreviewActivity overlayFragment; + public ChatAttachAlert chatAttachAlert; + private FrameLayout chatAttachButton; + private AnimatedTextView chatAttachButtonText; + public ChatThemeBottomSheet(final ChatActivity chatActivity, ChatActivity.ThemeDelegate themeDelegate) { super(chatActivity.getParentActivity(), true, themeDelegate); this.chatActivity = chatActivity; this.themeDelegate = themeDelegate; this.originalTheme = themeDelegate.getCurrentTheme(); + this.currentWallpaper = themeDelegate.getCurrentWallpaper(); this.originalIsDark = Theme.getActiveTheme().isDark(); adapter = new Adapter(currentAccount, themeDelegate, ThemeSmallPreviewView.TYPE_DEFAULT); setDimBehind(false); setCanDismissWithSwipe(false); setApplyBottomPadding(false); - drawNavigationBar = true; - - fixNavigationBar(); + if (Build.VERSION.SDK_INT >= 30) { + navBarColorKey = -1; + navBarColor = getThemedColor(Theme.key_dialogBackgroundGray); + AndroidUtilities.setNavigationBarColor(getWindow(), getThemedColor(Theme.key_dialogBackgroundGray), false); + AndroidUtilities.setLightNavigationBar(getWindow(), AndroidUtilities.computePerceivedBrightness(navBarColor) > 0.721); + } else { + fixNavigationBar(getThemedColor(Theme.key_dialogBackgroundGray)); + } rootLayout = new FrameLayout(getContext()); setCustomView(rootLayout); @@ -117,8 +149,23 @@ public ChatThemeBottomSheet(final ChatActivity chatActivity, ChatActivity.ThemeD titleView.setTextColor(getThemedColor(Theme.key_dialogTextBlack)); titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); titleView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - titleView.setPadding(AndroidUtilities.dp(21), AndroidUtilities.dp(6), AndroidUtilities.dp(21), AndroidUtilities.dp(8)); - rootLayout.addView(titleView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.START, 0, 0, 62, 0)); + titleView.setPadding(AndroidUtilities.dp(12), AndroidUtilities.dp(6), AndroidUtilities.dp(12), AndroidUtilities.dp(8)); + + backButtonView = new ImageView(getContext()); + int padding = AndroidUtilities.dp(10); + backButtonView.setPadding(padding, padding, padding, padding); + backButtonDrawable = new BackDrawable(false); + backButtonView.setImageDrawable(backButtonDrawable); + backButtonView.setOnClickListener(v -> { + if (hasChanges()) { + resetToPrimaryState(true); + updateState(true); + } else { + dismiss(); + } + }); + rootLayout.addView(backButtonView, LayoutHelper.createFrame(44, 44, Gravity.TOP | Gravity.START, 4, -2, 62, 12)); + rootLayout.addView(titleView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.START, 44, 0, 62, 0)); int drawableColor = getThemedColor(Theme.key_featuredStickers_addButton); int drawableSize = AndroidUtilities.dp(28); @@ -129,7 +176,7 @@ public ChatThemeBottomSheet(final ChatActivity chatActivity, ChatActivity.ThemeD darkThemeDrawable.setPlayInDirectionOfCustomEndFrame(true); darkThemeDrawable.setColorFilter(new PorterDuffColorFilter(drawableColor, PorterDuff.Mode.MULTIPLY)); - darkThemeView = new RLottieImageView(getContext()){ + darkThemeView = new RLottieImageView(getContext()) { @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); @@ -158,6 +205,7 @@ protected int calculateTimeForScrolling(int dx) { }; recyclerView = new RecyclerListView(getContext()); recyclerView.setAdapter(adapter); + recyclerView.setDrawSelection(false); recyclerView.setClipChildren(false); recyclerView.setClipToPadding(false); recyclerView.setHasFixedSize(true); @@ -170,19 +218,7 @@ protected int calculateTimeForScrolling(int dx) { return; } selectedItem = adapter.items.get(position); - isLightDarkChangeAnimation = false; - if (selectedItem.chatTheme == null || selectedItem.chatTheme.showAsDefaultStub) { - applyTextView.animate().alpha(0f).setDuration(300).start(); - resetTextView.animate().alpha(1f).setDuration(300).start(); - } else { - resetTextView.animate().alpha(0f).setDuration(300).start(); - applyTextView.animate().alpha(1f).setDuration(300).start(); - } - if (selectedItem.chatTheme.showAsDefaultStub) { - themeDelegate.setCurrentTheme(null, true, forceDark); - } else { - themeDelegate.setCurrentTheme(selectedItem.chatTheme, true, forceDark); - } + previewSelectedTheme(); adapter.setSelectedItem(position); containerView.postDelayed(new Runnable() { @Override @@ -207,6 +243,7 @@ public void run() { if (!adapter.items.get(position).chatTheme.showAsDefaultStub) { ((ThemeSmallPreviewView) view).playEmojiAnimation(); } + updateState(true); }); progressView = new FlickerLoadingView(getContext(), resourcesProvider); @@ -218,34 +255,148 @@ public void run() { applyButton = new View(getContext()); applyButton.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(6), getThemedColor(Theme.key_featuredStickers_addButton), getThemedColor(Theme.key_featuredStickers_addButtonPressed))); - applyButton.setEnabled(false); applyButton.setOnClickListener((view) -> applySelectedTheme()); rootLayout.addView(applyButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.START, 16, 162, 16, 16)); - resetTextView = new TextView(getContext()); - resetTextView.setAlpha(0f); - resetTextView.setEllipsize(TextUtils.TruncateAt.END); - resetTextView.setGravity(Gravity.CENTER); - resetTextView.setLines(1); - resetTextView.setSingleLine(true); - resetTextView.setText(themeDelegate.getCurrentTheme() == null ? LocaleController.getString("DoNoSetTheme", R.string.DoNoSetTheme) : LocaleController.getString("ChatResetTheme", R.string.ChatResetTheme)); - resetTextView.setTextColor(getThemedColor(Theme.key_featuredStickers_buttonText)); - resetTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); - resetTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - resetTextView.setVisibility(View.INVISIBLE); - rootLayout.addView(resetTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.START, 16, 162, 16, 16)); - - applyTextView = new TextView(getContext()); - applyTextView.setEllipsize(TextUtils.TruncateAt.END); + chooseBackgroundTextView = new TextView(getContext()); + chooseBackgroundTextView.setEllipsize(TextUtils.TruncateAt.END); + chooseBackgroundTextView.setGravity(Gravity.CENTER); + chooseBackgroundTextView.setLines(1); + chooseBackgroundTextView.setSingleLine(true); +// SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(); +// spannableStringBuilder +// .append("d ") +// .append(LocaleController.getString("ChooseBackgroundFromGallery", R.string.ChooseBackgroundFromGallery)); +// spannableStringBuilder.setSpan(new ColoredImageSpan(R.drawable.msg_button_wallpaper), 0, 1, 0); +// chooseBackgroundTextView.setText(spannableStringBuilder); + if (currentWallpaper == null) { + chooseBackgroundTextView.setText(LocaleController.getString("ChooseBackgroundFromGallery", R.string.ChooseBackgroundFromGallery)); + } else { + chooseBackgroundTextView.setText(LocaleController.getString("ChooseANewWallpaper", R.string.ChooseANewWallpaper)); + } + + chooseBackgroundTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); + chooseBackgroundTextView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + openGalleryForBackground(); + } + }); + rootLayout.addView(chooseBackgroundTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.START, 16, 162, 16, 16)); + + applyTextView = new AnimatedTextView(getContext(), true, true, true); + applyTextView.getDrawable().setEllipsizeByGradient(true); + applyTextView.adaptWidth = false; applyTextView.setGravity(Gravity.CENTER); - applyTextView.setLines(1); - applyTextView.setSingleLine(true); - applyTextView.setText(LocaleController.getString("ChatApplyTheme", R.string.ChatApplyTheme)); applyTextView.setTextColor(getThemedColor(Theme.key_featuredStickers_buttonText)); - applyTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); + applyTextView.setTextSize(AndroidUtilities.dp(15)); applyTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - applyTextView.setVisibility(View.INVISIBLE); rootLayout.addView(applyTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.START, 16, 162, 16, 16)); + + + if (currentWallpaper != null) { + cancelOrResetTextView = new TextView(getContext()); + cancelOrResetTextView.setEllipsize(TextUtils.TruncateAt.END); + cancelOrResetTextView.setGravity(Gravity.CENTER); + cancelOrResetTextView.setLines(1); + cancelOrResetTextView.setSingleLine(true); + cancelOrResetTextView.setText(LocaleController.getString("RestToDefaultBackground", R.string.RestToDefaultBackground)); + + cancelOrResetTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); + cancelOrResetTextView.setOnClickListener(v -> { + if (currentWallpaper != null) { + currentWallpaper = null; + dismiss(); + ChatThemeController.getInstance(currentAccount).clearWallpaper(chatActivity.getDialogId(), true); + } else { + dismiss(); + } + }); + + rootLayout.addView(cancelOrResetTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.START, 16, 214, 16, 12)); + + themeHintTextView = new TextView(getContext()); + themeHintTextView.setEllipsize(TextUtils.TruncateAt.END); + themeHintTextView.setGravity(Gravity.CENTER); + themeHintTextView.setLines(1); + themeHintTextView.setSingleLine(true); + themeHintTextView.setText(LocaleController.formatString("ChatThemeApplyHint", R.string.ChatThemeApplyHint, chatActivity.getCurrentUser().first_name)); + themeHintTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); + rootLayout.addView(themeHintTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.START, 16, 214, 16, 12)); + } + updateButtonColors(); + updateState(false); + } + + private void updateButtonColors() { + if (themeHintTextView != null) { + themeHintTextView.setTextColor(getThemedColor(Theme.key_dialogTextGray)); + themeHintTextView.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(6), Color.TRANSPARENT, ColorUtils.setAlphaComponent(getThemedColor(Theme.key_featuredStickers_addButton), (int) (0.3f * 255)))); + } + if (cancelOrResetTextView != null) { + cancelOrResetTextView.setTextColor(getThemedColor(Theme.key_text_RedRegular)); + cancelOrResetTextView.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(6), Color.TRANSPARENT, ColorUtils.setAlphaComponent(getThemedColor(Theme.key_text_RedRegular), (int) (0.3f * 255)))); + } + backButtonView.setBackground(Theme.createSelectorDrawable(ColorUtils.setAlphaComponent(getThemedColor(Theme.key_dialogTextBlack), 30), 1)); + backButtonDrawable.setColor(getThemedColor(Theme.key_dialogTextBlack)); + backButtonDrawable.setRotatedColor(getThemedColor(Theme.key_dialogTextBlack)); + backButtonView.invalidate(); + + darkThemeView.setBackground(Theme.createSelectorDrawable(ColorUtils.setAlphaComponent(getThemedColor(Theme.key_featuredStickers_addButton), 30), 1)); + chooseBackgroundTextView.setTextColor(getThemedColor(Theme.key_dialogTextBlue)); + chooseBackgroundTextView.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(6), Color.TRANSPARENT, ColorUtils.setAlphaComponent(getThemedColor(Theme.key_featuredStickers_addButton), (int) (0.3f * 255)))); + + } + + private void previewSelectedTheme() { + if (isDismissed() || isApplyClicked) { + return; + } + isLightDarkChangeAnimation = false; + chatActivity.forceDisallowApplyWallpeper = false; + TLRPC.WallPaper wallpaper = hasChanges() ? null : currentWallpaper; + if (selectedItem.chatTheme.showAsDefaultStub) { + themeDelegate.setCurrentTheme(null, wallpaper, true, forceDark); + } else { + themeDelegate.setCurrentTheme(selectedItem.chatTheme, wallpaper, true, forceDark); + } + } + + private void updateState(boolean animated) { + if (!dataLoaded) { + backButtonDrawable.setRotation(1f, animated); + applyButton.setEnabled(false); + AndroidUtilities.updateViewVisibilityAnimated(chooseBackgroundTextView, false, 0.9f, false, animated); + AndroidUtilities.updateViewVisibilityAnimated(cancelOrResetTextView, false, 0.9f, false, animated); + AndroidUtilities.updateViewVisibilityAnimated(applyButton, false, 1f, false, animated); + AndroidUtilities.updateViewVisibilityAnimated(applyTextView, false, 0.9f, false, animated); + AndroidUtilities.updateViewVisibilityAnimated(themeHintTextView, false, 0.9f, false, animated); + AndroidUtilities.updateViewVisibilityAnimated(progressView, true, 1f, true, animated); + } else { + AndroidUtilities.updateViewVisibilityAnimated(progressView, false, 1f, true, animated); + if (hasChanges()) { + backButtonDrawable.setRotation(0, animated); + applyButton.setEnabled(true); + AndroidUtilities.updateViewVisibilityAnimated(chooseBackgroundTextView, false, 0.9f, false, animated); + AndroidUtilities.updateViewVisibilityAnimated(cancelOrResetTextView, false, 0.9f, false, animated); + AndroidUtilities.updateViewVisibilityAnimated(applyButton, true, 1f, false, animated); + AndroidUtilities.updateViewVisibilityAnimated(applyTextView, true, 0.9f, false, animated); + AndroidUtilities.updateViewVisibilityAnimated(themeHintTextView, true, 0.9f, false, animated); + if (selectedItem != null && selectedItem.chatTheme != null && selectedItem.chatTheme.showAsDefaultStub && selectedItem.chatTheme.wallpaper == null) { + applyTextView.setText(LocaleController.getString("ChatResetTheme", R.string.ChatResetTheme)); + } else { + applyTextView.setText(LocaleController.getString("ChatApplyTheme", R.string.ChatApplyTheme)); + } + } else { + backButtonDrawable.setRotation(1f, animated); + applyButton.setEnabled(false); + AndroidUtilities.updateViewVisibilityAnimated(chooseBackgroundTextView, true, 0.9f, false, animated); + AndroidUtilities.updateViewVisibilityAnimated(cancelOrResetTextView, true, 0.9f, false, animated); + AndroidUtilities.updateViewVisibilityAnimated(applyButton, false, 1f, false, animated); + AndroidUtilities.updateViewVisibilityAnimated(applyTextView, false, 0.9f, false, animated); + AndroidUtilities.updateViewVisibilityAnimated(themeHintTextView, false, 0.9f, false, animated); + } + } } @Override @@ -266,7 +417,9 @@ public void onComplete(List result) { if (result != null && !result.isEmpty()) { themeDelegate.setCachedThemes(result); } - onDataLoaded(result); + NotificationCenter.getInstance(currentAccount).doOnIdle(() -> { + onDataLoaded(result); + }); } @Override @@ -285,7 +438,11 @@ public void onError(TLRPC.TL_error error) { hintView.setVisibility(View.INVISIBLE); hintView.setShowingDuration(5000); hintView.setBottomOffset(-AndroidUtilities.dp(8)); - hintView.setText(AndroidUtilities.replaceTags(LocaleController.formatString("ChatThemeDayNightSwitchTooltip", R.string.ChatThemeDayNightSwitchTooltip, chatActivity.getCurrentUser().first_name))); + if (forceDark) { + hintView.setText(AndroidUtilities.replaceTags(LocaleController.formatString("ChatThemeDaySwitchTooltip", R.string.ChatThemeDaySwitchTooltip))); + } else { + hintView.setText(AndroidUtilities.replaceTags(LocaleController.formatString("ChatThemeNightSwitchTooltip", R.string.ChatThemeNightSwitchTooltip))); + } AndroidUtilities.runOnUIThread(() -> { hintView.showForView(darkThemeView, true); }, 1500); @@ -310,8 +467,32 @@ public void onBackPressed() { public void dismiss() { NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.emojiLoaded); super.dismiss(); + chatActivity.forceDisallowApplyWallpeper = false; if (!isApplyClicked) { - themeDelegate.setCurrentTheme(originalTheme, true, originalIsDark); + TLRPC.WallPaper wallpaper = themeDelegate.getCurrentWallpaper(); + if (wallpaper == null) { + wallpaper = currentWallpaper; + } + themeDelegate.setCurrentTheme(originalTheme, wallpaper, true, originalIsDark); + } + if (forceDark != originalIsDark) { + Theme.ThemeInfo activeTheme; + if (Theme.getActiveTheme().isDark() == originalIsDark) { + activeTheme = Theme.getActiveTheme(); + } else { + SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("themeconfig", Activity.MODE_PRIVATE); + String dayThemeName = preferences.getString("lastDayTheme", "Blue"); + if (Theme.getTheme(dayThemeName) == null || Theme.getTheme(dayThemeName).isDark()) { + dayThemeName = "Blue"; + } + String nightThemeName = preferences.getString("lastDarkTheme", "Dark Blue"); + if (Theme.getTheme(nightThemeName) == null || !Theme.getTheme(nightThemeName).isDark()) { + nightThemeName = "Dark Blue"; + } + activeTheme = originalIsDark ? Theme.getTheme(nightThemeName) : Theme.getTheme(dayThemeName); + } + + Theme.applyTheme(activeTheme, false, originalIsDark); } } @@ -332,7 +513,9 @@ public void close() { @Override public void didReceivedNotification(int id, int account, Object... args) { if (id == NotificationCenter.emojiLoaded) { - adapter.notifyDataSetChanged(); + NotificationCenter.getInstance(currentAccount).doOnIdle(() -> { + adapter.notifyDataSetChanged(); + }); } } @@ -357,6 +540,13 @@ public void onAnimationProgress(float progress) { onAnimationEnd(); isAnimationStarted = false; } + updateButtonColors(); + if (chatAttachButton != null) { + chatAttachButton.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(0), getThemedColor(Theme.key_windowBackgroundWhite), ColorUtils.setAlphaComponent(getThemedColor(Theme.key_featuredStickers_addButton), (int) (0.3f * 255)))); + } + if (chatAttachButtonText != null) { + chatAttachButtonText.setTextColor(getThemedColor(Theme.key_featuredStickers_addButton)); + } } @Override @@ -364,6 +554,13 @@ public void didSetColor() { } }; ArrayList themeDescriptions = new ArrayList<>(); + if (chatActivity.forceDisallowRedrawThemeDescriptions && overlayFragment != null) { + themeDescriptions.addAll(overlayFragment.getThemeDescriptionsInternal()); + return themeDescriptions; + } + if (chatAttachAlert != null) { + themeDescriptions.addAll(chatAttachAlert.getThemeDescriptions()); + } themeDescriptions.add(new ThemeDescription(null, ThemeDescription.FLAG_BACKGROUNDFILTER, null, null, new Drawable[]{shadowDrawable}, descriptionDelegate, Theme.key_dialogBackground)); themeDescriptions.add(new ThemeDescription(titleView, ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_dialogTextBlack)); themeDescriptions.add(new ThemeDescription(recyclerView, ThemeDescription.FLAG_CELLBACKGROUNDCOLOR, new Class[]{ThemeSmallPreviewView.class}, null, null, null, Theme.key_dialogBackgroundGray)); @@ -372,11 +569,15 @@ public void didSetColor() { for (ThemeDescription description : themeDescriptions) { description.resourcesProvider = themeDelegate; } + return themeDescriptions; } @SuppressLint("NotifyDataSetChanged") public void setupLightDarkTheme(boolean isDark) { + if (isDismissed()) { + return; + } if (changeDayNightViewAnimator != null) { changeDayNightViewAnimator.cancel(); } @@ -424,18 +625,21 @@ protected void onDraw(Canvas canvas) { canvas.restore(); } }; + changeDayNightView.setOnTouchListener((v, event) -> true); changeDayNightViewProgress = 0f; changeDayNightViewAnimator = ValueAnimator.ofFloat(0, 1f); changeDayNightViewAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { boolean changedNavigationBarColor = false; + @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { changeDayNightViewProgress = (float) valueAnimator.getAnimatedValue(); changeDayNightView.invalidate(); if (!changedNavigationBarColor && changeDayNightViewProgress > .5f) { changedNavigationBarColor = true; - AndroidUtilities.setLightNavigationBar(getWindow(), !isDark); - AndroidUtilities.setNavigationBarColor(getWindow(), getThemedColor(Theme.key_windowBackgroundGray)); + // fixNavigationBar(getThemedColor(Theme.key_windowBackgroundGray)); +// AndroidUtilities.setLightNavigationBar(getWindow(), !isDark); +// AndroidUtilities.setNavigationBarColor(getWindow(), ); } } }); @@ -459,16 +663,17 @@ public void onAnimationEnd(Animator animation) { decorView2.addView(changeDayNightView, new ViewGroup.LayoutParams(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); AndroidUtilities.runOnUIThread(() -> { - if (adapter == null || adapter.items == null) { + if (adapter == null || adapter.items == null || isDismissed()) { return; } setForceDark(isDark, true); if (selectedItem != null) { isLightDarkChangeAnimation = true; + TLRPC.WallPaper wallpaper = hasChanges() ? null : themeDelegate.getCurrentWallpaper(); if (selectedItem.chatTheme.showAsDefaultStub) { - themeDelegate.setCurrentTheme(null, false, isDark); + themeDelegate.setCurrentTheme(null, wallpaper, false, isDark); } else { - themeDelegate.setCurrentTheme(selectedItem.chatTheme, false, isDark); + themeDelegate.setCurrentTheme(selectedItem.chatTheme, wallpaper, false, isDark); } } if (adapter != null && adapter.items != null) { @@ -503,13 +708,32 @@ private void onDataLoaded(List result) { return; } + dataLoaded = true; ChatThemeItem noThemeItem = new ChatThemeItem(result.get(0)); List items = new ArrayList<>(result.size()); - EmojiThemes currentTheme = themeDelegate.getCurrentTheme(); + currentTheme = themeDelegate.getCurrentTheme(); items.add(0, noThemeItem); selectedItem = noThemeItem; +// if (chatActivity.getCurrentUserInfo() != null && chatActivity.getCurrentUserInfo().wallpaper != null) { +// EmojiThemes wallpaperItem = EmojiThemes.createChatThemesDefault(); +// wallpaperItem.wallpaper = chatActivity.getCurrentUserInfo().wallpaper; +// wallpaperItem.showAsDefaultStub = true; +// wallpaperItem.emoji = "\uD83C\uDFA8"; +// +// for (int i = 0; i < wallpaperItem.items.size(); i++) { +// EmojiThemes.ThemeItem item = wallpaperItem.items.get(i); +// item.inBubbleColor = Color.WHITE;//Theme.getDefaultColor(Theme.key_chat_inBubble); +// item.outBubbleColor = Color.GRAY;//Theme.getDefaultColor(Theme.key_chat_outBubble); +// item.outLineColor = Color.BLACK; +// } +// if (currentTheme == null || currentTheme.showAsDefaultStub) { +// currentTheme = wallpaperItem; +// } +// items.add(new ChatThemeItem(wallpaperItem)); +// } + for (int i = 1; i < result.size(); ++i) { EmojiThemes chatTheme = result.get(i); ChatThemeItem item = new ChatThemeItem(chatTheme); @@ -520,16 +744,16 @@ private void onDataLoaded(List result) { items.add(item); } adapter.setItems(items); - applyButton.setEnabled(true); - applyTextView.setAlpha(0f); - resetTextView.setAlpha(0f); - recyclerView.setAlpha(0f); - applyTextView.setVisibility(View.VISIBLE); - resetTextView.setVisibility(View.VISIBLE); darkThemeView.setVisibility(View.VISIBLE); - boolean showRestText = false; + resetToPrimaryState(false); + recyclerView.animate().alpha(1f).setDuration(150).start(); + updateState(true); + } + + private void resetToPrimaryState(boolean animated) { + List items = adapter.items; if (currentTheme != null) { int selectedPosition = -1; for (int i = 0; i != items.size(); ++i) { @@ -546,18 +770,22 @@ private void onDataLoaded(List result) { selectedPosition -= 1; } int finalSelectedPosition = Math.min(selectedPosition, adapter.items.size() - 1); - layoutManager.scrollToPositionWithOffset(finalSelectedPosition, 0); + if (animated) { + recyclerView.smoothScrollToPosition(finalSelectedPosition); + } else { + layoutManager.scrollToPositionWithOffset(finalSelectedPosition, 0); + } } } else { - showRestText = true; + selectedItem = items.get(0); adapter.setSelectedItem(0); - layoutManager.scrollToPositionWithOffset(0, 0); + if (animated) { + recyclerView.smoothScrollToPosition(0); + } else { + layoutManager.scrollToPositionWithOffset(0, 0); + } } - - recyclerView.animate().alpha(1f).setDuration(150).start(); - resetTextView.animate().alpha(showRestText ? 1f : 0).setDuration(150).start(); - applyTextView.animate().alpha(showRestText ? 0f : 1).setDuration(150).start(); - progressView.animate().alpha(0f).setListener(new HideViewAfterAnimation(progressView)).setDuration(150).start(); + previewSelectedTheme(); } private void onAnimationStart() { @@ -611,17 +839,16 @@ private void setItemsAnimationProgress(float progress) { private void applySelectedTheme() { Bulletin bulletin = null; EmojiThemes newTheme = selectedItem.chatTheme; - if (newTheme.showAsDefaultStub) { - newTheme = null; - } - if (selectedItem != null && newTheme != originalTheme) { + if (selectedItem != null && newTheme != currentTheme) { EmojiThemes chatTheme = selectedItem.chatTheme; - String emoticon = (chatTheme != null && !chatTheme.showAsDefaultStub) ? chatTheme.getEmoticon() : null; + String emoticon = !chatTheme.showAsDefaultStub ? chatTheme.getEmoticon() : null; + ChatThemeController.getInstance(currentAccount).clearWallpaper(chatActivity.getDialogId(), false); ChatThemeController.getInstance(currentAccount).setDialogTheme(chatActivity.getDialogId(), emoticon, true); - if (chatTheme != null && !chatTheme.showAsDefaultStub) { - themeDelegate.setCurrentTheme(chatTheme, true, originalIsDark); + TLRPC.WallPaper wallpaper = hasChanges() ? null : themeDelegate.getCurrentWallpaper(); + if (!chatTheme.showAsDefaultStub) { + themeDelegate.setCurrentTheme(chatTheme, wallpaper, true, originalIsDark); } else { - themeDelegate.setCurrentTheme(null, true, originalIsDark); + themeDelegate.setCurrentTheme(null, wallpaper, true, originalIsDark); } isApplyClicked = true; @@ -654,7 +881,7 @@ private boolean hasChanges() { if (selectedItem == null) { return false; } else { - String oldEmoticon = originalTheme != null ? originalTheme.getEmoticon() : null; + String oldEmoticon = currentTheme != null ? currentTheme.getEmoticon() : null; if (TextUtils.isEmpty(oldEmoticon)) { oldEmoticon = "❌"; } @@ -667,7 +894,7 @@ private boolean hasChanges() { } @SuppressLint("NotifyDataSetChanged") - public static class Adapter extends RecyclerView.Adapter { + public static class Adapter extends RecyclerListView.SelectionAdapter { private final Theme.ResourcesProvider resourcesProvider; @@ -799,8 +1026,8 @@ private boolean parseTheme(Theme.ThemeInfo themeInfo) { break; } else { if ((idx = line.indexOf('=')) != -1) { - String key = line.substring(0, idx); - if (key.equals(Theme.key_chat_inBubble) || key.equals(Theme.key_chat_outBubble) || key.equals(Theme.key_chat_wallpaper) || key.equals(Theme.key_chat_wallpaper_gradient_to1) || key.equals(Theme.key_chat_wallpaper_gradient_to2) || key.equals(Theme.key_chat_wallpaper_gradient_to3)) { + int key = ThemeColors.stringKeyToInt(line.substring(0, idx)); + if (key == Theme.key_chat_inBubble || key == Theme.key_chat_outBubble || key == Theme.key_chat_wallpaper || key == Theme.key_chat_wallpaper_gradient_to1 || key == Theme.key_chat_wallpaper_gradient_to2 || key == Theme.key_chat_wallpaper_gradient_to3) { String param = line.substring(idx + 1); int value; if (param.length() > 0 && param.charAt(0) == '#') { @@ -812,25 +1039,18 @@ private boolean parseTheme(Theme.ThemeInfo themeInfo) { } else { value = Utilities.parseInt(param); } - switch (key) { - case Theme.key_chat_inBubble: - themeInfo.setPreviewInColor(value); - break; - case Theme.key_chat_outBubble: - themeInfo.setPreviewOutColor(value); - break; - case Theme.key_chat_wallpaper: - themeInfo.setPreviewBackgroundColor(value); - break; - case Theme.key_chat_wallpaper_gradient_to1: - themeInfo.previewBackgroundGradientColor1 = value; - break; - case Theme.key_chat_wallpaper_gradient_to2: - themeInfo.previewBackgroundGradientColor2 = value; - break; - case Theme.key_chat_wallpaper_gradient_to3: - themeInfo.previewBackgroundGradientColor3 = value; - break; + if (key == Theme.key_chat_inBubble) { + themeInfo.setPreviewInColor(value); + } else if (key == Theme.key_chat_outBubble) { + themeInfo.setPreviewOutColor(value); + } else if (key == Theme.key_chat_wallpaper) { + themeInfo.setPreviewBackgroundColor(value); + } else if (key == Theme.key_chat_wallpaper_gradient_to1) { + themeInfo.previewBackgroundGradientColor1 = value; + } else if (key == Theme.key_chat_wallpaper_gradient_to2) { + themeInfo.previewBackgroundGradientColor2 = value; + } else if (key == Theme.key_chat_wallpaper_gradient_to3) { + themeInfo.previewBackgroundGradientColor3 = value; } } } @@ -902,6 +1122,171 @@ public void setSelectedItem(int position) { notifyItemChanged(selectedItemPosition); } + @Override + public boolean isEnabled(RecyclerView.ViewHolder holder) { + return false; + } + } + + private void openGalleryForBackground() { + chatAttachAlert = new ChatAttachAlert(chatActivity.getParentActivity(), chatActivity, false, false, false, chatActivity.getResourceProvider()); + chatAttachAlert.drawNavigationBar = true; + chatAttachAlert.setupPhotoPicker(LocaleController.getString("ChooseBackground", R.string.ChooseBackground)); + chatAttachAlert.setDelegate(new ChatAttachAlert.ChatAttachViewDelegate() { + @Override + public void didPressedButton(int button, boolean arg, boolean notify, int scheduleDate, boolean forceDocument) { + try { + HashMap photos = chatAttachAlert.getPhotoLayout().getSelectedPhotos(); + if (!photos.isEmpty()) { + MediaController.PhotoEntry entry = (MediaController.PhotoEntry) photos.values().iterator().next(); + String path; + if (entry.imagePath != null) { + path = entry.imagePath; + } else { + path = entry.path; + } + if (path != null) { + File currentWallpaperPath = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), Utilities.random.nextInt() + ".jpg"); + Point screenSize = AndroidUtilities.getRealScreenSize(); + Bitmap bitmap = ImageLoader.loadBitmap(path, null, screenSize.x, screenSize.y, true); + FileOutputStream stream = new FileOutputStream(currentWallpaperPath); + bitmap.compress(Bitmap.CompressFormat.JPEG, 87, stream); + + ThemePreviewActivity themePreviewActivity = new ThemePreviewActivity(new WallpapersListActivity.FileWallpaper("", currentWallpaperPath, currentWallpaperPath), bitmap); + themePreviewActivity.setDialogId(chatActivity.getDialogId()); + themePreviewActivity.setDelegate(() -> { + chatAttachAlert.dismissInternal(); + dismiss(); + }); + showAsSheet(themePreviewActivity); + } + } + } catch (Throwable e) { + FileLog.e(e); + } + } + + @Override + public void onWallpaperSelected(Object object) { + ThemePreviewActivity wallpaperActivity = new ThemePreviewActivity(object, null, true, false); + wallpaperActivity.setDialogId(chatActivity.getDialogId()); + wallpaperActivity.setDelegate(() -> { + chatAttachAlert.dismissInternal(); + dismiss(); + }); + showAsSheet(wallpaperActivity); + } + }); + chatAttachAlert.setMaxSelectedPhotos(1, false); + chatAttachAlert.init(); + chatAttachAlert.getPhotoLayout().loadGalleryPhotos(); + chatAttachAlert.show(); + + chatAttachButton = new FrameLayout(getContext()) { + + Paint paint = new Paint(); + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(48), MeasureSpec.EXACTLY)); + } + + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + paint.setColor(getThemedColor(Theme.key_divider)); + canvas.drawRect(0, 0, getMeasuredWidth(), 1, paint); + } + }; + chatAttachButtonText = new AnimatedTextView(getContext(), true, true, true); + chatAttachButtonText.setTextSize(AndroidUtilities.dp(14)); + chatAttachButtonText.setText(LocaleController.getString("SetColorAsBackground", R.string.SetColorAsBackground)); + chatAttachButtonText.setGravity(Gravity.CENTER); + chatAttachButtonText.setTextColor(getThemedColor(Theme.key_featuredStickers_addButton)); + chatAttachButton.addView(chatAttachButtonText, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); + chatAttachButton.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(0), getThemedColor(Theme.key_windowBackgroundWhite), ColorUtils.setAlphaComponent(getThemedColor(Theme.key_featuredStickers_addButton), (int) (0.3f * 255)))); + chatAttachButton.setOnClickListener(v -> { + if (chatAttachAlert.getCurrentAttachLayout() == chatAttachAlert.getPhotoLayout()) { + chatAttachButtonText.setText(LocaleController.getString("ChooseBackgroundFromGallery", R.string.ChooseBackgroundFromGallery)); + chatAttachAlert.openColorsLayout(); + chatAttachAlert.colorsLayout.updateColors(forceDark); + } else { + chatAttachButtonText.setText(LocaleController.getString("SetColorAsBackground", R.string.SetColorAsBackground)); + chatAttachAlert.showLayout(chatAttachAlert.getPhotoLayout()); + } +// WallpapersListActivity wallpapersListActivity = new WallpapersListActivity(WallpapersListActivity.TYPE_ALL, chatActivity.getDialogId()); +// chatActivity.presentFragment(wallpapersListActivity); +// chatAttachAlert.dismiss(); +// dismiss(); + }); + chatAttachAlert.sizeNotifierFrameLayout.addView(chatAttachButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM)); + } + + private void fixColorsAfterAnotherWindow() { + if (isDismissed() || isApplyClicked) { + return; + } + Theme.disallowChangeServiceMessageColor = false; + TLRPC.WallPaper wallpaper = hasChanges() ? null : themeDelegate.getCurrentWallpaper(); + if (selectedItem.chatTheme.showAsDefaultStub) { + themeDelegate.setCurrentTheme(null, wallpaper, false, forceDark, true); + } else { + themeDelegate.setCurrentTheme(selectedItem.chatTheme, wallpaper, false, forceDark, true); + } + if (chatAttachAlert != null) { + if (chatAttachAlert.colorsLayout != null) { + chatAttachAlert.colorsLayout.updateColors(forceDark); + } + chatAttachAlert.checkColors(); + } + if (adapter != null && adapter.items != null) { + for (int i = 0; i < adapter.items.size(); i++) { + adapter.items.get(i).themeIndex = forceDark ? 1 : 0; + } + adapter.notifyDataSetChanged(); + } + } + + private void showAsSheet(ThemePreviewActivity themePreviewActivity) { + BaseFragment.BottomSheetParams params = new BaseFragment.BottomSheetParams(); + params.transitionFromLeft = true; + params.allowNestedScroll = false; + themePreviewActivity.setResourceProvider(chatActivity.getResourceProvider()); + themePreviewActivity.setOnSwitchDayNightDelegate(new ThemePreviewActivity.DayNightSwitchDelegate() { + private Runnable fixRedraw; + + @Override + public boolean isDark() { + return forceDark; + } + + @Override + public void switchDayNight() { + forceDark = !forceDark; + if (selectedItem != null) { + isLightDarkChangeAnimation = true; + chatActivity.forceDisallowRedrawThemeDescriptions = true; + TLRPC.WallPaper wallpaper = hasChanges() ? null : themeDelegate.getCurrentWallpaper(); + if (selectedItem.chatTheme.showAsDefaultStub) { + themeDelegate.setCurrentTheme(null, wallpaper, true, forceDark); + } else { + themeDelegate.setCurrentTheme(selectedItem.chatTheme, wallpaper, true, forceDark); + } + chatActivity.forceDisallowRedrawThemeDescriptions = false; + } + } + }); + params.onOpenAnimationFinished = () -> { + PhotoViewer.getInstance().closePhoto(false, false); + }; + params.onPreFinished = () -> { + fixColorsAfterAnotherWindow(); + }; + params.onDismiss = () -> { + overlayFragment = null; + }; + overlayFragment = themePreviewActivity; + chatActivity.showAsSheet(themePreviewActivity, params); } public static class ChatThemeItem { @@ -917,10 +1302,4 @@ public ChatThemeItem(EmojiThemes chatTheme) { this.chatTheme = chatTheme; } } - - @Override - public void show() { - super.show(); - resetTextView.setText(themeDelegate.getCurrentTheme() == null ? LocaleController.getString("DoNoSetTheme", R.string.DoNoSetTheme) : LocaleController.getString("ChatResetTheme", R.string.ChatResetTheme)); - } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/CheckBox2.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/CheckBox2.java index b2fd95340d..ababb04b8e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/CheckBox2.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/CheckBox2.java @@ -46,6 +46,10 @@ public void setChecked(boolean checked, boolean animated) { checkBoxBase.setChecked(checked, animated); } + public CheckBoxBase getCheckBoxBase() { + return checkBoxBase; + } + public void setNum(int num) { checkBoxBase.setNum(num); } @@ -54,7 +58,7 @@ public boolean isChecked() { return checkBoxBase.isChecked(); } - public void setColor(String background, String background2, String check) { + public void setColor(int background, int background2, int check) { checkBoxBase.setColor(background, background2, check); } @@ -115,6 +119,10 @@ protected void onDraw(Canvas canvas) { } } + public void setForbidden(boolean forbidden) { + checkBoxBase.setForbidden(forbidden); + } + @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/CheckBoxBase.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/CheckBoxBase.java index c2200985b6..84e37449d2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/CheckBoxBase.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/CheckBoxBase.java @@ -1,10 +1,13 @@ package org.telegram.ui.Components; +import static org.telegram.messenger.AndroidUtilities.lerp; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.DashPathEffect; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; @@ -17,9 +20,11 @@ import android.view.View; import androidx.annotation.Keep; +import androidx.core.graphics.ColorUtils; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.GenericProvider; +import org.telegram.messenger.LocaleController; import org.telegram.ui.ActionBar.Theme; public class CheckBoxBase { @@ -33,6 +38,13 @@ public class CheckBoxBase { private Paint checkPaint; private Paint backgroundPaint; private TextPaint textPaint; + private static Paint forbidPaint; + + private float alpha = 1; + public void setAlpha(float alpha) { + this.alpha = alpha; + invalidate(); + } private Path path = new Path(); @@ -42,14 +54,15 @@ public class CheckBoxBase { private float backgroundAlpha = 1.0f; + private boolean forbidden; private float progress; private ObjectAnimator checkAnimator; private boolean isChecked; - private String checkColorKey = Theme.key_checkboxCheck; - private String backgroundColorKey = Theme.key_chat_serviceBackground; - private String background2ColorKey = Theme.key_chat_serviceBackground; + private int checkColorKey = Theme.key_checkboxCheck; + private int backgroundColorKey = Theme.key_chat_serviceBackground; + private int background2ColorKey = Theme.key_chat_serviceBackground; private boolean useDefaultCheck; @@ -128,6 +141,14 @@ public void setProgress(float value) { } } + public void setForbidden(boolean value) { + if (forbidden == value) { + return; + } + forbidden = value; + invalidate(); + } + private void invalidate() { if (parentView.getParent() != null) { View parent = (View) parentView.getParent(); @@ -163,7 +184,7 @@ public void setBackgroundType(int type) { checkPaint.setStrokeWidth(AndroidUtilities.dp(1.5f)); } } else if (type == 3) { - backgroundPaint.setStrokeWidth(AndroidUtilities.dp(1.2f)); + backgroundPaint.setStrokeWidth(AndroidUtilities.dp(3f)); } else if (type != 0) { backgroundPaint.setStrokeWidth(AndroidUtilities.dp(1.5f)); } @@ -195,7 +216,7 @@ public void onAnimationEnd(Animator animation) { checkAnimator.start(); } - public void setColor(String background, String background2, String check) { + public void setColor(int background, int background2, int check) { backgroundColorKey = background; background2ColorKey = background2; checkColorKey = check; @@ -256,12 +277,13 @@ public void draw(Canvas canvas) { } } + float progress = forbidden ? 1f : this.progress; float roundProgress = progress >= 0.5f ? 1.0f : progress / 0.5f; int cx = bounds.centerX(); int cy = bounds.centerY(); - if (backgroundColorKey != null) { + if (backgroundColorKey >= 0) { if (drawUnchecked) { if (backgroundType == 12 || backgroundType == 13) { paint.setColor(getThemedColor(backgroundColorKey)); @@ -277,7 +299,7 @@ public void draw(Canvas canvas) { backgroundPaint.setColor(getThemedColor(checkColorKey)); } } else { - backgroundPaint.setColor(AndroidUtilities.getOffsetColor(0x00ffffff, getThemedColor(background2ColorKey != null ? background2ColorKey : checkColorKey), progress, backgroundAlpha)); + backgroundPaint.setColor(AndroidUtilities.getOffsetColor(0x00ffffff, getThemedColor(background2ColorKey >= 0 ? background2ColorKey : checkColorKey), progress, backgroundAlpha)); } } else { if (drawUnchecked) { @@ -288,7 +310,7 @@ public void draw(Canvas canvas) { backgroundPaint.setColor(AndroidUtilities.getOffsetColor(0xffffffff, getThemedColor(checkColorKey), progress, backgroundAlpha)); } } else { - backgroundPaint.setColor(AndroidUtilities.getOffsetColor(0x00ffffff, getThemedColor(background2ColorKey != null ? background2ColorKey : checkColorKey), progress, backgroundAlpha)); + backgroundPaint.setColor(AndroidUtilities.getOffsetColor(0x00ffffff, getThemedColor(background2ColorKey >= 0 ? background2ColorKey : checkColorKey), progress, backgroundAlpha)); } } @@ -336,6 +358,9 @@ public void draw(Canvas canvas) { } else { startAngle = 90; sweepAngle = (int) (270 * progress); + if (LocaleController.isRTL) { + sweepAngle = -sweepAngle; + } } if (backgroundType == 6) { @@ -358,16 +383,24 @@ public void draw(Canvas canvas) { if (backgroundType == 9) { paint.setColor(getThemedColor(background2ColorKey)); - } else if (backgroundType == 11 || backgroundType == 6 || backgroundType == 7 || backgroundType == 10 || !drawUnchecked && backgroundColorKey != null || backgroundType == 14) { + } else if (backgroundType == 11 || backgroundType == 6 || backgroundType == 7 || backgroundType == 10 || !drawUnchecked && backgroundColorKey >= 0 || backgroundType == 14) { paint.setColor(getThemedColor(backgroundColorKey)); } else { paint.setColor(getThemedColor(enabled ? Theme.key_checkbox : Theme.key_checkboxDisabled)); } - if (!useDefaultCheck && checkColorKey != null) { + if (forbidden) { + paint.setColor(backgroundPaint.getColor()); + } else if (alpha < 1) { + paint.setColor(ColorUtils.blendARGB(backgroundPaint.getColor(), paint.getColor(), alpha)); + } + if (!useDefaultCheck && checkColorKey >= 0) { checkPaint.setColor(getThemedColor(checkColorKey)); } else { checkPaint.setColor(getThemedColor(Theme.key_checkboxCheck)); } + if (alpha < 1 && Theme.isCurrentThemeDark()) { + checkPaint.setColor(ColorUtils.blendARGB(paint.getColor(), checkPaint.getColor(), alpha)); + } if (backgroundType != -1) { float sizeHalf = AndroidUtilities.dp(size) / 2f; @@ -389,7 +422,19 @@ public void draw(Canvas canvas) { } canvas.restoreToCount(restoreCount); } - if (checkProgress != 0) { + + if (forbidden) { + if (forbidPaint == null) { + forbidPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + forbidPaint.setStyle(Paint.Style.STROKE); + forbidPaint.setStrokeCap(Paint.Cap.ROUND); + forbidPaint.setStrokeJoin(Paint.Join.ROUND); + forbidPaint.setPathEffect(new DashPathEffect(new float[] { AndroidUtilities.dp(0.66f), AndroidUtilities.dp(4) }, 0)); + } + forbidPaint.setStrokeWidth(AndroidUtilities.dp(1.66f)); + forbidPaint.setColor(getThemedColor(Theme.key_switchTrack)); + canvas.drawCircle(cx, cy, AndroidUtilities.dp(9), forbidPaint); + } else if (checkProgress != 0) { if (checkedText != null) { if (textPaint == null) { textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); @@ -444,8 +489,7 @@ public void setCirclePaintProvider(GenericProvider circlePaintProvi this.circlePaintProvider = circlePaintProvider; } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/CheckBoxSquare.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/CheckBoxSquare.java index 2330ecf3f6..b0bb9b5f6b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/CheckBoxSquare.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/CheckBoxSquare.java @@ -38,9 +38,9 @@ public class CheckBoxSquare extends View { private final static float progressBounceDiff = 0.2f; - private String key1; - private String key2; - private String key3; + private int key1; + private int key2; + private int key3; private final Theme.ResourcesProvider resourcesProvider; public CheckBoxSquare(Context context, boolean alert) { @@ -64,7 +64,7 @@ public CheckBoxSquare(Context context, boolean alert, Theme.ResourcesProvider re isAlert = alert; } - public void setColors(String unchecked, String checked, String check) { + public void setColors(int unchecked, int checked, int check) { key1 = unchecked; key2 = checked; key3 = check; @@ -187,8 +187,7 @@ protected void onDraw(Canvas canvas) { canvas.drawBitmap(drawBitmap, 0, 0, null); } - protected int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + protected int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChecksHintView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChecksHintView.java index aa71a41019..b7648ea003 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChecksHintView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChecksHintView.java @@ -209,8 +209,7 @@ public void onAnimationEnd(Animator animation) { animatorSet.start(); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ClearHistoryAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ClearHistoryAlert.java index 51f7eb1f35..917aed9920 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ClearHistoryAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ClearHistoryAlert.java @@ -106,9 +106,8 @@ public void setText(CharSequence text) { textView.setText(text); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + protected int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } @@ -297,7 +296,7 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) { linearLayout.addView(clearButton, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 50, Gravity.LEFT | Gravity.TOP, 0, 0, 0, 0)); ShadowSectionCell shadowSectionCell = new ShadowSectionCell(context); - Drawable drawable = Theme.getThemedDrawable(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow); + Drawable drawable = Theme.getThemedDrawableByKey(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow); CombinedDrawable combinedDrawable = new CombinedDrawable(new ColorDrawable(getThemedColor(Theme.key_windowBackgroundGray)), drawable); combinedDrawable.setFullsize(true); shadowSectionCell.setBackgroundDrawable(combinedDrawable); @@ -360,7 +359,7 @@ public void onTouchEnd() { linearLayout.addView(slideChooseView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 8, 0, 0)); FrameLayout buttonContainer = new FrameLayout(context); - Drawable drawable = Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow); + Drawable drawable = Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow); CombinedDrawable combinedDrawable = new CombinedDrawable(new ColorDrawable(getThemedColor(Theme.key_windowBackgroundGray)), drawable); combinedDrawable.setFullsize(true); buttonContainer.setBackgroundDrawable(combinedDrawable); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ClippingImageView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ClippingImageView.java index 5042e210d0..d96429fda6 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ClippingImageView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ClippingImageView.java @@ -32,7 +32,7 @@ public class ClippingImageView extends View { private int clipLeft; private int clipRight; private int clipTop; - private int orientation; + private int orientation, invert; private int imageY; private int imageX; private RectF drawRect; @@ -166,7 +166,7 @@ public void onDraw(Canvas canvas) { shaderMatrix.reset(); roundRect.set(imageX / scaleY, imageY / scaleY, getWidth() - imageX / scaleY, getHeight() - imageY / scaleY); bitmapRect.set(0, 0, bmp.getWidth(), bmp.getHeight()); - AndroidUtilities.setRectToRect(shaderMatrix, bitmapRect, roundRect, orientation, false); + AndroidUtilities.setRectToRect(shaderMatrix, bitmapRect, roundRect, orientation, invert, false); bitmapShader.setLocalMatrix(shaderMatrix); canvas.clipRect(clipLeft / scaleY, clipTop / scaleY, getWidth() - clipRight / scaleY, getHeight() - clipBottom / scaleY); @@ -182,15 +182,30 @@ public void onDraw(Canvas canvas) { if (orientation == 90 || orientation == 270) { drawRect.set(-getHeight() / 2, -getWidth() / 2, getHeight() / 2, getWidth() / 2); matrix.setRectToRect(bitmapRect, drawRect, Matrix.ScaleToFit.FILL); + if (invert == 1) { + matrix.postScale(-1, 1); + } else if (invert == 2) { + matrix.postScale(1, -1); + } matrix.postRotate(orientation, 0, 0); matrix.postTranslate(getWidth() / 2, getHeight() / 2); } else if (orientation == 180) { drawRect.set(-getWidth() / 2, -getHeight() / 2, getWidth() / 2, getHeight() / 2); matrix.setRectToRect(bitmapRect, drawRect, Matrix.ScaleToFit.FILL); + if (invert == 1) { + matrix.postScale(-1, 1); + } else if (invert == 2) { + matrix.postScale(1, -1); + } matrix.postRotate(orientation, 0, 0); matrix.postTranslate(getWidth() / 2, getHeight() / 2); } else { drawRect.set(0, 0, getWidth(), getHeight()); + if (invert == 1) { + matrix.postScale(-1, 1, getWidth() / 2, getHeight() / 2); + } else if (invert == 2) { + matrix.postScale(1, -1, getWidth() / 2, getHeight() / 2); + } matrix.setRectToRect(bitmapRect, drawRect, Matrix.ScaleToFit.FILL); } @@ -247,6 +262,12 @@ public void setImageX(int value) { public void setOrientation(int angle) { orientation = angle; + invert = 0; + } + + public void setOrientation(int angle, int invert) { + orientation = angle; + this.invert = invert; } public float getCenterX() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ColorPicker.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ColorPicker.java index 12112e8247..7256ed1f2a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ColorPicker.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ColorPicker.java @@ -110,6 +110,8 @@ public class ColorPicker extends FrameLayout { private static final int item_share = 2; private static final int item_delete = 3; + Theme.ResourcesProvider resourcesProvider; + private static class RadioButton extends View { private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); @@ -223,12 +225,10 @@ public ColorPicker(Context context, boolean hasMenu, ColorPickerDelegate colorPi private RectF rect = new RectF(); private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); - { - paint.setColor(Theme.getColor(Theme.key_dialogBackgroundGray)); - } @Override protected void onDraw(Canvas canvas) { + paint.setColor(getThemedColor(Theme.key_dialogBackgroundGray)); int left = colorEditText[0].getLeft() - AndroidUtilities.dp(13); int width = (int) (AndroidUtilities.dp(91) + (clearButton.getVisibility() == VISIBLE ? AndroidUtilities.dp(25) * clearButton.getAlpha() : 0)); rect.set(left, AndroidUtilities.dp(5), left + width, AndroidUtilities.dp(5 + 32)); @@ -373,14 +373,14 @@ public void afterTextChanged(Editable editable) { }); } colorEditText[a].setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); - colorEditText[a].setHintTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteHintText)); - colorEditText[a].setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); - colorEditText[a].setCursorColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + colorEditText[a].setHintTextColor(getThemedColor(Theme.key_windowBackgroundWhiteHintText)); + colorEditText[a].setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteBlackText)); + colorEditText[a].setCursorColor(getThemedColor(Theme.key_windowBackgroundWhiteBlackText)); colorEditText[a].setCursorSize(AndroidUtilities.dp(18)); colorEditText[a].setCursorWidth(1.5f); colorEditText[a].setSingleLine(true); colorEditText[a].setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL); - colorEditText[a].setHeaderHintColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlueHeader)); + colorEditText[a].setHeaderHintColor(getThemedColor(Theme.key_windowBackgroundWhiteBlueHeader)); colorEditText[a].setTransformHintToHeader(true); colorEditText[a].setInputType(EditorInfo.TYPE_TEXT_VARIATION_PASSWORD | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); colorEditText[a].setImeOptions(EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI); @@ -392,9 +392,9 @@ public void afterTextChanged(Editable editable) { } addButton = new ImageView(getContext()); - addButton.setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_dialogButtonSelector), 1)); + addButton.setBackground(Theme.createSelectorDrawable(getThemedColor(Theme.key_dialogButtonSelector), 1)); addButton.setImageResource(R.drawable.msg_add); - addButton.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText), PorterDuff.Mode.MULTIPLY)); + addButton.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_windowBackgroundWhiteBlackText), PorterDuff.Mode.MULTIPLY)); addButton.setScaleType(ImageView.ScaleType.CENTER); addButton.setOnClickListener(v -> { if (colorsAnimator != null) { @@ -486,9 +486,9 @@ public void setAlpha(float alpha) { linearLayout.invalidate(); } }; - clearButton.setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_dialogButtonSelector), 1)); + clearButton.setBackground(Theme.createSelectorDrawable(getThemedColor(Theme.key_dialogButtonSelector), 1)); clearButton.setImageResource(R.drawable.msg_close); - clearButton.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText), PorterDuff.Mode.SRC_IN)); + clearButton.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_windowBackgroundWhiteBlackText), PorterDuff.Mode.MULTIPLY)); clearButton.setAlpha(0.0f); clearButton.setScaleX(0.0f); clearButton.setScaleY(0.0f); @@ -572,7 +572,7 @@ public void onAnimationEnd(Animator animation) { resetButton.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); resetButton.setGravity(Gravity.CENTER); resetButton.setPadding(AndroidUtilities.dp(4), 0, AndroidUtilities.dp(4), 0); - resetButton.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + resetButton.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteBlackText)); addView(resetButton, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 36, Gravity.TOP | Gravity.RIGHT, 0, 3, 14, 0)); resetButton.setOnClickListener(v -> { /*if (resetButton.getAlpha() != 1.0f) { TODO @@ -584,7 +584,7 @@ public void onAnimationEnd(Animator animation) { }); if (hasMenu) { - menuItem = new ActionBarMenuItem(context, null, 0, Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + menuItem = new ActionBarMenuItem(context, null, 0, getThemedColor(Theme.key_windowBackgroundWhiteBlackText)); menuItem.setLongClickEnabled(false); menuItem.setIcon(R.drawable.ic_ab_other); menuItem.setContentDescription(LocaleController.getString("AccDescrMoreOptions", R.string.AccDescrMoreOptions)); @@ -602,13 +602,17 @@ public void onAnimationEnd(Animator animation) { }); menuItem.setAdditionalYOffset(AndroidUtilities.dp(72)); menuItem.setTranslationX(AndroidUtilities.dp(6)); - menuItem.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.getColor(Theme.key_dialogButtonSelector), 1)); + menuItem.setBackgroundDrawable(Theme.createSelectorDrawable(getThemedColor(Theme.key_dialogButtonSelector), 1)); addView(menuItem, LayoutHelper.createFrame(30, 30, Gravity.TOP | Gravity.RIGHT, 0, 2, 10, 0)); menuItem.setOnClickListener(v -> menuItem.toggleSubMenu()); } updateColorsPosition(null, 0, false, getMeasuredWidth()); } + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); + } + @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); @@ -1047,11 +1051,11 @@ public void provideThemeDescriptions(List arrayList) { arrayList.add(new ThemeDescription(clearButton, ThemeDescription.FLAG_BACKGROUNDFILTER, null, null, null, null, Theme.key_dialogButtonSelector)); if (menuItem != null) { ThemeDescription.ThemeDescriptionDelegate delegate = () -> { - menuItem.setIconColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); - Theme.setDrawableColor(menuItem.getBackground(), Theme.getColor(Theme.key_dialogButtonSelector)); - menuItem.setPopupItemsColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuItem), false); - menuItem.setPopupItemsColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuItemIcon), true); - menuItem.redrawPopup(Theme.getColor(Theme.key_actionBarDefaultSubmenuBackground)); + menuItem.setIconColor(getThemedColor(Theme.key_windowBackgroundWhiteBlackText)); + Theme.setDrawableColor(menuItem.getBackground(), getThemedColor(Theme.key_dialogButtonSelector)); + menuItem.setPopupItemsColor(getThemedColor(Theme.key_actionBarDefaultSubmenuItem), false); + menuItem.setPopupItemsColor(getThemedColor(Theme.key_actionBarDefaultSubmenuItemIcon), true); + menuItem.redrawPopup(getThemedColor(Theme.key_actionBarDefaultSubmenuBackground)); }; arrayList.add(new ThemeDescription(menuItem, 0, null, null, null, delegate, Theme.key_windowBackgroundWhiteBlackText)); arrayList.add(new ThemeDescription(menuItem, 0, null, null, null, delegate, Theme.key_dialogButtonSelector)); @@ -1100,4 +1104,14 @@ public static int generateGradientColors(int color) { } return Color.HSVToColor(255, hsv); } + + public void setResourcesProvider(Theme.ResourcesProvider resourcesProvider) { + this.resourcesProvider = resourcesProvider; + } + + @Override + public void invalidate() { + super.invalidate(); + linearLayout.invalidate(); + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ColoredImageSpan.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ColoredImageSpan.java index 50fbe90369..2abd410879 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ColoredImageSpan.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ColoredImageSpan.java @@ -21,15 +21,19 @@ public class ColoredImageSpan extends ReplacementSpan { Drawable drawable; boolean usePaintColor = true; - String colorKey; + int colorKey; private int topOffset = 0; + private float translateX; private int size; + private int sizeWidth; public static final int ALIGN_DEFAULT = 0; public static final int ALIGN_BASELINE = 1; public static final int ALIGN_CENTER = 2; private final int verticalAlignment; + private float scale = 1f; + private Runnable checkColorDelegate; public ColoredImageSpan(int imageRes) { this(imageRes, ALIGN_DEFAULT); @@ -40,7 +44,7 @@ public ColoredImageSpan(Drawable drawable) { } public ColoredImageSpan(int imageRes, int verticalAlignment) { - this(ContextCompat.getDrawable(ApplicationLoader.applicationContext, imageRes), verticalAlignment); + this(ContextCompat.getDrawable(ApplicationLoader.applicationContext, imageRes).mutate(), verticalAlignment); } public ColoredImageSpan(Drawable drawable, int verticalAlignment) { @@ -66,29 +70,42 @@ public void setSize(int size) { this.size = size; drawable.setBounds(0, 0, size, size); } + public void setTranslateX(float tx) { + translateX = tx; + } + + public void setWidth(int width) { + sizeWidth = width; + } @Override public int getSize(@NonNull Paint paint, CharSequence charSequence, int i, int i1, @Nullable Paint.FontMetricsInt fontMetricsInt) { - return size != 0 ? size : drawable.getIntrinsicWidth(); + if (sizeWidth != 0) + return (int) (scale * sizeWidth); + return (int) (scale * (size != 0 ? size : drawable.getIntrinsicWidth())); } @Override public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) { int color; - if (usePaintColor) { - color = paint.getColor(); + if (checkColorDelegate != null) { + checkColorDelegate.run(); } else { - color = Theme.getColor(colorKey); - } - if (drawableColor != color) { - drawableColor = color; - drawable.setColorFilter(new PorterDuffColorFilter(drawableColor, PorterDuff.Mode.MULTIPLY)); + if (usePaintColor) { + color = paint.getColor(); + } else { + color = Theme.getColor(colorKey); + } + if (drawableColor != color) { + drawableColor = color; + drawable.setColorFilter(new PorterDuffColorFilter(drawableColor, PorterDuff.Mode.MULTIPLY)); + } } canvas.save(); int transY = bottom - (drawable != null ? drawable.getBounds().bottom : bottom); if (verticalAlignment == ALIGN_BASELINE) { - transY -= paint.getFontMetricsInt().descent; +// transY -= paint.getFontMetricsInt().descent; } else if (verticalAlignment == ALIGN_CENTER) { transY = top + (bottom - top) / 2 - (drawable != null ? drawable.getBounds().height() / 2 : 0); } else if (verticalAlignment == ALIGN_DEFAULT) { @@ -97,19 +114,30 @@ public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, int padding = (lineHeight - drawableHeight) / 2; transY = top + padding + AndroidUtilities.dp(topOffset); } - canvas.translate(x, transY); + canvas.translate(x + translateX, transY); if (drawable != null) { + if (scale != 1f) { + canvas.scale(scale, scale, 0, drawable.getBounds().centerY()); + } drawable.draw(canvas); } canvas.restore(); } - public void setColorKey(String colorKey) { + public void setColorKey(int colorKey) { this.colorKey = colorKey; - usePaintColor = colorKey == null; + usePaintColor = colorKey < 0; } public void setTopOffset(int topOffset) { this.topOffset = topOffset; } + + public void setCheckColorDelegate(Runnable checkColorDelegate) { + this.checkColorDelegate = checkColorDelegate; + } + + public void setScale(float v) { + scale = v; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ContextProgressView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ContextProgressView.java index 15952d2b16..ae2336cc3f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ContextProgressView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ContextProgressView.java @@ -25,8 +25,8 @@ public class ContextProgressView extends View { private int radOffset = 0; private long lastUpdateTime; private int currentColorType; - private String innerKey; - private String outerKey; + private int innerKey; + private int outerKey; private int innerColor; private int outerColor; @@ -54,20 +54,20 @@ public ContextProgressView(Context context, int colorType) { } public void setColors(int innerColor, int outerColor) { - innerKey = null; - outerKey = null; + innerKey = -1; + outerKey = -1; this.innerColor = innerColor; this.outerColor = outerColor; updateColors(); } public void updateColors() { - if (innerKey != null) { + if (innerKey >= 0) { innerPaint.setColor(Theme.getColor(innerKey)); } else { innerPaint.setColor(innerColor); } - if (outerKey != null) { + if (outerKey >= 0) { outerPaint.setColor(Theme.getColor(outerKey)); } else { outerPaint.setColor(outerColor); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/CounterView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/CounterView.java index dcba7af667..78b6c01762 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/CounterView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/CounterView.java @@ -45,7 +45,7 @@ protected void onDraw(Canvas canvas) { } - public void setColors(String textKey, String circleKey) { + public void setColors(int textKey, int circleKey) { counterDrawable.textColorKey = textKey; counterDrawable.circleColorKey = circleKey; } @@ -62,9 +62,8 @@ public void setCount(int count, boolean animated) { counterDrawable.setCount(count, animated); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } public static class CounterDrawable { @@ -96,8 +95,8 @@ public static class CounterDrawable { private int circleColor; private int textColor; - private String textColorKey = Theme.key_chat_goDownButtonCounter; - private String circleColorKey = Theme.key_chat_goDownButtonCounterBackground; + private int textColorKey = Theme.key_chat_goDownButtonCounter; + private int circleColorKey = Theme.key_chat_goDownButtonCounterBackground; int lastH; int width; @@ -453,9 +452,8 @@ public void setParent(View parent) { this.parent = parent; } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + protected int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } public int getWidth() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/CreateGroupCallBottomSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/CreateGroupCallBottomSheet.java new file mode 100644 index 0000000000..b1b4f6167c --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/CreateGroupCallBottomSheet.java @@ -0,0 +1,280 @@ +package org.telegram.ui.Components; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.os.Build; +import android.text.TextUtils; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.core.graphics.ColorUtils; +import androidx.recyclerview.widget.RecyclerView; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ChatObject; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessageObject; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.R; +import org.telegram.tgnet.TLObject; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Cells.GroupCreateUserCell; +import org.telegram.ui.Cells.HeaderCell; +import org.telegram.ui.Cells.ShadowSectionCell; + +import java.util.ArrayList; +import java.util.List; + +public class CreateGroupCallBottomSheet extends BottomSheetWithRecyclerListView { + private static final int + HOLDER_TYPE_HEADER = 0, + HOLDER_TYPE_DIVIDER = 1, + HOLDER_TYPE_SUBTITLE = 2, + HOLDER_TYPE_USER = 3; + + private static final int CONTENT_VIEWS_COUNT = 3; + private static final int CONTAINER_HEIGHT_DP = 120; + + public static void show(ArrayList peers, BaseFragment fragment, long dialogId, JoinCallAlert.JoinCallAlertDelegate joinCallDelegate) { + if (peers.isEmpty()) { + return; + } + CreateGroupCallBottomSheet alert = new CreateGroupCallBottomSheet(fragment, peers, dialogId, joinCallDelegate); + if (fragment != null && fragment.getParentActivity() != null) { + fragment.showDialog(alert); + } else { + alert.show(); + } + } + + private final JoinCallAlert.JoinCallAlertDelegate joinCallDelegate; + private final List chats; + private final boolean needSelector; + private final boolean isChannelOrGiga; + private boolean isScheduleSelected; + private TLRPC.Peer selectedPeer; + private TLRPC.InputPeer selectAfterDismiss; + + public CreateGroupCallBottomSheet(BaseFragment fragment, ArrayList arrayList, long dialogId, JoinCallAlert.JoinCallAlertDelegate joinCallDelegate) { + super(fragment, false, false); + TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-dialogId); + this.topPadding = 0.26f; + this.chats = new ArrayList<>(arrayList); + this.joinCallDelegate = joinCallDelegate; + this.isChannelOrGiga = ChatObject.isChannelOrGiga(chat); + this.selectedPeer = chats.get(0); + this.needSelector = chats.size() > 1; + + Context context = containerView.getContext(); + View divider = new View(context) { + @Override + protected void onDraw(Canvas canvas) { + if (needSelector) { + canvas.drawRect(backgroundPaddingLeft, 0, getMeasuredWidth() - backgroundPaddingLeft, 1, Theme.dividerPaint); + } + } + }; + containerView.addView(divider, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, CONTAINER_HEIGHT_DP, Gravity.BOTTOM, 0, 0, 0, 0)); + + TextView startBtn = new TextView(context); + startBtn.setGravity(Gravity.CENTER); + startBtn.setEllipsize(TextUtils.TruncateAt.END); + startBtn.setSingleLine(true); + startBtn.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + startBtn.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + startBtn.setText(isChannelOrGiga + ? LocaleController.formatString("VoipChannelStartVoiceChat", R.string.VoipChannelStartVoiceChat) + : LocaleController.formatString("VoipGroupStartVoiceChat", R.string.VoipGroupStartVoiceChat) + ); + startBtn.setTextColor(Theme.getColor(Theme.key_featuredStickers_buttonText)); + startBtn.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(8), Theme.getColor(Theme.key_featuredStickers_addButton), ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_windowBackgroundWhite), 120))); + containerView.addView(startBtn, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.BOTTOM, 16, 0, 16, 6 + 48 + 6)); + + TextView scheduleBtn = new TextView(context); + scheduleBtn.setGravity(Gravity.CENTER); + scheduleBtn.setEllipsize(TextUtils.TruncateAt.END); + scheduleBtn.setSingleLine(true); + scheduleBtn.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + scheduleBtn.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + scheduleBtn.setText(isChannelOrGiga + ? LocaleController.formatString("VoipChannelScheduleVoiceChat", R.string.VoipChannelScheduleVoiceChat) + : LocaleController.formatString("VoipGroupScheduleVoiceChat", R.string.VoipGroupScheduleVoiceChat) + ); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + scheduleBtn.setLetterSpacing(0.025f); + } + scheduleBtn.setTextColor(Theme.getColor(Theme.key_featuredStickers_addButton)); + scheduleBtn.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(8), Color.TRANSPARENT, ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_featuredStickers_addButton), 120))); + containerView.addView(scheduleBtn, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.BOTTOM, 16, 0, 16, 6)); + + startBtn.setOnClickListener(view -> { + selectAfterDismiss = MessagesController.getInstance(currentAccount).getInputPeer(MessageObject.getPeerId(selectedPeer)); + dismiss(); + }); + scheduleBtn.setOnClickListener(view -> { + selectAfterDismiss = MessagesController.getInstance(currentAccount).getInputPeer(MessageObject.getPeerId(selectedPeer)); + isScheduleSelected = true; + dismiss(); + }); + + recyclerListView.setPadding(backgroundPaddingLeft, 0, backgroundPaddingLeft, AndroidUtilities.dp(CONTAINER_HEIGHT_DP)); + recyclerListView.setOnItemClickListener((view, position) -> { + if (position <= CONTENT_VIEWS_COUNT) { + return; + } + selectedPeer = chats.get(position - CONTENT_VIEWS_COUNT - 1); + if (view instanceof GroupCreateUserCell) { + ((GroupCreateUserCell) view).setChecked(true, true); + } + for (int i = 0; i < recyclerListView.getChildCount(); i++) { + View child = recyclerListView.getChildAt(i); + if (child != view) { + if (child instanceof GroupCreateUserCell) { + ((GroupCreateUserCell) child).setChecked(false, true); + } + } + } + }); + + fixNavigationBar(); + updateTitle(); + } + + @Override + public void dismissInternal() { + super.dismissInternal(); + if (selectAfterDismiss != null) { + joinCallDelegate.didSelectChat(selectAfterDismiss, chats.size() > 1, isScheduleSelected); + } + } + + @Override + protected CharSequence getTitle() { + if (isChannelOrGiga) { + return LocaleController.getString("StartVoipChannelTitle", R.string.StartVoipChannelTitle); + } else { + return LocaleController.getString("StartVoipChatTitle", R.string.StartVoipChatTitle); + } + } + + @Override + public RecyclerListView.SelectionAdapter createAdapter() { + return new RecyclerListView.SelectionAdapter() { + @Override + public boolean isEnabled(RecyclerView.ViewHolder holder) { + return holder.getItemViewType() == HOLDER_TYPE_USER; + } + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view; + Context context = parent.getContext(); + switch (viewType) { + default: + case HOLDER_TYPE_HEADER: + view = new TopCell(context, isChannelOrGiga); + break; + case HOLDER_TYPE_DIVIDER: + view = new ShadowSectionCell(context, 12, Theme.getColor(Theme.key_windowBackgroundGray)); + break; + case HOLDER_TYPE_SUBTITLE: + view = new HeaderCell(context, 22); + break; + case HOLDER_TYPE_USER: + view = new GroupCreateUserCell(context, 1, 0, false); + break; + } + view.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + return new RecyclerListView.Holder(view); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + if (holder.getItemViewType() == HOLDER_TYPE_USER) { + TLRPC.Peer peer = chats.get(position - CONTENT_VIEWS_COUNT); + long did = MessageObject.getPeerId(peer); + TLObject object; + String status; + if (did > 0) { + object = MessagesController.getInstance(currentAccount).getUser(did); + status = LocaleController.getString("VoipGroupPersonalAccount", R.string.VoipGroupPersonalAccount); + } else { + object = MessagesController.getInstance(currentAccount).getChat(-did); + status = null; + } + GroupCreateUserCell cell = (GroupCreateUserCell) holder.itemView; + cell.setObject(object, null, status, position != getItemCount() - 1); + cell.setChecked(peer == selectedPeer, false); + } else if (holder.getItemViewType() == HOLDER_TYPE_SUBTITLE) { + HeaderCell cell = (HeaderCell) holder.itemView; + cell.setTextSize(15); + cell.setPadding(0, 0, 0, AndroidUtilities.dp(2)); + cell.setText(LocaleController.getString("VoipChatDisplayedAs", R.string.VoipChatDisplayedAs).replace(":", "")); + } + } + + @Override + public int getItemViewType(int position) { + switch (position) { + case 0: + return HOLDER_TYPE_HEADER; + case 1: + return HOLDER_TYPE_DIVIDER; + case 2: + return HOLDER_TYPE_SUBTITLE; + default: + return HOLDER_TYPE_USER; + } + } + + @Override + public int getItemCount() { + return needSelector ? CONTENT_VIEWS_COUNT + chats.size() : 1; + } + }; + } + + private static class TopCell extends LinearLayout { + + public TopCell(Context context, boolean isChannelOrGiga) { + super(context); + setOrientation(LinearLayout.VERTICAL); + + RLottieImageView imageView = new RLottieImageView(context); + imageView.setAutoRepeat(true); + imageView.setAnimation(R.raw.utyan_schedule, 112, 112); + imageView.playAnimation(); + addView(imageView, LayoutHelper.createLinear(112, 112, Gravity.CENTER_HORIZONTAL | Gravity.TOP, 0, 24, 0, 0)); + + TextView title = new TextView(context); + title.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + title.setText(isChannelOrGiga + ? LocaleController.formatString("StartVoipChannelTitle", R.string.StartVoipChannelTitle) + : LocaleController.formatString("StartVoipChatTitle", R.string.StartVoipChatTitle) + ); + title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); + title.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + addView(title, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 0, 14, 0, 7)); + + TextView description = new TextView(context); + description.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + description.setGravity(Gravity.CENTER_HORIZONTAL); + description.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); + description.setText(isChannelOrGiga + ? LocaleController.formatString("VoipChannelStart2", R.string.VoipChannelStart2) + : LocaleController.formatString("VoipGroupStart2", R.string.VoipGroupStart2) + ); + description.setLineSpacing(description.getLineSpacingExtra(), description.getLineSpacingMultiplier() * 1.1f); + addView(description, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 28, 0, 28, 17)); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Crop/CropView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Crop/CropView.java index bcd60e981f..f294342d96 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Crop/CropView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Crop/CropView.java @@ -451,6 +451,9 @@ public void updateMatrix() { } public void updateMatrix(boolean force) { + if (state == null) { + return; + } overlayMatrix.reset(); if (state.getBaseRotation() == 90 || state.getBaseRotation() == 270) { overlayMatrix.postTranslate(-state.getHeight() / 2, -state.getWidth() / 2); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/CrossOutDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/CrossOutDrawable.java index d9d1650769..73d1923831 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/CrossOutDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/CrossOutDrawable.java @@ -27,7 +27,7 @@ public class CrossOutDrawable extends Drawable { final Paint xRefPaint = new Paint(Paint.ANTI_ALIAS_FLAG); int color; - String colorKey; + int colorKey; float progress; boolean cross; @@ -35,7 +35,7 @@ public class CrossOutDrawable extends Drawable { private float lenOffsetTop; private float lenOffsetBottom; - public CrossOutDrawable(Context context, int iconRes, String colorKey) { + public CrossOutDrawable(Context context, int iconRes, int colorKey) { iconDrawable = ContextCompat.getDrawable(context, iconRes); this.colorKey = colorKey; paint.setStyle(Paint.Style.STROKE); @@ -74,7 +74,7 @@ public void draw(@NonNull Canvas canvas) { progress = 0; } } - int newColor = colorKey == null ? Color.WHITE : Theme.getColor(colorKey); + int newColor = colorKey < 0 ? Color.WHITE : Theme.getColor(colorKey); if (color != newColor) { color = newColor; paint.setColor(newColor); @@ -136,7 +136,7 @@ public int getOpacity() { return PixelFormat.TRANSPARENT; } - public void setColorKey(String colorKey) { + public void setColorKey(int colorKey) { this.colorKey = colorKey; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/CrossfadeDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/CrossfadeDrawable.java index 9b173c9a7f..c031800fca 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/CrossfadeDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/CrossfadeDrawable.java @@ -52,9 +52,17 @@ public void invalidateDrawable(@NonNull Drawable drawable) { } } @Override - public void scheduleDrawable(@NonNull Drawable drawable, @NonNull Runnable runnable, long l) {} + public void scheduleDrawable(@NonNull Drawable drawable, @NonNull Runnable runnable, long l) { + if (progress > 0.0f) { + CrossfadeDrawable.this.scheduleSelf(runnable, l); + } + } @Override - public void unscheduleDrawable(@NonNull Drawable drawable, @NonNull Runnable runnable) {} + public void unscheduleDrawable(@NonNull Drawable drawable, @NonNull Runnable runnable) { + if (progress > 0.0f) { + CrossfadeDrawable.this.unscheduleSelf(runnable); + } + } }); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/CubicBezierInterpolator.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/CubicBezierInterpolator.java index db7058180a..e29b0a2c04 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/CubicBezierInterpolator.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/CubicBezierInterpolator.java @@ -1,7 +1,12 @@ package org.telegram.ui.Components; import android.graphics.PointF; +import android.os.Build; import android.view.animation.Interpolator; +import android.view.animation.LinearInterpolator; +import android.view.animation.PathInterpolator; + +import androidx.core.graphics.PathParser; public class CubicBezierInterpolator implements Interpolator { @@ -11,7 +16,7 @@ public class CubicBezierInterpolator implements Interpolator { public static final CubicBezierInterpolator EASE_IN = new CubicBezierInterpolator(.42, 0, 1, 1); public static final CubicBezierInterpolator EASE_BOTH = new CubicBezierInterpolator(.42, 0, .58, 1); public static final CubicBezierInterpolator EASE_OUT_BACK = new CubicBezierInterpolator(.34, 1.56, .64, 1); - + public static final Interpolator Emphasized = Build.VERSION.SDK_INT >= 21 ? new PathInterpolator(PathParser.createPathFromPathData("M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1")) : new LinearInterpolator(); protected PointF start; protected PointF end; protected PointF a = new PointF(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/CustomPopupMenu.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/CustomPopupMenu.java new file mode 100644 index 0000000000..037a9abe0b --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/CustomPopupMenu.java @@ -0,0 +1,76 @@ +package org.telegram.ui.Components; + +import android.content.Context; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.View; +import android.view.WindowManager; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.R; +import org.telegram.ui.ActionBar.ActionBarPopupWindow; +import org.telegram.ui.ActionBar.Theme; + +public abstract class CustomPopupMenu { + + ActionBarPopupWindow.ActionBarPopupWindowLayout popupLayout; + ActionBarPopupWindow popupWindow; + boolean isShowing; + + public CustomPopupMenu(Context context, Theme.ResourcesProvider resourcesProvider, boolean containsSwipeBack) { + popupLayout = new ActionBarPopupWindow.ActionBarPopupWindowLayout(context, R.drawable.popup_fixed_alert2, resourcesProvider, containsSwipeBack ? ActionBarPopupWindow.ActionBarPopupWindowLayout.FLAG_USE_SWIPEBACK : 0); + popupLayout.setAnimationEnabled(false); + popupLayout.setOnTouchListener((v, event) -> { + if (event.getActionMasked() == MotionEvent.ACTION_UP) { + if (popupWindow != null && popupWindow.isShowing()) { + v.getHitRect(AndroidUtilities.rectTmp2); + if (!AndroidUtilities.rectTmp2.contains((int) event.getX(), (int) event.getY())) { + popupWindow.dismiss(); + } + } + } + return false; + }); + popupLayout.setDispatchKeyEventListener(keyEvent -> { + if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_BACK && keyEvent.getRepeatCount() == 0 && popupWindow != null && popupWindow.isShowing()) { + popupWindow.dismiss(); + } + }); + popupLayout.setShownFromBottom(false); + + onCreate(popupLayout); + + popupWindow = new ActionBarPopupWindow(popupLayout, LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT); + popupWindow.setAnimationEnabled(false); + popupWindow.setAnimationStyle(R.style.PopupContextAnimation2); + popupWindow.setOutsideTouchable(true); + popupWindow.setClippingEnabled(true); + popupWindow.setInputMethodMode(ActionBarPopupWindow.INPUT_METHOD_NOT_NEEDED); + popupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED); + popupWindow.getContentView().setFocusableInTouchMode(true); + popupWindow.setOnDismissListener(() -> { + onDismissed(); + isShowing = false; + }); + } + + public void show(View anchorView, int x, int y) { + isShowing = true; + popupWindow.showAsDropDown(anchorView, x, y); + } + + + protected abstract void onCreate(ActionBarPopupWindow.ActionBarPopupWindowLayout popupLayout); + + protected abstract void onDismissed(); + + public void dismiss() { + if (popupWindow != null) { + popupWindow.dismiss(); + } + } + + public boolean isShowing() { + return isShowing; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/DotDividerSpan.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/DotDividerSpan.java index fc15c508bf..b886854f12 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/DotDividerSpan.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/DotDividerSpan.java @@ -14,10 +14,11 @@ public class DotDividerSpan extends ReplacementSpan { Paint p = new Paint(Paint.ANTI_ALIAS_FLAG); int color; int topPadding; + private int size = 3; @Override public int getSize(@NonNull Paint paint, CharSequence charSequence, int i, int i1, @Nullable Paint.FontMetricsInt fontMetricsInt) { - return AndroidUtilities.dp(3); + return AndroidUtilities.dp(size); } @Override @@ -25,11 +26,16 @@ public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, if (color != paint.getColor()) { p.setColor(paint.getColor()); } + float offset = AndroidUtilities.dpf2(size) / 2f; float radius = AndroidUtilities.dpf2(3) / 2f; - canvas.drawCircle(x + radius, (bottom - top) / 2 + topPadding, radius, p); + canvas.drawCircle(x + offset, (bottom - top) / 2 + topPadding, radius, p); } public void setTopPadding(int topPadding) { this.topPadding = topPadding; } + + public void setSize(int size) { + this.size = size; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/DownloadsInfoBottomSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/DownloadsInfoBottomSheet.java new file mode 100644 index 0000000000..d9d23e8bb1 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/DownloadsInfoBottomSheet.java @@ -0,0 +1,115 @@ +package org.telegram.ui.Components; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Color; +import android.os.Build; +import android.text.TextUtils; +import android.util.TypedValue; +import android.view.Gravity; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.core.graphics.ColorUtils; +import androidx.core.widget.NestedScrollView; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.DownloadController; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.R; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.BottomSheet; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.CacheControlActivity; + +public class DownloadsInfoBottomSheet extends BottomSheet { + + public static void show(Activity parentActivity, BaseFragment parentFragment) { + if (parentFragment == null || parentActivity == null) { + return; + } + new DownloadsInfoBottomSheet(parentActivity, parentFragment, false).show(); + } + + public DownloadsInfoBottomSheet(Context context, BaseFragment parentFragment, boolean needFocus) { + super(context, needFocus); + setApplyBottomPadding(false); + setApplyTopPadding(false); + fixNavigationBar(getThemedColor(Theme.key_windowBackgroundWhite)); + + LinearLayout linearLayout = new LinearLayout(context); + linearLayout.setOrientation(LinearLayout.VERTICAL); + + FrameLayout frameLayout = new FrameLayout(context); + frameLayout.addView(linearLayout); + + ImageView closeView = new ImageView(context); + closeView.setBackground(Theme.createSelectorDrawable(getThemedColor(Theme.key_listSelector))); + closeView.setColorFilter(getThemedColor(Theme.key_sheet_other)); + closeView.setImageResource(R.drawable.ic_layer_close); + closeView.setOnClickListener((view) -> dismiss()); + int closeViewPadding = AndroidUtilities.dp(8); + closeView.setPadding(closeViewPadding, closeViewPadding, closeViewPadding, closeViewPadding); + frameLayout.addView(closeView, LayoutHelper.createFrame(36, 36, Gravity.TOP | Gravity.END, 6, 8, 8, 0)); + + StickerImageView imageView = new StickerImageView(context, currentAccount); + imageView.setStickerNum(9); + imageView.getImageReceiver().setAutoRepeat(1); + linearLayout.addView(imageView, LayoutHelper.createLinear(110, 110, Gravity.CENTER_HORIZONTAL, 0, 26, 0, 0)); + + TextView title = new TextView(context); + title.setGravity(Gravity.CENTER_HORIZONTAL); + title.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); + title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); + title.setText(LocaleController.getString("DownloadedFiles", R.string.DownloadedFiles)); + linearLayout.addView(title, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 21, 20, 21, 0)); + + TextView description = new TextView(context); + description.setGravity(Gravity.CENTER_HORIZONTAL); + description.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + description.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); + description.setLineSpacing(description.getLineSpacingExtra(), description.getLineSpacingMultiplier() * 1.1f); + description.setText(LocaleController.formatString("DownloadedFilesMessage", R.string.DownloadedFilesMessage)); + linearLayout.addView(description, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 28, 7, 28, 0)); + + TextView storageBtn = new TextView(context); + storageBtn.setGravity(Gravity.CENTER); + storageBtn.setEllipsize(TextUtils.TruncateAt.END); + storageBtn.setSingleLine(true); + storageBtn.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + storageBtn.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + storageBtn.setText(LocaleController.getString("ManageDeviceStorage", R.string.ManageDeviceStorage)); + storageBtn.setTextColor(Theme.getColor(Theme.key_featuredStickers_buttonText)); + storageBtn.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(8), Theme.getColor(Theme.key_featuredStickers_addButton), ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_windowBackgroundWhite), 120))); + linearLayout.addView(storageBtn, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, 0, 14, 28, 14, 6)); + + TextView clearBtn = new TextView(context); + clearBtn.setGravity(Gravity.CENTER); + clearBtn.setEllipsize(TextUtils.TruncateAt.END); + clearBtn.setSingleLine(true); + clearBtn.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + clearBtn.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + clearBtn.setText(LocaleController.getString("ClearDownloadsList", R.string.ClearDownloadsList)); + clearBtn.setTextColor(Theme.getColor(Theme.key_featuredStickers_addButton)); + clearBtn.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(8), Color.TRANSPARENT, ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_featuredStickers_addButton), 120))); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + clearBtn.setLetterSpacing(0.025f); + } + linearLayout.addView(clearBtn, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, 0, 14, 0, 14, 6)); + + NestedScrollView scrollView = new NestedScrollView(context); + scrollView.addView(frameLayout); + setCustomView(scrollView); + + storageBtn.setOnClickListener(view -> { + dismiss(); + parentFragment.presentFragment(new CacheControlActivity()); + }); + clearBtn.setOnClickListener(view -> { + dismiss(); + DownloadController.getInstance(currentAccount).clearRecentDownloadedFiles(); + }); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextBoldCursor.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextBoldCursor.java index d5cd5cdee7..ec4df0007c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextBoldCursor.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextBoldCursor.java @@ -113,6 +113,7 @@ public void run() { private float cursorWidth = 2.0f; private boolean supportRtlHint; + public boolean ignoreClipTop; private boolean cursorDrawn; private boolean lineVisible = false; @@ -672,6 +673,9 @@ public void setLineSpacing(float add, float mult) { @Override public int getExtendedPaddingTop() { + if (ignoreClipTop) { + return 0; + } if (ignoreTopCount != 0) { ignoreTopCount--; return 0; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextCaption.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextCaption.java index c0075fad4e..ac34000208 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextCaption.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextCaption.java @@ -151,6 +151,7 @@ public void makeSelectedSpoiler() { TextStyleSpan.TextStyleRun run = new TextStyleSpan.TextStyleRun(); run.flags |= TextStyleSpan.FLAG_STYLE_SPOILER; applyTextStyleToSelection(new TextStyleSpan(run)); + invalidateSpoilers(); } public void makeSelectedItalic() { @@ -420,7 +421,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { editText.setSingleLine(true); editText.setFocusable(true); editText.setTransformHintToHeader(true); - editText.setLineColors(getThemedColor(Theme.key_windowBackgroundWhiteInputField), getThemedColor(Theme.key_windowBackgroundWhiteInputFieldActivated), getThemedColor(Theme.key_windowBackgroundWhiteRedText3)); + editText.setLineColors(getThemedColor(Theme.key_windowBackgroundWhiteInputField), getThemedColor(Theme.key_windowBackgroundWhiteInputFieldActivated), getThemedColor(Theme.key_text_RedRegular)); editText.setImeOptions(EditorInfo.IME_ACTION_DONE); editText.setBackgroundDrawable(null); editText.requestFocus(); @@ -764,9 +765,8 @@ public boolean performAccessibilityAction(int action, Bundle arguments) { return performMenuAction(action) || super.performAccessibilityAction(action, arguments); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextEffects.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextEffects.java index 85780b7eed..225e950526 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextEffects.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextEffects.java @@ -2,7 +2,10 @@ import android.content.Context; import android.graphics.Canvas; +import android.graphics.ColorFilter; import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; import android.graphics.Region; import android.os.Looper; @@ -29,24 +32,17 @@ public class EditTextEffects extends EditText { private boolean isSpoilersRevealed; private boolean shouldRevealSpoilersByTouch = true; private SpoilersClickDetector clickDetector; - private boolean suppressOnTextChanged; + public boolean suppressOnTextChanged; private Path path = new Path(); private int selStart, selEnd; private float lastRippleX, lastRippleY; private boolean postedSpoilerTimeout; private AnimatedEmojiSpan.EmojiGroupedSpans animatedEmojiDrawables; - protected boolean animatedEmojiRawDraw; - protected int animatedEmojiRawDrawFps; - protected int animatedEmojiOffsetX; + private ColorFilter animatedEmojiColorFilter; + public boolean drawAnimatedEmojiDrawables = true; private Layout lastLayout = null; private int lastTextLength; - public void incrementFrames(int frames) { - if (animatedEmojiDrawables != null) { - animatedEmojiDrawables.incrementFrames(frames); - } - } - private Runnable spoilerTimeout = () -> { postedSpoilerTimeout = false; isSpoilersRevealed = false; @@ -200,6 +196,12 @@ public void setText(CharSequence text, BufferType type) { super.setText(text, type); } + @Override + public void setTextColor(int color) { + super.setTextColor(color); + animatedEmojiColorFilter = new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN); + } + @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); @@ -266,14 +268,10 @@ protected void onDraw(Canvas canvas) { canvas.clipPath(path, Region.Op.DIFFERENCE); updateAnimatedEmoji(false); super.onDraw(canvas); - if (animatedEmojiDrawables != null) { + if (drawAnimatedEmojiDrawables && animatedEmojiDrawables != null) { canvas.save(); - canvas.translate(animatedEmojiOffsetX, 0); - if (animatedEmojiRawDraw) { - AnimatedEmojiSpan.drawRawAnimatedEmojis(canvas, getLayout(), animatedEmojiDrawables, 0, spoilers, computeVerticalScrollOffset() - AndroidUtilities.dp(6), computeVerticalScrollOffset() + computeVerticalScrollExtent(), 0, 1f, animatedEmojiRawDrawFps); - } else { - AnimatedEmojiSpan.drawAnimatedEmojis(canvas, getLayout(), animatedEmojiDrawables, 0, spoilers, computeVerticalScrollOffset() - AndroidUtilities.dp(6), computeVerticalScrollOffset() + computeVerticalScrollExtent(), 0, 1f); - } + canvas.translate(getPaddingLeft(), 0); + AnimatedEmojiSpan.drawAnimatedEmojis(canvas, getLayout(), animatedEmojiDrawables, 0, spoilers, computeVerticalScrollOffset() - AndroidUtilities.dp(6), computeVerticalScrollOffset() + computeVerticalScrollExtent(), 0, 1f, animatedEmojiColorFilter); canvas.restore(); } canvas.restore(); @@ -302,6 +300,9 @@ protected void onDraw(Canvas canvas) { } public void updateAnimatedEmoji(boolean force) { + if (!drawAnimatedEmojiDrawables) { + return; + } int newTextLength = (getLayout() == null || getLayout().getText() == null) ? 0 : getLayout().getText().length(); if (force || lastLayout != getLayout() || lastTextLength != newTextLength) { animatedEmojiDrawables = AnimatedEmojiSpan.update(AnimatedEmojiDrawable.getCacheTypeForEnterView(), this, animatedEmojiDrawables, getLayout()); @@ -322,7 +323,7 @@ public void invalidateEffects() { invalidateSpoilers(); } - private void invalidateSpoilers() { + protected void invalidateSpoilers() { if (spoilers == null) return; // A null-check for super constructor, because it calls onTextChanged spoilersPool.addAll(spoilers); spoilers.clear(); @@ -334,11 +335,11 @@ private void invalidateSpoilers() { Layout layout = getLayout(); if (layout != null && layout.getText() instanceof Spannable) { - if (animatedEmojiDrawables != null) { + if (drawAnimatedEmojiDrawables && animatedEmojiDrawables != null) { animatedEmojiDrawables.recordPositions(false); } SpoilerEffect.addSpoilers(this, spoilersPool, spoilers); - if (animatedEmojiDrawables != null) { + if (drawAnimatedEmojiDrawables && animatedEmojiDrawables != null) { animatedEmojiDrawables.recordPositions(true); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextEmoji.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextEmoji.java index e54f0ced35..c13ed1ec4f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextEmoji.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextEmoji.java @@ -7,6 +7,7 @@ import android.app.Dialog; import android.content.Context; import android.content.ContextWrapper; +import android.graphics.Canvas; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.os.Build; @@ -16,14 +17,20 @@ import android.text.SpannableString; import android.text.Spanned; import android.util.TypedValue; +import android.view.ActionMode; +import android.view.ContextMenu; import android.view.Gravity; import android.view.KeyEvent; +import android.view.Menu; import android.view.MotionEvent; import android.view.View; +import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; import android.widget.FrameLayout; import android.widget.ImageView; +import androidx.core.graphics.ColorUtils; + import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.Emoji; import org.telegram.messenger.FileLog; @@ -37,7 +44,9 @@ import org.telegram.ui.ActionBar.AdjustPanLayoutHelper; import org.telegram.ui.ActionBar.AlertDialog; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.FloatingToolbar; import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.ChatActivity; import org.telegram.ui.Components.Premium.PremiumFeatureBottomSheet; import org.telegram.ui.PremiumPreviewFragment; @@ -62,6 +71,7 @@ public class EditTextEmoji extends FrameLayout implements NotificationCenter.Not private boolean lastSizeChangeValue2; private int innerTextChange; private boolean allowAnimatedEmoji; + public boolean includeNavigationBar; AdjustPanLayoutHelper adjustPanLayoutHelper; private EditTextEmojiDelegate delegate; @@ -71,6 +81,7 @@ public class EditTextEmoji extends FrameLayout implements NotificationCenter.Not public static final int STYLE_FRAGMENT = 0; public static final int STYLE_DIALOG = 1; + public static final int STYLE_STORY = 2; private boolean waitingForKeyboardOpen; private boolean isAnimatePopupClosing; @@ -125,6 +136,7 @@ public EditTextEmoji(Context context, SizeNotifierFrameLayout parent, BaseFragme @Override public boolean onTouchEvent(MotionEvent event) { if (isPopupShowing() && event.getAction() == MotionEvent.ACTION_DOWN) { + onWaitingForKeyboard(); showPopup(AndroidUtilities.usingHardwareInput ? 0 : 2); openKeyboardInternal(); } @@ -147,24 +159,56 @@ public boolean onTouchEvent(MotionEvent event) { protected void onLineCountChanged(int oldLineCount, int newLineCount) { EditTextEmoji.this.onLineCountChanged(oldLineCount, newLineCount); } + + @Override + protected int getActionModeStyle() { + if (style == STYLE_STORY) { + return FloatingToolbar.STYLE_BLACK; + } + return super.getActionModeStyle(); + } + + @Override + protected void extendActionMode(ActionMode actionMode, Menu menu) { + if (style == STYLE_STORY) { + ChatActivity.fillActionModeMenu(menu, null); + } + super.extendActionMode(actionMode, menu); + } }; editText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); editText.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI); editText.setInputType(editText.getInputType() | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES); - editText.setMaxLines(4); editText.setFocusable(editText.isEnabled()); editText.setCursorSize(AndroidUtilities.dp(20)); editText.setCursorWidth(1.5f); editText.setCursorColor(getThemedColor(Theme.key_windowBackgroundWhiteBlackText)); if (style == STYLE_FRAGMENT) { + editText.setMaxLines(4); editText.setGravity(Gravity.CENTER_VERTICAL | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT)); editText.setBackground(null); - editText.setLineColors(getThemedColor(Theme.key_windowBackgroundWhiteInputField), getThemedColor(Theme.key_windowBackgroundWhiteInputFieldActivated), getThemedColor(Theme.key_windowBackgroundWhiteRedText3)); + editText.setLineColors(getThemedColor(Theme.key_windowBackgroundWhiteInputField), getThemedColor(Theme.key_windowBackgroundWhiteInputFieldActivated), getThemedColor(Theme.key_text_RedRegular)); editText.setHintTextColor(getThemedColor(Theme.key_windowBackgroundWhiteHintText)); editText.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteBlackText)); editText.setPadding(LocaleController.isRTL ? AndroidUtilities.dp(40) : 0, 0, LocaleController.isRTL ? 0 : AndroidUtilities.dp(40), AndroidUtilities.dp(8)); addView(editText, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.CENTER_VERTICAL, LocaleController.isRTL ? 11 : 0, 1, LocaleController.isRTL ? 0 : 11, 0)); + } else if (style == STYLE_STORY) { + editText.setMaxLines(4); + editText.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT); + editText.setAllowTextEntitiesIntersection(true); + editText.setHintTextColor(0x8cffffff); + editText.setTextColor(0xffffffff); + editText.setCursorColor(0xffffffff); + editText.setBackground(null); + editText.setClipToPadding(false); + editText.setPadding(0, AndroidUtilities.dp(9), 0, AndroidUtilities.dp(9)); + editText.setHandlesColor(0xffffffff); + editText.setHighlightColor(0x30ffffff); + editText.setLinkTextColor(0xFF46A3EB); + editText.setTextIsSelectable(true); + addView(editText, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.CENTER_VERTICAL, 40, 0, 24, 0)); } else { + editText.setMaxLines(4); editText.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT); editText.setHintTextColor(getThemedColor(Theme.key_dialogTextHint)); editText.setTextColor(getThemedColor(Theme.key_dialogTextBlack)); @@ -176,11 +220,16 @@ protected void onLineCountChanged(int oldLineCount, int newLineCount) { emojiButton = new ImageView(context); emojiButton.setScaleType(ImageView.ScaleType.CENTER_INSIDE); emojiButton.setImageDrawable(emojiIconDrawable = new ReplaceableIconDrawable(context)); - emojiIconDrawable.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_chat_messagePanelIcons), PorterDuff.Mode.SRC_IN)); if (style == STYLE_FRAGMENT) { + emojiIconDrawable.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_chat_messagePanelIcons), PorterDuff.Mode.MULTIPLY)); emojiIconDrawable.setIcon(R.drawable.smiles_tab_smiles, false); addView(emojiButton, LayoutHelper.createFrame(48, 48, Gravity.CENTER_VERTICAL | (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT), 0, 0, 0, 7)); + } else if (style == STYLE_STORY) { + emojiIconDrawable.setColorFilter(new PorterDuffColorFilter(0x8cffffff, PorterDuff.Mode.MULTIPLY)); + emojiIconDrawable.setIcon(R.drawable.input_smile, false); + addView(emojiButton, LayoutHelper.createFrame(40, 40, Gravity.BOTTOM | Gravity.LEFT, 0, 0, 0, 0)); } else { + emojiIconDrawable.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_chat_messagePanelIcons), PorterDuff.Mode.MULTIPLY)); emojiIconDrawable.setIcon(R.drawable.input_smile, false); addView(emojiButton, LayoutHelper.createFrame(48, 48, Gravity.BOTTOM | Gravity.LEFT, 0, 0, 0, 0)); } @@ -202,6 +251,18 @@ protected void onLineCountChanged(int oldLineCount, int newLineCount) { emojiButton.setContentDescription(LocaleController.getString("Emoji", R.string.Emoji)); } + public void setSuggestionsEnabled(boolean enabled) { + int inputType = editText.getInputType(); + if (!enabled) { + inputType |= EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS; + } else { + inputType &= ~EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS; + } + if (editText.getInputType() != inputType) { + editText.setInputType(inputType); + } + } + protected void onLineCountChanged(int oldLineCount, int newLineCount) { } @@ -269,6 +330,7 @@ public void onResume() { AndroidUtilities.showKeyboard(editText); if (!AndroidUtilities.usingHardwareInput && !keyboardVisible && !AndroidUtilities.isInMultiwindow && !AndroidUtilities.isTablet()) { waitingForKeyboardOpen = true; + onWaitingForKeyboard(); AndroidUtilities.cancelRunOnUIThread(openKeyboardRunnable); AndroidUtilities.runOnUIThread(openKeyboardRunnable, 100); } @@ -291,6 +353,12 @@ public void updateColors() { editText.setHintTextColor(getThemedColor(Theme.key_windowBackgroundWhiteHintText)); editText.setCursorColor(getThemedColor(Theme.key_windowBackgroundWhiteBlackText)); editText.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteBlackText)); + } else if (currentStyle == STYLE_STORY) { + editText.setHintTextColor(0x8cffffff); + editText.setTextColor(0xffffffff); + editText.setCursorColor(0xffffffff); + editText.setHandlesColor(0xffffffff); + editText.setHighlightColor(0x30ffffff); } else { editText.setHintTextColor(getThemedColor(Theme.key_dialogTextHint)); editText.setTextColor(getThemedColor(Theme.key_dialogTextBlack)); @@ -336,11 +404,15 @@ public void hidePopup(boolean byBackButton) { if (byBackButton) { if (emojiView != null && emojiView.getVisibility() == View.VISIBLE && !waitingForKeyboardOpen) { int height = emojiView.getMeasuredHeight(); - ValueAnimator animator = ValueAnimator.ofFloat(0, height); + if (emojiView.getParent() instanceof ViewGroup) { + height += ((ViewGroup) emojiView.getParent()).getHeight() - emojiView.getBottom(); + } + final int finalHeight = height; + ValueAnimator animator = ValueAnimator.ofFloat(0, finalHeight); animator.addUpdateListener(animation -> { float v = (float) animation.getAnimatedValue(); emojiView.setTranslationY(v); - bottomPanelTranslationY(v - height); + bottomPanelTranslationY(v - finalHeight); }); isAnimatePopupClosing = true; animator.addListener(new AnimatorListenerAdapter() { @@ -382,6 +454,7 @@ public boolean isKeyboardVisible() { } protected void openKeyboardInternal() { + onWaitingForKeyboard(); showPopup(AndroidUtilities.usingHardwareInput || isPaused ? 0 : 2); editText.requestFocus(); AndroidUtilities.showKeyboard(editText); @@ -401,6 +474,7 @@ protected void showPopup(int show) { emojiView.setVisibility(VISIBLE); emojiViewVisible = true; + onEmojiKeyboardUpdate(); View currentView = emojiView; if (keyboardHeight <= 0) { @@ -417,7 +491,7 @@ protected void showPopup(int show) { keyboardHeightLand = MessagesController.getGlobalEmojiSettings().getInt("kbd_height_land3", AndroidUtilities.dp(200)); } } - int currentHeight = AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y ? keyboardHeightLand : keyboardHeight; + int currentHeight = (AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y ? keyboardHeightLand : keyboardHeight) + (includeNavigationBar ? AndroidUtilities.navigationBarHeight : 0); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) currentView.getLayoutParams(); layoutParams.height = currentHeight; @@ -454,13 +528,14 @@ public void onAnimationEnd(Animator animation) { } else { if (emojiButton != null) { if (currentStyle == STYLE_FRAGMENT) { - emojiIconDrawable.setIcon(R.drawable.smiles_tab_smiles, true); + emojiIconDrawable.setIcon(R.drawable.smiles_tab_smiles, true); } else { - emojiIconDrawable.setIcon(R.drawable.input_smile, true); + emojiIconDrawable.setIcon(R.drawable.input_smile, true); } } if (emojiView != null) { emojiViewVisible = false; + onEmojiKeyboardUpdate(); if (AndroidUtilities.usingHardwareInput || AndroidUtilities.isInMultiwindow) { emojiView.setVisibility(GONE); } @@ -489,6 +564,10 @@ protected void closeParent() { } + protected void drawEmojiBackground(Canvas canvas, View view) { + + } + protected void createEmojiView() { if (emojiView != null && emojiView.currentAccount != UserConfig.selectedAccount) { sizeNotifierLayout.removeView(emojiView); @@ -497,7 +576,15 @@ protected void createEmojiView() { if (emojiView != null) { return; } - emojiView = new EmojiView(parentFragment, allowAnimatedEmoji, false, false, getContext(), false, null, null, resourcesProvider); + emojiView = new EmojiView(parentFragment, allowAnimatedEmoji, false, false, getContext(), false, null, null, currentStyle != STYLE_STORY, resourcesProvider) { + @Override + protected void dispatchDraw(Canvas canvas) { + if (currentStyle == STYLE_STORY) { + drawEmojiBackground(canvas, this); + } + super.dispatchDraw(canvas); + } + }; emojiView.setVisibility(GONE); if (AndroidUtilities.isTablet()) { emojiView.setForseMultiwindowLayout(true); @@ -618,6 +705,9 @@ public void onClearEmojiRecent() { sizeNotifierLayout.addView(emojiView); } + protected void onEmojiKeyboardUpdate() {} + protected void onWaitingForKeyboard() {} + public boolean isPopupView(View view) { return view == emojiView; } @@ -626,9 +716,13 @@ public int getEmojiPadding() { return emojiPadding; } + public int getKeyboardHeight() { + return (AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y ? keyboardHeightLand : keyboardHeight) + (includeNavigationBar ? AndroidUtilities.navigationBarHeight : 0); + } + @Override public void onSizeChanged(int height, boolean isWidthGreater) { - if (height > AndroidUtilities.dp(50) && keyboardVisible && !AndroidUtilities.isInMultiwindow && !AndroidUtilities.isTablet()) { + if (height > AndroidUtilities.dp(50) && (keyboardVisible || currentStyle == STYLE_STORY) && !AndroidUtilities.isInMultiwindow && !AndroidUtilities.isTablet()) { if (isWidthGreater) { keyboardHeightLand = height; MessagesController.getGlobalEmojiSettings().edit().putInt("kbd_height_land3", keyboardHeightLand).apply(); @@ -639,7 +733,7 @@ public void onSizeChanged(int height, boolean isWidthGreater) { } if (isPopupShowing()) { - int newHeight = isWidthGreater ? keyboardHeightLand : keyboardHeight; + int newHeight = (isWidthGreater ? keyboardHeightLand : keyboardHeight) + (includeNavigationBar ? AndroidUtilities.navigationBarHeight : 0);; FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) emojiView.getLayoutParams(); if (layoutParams.width != AndroidUtilities.displaySize.x || layoutParams.height != newHeight) { @@ -681,8 +775,11 @@ public EditTextCaption getEditText() { return editText; } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + public View getEmojiButton() { + return emojiButton; + } + + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/EffectsTextView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/EffectsTextView.java new file mode 100644 index 0000000000..f74aa0928e --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/EffectsTextView.java @@ -0,0 +1,161 @@ +package org.telegram.ui.Components; + +import android.content.Context; +import android.graphics.Canvas; +import android.text.Layout; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.style.ClickableSpan; +import android.view.MotionEvent; +import android.view.ViewConfiguration; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.Emoji; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.spoilers.SpoilersTextView; + +// TextView with both Spoilers, Links and Emojis (not animated though) +public class EffectsTextView extends SpoilersTextView { + public interface OnLinkPress { + public void run(ClickableSpan span); + } + + private boolean isCustomLinkCollector; + private LinkSpanDrawable.LinkCollector links; + private Theme.ResourcesProvider resourcesProvider; + + private LinkSpanDrawable pressedLink; + + private LinkSpanDrawable.LinksTextView.OnLinkPress onPressListener; + private LinkSpanDrawable.LinksTextView.OnLinkPress onLongPressListener; + + private boolean disablePaddingsOffset; + private boolean disablePaddingsOffsetX; + private boolean disablePaddingsOffsetY; + + public EffectsTextView(Context context) { + this(context, null); + } + + public EffectsTextView(Context context, Theme.ResourcesProvider resourcesProvider) { + super(context, true); + this.isCustomLinkCollector = false; + this.links = new LinkSpanDrawable.LinkCollector(this); + this.resourcesProvider = resourcesProvider; + } + + public EffectsTextView(Context context, LinkSpanDrawable.LinkCollector customLinkCollector, Theme.ResourcesProvider resourcesProvider) { + super(context, true); + this.isCustomLinkCollector = true; + this.links = customLinkCollector; + this.resourcesProvider = resourcesProvider; + } + + @Override + public void setText(CharSequence text, BufferType type) { + text = Emoji.replaceEmoji(text, getPaint().getFontMetricsInt(), AndroidUtilities.dp(14), false); + super.setText(text, type); + } + + public void setDisablePaddingsOffset(boolean disablePaddingsOffset) { + this.disablePaddingsOffset = disablePaddingsOffset; + } + + public void setDisablePaddingsOffsetX(boolean disablePaddingsOffsetX) { + this.disablePaddingsOffsetX = disablePaddingsOffsetX; + } + + public void setDisablePaddingsOffsetY(boolean disablePaddingsOffsetY) { + this.disablePaddingsOffsetY = disablePaddingsOffsetY; + } + + public void setOnLinkPressListener(LinkSpanDrawable.LinksTextView.OnLinkPress listener) { + onPressListener = listener; + } + + public void setOnLinkLongPressListener(LinkSpanDrawable.LinksTextView.OnLinkPress listener) { + onLongPressListener = listener; + } + + public ClickableSpan hit(int x, int y) { + Layout textLayout = getLayout(); + if (textLayout == null) { + return null; + } + x -= getPaddingLeft(); + y -= getPaddingTop(); + final int line = textLayout.getLineForVertical(y); + final int off = textLayout.getOffsetForHorizontal(line, x); + final float left = getLayout().getLineLeft(line); + if (left <= x && left + textLayout.getLineWidth(line) >= x && y >= 0 && y <= textLayout.getHeight()) { + Spannable buffer = new SpannableString(textLayout.getText()); + ClickableSpan[] spans = buffer.getSpans(off, off, ClickableSpan.class); + if (spans.length != 0 && !AndroidUtilities.isAccessibilityScreenReaderEnabled()) { + return spans[0]; + } + } + return null; + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (links != null) { + Layout textLayout = getLayout(); + ClickableSpan span; + if ((span = hit((int) event.getX(), (int) event.getY())) != null) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + final LinkSpanDrawable link = new LinkSpanDrawable(span, resourcesProvider, event.getX(), event.getY()); + pressedLink = link; + links.addLink(pressedLink); + Spannable buffer = new SpannableString(textLayout.getText()); + int start = buffer.getSpanStart(pressedLink.getSpan()); + int end = buffer.getSpanEnd(pressedLink.getSpan()); + LinkPath path = pressedLink.obtainNewPath(); + path.setCurrentLayout(textLayout, start, getPaddingTop()); + textLayout.getSelectionPath(start, end, path); + AndroidUtilities.runOnUIThread(() -> { + if (onLongPressListener != null && pressedLink == link) { + onLongPressListener.run(span); + pressedLink = null; + links.clear(); + } + }, ViewConfiguration.getLongPressTimeout()); + return true; + } + } + if (event.getAction() == MotionEvent.ACTION_UP) { + links.clear(); + if (pressedLink != null && pressedLink.getSpan() == span) { + if (onPressListener != null) { + onPressListener.run(pressedLink.getSpan()); + } else if (pressedLink.getSpan() != null) { + pressedLink.getSpan().onClick(this); + } + pressedLink = null; + return true; + } + pressedLink = null; + } + if (event.getAction() == MotionEvent.ACTION_CANCEL) { + links.clear(); + pressedLink = null; + } + } + return pressedLink != null || super.onTouchEvent(event); + } + + @Override + protected void onDraw(Canvas canvas) { + if (!isCustomLinkCollector) { + canvas.save(); + if (!disablePaddingsOffset) { + canvas.translate(disablePaddingsOffsetX ? 0 : getPaddingLeft(), disablePaddingsOffsetY ? 0 : getPaddingTop()); + } + if (links.draw(canvas)) { + invalidate(); + } + canvas.restore(); + } + super.onDraw(canvas); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/EllipsizeSpanAnimator.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/EllipsizeSpanAnimator.java index 8aff031076..d7c7daf625 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/EllipsizeSpanAnimator.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/EllipsizeSpanAnimator.java @@ -9,6 +9,8 @@ import android.text.style.CharacterStyle; import android.view.View; +import org.telegram.ui.Components.Reactions.HwEmojis; + import java.util.ArrayList; public class EllipsizeSpanAnimator { @@ -81,7 +83,9 @@ private Animator createEllipsizeAnimator(TextAlphaSpan target, int startVal, int a.addUpdateListener(valueAnimator -> { target.setAlpha((int) valueAnimator.getAnimatedValue()); for (int i = 0; i < ellipsizedViews.size(); i++) { - ellipsizedViews.get(i).invalidate(); + if (!HwEmojis.isHwEnabled()) { + ellipsizedViews.get(i).invalidate(); + } } }); a.setDuration(duration); @@ -119,7 +123,7 @@ public void setAlpha(int alpha) { @Override public void updateDrawState(TextPaint tp) { - tp.setAlpha(alpha); + tp.setAlpha((int) (tp.getAlpha() * (alpha / 255f))); } } } \ No newline at end of file diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiColorPickerWindow.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiColorPickerWindow.java new file mode 100644 index 0000000000..2a66609fed --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiColorPickerWindow.java @@ -0,0 +1,461 @@ +package org.telegram.ui.Components; + +import static org.telegram.messenger.AndroidUtilities.dp; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; +import android.util.Pair; +import android.view.HapticFeedbackConstants; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewTreeObserver; +import android.view.WindowManager; +import android.widget.PopupWindow; + +import com.google.zxing.common.detector.MathUtils; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.CompoundEmoji; +import org.telegram.messenger.Emoji; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.R; +import org.telegram.messenger.Utilities; +import org.telegram.ui.ActionBar.Theme; + +import java.lang.reflect.Field; + +public class EmojiColorPickerWindow extends PopupWindow { + + private ViewTreeObserver.OnScrollChangedListener mSuperScrollListener; + private ViewTreeObserver mViewTreeObserver; + + private static Field superListenerField; + private static final ViewTreeObserver.OnScrollChangedListener NOP = () -> {}; + + public EmojiColorPickerView pickerView; + private boolean isCompound; + + public static EmojiColorPickerWindow create(Context context, Theme.ResourcesProvider resourcesProvider) { + EmojiColorPickerView view = new EmojiColorPickerView(context, resourcesProvider); + EmojiColorPickerWindow window = new EmojiColorPickerWindow(view); + window.init(); + return window; + } + + private EmojiColorPickerWindow(EmojiColorPickerView view) { + super(view); + this.pickerView = view; + + setOutsideTouchable(true); + setClippingEnabled(true); + setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); + setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED); + pickerView.setFocusableInTouchMode(true); + pickerView.setOnKeyListener((v, keyCode, event) -> { + if (keyCode == KeyEvent.KEYCODE_MENU && event.getRepeatCount() == 0 && event.getAction() == KeyEvent.ACTION_UP && isShowing()) { + dismiss(); + return true; + } + return false; + }); + } + + private final int emojiSize = AndroidUtilities.dp(AndroidUtilities.isTablet() ? 40 : 32); + + public int getPopupWidth() { + return emojiSize * 6 + AndroidUtilities.dp( 10 + 4 * 5 + (isCompound ? 3 : 0)); + } + + public int getPopupHeight() { + return AndroidUtilities.dp(isCompound ? 11.66f : 15) + (isCompound ? 2 : 1) * emojiSize; + } + + public int getSelection() { + return pickerView.getSelection(0); + } + + public String getSkinTone(int side) { + int value = pickerView.getSelection(side); + if (value < 1 || value > 5) { + return null; + } + return CompoundEmoji.skinTones.get(value - 1); + } + + public void setSelection(int value) { + pickerView.setSelection(0, value); + } + + public void onTouchMove(int x) { + if (isCompound) { + return; + } + int newSelection = Math.max(0, Math.min(5, x / (emojiSize + dp(4)))); + if (getSelection() != newSelection) { + try { + pickerView.performHapticFeedback(HapticFeedbackConstants.TEXT_HANDLE_MOVE, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); + } catch (Exception ignore) {} + setSelection(newSelection); + } + } + + public boolean isCompound() { + return isCompound; + } + + public void setupArrow(int arrowX) { + pickerView.setArrowX(arrowX); + } + + public void setEmoji(String emoji) { + isCompound = CompoundEmoji.getCompoundEmojiDrawable(emoji) != null; + pickerView.setEmoji(isCompound, emoji); + setWidth(getPopupWidth()); + setHeight(getPopupHeight()); + } + + public void updateColors() { + pickerView.updateColors(); + } + + public void setOnSelectionUpdateListener(Utilities.Callback2 onSelectionUpdateListener) { + pickerView.setOnSelectionUpdateListener(onSelectionUpdateListener); + } + + public static class EmojiColorPickerView extends View { + + private final int emojiSize = AndroidUtilities.dp(AndroidUtilities.isTablet() ? 40 : 32); + private Drawable[] drawables = new Drawable[11]; + private Drawable backgroundDrawable; + private Drawable arrowDrawable; + private String currentEmoji; + private boolean isCompound; + private int arrowX; + private int[] selection = new int[] { 0, 0 }; + private int[] lastSelection = new int[] { 0, 0 }; + private Paint rectPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private RectF rect = new RectF(); + private AnimatedFloat selection1Animated = new AnimatedFloat(this, 125, CubicBezierInterpolator.EASE_OUT_QUINT); + private AnimatedFloat selection2Animated = new AnimatedFloat(this, 125, CubicBezierInterpolator.EASE_OUT_QUINT); + + private Theme.ResourcesProvider resourcesProvider; + + private Utilities.Callback2 onSelectionUpdate; + + public void setOnSelectionUpdateListener(Utilities.Callback2 onSelectionUpdateListener) { + this.onSelectionUpdate = onSelectionUpdateListener; + } + + public EmojiColorPickerView(Context context, Theme.ResourcesProvider resourcesProvider) { + super(context); + + this.resourcesProvider = resourcesProvider; + + backgroundDrawable = getResources().getDrawable(R.drawable.stickers_back_all); + arrowDrawable = getResources().getDrawable(R.drawable.stickers_back_arrow); + updateColors(); + } + + public void updateColors() { + Theme.setDrawableColor(backgroundDrawable, Theme.getColor(Theme.key_dialogBackground, resourcesProvider)); + Theme.setDrawableColor(arrowDrawable, Theme.getColor(Theme.key_dialogBackground, resourcesProvider)); + CompoundEmoji.setPlaceholderColor(Theme.getColor(Theme.key_chat_emojiPanelIcon, resourcesProvider)); + } + + public void setArrowX(int arrowX) { + this.arrowX = arrowX; + invalidate(); + } + + public void setEmoji(boolean compound, String emoji) { + isCompound = compound; + currentEmoji = emoji; + if (compound) { + drawables[0] = CompoundEmoji.getCompoundEmojiDrawable(currentEmoji, -1, -1); + + drawables[1] = CompoundEmoji.getCompoundEmojiDrawable(currentEmoji, 0, -2); + drawables[2] = CompoundEmoji.getCompoundEmojiDrawable(currentEmoji, 1, -2); + drawables[3] = CompoundEmoji.getCompoundEmojiDrawable(currentEmoji, 2, -2); + drawables[4] = CompoundEmoji.getCompoundEmojiDrawable(currentEmoji, 3, -2); + drawables[5] = CompoundEmoji.getCompoundEmojiDrawable(currentEmoji, 4, -2); + + drawables[6] = CompoundEmoji.getCompoundEmojiDrawable(currentEmoji, -2, 0); + drawables[7] = CompoundEmoji.getCompoundEmojiDrawable(currentEmoji, -2, 1); + drawables[8] = CompoundEmoji.getCompoundEmojiDrawable(currentEmoji, -2, 2); + drawables[9] = CompoundEmoji.getCompoundEmojiDrawable(currentEmoji, -2, 3); + drawables[10] = CompoundEmoji.getCompoundEmojiDrawable(currentEmoji, -2, 4); + + Pair pair = CompoundEmoji.isHandshake(emoji); + if (pair != null) { + setSelection(0, pair.first); + setSelection(1, pair.second); + both = selection[0] == selection[1]; + } + + ignore = true; + } else { + for (int i = 0; i < 6; ++i) { + String coloredCode = emoji; + if (i != 0) { + String color = CompoundEmoji.skinTones.get(i - 1); + coloredCode = EmojiView.addColorToCode(emoji, color); + } + drawables[i] = Emoji.getEmojiBigDrawable(coloredCode); + } + } + invalidate(); + } + + public String getEmoji() { + return currentEmoji; + } + + public void setSelection(int side, int position) { + if (selection[side] == position) { + return; + } + selection[side] = position; + invalidate(); + } + + public int getSelection(int side) { + return selection[side]; + } + + private int touchY = -1; + private boolean both = true; + private long downStart; + private boolean ignore; + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (ignore) { + ignore = false; + return false; + } + if (!isCompound) { + return super.onTouchEvent(event); + } + + int index = -1; + for (int i = 0; i < drawables.length; ++i) { + if ( + drawables[i].getBounds().contains((int) event.getX(), (int) event.getY()) || + touchY != -1 && (i == 0 || ( + touchY == 0 && i >= 1 && i <= 5 || + touchY == 1 && i >= 6 && i <= 10 + )) && ( + (int) event.getX() >= drawables[i].getBounds().left && + (int) event.getX() <= drawables[i].getBounds().right + ) + ) { + index = i; + break; + } + } + + if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE || event.getAction() == MotionEvent.ACTION_UP) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + touchY = -1; + downStart = System.currentTimeMillis(); + both = selection[0] == selection[1]; + } + lastSelection[0] = selection[0]; + lastSelection[1] = selection[1]; + + boolean dragging = System.currentTimeMillis() - downStart > 300 && event.getAction() == MotionEvent.ACTION_MOVE; + if (index == 0) { + selection[0] = -1; + selection[1] = -1; + } else if (index >= 1 && index <= 5 && (touchY == -1 || touchY == 0)) { + touchY = 0; + selection[0] = index - 1; + if (selection[1] == -1 || both && dragging) { + selection[1] = selection[0]; + } + } else if (index >= 6 && index <= 10 && (touchY == -1 || touchY == 1)) { + touchY = 1; + selection[1] = index - 6; + if (selection[0] == -1 || both && dragging) { + selection[0] = selection[1]; + } + } + if (lastSelection[0] != selection[0] || lastSelection[1] != selection[1]) { + try { + try { + performHapticFeedback(HapticFeedbackConstants.TEXT_HANDLE_MOVE, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); + } catch (Exception ignore) {} + } catch (Exception ignore) {} + if (onSelectionUpdate != null) { + onSelectionUpdate.run(selection[0], selection[1]); + } + } + invalidate(); + if (event.getAction() == MotionEvent.ACTION_UP) { + touchY = -1; + } + return true; + } + + return false; + } + + @Override + protected void onDraw(Canvas canvas) { + backgroundDrawable.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight() - AndroidUtilities.dp(2)); + backgroundDrawable.draw(canvas); + + arrowDrawable.setBounds(arrowX - AndroidUtilities.dp(9), getMeasuredHeight() - AndroidUtilities.dp(6.34f), arrowX + AndroidUtilities.dp(9), getMeasuredHeight()); + arrowDrawable.draw(canvas); + + int x, y; + + if (currentEmoji != null) { + if (isCompound) { + for (int iy = 0; iy < 2; ++iy) { + float select = (iy == 0 ? selection1Animated : selection2Animated).set(selection[iy]); + x = (int) (emojiSize * (1 + select) + AndroidUtilities.dp(5 + 3 * Math.max(0, Math.min(1, 1 + select)) + 4 * (1 + select))); + float mi = Math.max(0, Math.min(1, -select)); + y = AndroidUtilities.lerp( + AndroidUtilities.dp(3) + iy * (emojiSize + AndroidUtilities.dp(1)), + (getMeasuredHeight() - emojiSize) / 2, + mi + ); + rect.set(x, y, x + emojiSize, y + emojiSize); + rect.inset(AndroidUtilities.dp(-2), AndroidUtilities.dp(-2 * mi)); + rectPaint.setColor(Theme.multAlpha(Theme.getColor(Theme.key_listSelector, resourcesProvider), AndroidUtilities.lerp(1, 0.5f, mi))); + canvas.drawRoundRect(rect, AndroidUtilities.dp(4), AndroidUtilities.dp(4), rectPaint); + + for (int ix = 0; ix < 5; ++ix) { + int i = 1 + ix + iy * 5; + x = emojiSize * (1 + ix) + AndroidUtilities.dp(8 + 4 * (1 + ix)); + y = AndroidUtilities.dp(3) + (emojiSize + AndroidUtilities.dp(1)) * iy; + drawables[i].setBounds(x, y, x + emojiSize, y + emojiSize); + drawables[i].draw(canvas); + } + } + drawables[0].setBounds( + AndroidUtilities.dp(5), + (getMeasuredHeight() - emojiSize) / 2, + AndroidUtilities.dp(5) + emojiSize, + (getMeasuredHeight() + emojiSize) / 2 + ); + drawables[0].draw(canvas); + canvas.drawRect( + AndroidUtilities.dp(8.45f) + emojiSize, + AndroidUtilities.dp(2), + AndroidUtilities.dp(8.45f) + emojiSize + 1, + getMeasuredHeight() - AndroidUtilities.dp(6), + Theme.dividerPaint + ); + } else { + float select = selection1Animated.set(selection[0]); + x = (int) (emojiSize * select + AndroidUtilities.dp(5 + 4 * select)); + y = AndroidUtilities.dp(5); + rect.set(x, y, x + emojiSize, y + emojiSize); + rect.inset(AndroidUtilities.dp(-2), AndroidUtilities.dp(-2)); + rectPaint.setColor(Theme.getColor(Theme.key_listSelector, resourcesProvider)); + canvas.drawRoundRect(rect, AndroidUtilities.dp(4), AndroidUtilities.dp(4), rectPaint); + + for (int a = 0; a < 6; a++) { + Drawable drawable = drawables[a]; + if (drawable != null) { + x = emojiSize * a + AndroidUtilities.dp(5 + 4 * a); + float scale = .9f + .1f * (1f - Math.min(.5f, Math.abs(a - select)) * 2f); + canvas.save(); + canvas.scale(scale, scale, x + emojiSize / 2f, y + emojiSize / 2f); + drawable.setBounds((int) x, (int) y, (int) x + emojiSize, (int) y + emojiSize); + drawable.draw(canvas); + canvas.restore(); + } + } + } + } + } + } + + @SuppressLint("SoonBlockedPrivateApi") + private void init() { + if (superListenerField == null) { + Field f = null; + try { + f = PopupWindow.class.getDeclaredField("mOnScrollChangedListener"); + f.setAccessible(true); + } catch (Exception ignore) {} + superListenerField = f; + } + if (superListenerField != null) { + try { + mSuperScrollListener = (ViewTreeObserver.OnScrollChangedListener) superListenerField.get(this); + superListenerField.set(this, NOP); + } catch (Exception e) { + mSuperScrollListener = null; + } + } + } + + private void unregisterListener() { + if (mSuperScrollListener != null && mViewTreeObserver != null) { + if (mViewTreeObserver.isAlive()) { + mViewTreeObserver.removeOnScrollChangedListener(mSuperScrollListener); + } + mViewTreeObserver = null; + } + } + + private void registerListener(View anchor) { + if (mSuperScrollListener != null) { + ViewTreeObserver vto = (anchor.getWindowToken() != null) ? anchor.getViewTreeObserver() : null; + if (vto != mViewTreeObserver) { + if (mViewTreeObserver != null && mViewTreeObserver.isAlive()) { + mViewTreeObserver.removeOnScrollChangedListener(mSuperScrollListener); + } + if ((mViewTreeObserver = vto) != null) { + vto.addOnScrollChangedListener(mSuperScrollListener); + } + } + } + } + + @Override + public void showAsDropDown(View anchor, int xoff, int yoff) { + try { + super.showAsDropDown(anchor, xoff, yoff); + registerListener(anchor); + } catch (Exception e) { + FileLog.e(e); + } + } + + @Override + public void update(View anchor, int xoff, int yoff, int width, int height) { + super.update(anchor, xoff, yoff, width, height); + registerListener(anchor); + } + + @Override + public void update(View anchor, int width, int height) { + super.update(anchor, width, height); + registerListener(anchor); + } + + @Override + public void showAtLocation(View parent, int gravity, int x, int y) { + super.showAtLocation(parent, gravity, x, y); + unregisterListener(); + } + + @Override + public void dismiss() { + setFocusable(false); + try { + super.dismiss(); + } catch (Exception ignore) {} + unregisterListener(); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiPacksAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiPacksAlert.java index accf16b093..62f99e02e4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiPacksAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiPacksAlert.java @@ -103,6 +103,8 @@ public class EmojiPacksAlert extends BottomSheet implements NotificationCenter.N private CircularProgressDrawable progressDrawable; private ActionBarPopupWindow popupWindow; + private boolean limitCount; + private boolean hasDescription; private float loadT; private float lastY; @@ -132,8 +134,8 @@ public void sendEmoji(TLRPC.Document emoji) { } @Override - public boolean needCopy() { - return UserConfig.getInstance(UserConfig.selectedAccount).isPremium(); + public boolean needCopy(TLRPC.Document document) { + return UserConfig.getInstance(UserConfig.selectedAccount).isPremium() && MessageObject.isAnimatedEmoji(document); } @Override @@ -147,7 +149,7 @@ public void copyEmoji(TLRPC.Document document) { @Override public Boolean canSetAsStatus(TLRPC.Document document) { - if (!UserConfig.getInstance(UserConfig.selectedAccount).isPremium()) { + if (!UserConfig.getInstance(UserConfig.selectedAccount).isPremium() || !MessageObject.isAnimatedEmoji(document)) { return null; } TLRPC.User user = UserConfig.getInstance(UserConfig.selectedAccount).getCurrentUser(); @@ -233,6 +235,10 @@ private EmojiPacksAlert(BaseFragment fragment, Context context, Theme.ResourcesP this.fragment = fragment; fixNavigationBar(); + if (stickerSets != null) { + limitCount = stickerSets.size() > 1; + } + customEmojiPacks = new EmojiPacksLoader(currentAccount, stickerSets, parentObject) { @Override protected void onUpdate() { @@ -255,7 +261,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec((int) (AndroidUtilities.displaySize.y * (isPortrait ? .56f : .3f)), MeasureSpec.EXACTLY)); } }; - listView = new RecyclerListView(context) { + listView = new RecyclerListView(context, resourcesProvider) { @Override public boolean onInterceptTouchEvent(MotionEvent event) { @@ -266,7 +272,7 @@ public boolean onInterceptTouchEvent(MotionEvent event) { @Override protected void onMeasure(int widthSpec, int heightSpec) { int width = MeasureSpec.getSize(widthSpec); - gridLayoutManager.setSpanCount(Math.max(1, width / AndroidUtilities.dp(AndroidUtilities.isTablet() ? 60 : 45))); + gridLayoutManager.setSpanCount(40); super.onMeasure(widthSpec, heightSpec); } @@ -333,7 +339,7 @@ protected void dispatchDraw(Canvas canvas) { listView.setWillNotDraw(false); listView.setSelectorRadius(AndroidUtilities.dp(6)); listView.setSelectorDrawableColor(Theme.getColor(Theme.key_listSelector, resourceProvider)); - listView.setPadding(AndroidUtilities.dp(8), 0, AndroidUtilities.dp(8), AndroidUtilities.dp(68)); + listView.setPadding(AndroidUtilities.dp(8), 0, AndroidUtilities.dp(8), AndroidUtilities.dp(limitCount ? 8 : 68)); listView.setLayoutManager(gridLayoutManager = new GridLayoutManager(context, 8)); listView.addItemDecoration(new RecyclerView.ItemDecoration() { @Override @@ -463,7 +469,27 @@ protected void onCloseByLink() { gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { - return listView.getAdapter() != null && listView.getAdapter().getItemViewType(position) != 1 ? gridLayoutManager.getSpanCount() : 1; + if (listView.getAdapter() == null || listView.getAdapter().getItemViewType(position) != 1) { + return gridLayoutManager.getSpanCount(); + } else { + int i = 0; + int sz; + for (int j = 0; i < customEmojiPacks.data.length; ++i) { + sz = customEmojiPacks.data[i].size(); + if (customEmojiPacks.data.length > 1) { + sz = Math.min(gridLayoutManager.getSpanCount() * 2, sz); + } + j += 1 + sz + 1; + if (position < j) { + break; + } + } + TLRPC.TL_messages_stickerSet stickerSet = customEmojiPacks.stickerSets == null || i >= customEmojiPacks.stickerSets.size() ? null : customEmojiPacks.stickerSets.get(i); + if (stickerSet == null || stickerSet.set == null || stickerSet.set.emojis) { + return 5; + } + return 8; + } } }); scrollHelper = new RecyclerAnimationScrollHelper(listView, gridLayoutManager); @@ -489,8 +515,8 @@ public int getSpanSize(int position) { removeButtonView = new TextView(context); removeButtonView.setVisibility(View.GONE); - removeButtonView.setBackground(Theme.createRadSelectorDrawable(0x0fffffff & getThemedColor(Theme.key_dialogTextRed), 0, 0)); - removeButtonView.setTextColor(getThemedColor(Theme.key_dialogTextRed)); + removeButtonView.setBackground(Theme.createRadSelectorDrawable(0x0fffffff & getThemedColor(Theme.key_text_RedBold), 0, 0)); + removeButtonView.setTextColor(getThemedColor(Theme.key_text_RedBold)); removeButtonView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); removeButtonView.setGravity(Gravity.CENTER); removeButtonView.setClickable(true); @@ -530,13 +556,13 @@ private void updateShowButton(boolean show) { if (animated) { buttonsView.animate().translationY(show ? removeOffset : AndroidUtilities.dp(16)).alpha(show ? 1 : 0).setDuration(250).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).start(); shadowView.animate().translationY(show ? -(AndroidUtilities.dp(68) - removeOffset) : 0).alpha(show ? 1 : 0).setDuration(250).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).start(); - listView.animate().translationY(!show ? (AndroidUtilities.dp(68) - removeOffset) : 0).setDuration(250).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).start(); + listView.animate().translationY(limitCount ? 0 : !show ? (AndroidUtilities.dp(68) - removeOffset) : 0).setDuration(250).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).start(); } else { buttonsView.setAlpha(show ? 1f : 0); buttonsView.setTranslationY(show ? removeOffset : AndroidUtilities.dp(16)); shadowView.setAlpha(show ? 1f : 0); shadowView.setTranslationY(show ? -(AndroidUtilities.dp(68) - removeOffset) : 0); - listView.setTranslationY(!show ? (AndroidUtilities.dp(68) - removeOffset) : 0); + listView.setTranslationY(limitCount ? 0 : !show ? (AndroidUtilities.dp(68) - removeOffset) : 0); } shown = show; } @@ -556,6 +582,8 @@ public ContentView(Context context) { ArrayList> unusedArrays = new ArrayList<>(); ArrayList unusedLineDrawables = new ArrayList<>(); + private final AnimatedFloat statusBarT = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + @Override protected void dispatchDraw(Canvas canvas) { if (!attached) { @@ -566,19 +594,19 @@ protected void dispatchDraw(Canvas canvas) { path.reset(); float y = lastY = getListTop(); float pad = 0; - if (fromY != null) { - float wasY = y; - y = AndroidUtilities.lerp(fromY, y + containerView.getY(), loadT) - containerView.getY(); - pad = y - wasY; - } - float stickToTop = (1f - MathUtils.clamp((y - containerView.getPaddingTop()) / AndroidUtilities.dp(32), 0, 1)); - y -= stickToTop * containerView.getPaddingTop(); - float r = dp((1f - stickToTop) * 14); +// if (fromY != null) { +// float wasY = y; +// y = AndroidUtilities.lerp(fromY, y + containerView.getY(), loadT) - containerView.getY(); +// pad = y - wasY; +// } + final float statusBarT = this.statusBarT.set(y <= containerView.getPaddingTop()); + y = AndroidUtilities.lerp(y, 0, statusBarT); + float r = dp((1f - statusBarT) * 14); AndroidUtilities.rectTmp.set(getPaddingLeft(), y, getWidth() - getPaddingRight(), getBottom() + r); path.addRoundRect(AndroidUtilities.rectTmp, r, r, Path.Direction.CW); canvas.drawPath(path, paint); - boolean open = stickToTop > .75f; + boolean open = statusBarT > .5f; if (lastOpen == null || open != lastOpen) { updateLightStatusBar(lastOpen = open); } @@ -593,7 +621,7 @@ protected void dispatchDraw(Canvas canvas) { shadowView.setVisibility(listView.canScrollVertically(1) || removeButtonView.getVisibility() == View.VISIBLE ? View.VISIBLE : View.INVISIBLE); if (listView != null) { canvas.save(); - canvas.translate(listView.getLeft(), listView.getTop() + pad); + canvas.translate(listView.getLeft(), listView.getY() + pad); canvas.clipRect(0, 0, listView.getWidth(), listView.getHeight()); canvas.saveLayerAlpha(0, 0, listView.getWidth(), listView.getHeight(), (int) (255 * listView.getAlpha()), Canvas.ALL_SAVE_FLAG); @@ -619,7 +647,7 @@ protected void dispatchDraw(Canvas canvas) { if (drawable == null) { continue; } - drawable.setColorFilter(Theme.chat_animatedEmojiTextColorFilter); + drawable.setColorFilter(Theme.getAnimatedEmojiColorFilter(resourcesProvider)); // drawable.addView(this); ArrayList arrayList = viewsGroupedByLines.get(child.getTop()); if (arrayList == null) { @@ -753,7 +781,7 @@ public void prepareDraw(long time) { drawable.setAlpha(255); AndroidUtilities.rectTmp2.set(imageView.getLeft() + imageView.getPaddingLeft(), imageView.getPaddingTop(), imageView.getRight() - imageView.getPaddingRight(), imageView.getMeasuredHeight() - imageView.getPaddingBottom()); imageView.backgroundThreadDrawHolder[threadIndex].setBounds(AndroidUtilities.rectTmp2); - drawable.setColorFilter(Theme.chat_animatedEmojiTextColorFilter); + drawable.setColorFilter(Theme.getAnimatedEmojiColorFilter(resourcesProvider)); imageView.imageReceiver = drawable.getImageReceiver();; drawInBackgroundViews.add(imageView); } @@ -1064,7 +1092,7 @@ private void updateButton() { highlightIndex = -1; } - if (!loaded) { + if (!loaded || limitCount) { premiumButtonView.setVisibility(View.GONE); addButtonView.setVisibility(View.GONE); removeButtonView.setVisibility(View.GONE); @@ -1149,7 +1177,7 @@ private int getListTop() { if (view != paddingView) { return containerView.getPaddingTop(); } - return paddingView.getBottom() + containerView.getPaddingTop(); + return paddingView.getBottom() + (int) listView.getY(); } @Override @@ -1730,7 +1758,11 @@ public void onClick(View widget) { } if (subtitleView != null) { - subtitleView.setText(LocaleController.formatPluralString("EmojiCount", size)); + if (set == null || set.set == null || set.set.emojis) { + subtitleView.setText(LocaleController.formatPluralString("EmojiCount", size)); + } else { + subtitleView.setText(LocaleController.formatPluralString("Stickers", size)); + } } if (premium && unlockButtonView != null && !UserConfig.getInstance(currentAccount).isPremium()) { @@ -1931,6 +1963,12 @@ private void putStickerSet(int index, TLRPC.TL_messages_stickerSet stickerSet) { customEmoji.stickerSet = stickerSet; customEmoji.documentId = document.id; data[index].add(customEmoji); + if (limitCount) { + final int maxCount = stickerSet == null || stickerSet.set == null || stickerSet.set.emojis ? 16 : 10; + if (data[index].size() >= maxCount) { + break; + } + } } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiTabsStrip.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiTabsStrip.java index 1f8c612ab2..1dece4458c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiTabsStrip.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiTabsStrip.java @@ -37,6 +37,7 @@ import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Components.Premium.PremiumLockIconView; +import org.telegram.ui.Components.Reactions.HwEmojis; import org.telegram.ui.SelectAnimatedEmojiDialog; import java.util.ArrayList; @@ -100,7 +101,7 @@ public class EmojiTabsStrip extends ScrollableHorizontalScrollView { private int currentType; public boolean updateButtonDrawables = true; - public EmojiTabsStrip(Context context, Theme.ResourcesProvider resourcesProvider, boolean includeStandard, boolean includeAnimated, int type, Runnable onSettingsOpen) { + public EmojiTabsStrip(Context context, Theme.ResourcesProvider resourcesProvider, boolean includeRecent, boolean includeStandard, boolean includeAnimated, int type, Runnable onSettingsOpen) { super(context); this.includeAnimated = includeAnimated; this.resourcesProvider = resourcesProvider; @@ -129,7 +130,7 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) { child.setScaleX(0); child.setScaleY(0); child.setAlpha(0); - child.animate().scaleX(1f).scaleY(1f).alpha(1f).setDuration(200).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).start(); + child.animate().scaleX(1f).scaleY(1f).alpha(1f).setDuration(HwEmojis.isHwEnabledOrPreparing() ? 0 : 200).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).start(); } if (id != null) { if (lastX.get(id) != null && lastX.get(id) != x) { @@ -227,6 +228,8 @@ protected void dispatchDraw(Canvas canvas) { paint.setColor(selectorColor()); if (forceTabsShow) { paint.setAlpha((int) (paint.getAlpha() * alpha * (1f - isEmojiTabs * .5f))); + } else { + paint.setAlpha((int) (paint.getAlpha() * alpha)); } path.rewind(); @@ -264,6 +267,9 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { private void getChildBounds(int i, RectF out) { View child = getChildAt(MathUtils.clamp(i, 0, getChildCount() - 1)); + if (child == null) { + return; + } out.set(child.getLeft(), child.getTop(), child.getRight(), child.getBottom()); out.set( out.centerX() - out.width() / 2f * child.getScaleX(), @@ -286,8 +292,10 @@ private void getChildBounds(int i, RectF out) { if (type == SelectAnimatedEmojiDialog.TYPE_TOPIC_ICON) { recentDrawableId = R.drawable.msg_emoji_smiles; } - contentView.addView(recentTab = new EmojiTabButton(context, recentDrawableId, false, false)); - recentTab.id = "recent".hashCode(); + if (includeRecent) { + contentView.addView(recentTab = new EmojiTabButton(context, recentDrawableId, false, false)); + recentTab.id = "recent".hashCode(); + } if (!includeAnimated) { for (int i = 0; i < emojiTabsDrawableIds.length; ++i) { contentView.addView(new EmojiTabButton(context, emojiTabsDrawableIds[i], false, i == 0)); @@ -308,6 +316,17 @@ private void getChildBounds(int i, RectF out) { } } + public void showRecentTabStub(boolean show) { + if (recentTab == null) { + return; + } + if (show) { + recentTab.setBackground(new StabDrawable(selectorColor())); + } else { + recentTab.setBackground(null); + } + } + public void showSelected(boolean show) { this.showSelected = show; this.contentView.invalidate(); @@ -367,6 +386,37 @@ private TLRPC.Document getThumbDocument(TLRPC.StickerSet set, ArrayList emojiPacks) { if (currentPackButton != null) { contentView.removeView(currentPackButton); } + } else if (newPack.resId != 0) { + if (currentPackButton == null) { + currentPackButton = new EmojiTabButton(getContext(), newPack.resId, false, false); + onTabCreate(currentPackButton); + contentView.addView(currentPackButton, packsIndexStart + i); + } else { + currentPackButton.setDrawable(getResources().getDrawable(newPack.resId).mutate()); + currentPackButton.updateColor(); + currentPackButton.setLock(null); + } } else { - final boolean free = isFreeEmojiPack(newPack.set, newPack.documents); + final boolean free = newPack.free; // isFreeEmojiPack(newPack.set, newPack.documents); DelayedAnimatedEmojiDrawable drawable = currentPackButton == null ? null : (DelayedAnimatedEmojiDrawable) currentPackButton.getDrawable(); TLRPC.Document thumbDocument = getThumbDocument(newPack.set, newPack.documents); if (thumbDocument != null && (drawable == null || !drawable.equals(thumbDocument.id))) { @@ -666,7 +726,7 @@ public void updateEmojiPacks(ArrayList emojiPacks) { if (settingsTab != null) { settingsTab.bringToFront(); if (settingsTab.getAlpha() < 1) { - settingsTab.animate().alpha(1f).setDuration(200).setInterpolator(CubicBezierInterpolator.DEFAULT).start(); + settingsTab.animate().alpha(1f).setDuration(HwEmojis.isHwEnabledOrPreparing() ? 0 : 200).setInterpolator(CubicBezierInterpolator.DEFAULT).start(); } } // if (doAppearAnimation) { @@ -842,11 +902,11 @@ public void select(int index, boolean animated) { } protected ColorFilter getEmojiColorFilter() { - return Theme.chat_animatedEmojiTextColorFilter; + return Theme.getAnimatedEmojiColorFilter(resourcesProvider); } private int selectorColor() { - return 0x2effffff & Theme.getColor(Theme.key_chat_emojiPanelIcon, resourcesProvider); + return Theme.multAlpha(Theme.getColor(Theme.key_chat_emojiPanelIcon, resourcesProvider), .18f); } public void setAnimatedEmojiCacheType(int cacheType) { @@ -923,10 +983,21 @@ public EmojiTabButton(Context context, Drawable drawable, boolean free, boolean imageView = new ImageView(context) { @Override public void invalidate() { + if (HwEmojis.grab(this)) { + return; + } super.invalidate(); updateLockImageReceiver(); } + @Override + public void invalidate(int l, int t, int r, int b) { + if (HwEmojis.grab(this)) { + return; + } + super.invalidate(l, t, r, b); + } + @Override protected void onDraw(Canvas canvas) { @@ -948,13 +1019,29 @@ public void setImageDrawable(@Nullable Drawable drawable) { } }; imageView.setImageDrawable(drawable); - if (drawable instanceof AnimatedEmojiDrawable) { + if (drawable instanceof AnimatedEmojiDrawable || drawable instanceof DelayedAnimatedEmojiDrawable) { isAnimatedEmoji = true; imageView.setColorFilter(getEmojiColorFilter()); } addView(imageView); - lockView = new PremiumLockIconView(context, PremiumLockIconView.TYPE_STICKERS_PREMIUM_LOCKED, resourcesProvider); + lockView = new PremiumLockIconView(context, PremiumLockIconView.TYPE_STICKERS_PREMIUM_LOCKED, resourcesProvider) { + @Override + public void invalidate() { + if (HwEmojis.grab(this)) { + return; + } + super.invalidate(); + } + + @Override + public void invalidate(int l, int t, int r, int b) { + if (HwEmojis.grab(this)) { + return; + } + super.invalidate(l, t, r, b); + } + }; lockView.setAlpha(0f); lockView.setScaleX(0); lockView.setScaleY(0); @@ -964,6 +1051,22 @@ public void setImageDrawable(@Nullable Drawable drawable) { setColor(Theme.getColor(Theme.key_chat_emojiPanelIcon, resourcesProvider)); } + @Override + public void invalidate() { + if (HwEmojis.grab(this)) { + return; + } + super.invalidate(); + } + + @Override + public void invalidate(int l, int t, int r, int b) { + if (HwEmojis.grab(this)) { + return; + } + super.invalidate(l, t, r, b); + } + @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); @@ -1104,7 +1207,7 @@ public void onAnimationEnd(Animator animation) { } }); lockAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); - lockAnimator.setDuration(200); + lockAnimator.setDuration(HwEmojis.isHwEnabledOrPreparing() ? 0 : 200); lockAnimator.start(); } @@ -1163,7 +1266,7 @@ public void setDrawable(Drawable drawable) { if (animatedEmoji != null && attached && wasVisible) { animatedEmoji.updateView(imageView); } - if (wasVisible) { + if (wasVisible && animatedEmoji != null) { animatedEmoji.load(); } initLock(); @@ -1238,7 +1341,7 @@ public void onAnimationEnd(Animator animation) { } } }); - selectAnimator.setDuration(350); + selectAnimator.setDuration(HwEmojis.isHwEnabledOrPreparing() ? 0 : 350); selectAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); selectAnimator.start(); } else { @@ -1494,4 +1597,20 @@ void updateButtonsVisibility() { } } } + + private boolean touch; + + @Override + public boolean onTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + touch = true; + } else if (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL) { + touch = false; + } + return super.onTouchEvent(ev); + } + + public boolean isTouch() { + return touch; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiView.java index 116a3dcd03..cb3ae3e05a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiView.java @@ -25,13 +25,16 @@ import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.DashPathEffect; +import android.graphics.LinearGradient; import android.graphics.Outline; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; +import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; +import android.graphics.Shader; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.graphics.drawable.RippleDrawable; @@ -91,6 +94,7 @@ import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.BuildVars; import org.telegram.messenger.ChatObject; +import org.telegram.messenger.CompoundEmoji; import org.telegram.messenger.DocumentObject; import org.telegram.messenger.Emoji; import org.telegram.messenger.EmojiData; @@ -155,6 +159,8 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific private final static int TAB_GIFS = 1; private final static int TAB_STICKERS = 2; + public int emojiCacheType = AnimatedEmojiDrawable.CACHE_TYPE_KEYBOARD; + private ArrayList allTabs = new ArrayList<>(); private ArrayList currentTabs = new ArrayList<>(); private boolean ignorePagerScroll; @@ -205,6 +211,8 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific private int gifRecentTabNum = -2; private int gifTrendingTabNum = -2; private int gifFirstEmojiTabNum = -2; + private boolean shouldDrawBackground = true; + public boolean shouldLightenBackground = true; private FrameLayout stickersContainer; private StickersGridAdapter stickersGridAdapter; @@ -261,6 +269,13 @@ public void allowEmojisForNonPremium(boolean allow) { allowEmojisForNonPremium = allow; } + public void setShouldDrawBackground(boolean shouldDrawBackground) { + if (this.shouldDrawBackground != shouldDrawBackground) { + this.shouldDrawBackground = shouldDrawBackground; + updateColors(); + } + } + @IntDef({Type.STICKERS, Type.EMOJIS, Type.GIFS}) @Retention(RetentionPolicy.SOURCE) @@ -313,10 +328,7 @@ public void allowEmojisForNonPremium(boolean allow) { private int currentPage; - private EmojiColorPickerView pickerView; - private EmojiPopupWindow pickerViewPopup; - private int popupWidth; - private int popupHeight; + private EmojiColorPickerWindow colorPickerView; private int emojiSize; private int location[] = new int[2]; private int stickersTabOffset; @@ -486,7 +498,7 @@ public interface DragListener { private ContentPreviewViewer.ContentPreviewViewerDelegate contentPreviewViewerDelegate = new ContentPreviewViewer.ContentPreviewViewerDelegate() { @Override public boolean can() { - return fragment != null; + return fragment != null || !shouldDrawBackground; } @Override @@ -558,7 +570,7 @@ public void copyEmoji(TLRPC.Document document) { } @Override - public boolean needCopy() { + public boolean needCopy(TLRPC.Document document) { return true; } @@ -644,23 +656,6 @@ public String getQuery(boolean isGif) { } }; - private static final Field superListenerField; - - static { - Field f = null; - try { - f = PopupWindow.class.getDeclaredField("mOnScrollChangedListener"); - f.setAccessible(true); - } catch (NoSuchFieldException e) { - /* ignored */ - } - superListenerField = f; - } - - private static final ViewTreeObserver.OnScrollChangedListener NOP = () -> { - /* do nothing */ - }; - @Override public void setEnabled(boolean enabled) { super.setEnabled(enabled); @@ -689,6 +684,7 @@ private class SearchField extends FrameLayout { private StickerCategoriesListView categoriesListView; private FrameLayout inputBox; private View inputBoxGradient; + private float inputBoxGradientAlpha; private StickerCategoriesListView.EmojiCategory recent; private StickerCategoriesListView.EmojiCategory trending; @@ -725,7 +721,28 @@ public void getOutline(View view, Outline outline) { addView(box, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 36, Gravity.FILL, 10, 6, 10, 8)); } - inputBox = new FrameLayout(context); + inputBox = new FrameLayout(context) { + + Paint fadePaint; + + @Override + protected void dispatchDraw(Canvas canvas) { + if (!shouldDrawBackground && inputBoxGradientAlpha > 0) { + if (fadePaint == null) { + fadePaint = new Paint(); + fadePaint.setShader(new LinearGradient(0, 0, AndroidUtilities.dp(18), 0, new int[]{0xffffffff, 0}, new float[]{0f, 1f}, Shader.TileMode.CLAMP)); + fadePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); + } + canvas.saveLayerAlpha(0, 0, getMeasuredWidth(), getMeasuredHeight(), 255, Canvas.ALL_SAVE_FLAG); + super.dispatchDraw(canvas); + fadePaint.setAlpha((int) (inputBoxGradientAlpha * 255)); + canvas.drawRect(0, 0, AndroidUtilities.dp(18), getMeasuredHeight(), fadePaint); + canvas.restore(); + } else { + super.dispatchDraw(canvas); + } + } + }; box.addView(inputBox, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 40, Gravity.LEFT | Gravity.TOP, 38, 0, 0, 0)); searchImageView = new ImageView(context); @@ -812,13 +829,14 @@ public void afterTextChanged(Editable s) { } }); - inputBoxGradient = new View(context); - Drawable gradientDrawable = context.getResources().getDrawable(R.drawable.gradient_right).mutate(); - gradientDrawable.setColorFilter(new PorterDuffColorFilter(Theme.blendOver(getThemedColor(Theme.key_chat_emojiPanelBackground), getThemedColor(Theme.key_chat_emojiSearchBackground)), PorterDuff.Mode.MULTIPLY)); - inputBoxGradient.setBackground(gradientDrawable); - inputBoxGradient.setAlpha(0f); - inputBox.addView(inputBoxGradient, LayoutHelper.createFrame(18, LayoutHelper.MATCH_PARENT, Gravity.LEFT)); - + if (shouldDrawBackground) { + inputBoxGradient = new View(context); + Drawable gradientDrawable = context.getResources().getDrawable(R.drawable.gradient_right).mutate(); + gradientDrawable.setColorFilter(new PorterDuffColorFilter(Theme.blendOver(getThemedColor(Theme.key_chat_emojiPanelBackground), getThemedColor(Theme.key_chat_emojiSearchBackground)), PorterDuff.Mode.MULTIPLY)); + inputBoxGradient.setBackground(gradientDrawable); + inputBoxGradient.setAlpha(0f); + inputBox.addView(inputBoxGradient, LayoutHelper.createFrame(18, LayoutHelper.MATCH_PARENT, Gravity.LEFT)); + } clear = new ImageView(context); clear.setScaleType(ImageView.ScaleType.CENTER); clear.setImageDrawable(new CloseProgressDrawable2(1.25f) { @@ -867,7 +885,9 @@ protected boolean isTabIconsAnimationEnabled(boolean loaded) { } }; categoriesListView.setDontOccupyWidth((int) (searchEditText.getPaint().measureText(searchEditText.getHint() + "")) + dp(16)); - categoriesListView.setBackgroundColor(Theme.blendOver(getThemedColor(Theme.key_chat_emojiPanelBackground), getThemedColor(Theme.key_chat_emojiSearchBackground))); + if (shouldDrawBackground) { + categoriesListView.setBackgroundColor(Theme.blendOver(getThemedColor(Theme.key_chat_emojiPanelBackground), getThemedColor(Theme.key_chat_emojiSearchBackground))); + } categoriesListView.setOnScrollIntoOccupiedWidth(scrolled -> { searchEditText.setTranslationX(-Math.max(0, scrolled)); showInputBoxGradient(scrolled > 0); @@ -929,14 +949,29 @@ public void search(String text, boolean delay) { } private boolean inputBoxShown = false; + ValueAnimator inputBoxGradientAnimator; private void showInputBoxGradient(boolean show) { - if (show == inputBoxShown || inputBoxGradient == null) { + if (show == inputBoxShown) { return; } inputBoxShown = show; - inputBoxGradient.clearAnimation(); - inputBoxGradient.animate().alpha(show ? 1 : 0).setDuration(120).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).start(); + if (inputBoxGradientAnimator != null) { + inputBoxGradientAnimator.cancel(); + } + inputBoxGradientAnimator = ValueAnimator.ofFloat(inputBoxGradientAlpha, show ? 1f : 0); + inputBoxGradientAnimator.addUpdateListener(animation -> { + inputBoxGradientAlpha = (float) animation.getAnimatedValue(); + if (inputBoxGradient != null) { + inputBoxGradient.setAlpha(inputBoxGradientAlpha); + } else if (inputBox != null) { + inputBox.invalidate(); + } + }); + inputBoxGradientAnimator.setDuration(120); + inputBoxGradientAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + inputBoxGradientAnimator.start(); + } public boolean isInProgress() { @@ -1422,219 +1457,9 @@ protected void onDraw(Canvas canvas) { } } - private class EmojiPopupWindow extends PopupWindow { - - private ViewTreeObserver.OnScrollChangedListener mSuperScrollListener; - private ViewTreeObserver mViewTreeObserver; - - public EmojiPopupWindow() { - super(); - init(); - } - - public EmojiPopupWindow(Context context) { - super(context); - init(); - } - - public EmojiPopupWindow(int width, int height) { - super(width, height); - init(); - } - - public EmojiPopupWindow(View contentView) { - super(contentView); - init(); - } - - public EmojiPopupWindow(View contentView, int width, int height, boolean focusable) { - super(contentView, width, height, focusable); - init(); - } - - public EmojiPopupWindow(View contentView, int width, int height) { - super(contentView, width, height); - init(); - } - - private void init() { - if (superListenerField != null) { - try { - mSuperScrollListener = (ViewTreeObserver.OnScrollChangedListener) superListenerField.get(this); - superListenerField.set(this, NOP); - } catch (Exception e) { - mSuperScrollListener = null; - } - } - } - - private void unregisterListener() { - if (mSuperScrollListener != null && mViewTreeObserver != null) { - if (mViewTreeObserver.isAlive()) { - mViewTreeObserver.removeOnScrollChangedListener(mSuperScrollListener); - } - mViewTreeObserver = null; - } - } - - private void registerListener(View anchor) { - if (mSuperScrollListener != null) { - ViewTreeObserver vto = (anchor.getWindowToken() != null) ? anchor.getViewTreeObserver() : null; - if (vto != mViewTreeObserver) { - if (mViewTreeObserver != null && mViewTreeObserver.isAlive()) { - mViewTreeObserver.removeOnScrollChangedListener(mSuperScrollListener); - } - if ((mViewTreeObserver = vto) != null) { - vto.addOnScrollChangedListener(mSuperScrollListener); - } - } - } - } - - @Override - public void showAsDropDown(View anchor, int xoff, int yoff) { - try { - super.showAsDropDown(anchor, xoff, yoff); - registerListener(anchor); - } catch (Exception e) { - FileLog.e(e); - } - } - - @Override - public void update(View anchor, int xoff, int yoff, int width, int height) { - super.update(anchor, xoff, yoff, width, height); - registerListener(anchor); - } - - @Override - public void update(View anchor, int width, int height) { - super.update(anchor, width, height); - registerListener(anchor); - } - - @Override - public void showAtLocation(View parent, int gravity, int x, int y) { - super.showAtLocation(parent, gravity, x, y); - unregisterListener(); - } - - @Override - public void dismiss() { - setFocusable(false); - try { - super.dismiss(); - } catch (Exception ignore) { - - } - unregisterListener(); - } - } - - private class EmojiColorPickerView extends View { - - private Drawable[] drawables = new Drawable[6]; - private Drawable backgroundDrawable; - private Drawable arrowDrawable; - private String currentEmoji; - private int arrowX; - private int selection; - private Paint rectPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - private RectF rect = new RectF(); - private AnimatedFloat selectionAnimated = new AnimatedFloat(this, 125, CubicBezierInterpolator.EASE_OUT_QUINT); - - public void setEmoji(String emoji, int arrowPosition) { - currentEmoji = emoji; - arrowX = arrowPosition; - for (int i = 0; i < drawables.length; ++i) { - String coloredCode = emoji; - if (i != 0) { - String color; - switch (i) { - case 1: - color = "\uD83C\uDFFB"; - break; - case 2: - color = "\uD83C\uDFFC"; - break; - case 3: - color = "\uD83C\uDFFD"; - break; - case 4: - color = "\uD83C\uDFFE"; - break; - case 5: - color = "\uD83C\uDFFF"; - break; - default: - color = ""; - } - coloredCode = addColorToCode(emoji, color); - } - drawables[i] = Emoji.getEmojiBigDrawable(coloredCode); - } - invalidate(); - } - - public String getEmoji() { - return currentEmoji; - } - - public void setSelection(int position) { - if (selection == position) { - return; - } - selection = position; - invalidate(); - } - - public int getSelection() { - return selection; - } - - public EmojiColorPickerView(Context context) { - super(context); - - backgroundDrawable = getResources().getDrawable(R.drawable.stickers_back_all); - arrowDrawable = getResources().getDrawable(R.drawable.stickers_back_arrow); - Theme.setDrawableColor(backgroundDrawable, getThemedColor(Theme.key_dialogBackground)); - Theme.setDrawableColor(arrowDrawable, getThemedColor(Theme.key_dialogBackground)); - } - - @Override - protected void onDraw(Canvas canvas) { - backgroundDrawable.setBounds(0, 0, getMeasuredWidth(), AndroidUtilities.dp(AndroidUtilities.isTablet() ? 60 : 52)); - backgroundDrawable.draw(canvas); - - arrowDrawable.setBounds(arrowX - AndroidUtilities.dp(9), AndroidUtilities.dp(AndroidUtilities.isTablet() ? 55.5f : 47.5f), arrowX + AndroidUtilities.dp(9), AndroidUtilities.dp((AndroidUtilities.isTablet() ? 55.5f : 47.5f) + 8)); - arrowDrawable.draw(canvas); - - float select = selectionAnimated.set(selection); - float x = emojiSize * select + AndroidUtilities.dp(5 + 4 * select); - float y = AndroidUtilities.dp(9); - rect.set(x, y - (int) AndroidUtilities.dpf2(3.5f), x + emojiSize, y + emojiSize + AndroidUtilities.dp(3)); - rectPaint.setColor(getThemedColor(Theme.key_listSelector)); - canvas.drawRoundRect(rect, AndroidUtilities.dp(4), AndroidUtilities.dp(4), rectPaint); - - if (currentEmoji != null) { - for (int a = 0; a < 6; a++) { - Drawable drawable = drawables[a]; - if (drawable != null) { - x = emojiSize * a + AndroidUtilities.dp(5 + 4 * a); - float scale = .9f + .1f * (1f - Math.min(.5f, Math.abs(a - select)) * 2f); - canvas.save(); - canvas.scale(scale, scale, x + emojiSize / 2f, y + emojiSize / 2f); - drawable.setBounds((int) x, (int) y, (int) x + emojiSize, (int) y + emojiSize); - drawable.draw(canvas); - canvas.restore(); - } - } - } - } - } - - public EmojiView(BaseFragment fragment, boolean needAnimatedEmoji, boolean needStickers, boolean needGif, final Context context, boolean needSearch, final TLRPC.ChatFull chatFull, ViewGroup parentView, Theme.ResourcesProvider resourcesProvider) { + public EmojiView(BaseFragment fragment, boolean needAnimatedEmoji, boolean needStickers, boolean needGif, final Context context, boolean needSearch, final TLRPC.ChatFull chatFull, ViewGroup parentView, boolean shouldDrawBackground, Theme.ResourcesProvider resourcesProvider) { super(context); + this.shouldDrawBackground = shouldDrawBackground; this.fragment = fragment; this.allowAnimatedEmoji = needAnimatedEmoji; this.resourcesProvider = resourcesProvider; @@ -1693,7 +1518,23 @@ public void getOutline(View view, Outline outline) { }; } - emojiContainer = new FrameLayout(context); + emojiContainer = new FrameLayout(context) { + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + if (child == emojiGridView || child == emojiSearchField) { + canvas.save(); + float top = emojiTabs.getY() + emojiTabs.getMeasuredHeight() + 1; + if (child == emojiGridView && emojiSearchField != null) { + top = Math.max(top, emojiSearchField.getY() + emojiSearchField.getMeasuredHeight() + 1); + } + canvas.clipRect(0, top, getMeasuredWidth(), getMeasuredHeight()); + boolean rez = super.drawChild(canvas, child, drawingTime); + canvas.restore(); + return rez; + } + return super.drawChild(canvas, child, drawingTime); + } + }; Tab emojiTab = new Tab(); emojiTab.type = TAB_EMOJI; emojiTab.view = emojiContainer; @@ -1731,69 +1572,34 @@ public boolean onItemClick(View view, int position) { if (code == null) { return false; } + code = code.replace("\uD83C\uDFFB", "").replace("\uD83C\uDFFC", "").replace("\uD83C\uDFFD", "").replace("\uD83C\uDFFE", "").replace("\uD83C\uDFFF", ""); String color = null; - - String toCheck = code.replace("\uD83C\uDFFB", ""); - if (toCheck != code) { - color = "\uD83C\uDFFB"; - } - if (color == null) { - toCheck = code.replace("\uD83C\uDFFC", ""); - if (toCheck != code) { - color = "\uD83C\uDFFC"; - } - } - if (color == null) { - toCheck = code.replace("\uD83C\uDFFD", ""); - if (toCheck != code) { - color = "\uD83C\uDFFD"; - } + if (!viewEmoji.isRecent) { + color = Emoji.emojiColor.get(code); } - if (color == null) { - toCheck = code.replace("\uD83C\uDFFE", ""); - if (toCheck != code) { - color = "\uD83C\uDFFE"; - } - } - if (color == null) { - toCheck = code.replace("\uD83C\uDFFF", ""); - if (toCheck != code) { - color = "\uD83C\uDFFF"; - } - } - if (EmojiData.emojiColoredMap.contains(toCheck)) { + String toCheck = code; + boolean isCompound = false; + if (CompoundEmoji.isCompound(toCheck) && (isCompound = true) || EmojiData.emojiColoredMap.contains(code)) { emojiTouchedView = viewEmoji; emojiTouchedX = emojiLastX; emojiTouchedY = emojiLastY; - if (color == null && !viewEmoji.isRecent) { - color = Emoji.emojiColor.get(toCheck); - } - - if (color != null) { - switch (color) { - case "\uD83C\uDFFB": - pickerView.setSelection(1); - break; - case "\uD83C\uDFFC": - pickerView.setSelection(2); - break; - case "\uD83C\uDFFD": - pickerView.setSelection(3); - break; - case "\uD83C\uDFFE": - pickerView.setSelection(4); - break; - case "\uD83C\uDFFF": - pickerView.setSelection(5); - break; - } + if (isCompound) { + toCheck = addColorToCode(code, color); } else { - pickerView.setSelection(0); + colorPickerView.setSelection(1 + CompoundEmoji.skinTones.indexOf(color)); } + colorPickerView.setEmoji(toCheck); + + int popupWidth = colorPickerView.getPopupWidth(); + int popupHeight = colorPickerView.getPopupHeight(); + viewEmoji.getLocationOnScreen(location); - int x = emojiSize * pickerView.getSelection() + AndroidUtilities.dp(4 * pickerView.getSelection() - (AndroidUtilities.isTablet() ? 5 : 1)); + int x = 0; + if (!colorPickerView.isCompound()) { + x = emojiSize * colorPickerView.getSelection() + AndroidUtilities.dp(4 * colorPickerView.getSelection() - (AndroidUtilities.isTablet() ? 5 : 1)); + } if (location[0] - x < AndroidUtilities.dp(5)) { x += (location[0] - x) - AndroidUtilities.dp(5); } else if (location[0] - x + popupWidth > AndroidUtilities.displaySize.x - AndroidUtilities.dp(5)) { @@ -1802,10 +1608,9 @@ public boolean onItemClick(View view, int position) { int xOffset = -x; int yOffset = viewEmoji.getTop() < 0 ? viewEmoji.getTop() : 0; - pickerView.setEmoji(toCheck, AndroidUtilities.dp(AndroidUtilities.isTablet() ? 30 : 22) - xOffset + (int) AndroidUtilities.dpf2(0.5f)); - - pickerViewPopup.setFocusable(true); - pickerViewPopup.showAsDropDown(view, xOffset, -view.getMeasuredHeight() - popupHeight + (view.getMeasuredHeight() - emojiSize) / 2 - yOffset); + colorPickerView.setupArrow(AndroidUtilities.dp(AndroidUtilities.isTablet() ? 30 : 22) - xOffset + (int) AndroidUtilities.dpf2(0.5f)); + colorPickerView.setFocusable(true); + colorPickerView.showAsDropDown(view, xOffset, -view.getMeasuredHeight() - popupHeight + (view.getMeasuredHeight() - emojiSize) / 2 - yOffset); pager.requestDisallowInterceptTouchEvent(true); emojiGridView.hideSelector(true); emojiGridView.clearTouchesFor(view); @@ -1838,7 +1643,7 @@ public void onEnd() { emojiGridView.setBottomGlowOffset(AndroidUtilities.dp(36)); emojiGridView.setPadding(AndroidUtilities.dp(5), AndroidUtilities.dp(36), AndroidUtilities.dp(5), AndroidUtilities.dp(44)); emojiGridView.setGlowColor(getThemedColor(Theme.key_chat_emojiPanelBackground)); - emojiGridView.setSelectorDrawableColor(0); + emojiGridView.setItemSelectorColorProvider(p -> 0); emojiGridView.setClipToPadding(false); emojiLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override @@ -1912,7 +1717,7 @@ public void onScrollStateChanged(RecyclerView recyclerView, int newState) { } }); - emojiTabs = new EmojiTabsStrip(context, resourcesProvider, true, needAnimatedEmoji, 0, fragment != null ? () -> { + emojiTabs = new EmojiTabsStrip(context, resourcesProvider, true, true, needAnimatedEmoji, 0, fragment != null ? () -> { if (delegate != null) { delegate.onEmojiSettingsClick(emojiAdapter.frozenEmojiPacks); } @@ -1929,9 +1734,12 @@ protected boolean allowEmojisForNonPremium() { @Override public void setTranslationY(float translationY) { - super.setTranslationY(translationY); - if (emojiTabsShadow != null) { - emojiTabsShadow.setTranslationY(translationY); + if (getTranslationY() != translationY) { + super.setTranslationY(translationY); + if (emojiTabsShadow != null) { + emojiTabsShadow.setTranslationY(translationY); + } + emojiContainer.invalidate(); } } @@ -1997,7 +1805,15 @@ protected ColorFilter getEmojiColorFilter() { } }; if (needSearch) { - emojiSearchField = new SearchField(context, 1); + emojiSearchField = new SearchField(context, 1) { + @Override + public void setTranslationY(float translationY) { + if (translationY != getTranslationY()) { + super.setTranslationY(translationY); + emojiContainer.invalidate(); + } + } + }; emojiContainer.addView(emojiSearchField, new FrameLayout.LayoutParams(LayoutHelper.MATCH_PARENT, searchFieldHeight + AndroidUtilities.getShadowHeight())); emojiSearchField.searchEditText.setOnFocusChangeListener(new OnFocusChangeListener() { @Override @@ -2010,7 +1826,10 @@ public void onFocusChange(View v, boolean hasFocus) { }); } - emojiTabs.setBackgroundColor(getThemedColor(Theme.key_chat_emojiPanelBackground)); + final int emojiTabsColor = getThemedColor(Theme.key_chat_emojiPanelBackground); + if (Color.alpha(emojiTabsColor) >= 0xFF) { + emojiTabs.setBackgroundColor(emojiTabsColor); + } emojiAdapter.processEmoji(true); emojiTabs.updateEmojiPacks(getEmojipacks()); emojiContainer.addView(emojiTabs, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 36)); @@ -2025,7 +1844,19 @@ public void onFocusChange(View v, boolean hasFocus) { if (needStickers) { if (needGif) { - gifContainer = new FrameLayout(context); + gifContainer = new FrameLayout(context) { + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + if (child == gifGridView) { + canvas.save(); + canvas.clipRect(0, gifSearchField.getY() + gifSearchField.getMeasuredHeight(), getMeasuredWidth(), getMeasuredHeight()); + boolean rez = super.drawChild(canvas, child, drawingTime); + canvas.restore(); + return rez; + } + return super.drawChild(canvas, child, drawingTime); + } + }; Tab gifTab = new Tab(); gifTab.type = TAB_GIFS; gifTab.view = gifContainer; @@ -2137,7 +1968,15 @@ public void getItemOffsets(android.graphics.Rect outRect, View view, RecyclerVie gifGridView.setOnItemClickListener(gifOnItemClickListener); gifContainer.addView(gifGridView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); - gifSearchField = new SearchField(context, 2); + gifSearchField = new SearchField(context, 2) { + @Override + public void setTranslationY(float translationY) { + if (getTranslationY() != translationY) { + super.setTranslationY(translationY); + gifContainer.invalidate(); + } + } + }; // gifSearchField.setVisibility(INVISIBLE); gifContainer.addView(gifSearchField, new FrameLayout.LayoutParams(LayoutHelper.MATCH_PARENT, searchFieldHeight + AndroidUtilities.getShadowHeight())); @@ -2206,6 +2045,22 @@ protected void onDetachedFromWindow() { chooseStickerActionTracker.checkVisibility(); } } + + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + if (!shouldDrawBackground && (child == stickersGridView || child == stickersSearchField)) { + canvas.save(); + float top = stickersTab.getY() + stickersTab.getMeasuredHeight() + 1; + if (child == stickersGridView) { + top = Math.max(top, stickersSearchField.getY() + stickersSearchField.getMeasuredHeight() + 1); + } + canvas.clipRect(0, top, getMeasuredWidth(), getMeasuredHeight()); + boolean rez = super.drawChild(canvas, child, drawingTime); + canvas.restore(); + return rez; + } + return super.drawChild(canvas, child, drawingTime); + } }; MediaDataController.getInstance(currentAccount).checkStickers(MediaDataController.TYPE_IMAGE); @@ -2345,14 +2200,24 @@ public int getSpanSize(int position) { stickersContainer.addView(stickersGridView); stickersScrollHelper = new RecyclerAnimationScrollHelper(stickersGridView, stickersLayoutManager); - stickersSearchField = new SearchField(context, 0); + stickersSearchField = new SearchField(context, 0) { + @Override + public void setTranslationY(float translationY) { + if (translationY != getTranslationY()) { + super.setTranslationY(translationY); + stickersContainer.invalidate(); + } + } + }; stickersContainer.addView(stickersSearchField, new FrameLayout.LayoutParams(LayoutHelper.MATCH_PARENT, searchFieldHeight + AndroidUtilities.getShadowHeight())); stickersTab = new DraggableScrollSlidingTabStrip(context, resourcesProvider) { @Override protected void updatePosition() { updateStickerTabsPosition(); - stickersTabContainer.invalidate(); + if (stickersTabContainer != null) { + stickersTabContainer.invalidate(); + } invalidate(); if (delegate != null) { delegate.invalidateEnterView(); @@ -2423,7 +2288,19 @@ private void sendReorder() { @Override protected void invalidateOverlays() { - stickersTabContainer.invalidate(); + if (stickersTabContainer != null) { + stickersTabContainer.invalidate(); + } + } + + @Override + public void setTranslationY(float translationY) { + if (getTranslationY() != translationY) { + super.setTranslationY(translationY); + if (!shouldDrawBackground) { + stickersContainer.invalidate(); + } + } } }; stickersTab.setDragEnabled(true); @@ -2433,7 +2310,7 @@ protected void invalidateOverlays() { stickersTab.setIndicatorColor(getThemedColor(Theme.key_chat_emojiPanelStickerPackSelectorLine)); stickersTab.setUnderlineColor(getThemedColor(Theme.key_chat_emojiPanelShadowLine)); - if (parentView != null) { + if (parentView != null && shouldDrawBackground) { stickersTabContainer = new FrameLayout(context) { Paint paint = new Paint(); @@ -2613,7 +2490,15 @@ public void onClick(View v) { addView(bulletinContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 100, Gravity.BOTTOM | Gravity.FILL_HORIZONTAL, 0, 0, 0, 0)); } - bottomTabContainer = new FrameLayout(context); + bottomTabContainer = new FrameLayout(context) { + @Override + public void setTranslationY(float translationY) { + if (getTranslationY() != translationY) { + super.setTranslationY(translationY); + EmojiView.this.invalidate(); + } + } + }; bottomTabContainer.setClickable(true); shadowLine = new View(context); @@ -2653,7 +2538,7 @@ public void onClick(View v) { typeTabs.setViewPager(pager); typeTabs.setShouldExpand(false); typeTabs.setIndicatorHeight(AndroidUtilities.dp(3)); - typeTabs.setIndicatorColor(getThemedColor(Theme.key_chat_emojiPanelIconSelected)); + typeTabs.setIndicatorColor(ColorUtils.setAlphaComponent(getThemedColor(Theme.key_chat_emojiPanelIconSelected), 20)); typeTabs.setUnderlineHeight(0); typeTabs.setTabPaddingLeftRight(AndroidUtilities.dp(13)); bottomTabContainer.addView(typeTabs, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 40, Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM)); @@ -2696,7 +2581,7 @@ public void onPageScrolled(int position, float positionOffset, int positionOffse public void onPageSelected(int position) { saveNewPage(); showBackspaceButton(position == 0, true); - showStickerSettingsButton(position == 2, true); + showStickerSettingsButton(position == 2 && shouldDrawBackground, true); if (delegate.isSearchOpened()) { if (position == 0) { if (emojiSearchField != null) { @@ -2799,23 +2684,24 @@ public void getOutline(View view, Outline outline) { addView(mediaBanTooltip, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM, 5, 0, 5, 48 + 5)); emojiSize = AndroidUtilities.dp(AndroidUtilities.isTablet() ? 40 : 32); - pickerView = new EmojiColorPickerView(context); - pickerViewPopup = new EmojiPopupWindow(pickerView, popupWidth = AndroidUtilities.dp((AndroidUtilities.isTablet() ? 40 : 32) * 6 + 10 + 4 * 5), popupHeight = AndroidUtilities.dp(AndroidUtilities.isTablet() ? 64 : 56)); - pickerViewPopup.setOutsideTouchable(true); - pickerViewPopup.setClippingEnabled(true); - pickerViewPopup.setInputMethodMode(EmojiPopupWindow.INPUT_METHOD_NOT_NEEDED); - pickerViewPopup.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED); - pickerViewPopup.getContentView().setFocusableInTouchMode(true); - pickerViewPopup.getContentView().setOnKeyListener((v, keyCode, event) -> { - if (keyCode == KeyEvent.KEYCODE_MENU && event.getRepeatCount() == 0 && event.getAction() == KeyEvent.ACTION_UP && pickerViewPopup != null && pickerViewPopup.isShowing()) { - pickerViewPopup.dismiss(); - return true; + colorPickerView = EmojiColorPickerWindow.create(context, resourcesProvider); + colorPickerView.setOnSelectionUpdateListener((skinTone1, skinTone2) -> { + if (emojiTouchedView != null && emojiTouchedView.getDrawable() instanceof CompoundEmoji.CompoundEmojiDrawable) { + CompoundEmoji.CompoundEmojiDrawable drawable = (CompoundEmoji.CompoundEmojiDrawable) emojiTouchedView.getDrawable(); + drawable.update(skinTone1, skinTone2); + + String code = (String) emojiTouchedView.getTag(); + if (skinTone1 == -1 && skinTone2 == -1) { + Emoji.emojiColor.remove(code); + } else { + String colored = (skinTone1 >= 0 ? CompoundEmoji.skinTones.get(skinTone1) : "") + "\u200D" + (skinTone2 >= 0 ? CompoundEmoji.skinTones.get(skinTone2) : ""); + Emoji.emojiColor.put(code, colored); + } + Emoji.saveEmojiColors(); } - return false; }); currentPage = MessagesController.getGlobalEmojiSettings().getInt("selected_page", 0); - Emoji.loadRecentEmoji(); emojiAdapter.notifyDataSetChanged(); @@ -2891,29 +2777,12 @@ public void requestLayout() { @Override public boolean onTouchEvent(MotionEvent event) { - if (emojiTouchedView != null) { + if (emojiTouchedView != null && colorPickerView != null) { if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL) { - if (pickerViewPopup != null && pickerViewPopup.isShowing()) { - pickerViewPopup.dismiss(); + if (colorPickerView != null && colorPickerView.isShowing() && !colorPickerView.isCompound()) { + colorPickerView.dismiss(); - String color = null; - switch (pickerView.getSelection()) { - case 1: - color = "\uD83C\uDFFB"; - break; - case 2: - color = "\uD83C\uDFFC"; - break; - case 3: - color = "\uD83C\uDFFD"; - break; - case 4: - color = "\uD83C\uDFFE"; - break; - case 5: - color = "\uD83C\uDFFF"; - break; - } + String color = colorPickerView.getSkinTone(0); String code = (String) emojiTouchedView.getTag(); if (!emojiTouchedView.isRecent) { if (color != null) { @@ -2942,7 +2811,9 @@ public boolean onTouchEvent(MotionEvent event) { } } } - emojiTouchedView = null; + if (colorPickerView == null || !colorPickerView.isCompound()) { + emojiTouchedView = null; + } emojiTouchedX = -10000; emojiTouchedY = -10000; } else if (event.getAction() == MotionEvent.ACTION_MOVE) { @@ -2958,23 +2829,14 @@ public boolean onTouchEvent(MotionEvent event) { if (!ignore) { getLocationOnScreen(location); float x = location[0] + event.getX(); - pickerView.getLocationOnScreen(location); - x -= location[0] + AndroidUtilities.dp(3); - int position = (int) (x / (emojiSize + AndroidUtilities.dp(4))); - if (position < 0) { - position = 0; - } else if (position > 5) { - position = 5; - } - if (pickerView.getSelection() != position) { - try { - performHapticFeedback(HapticFeedbackConstants.TEXT_HANDLE_MOVE, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); - } catch (Exception ignoreException) {} - } - pickerView.setSelection(position); + colorPickerView.pickerView.getLocationOnScreen(location); + x -= location[0] + dp(3); + colorPickerView.onTouchMove((int) x); } } - return true; + if (colorPickerView == null || !colorPickerView.isCompound() || colorPickerView.isShowing()) { + return true; + } } emojiLastX = event.getX(); emojiLastY = event.getY(); @@ -2982,7 +2844,7 @@ public boolean onTouchEvent(MotionEvent event) { } public void updateEmojiDrawables() { - animatedEmojiDrawables = AnimatedEmojiSpan.update(AnimatedEmojiDrawable.CACHE_TYPE_KEYBOARD, this, getAnimatedEmojiSpans(), animatedEmojiDrawables); + animatedEmojiDrawables = AnimatedEmojiSpan.update(emojiCacheType, this, getAnimatedEmojiSpans(), animatedEmojiDrawables); } @Override @@ -3233,8 +3095,8 @@ public void prepareDraw(long time) { AndroidUtilities.rectTmp2.set(imageView.getLeft() + imageView.getPaddingLeft() - startOffset, topOffset, imageView.getRight() - imageView.getPaddingRight() - startOffset, topOffset + imageView.getMeasuredHeight() - imageView.getPaddingTop() - imageView.getPaddingBottom()); imageView.backgroundThreadDrawHolder[threadIndex].setBounds(AndroidUtilities.rectTmp2); imageView.drawable = drawable; - imageView.drawable.setColorFilter(animatedEmojiTextColorFilter); imageView.imageReceiver = drawable.getImageReceiver(); + imageView.backgroundThreadDrawHolder[threadIndex].colorFilter = drawable.canOverrideColor() ? animatedEmojiTextColorFilter : null; drawInBackgroundViews.add(imageView); } } @@ -3292,6 +3154,7 @@ protected void drawInUiThread(Canvas canvas, float alpha) { } drawable.setAlpha((int) (255 * alpha)); drawable.setBounds(AndroidUtilities.rectTmp2); + drawable.setColorFilter(animatedEmojiTextColorFilter); if (scale != 1) { canvas.save(); canvas.scale(scale, scale, AndroidUtilities.rectTmp2.centerX(), AndroidUtilities.rectTmp2.centerY()); @@ -3356,7 +3219,7 @@ public boolean dispatchTouchEvent(MotionEvent ev) { Math.sqrt(Math.pow(x - touch.x, 2) + Math.pow(y - touch.y, 2)) < AndroidUtilities.touchSlop * 3 && // (SystemClock.elapsedRealtime() - touch.time) <= ViewConfiguration.getTapTimeout() * 1.2f && !cancel && - (!pickerViewPopup.isShowing() || SystemClock.elapsedRealtime() - touch.time < ViewConfiguration.getLongPressTimeout()) + (!colorPickerView.isShowing() || SystemClock.elapsedRealtime() - touch.time < ViewConfiguration.getLongPressTimeout()) ) { View view = touch.view; int position = getChildAdapterPosition(touch.view); @@ -3584,19 +3447,25 @@ private void checkGridVisibility(int position, float positionOffset) { gifGridView.setVisibility(positionOffset == 0 ? View.GONE : View.VISIBLE); gifTabs.setVisibility(positionOffset == 0 ? View.GONE : View.VISIBLE); stickersGridView.setVisibility(View.GONE); - stickersTabContainer.setVisibility(View.GONE); + if (stickersTabContainer != null) { + stickersTabContainer.setVisibility(View.GONE); + } } else if (position == 1) { emojiGridView.setVisibility(View.GONE); gifGridView.setVisibility(View.VISIBLE); gifTabs.setVisibility(View.VISIBLE); stickersGridView.setVisibility(positionOffset == 0 ? View.GONE : View.VISIBLE); - stickersTabContainer.setVisibility(positionOffset == 0 ? View.GONE : View.VISIBLE); + if (stickersTabContainer != null) { + stickersTabContainer.setVisibility(positionOffset == 0 ? View.GONE : View.VISIBLE); + } } else if (position == 2) { emojiGridView.setVisibility(View.GONE); gifGridView.setVisibility(View.GONE); gifTabs.setVisibility(View.GONE); stickersGridView.setVisibility(View.VISIBLE); - stickersTabContainer.setVisibility(View.VISIBLE); + if (stickersTabContainer != null) { + stickersTabContainer.setVisibility(View.VISIBLE); + } } } @@ -4158,21 +4027,25 @@ public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull R } } - private static String addColorToCode(String code, String color) { - String end = null; - int length = code.length(); - if (length > 2 && code.charAt(code.length() - 2) == '\u200D') { - end = code.substring(code.length() - 2); - code = code.substring(0, code.length() - 2); - } else if (length > 3 && code.charAt(code.length() - 3) == '\u200D') { - end = code.substring(code.length() - 3); - code = code.substring(0, code.length() - 3); - } - code += color; - if (end != null) { - code += end; - } - return code; + public static String addColorToCode(String code, String color) { + if (CompoundEmoji.isHandshake(code) != null) { + return CompoundEmoji.applyColor(code, color); + } else { + String end = null; + int length = code.length(); + if (length > 2 && code.charAt(code.length() - 2) == '\u200D') { + end = code.substring(code.length() - 2); + code = code.substring(0, code.length() - 2); + } else if (length > 3 && code.charAt(code.length() - 3) == '\u200D') { + end = code.substring(code.length() - 3); + code = code.substring(0, code.length() - 3); + } + code += color; + if (end != null) { + code += end; + } + return code; + } } private void openTrendingStickers(TLRPC.StickerSetCovered set) { @@ -4239,16 +4112,23 @@ public void setTranslationY(float translationY) { updateStickerTabsPosition(); updateBottomTabContainerPosition(); } + + public boolean fixBottomTabContainerTranslation = true; private void updateBottomTabContainerPosition() { View parent = (View) getParent(); if (parent != null) { - float y = getY() - parent.getHeight(); + float y = getY() ; if (getLayoutParams().height > 0) { y += getLayoutParams().height; } else { y += getMeasuredHeight(); } - if (bottomTabContainer.getTop() - y < 0) { + if (!AndroidUtilities.isInMultiwindow && (fragment == null || !fragment.isInBubbleMode())) { + y -= parent.getHeight(); + } else { + y -= AndroidUtilities.dp(1); + } + if (bottomTabContainer.getTop() - y < 0 || !fixBottomTabContainerTranslation) { y = 0; } bottomTabMainTranslation = -y; @@ -4261,6 +4141,9 @@ private void updateBottomTabContainerPosition() { Rect rect = new Rect(); private void updateStickerTabsPosition() { + if (stickersTab != null && stickersTabContainer == null && delegate != null) { + stickersTab.setTranslationY(-AndroidUtilities.dp(50) * delegate.getProgressToSearchOpened()); + } if (stickersTabContainer == null) { return; } @@ -4298,6 +4181,23 @@ protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); } + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + if (child == pager && bottomTabContainer.getVisibility() != View.GONE) { + canvas.save(); + if (needEmojiSearch) { + canvas.clipRect(0, 0, getMeasuredWidth(), bottomTabContainer.getY() - 1); + } + if (!shouldDrawBackground && shouldLightenBackground) { + canvas.drawColor(ColorUtils.setAlphaComponent(Color.WHITE, 25)); + } + boolean res = super.drawChild(canvas, child, drawingTime); + canvas.restore(); + return res; + } + return super.drawChild(canvas, child, drawingTime); + } + private void startStopVisibleGifs(boolean start) { if (gifGridView == null) { return; @@ -5510,7 +5410,10 @@ public void requestLayout() { } public void updateColors() { - if (AndroidUtilities.isInMultiwindow || forseMultiwindowLayout) { + if (!shouldDrawBackground) { + setBackground(null); + bottomTabContainerBackground.setBackground(null); + } else if (AndroidUtilities.isInMultiwindow || forseMultiwindowLayout) { Drawable background = getBackground(); if (background != null) { background.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_chat_emojiPanelBackground), PorterDuff.Mode.SRC_IN)); @@ -5522,12 +5425,15 @@ public void updateColors() { } } if (emojiTabs != null) { - emojiTabs.setBackgroundColor(getThemedColor(Theme.key_chat_emojiPanelBackground)); - emojiTabsShadow.setBackgroundColor(getThemedColor(Theme.key_chat_emojiPanelShadowLine)); + if (shouldDrawBackground) { + emojiTabs.setBackgroundColor(getThemedColor(Theme.key_chat_emojiPanelBackground)); + emojiTabsShadow.setBackgroundColor(getThemedColor(Theme.key_chat_emojiPanelShadowLine)); + } else { + emojiTabs.setBackground(null); + } } - if (pickerView != null) { - Theme.setDrawableColor(pickerView.backgroundDrawable, getThemedColor(Theme.key_dialogBackground)); - Theme.setDrawableColor(pickerView.arrowDrawable, getThemedColor(Theme.key_dialogBackground)); + if (colorPickerView != null) { + colorPickerView.updateColors(); } for (int a = 0; a < 3; a++) { SearchField searchField; @@ -5541,10 +5447,14 @@ public void updateColors() { if (searchField == null) { continue; } - searchField.backgroundView.setBackgroundColor(getThemedColor(Theme.key_chat_emojiPanelBackground)); + if (shouldDrawBackground) { + searchField.backgroundView.setBackgroundColor(getThemedColor(Theme.key_chat_emojiPanelBackground)); + } else { + searchField.backgroundView.setBackground(null); + } searchField.shadowView.setBackgroundColor(getThemedColor(Theme.key_chat_emojiPanelShadowLine)); searchField.searchStateDrawable.setColor(getThemedColor(Theme.key_chat_emojiSearchIcon)); - Theme.setDrawableColorByKey(searchField.box.getBackground(), Theme.key_chat_emojiSearchBackground); + Theme.setDrawableColor(searchField.box.getBackground(), getThemedColor(Theme.key_chat_emojiSearchBackground)); searchField.box.invalidate(); searchField.searchEditText.setHintTextColor(getThemedColor(Theme.key_chat_emojiSearchIcon)); searchField.searchEditText.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteBlackText)); @@ -5561,12 +5471,20 @@ public void updateColors() { if (stickersTab != null) { stickersTab.setIndicatorColor(getThemedColor(Theme.key_chat_emojiPanelStickerPackSelectorLine)); stickersTab.setUnderlineColor(getThemedColor(Theme.key_chat_emojiPanelShadowLine)); - stickersTab.setBackgroundColor(getThemedColor(Theme.key_chat_emojiPanelBackground)); + if (shouldDrawBackground) { + stickersTab.setBackgroundColor(getThemedColor(Theme.key_chat_emojiPanelBackground)); + } else { + stickersTab.setBackground(null); + } } if (gifTabs != null) { gifTabs.setIndicatorColor(getThemedColor(Theme.key_chat_emojiPanelStickerPackSelectorLine)); gifTabs.setUnderlineColor(getThemedColor(Theme.key_chat_emojiPanelShadowLine)); - gifTabs.setBackgroundColor(getThemedColor(Theme.key_chat_emojiPanelBackground)); + if (shouldDrawBackground) { + gifTabs.setBackgroundColor(getThemedColor(Theme.key_chat_emojiPanelBackground)); + } else { + gifTabs.setBackground(null); + } } if (backspaceButton != null) { backspaceButton.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_chat_emojiPanelBackspace), PorterDuff.Mode.SRC_IN)); @@ -5638,8 +5556,8 @@ public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setElevation(AndroidUtilities.dp(2)); } setBackgroundResource(R.drawable.smiles_popup); - getBackground().setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_chat_emojiPanelBackground), PorterDuff.Mode.SRC_IN)); - if (needEmojiSearch) { + getBackground().setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_chat_emojiPanelBackground), PorterDuff.Mode.MULTIPLY)); + if (needEmojiSearch && shouldDrawBackground) { bottomTabContainerBackground.setBackgroundColor(getThemedColor(Theme.key_chat_emojiPanelBackground)); } currentBackgroundType = 1; @@ -5651,9 +5569,11 @@ public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setClipToOutline(false); setElevation(0); } - setBackgroundColor(getThemedColor(Theme.key_chat_emojiPanelBackground)); - if (needEmojiSearch) { - bottomTabContainerBackground.setBackgroundColor(getThemedColor(Theme.key_chat_emojiPanelBackground)); + if (shouldDrawBackground) { + setBackgroundColor(getThemedColor(Theme.key_chat_emojiPanelBackground)); + if (needEmojiSearch) { + bottomTabContainerBackground.setBackgroundColor(getThemedColor(Theme.key_chat_emojiPanelBackground)); + } } currentBackgroundType = 0; } @@ -5723,7 +5643,7 @@ public void onOpen(boolean forceEmoji) { } } else if (currentPage == 1) { showBackspaceButton(false, false); - showStickerSettingsButton(true, false); + showStickerSettingsButton(shouldDrawBackground, false); if (pager.getCurrentItem() != 2) { pager.setCurrentItem(2, false); } @@ -5821,9 +5741,10 @@ public void onDestroy() { @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - if (pickerViewPopup != null && pickerViewPopup.isShowing()) { - pickerViewPopup.dismiss(); + if (colorPickerView != null && colorPickerView.isShowing()) { + colorPickerView.dismiss(); } + ContentPreviewViewer.getInstance().clearDelegate(contentPreviewViewerDelegate); } private void checkDocuments(boolean isGif) { @@ -6107,8 +6028,8 @@ public void didReceivedNotification(int id, int account, Object... args) { } } } - if (pickerView != null) { - pickerView.invalidate(); + if (colorPickerView != null) { + colorPickerView.pickerView.invalidate(); } if (gifTabs != null) { gifTabs.invalidateTabs(); @@ -6126,9 +6047,11 @@ public void didReceivedNotification(int id, int account, Object... args) { } } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + if (resourcesProvider != null) { + return resourcesProvider.getColor(key); + } + return Theme.getColor(key); } private class TrendingAdapter extends RecyclerListView.SelectionAdapter { @@ -6159,10 +6082,10 @@ protected void onDraw(Canvas canvas) { } } }; - imageView.setSize(AndroidUtilities.dp(emoji ? 24 : 30), AndroidUtilities.dp(emoji ? 24 : 30)); + imageView.setSize(AndroidUtilities.dp(24), AndroidUtilities.dp(24)); imageView.setLayerNum(1); imageView.setAspectFit(true); - imageView.setLayoutParams(new RecyclerView.LayoutParams(AndroidUtilities.dp(emoji ? 34 : 42), AndroidUtilities.dp(emoji ? 34 : 42))); + imageView.setLayoutParams(new RecyclerView.LayoutParams(AndroidUtilities.dp(34), AndroidUtilities.dp(34))); return new RecyclerListView.Holder(imageView); } @@ -6207,7 +6130,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } if (this.emoji) { - imageView.setColorFilter(MessageObject.isTextColorEmoji(document) ? Theme.chat_animatedEmojiTextColorFilter : null); + imageView.setColorFilter(MessageObject.isTextColorEmoji(document) ? Theme.getAnimatedEmojiColorFilter(resourcesProvider) : null); } TLObject object = FileLoader.getClosestPhotoSizeWithSize(set.set.thumbs, 90); @@ -6398,7 +6321,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType View view = null; switch (viewType) { case 0: - view = new StickerEmojiCell(context, true) { + view = new StickerEmojiCell(context, true, resourcesProvider) { public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(82), MeasureSpec.EXACTLY)); } @@ -6440,7 +6363,7 @@ public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { alertDialog.show(); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } } @@ -6476,10 +6399,12 @@ public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { break; case 6: TrendingListView listView = new TrendingListView(context, trendingAdapter = new TrendingAdapter(false)); + listView.setPadding(AndroidUtilities.dp(8), AndroidUtilities.dp(4), AndroidUtilities.dp(8), 0); + listView.setClipToPadding(false); listView.addItemDecoration(new RecyclerView.ItemDecoration() { @Override public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { - outRect.right = AndroidUtilities.dp(8); + outRect.right = AndroidUtilities.dp(2); } }); listView.setOnItemClickListener((view1, position) -> { @@ -6738,6 +6663,8 @@ public static class EmojiPack { public boolean installed; public boolean featured; public boolean expanded; + + public int resId; } private class EmojiGridAdapter extends RecyclerListView.SelectionAdapter { @@ -6839,7 +6766,7 @@ public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull R } MediaDataController.getInstance(currentAccount).markFeaturedStickersAsRead(true, true); - EmojiPacksAlert alert = new EmojiPacksAlert(fragment, getContext(), fragment == null ? null : fragment.getResourceProvider(), inputStickerSets); + EmojiPacksAlert alert = new EmojiPacksAlert(fragment, getContext(), fragment == null ? resourcesProvider : fragment.getResourceProvider(), inputStickerSets); if (highlight >= 0) { alert.highlight(highlight); } @@ -7237,7 +7164,7 @@ public void updateRows() { itemCount++; rowHashCodes.add(-1); } - if (isPremium && allowAnimatedEmoji && featuredEmojiSets.size() > 0 && featuredEmojiSets.get(0).set != null && MessagesController.getEmojiSettings(currentAccount).getLong("emoji_featured_hidden", 0) != featuredEmojiSets.get(0).set.id) { + if (isPremium && allowAnimatedEmoji && featuredEmojiSets.size() > 0 && featuredEmojiSets.get(0).set != null && MessagesController.getEmojiSettings(currentAccount).getLong("emoji_featured_hidden", 0) != featuredEmojiSets.get(0).set.id && needEmojiSearch) { trendingHeaderRow = itemCount++; trendingRow = itemCount++; recentlyUsedHeaderRow = itemCount++; @@ -8712,7 +8639,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType View view = null; switch (viewType) { case 0: - view = new StickerEmojiCell(context, true) { + view = new StickerEmojiCell(context, true, resourcesProvider) { public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(82), MeasureSpec.EXACTLY)); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/EmptyTextProgressView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmptyTextProgressView.java index 96e20fee76..f27f4030c8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/EmptyTextProgressView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmptyTextProgressView.java @@ -193,8 +193,7 @@ public boolean hasOverlappingRendering() { return false; } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/FilterGLThread.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/FilterGLThread.java index 435fe03865..7054d692e9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/FilterGLThread.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/FilterGLThread.java @@ -6,11 +6,14 @@ import android.opengl.GLES20; import android.opengl.GLUtils; import android.os.Looper; +import android.util.Log; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.BuildVars; import org.telegram.messenger.DispatchQueue; import org.telegram.messenger.FileLog; +import org.telegram.messenger.SharedConfig; +import org.telegram.ui.Stories.recorder.StoryEntry; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -72,12 +75,17 @@ public interface FilterGLThreadVideoDelegate { private FilterGLThreadVideoDelegate videoDelegate; - public FilterGLThread(SurfaceTexture surface, Bitmap bitmap, int bitmapOrientation, boolean mirror) { + public FilterGLThread(SurfaceTexture surface, Bitmap bitmap, int bitmapOrientation, boolean mirror, StoryEntry.HDRInfo hdrInfo) { + this(surface, bitmap, bitmapOrientation, mirror, hdrInfo, true); + } + + public FilterGLThread(SurfaceTexture surface, Bitmap bitmap, int bitmapOrientation, boolean mirror, StoryEntry.HDRInfo hdrInfo, boolean allowBitmapScaling) { super("PhotoFilterGLThread", false); surfaceTexture = surface; currentBitmap = bitmap; orientation = bitmapOrientation; - filterShaders = new FilterShaders(false); + filterShaders = new FilterShaders(false, hdrInfo); + filterShaders.setScaleBitmap(allowBitmapScaling); float[] textureCoordinates = { 0.0f, 0.0f, @@ -104,14 +112,21 @@ public FilterGLThread(SurfaceTexture surface, Bitmap bitmap, int bitmapOrientati start(); } - public FilterGLThread(SurfaceTexture surface, FilterGLThreadVideoDelegate filterGLThreadVideoDelegate) { + public FilterGLThread(SurfaceTexture surface, FilterGLThreadVideoDelegate filterGLThreadVideoDelegate, StoryEntry.HDRInfo hdrInfo) { super("VideoFilterGLThread", false); surfaceTexture = surface; videoDelegate = filterGLThreadVideoDelegate; - filterShaders = new FilterShaders(true); + filterShaders = new FilterShaders(true, hdrInfo); start(); } + public void updateHDRInfo(StoryEntry.HDRInfo hdrInfo) { + postRunnable(() -> { + makeCurrentContext(); + filterShaders.updateHDRInfo(hdrInfo); + }); + } + public void setFilterGLThreadDelegate(FilterShaders.FilterShadersDelegate filterShadersDelegate) { postRunnable(() -> filterShaders.setDelegate(filterShadersDelegate)); } @@ -270,9 +285,27 @@ public void setVideoSize(int width, int height) { } videoWidth = width; videoHeight = height; - if (videoWidth > 1280 || videoHeight > 1280) { - videoWidth /= 2; - videoHeight /= 2; + int maxSide; + switch (SharedConfig.getDevicePerformanceClass()) { + case SharedConfig.PERFORMANCE_CLASS_HIGH: + maxSide = Math.min(1920, Math.max(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y)); + break; + case SharedConfig.PERFORMANCE_CLASS_AVERAGE: + maxSide = 1920; + break; + case SharedConfig.PERFORMANCE_CLASS_LOW: + default: + maxSide = 1280; + break; + } + if (videoWidth > maxSide || videoHeight > maxSide) { + if (videoWidth > videoHeight) { + videoHeight = (int) (videoHeight / ((float) maxSide / videoWidth)); + videoWidth = maxSide; + } else { + videoWidth = (int) (videoWidth / ((float) maxSide / videoHeight)); + videoHeight = maxSide; + } } renderDataSet = false; setRenderData(); @@ -310,6 +343,19 @@ private void setRenderData() { renderBufferHeight = filterShaders.getRenderBufferHeight(); } + private void makeCurrentContext() { + if (!eglContext.equals(egl10.eglGetCurrentContext()) || !eglSurface.equals(egl10.eglGetCurrentSurface(EGL10.EGL_DRAW))) { + if (!egl10.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) { + if (BuildVars.LOGS_ENABLED) { + FileLog.e("eglMakeCurrent failed " + GLUtils.getEGLErrorString(egl10.eglGetError())); + } + return; + } + } + } + + private boolean filterTextureAvailable; + private Runnable drawRunnable = new Runnable() { @Override public void run() { @@ -317,14 +363,7 @@ public void run() { return; } - if (!eglContext.equals(egl10.eglGetCurrentContext()) || !eglSurface.equals(egl10.eglGetCurrentSurface(EGL10.EGL_DRAW))) { - if (!egl10.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) { - if (BuildVars.LOGS_ENABLED) { - FileLog.e("eglMakeCurrent failed " + GLUtils.getEGLErrorString(egl10.eglGetError())); - } - return; - } - } + makeCurrentContext(); if (updateSurface) { videoSurfaceTexture.updateTexImage(); @@ -348,22 +387,25 @@ public void run() { } filterShaders.drawCustomParamsPass(); blurred = filterShaders.drawBlurPass(); + filterTextureAvailable = true; } - GLES20.glViewport(0, 0, surfaceWidth, surfaceHeight); - GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); - - GLES20.glUseProgram(simpleShaderProgram); - GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, filterShaders.getRenderTexture(blurred ? 0 : 1)); + if (filterTextureAvailable) { + GLES20.glViewport(0, 0, surfaceWidth, surfaceHeight); + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); - GLES20.glUniform1i(simpleSourceImageHandle, 0); - GLES20.glEnableVertexAttribArray(simpleInputTexCoordHandle); - GLES20.glVertexAttribPointer(simpleInputTexCoordHandle, 2, GLES20.GL_FLOAT, false, 8, textureBuffer != null ? textureBuffer : filterShaders.getTextureBuffer()); - GLES20.glEnableVertexAttribArray(simplePositionHandle); - GLES20.glVertexAttribPointer(simplePositionHandle, 2, GLES20.GL_FLOAT, false, 8, filterShaders.getVertexBuffer()); - GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); - egl10.eglSwapBuffers(eglDisplay, eglSurface); + GLES20.glUseProgram(simpleShaderProgram); + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, filterShaders.getRenderTexture(blurred ? 0 : 1)); + + GLES20.glUniform1i(simpleSourceImageHandle, 0); + GLES20.glEnableVertexAttribArray(simpleInputTexCoordHandle); + GLES20.glVertexAttribPointer(simpleInputTexCoordHandle, 2, GLES20.GL_FLOAT, false, 8, textureBuffer != null ? textureBuffer : filterShaders.getTextureBuffer()); + GLES20.glEnableVertexAttribArray(simplePositionHandle); + GLES20.glVertexAttribPointer(simplePositionHandle, 2, GLES20.GL_FLOAT, false, 8, filterShaders.getVertexBuffer()); + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + egl10.eglSwapBuffers(eglDisplay, eglSurface); + } } }; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/FilterShaders.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/FilterShaders.java index 8517d016c2..78c561d3b9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/FilterShaders.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/FilterShaders.java @@ -6,12 +6,15 @@ import android.opengl.GLES11Ext; import android.opengl.GLES20; import android.opengl.GLUtils; +import android.util.Log; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.BuildVars; import org.telegram.messenger.FileLog; import org.telegram.messenger.MediaController; +import org.telegram.messenger.R; import org.telegram.messenger.Utilities; +import org.telegram.ui.Stories.recorder.StoryEntry; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -25,10 +28,10 @@ public class FilterShaders { private static final String YUCIHighPassSkinSmoothingMaskBoostFilterFragmentShaderCode = "precision lowp float;" + - "varying highp vec2 texCoord;" + - "uniform sampler2D sourceImage;" + + "varying highp vec2 vTextureCoord;" + + "uniform sampler2D sTexture;" + "void main() {" + - "vec4 color = texture2D(sourceImage, texCoord);" + + "vec4 color = texture2D(sTexture, vTextureCoord);" + "float hardLightColor = color.b;" + "for (int i = 0; i < 3; ++i)" + "{" + @@ -46,14 +49,14 @@ public class FilterShaders { private static final String highpassSkinSmoothingCompositingFilterFragmentShaderString = "%1$s\n" + "precision lowp float;" + - "varying highp vec2 texCoord;" + + "varying highp vec2 vTextureCoord;" + "varying highp vec2 texCoord2;" + - "uniform %2$s sourceImage;" + + "uniform %2$s sTexture;" + "uniform sampler2D toneCurveTexture;" + "uniform sampler2D inputImageTexture3;" + "uniform lowp float mixturePercent;" + "void main() {" + - "vec4 image = texture2D(sourceImage, texCoord);" + + "vec4 image = texture2D(sTexture, vTextureCoord);" + "vec4 mask = texture2D(inputImageTexture3, texCoord2);" + "float redCurveValue = texture2D(toneCurveTexture, vec2(image.r, 0.0)).r;" + "float greenCurveValue = texture2D(toneCurveTexture, vec2(image.g, 0.0)).g;" + @@ -63,13 +66,32 @@ public class FilterShaders { "gl_FragColor = vec4(mix(image.rgb, tone.rgb, 1.0 - mask.b), 1.0);" + "}"; + private static final String yuv_highpassSkinSmoothingCompositingFilterFragmentShaderString_300 = + "%1$s\n" + + "in highp vec2 vTextureCoord;" + + "in highp vec2 texCoord2;" + + "uniform sampler2D toneCurveTexture;" + + "uniform sampler2D inputImageTexture3;" + + "uniform lowp float mixturePercent;" + + "out vec4 fragColor;" + + "void main() {" + + "vec4 image = TEX(vTextureCoord);" + + "vec4 mask = texture(inputImageTexture3, texCoord2);" + + "float redCurveValue = texture(toneCurveTexture, vec2(image.r, 0.0)).r;" + + "float greenCurveValue = texture(toneCurveTexture, vec2(image.g, 0.0)).g;" + + "float blueCurveValue = texture(toneCurveTexture, vec2(image.b, 0.0)).b;" + + "vec4 result = vec4(redCurveValue, greenCurveValue, blueCurveValue, image.a);" + + "vec4 tone = mix(image, result, mixturePercent);" + + "fragColor = vec4(mix(image.rgb, tone.rgb, 1.0 - mask.b), 1.0);" + + "}"; + private static final String greenAndBlueChannelOverlayFragmentShaderCode = "%1$s\n" + "precision lowp float;" + - "varying highp vec2 texCoord;" + - "uniform %2$s sourceImage;" + + "varying highp vec2 vTextureCoord;" + + "uniform %2$s sTexture;" + "void main() {" + - "vec4 inp = texture2D(sourceImage, texCoord);" + + "vec4 inp = texture2D(sTexture, vTextureCoord);" + "vec4 image = vec4(inp.rgb * pow(2.0, -1.0), inp.w);" + "vec4 base = vec4(image.g, image.g, image.g, 1.0);" + "vec4 overlay = vec4(image.b, image.b, image.b, 1.0);" + @@ -77,37 +99,50 @@ public class FilterShaders { "gl_FragColor = vec4(ba,ba,ba,image.a);" + "}"; + private static final String greenAndBlueChannelOverlayFragmentShaderCode_300 = + "%1$s\n" + + "in highp vec2 vTextureCoord;" + + "out vec4 fragColor;" + + "void main() {" + + "vec4 inp = TEX(vTextureCoord);" + + "vec4 image = vec4(inp.rgb * pow(2.0, -1.0), inp.w);" + + "vec4 base = vec4(image.g, image.g, image.g, 1.0);" + + "vec4 overlay = vec4(image.b, image.b, image.b, 1.0);" + + "float ba = 2.0 * overlay.b * base.b + overlay.b * (1.0 - base.a) + base.b * (1.0 - overlay.a);" + + "fragColor = vec4(ba,ba,ba,image.a);" + + "}"; + private static final String stillImageHighPassFilterFragmentShaderCode = "precision lowp float;" + - "varying highp vec2 texCoord;" + + "varying highp vec2 vTextureCoord;" + "varying highp vec2 texCoord2;" + - "uniform sampler2D sourceImage;" + + "uniform sampler2D sTexture;" + "uniform sampler2D inputImageTexture2;" + "void main() {" + - "vec4 image = texture2D(sourceImage, texCoord);" + + "vec4 image = texture2D(sTexture, vTextureCoord);" + "vec4 blurredImage = texture2D(inputImageTexture2, texCoord2);" + "gl_FragColor = vec4((image.rgb - blurredImage.rgb + vec3(0.5,0.5,0.5)), image.a);" + "}"; private static final String radialBlurFragmentShaderCode = - "varying highp vec2 texCoord;" + - "uniform sampler2D sourceImage;" + + "varying highp vec2 vTextureCoord;" + + "uniform sampler2D sTexture;" + "uniform sampler2D inputImageTexture2;" + "uniform lowp float excludeSize;" + "uniform lowp vec2 excludePoint;" + "uniform lowp float excludeBlurSize;" + "uniform highp float aspectRatio;" + "void main() {" + - "lowp vec4 sharpImageColor = texture2D(sourceImage, texCoord);" + - "lowp vec4 blurredImageColor = texture2D(inputImageTexture2, texCoord);" + - "highp vec2 texCoordToUse = vec2(texCoord.x, (texCoord.y * aspectRatio + 0.5 - 0.5 * aspectRatio));" + + "lowp vec4 sharpImageColor = texture2D(sTexture, vTextureCoord);" + + "lowp vec4 blurredImageColor = texture2D(inputImageTexture2, vTextureCoord);" + + "highp vec2 texCoordToUse = vec2(vTextureCoord.x, (vTextureCoord.y * aspectRatio + 0.5 - 0.5 * aspectRatio));" + "highp float distanceFromCenter = distance(excludePoint, texCoordToUse);" + "gl_FragColor = mix(sharpImageColor, blurredImageColor, smoothstep(excludeSize - excludeBlurSize, excludeSize, distanceFromCenter));" + "}"; private static final String linearBlurFragmentShaderCode = - "varying highp vec2 texCoord;" + - "uniform sampler2D sourceImage;" + + "varying highp vec2 vTextureCoord;" + + "uniform sampler2D sTexture;" + "uniform sampler2D inputImageTexture2;" + "uniform lowp float excludeSize;" + "uniform lowp vec2 excludePoint;" + @@ -115,9 +150,9 @@ public class FilterShaders { "uniform highp float angle;" + "uniform highp float aspectRatio;" + "void main() {" + - "lowp vec4 sharpImageColor = texture2D(sourceImage, texCoord);" + - "lowp vec4 blurredImageColor = texture2D(inputImageTexture2, texCoord);" + - "highp vec2 texCoordToUse = vec2(texCoord.x, (texCoord.y * aspectRatio + 0.5 - 0.5 * aspectRatio));" + + "lowp vec4 sharpImageColor = texture2D(sTexture, vTextureCoord);" + + "lowp vec4 blurredImageColor = texture2D(inputImageTexture2, vTextureCoord);" + + "highp vec2 texCoordToUse = vec2(vTextureCoord.x, (vTextureCoord.y * aspectRatio + 0.5 - 0.5 * aspectRatio));" + "highp float distanceFromCenter = abs((texCoordToUse.x - excludePoint.x) * aspectRatio * cos(angle) + (texCoordToUse.y - excludePoint.y) * sin(angle));" + "gl_FragColor = mix(sharpImageColor, blurredImageColor, smoothstep(excludeSize - excludeBlurSize, excludeSize, distanceFromCenter));" + "}"; @@ -183,21 +218,21 @@ private static String fragmentShaderForOptimizedBlurOfRadius(int blurRadius, flo StringBuilder shaderString = new StringBuilder(); - shaderString.append("uniform sampler2D sourceImage;\n"); + shaderString.append("uniform sampler2D sTexture;\n"); shaderString.append("uniform highp float texelWidthOffset;\n"); shaderString.append("uniform highp float texelHeightOffset;\n"); shaderString.append(String.format(Locale.US, "varying highp vec2 blurCoordinates[%d];\n", 1 + (numberOfOptimizedOffsets * 2))); shaderString.append("void main()\n"); shaderString.append("{\n"); shaderString.append("lowp vec4 sum = vec4(0.0);\n"); - shaderString.append(String.format(Locale.US, "sum += texture2D(sourceImage, blurCoordinates[0]) * %f;\n", standardGaussianWeights[0])); + shaderString.append(String.format(Locale.US, "sum += texture2D(sTexture, blurCoordinates[0]) * %f;\n", standardGaussianWeights[0])); for (int currentBlurCoordinateIndex = 0; currentBlurCoordinateIndex < numberOfOptimizedOffsets; currentBlurCoordinateIndex++) { float firstWeight = standardGaussianWeights[currentBlurCoordinateIndex * 2 + 1]; float secondWeight = standardGaussianWeights[currentBlurCoordinateIndex * 2 + 2]; float optimizedWeight = firstWeight + secondWeight; - shaderString.append(String.format(Locale.US, "sum += texture2D(sourceImage, blurCoordinates[%d]) * %f;\n", ((currentBlurCoordinateIndex * 2) + 1), optimizedWeight)); - shaderString.append(String.format(Locale.US, "sum += texture2D(sourceImage, blurCoordinates[%d]) * %f;\n", ((currentBlurCoordinateIndex * 2) + 2), optimizedWeight)); + shaderString.append(String.format(Locale.US, "sum += texture2D(sTexture, blurCoordinates[%d]) * %f;\n", ((currentBlurCoordinateIndex * 2) + 1), optimizedWeight)); + shaderString.append(String.format(Locale.US, "sum += texture2D(sTexture, blurCoordinates[%d]) * %f;\n", ((currentBlurCoordinateIndex * 2) + 2), optimizedWeight)); } if (trueNumberOfOptimizedOffsets > numberOfOptimizedOffsets) { @@ -208,8 +243,8 @@ private static String fragmentShaderForOptimizedBlurOfRadius(int blurRadius, flo float secondWeight = standardGaussianWeights[currentOverlowTextureRead * 2 + 2]; float optimizedWeight = firstWeight + secondWeight; float optimizedOffset = (firstWeight * (currentOverlowTextureRead * 2 + 1) + secondWeight * (currentOverlowTextureRead * 2 + 2)) / optimizedWeight; - shaderString.append(String.format(Locale.US, "sum += texture2D(sourceImage, blurCoordinates[0] + singleStepOffset * %f) * %f;\n", optimizedOffset, optimizedWeight)); - shaderString.append(String.format(Locale.US, "sum += texture2D(sourceImage, blurCoordinates[0] - singleStepOffset * %f) * %f;\n", optimizedOffset, optimizedWeight)); + shaderString.append(String.format(Locale.US, "sum += texture2D(sTexture, blurCoordinates[0] + singleStepOffset * %f) * %f;\n", optimizedOffset, optimizedWeight)); + shaderString.append(String.format(Locale.US, "sum += texture2D(sTexture, blurCoordinates[0] - singleStepOffset * %f) * %f;\n", optimizedOffset, optimizedWeight)); } } @@ -281,7 +316,7 @@ public boolean create() { } else { blurPositionHandle = GLES20.glGetAttribLocation(blurShaderProgram, "position"); blurInputTexCoordHandle = GLES20.glGetAttribLocation(blurShaderProgram, "inputTexCoord"); - blurSourceImageHandle = GLES20.glGetUniformLocation(blurShaderProgram, "sourceImage"); + blurSourceImageHandle = GLES20.glGetUniformLocation(blurShaderProgram, "sTexture"); blurWidthHandle = GLES20.glGetUniformLocation(blurShaderProgram, "texelWidthOffset"); blurHeightHandle = GLES20.glGetUniformLocation(blurShaderProgram, "texelHeightOffset"); } @@ -293,29 +328,53 @@ public boolean create() { "attribute vec4 position;" + "uniform mat4 videoMatrix;" + "attribute vec4 inputTexCoord;" + - "varying vec2 texCoord;" + + "varying vec2 vTextureCoord;" + "void main() {" + "gl_Position = position;" + - "texCoord = vec2(videoMatrix * inputTexCoord).xy;" + + "vTextureCoord = vec2(videoMatrix * inputTexCoord).xy;" + + "}"; + + private static final String simpleVertexVideoShaderCode_300 = + "#version 320 es\n" + + "in vec4 position;" + + "uniform mat4 videoMatrix;" + + "in vec4 inputTexCoord;" + + "out vec2 vTextureCoord;" + + "void main() {" + + "gl_Position = position;" + + "vTextureCoord = vec2(videoMatrix * inputTexCoord).xy;" + "}"; private static final String simpleTwoTextureVertexVideoShaderCode = "attribute vec4 position;" + "uniform mat4 videoMatrix;" + "attribute vec4 inputTexCoord;" + - "varying vec2 texCoord;" + + "varying vec2 vTextureCoord;" + "varying vec2 texCoord2;" + "void main() {" + "gl_Position = position;" + - "texCoord = vec2(videoMatrix * inputTexCoord).xy;" + + "vTextureCoord = vec2(videoMatrix * inputTexCoord).xy;" + "texCoord2 = inputTexCoord.xy;" + "}"; + private static final String simpleTwoTextureVertexVideoShaderCode_300 = + "#version 320 es\n" + + "in vec4 position;" + + "uniform mat4 videoMatrix;" + + "in vec4 inputTexCoord;" + + "out vec2 vTextureCoord;" + + "out vec2 texCoord2;" + + "void main() {" + + "gl_Position = position;" + + "vTextureCoord = vec2(videoMatrix * inputTexCoord).xy;" + + "texCoord2 = inputTexCoord.xy;" + + "}"; + private static final String rgbToHsvFragmentShaderCode = "%1$s\n" + "precision highp float;" + - "varying vec2 texCoord;" + - "uniform %2$s sourceImage;" + + "varying vec2 vTextureCoord;" + + "uniform %2$s sTexture;" + "vec3 rgb_to_hsv(vec3 c) {" + "vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);" + "vec4 p = c.g < c.b ? vec4(c.bg, K.wz) : vec4(c.gb, K.xy);" + @@ -325,20 +384,37 @@ public boolean create() { "return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);" + "}" + "void main() {" + - "vec4 texel = texture2D(sourceImage, texCoord);" + + "vec4 texel = texture2D(sTexture, vTextureCoord);" + "gl_FragColor = vec4(rgb_to_hsv(texel.rgb), texel.a);" + "}"; + private static final String yuvToHsvFragmentShaderCode_300 = + "%1$s\n" + + "in vec2 vTextureCoord;" + + "out vec4 fragColor;" + + "highp vec3 rgb_to_hsv(vec3 c) {" + + "highp vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);" + + "highp vec4 p = c.g < c.b ? vec4(c.bg, K.wz) : vec4(c.gb, K.xy);" + + "highp vec4 q = c.r < p.x ? vec4(p.xyw, c.r) : vec4(c.r, p.yzx);" + + "highp float d = q.x - min(q.w, q.y);" + + "highp float e = 1.0e-10;" + + "return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);" + + "}" + + "void main() {" + + "vec4 texel = TEX(vTextureCoord);" + + "fragColor = vec4(rgb_to_hsv(texel.rgb), texel.a);" + + "}"; + private static final String enhanceFragmentShaderCode = "precision highp float;" + - "varying vec2 texCoord;" + - "uniform sampler2D sourceImage;" + + "varying vec2 vTextureCoord;" + + "uniform sampler2D sTexture;" + "uniform sampler2D inputImageTexture2;" + "uniform float intensity;" + "float enhance(float value) {" + "const vec2 offset = vec2(0.001953125, 0.03125);" + "value = value + offset.x;" + - "vec2 coord = (clamp(texCoord, 0.125, 1.0 - 0.125001) - 0.125) * 4.0;" + + "vec2 coord = (clamp(vTextureCoord, 0.125, 1.0 - 0.125001) - 0.125) * 4.0;" + "vec2 frac = fract(coord);" + "coord = floor(coord);" + "float p00 = float(coord.y * 4.0 + coord.x) * 0.0625 + offset.y;" + @@ -363,7 +439,7 @@ public boolean create() { "return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);" + "}" + "void main() {" + - "vec4 texel = texture2D(sourceImage, texCoord);" + + "vec4 texel = texture2D(sTexture, vTextureCoord);" + "vec4 hsv = texel;" + "hsv.y = min(1.0, hsv.y * 1.2);" + "hsv.z = min(1.0, enhance(hsv.z) * 1.1);" + @@ -373,34 +449,34 @@ public boolean create() { public static final String simpleVertexShaderCode = "attribute vec4 position;" + "attribute vec2 inputTexCoord;" + - "varying vec2 texCoord;" + + "varying vec2 vTextureCoord;" + "void main() {" + "gl_Position = position;" + - "texCoord = inputTexCoord;" + + "vTextureCoord = inputTexCoord;" + "}"; public static final String simpleTwoTextureVertexShaderCode = "attribute vec4 position;" + "attribute vec2 inputTexCoord;" + - "varying vec2 texCoord;" + + "varying vec2 vTextureCoord;" + "varying vec2 texCoord2;" + "void main() {" + "gl_Position = position;" + - "texCoord = inputTexCoord;" + + "vTextureCoord = inputTexCoord;" + "texCoord2 = inputTexCoord;" + "}"; public static final String simpleFragmentShaderCode = - "varying highp vec2 texCoord;" + - "uniform sampler2D sourceImage;" + + "varying highp vec2 vTextureCoord;" + + "uniform sampler2D sTexture;" + "void main() {" + - "gl_FragColor = texture2D(sourceImage, texCoord);" + + "gl_FragColor = texture2D(sTexture, vTextureCoord);" + "}"; private static final String sharpenVertexShaderCode = "attribute vec4 position;" + "attribute vec2 inputTexCoord;" + - "varying vec2 texCoord;" + + "varying vec2 vTextureCoord;" + "uniform highp float inputWidth;" + "uniform highp float inputHeight;" + @@ -411,7 +487,7 @@ public boolean create() { "void main() {" + "gl_Position = position;" + - "texCoord = inputTexCoord;" + + "vTextureCoord = inputTexCoord;" + "highp vec2 widthStep = vec2(1.0 / inputWidth, 0.0);" + "highp vec2 heightStep = vec2(0.0, 1.0 / inputHeight);" + "leftTexCoord = inputTexCoord - widthStep;" + @@ -422,29 +498,29 @@ public boolean create() { private static final String sharpenFragmentShaderCode = "precision highp float;" + - "varying vec2 texCoord;" + + "varying vec2 vTextureCoord;" + "varying vec2 leftTexCoord;" + "varying vec2 rightTexCoord;" + "varying vec2 topTexCoord;" + "varying vec2 bottomTexCoord;" + - "uniform sampler2D sourceImage;" + + "uniform sampler2D sTexture;" + "uniform float sharpen;" + "void main() {" + - "vec4 result = texture2D(sourceImage, texCoord);" + + "vec4 result = texture2D(sTexture, vTextureCoord);" + - "vec3 leftTextureColor = texture2D(sourceImage, leftTexCoord).rgb;" + - "vec3 rightTextureColor = texture2D(sourceImage, rightTexCoord).rgb;" + - "vec3 topTextureColor = texture2D(sourceImage, topTexCoord).rgb;" + - "vec3 bottomTextureColor = texture2D(sourceImage, bottomTexCoord).rgb;" + + "vec3 leftTextureColor = texture2D(sTexture, leftTexCoord).rgb;" + + "vec3 rightTextureColor = texture2D(sTexture, rightTexCoord).rgb;" + + "vec3 topTextureColor = texture2D(sTexture, topTexCoord).rgb;" + + "vec3 bottomTextureColor = texture2D(sTexture, bottomTexCoord).rgb;" + "result.rgb = result.rgb * (1.0 + 4.0 * sharpen) - (leftTextureColor + rightTextureColor + topTextureColor + bottomTextureColor) * sharpen;" + "gl_FragColor = result;" + "}"; private static final String toolsFragmentShaderCode = - "varying highp vec2 texCoord;" + - "uniform sampler2D sourceImage;" + + "varying highp vec2 vTextureCoord;" + + "uniform sampler2D sTexture;" + "uniform highp float width;" + "uniform highp float height;" + "uniform sampler2D curvesImage;" + @@ -619,7 +695,7 @@ public boolean create() { "return vec2(((tc.x * 2.0 - 1.0) * cos(angle) - (tc.y * 2.0 - 1.0) * sin(angle)) * 0.5 + 0.5, ((tc.y * 2.0 - 1.0) * cos(angle) + (tc.x * 2.0 - 1.0) * sin(angle)) * 0.5 + 0.5);" + "}" + "void main() {" + - "lowp vec4 source = texture2D(sourceImage, texCoord);" + + "lowp vec4 source = texture2D(sTexture, vTextureCoord);" + "lowp vec4 result = source;" + "const lowp float toolEpsilon = 0.005;" + "if (skipTone < toolEpsilon) {" + @@ -676,7 +752,7 @@ public boolean create() { "}" + "if (abs(grain) > toolEpsilon) {" + "highp vec3 rotOffset = vec3(1.425, 3.892, 5.835);" + - "highp vec2 rotCoordsR = coordRot(texCoord, rotOffset.x);" + + "highp vec2 rotCoordsR = coordRot(vTextureCoord, rotOffset.x);" + "highp vec3 noise = vec3(pnoise3D(vec3(rotCoordsR * vec2(width / grainsize, height / grainsize),0.0)));" + "lowp vec3 lumcoeff = vec3(0.299,0.587,0.114);" + "lowp float luminance = dot(result.rgb, lumcoeff);" + @@ -688,7 +764,7 @@ public boolean create() { "if (abs(vignette) > toolEpsilon) {" + "const lowp float midpoint = 0.7;" + "const lowp float fuzziness = 0.62;" + - "lowp float radDist = length(texCoord - 0.5) / sqrt(0.5);" + + "lowp float radDist = length(vTextureCoord - 0.5) / sqrt(0.5);" + "lowp float mag = easeInOutSigmoid(radDist * midpoint, fuzziness) * vignette * 0.645;" + "result.rgb = mix(pow(result.rgb, vec3(1.0 / (1.0 - mag))), vec3(0.0), mag * mag);" + "}" + @@ -908,6 +984,7 @@ public int getCurveTexture() { private int greenAndBlueChannelOverlayInputTexCoordHandle; private int greenAndBlueChannelOverlaySourceImageHandle; private int greenAndBlueChannelOverlayMatrixHandle; + private int greenAndBlueChannelOverlayTexSizeHandle; private ToneCurve toneCurve; private int highPassProgram; @@ -924,6 +1001,7 @@ public int getCurveTexture() { private int compositeCurveImageHandle; private int compositeMixtureHandle; private int compositeMatrixHandle; + private int compositeTexSizeHandle; private int boostProgram; private int boostPositionHandle; @@ -935,6 +1013,7 @@ public int getCurveTexture() { private int[] rgbToHsvInputTexCoordHandle = new int[2]; private int[] rgbToHsvSourceImageHandle = new int[2]; private int rgbToHsvMatrixHandle; + private int rgbToHsvTexSizeHandle; private int enhanceShaderProgram; private int enhancePositionHandle; @@ -996,6 +1075,7 @@ public int getCurveTexture() { private int sharpenSourceImageHandle; private int videoTexture; + private int videoWidth, videoHeight; private float[] videoMatrix; private int videoFramesCount; @@ -1023,9 +1103,11 @@ public int getCurveTexture() { private FilterShadersDelegate delegate; private boolean isVideo; + private StoryEntry.HDRInfo hdrInfo; - public FilterShaders(boolean video) { + public FilterShaders(boolean video, StoryEntry.HDRInfo hdrInfo) { isVideo = video; + this.hdrInfo = hdrInfo; float[] squareCoordinates = { -1.0f, 1.0f, @@ -1106,7 +1188,7 @@ public boolean create() { } else { positionHandle = GLES20.glGetAttribLocation(toolsShaderProgram, "position"); inputTexCoordHandle = GLES20.glGetAttribLocation(toolsShaderProgram, "inputTexCoord"); - sourceImageHandle = GLES20.glGetUniformLocation(toolsShaderProgram, "sourceImage"); + sourceImageHandle = GLES20.glGetUniformLocation(toolsShaderProgram, "sTexture"); shadowsHandle = GLES20.glGetUniformLocation(toolsShaderProgram, "shadows"); highlightsHandle = GLES20.glGetUniformLocation(toolsShaderProgram, "highlights"); exposureHandle = GLES20.glGetUniformLocation(toolsShaderProgram, "exposure"); @@ -1147,7 +1229,7 @@ public boolean create() { } else { sharpenPositionHandle = GLES20.glGetAttribLocation(sharpenShaderProgram, "position"); sharpenInputTexCoordHandle = GLES20.glGetAttribLocation(sharpenShaderProgram, "inputTexCoord"); - sharpenSourceImageHandle = GLES20.glGetUniformLocation(sharpenShaderProgram, "sourceImage"); + sharpenSourceImageHandle = GLES20.glGetUniformLocation(sharpenShaderProgram, "sTexture"); sharpenWidthHandle = GLES20.glGetUniformLocation(sharpenShaderProgram, "inputWidth"); sharpenHeightHandle = GLES20.glGetUniformLocation(sharpenShaderProgram, "inputHeight"); sharpenHandle = GLES20.glGetUniformLocation(sharpenShaderProgram, "sharpen"); @@ -1161,8 +1243,9 @@ public boolean create() { return false; } - String extension = isVideo ? "#extension GL_OES_EGL_image_external : require" : ""; - String sampler2D = isVideo ? "samplerExternalOES" : "sampler2D"; + if (!setupExternalShaders()) { + return false; + } vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, simpleVertexShaderCode); fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, linearBlurFragmentShaderCode); @@ -1182,7 +1265,7 @@ public boolean create() { } else { linearBlurPositionHandle = GLES20.glGetAttribLocation(linearBlurShaderProgram, "position"); linearBlurInputTexCoordHandle = GLES20.glGetAttribLocation(linearBlurShaderProgram, "inputTexCoord"); - linearBlurSourceImageHandle = GLES20.glGetUniformLocation(linearBlurShaderProgram, "sourceImage"); + linearBlurSourceImageHandle = GLES20.glGetUniformLocation(linearBlurShaderProgram, "sTexture"); linearBlurSourceImage2Handle = GLES20.glGetUniformLocation(linearBlurShaderProgram, "inputImageTexture2"); linearBlurExcludeSizeHandle = GLES20.glGetUniformLocation(linearBlurShaderProgram, "excludeSize"); linearBlurExcludePointHandle = GLES20.glGetUniformLocation(linearBlurShaderProgram, "excludePoint"); @@ -1212,7 +1295,7 @@ public boolean create() { } else { radialBlurPositionHandle = GLES20.glGetAttribLocation(radialBlurShaderProgram, "position"); radialBlurInputTexCoordHandle = GLES20.glGetAttribLocation(radialBlurShaderProgram, "inputTexCoord"); - radialBlurSourceImageHandle = GLES20.glGetUniformLocation(radialBlurShaderProgram, "sourceImage"); + radialBlurSourceImageHandle = GLES20.glGetUniformLocation(radialBlurShaderProgram, "sTexture"); radialBlurSourceImage2Handle = GLES20.glGetUniformLocation(radialBlurShaderProgram, "inputImageTexture2"); radialBlurExcludeSizeHandle = GLES20.glGetUniformLocation(radialBlurShaderProgram, "excludeSize"); radialBlurExcludePointHandle = GLES20.glGetUniformLocation(radialBlurShaderProgram, "excludePoint"); @@ -1223,39 +1306,6 @@ public boolean create() { return false; } - for (int a = 0; a < (isVideo ? 2 : 1); a++) { - if (a == 1 && isVideo) { - vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, simpleVertexVideoShaderCode); - fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, String.format(Locale.US, rgbToHsvFragmentShaderCode, extension, sampler2D)); - } else { - vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, simpleVertexShaderCode); - fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, String.format(Locale.US, rgbToHsvFragmentShaderCode, "", "sampler2D")); - } - if (vertexShader != 0 && fragmentShader != 0) { - rgbToHsvShaderProgram[a] = GLES20.glCreateProgram(); - GLES20.glAttachShader(rgbToHsvShaderProgram[a], vertexShader); - GLES20.glAttachShader(rgbToHsvShaderProgram[a], fragmentShader); - GLES20.glBindAttribLocation(rgbToHsvShaderProgram[a], 0, "position"); - GLES20.glBindAttribLocation(rgbToHsvShaderProgram[a], 1, "inputTexCoord"); - - GLES20.glLinkProgram(rgbToHsvShaderProgram[a]); - GLES20.glGetProgramiv(rgbToHsvShaderProgram[a], GLES20.GL_LINK_STATUS, linkStatus, 0); - if (linkStatus[0] == 0) { - GLES20.glDeleteProgram(rgbToHsvShaderProgram[a]); - rgbToHsvShaderProgram[a] = 0; - } else { - rgbToHsvPositionHandle[a] = GLES20.glGetAttribLocation(rgbToHsvShaderProgram[a], "position"); - rgbToHsvInputTexCoordHandle[a] = GLES20.glGetAttribLocation(rgbToHsvShaderProgram[a], "inputTexCoord"); - rgbToHsvSourceImageHandle[a] = GLES20.glGetUniformLocation(rgbToHsvShaderProgram[a], "sourceImage"); - if (a == 1) { - rgbToHsvMatrixHandle = GLES20.glGetUniformLocation(rgbToHsvShaderProgram[a], "videoMatrix"); - } - } - } else { - return false; - } - } - vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, simpleVertexShaderCode); fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, enhanceFragmentShaderCode); if (vertexShader != 0 && fragmentShader != 0) { @@ -1273,7 +1323,7 @@ public boolean create() { } else { enhancePositionHandle = GLES20.glGetAttribLocation(enhanceShaderProgram, "position"); enhanceInputTexCoordHandle = GLES20.glGetAttribLocation(enhanceShaderProgram, "inputTexCoord"); - enhanceSourceImageHandle = GLES20.glGetUniformLocation(enhanceShaderProgram, "sourceImage"); + enhanceSourceImageHandle = GLES20.glGetUniformLocation(enhanceShaderProgram, "sTexture"); enhanceIntensityHandle = GLES20.glGetUniformLocation(enhanceShaderProgram, "intensity"); enhanceInputImageTexture2Handle = GLES20.glGetUniformLocation(enhanceShaderProgram, "inputImageTexture2"); } @@ -1281,36 +1331,6 @@ public boolean create() { return false; } - if (isVideo) { - vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, simpleVertexVideoShaderCode); - } else { - vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, simpleVertexShaderCode); - } - fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, String.format(Locale.US, greenAndBlueChannelOverlayFragmentShaderCode, extension, sampler2D)); - if (vertexShader != 0 && fragmentShader != 0) { - greenAndBlueChannelOverlayProgram = GLES20.glCreateProgram(); - GLES20.glAttachShader(greenAndBlueChannelOverlayProgram, vertexShader); - GLES20.glAttachShader(greenAndBlueChannelOverlayProgram, fragmentShader); - GLES20.glBindAttribLocation(greenAndBlueChannelOverlayProgram, 0, "position"); - GLES20.glBindAttribLocation(greenAndBlueChannelOverlayProgram, 1, "inputTexCoord"); - - GLES20.glLinkProgram(greenAndBlueChannelOverlayProgram); - GLES20.glGetProgramiv(greenAndBlueChannelOverlayProgram, GLES20.GL_LINK_STATUS, linkStatus, 0); - if (linkStatus[0] == 0) { - GLES20.glDeleteProgram(greenAndBlueChannelOverlayProgram); - greenAndBlueChannelOverlayProgram = 0; - } else { - greenAndBlueChannelOverlayPositionHandle = GLES20.glGetAttribLocation(greenAndBlueChannelOverlayProgram, "position"); - greenAndBlueChannelOverlayInputTexCoordHandle = GLES20.glGetAttribLocation(greenAndBlueChannelOverlayProgram, "inputTexCoord"); - greenAndBlueChannelOverlaySourceImageHandle = GLES20.glGetUniformLocation(greenAndBlueChannelOverlayProgram, "sourceImage"); - if (isVideo) { - greenAndBlueChannelOverlayMatrixHandle = GLES20.glGetUniformLocation(greenAndBlueChannelOverlayProgram, "videoMatrix"); - } - } - } else { - return false; - } - fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, stillImageHighPassFilterFragmentShaderCode); vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, simpleTwoTextureVertexShaderCode); if (vertexShader != 0 && fragmentShader != 0) { @@ -1328,7 +1348,7 @@ public boolean create() { } else { highPassPositionHandle = GLES20.glGetAttribLocation(highPassProgram, "position"); highPassInputTexCoordHandle = GLES20.glGetAttribLocation(highPassProgram, "inputTexCoord"); - highPassSourceImageHandle = GLES20.glGetUniformLocation(highPassProgram, "sourceImage"); + highPassSourceImageHandle = GLES20.glGetUniformLocation(highPassProgram, "sTexture"); highPassInputImageHandle = GLES20.glGetUniformLocation(highPassProgram, "inputImageTexture2"); } } else { @@ -1352,18 +1372,135 @@ public boolean create() { } else { boostPositionHandle = GLES20.glGetAttribLocation(boostProgram, "position"); boostInputTexCoordHandle = GLES20.glGetAttribLocation(boostProgram, "inputTexCoord"); - boostSourceImageHandle = GLES20.glGetUniformLocation(boostProgram, "sourceImage"); + boostSourceImageHandle = GLES20.glGetUniformLocation(boostProgram, "sTexture"); + } + } else { + return false; + } + + toneCurve = new ToneCurve(); + + return true; + } + + public void updateHDRInfo(StoryEntry.HDRInfo hdrInfo) { + this.hdrInfo = hdrInfo; + setupExternalShaders(); + } + + private boolean setupExternalShaders() { + String yuvProcessor = ""; + int hdrType = hdrInfo != null ? hdrInfo.getHDRType() : 0; + if (hdrType == 1) { + yuvProcessor = RLottieDrawable.readRes(null, R.raw.yuv_hlg2rgb); + } else if (hdrType == 2) { + yuvProcessor = RLottieDrawable.readRes(null, R.raw.yuv_pq2rgb); + } + String extension = isVideo ? "#extension GL_OES_EGL_image_external : require" : ""; + String sampler2D = isVideo ? "samplerExternalOES" : "sampler2D"; + + int[] linkStatus = new int[1]; + int vertexShader, fragmentShader; + for (int a = 0; a < (isVideo ? 2 : 1); a++) { + if (a == 1 && isVideo) { + if (hdrType != 0) { + vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, simpleVertexVideoShaderCode_300); + fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, String.format(Locale.US, yuvToHsvFragmentShaderCode_300, yuvProcessor)); + } else { + vertexShader = fragmentShader = 0; + } + if (vertexShader == 0 || fragmentShader == 0) { + vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, simpleVertexVideoShaderCode); + fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, String.format(Locale.US, rgbToHsvFragmentShaderCode, extension, sampler2D)); + } + } else { + vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, simpleVertexShaderCode); + fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, String.format(Locale.US, rgbToHsvFragmentShaderCode, "", "sampler2D")); + } + GLES20.glDeleteProgram(rgbToHsvShaderProgram[a]); + if (vertexShader != 0 && fragmentShader != 0) { + rgbToHsvShaderProgram[a] = GLES20.glCreateProgram(); + GLES20.glAttachShader(rgbToHsvShaderProgram[a], vertexShader); + GLES20.glAttachShader(rgbToHsvShaderProgram[a], fragmentShader); + GLES20.glBindAttribLocation(rgbToHsvShaderProgram[a], 0, "position"); + GLES20.glBindAttribLocation(rgbToHsvShaderProgram[a], 1, "inputTexCoord"); + + GLES20.glLinkProgram(rgbToHsvShaderProgram[a]); + GLES20.glGetProgramiv(rgbToHsvShaderProgram[a], GLES20.GL_LINK_STATUS, linkStatus, 0); + if (linkStatus[0] == 0) { + GLES20.glDeleteProgram(rgbToHsvShaderProgram[a]); + rgbToHsvShaderProgram[a] = 0; + } else { + rgbToHsvPositionHandle[a] = GLES20.glGetAttribLocation(rgbToHsvShaderProgram[a], "position"); + rgbToHsvInputTexCoordHandle[a] = GLES20.glGetAttribLocation(rgbToHsvShaderProgram[a], "inputTexCoord"); + rgbToHsvSourceImageHandle[a] = GLES20.glGetUniformLocation(rgbToHsvShaderProgram[a], "sTexture"); + if (a == 1) { + rgbToHsvMatrixHandle = GLES20.glGetUniformLocation(rgbToHsvShaderProgram[a], "videoMatrix"); + rgbToHsvTexSizeHandle = GLES20.glGetUniformLocation(rgbToHsvShaderProgram[a], "texSize"); + } + } + } else { + return false; + } + } + + if (isVideo) { + if (hdrType != 0) { + vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, simpleVertexVideoShaderCode_300); + fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, String.format(Locale.US, greenAndBlueChannelOverlayFragmentShaderCode_300, yuvProcessor)); + } else { + vertexShader = fragmentShader = 0; + } + if (vertexShader == 0 || fragmentShader == 0) { + vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, simpleVertexVideoShaderCode); + fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, String.format(Locale.US, greenAndBlueChannelOverlayFragmentShaderCode, extension, sampler2D)); + } + } else { + vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, simpleVertexShaderCode); + fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, String.format(Locale.US, greenAndBlueChannelOverlayFragmentShaderCode, extension, sampler2D)); + } + GLES20.glDeleteProgram(greenAndBlueChannelOverlayProgram); + if (vertexShader != 0 && fragmentShader != 0) { + greenAndBlueChannelOverlayProgram = GLES20.glCreateProgram(); + GLES20.glAttachShader(greenAndBlueChannelOverlayProgram, vertexShader); + GLES20.glAttachShader(greenAndBlueChannelOverlayProgram, fragmentShader); + GLES20.glBindAttribLocation(greenAndBlueChannelOverlayProgram, 0, "position"); + GLES20.glBindAttribLocation(greenAndBlueChannelOverlayProgram, 1, "inputTexCoord"); + + GLES20.glLinkProgram(greenAndBlueChannelOverlayProgram); + GLES20.glGetProgramiv(greenAndBlueChannelOverlayProgram, GLES20.GL_LINK_STATUS, linkStatus, 0); + if (linkStatus[0] == 0) { + GLES20.glDeleteProgram(greenAndBlueChannelOverlayProgram); + greenAndBlueChannelOverlayProgram = 0; + } else { + greenAndBlueChannelOverlayPositionHandle = GLES20.glGetAttribLocation(greenAndBlueChannelOverlayProgram, "position"); + greenAndBlueChannelOverlayInputTexCoordHandle = GLES20.glGetAttribLocation(greenAndBlueChannelOverlayProgram, "inputTexCoord"); + greenAndBlueChannelOverlaySourceImageHandle = GLES20.glGetUniformLocation(greenAndBlueChannelOverlayProgram, "sTexture"); + if (isVideo) { + greenAndBlueChannelOverlayMatrixHandle = GLES20.glGetUniformLocation(greenAndBlueChannelOverlayProgram, "videoMatrix"); + greenAndBlueChannelOverlayTexSizeHandle = GLES20.glGetUniformLocation(greenAndBlueChannelOverlayProgram, "texSize"); + } } } else { return false; } if (isVideo) { - vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, simpleTwoTextureVertexVideoShaderCode); + if (hdrType != 0) { + vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, simpleTwoTextureVertexVideoShaderCode_300); + fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, String.format(Locale.US, yuv_highpassSkinSmoothingCompositingFilterFragmentShaderString_300, yuvProcessor)); + } else { + vertexShader = fragmentShader = 0; + } + if (vertexShader == 0 || fragmentShader == 0) { + vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, simpleTwoTextureVertexVideoShaderCode); + fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, String.format(Locale.US, highpassSkinSmoothingCompositingFilterFragmentShaderString, extension, sampler2D)); + } } else { vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, simpleTwoTextureVertexShaderCode); + fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, String.format(Locale.US, highpassSkinSmoothingCompositingFilterFragmentShaderString, extension, sampler2D)); } - fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, String.format(Locale.US, highpassSkinSmoothingCompositingFilterFragmentShaderString, extension, sampler2D)); + GLES20.glDeleteProgram(compositeProgram); if (vertexShader != 0 && fragmentShader != 0) { compositeProgram = GLES20.glCreateProgram(); GLES20.glAttachShader(compositeProgram, vertexShader); @@ -1379,26 +1516,27 @@ public boolean create() { } else { compositePositionHandle = GLES20.glGetAttribLocation(compositeProgram, "position"); compositeInputTexCoordHandle = GLES20.glGetAttribLocation(compositeProgram, "inputTexCoord"); - compositeSourceImageHandle = GLES20.glGetUniformLocation(compositeProgram, "sourceImage"); + compositeSourceImageHandle = GLES20.glGetUniformLocation(compositeProgram, "sTexture"); compositeInputImageHandle = GLES20.glGetUniformLocation(compositeProgram, "inputImageTexture3"); compositeCurveImageHandle = GLES20.glGetUniformLocation(compositeProgram, "toneCurveTexture"); compositeMixtureHandle = GLES20.glGetUniformLocation(compositeProgram, "mixturePercent"); if (isVideo) { compositeMatrixHandle = GLES20.glGetUniformLocation(compositeProgram, "videoMatrix"); + compositeTexSizeHandle = GLES20.glGetUniformLocation(compositeProgram, "texSize"); } } } else { return false; } - toneCurve = new ToneCurve(); - return true; } public void setRenderData(Bitmap currentBitmap, int orientation, int videoTex, int w, int h) { loadTexture(currentBitmap, orientation, w, h); videoTexture = videoTex; + videoWidth = w; + videoHeight = h; GLES20.glBindTexture(GL10.GL_TEXTURE_2D, enhanceTextures[0]); GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR); @@ -1439,6 +1577,9 @@ public void drawEnhancePass() { GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, videoTexture); GLES20.glUniformMatrix4fv(rgbToHsvMatrixHandle, 1, false, videoMatrix, 0); + if (rgbToHsvTexSizeHandle != -1) { + GLES20.glUniform2f(rgbToHsvTexSizeHandle, renderBufferWidth, renderBufferHeight); + } index = 1; } else { GLES20.glUseProgram(rgbToHsvShaderProgram[0]); @@ -1627,6 +1768,9 @@ public boolean drawSkinSmoothPass() { GLES20.glEnableVertexAttribArray(greenAndBlueChannelOverlayPositionHandle); if (isVideo) { GLES20.glUniformMatrix4fv(greenAndBlueChannelOverlayMatrixHandle, 1, false, videoMatrix, 0); + if (greenAndBlueChannelOverlayTexSizeHandle != -1) { + GLES20.glUniform2f(greenAndBlueChannelOverlayTexSizeHandle, renderBufferWidth, renderBufferHeight); + } } GLES20.glVertexAttribPointer(greenAndBlueChannelOverlayPositionHandle, 2, GLES20.GL_FLOAT, false, 8, vertexInvertBuffer); @@ -1710,6 +1854,9 @@ public boolean drawSkinSmoothPass() { GLES20.glVertexAttribPointer(compositePositionHandle, 2, GLES20.GL_FLOAT, false, 8, vertexInvertBuffer); if (isVideo) { GLES20.glUniformMatrix4fv(compositeMatrixHandle, 1, false, videoMatrix, 0); + if (compositeTexSizeHandle != -1) { + GLES20.glUniform2f(compositeTexSizeHandle, renderBufferWidth, renderBufferHeight); + } } GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, renderFrameBuffer[1]); @@ -1827,6 +1974,12 @@ private Bitmap createBitmap(Bitmap bitmap, int orientation, float scale) { return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); } + + private boolean scaleBitmap = true; + public void setScaleBitmap(boolean scale) { + this.scaleBitmap = scale; + } + private void loadTexture(Bitmap bitmap, int orientation, int w, int h) { renderBufferWidth = w; renderBufferHeight = h; @@ -1841,9 +1994,9 @@ private void loadTexture(Bitmap bitmap, int orientation, int w, int h) { GLES20.glGenTextures(1, bitmapTextre, 0); float maxSize = AndroidUtilities.getPhotoSize(); - if (renderBufferWidth > maxSize || renderBufferHeight > maxSize || orientation % 360 != 0) { + if (scaleBitmap && (renderBufferWidth > maxSize || renderBufferHeight > maxSize) || orientation % 360 != 0) { float scale = 1; - if (renderBufferWidth > maxSize || renderBufferHeight > maxSize) { + if (scaleBitmap && (renderBufferWidth > maxSize || renderBufferHeight > maxSize)) { float scaleX = maxSize / bitmap.getWidth(); float scaleY = maxSize / bitmap.getHeight(); if (scaleX < scaleY) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/FilterTabsView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/FilterTabsView.java index 95094975a8..85dbfa7b94 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/FilterTabsView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/FilterTabsView.java @@ -29,6 +29,7 @@ import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; +import android.util.Log; import android.util.Property; import android.util.SparseIntArray; import android.view.HapticFeedbackConstants; @@ -75,14 +76,15 @@ public int getStableId(int selectedType) { return positionToStableId.get(selectedType, -1); } - public void selectTabWithStableId(int stableId) { + public boolean selectTabWithStableId(int stableId) { for (int i = 0; i < tabs.size(); i++) { if (positionToStableId.get(i, -1) == stableId) { currentPosition = i; selectedTabId = positionToId.get(i); - break; + return true; } } + return false; } public interface FilterTabsViewDelegate { @@ -278,12 +280,12 @@ protected void onDraw(Canvas canvas) { ); canvas.rotate(1.4f * s, getMeasuredWidth() / 2f, getMeasuredHeight() / 2f); } - String key; - String animateToKey; - String otherKey; - String animateToOtherKey; - String unreadKey; - String unreadOtherKey; + int key; + int animateToKey; + int otherKey; + int animateToOtherKey; + int unreadKey; + int unreadOtherKey; int id1; int id2; if (manualScrollingToId != -1) { @@ -308,7 +310,7 @@ protected void onDraw(Canvas canvas) { unreadKey = Theme.key_chats_tabUnreadUnactiveBackground; unreadOtherKey = Theme.key_chats_tabUnreadActiveBackground; } - if (animateToKey == null) { + if (animateToKey < 0) { if ((animatingIndicator || manualScrollingToId != -1) && (currentTab.id == id1 || currentTab.id == id2)) { textPaint.setColor(ColorUtils.blendARGB(Theme.getColor(otherKey), Theme.getColor(key), animatingIndicatorProgress)); } else { @@ -452,7 +454,7 @@ protected void onDraw(Canvas canvas) { } if (animateCounterEnter || counterText != null || showRemove && (isEditing || editingStartAnimationProgress != 0)) { - if (aBackgroundColorKey == null) { + if (aBackgroundColorKey < 0) { textCounterPaint.setColor(Theme.getColor(backgroundColorKey)); } else { int color1 = Theme.getColor(backgroundColorKey); @@ -586,7 +588,7 @@ protected void onDraw(Canvas canvas) { } progressToLocked = Utilities.clamp(progressToLocked, 1f, 0); int unactiveColor = Theme.getColor(unactiveTextColorKey); - if (aUnactiveTextColorKey != null) { + if (aUnactiveTextColorKey >= 0) { unactiveColor = ColorUtils.blendARGB(unactiveColor, Theme.getColor(aUnactiveTextColorKey), animationValue); } if (lockDrawableColor != unactiveColor) { @@ -846,15 +848,15 @@ public void onAnimationEnd(Animator animation) { private int scrollingToChild = -1; private GradientDrawable selectorDrawable; - private String tabLineColorKey = Theme.key_actionBarTabLine; - private String activeTextColorKey = Theme.key_actionBarTabActiveText; - private String unactiveTextColorKey = Theme.key_actionBarTabUnactiveText; - private String selectorColorKey = Theme.key_actionBarTabSelector; - private String backgroundColorKey = Theme.key_actionBarDefault; - private String aTabLineColorKey; - private String aActiveTextColorKey; - private String aUnactiveTextColorKey; - private String aBackgroundColorKey; + private int tabLineColorKey = Theme.key_actionBarTabLine; + private int activeTextColorKey = Theme.key_actionBarTabActiveText; + private int unactiveTextColorKey = Theme.key_actionBarTabUnactiveText; + private int selectorColorKey = Theme.key_actionBarTabSelector; + private int backgroundColorKey = Theme.key_actionBarDefault; + private int aTabLineColorKey = -1; + private int aActiveTextColorKey = -1; + private int aUnactiveTextColorKey = -1; + private int aBackgroundColorKey = -1; private int prevLayoutWidth; @@ -1166,7 +1168,11 @@ public boolean isAnimatingIndicator() { return animatingIndicator; } - private void scrollToTab(Tab tab, int position) { + public void stopAnimatingIndicator() { + animatingIndicator = false; + } + + public void scrollToTab(Tab tab, int position) { if (tab.isLocked) { if (delegate != null) { delegate.onPageSelected(tab, false); @@ -1214,6 +1220,17 @@ public void selectDefaultTab() { scrollToTab(defaultTab, defaultTab.id); } + public boolean isFirstTab() { + return currentPosition <= 0; + } + + public void selectLastTab() { + if (tabs.isEmpty()) { + return; + } + scrollToTab(tabs.get(tabs.size() - 1), tabs.size() - 1); + } + public void setAnimationIdicatorProgress(float value) { animatingIndicatorProgress = value; listView.invalidateViews(); @@ -1271,12 +1288,23 @@ public void addTab(int id, int stableId, String text, String emoticon, boolean i tabs.add(tab); } + public int getTabsCount() { + return tabs.size(); + } + + public Tab getTab(int i) { + if (i < 0 || i >= getTabsCount()) { + return null; + } + return tabs.get(i); + } + public void finishAddingTabs(boolean animated) { listView.setItemAnimator(animated ? itemAnimator : null); adapter.notifyDataSetChanged(); } - public void animateColorsTo(String line, String active, String unactive, String selector, String background) { + public void animateColorsTo(int line, int active, int unactive, int selector, int background) { if (colorChangeAnimator != null) { colorChangeAnimator.cancel(); } @@ -1297,10 +1325,10 @@ public void onAnimationEnd(Animator animation) { backgroundColorKey = aBackgroundColorKey; activeTextColorKey = aActiveTextColorKey; unactiveTextColorKey = aUnactiveTextColorKey; - aTabLineColorKey = null; - aActiveTextColorKey = null; - aUnactiveTextColorKey = null; - aBackgroundColorKey = null; + aTabLineColorKey = -1; + aActiveTextColorKey = -1; + aUnactiveTextColorKey = -1; + aBackgroundColorKey = -1; } }); colorChangeAnimator.start(); @@ -1314,7 +1342,7 @@ public int getFirstTabId() { return positionToId.get(0, 0); } - public String getSelectorColorKey() { + public int getSelectorColorKey() { return selectorColorKey; } @@ -1582,7 +1610,7 @@ public void setIsEditing(boolean value) { if (!isEditing && orderChanged) { MessagesStorage.getInstance(UserConfig.selectedAccount).saveDialogFiltersOrder(); TLRPC.TL_messages_updateDialogFiltersOrder req = new TLRPC.TL_messages_updateDialogFiltersOrder(); - ArrayList filters = MessagesController.getInstance(UserConfig.selectedAccount).dialogFilters; + ArrayList filters = MessagesController.getInstance(UserConfig.selectedAccount).getDialogFilters(); for (int a = 0, N = filters.size(); a < N; a++) { MessagesController.DialogFilter filter = filters.get(a); if (filter.isDefault()) { @@ -1707,7 +1735,7 @@ public void swapElements(int fromIndex, int toIndex) { if (idx1 < 0 || idx2 < 0 || idx1 >= count || idx2 >= count) { return; } - ArrayList filters = MessagesController.getInstance(UserConfig.selectedAccount).dialogFilters; + ArrayList filters = MessagesController.getInstance(UserConfig.selectedAccount).getDialogFilters(); if (NekoConfig.hideAllTab.Bool()) { int defaultPosition = 0; for (int i = 0; i < filters.size(); i++) { @@ -1776,7 +1804,7 @@ public void moveElementToStart(int theIndex) { if (theIndex < 0 || theIndex >= count) { return; } - ArrayList filters = MessagesController.getInstance(UserConfig.selectedAccount).dialogFilters; + ArrayList filters = MessagesController.getInstance(UserConfig.selectedAccount).getDialogFilters(); int temp = positionToStableId.get(theIndex), temp2 = tabs.get(theIndex).id; for (int i = theIndex - 1; i >= 0; --i) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/FlickerLoadingView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/FlickerLoadingView.java index ab95d7b6ee..7d8b56903b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/FlickerLoadingView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/FlickerLoadingView.java @@ -44,6 +44,8 @@ public class FlickerLoadingView extends View { public static final int TOPIC_CELL_TYPE = 24; public static final int DIALOG_CACHE_CONTROL = 25; public static final int CHECKBOX_TYPE = 26; + public final static int STORIES_TYPE = 27; + public static final int SOTRY_VIEWS_USER_TYPE = 28; private int gradientWidth; private LinearGradient gradient; @@ -65,9 +67,9 @@ public class FlickerLoadingView extends View { private int paddingTop; private int paddingLeft; - private String colorKey1 = Theme.key_actionBarDefaultSubmenuBackground; - private String colorKey2 = Theme.key_listSelector; - private String colorKey3; + private int colorKey1 = Theme.key_actionBarDefaultSubmenuBackground; + private int colorKey2 = Theme.key_listSelector; + private int colorKey3; private int itemsCount = 1; private final Theme.ResourcesProvider resourcesProvider; @@ -104,7 +106,7 @@ public int getColumnsCount() { return 2; } - public void setColors(String key1, String key2, String key3) { + public void setColors(int key1, int key2, int key3) { colorKey1 = key1; colorKey2 = key2; colorKey3 = key3; @@ -157,10 +159,10 @@ protected void onDraw(Canvas canvas) { int h = paddingTop; if (useHeaderOffset) { h += AndroidUtilities.dp(32); - if (colorKey3 != null) { + if (colorKey3 >= 0) { headerPaint.setColor(getThemedColor(colorKey3)); } - canvas.drawRect(0,0, getMeasuredWidth(), AndroidUtilities.dp(32), colorKey3 != null ? headerPaint : paint); + canvas.drawRect(0,0, getMeasuredWidth(), AndroidUtilities.dp(32), colorKey3 >= 0 ? headerPaint : paint); } if (getViewType() == DIALOG_CELL_TYPE) { int k = 0; @@ -308,18 +310,19 @@ protected void onDraw(Canvas canvas) { break; } } - } else if (getViewType() == PHOTOS_TYPE) { + } else if (getViewType() == PHOTOS_TYPE || getViewType() == STORIES_TYPE) { int photoWidth = (getMeasuredWidth() - (AndroidUtilities.dp(2) * (getColumnsCount() - 1))) / getColumnsCount(); + int photoHeight = getViewType() == STORIES_TYPE ? (int) (photoWidth * 1.25f) : photoWidth; int k = 0; while (h < getMeasuredHeight() || isSingleCell) { for (int i = 0; i < getColumnsCount(); i++) { if (k == 0 && i < skipDrawItemsCount) { - continue; + continue; } int x = i * (photoWidth + AndroidUtilities.dp(2)); - canvas.drawRect(x, h, x + photoWidth, h + photoWidth, paint); + canvas.drawRect(x, h, x + photoWidth, h + photoHeight, paint); } - h += photoWidth + AndroidUtilities.dp(2); + h += photoHeight + AndroidUtilities.dp(2); k++; if (isSingleCell && k >= 2) { break; @@ -703,6 +706,32 @@ protected void onDraw(Canvas canvas) { checkRtl(rectF); canvas.drawRoundRect(rectF, AndroidUtilities.dp(4), AndroidUtilities.dp(4), paint); + h += getCellHeight(getMeasuredWidth()); + k++; + if (isSingleCell && k >= itemsCount) { + break; + } + } + } else if (getViewType() == SOTRY_VIEWS_USER_TYPE) { + int k = 0; + while (h <= getMeasuredHeight()) { + int r = AndroidUtilities.dp(24); + canvas.drawCircle(checkRtl(paddingLeft + AndroidUtilities.dp(10) + r), h + (AndroidUtilities.dp(58) >> 1), r, paint); + + rectF.set(paddingLeft + AndroidUtilities.dp(68), h + AndroidUtilities.dp(17), paddingLeft + AndroidUtilities.dp(260), h + AndroidUtilities.dp(25)); + checkRtl(rectF); + canvas.drawRoundRect(rectF, AndroidUtilities.dp(4), AndroidUtilities.dp(4), paint); + + rectF.set(paddingLeft + AndroidUtilities.dp(68), h + AndroidUtilities.dp(39), paddingLeft + AndroidUtilities.dp(140), h + AndroidUtilities.dp(47)); + checkRtl(rectF); + canvas.drawRoundRect(rectF, AndroidUtilities.dp(4), AndroidUtilities.dp(4), paint); + + if (showDate) { + rectF.set(getMeasuredWidth() - AndroidUtilities.dp(50), h + AndroidUtilities.dp(20), getMeasuredWidth() - AndroidUtilities.dp(12), h + AndroidUtilities.dp(28)); + checkRtl(rectF); + canvas.drawRoundRect(rectF, AndroidUtilities.dp(4), AndroidUtilities.dp(4), paint); + } + h += getCellHeight(getMeasuredWidth()); k++; if (isSingleCell && k >= itemsCount) { @@ -830,6 +859,8 @@ private int getCellHeight(int width) { return AndroidUtilities.dp(51); case CHECKBOX_TYPE: return AndroidUtilities.dp(50) + 1; + case SOTRY_VIEWS_USER_TYPE: + return AndroidUtilities.dp(58); } return 0; } @@ -860,9 +891,8 @@ public void setItemsCount(int i) { this.itemsCount = i; } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } public void setGlobalGradientView(FlickerLoadingView globalGradientView) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/FolderBottomSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/FolderBottomSheet.java new file mode 100644 index 0000000000..d91d72051d --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/FolderBottomSheet.java @@ -0,0 +1,1456 @@ +package org.telegram.ui.Components; + +import static org.telegram.messenger.AndroidUtilities.dp; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.DialogInterface; +import android.graphics.Canvas; +import android.graphics.LinearGradient; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.Shader; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.ShapeDrawable; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.util.Pair; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.animation.OvershootInterpolator; +import android.widget.FrameLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.core.view.ViewCompat; +import androidx.recyclerview.widget.RecyclerView; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.BotWebViewVibrationEffect; +import org.telegram.messenger.ChatObject; +import org.telegram.messenger.DialogObject; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.R; +import org.telegram.messenger.UserObject; +import org.telegram.messenger.Utilities; +import org.telegram.tgnet.TLObject; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.AlertDialog; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.INavigationLayout; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Cells.GroupCreateUserCell; +import org.telegram.ui.Cells.TextInfoPrivacyCell; +import org.telegram.ui.ChatActivity; +import org.telegram.ui.DialogsActivity; +import org.telegram.ui.FilterCreateActivity; +import org.telegram.ui.FiltersSetupActivity; + +import java.util.ArrayList; +import java.util.List; + +public class FolderBottomSheet extends BottomSheetWithRecyclerListView { + + private String slug; + private int filterId = -1; + private TLRPC.chatlist_ChatlistInvite invite; + private TLRPC.TL_chatlists_chatlistUpdates updates; + private boolean deleting; + + private String title = ""; + private String escapedTitle = ""; + private ArrayList peers; + private ArrayList alreadyJoined = new ArrayList<>(); + private ArrayList selectedPeers = new ArrayList<>(); + private ArrayList alreadyPeers; + + private FrameLayout bulletinContainer; + + private Button button; + private View buttonShadow; + private TitleCell titleCell; + + private int rowsCount; + + private int titleRow; + private int sectionRow; + private int headerRow; + private int usersStartRow; + private int usersEndRow; + private int usersSectionRow; + private int alreadyHeaderRow; + private int alreadyUsersStartRow; + private int alreadyUsersEndRow; + private int alreadySectionRow; + + private HeaderCell headerCell; + + public static void showForDeletion(final BaseFragment fragment, final int filterId, final Utilities.Callback whenDone) { + ArrayList myFilters = fragment.getMessagesController().dialogFilters; + MessagesController.DialogFilter f = null; + if (myFilters != null) { + for (int i = 0; i < myFilters.size(); ++i) { + if (myFilters.get(i).id == filterId) { + f = myFilters.get(i); + break; + } + } + } + final MessagesController.DialogFilter filter = f; + + Runnable showDeleteAlert = () -> { + TLRPC.TL_chatlists_getLeaveChatlistSuggestions req = new TLRPC.TL_chatlists_getLeaveChatlistSuggestions(); + req.chatlist = new TLRPC.TL_inputChatlistDialogFilter(); + req.chatlist.filter_id = filterId; + fragment.getConnectionsManager().sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + FolderBottomSheet sheet; + if (res instanceof TLRPC.Vector) { + ArrayList suggestions = new ArrayList<>(); + try { + for (int i = 0; i < ((TLRPC.Vector) res).objects.size(); ++i) { + TLRPC.Peer peer = (TLRPC.Peer) ((TLRPC.Vector) res).objects.get(i); + suggestions.add(DialogObject.getPeerDialogId(peer)); + } + } catch (Exception ignore) { + } + sheet = new FolderBottomSheet(fragment, filterId, suggestions); + } else { + sheet = new FolderBottomSheet(fragment, filterId, (List) null); + } + sheet.setOnDone(whenDone); + fragment.showDialog(sheet); + })); + }; + + if (filter != null && filter.isMyChatlist()) { + AlertDialog alertDialog = new AlertDialog.Builder(fragment.getContext()) + .setTitle(LocaleController.getString("FilterDelete", R.string.FilterDelete)) + .setMessage(LocaleController.getString("FilterDeleteAlertLinks", R.string.FilterDeleteAlertLinks)) + .setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), (d, w) -> { + if (whenDone != null) { + whenDone.run(false); + } + }) + .setPositiveButton(LocaleController.getString("Delete", R.string.Delete), (d, w) -> { + showDeleteAlert.run(); + }) + .create(); + fragment.showDialog(alertDialog); + TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); + if (button != null) { + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); + } + } else { + showDeleteAlert.run(); + } + } + + public FolderBottomSheet(BaseFragment fragment, int filterId, List select) { + super(fragment, false, false); + + this.filterId = filterId; + this.deleting = true; + + peers = new ArrayList<>(); + selectedPeers.clear(); + if (select != null) { + selectedPeers.addAll(select); + } + + ArrayList myFilters = fragment.getMessagesController().dialogFilters; + MessagesController.DialogFilter filter = null; + if (myFilters != null) { + for (int i = 0; i < myFilters.size(); ++i) { + if (myFilters.get(i).id == filterId) { + filter = myFilters.get(i); + break; + } + } + } + if (filter != null) { + title = filter.name; + + for (int i = 0; i < selectedPeers.size(); ++i) { + long did = selectedPeers.get(i); + TLRPC.Peer peer = fragment.getMessagesController().getPeer(did); + if (peer instanceof TLRPC.TL_peerChat || peer instanceof TLRPC.TL_peerChannel) { + peers.add(peer); + } + } + + for (int i = 0; i < filter.alwaysShow.size(); ++i) { + long did = filter.alwaysShow.get(i); + if (selectedPeers.contains(did)) { + continue; + } + TLRPC.Peer peer = fragment.getMessagesController().getPeer(did); + if (peer instanceof TLRPC.TL_peerChat || peer instanceof TLRPC.TL_peerChannel) { + TLRPC.Chat chat = fragment.getMessagesController().getChat(-did); + if (chat == null || !ChatObject.isNotInChat(chat)) { + peers.add(peer); + } + } + } + } + + init(); + } + + public FolderBottomSheet(BaseFragment fragment, int filterId, TLRPC.TL_chatlists_chatlistUpdates updates) { + super(fragment, false, false); + + this.filterId = filterId; + this.updates = updates; + + selectedPeers.clear(); + peers = updates.missing_peers; + ArrayList myFilters = fragment.getMessagesController().dialogFilters; + if (myFilters != null) { + for (int i = 0; i < myFilters.size(); ++i) { + if (myFilters.get(i).id == filterId) { + title = myFilters.get(i).name; + break; + } + } + } + + init(); + } + + public FolderBottomSheet(BaseFragment fragment, String slug, TLRPC.chatlist_ChatlistInvite invite) { + super(fragment, false, false); + + this.slug = slug; + this.invite = invite; + + selectedPeers.clear(); + if (invite instanceof TLRPC.TL_chatlists_chatlistInvite) { + title = ((TLRPC.TL_chatlists_chatlistInvite) invite).title; + peers = ((TLRPC.TL_chatlists_chatlistInvite) invite).peers; + } else if (invite instanceof TLRPC.TL_chatlists_chatlistInviteAlready) { + TLRPC.TL_chatlists_chatlistInviteAlready inv = (TLRPC.TL_chatlists_chatlistInviteAlready) invite; + peers = inv.missing_peers; + alreadyPeers = inv.already_peers; + this.filterId = inv.filter_id; + ArrayList myFilters = fragment.getMessagesController().dialogFilters; + if (myFilters != null) { + for (int i = 0; i < myFilters.size(); ++i) { + if (myFilters.get(i).id == filterId) { + title = myFilters.get(i).name; + break; + } + } + } + } + + init(); + } + + private void init() { + escapedTitle = title.replace('*', '✱'); + + if (peers != null) { + for (int i = 0; i < peers.size(); ++i) { + TLRPC.Peer peer = peers.get(i); + if (peer == null) { + continue; + } + boolean joined = false; + long did = 0; + if (peer instanceof TLRPC.TL_peerUser) { + did = peer.user_id; + } else if (peer instanceof TLRPC.TL_peerChat) { + did = -peer.chat_id; + joined = !ChatObject.isNotInChat(getBaseFragment().getMessagesController().getChat(-did)); + } else if (peer instanceof TLRPC.TL_peerChannel) { + did = -peer.channel_id; + joined = !ChatObject.isNotInChat(getBaseFragment().getMessagesController().getChat(-did)); + } + if (did != 0 && !deleting) { + if (joined) { + alreadyJoined.add(did); + } + selectedPeers.add(did); + } + } + } + + rowsCount = 0; + titleRow = rowsCount++; + if (peers != null && !peers.isEmpty()) { + sectionRow = rowsCount++; + headerRow = rowsCount++; + usersStartRow = rowsCount; + usersEndRow = (rowsCount += peers.size()); + } else { + sectionRow = -1; + headerRow = -1; + usersStartRow = -1; + usersEndRow = -1; + } + usersSectionRow = rowsCount++; + if (alreadyPeers != null && !alreadyPeers.isEmpty()) { + alreadyHeaderRow = rowsCount++; + alreadyUsersStartRow = rowsCount; + alreadyUsersEndRow = (rowsCount += alreadyPeers.size()); + alreadySectionRow = rowsCount++; + } else { + alreadyHeaderRow = -1; + alreadyUsersStartRow = -1; + alreadyUsersEndRow = -1; + alreadySectionRow = -1; + } + + button = new Button(getContext(), ""); + button.setOnClickListener(e -> onJoinButtonClicked()); + containerView.addView(button, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.BOTTOM | Gravity.FILL_HORIZONTAL, 16, 10, 16, 10)); + + buttonShadow = new View(getContext()); + buttonShadow.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundGray)); + containerView.addView(buttonShadow, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 1f / AndroidUtilities.density, Gravity.BOTTOM | Gravity.FILL_HORIZONTAL, 6, 0, 6, 68)); + recyclerListView.setPadding(dp(6), 0, dp(6), dp(button != null ? 68 : 0)); + + bulletinContainer = new FrameLayout(getContext()); + containerView.addView(bulletinContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 100, Gravity.BOTTOM | Gravity.FILL_HORIZONTAL, 6, 0, 6, 68)); + + fixNavigationBar(Theme.getColor(Theme.key_dialogBackground)); + + updateCount(false); + + actionBar.setTitle(getTitle()); + } + + private int reqId = -1; + + private void onJoinButtonClicked() { + if (button != null && button.isLoading()) { + return; + } + if (peers == null) { + dismiss(); + return; + } + if (peers.isEmpty() && !deleting) { + dismiss(); + return; + } + + if (selectedPeers.isEmpty() && invite instanceof TLRPC.TL_chatlists_chatlistInvite) { + AndroidUtilities.shakeViewSpring(button, shiftDp = -shiftDp); + BotWebViewVibrationEffect.APP_ERROR.vibrate(); + return; + } + + final ArrayList inputPeers = new ArrayList<>(); + for (int i = 0; i < peers.size(); ++i) { + long did = DialogObject.getPeerDialogId(peers.get(i)); + if (selectedPeers.contains(did)) { + inputPeers.add(getBaseFragment().getMessagesController().getInputPeer(did)); + } + } + + final Utilities.Callback after; + TLObject reqObject; + if (deleting) { + TLRPC.TL_chatlists_leaveChatlist req = new TLRPC.TL_chatlists_leaveChatlist(); + req.chatlist = new TLRPC.TL_inputChatlistDialogFilter(); + req.chatlist.filter_id = filterId; + req.peers.addAll(inputPeers); + reqObject = req; + } else if (updates != null) { + if (inputPeers.isEmpty()) { + TLRPC.TL_chatlists_hideChatlistUpdates req = new TLRPC.TL_chatlists_hideChatlistUpdates(); + req.chatlist = new TLRPC.TL_inputChatlistDialogFilter(); + req.chatlist.filter_id = filterId; + getBaseFragment().getConnectionsManager().sendRequest(req, null); + getBaseFragment().getMessagesController().invalidateChatlistFolderUpdate(filterId); + dismiss(); + return; + } + TLRPC.TL_chatlists_joinChatlistUpdates req = new TLRPC.TL_chatlists_joinChatlistUpdates(); + req.chatlist = new TLRPC.TL_inputChatlistDialogFilter(); + req.chatlist.filter_id = filterId; + req.peers.addAll(inputPeers); + reqObject = req; + } else { + if (invite instanceof TLRPC.TL_chatlists_chatlistInviteAlready && inputPeers.isEmpty()) { + dismiss(); + return; + } + TLRPC.TL_chatlists_joinChatlistInvite req = new TLRPC.TL_chatlists_joinChatlistInvite(); + req.slug = slug; + req.peers.addAll(inputPeers); + reqObject = req; + } + + final INavigationLayout parentLayout = getBaseFragment().getParentLayout(); + if (deleting) { + if (parentLayout != null) { + BaseFragment fragment = parentLayout.getLastFragment(); + UndoView undoView = null; + if (fragment instanceof ChatActivity) { + undoView = ((ChatActivity) fragment).getUndoView(); + } else if (fragment instanceof DialogsActivity) { + undoView = ((DialogsActivity) fragment).getUndoView(); + } else if (fragment instanceof FiltersSetupActivity) { + undoView = ((FiltersSetupActivity) fragment).getUndoView(); + } else if (fragment instanceof FilterCreateActivity) { + List stack = parentLayout.getFragmentStack(); + if (stack.size() >= 2 && stack.get(stack.size() - 2) instanceof FiltersSetupActivity) { + FiltersSetupActivity setupFragment = (FiltersSetupActivity) stack.get(stack.size() - 2); + fragment.finishFragment(); + undoView = setupFragment.getUndoView(); + } + } + if (undoView == null) { + button.setLoading(true); + reqId = getBaseFragment().getConnectionsManager().sendRequest(reqObject, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + reqId = -1; + BulletinFactory.of(fragment).createSimpleBulletin(R.raw.ic_delete, LocaleController.formatString("FolderLinkDeletedTitle", R.string.FolderLinkDeletedTitle, title), LocaleController.formatPluralString("FolderLinkDeletedSubtitle", inputPeers.size())).setDuration(Bulletin.DURATION_PROLONG).show(); + success = true; + dismiss(); + getBaseFragment().getMessagesController().invalidateChatlistFolderUpdate(filterId); + })); + return; + } + + ArrayList dids = new ArrayList<>(); + for (int i = 0; i < inputPeers.size(); ++i) { + dids.add(DialogObject.getPeerDialogId(inputPeers.get(i))); + } + + final Pair applyOrUndo = getBaseFragment().getMessagesController().removeFolderTemporarily(filterId, dids); + undoView.showWithAction(0, UndoView.ACTION_SHARED_FOLDER_DELETED, title, (Integer) inputPeers.size(), () -> { + reqId = getBaseFragment().getConnectionsManager().sendRequest(reqObject, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + reqId = -1; + applyOrUndo.first.run(); + })); + }, applyOrUndo.second); + + success = true; + dismiss(); + getBaseFragment().getMessagesController().invalidateChatlistFolderUpdate(filterId); + } + } else if (parentLayout != null) { + final Utilities.Callback bulletin = (fragment) -> { + if (updates != null || invite instanceof TLRPC.TL_chatlists_chatlistInviteAlready) { + BulletinFactory.of(fragment) + .createSimpleBulletin( + R.raw.folder_in, + AndroidUtilities.replaceTags(LocaleController.formatString("FolderLinkUpdatedTitle", R.string.FolderLinkUpdatedTitle, escapedTitle)), + inputPeers.size() <= 0 ? + LocaleController.formatPluralString("FolderLinkUpdatedSubtitle", alreadyJoined.size()) : + LocaleController.formatPluralString("FolderLinkUpdatedJoinedSubtitle", inputPeers.size()) + ) + .setDuration(Bulletin.DURATION_PROLONG) + .show(); + } else { + BulletinFactory.of(fragment) + .createSimpleBulletin( + R.raw.contact_check, + AndroidUtilities.replaceTags(LocaleController.formatString("FolderLinkAddedTitle", R.string.FolderLinkAddedTitle, escapedTitle)), + LocaleController.formatPluralString("FolderLinkAddedSubtitle", inputPeers.size()) + ) + .setDuration(Bulletin.DURATION_PROLONG) + .show(); + } + }; + if (updates != null) { + after = (fid) -> bulletin.run(parentLayout.getLastFragment()); + } else { + after = (fid) -> { + List fragments = parentLayout.getFragmentStack(); + boolean last = true; + BaseFragment lastFragment = null; + for (int i = fragments.size() - 1; i >= 0; --i) { + lastFragment = fragments.get(i); + if (lastFragment instanceof DialogsActivity) { + break; + } + + if (last) { + lastFragment.finishFragment(); + last = false; + } else { + lastFragment.removeSelfFromStack(); + } + } + final BaseFragment fragment = lastFragment; + if (lastFragment instanceof DialogsActivity) { + DialogsActivity dialogsActivity = (DialogsActivity) lastFragment; + dialogsActivity.closeSearching(); + AndroidUtilities.runOnUIThread(() -> { + dialogsActivity.scrollToFolder(fid); + AndroidUtilities.runOnUIThread(() -> bulletin.run(fragment), 200); + }, 80); + } else { + bulletin.run(fragment); + } + }; + } + + boolean hasNewChatsInArchived = false; + for (int i = 0; i < inputPeers.size(); ++i) { + TLRPC.InputPeer peer = inputPeers.get(i); + long did = DialogObject.getPeerDialogId(peer); + if (!alreadyJoined.contains(did)) { + hasNewChatsInArchived = true; + break; + } + } + + if (hasNewChatsInArchived) { + boolean[] folderCreated = new boolean[1]; + getBaseFragment().getMessagesController().ensureFolderDialogExists(1, folderCreated); + if (folderCreated[0]) { + getBaseFragment().getNotificationCenter().postNotificationName(NotificationCenter.dialogsNeedReload); + } + } + + button.setLoading(true); + reqId = getBaseFragment().getConnectionsManager().sendRequest(reqObject, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + reqId = -1; + if (FilterCreateActivity.processErrors(err, getBaseFragment(), BulletinFactory.of(getBaseFragment())) && res != null) { + int foundFilterId = -1; + if (res instanceof TLRPC.Updates) { + ArrayList updates = ((TLRPC.Updates) res).updates; + if (!updates.isEmpty()) { + for (int i = 0; i < updates.size(); ++i) { + if (updates.get(i) instanceof TLRPC.TL_updateDialogFilter) { + TLRPC.TL_updateDialogFilter upd = (TLRPC.TL_updateDialogFilter) updates.get(i); + foundFilterId = upd.id; + break; + } + } + } else if (((TLRPC.Updates) res).update instanceof TLRPC.TL_updateDialogFilter) { + TLRPC.TL_updateDialogFilter upd = (TLRPC.TL_updateDialogFilter) ((TLRPC.Updates) res).update; + foundFilterId = upd.id; + } + } + final int newFilterId = foundFilterId; + + if (invite instanceof TLRPC.TL_chatlists_chatlistInvite) { + getBaseFragment().getMessagesController().loadRemoteFilters(true, success -> { + FolderBottomSheet.this.success = success; + dismiss(); + after.run(newFilterId); + }); + } else { + if (updates != null) { + getBaseFragment().getMessagesController().checkChatlistFolderUpdate(filterId, true); + } + success = true; + dismiss(); + after.run(newFilterId); + } + } else { + button.setLoading(false); + } + })); + } + } + + private boolean success; + private Utilities.Callback onDone; + public void setOnDone(Utilities.Callback listener) { + this.onDone = listener; + } + + @Override + public void dismiss() { + super.dismiss(); + if (reqId >= 0) { + getBaseFragment().getConnectionsManager().cancelRequest(reqId, true); + } + if (onDone != null) { + onDone.run(success); + onDone = null; + } + } + + private int shiftDp = -5; + private long lastClickedDialogId, lastClicked; + + @Override + public void onViewCreated(FrameLayout containerView) { + super.onViewCreated(containerView); + recyclerListView.setOverScrollMode(View.OVER_SCROLL_NEVER); + recyclerListView.setPadding(dp(6), 0, dp(6), dp(button != null ? 68 : 0)); + recyclerListView.setOnItemClickListener((view, position) -> { + if (view instanceof GroupCreateUserCell) { + int peersPosition = position - 1 - usersStartRow; + if (peersPosition < 0 || peersPosition >= peers.size()) { + return; + } + long did = DialogObject.getPeerDialogId(peers.get(peersPosition)); + if (selectedPeers.contains(did)) { + if (alreadyJoined.contains(did)) { + AndroidUtilities.shakeViewSpring(view, shiftDp = -shiftDp); + BotWebViewVibrationEffect.APP_ERROR.vibrate(); + ArrayList array = new ArrayList<>(); + String text; + if (did >= 0) { + array.add(getBaseFragment().getMessagesController().getUser(did)); + text = "beep boop."; + } else { + TLRPC.Chat chat = getBaseFragment().getMessagesController().getChat(-did); + if (ChatObject.isChannelAndNotMegaGroup(chat)) { + text = LocaleController.getString("FolderLinkAlreadySubscribed", R.string.FolderLinkAlreadySubscribed); + } else { + text = LocaleController.getString("FolderLinkAlreadyJoined", R.string.FolderLinkAlreadyJoined); + } + array.add(chat); + } + if (lastClickedDialogId != did || System.currentTimeMillis() - lastClicked > Bulletin.DURATION_SHORT) { + lastClickedDialogId = did; + lastClicked = System.currentTimeMillis(); + BulletinFactory.of(bulletinContainer, null).createChatsBulletin(array, text, null).setDuration(Bulletin.DURATION_SHORT).show(); + } + return; + } + + selectedPeers.remove(did); + ((GroupCreateUserCell) view).setChecked(false, true); + } else { + selectedPeers.add(did); + ((GroupCreateUserCell) view).setChecked(true, true); + } + updateCount(true); + updateHeaderCell(true); + announceSelection(false); + } + }); + } + + public void updateCount(boolean animated) { + int count = selectedPeers.size(); + if (button != null) { + if (deleting) { + button.setText(count > 0 ? LocaleController.getString("FolderLinkButtonRemoveChats", R.string.FolderLinkButtonRemoveChats) : LocaleController.getString("FolderLinkButtonRemove", R.string.FolderLinkButtonRemove), animated); + } else if (peers == null || peers.isEmpty()) { + button.setText(LocaleController.getString("OK", R.string.OK), animated); + } else if (invite instanceof TLRPC.TL_chatlists_chatlistInvite) { + button.setText(LocaleController.formatString("FolderLinkButtonAdd", R.string.FolderLinkButtonAdd, title), animated); + } else { + button.setText(count > 0 ? LocaleController.formatPluralString("FolderLinkButtonJoinPlural", count) : LocaleController.getString("FolderLinkButtonNone", R.string.FolderLinkButtonNone), animated); + } + button.setCount(count, animated); + if (invite instanceof TLRPC.TL_chatlists_chatlistInvite) { + button.setEnabled(!selectedPeers.isEmpty()); + } + } + if (titleCell != null) { + titleCell.setSelectedCount(count, animated); + } + } + + private static class Button extends FrameLayout { + Paint paint; + AnimatedTextView.AnimatedTextDrawable text; + AnimatedTextView.AnimatedTextDrawable countText; + float countAlpha; + AnimatedFloat countAlphaAnimated = new AnimatedFloat(350, CubicBezierInterpolator.EASE_OUT_QUINT); + private View rippleView; + private ShapeDrawable background; + + public Button(Context context, String string) { + super(context); + + rippleView = new View(context); + rippleView.setBackground(Theme.AdaptiveRipple.rect(Theme.getColor(Theme.key_featuredStickers_addButton), 8)); + addView(rippleView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + + setBackground(background = (ShapeDrawable) Theme.createRoundRectDrawable(dp(8), Theme.getColor(Theme.key_featuredStickers_addButton))); + + paint = new Paint(Paint.ANTI_ALIAS_FLAG); + paint.setColor(Theme.getColor(Theme.key_featuredStickers_buttonText)); + + text = new AnimatedTextView.AnimatedTextDrawable(true, true, false); + text.setAnimationProperties(.3f, 0, 250, CubicBezierInterpolator.EASE_OUT_QUINT); + text.setCallback(this); + text.setTextSize(dp(14)); + text.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + text.setTextColor(Theme.getColor(Theme.key_featuredStickers_buttonText)); + text.setText(string); + text.setGravity(Gravity.CENTER_HORIZONTAL); + + countText = new AnimatedTextView.AnimatedTextDrawable(false, false, true); + countText.setAnimationProperties(.3f, 0, 250, CubicBezierInterpolator.EASE_OUT_QUINT); + countText.setCallback(this); + countText.setTextSize(dp(12)); + countText.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + countText.setTextColor(Theme.getColor(Theme.key_featuredStickers_addButton)); + countText.setText(""); + countText.setGravity(Gravity.CENTER_HORIZONTAL); + + setWillNotDraw(false); + } + + public void setText(String newText, boolean animated) { + if (animated) { + text.cancelAnimation(); + } + text.setText(newText, animated); + invalidate(); + } + + private float loadingT = 0; + private boolean loading; + private ValueAnimator loadingAnimator; + public void setLoading(boolean loading) { + if (this.loading != loading) { + if (loadingAnimator != null) { + loadingAnimator.cancel(); + loadingAnimator = null; + } + + loadingAnimator = ValueAnimator.ofFloat(loadingT, (this.loading = loading) ? 1 : 0); + loadingAnimator.addUpdateListener(anm -> { + loadingT = (float) anm.getAnimatedValue(); + invalidate(); + }); + loadingAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + loadingT = loading ? 1 : 0; + invalidate(); + } + }); + loadingAnimator.setDuration(320); + loadingAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + loadingAnimator.start(); + } + } + + public boolean isLoading() { + return loading; + } + + private float countScale = 1; + private ValueAnimator countAnimator; + private void animateCount() { + if (countAnimator != null) { + countAnimator.cancel(); + countAnimator = null; + } + + countAnimator = ValueAnimator.ofFloat(0, 1); + countAnimator.addUpdateListener(anm -> { + countScale = Math.max(1, (float) anm.getAnimatedValue()); + invalidate(); + }); + countAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + countScale = 1; + invalidate(); + } + }); + countAnimator.setInterpolator(new OvershootInterpolator(2.0f)); + countAnimator.setDuration(200); + countAnimator.start(); + } + + private int lastCount; + + public void setCount(int count, boolean animated) { + if (animated) { + countText.cancelAnimation(); + } + if (animated && count != lastCount && count > 0 && lastCount > 0) { + animateCount(); + } + lastCount = count; + countAlpha = count != 0 ? 1f : 0f; + countText.setText("" + count, animated); + invalidate(); + } + + private float enabledT = 1; + private boolean enabled = true; + private ValueAnimator enabledAnimator; + + @Override + public void setEnabled(boolean enabled) { + if (this.enabled != enabled) { + if (enabledAnimator != null) { + enabledAnimator.cancel(); + enabledAnimator = null; + } + + enabledAnimator = ValueAnimator.ofFloat(enabledT, (this.enabled = enabled) ? 1 : 0); + enabledAnimator.addUpdateListener(anm -> { + enabledT = (float) anm.getAnimatedValue(); + invalidate(); +// background.getPaint().setColor(ColorUtils.blendARGB( +// Theme.getColor(Theme.key_checkboxDisabled), +// Theme.getColor(Theme.key_featuredStickers_addButton), +// enabledT +// )); + }); + enabledAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { +// if (enabled) { +// rippleView.setBackground(Theme.AdaptiveRipple.rect(Theme.getColor(Theme.key_featuredStickers_addButton), 8)); +// } else { +// rippleView.setBackground(Theme.createRadSelectorDrawable(0x0fffffff, 8, 8)); +// } + } + }); + enabledAnimator.start(); + } + } + + @Override + protected boolean verifyDrawable(@NonNull Drawable who) { + return text == who || countText == who || super.verifyDrawable(who); + } + + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + return false; + } + + private CircularProgressDrawable loadingDrawable; + + @Override + protected void onDraw(Canvas canvas) { + rippleView.draw(canvas); + + if (loadingT > 0) { + if (loadingDrawable == null) { + loadingDrawable = new CircularProgressDrawable(text.getTextColor()); + } + int y = (int) ((1f - loadingT) * dp(24)); + loadingDrawable.setBounds(0, y, getWidth(), y + getHeight()); + loadingDrawable.setAlpha((int) (0xFF * loadingT)); + loadingDrawable.draw(canvas); + invalidate(); + } + + if (loadingT < 1) { + boolean restore = false; + if (loadingT != 0) { + canvas.save(); + canvas.translate(0, (int) (loadingT * dp(-24))); + canvas.scale(1, 1f - .4f * loadingT); + restore = true; + } + float textWidth = text.getCurrentWidth(); + float countAlpha = countAlphaAnimated.set(this.countAlpha); + + float width = textWidth + (dp(5.66f + 5 + 5) + countText.getCurrentWidth()) * countAlpha; + AndroidUtilities.rectTmp2.set( + (int) ((getMeasuredWidth() - width - getWidth()) / 2f), + (int) ((getMeasuredHeight() - text.getHeight()) / 2f - dp(1)), + (int) ((getMeasuredWidth() - width + getWidth()) / 2f + textWidth), + (int) ((getMeasuredHeight() + text.getHeight()) / 2f - dp(1)) + ); + text.setAlpha((int) (0xFF * (1f - loadingT) * AndroidUtilities.lerp(.5f, 1f, enabledT))); + text.setBounds(AndroidUtilities.rectTmp2); + text.draw(canvas); + + AndroidUtilities.rectTmp2.set( + (int) ((getMeasuredWidth() - width) / 2f + textWidth + dp(5f)), + (int) ((getMeasuredHeight() - dp(18)) / 2f), + (int) ((getMeasuredWidth() - width) / 2f + textWidth + dp(5f + 4 + 4) + Math.max(dp(9), countText.getCurrentWidth())), + (int) ((getMeasuredHeight() + dp(18)) / 2f) + ); + AndroidUtilities.rectTmp.set(AndroidUtilities.rectTmp2); + + if (countScale != 1) { + canvas.save(); + canvas.scale(countScale, countScale, AndroidUtilities.rectTmp2.centerX(), AndroidUtilities.rectTmp2.centerY()); + } + paint.setAlpha((int) (0xFF * (1f - loadingT) * countAlpha * countAlpha)); + canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(10), dp(10), paint); + + AndroidUtilities.rectTmp2.offset(-dp(.3f), -dp(.4f)); + countText.setAlpha((int) (0xFF * (1f - loadingT) * countAlpha)); + countText.setBounds(AndroidUtilities.rectTmp2); + countText.draw(canvas); + if (countScale != 1) { + canvas.restore(); + } + if (restore) { + canvas.restore(); + } + } + } + + @Override + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(info); + info.setClassName("android.widget.Button"); + info.setContentDescription(text.getText() + (lastCount > 0 ? ", " + LocaleController.formatPluralString("Chats", lastCount) : "")); + } + } + + @Override + protected CharSequence getTitle() { + if (deleting) { + return LocaleController.getString("FolderLinkTitleRemove", R.string.FolderLinkTitleRemove); + } else if (invite instanceof TLRPC.TL_chatlists_chatlistInvite) { + return LocaleController.getString("FolderLinkTitleAdd", R.string.FolderLinkTitleAdd); + } else if (peers == null || peers.isEmpty()) { + return LocaleController.getString("FolderLinkTitleAlready", R.string.FolderLinkTitleAlready); + } else { + return LocaleController.getString("FolderLinkTitleAddChats", R.string.FolderLinkTitleAddChats); + } + } + + @Override + protected RecyclerListView.SelectionAdapter createAdapter() { + return new RecyclerListView.SelectionAdapter() { + + private static final int + VIEW_TYPE_TITLE = 0, + VIEW_TYPE_HINT = 1, + VIEW_TYPE_USER = 2, + VIEW_TYPE_HEADER = 3; + + @Override + public boolean isEnabled(RecyclerView.ViewHolder holder) { + return holder.getItemViewType() == VIEW_TYPE_USER && holder.getAdapterPosition() >= usersStartRow && holder.getAdapterPosition() <= usersEndRow; + } + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = null; + if (viewType == VIEW_TYPE_TITLE) { + view = titleCell = new TitleCell(getContext(), invite instanceof TLRPC.TL_chatlists_chatlistInviteAlready || updates != null, escapedTitle); + } else if (viewType == VIEW_TYPE_HINT) { + view = new TextInfoPrivacyCell(getContext()); + view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundGray)); + } else if (viewType == VIEW_TYPE_USER) { + GroupCreateUserCell userCell = new GroupCreateUserCell(getContext(), 1, 0, false); + userCell.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + view = userCell; + } else if (viewType == VIEW_TYPE_HEADER) { + view = new HeaderCell(getContext()); + view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + } + return new RecyclerListView.Holder(view); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + final int viewType = holder.getItemViewType(); + if (viewType == VIEW_TYPE_USER) { + GroupCreateUserCell userCell = (GroupCreateUserCell) holder.itemView; + TLRPC.Peer peer = null; + if (position >= usersStartRow && position <= usersEndRow) { + peer = peers != null ? peers.get(position - usersStartRow) : null; + } else if (position >= alreadyUsersStartRow && position <= alreadyUsersEndRow) { + peer = alreadyPeers != null ? alreadyPeers.get(position - alreadyUsersStartRow) : null; + } + + TLObject object = null; + String name = null, status = null; + long did = 0; + if (peer != null) { + if (peer instanceof TLRPC.TL_peerUser) { + did = peer.user_id; + TLRPC.User user = getBaseFragment().getMessagesController().getUser(peer.user_id); + object = user; + name = UserObject.getUserName(user); + if (user != null && user.bot) { + status = LocaleController.getString("FilterInviteBot", R.string.FilterInviteBot); + } else { + status = LocaleController.getString("FilterInviteUser", R.string.FilterInviteUser); + } + } else if (peer instanceof TLRPC.TL_peerChat) { + did = -peer.chat_id; + object = getBaseFragment().getMessagesController().getChat(peer.chat_id); + } else if (peer instanceof TLRPC.TL_peerChannel) { + did = -peer.channel_id; + object = getBaseFragment().getMessagesController().getChat(peer.channel_id); + } + } + if (object instanceof TLRPC.Chat) { + TLRPC.Chat chat = (TLRPC.Chat) object; + name = chat.title; + if (chat.participants_count != 0) { + if (ChatObject.isChannelAndNotMegaGroup(chat)) { + status = LocaleController.formatPluralStringComma("Subscribers", chat.participants_count); + } else { + status = LocaleController.formatPluralStringComma("Members", chat.participants_count); + } + } else { + if (ChatObject.isChannelAndNotMegaGroup(chat)) { + status = LocaleController.getString("ChannelPublic", R.string.ChannelPublic); + } else { + status = LocaleController.getString("MegaPublic", R.string.MegaPublic); + } + } + } + userCell.setTag(did); + userCell.getCheckBox().getCheckBoxBase().setAlpha(alreadyJoined.contains(did) ? .5f : 1f); + userCell.setChecked(selectedPeers.contains(did), false); + userCell.setObject(object, name, status); + } else if (viewType == VIEW_TYPE_HEADER) { + HeaderCell headerCell = (HeaderCell) holder.itemView; + if (position == alreadyHeaderRow) { + headerCell.setText(LocaleController.getString("FolderLinkHeaderAlready", R.string.FolderLinkHeaderAlready), false); + headerCell.setAction("", null); + } else { + FolderBottomSheet.this.headerCell = headerCell; + updateHeaderCell(false); + } + } else if (viewType == VIEW_TYPE_HINT) { + TextInfoPrivacyCell hintCell = (TextInfoPrivacyCell) holder.itemView; + hintCell.setForeground(Theme.getThemedDrawableByKey(getContext(), R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + if (position == alreadySectionRow || position == sectionRow || peers == null || peers.isEmpty()) { + hintCell.setFixedSize(12); + hintCell.setText(""); + } else { + hintCell.setFixedSize(0); + if (deleting) { + hintCell.setText(LocaleController.getString("FolderLinkHintRemove", R.string.FolderLinkHintRemove)); + } else { + hintCell.setText(LocaleController.getString("FolderLinkHint", R.string.FolderLinkHint)); + } + } + } else if (viewType == VIEW_TYPE_TITLE) { + titleCell = (TitleCell) holder.itemView; + updateCount(false); + } + } + + @Override + public int getItemViewType(int position) { + if (position == titleRow) { + return VIEW_TYPE_TITLE; + } else if (position == sectionRow || position == usersSectionRow || position == alreadySectionRow) { + return VIEW_TYPE_HINT; + } else if (position == headerRow || position == alreadyHeaderRow) { + return VIEW_TYPE_HEADER; + } + return VIEW_TYPE_USER; + } + + @Override + public int getItemCount() { + return rowsCount; + } + }; + } + + private String getFilterName(MessagesController.DialogFilter filter) { + if (filter == null) + return null; + if (filter.isDefault()) + return LocaleController.getString("FilterAllChats", R.string.FilterAllChats); + return filter.name; + } + + public static class HeaderCell extends FrameLayout { + public AnimatedTextView textView; + public AnimatedTextView actionTextView; + public HeaderCell(Context context) { + super(context); + + textView = new AnimatedTextView(context, true, true, false); + textView.setTextSize(dp(15)); + textView.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlueHeader)); + textView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); + addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 20, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.BOTTOM, 21, 15, 21, 2)); + + actionTextView = new AnimatedTextView(context, true, true, true); + actionTextView.setAnimationProperties(.45f, 0, 250, CubicBezierInterpolator.EASE_OUT_QUINT); + actionTextView.setTextSize(dp(15)); + actionTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlueHeader)); + actionTextView.setGravity(LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT); + addView(actionTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 20, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.BOTTOM, 21, 15, 21, 2)); + + ViewCompat.setAccessibilityHeading(this, true); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), heightMeasureSpec); + } + + public void setText(CharSequence text, boolean animated) { + if (animated) { + textView.cancelAnimation(); + } + textView.setText(text, animated && !LocaleController.isRTL); + } + + public void setAction(CharSequence text, Runnable onClick) { + actionTextView.setText(text, !LocaleController.isRTL); + actionTextView.setOnClickListener(e -> { + if (onClick != null) { + onClick.run(); + } + }); + } + + @Override + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(info); + info.setClassName("android.widget.TextView"); + info.setText(textView.getText()); + } + } + + private class TitleCell extends FrameLayout { + + private boolean already; + private String title; + + private FoldersPreview preview; + private TextView titleTextView, subtitleTextView; + + public TitleCell(Context context, boolean already, String title) { + super(context); + this.already = already; + this.title = title; + + String left2Folder = null, + left1Folder = LocaleController.getString("FolderLinkPreviewLeft"), + right1Folder = LocaleController.getString("FolderLinkPreviewRight"), + right2Folder = null; +// try { +// ArrayList filters = MessagesController.getInstance(UserConfig.selectedAccount).dialogFilters; +// if (filterId >= 0) { +// for (int i = 0; i < filters.size(); ++i) { +// if (filters.get(i).id == filterId) { +// if (i - 2 >= 0) { +// left2Folder = getFilterName(filters.get(i - 2)); +// } +// if (i - 1 >= 0) { +// left1Folder = getFilterName(filters.get(i - 1)); +// } +// if (i + 1 < filters.size()) { +// right1Folder = getFilterName(filters.get(i + 1)); +// } +// if (i + 2 < filters.size()) { +// right2Folder = getFilterName(filters.get(i + 2)); +// } +// break; +// } +// } +// } else if (filters.size() > 1) { +// left2Folder = getFilterName(filters.get(filters.size() - 2)); +// left1Folder = getFilterName(filters.get(filters.size() - 1)); +// } +// } catch (Exception ignore) {} + preview = new FoldersPreview(context, left2Folder, left1Folder, title, right1Folder, right2Folder); + addView(preview, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 44, Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 17.33f, 0, 0)); + + titleTextView = new TextView(context); + titleTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); + titleTextView.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + titleTextView.setText(getTitle()); + titleTextView.setGravity(Gravity.CENTER); + addView(titleTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP, 32, 78.3f, 32, 0)); + + subtitleTextView = new TextView(context); + subtitleTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + subtitleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + subtitleTextView.setLines(2); + subtitleTextView.setGravity(Gravity.CENTER); + subtitleTextView.setLineSpacing(0, 1.15f); + addView(subtitleTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP, 32, 113, 32, 0)); + + setSelectedCount(0, false); + } + + public void setSelectedCount(int count, boolean animated) { + if (deleting) { + subtitleTextView.setText(AndroidUtilities.replaceTags(LocaleController.formatString("FolderLinkSubtitleRemove", R.string.FolderLinkSubtitleRemove, title))); + } else if (already) { + preview.setCount(peers != null ? peers.size() : 0, false); + if (peers == null || peers.isEmpty()) { + subtitleTextView.setText(AndroidUtilities.replaceTags(LocaleController.formatString("FolderLinkSubtitleAlready", R.string.FolderLinkSubtitleAlready, title))); + } else { + subtitleTextView.setText(AndroidUtilities.replaceTags(LocaleController.formatPluralString("FolderLinkSubtitleChats", peers != null ? peers.size() : 0, title))); + } + } else { + if (peers == null || peers.isEmpty()) { + subtitleTextView.setText(AndroidUtilities.replaceTags(LocaleController.formatString("FolderLinkSubtitleAlready", R.string.FolderLinkSubtitleAlready, title))); + } else { + subtitleTextView.setText(AndroidUtilities.replaceTags(LocaleController.formatString("FolderLinkSubtitle", R.string.FolderLinkSubtitle, title))); + } + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure( + MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(dp(172), MeasureSpec.EXACTLY) + ); + } + + private class FoldersPreview extends View { + + TextPaint paint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + TextPaint selectedTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + Paint selectedPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + Path path = new Path(); + float[] radii = new float[8]; + + StaticLayout leftFolder2; + float leftFolder2Width; + StaticLayout leftFolder; + float leftFolderWidth; + StaticLayout middleFolder; + float middleFolderWidth; + StaticLayout rightFolder; + float rightFolderWidth; + StaticLayout rightFolder2; + float rightFolder2Width; + + LinearGradient leftGradient, rightGradient; + Paint leftPaint = new Paint(Paint.ANTI_ALIAS_FLAG), rightPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + Matrix leftMatrix = new Matrix(), rightMatrix = new Matrix(); + + AnimatedTextView.AnimatedTextDrawable countText; + + public FoldersPreview( + Context context, + CharSequence left2FolderText, + CharSequence left1FolderText, + CharSequence middleFolderText, + CharSequence right1FolderText, + CharSequence right2FolderText + ) { + super(context); + + paint.setColor(Theme.multAlpha(Theme.getColor(Theme.key_profile_tabText), .8f)); + paint.setTextSize(dp(15.33f)); + paint.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + + selectedTextPaint.setColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlueText2)); + selectedTextPaint.setTextSize(dp(17)); + selectedTextPaint.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + + selectedPaint.setColor(Theme.getColor(Theme.key_featuredStickers_unread)); + + countText = new AnimatedTextView.AnimatedTextDrawable(false, true, true); + countText.setAnimationProperties(.3f, 0, 250, CubicBezierInterpolator.EASE_OUT_QUINT); + countText.setCallback(this); + countText.setTextSize(dp(11.66f)); + countText.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + countText.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + countText.setGravity(Gravity.CENTER_HORIZONTAL); + + if (left2FolderText != null) { + leftFolder2 = makeLayout(left2FolderText, false); + leftFolder2Width = leftFolder2.getLineWidth(0); + } + if (left1FolderText != null) { + leftFolder = makeLayout(left1FolderText, false); + leftFolderWidth = leftFolder.getLineWidth(0); + } + middleFolder = makeLayout(middleFolderText, true); + middleFolderWidth = middleFolder.getLineWidth(0); + if (right1FolderText != null) { + rightFolder = makeLayout(right1FolderText, false); + rightFolderWidth = rightFolder.getLineWidth(0); + } + if (right2FolderText != null) { + rightFolder2 = makeLayout(right2FolderText, false); + rightFolder2Width = rightFolder2.getLineWidth(0); + } + + radii[0] = radii[1] = radii[2] = radii[3] = dp(3); + radii[4] = radii[5] = radii[6] = radii[7] = dp(1); + + leftGradient = new LinearGradient(0, 0, dp(80), 0, new int[] {0xffffffff, 0x00ffffff}, new float[] {0, 1}, Shader.TileMode.CLAMP); + leftPaint.setShader(leftGradient); + leftPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); + rightGradient = new LinearGradient(0, 0, dp(80), 0, new int[] {0x00ffffff, 0xffffffff}, new float[] {0, 1f}, Shader.TileMode.CLAMP); + rightPaint.setShader(rightGradient); + rightPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); + } + + private StaticLayout makeLayout(CharSequence text, boolean selected) { + if (text == null || "ALL_CHATS".equals(text.toString())) { + text = LocaleController.getString("FilterAllChats", R.string.FilterAllChats); + } + return new StaticLayout(text, selected ? selectedTextPaint : paint, AndroidUtilities.displaySize.x, Layout.Alignment.ALIGN_NORMAL, 1f, 0, false); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + canvas.saveLayerAlpha(0, 0, getMeasuredWidth(), getMeasuredHeight(), 0xFF, Canvas.ALL_SAVE_FLAG); + + float cx = getMeasuredWidth() / 2f; + float cy = getMeasuredHeight() / 2f; + + canvas.save(); + float width = middleFolderWidth + (isCountEmpty() ? 0 : dp(4.66f + 5.33f + 5.33f) + countText.getCurrentWidth()); + float cleft = cx - width / 2f; + canvas.translate(cleft, cy - middleFolder.getHeight() / 2f); + middleFolder.draw(canvas); + canvas.restore(); + + if (!isCountEmpty()) { + AndroidUtilities.rectTmp2.set( + (int) (cleft + middleFolderWidth + dp(4.66f)), + (int) (cy - dp(9)), + (int) (cleft + middleFolderWidth + dp(4.66f + 5.33f + 5.33f) + countText.getCurrentWidth()), + (int) (cy + dp(9)) + ); + AndroidUtilities.rectTmp.set(AndroidUtilities.rectTmp2); + canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(9), dp(9), selectedPaint); + + AndroidUtilities.rectTmp2.offset(-dp(.33f), -dp(.66f)); + countText.setBounds(AndroidUtilities.rectTmp2); + countText.draw(canvas); + } + + final float gap = dp(30); + + float x = cleft - gap - leftFolderWidth; + float minx = x; + + if (leftFolder2 != null && leftFolderWidth < dp(64)) { + minx -= gap + leftFolder2Width; + canvas.save(); + canvas.translate(minx, cy - leftFolder2.getHeight() / 2f + dp(1)); + leftFolder2.draw(canvas); + canvas.restore(); + } + + if (leftFolder != null) { + canvas.save(); + canvas.translate(x, cy - leftFolder.getHeight() / 2f + dp(1)); + leftFolder.draw(canvas); + canvas.restore(); + } + x = cleft + width; + + if (rightFolder != null) { + canvas.save(); + canvas.translate(x + gap, cy - rightFolder.getHeight() / 2f + dp(1)); + rightFolder.draw(canvas); + canvas.restore(); + x += gap + rightFolderWidth; + } + + if (rightFolder2 != null && rightFolderWidth < dp(64)) { + canvas.save(); + canvas.translate(x + gap, cy - rightFolder2.getHeight() / 2f + dp(1)); + rightFolder2.draw(canvas); + canvas.restore(); + x += gap + rightFolder2Width; + } + + float y = cy + middleFolder.getHeight() / 2f + dp(12); + canvas.drawRect(0, y, getMeasuredWidth(), y + 1, paint); + + path.rewind(); + AndroidUtilities.rectTmp.set(cx - width / 2f - dp(4), y - dp(4), cx + width / 2f + dp(4), y); + path.addRoundRect(AndroidUtilities.rectTmp, radii, Path.Direction.CW); + canvas.drawPath(path, selectedPaint); + + canvas.save(); + float left = Math.max(dp(8), minx); + leftMatrix.reset(); + leftMatrix.postTranslate(Math.min(cleft, left + dp(8)), 0); + leftGradient.setLocalMatrix(leftMatrix); + + float right = Math.min(getMeasuredWidth() - dp(8), x); + rightMatrix.reset(); + rightMatrix.postTranslate(Math.max(cx + width / 2f, right - dp(80 + 8)), 0); + rightGradient.setLocalMatrix(rightMatrix); + + canvas.drawRect(0, 0, cx, getMeasuredHeight(), leftPaint); + canvas.drawRect(cx, 0, getMeasuredWidth(), getMeasuredHeight(), rightPaint); + canvas.restore(); + + canvas.restore(); + } + + @Override + protected boolean verifyDrawable(@NonNull Drawable who) { + return who == countText || super.verifyDrawable(who); + } + + private boolean isCountEmpty() { + return countText.getText() == null || countText.getText().length() == 0; + } + + public void setCount(int count, boolean animated) { + if (animated) { + countText.cancelAnimation(); + } + countText.setText(count > 0 ? "+" + count : "", animated); + invalidate(); + } + } + } + + private void updateHeaderCell(boolean animated) { + if (headerCell == null) { + return; + } + + if (deleting) { + headerCell.setText(LocaleController.formatPluralString("FolderLinkHeaderChatsQuit", peers.size()), false); + } else { + headerCell.setText(LocaleController.formatPluralString("FolderLinkHeaderChatsJoin", peers.size()), false); + } + if (peers != null && (peers.size() - alreadyJoined.size()) > 1) { + final boolean deselect = selectedPeers.size() >= peers.size() - alreadyJoined.size(); + headerCell.setAction( + deselect ? LocaleController.getString(R.string.DeselectAll) : LocaleController.getString(R.string.SelectAll), () -> deselectAll(headerCell, deselect) + ); + } else { + headerCell.setAction("", null); + } + } + + private void announceSelection(boolean buttonSelect) { + AndroidUtilities.makeAccessibilityAnnouncement( + LocaleController.formatPluralString("FilterInviteHeaderChats", selectedPeers.size()) + (buttonSelect && headerCell != null ? ", " + headerCell.actionTextView.getText() : "") + ); + } + + private void deselectAll(HeaderCell headerCell, boolean deselect) { + selectedPeers.clear(); + selectedPeers.addAll(alreadyJoined); + if (!deselect) { + for (int i = 0; i < peers.size(); ++i) { + long id = DialogObject.getPeerDialogId(peers.get(i)); + if (!selectedPeers.contains(id)) { + selectedPeers.add(id); + } + } + } + updateCount(true); + headerCell.setAction(deselect ? LocaleController.getString(R.string.SelectAll) : LocaleController.getString(R.string.DeselectAll), () -> deselectAll(headerCell, !deselect)); + announceSelection(true); + for (int i = 0; i < recyclerListView.getChildCount(); ++i) { + View child = recyclerListView.getChildAt(i); + if (child instanceof GroupCreateUserCell) { + Object tag = child.getTag(); + if (tag instanceof Long) { + ((GroupCreateUserCell) child).setChecked(selectedPeers.contains((long) tag),true); + } + } + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ForegroundColorSpanThemable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ForegroundColorSpanThemable.java index 66b5690eda..ecf9c52e45 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ForegroundColorSpanThemable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ForegroundColorSpanThemable.java @@ -11,14 +11,14 @@ public class ForegroundColorSpanThemable extends CharacterStyle implements UpdateAppearance { private int color; - private String colorKey; + private int colorKey; private final Theme.ResourcesProvider resourcesProvider; - public ForegroundColorSpanThemable(String colorKey) { + public ForegroundColorSpanThemable(int colorKey) { this(colorKey, null); } - public ForegroundColorSpanThemable(String colorKey, Theme.ResourcesProvider resourcesProvider) { + public ForegroundColorSpanThemable(int colorKey, Theme.ResourcesProvider resourcesProvider) { this.colorKey = colorKey; this.resourcesProvider = resourcesProvider; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Forum/ForumUtilities.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Forum/ForumUtilities.java index 894383b31b..444dc970da 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Forum/ForumUtilities.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Forum/ForumUtilities.java @@ -61,7 +61,7 @@ public static void setTopicIcon(BackupImageView backupImageView, TLRPC.TL_forumT backupImageView.setImageDrawable(null); if (backupImageView.animatedEmojiDrawable == null || forumTopic.icon_emoji_id != backupImageView.animatedEmojiDrawable.getDocumentId()) { AnimatedEmojiDrawable drawable = new AnimatedEmojiDrawable(largeIcon ? AnimatedEmojiDrawable.CACHE_TYPE_FORUM_TOPIC_LARGE : AnimatedEmojiDrawable.CACHE_TYPE_FORUM_TOPIC, UserConfig.selectedAccount, forumTopic.icon_emoji_id); - drawable.setColorFilter(actionBar ? new PorterDuffColorFilter(Theme.getColor(Theme.key_actionBarDefaultTitle), PorterDuff.Mode.SRC_IN) : Theme.chat_animatedEmojiTextColorFilter); + drawable.setColorFilter(actionBar ? new PorterDuffColorFilter(Theme.getColor(Theme.key_actionBarDefaultTitle), PorterDuff.Mode.SRC_IN) : Theme.getAnimatedEmojiColorFilter(resourcesProvider)); backupImageView.setAnimatedEmojiDrawable(drawable); } } else { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Forum/MessageTopicButton.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Forum/MessageTopicButton.java index 1f1b70cba4..b18ae19132 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Forum/MessageTopicButton.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Forum/MessageTopicButton.java @@ -490,9 +490,8 @@ public void resetClick() { } } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } private Paint getThemedPaint(String key) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ForwardingPreviewView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ForwardingPreviewView.java index 5bd4e387e9..3d6d2e442c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ForwardingPreviewView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ForwardingPreviewView.java @@ -29,6 +29,7 @@ import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.BuildVars; +import org.telegram.messenger.ChatMessageSharedResources; import org.telegram.messenger.ChatObject; import org.telegram.messenger.ContactsController; import org.telegram.messenger.FileLog; @@ -91,6 +92,7 @@ public interface ResourcesDelegate extends Theme.ResourcesProvider { ArrayList actionItems = new ArrayList<>(); Rect rect = new Rect(); + ChatMessageSharedResources sharedResources; private boolean firstLayout = true; ValueAnimator offsetsAnimator; @@ -116,6 +118,7 @@ public void run() { @SuppressLint("ClickableViewAccessibility") public ForwardingPreviewView(@NonNull Context context, ForwardingMessagesParams params, TLRPC.User user, TLRPC.Chat chat, int currentAccount, ResourcesDelegate resourcesProvider) { super(context); + sharedResources = new ChatMessageSharedResources(context); this.currentAccount = currentAccount; currentUser = user; currentChat = chat; @@ -976,10 +979,11 @@ public boolean isShowing() { } private class Adapter extends RecyclerView.Adapter { + @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - ChatMessageCell chatMessageCell = new ChatMessageCell(parent.getContext(), false, resourcesProvider); + ChatMessageCell chatMessageCell = new ChatMessageCell(parent.getContext(), false, sharedResources, resourcesProvider); return new RecyclerListView.Holder(chatMessageCell); } @@ -1028,8 +1032,7 @@ private MessageObject.GroupedMessages getValidGroupedMessage(MessageObject messa return groupedMessages; } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/FragmentContextView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/FragmentContextView.java index a49d5f8e43..493e755067 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/FragmentContextView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/FragmentContextView.java @@ -35,6 +35,7 @@ import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; +import android.util.Log; import android.util.TypedValue; import android.view.Gravity; import android.view.HapticFeedbackConstants; @@ -57,6 +58,7 @@ import org.telegram.messenger.AccountInstance; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.ChatObject; import org.telegram.messenger.ContactsController; import org.telegram.messenger.DialogObject; @@ -208,7 +210,11 @@ public void run() { AndroidUtilities.runOnUIThread(checkLocationRunnable, 1000); } }; - private int animationIndex = -1; + private AnimationNotificationsLocker notificationsLocker = new AnimationNotificationsLocker(); + private AnimationNotificationsLocker notificationsLocker2 = new AnimationNotificationsLocker(new int[]{ + NotificationCenter.messagesDidLoad + }); + private boolean checkCallAfterAnimation; private boolean checkPlayerAfterAnimation; @@ -415,7 +421,7 @@ protected TextView createTextView() { addView(subtitleTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 36, Gravity.LEFT | Gravity.TOP, 35, 10, 36, 0)); joinButtonFlicker = new CellFlickerDrawable(); - joinButtonFlicker.setProgress(2f); + joinButtonFlicker.setProgress(1); joinButtonFlicker.repeatEnabled = false; joinButton = new TextView(context) { @Override @@ -425,9 +431,6 @@ public void draw(Canvas canvas) { final int halfOutlineWidth = AndroidUtilities.dp(1); AndroidUtilities.rectTmp.set(halfOutlineWidth, halfOutlineWidth, getWidth() - halfOutlineWidth, getHeight() - halfOutlineWidth); joinButtonFlicker.draw(canvas, AndroidUtilities.rectTmp, AndroidUtilities.dp(16), this); - if (joinButtonFlicker.getProgress() < 1f && !joinButtonFlicker.repeatEnabled) { - invalidate(); - } } @Override @@ -473,6 +476,9 @@ private void updateJoinButtonWidth(int width) { joinButton.setPadding(AndroidUtilities.dp(14), 0, AndroidUtilities.dp(14), 0); addView(joinButton, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 28, Gravity.TOP | Gravity.RIGHT, 0, 10, 14, 0)); joinButton.setOnClickListener(v -> FragmentContextView.this.callOnClick()); + if (flickOnAttach) { + startJoinFlickerAnimation(); + } silentButton = new FrameLayout(context); silentButtonImage = new ImageView(context); @@ -672,7 +678,7 @@ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { builder.show(); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(getThemedColor(Theme.key_dialogTextRed)); + button.setTextColor(getThemedColor(Theme.key_text_RedBold)); } } else { MediaController.getInstance().cleanupPlayer(true, true); @@ -938,7 +944,7 @@ private void openSharingLocation(final LocationController.SharingLocationInfo in LocationActivity locationActivity = new LocationActivity(2); locationActivity.setMessageObject(info.messageObject); final long dialog_id = info.messageObject.getDialogId(); - locationActivity.setDelegate((location, live, notify, scheduleDate) -> SendMessagesHelper.getInstance(info.messageObject.currentAccount).sendMessage(location, dialog_id, null, null, null, null, notify, scheduleDate)); + locationActivity.setDelegate((location, live, notify, scheduleDate) -> SendMessagesHelper.getInstance(info.messageObject.currentAccount).sendMessage(SendMessagesHelper.SendMessageParams.of(location, dialog_id, null, null, null, null, notify, scheduleDate))); launchActivity.presentFragment(locationActivity); } @@ -1245,7 +1251,7 @@ protected void onDetachedFromWindow() { scheduleRunnableScheduled = false; } visible = false; - NotificationCenter.getInstance(account).onAnimationFinish(animationIndex); + notificationsLocker.unlock(); topPadding = 0; if (isLocation) { NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.liveLocationsChanged); @@ -1686,7 +1692,7 @@ private void checkPlayer(boolean create) { animatorSet.cancel(); animatorSet = null; } - animationIndex = NotificationCenter.getInstance(account).setAnimationInProgress(animationIndex, null); + notificationsLocker.lock(); animatorSet = new AnimatorSet(); animatorSet.playTogether(ObjectAnimator.ofFloat(this, "topPadding", 0)); animatorSet.setDuration(200); @@ -1696,7 +1702,7 @@ private void checkPlayer(boolean create) { animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - NotificationCenter.getInstance(account).onAnimationFinish(animationIndex); + notificationsLocker.unlock(); if (animatorSet != null && animatorSet.equals(animation)) { setVisibility(GONE); if (delegate != null) { @@ -1743,7 +1749,7 @@ public void onAnimationEnd(Animator animation) { animatorSet.cancel(); animatorSet = null; } - animationIndex = NotificationCenter.getInstance(account).setAnimationInProgress(animationIndex, null); + notificationsLocker.lock(); animatorSet = new AnimatorSet(); if (additionalContextView != null && additionalContextView.getVisibility() == VISIBLE) { ((LayoutParams) getLayoutParams()).topMargin = -AndroidUtilities.dp(getStyleHeight() + additionalContextView.getStyleHeight()); @@ -1758,7 +1764,7 @@ public void onAnimationEnd(Animator animation) { animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - NotificationCenter.getInstance(account).onAnimationFinish(animationIndex); + notificationsLocker.unlock(); if (animatorSet != null && animatorSet.equals(animation)) { if (delegate != null) { delegate.onAnimation(false, true); @@ -1874,7 +1880,7 @@ public void checkImport(boolean create) { animatorSet = null; } final int currentAccount = account; - animationIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(animationIndex, null); + notificationsLocker.lock(); animatorSet = new AnimatorSet(); animatorSet.playTogether(ObjectAnimator.ofFloat(this, "topPadding", 0)); animatorSet.setDuration(220); @@ -1882,7 +1888,7 @@ public void checkImport(boolean create) { animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - NotificationCenter.getInstance(currentAccount).onAnimationFinish(animationIndex); + notificationsLocker.unlock(); if (animatorSet != null && animatorSet.equals(animation)) { setVisibility(GONE); animatorSet = null; @@ -1925,7 +1931,7 @@ public void onAnimationEnd(Animator animation) { animatorSet.cancel(); animatorSet = null; } - animationIndex = NotificationCenter.getInstance(account).setAnimationInProgress(animationIndex, null); + notificationsLocker.lock(); animatorSet = new AnimatorSet(); if (additionalContextView != null && additionalContextView.getVisibility() == VISIBLE) { ((LayoutParams) getLayoutParams()).topMargin = -AndroidUtilities.dp(getStyleHeight() + additionalContextView.getStyleHeight()); @@ -1940,7 +1946,7 @@ public void onAnimationEnd(Animator animation) { animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - NotificationCenter.getInstance(account).onAnimationFinish(animationIndex); + notificationsLocker.unlock(); if (animatorSet != null && animatorSet.equals(animation)) { if (delegate != null) { delegate.onAnimation(false, true); @@ -2021,7 +2027,7 @@ public void checkCall(boolean create) { animatorSet = null; } final int currentAccount = account; - animationIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(animationIndex, null); + notificationsLocker.lock(); animatorSet = new AnimatorSet(); animatorSet.playTogether(ObjectAnimator.ofFloat(this, "topPadding", 0)); animatorSet.setDuration(220); @@ -2029,7 +2035,7 @@ public void checkCall(boolean create) { animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - NotificationCenter.getInstance(currentAccount).onAnimationFinish(animationIndex); + notificationsLocker.unlock(); if (animatorSet != null && animatorSet.equals(animation)) { setVisibility(GONE); animatorSet = null; @@ -2076,7 +2082,7 @@ public void onAnimationEnd(Animator animation) { animatorSet = null; } final int currentAccount = account; - animationIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(animationIndex, null); + notificationsLocker.lock(); animatorSet = new AnimatorSet(); animatorSet.playTogether(ObjectAnimator.ofFloat(this, "topPadding", 0)); animatorSet.setDuration(220); @@ -2084,7 +2090,7 @@ public void onAnimationEnd(Animator animation) { animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - NotificationCenter.getInstance(currentAccount).onAnimationFinish(animationIndex); + notificationsLocker.unlock(); if (animatorSet != null && animatorSet.equals(animation)) { visible = false; animatorSet = null; @@ -2171,14 +2177,14 @@ public void onAnimationEnd(Animator animation) { ((LayoutParams) getLayoutParams()).topMargin = -AndroidUtilities.dp(getStyleHeight()); } final int currentAccount = account; - animationIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(animationIndex, new int[]{NotificationCenter.messagesDidLoad}); + notificationsLocker2.lock(); animatorSet.playTogether(ObjectAnimator.ofFloat(this, "topPadding", AndroidUtilities.dp2(getStyleHeight()))); animatorSet.setDuration(220); animatorSet.setInterpolator(CubicBezierInterpolator.DEFAULT); animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - NotificationCenter.getInstance(currentAccount).onAnimationFinish(animationIndex); + notificationsLocker2.unlock(); if (animatorSet != null && animatorSet.equals(animation)) { animatorSet = null; } @@ -2208,12 +2214,16 @@ public void onAnimationEnd(Animator animation) { } } + private boolean flickOnAttach; private void startJoinFlickerAnimation() { - if (joinButtonFlicker != null && joinButtonFlicker.getProgress() > 1) { + if (joinButtonFlicker != null && joinButtonFlicker.getProgress() >= 1) { + flickOnAttach = false; AndroidUtilities.runOnUIThread(() -> { joinButtonFlicker.setProgress(0); joinButton.invalidate(); }, 150); + } else { + flickOnAttach = true; } } @@ -2441,8 +2451,7 @@ private int getTitleTextColor() { return getThemedColor(Theme.key_inappPlayerTitle); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/FragmentContextViewWavesDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/FragmentContextViewWavesDrawable.java index 42c355f185..dae4d14df3 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/FragmentContextViewWavesDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/FragmentContextViewWavesDrawable.java @@ -293,13 +293,13 @@ public WeavingState(int state) { createGradients(); } - String greenKey1 = Theme.key_voipgroup_topPanelGreen1; - String greenKey2 = Theme.key_voipgroup_topPanelGreen2; - String blueKey1 = Theme.key_voipgroup_topPanelBlue1; - String blueKey2 = Theme.key_voipgroup_topPanelBlue2; - String mutedByAdmin = Theme.key_voipgroup_mutedByAdminGradient; - String mutedByAdmin2 = Theme.key_voipgroup_mutedByAdminGradient2; - String mutedByAdmin3 = Theme.key_voipgroup_mutedByAdminGradient3; + int greenKey1 = Theme.key_voipgroup_topPanelGreen1; + int greenKey2 = Theme.key_voipgroup_topPanelGreen2; + int blueKey1 = Theme.key_voipgroup_topPanelBlue1; + int blueKey2 = Theme.key_voipgroup_topPanelBlue2; + int mutedByAdmin = Theme.key_voipgroup_mutedByAdminGradient; + int mutedByAdmin2 = Theme.key_voipgroup_mutedByAdminGradient2; + int mutedByAdmin3 = Theme.key_voipgroup_mutedByAdminGradient3; private void createGradients() { if (currentState == MUTE_BUTTON_STATE_UNMUTE) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/GestureDetectorFixDoubleTap.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/GestureDetectorFixDoubleTap.java index dac566b381..ddf95e6f8f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/GestureDetectorFixDoubleTap.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/GestureDetectorFixDoubleTap.java @@ -256,7 +256,7 @@ public boolean onTouchEvent(MotionEvent ev) { break; case MotionEvent.ACTION_DOWN: - if (mDoubleTapListener != null && mListener.hasDoubleTap()) { + if (mDoubleTapListener != null && mListener.hasDoubleTap(ev)) { boolean hadTapMessage = mHandler.hasMessages(TAP); if (hadTapMessage) mHandler.removeMessages(TAP); if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null) @@ -510,7 +510,7 @@ public void setOnDoubleTapListener(OnDoubleTapListener listener) { } public static class OnGestureListener extends GestureDetector.SimpleOnGestureListener { - public boolean hasDoubleTap() { + public boolean hasDoubleTap(MotionEvent e) { return false; } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/GigagroupConvertAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/GigagroupConvertAlert.java index a3eed7f473..d100da5523 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/GigagroupConvertAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/GigagroupConvertAlert.java @@ -40,7 +40,7 @@ public BottomSheetCell(Context context) { super(context); background = new View(context); - background.setBackground(Theme.AdaptiveRipple.filledRect(Theme.key_featuredStickers_addButton, 4)); + background.setBackground(Theme.AdaptiveRipple.filledRectByKey(Theme.key_featuredStickers_addButton, 4)); addView(background, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, 0, 16, 16, 16, 16)); textView = new TextView(context); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/GradientTools.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/GradientTools.java index e3ed8cbfdc..b1077983c9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/GradientTools.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/GradientTools.java @@ -15,7 +15,9 @@ public class GradientTools { - Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + public boolean isDiagonal; + public boolean isRotate; + public Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); int color1; int color2; int color3; @@ -52,7 +54,11 @@ public void setColors(int color1, int color2, int color3, int color4) { paint.setShader(shader = null); paint.setColor(color1); } else if (color3 == 0) { - paint.setShader(shader = new LinearGradient(0, 0, 0, INTERNAL_HEIGHT, new int[]{color1, color2}, null, Shader.TileMode.CLAMP)); + if (isDiagonal && isRotate) { + paint.setShader(shader = new LinearGradient(0, 0, INTERNAL_HEIGHT, INTERNAL_HEIGHT, new int[]{color1, color2}, null, Shader.TileMode.CLAMP)); + } else { + paint.setShader(shader = new LinearGradient(isDiagonal ? INTERNAL_HEIGHT : 0, 0, 0, INTERNAL_HEIGHT, new int[]{color1, color2}, null, Shader.TileMode.CLAMP)); + } } else { if (gradientBitmap == null) { gradientBitmap = Bitmap.createBitmap(INTERNAL_WIDTH, INTERNAL_HEIGHT, Bitmap.Config.ARGB_8888); @@ -71,7 +77,7 @@ public void setBounds(RectF bounds) { updateBounds(); } - private void updateBounds() { + protected void updateBounds() { if (shader == null) { return; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupCreateCheckBox.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupCreateCheckBox.java index bd9a6c58dd..e20d4c692f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupCreateCheckBox.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupCreateCheckBox.java @@ -15,9 +15,10 @@ import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; -import androidx.annotation.Keep; import android.view.View; +import androidx.annotation.Keep; + import org.telegram.messenger.AndroidUtilities; import org.telegram.ui.ActionBar.Theme; @@ -42,9 +43,9 @@ public class GroupCreateCheckBox extends View { private int innerRadDiff; private float checkScale = 1.0f; - private String backgroundKey = Theme.key_checkboxCheck; - private String checkKey = Theme.key_checkboxCheck; - private String innerKey = Theme.key_checkbox; + private int backgroundKey = Theme.key_checkboxCheck; + private int checkKey = Theme.key_checkboxCheck; + private int innerKey = Theme.key_checkbox; private final static float progressBounceDiff = 0.2f; @@ -74,7 +75,7 @@ public GroupCreateCheckBox(Context context) { updateColors(); } - public void setColorKeysOverrides(String check, String inner, String back) { + public void setColorKeysOverrides(int check, int inner, int back) { checkKey = check; innerKey = inner; backgroundKey = back; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupCreateSpan.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupCreateSpan.java index 49133e36b2..c804e142c4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupCreateSpan.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupCreateSpan.java @@ -56,6 +56,8 @@ public class GroupCreateSpan extends View { private boolean deleting; private long lastUpdateTime; private int[] colors = new int[8]; + private Theme.ResourcesProvider resourcesProvider; + private boolean small; public GroupCreateSpan(Context context, Object object) { this(context, object, null); @@ -66,11 +68,21 @@ public GroupCreateSpan(Context context, ContactsController.Contact contact) { } public GroupCreateSpan(Context context, Object object, ContactsController.Contact contact) { + this(context, object, contact, null); + } + + public GroupCreateSpan(Context context, Object object, ContactsController.Contact contact, Theme.ResourcesProvider resourcesProvider) { + this(context, object, contact, false, resourcesProvider); + } + + public GroupCreateSpan(Context context, Object object, ContactsController.Contact contact, boolean small, Theme.ResourcesProvider resourcesProvider) { super(context); + this.resourcesProvider = resourcesProvider; + this.small = small; currentContact = contact; deleteDrawable = getResources().getDrawable(R.drawable.delete); - textPaint.setTextSize(AndroidUtilities.dp(14)); + textPaint.setTextSize(AndroidUtilities.dp(small ? 13 : 14)); String firstName; @@ -87,43 +99,43 @@ public GroupCreateSpan(Context context, Object object, ContactsController.Contac switch (str) { case "contacts": avatarDrawable.setAvatarType(AvatarDrawable.AVATAR_TYPE_FILTER_CONTACTS); - uid = Integer.MIN_VALUE; + uid = Long.MIN_VALUE; firstName = LocaleController.getString("FilterContacts", R.string.FilterContacts); break; case "non_contacts": avatarDrawable.setAvatarType(AvatarDrawable.AVATAR_TYPE_FILTER_NON_CONTACTS); - uid = Integer.MIN_VALUE + 1; + uid = Long.MIN_VALUE + 1; firstName = LocaleController.getString("FilterNonContacts", R.string.FilterNonContacts); break; case "groups": avatarDrawable.setAvatarType(AvatarDrawable.AVATAR_TYPE_FILTER_GROUPS); - uid = Integer.MIN_VALUE + 2; + uid = Long.MIN_VALUE + 2; firstName = LocaleController.getString("FilterGroups", R.string.FilterGroups); break; case "channels": avatarDrawable.setAvatarType(AvatarDrawable.AVATAR_TYPE_FILTER_CHANNELS); - uid = Integer.MIN_VALUE + 3; + uid = Long.MIN_VALUE + 3; firstName = LocaleController.getString("FilterChannels", R.string.FilterChannels); break; case "bots": avatarDrawable.setAvatarType(AvatarDrawable.AVATAR_TYPE_FILTER_BOTS); - uid = Integer.MIN_VALUE + 4; + uid = Long.MIN_VALUE + 4; firstName = LocaleController.getString("FilterBots", R.string.FilterBots); break; case "muted": avatarDrawable.setAvatarType(AvatarDrawable.AVATAR_TYPE_FILTER_MUTED); - uid = Integer.MIN_VALUE + 5; + uid = Long.MIN_VALUE + 5; firstName = LocaleController.getString("FilterMuted", R.string.FilterMuted); break; case "read": avatarDrawable.setAvatarType(AvatarDrawable.AVATAR_TYPE_FILTER_READ); - uid = Integer.MIN_VALUE + 6; + uid = Long.MIN_VALUE + 6; firstName = LocaleController.getString("FilterRead", R.string.FilterRead); break; case "archived": default: avatarDrawable.setAvatarType(AvatarDrawable.AVATAR_TYPE_FILTER_ARCHIVED); - uid = Integer.MIN_VALUE + 7; + uid = Long.MIN_VALUE + 7; firstName = LocaleController.getString("FilterArchived", R.string.FilterArchived); break; } @@ -145,6 +157,10 @@ public GroupCreateSpan(Context context, Object object, ContactsController.Contac } else { avatarDrawable.setInfo(user); firstName = UserObject.getFirstName(user); + int index; + if ((index = firstName.indexOf(' ')) >= 0) { + firstName = firstName.substring(0, index); + } imageLocation = ImageLocation.getForUserOrChat(user, ImageLocation.TYPE_SMALL); imageParent = user; } @@ -171,13 +187,13 @@ public GroupCreateSpan(Context context, Object object, ContactsController.Contac imageReceiver = new ImageReceiver(); imageReceiver.setRoundRadius(AndroidUtilities.dp(16)); imageReceiver.setParentView(this); - imageReceiver.setImageCoords(0, 0, AndroidUtilities.dp(32), AndroidUtilities.dp(32)); + imageReceiver.setImageCoords(0, 0, AndroidUtilities.dp(small ? 28 : 32), AndroidUtilities.dp(small ? 28 : 32)); int maxNameWidth; if (AndroidUtilities.isTablet()) { - maxNameWidth = AndroidUtilities.dp(530 - 32 - 18 - 57 * 2) / 2; + maxNameWidth = AndroidUtilities.dp(530 - (small ? 28 : 32) - 18 - 57 * 2) / 2; } else { - maxNameWidth = (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) - AndroidUtilities.dp(32 + 18 + 57 * 2)) / 2; + maxNameWidth = (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) - AndroidUtilities.dp((small ? 28 : 32) + 18 + 57 * 2)) / 2; } firstName = firstName.replace('\n', ' '); @@ -197,8 +213,8 @@ public GroupCreateSpan(Context context, Object object, ContactsController.Contac public void updateColors() { int color = avatarDrawable.getColor(); - int back = Theme.getColor(Theme.key_groupcreate_spanBackground); - int delete = Theme.getColor(Theme.key_groupcreate_spanDelete); + int back = Theme.getColor(Theme.key_groupcreate_spanBackground, resourcesProvider); + int delete = Theme.getColor(Theme.key_groupcreate_spanDelete, resourcesProvider); colors[0] = Color.red(back); colors[1] = Color.red(color); colors[2] = Color.green(back); @@ -247,7 +263,7 @@ public ContactsController.Contact getContact() { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - setMeasuredDimension(AndroidUtilities.dp(32 + 25) + textWidth, AndroidUtilities.dp(32)); + setMeasuredDimension(AndroidUtilities.dp((small ? 28 - 8 : 32) + 25) + textWidth, AndroidUtilities.dp(small ? 28 : 32)); } @Override @@ -272,9 +288,9 @@ protected void onDraw(Canvas canvas) { invalidate(); } canvas.save(); - rect.set(0, 0, getMeasuredWidth(), AndroidUtilities.dp(32)); + rect.set(0, 0, getMeasuredWidth(), AndroidUtilities.dp(small ? 28 : 32)); backPaint.setColor(Color.argb(colors[6] + (int) ((colors[7] - colors[6]) * progress), colors[0] + (int) ((colors[1] - colors[0]) * progress), colors[2] + (int) ((colors[3] - colors[2]) * progress), colors[4] + (int) ((colors[5] - colors[4]) * progress))); - canvas.drawRoundRect(rect, AndroidUtilities.dp(16), AndroidUtilities.dp(16), backPaint); + canvas.drawRoundRect(rect, AndroidUtilities.dp(small ? 14 : 16), AndroidUtilities.dp(small ? 14 : 16), backPaint); if (progress != 1f) { imageReceiver.draw(canvas); } @@ -283,17 +299,17 @@ protected void onDraw(Canvas canvas) { float alpha = Color.alpha(color) / 255.0f; backPaint.setColor(color); backPaint.setAlpha((int) (255 * progress * alpha)); - canvas.drawCircle(AndroidUtilities.dp(16), AndroidUtilities.dp(16), AndroidUtilities.dp(16), backPaint); + canvas.drawCircle(AndroidUtilities.dp(small ? 14 : 16), AndroidUtilities.dp(small ? 14 : 16), AndroidUtilities.dp(small ? 14 : 16), backPaint); canvas.save(); canvas.rotate(45 * (1.0f - progress), AndroidUtilities.dp(16), AndroidUtilities.dp(16)); - deleteDrawable.setBounds(AndroidUtilities.dp(11), AndroidUtilities.dp(11), AndroidUtilities.dp(21), AndroidUtilities.dp(21)); + deleteDrawable.setBounds(AndroidUtilities.dp(small ? 9 : 11), AndroidUtilities.dp(small ? 9 : 11), AndroidUtilities.dp(small ? 19 : 21), AndroidUtilities.dp(small ? 19 : 21)); deleteDrawable.setAlpha((int) (255 * progress)); deleteDrawable.draw(canvas); canvas.restore(); } - canvas.translate(textX + AndroidUtilities.dp(32 + 9), AndroidUtilities.dp(8)); - int text = Theme.getColor(Theme.key_groupcreate_spanText); - int textSelected = Theme.getColor(Theme.key_avatar_text); + canvas.translate(textX + AndroidUtilities.dp((small ? 26 : 32) + 9), AndroidUtilities.dp(small ? 6 : 8)); + int text = Theme.getColor(Theme.key_groupcreate_spanText, resourcesProvider); + int textSelected = Theme.getColor(Theme.key_avatar_text, resourcesProvider); textPaint.setColor(ColorUtils.blendARGB(text, textSelected, progress)); nameLayout.draw(canvas); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupedPhotosListView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupedPhotosListView.java index b4dbe499f5..42d35f81f8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupedPhotosListView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupedPhotosListView.java @@ -345,6 +345,14 @@ public void onAnimationEnd(Animator animation) { } } + public int getCount() { + return currentPhotos.size(); + } + + public int getIndex() { + return currentImage; + } + public void setMoveProgress(float progress) { if (scrolling || animateToItem >= 0) { return; @@ -373,6 +381,7 @@ private ImageReceiver getFreeReceiver() { ImageReceiver receiver; if (unusedReceivers.isEmpty()) { receiver = new ImageReceiver(this); + receiver.setAllowLoadingOnAttachedOnly(false); } else { receiver = unusedReceivers.get(0); unusedReceivers.remove(0); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/HideViewAfterAnimation.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/HideViewAfterAnimation.java index 030ddd1017..7956ec6964 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/HideViewAfterAnimation.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/HideViewAfterAnimation.java @@ -7,14 +7,22 @@ public class HideViewAfterAnimation extends AnimatorListenerAdapter { private final View view; + private final boolean goneOnHide; + public HideViewAfterAnimation(View view) { this.view = view; + this.goneOnHide = true; + } + + public HideViewAfterAnimation(View view, boolean goneOnHide) { + this.view = view; + this.goneOnHide = goneOnHide; } @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); - view.setVisibility(View.GONE); + view.setVisibility(goneOnHide ? View.GONE : View.INVISIBLE); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/HintView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/HintView.java index 8b2c6fe202..14596e9ed9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/HintView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/HintView.java @@ -1,10 +1,17 @@ package org.telegram.ui.Components; +import static org.telegram.messenger.AndroidUtilities.dp; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.CornerPathEffect; +import android.graphics.Paint; +import android.graphics.Path; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.drawable.Drawable; @@ -15,6 +22,10 @@ import android.widget.ImageView; import android.widget.TextView; +import androidx.core.graphics.ColorUtils; + +import com.google.android.exoplayer2.DefaultLivePlaybackSpeedControl; + import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ImageReceiver; import org.telegram.messenger.LocaleController; @@ -32,6 +43,7 @@ public class HintView extends FrameLayout { public static final int TYPE_SEARCH_AS_LIST = 3; public static final int TYPE_COMMON = 4; public static final int TYPE_POLL_VOTE = 5; + public static final int TYPE_DEFAULT = 6; public TextView textView; private ImageView imageView; @@ -50,6 +62,12 @@ public class HintView extends FrameLayout { private int bottomOffset; private long showingDuration = 2000; private final Theme.ResourcesProvider resourcesProvider; + private boolean useScale; + + VisibilityListener visibleListener; + private boolean hasCloseButton; + private boolean drawPath; + private int backgroundColor; public HintView(Context context, int type) { this(context, type, false, null); @@ -109,6 +127,18 @@ public HintView(Context context, int type, boolean topArrow, Theme.ResourcesProv addView(arrowImageView, LayoutHelper.createFrame(14, 6, Gravity.LEFT | (topArrow ? Gravity.TOP : Gravity.BOTTOM), 0, 0, 0, 0)); } + public void createCloseButton() { + textView.setPadding(AndroidUtilities.dp(12), AndroidUtilities.dp(7), AndroidUtilities.dp(36), AndroidUtilities.dp(8)); + + hasCloseButton = true; + imageView = new ImageView(getContext()); + imageView.setImageResource(R.drawable.msg_mini_close_tooltip); + imageView.setScaleType(ImageView.ScaleType.CENTER); + imageView.setColorFilter(new PorterDuffColorFilter(ColorUtils.setAlphaComponent(getThemedColor(Theme.key_chat_gifSaveHintText), 125), PorterDuff.Mode.MULTIPLY)); + imageView.setOnClickListener(v -> hide(true)); + addView(imageView, LayoutHelper.createFrame(34, 34, Gravity.RIGHT | Gravity.CENTER_VERTICAL, 0, isTopArrow ? 3 : 0, 0, isTopArrow ? 0 : 3)); + } + public void setBackgroundColor(int background, int text) { textView.setTextColor(text); arrowImageView.setColorFilter(new PorterDuffColorFilter(background, PorterDuff.Mode.MULTIPLY)); @@ -262,6 +292,9 @@ public boolean showForMessageCell(ChatMessageCell cell, Object object, int x, in setTag(1); setVisibility(VISIBLE); + if (visibleListener != null) { + visibleListener.onVisible(true); + } if (animated) { animatorSet = new AnimatorSet(); animatorSet.playTogether( @@ -271,7 +304,9 @@ public boolean showForMessageCell(ChatMessageCell cell, Object object, int x, in @Override public void onAnimationEnd(Animator animation) { animatorSet = null; - AndroidUtilities.runOnUIThread(hideRunnable = () -> hide(), currentType == 0 ? 10000 : 2000); + if (!hasCloseButton) { + AndroidUtilities.runOnUIThread(hideRunnable = () -> hide(), currentType == 0 ? 10000 : 2000); + } } }); animatorSet.setDuration(300); @@ -304,19 +339,37 @@ public boolean showForView(View view, boolean animated) { setTag(1); setVisibility(VISIBLE); + if (visibleListener != null) { + visibleListener.onVisible(true); + } if (animated) { animatorSet = new AnimatorSet(); - animatorSet.playTogether( - ObjectAnimator.ofFloat(this, View.ALPHA, 0.0f, 1.0f) - ); + if (useScale) { + setPivotX(arrowImageView.getX() + arrowImageView.getMeasuredWidth() / 2f); + setPivotY(arrowImageView.getY() + arrowImageView.getMeasuredHeight() / 2f); + animatorSet.playTogether( + ObjectAnimator.ofFloat(this, View.ALPHA, 0.0f, 1.0f), + ObjectAnimator.ofFloat(this, View.SCALE_Y, 0.5f, 1.0f), + ObjectAnimator.ofFloat(this, View.SCALE_X, 0.5f, 1.0f) + ); + animatorSet.setDuration(350); + animatorSet.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + } else { + animatorSet.playTogether( + ObjectAnimator.ofFloat(this, View.ALPHA, 0.0f, 1.0f) + ); + animatorSet.setDuration(300); + } animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { animatorSet = null; - AndroidUtilities.runOnUIThread(hideRunnable = () -> hide(), showingDuration); + if (!hasCloseButton) { + AndroidUtilities.runOnUIThread(hideRunnable = () -> hide(), showingDuration); + } } }); - animatorSet.setDuration(300); + animatorSet.start(); } else { setAlpha(1.0f); @@ -325,6 +378,12 @@ public void onAnimationEnd(Animator animation) { return true; } + public void updatePosition() { + if (currentView == null) { + return; + } + updatePosition(currentView); + } private void updatePosition(View view) { measure(MeasureSpec.makeMeasureSpec(AndroidUtilities.displaySize.x, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(AndroidUtilities.displaySize.x, MeasureSpec.AT_MOST)); @@ -335,7 +394,7 @@ private void updatePosition(View view) { if (currentType == 4) { top += AndroidUtilities.dp(4); - } else if (currentType == 6) { + } else if (currentType == 6 && isTopArrow) { top += view.getMeasuredHeight() + getMeasuredHeight() + AndroidUtilities.dp(10); } else if (currentType == 7 || currentType == 8 && isTopArrow) { top += view.getMeasuredHeight() + getMeasuredHeight() + AndroidUtilities.dp(8); @@ -404,7 +463,7 @@ private void updatePosition(View view) { } } setTranslationX(offset); - float arrowX = centerX - (leftMargin + offset) - arrowImageView.getMeasuredWidth() / 2; + float arrowX = centerX - (leftMargin + offset) - arrowImageView.getMeasuredWidth() / 2f; if (currentType == 7) { arrowX += AndroidUtilities.dp(2); } @@ -447,28 +506,49 @@ public void hide(boolean animate) { } if (animate) { animatorSet = new AnimatorSet(); - animatorSet.playTogether( - ObjectAnimator.ofFloat(this, View.ALPHA, 0.0f) - ); + if (useScale) { + animatorSet.playTogether( + ObjectAnimator.ofFloat(this, View.ALPHA, 1.0f, 0.0f), + ObjectAnimator.ofFloat(this, View.SCALE_Y, 1.0f, 0.5f), + ObjectAnimator.ofFloat(this, View.SCALE_X, 1.0f, 0.5f) + ); + animatorSet.setDuration(150); + animatorSet.setInterpolator(CubicBezierInterpolator.DEFAULT); + } else { + animatorSet.playTogether( + ObjectAnimator.ofFloat(this, View.ALPHA, 0.0f) + ); + animatorSet.setDuration(300); + } animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { setVisibility(View.INVISIBLE); + if (visibleListener != null) { + visibleListener.onVisible(false); + } currentView = null; messageCell = null; animatorSet = null; } }); - animatorSet.setDuration(300); + animatorSet.start(); } else { setVisibility(View.INVISIBLE); + if (visibleListener != null) { + visibleListener.onVisible(false); + } currentView = null; messageCell = null; animatorSet = null; } } + public boolean isShowing() { + return getTag() != null; + } + public void setText(CharSequence text) { textView.setText(text); } @@ -485,8 +565,125 @@ public void setBottomOffset(int offset) { this.bottomOffset = offset; } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); + } + + public void setUseScale(boolean useScale) { + this.useScale = useScale; + } + + public void setVisibleListener(VisibilityListener visibleListener) { + this.visibleListener = visibleListener; + } + + public interface VisibilityListener { + void onVisible(boolean visible); + } + + android.graphics.Path path; + Paint backgroundPaint; + @Override + protected void dispatchDraw(Canvas canvas) { + if (drawPath && path != null) { + if (backgroundPaint == null) { + backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + backgroundPaint.setPathEffect(new CornerPathEffect(AndroidUtilities.dpf2(6))); + backgroundPaint.setColor(backgroundColor); + } + canvas.drawPath(path, backgroundPaint); + } + + super.dispatchDraw(canvas); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + if (drawPath) { + int height = getMeasuredHeight(); + int width = getMeasuredWidth(); + float cx = arrowImageView.getX() + arrowImageView.getMeasuredWidth() / 2f; + if (path == null) { + path = new Path(); + } else { + path.rewind(); + } + if (isTopArrow) { + path.moveTo(0, dp(6)); + path.lineTo(0, height); + path.lineTo(width, height); + path.lineTo(width, dp(6)); + + path.lineTo(cx + dp(7), dp(6)); + path.lineTo(cx, -dp(2)); + path.lineTo(cx - dp(7), dp(6)); + path.close(); + } else { + path.moveTo(0, height - dp(6)); + path.lineTo(0, 0); + path.lineTo(width, 0); + path.lineTo(width, height - dp(6)); + + path.lineTo(cx + dp(7), height - dp(6)); + path.lineTo(cx, height + dp(2)); + path.lineTo(cx - dp(7), height - dp(6)); + path.close(); + } + } + + } + + public static class Builder { + + HintView hintView; + boolean closeButton; + Context context; + Theme.ResourcesProvider resourcesProvider; + private boolean isTopArrow; + private boolean drawPath = true; + private int backgroundColor; + + public Builder(Context context, Theme.ResourcesProvider resourcesProvider) { + this.context = context; + this.resourcesProvider = resourcesProvider; + backgroundColor = Theme.getColor(Theme.key_chat_gifSaveHintBackground, resourcesProvider); + } + + public Builder setTopArrow(boolean topArrow) { + isTopArrow = topArrow; + return this; + } + + public Builder setDrawPath(boolean drawPath) { + this.drawPath = drawPath; + return this; + } + + public Builder withCloseButton() { + this.closeButton = true; + return this; + } + + public Builder setBackgroundColor(int color) { + this.backgroundColor = color; + return this; + } + + public HintView build() { + hintView = new HintView(context, 6, isTopArrow, resourcesProvider); + hintView.setUseScale(true); + if (drawPath) { + hintView.textView.setBackground(null); + hintView.arrowImageView.setImageDrawable(null); + hintView.drawPath = true; + hintView.backgroundColor = backgroundColor; + } + if (closeButton) { + hintView.createCloseButton(); + } + + return this.hintView; + } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/IPhotoPaintView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/IPhotoPaintView.java index 8ad8754ae2..3f8c39505b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/IPhotoPaintView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/IPhotoPaintView.java @@ -26,6 +26,8 @@ default View getView() { void shutdown(); void onResume(); + default void onAnimationStateChanged(boolean isStart) {} + default void setOffsetTranslationX(float x) {} void setOffsetTranslationY(float y, float panProgress, int keyboardHeight, boolean isPan); float getOffsetTranslationY(); void updateColors(); @@ -38,7 +40,7 @@ default View getView() { boolean onTouch(MotionEvent ev); void setTransform(float scale, float trX, float trY, float imageWidth, float imageHeight); void setOnDoneButtonClickedListener(Runnable callback); - void onBackPressed(); + boolean onBackPressed(); int getEmojiPadding(boolean panned); RenderView getRenderView(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ImageUpdater.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ImageUpdater.java index 1842ad599a..0b6eaa201e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ImageUpdater.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ImageUpdater.java @@ -22,6 +22,7 @@ import android.os.Build; import android.os.Bundle; import android.provider.MediaStore; +import android.util.Pair; import android.view.View; import androidx.core.content.FileProvider; @@ -332,7 +333,7 @@ public void openMenu(boolean hasAvatar, Runnable onDeleteAvatar, DialogInterface sheet.setOnHideListener(onDismiss); parentFragment.showDialog(sheet); if (hasAvatar) { - sheet.setItemColor(items.size() - 1, Theme.getColor(Theme.key_dialogTextRed), Theme.getColor(Theme.key_dialogRedIcon)); + sheet.setItemColor(items.size() - 1, Theme.getColor(Theme.key_text_RedBold), Theme.getColor(Theme.key_text_RedRegular)); } } @@ -764,8 +765,12 @@ private void startCrop(String path, Uri uri) { } public void openPhotoForEdit(String path, String thumb, int orientation, boolean isVideo) { + openPhotoForEdit(path, thumb, new Pair<>(orientation, 0), isVideo); + } + + public void openPhotoForEdit(String path, String thumb, Pair orientation, boolean isVideo) { final ArrayList arrayList = new ArrayList<>(); - MediaController.PhotoEntry photoEntry = new MediaController.PhotoEntry(0, 0, 0, path, orientation, false, 0, 0, 0); + MediaController.PhotoEntry photoEntry = new MediaController.PhotoEntry(0, 0, 0, path, orientation.first, false, 0, 0, 0).setOrientation(orientation); photoEntry.isVideo = isVideo; photoEntry.thumbPath = thumb; arrayList.add(photoEntry); @@ -800,24 +805,7 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { } else if (requestCode == 13) { parentFragment.getParentActivity().overridePendingTransition(R.anim.alpha_in, R.anim.alpha_out); PhotoViewer.getInstance().setParentActivity(parentFragment); - int orientation = 0; - try { - ExifInterface ei = new ExifInterface(currentPicturePath); - int exif = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); - switch (exif) { - case ExifInterface.ORIENTATION_ROTATE_90: - orientation = 90; - break; - case ExifInterface.ORIENTATION_ROTATE_180: - orientation = 180; - break; - case ExifInterface.ORIENTATION_ROTATE_270: - orientation = 270; - break; - } - } catch (Exception e) { - FileLog.e(e); - } + Pair orientation = AndroidUtilities.getImageOrientation(currentPicturePath); openPhotoForEdit(currentPicturePath, null, orientation, false); AndroidUtilities.addMediaToGallery(currentPicturePath); currentPicturePath = null; @@ -879,7 +867,7 @@ private void processBitmap(Bitmap bitmap, MessageObject avatarObject) { NotificationCenter.getInstance(currentAccount).addObserver(ImageUpdater.this, NotificationCenter.filePreparingStarted); NotificationCenter.getInstance(currentAccount).addObserver(ImageUpdater.this, NotificationCenter.filePreparingFailed); NotificationCenter.getInstance(currentAccount).addObserver(ImageUpdater.this, NotificationCenter.fileNewChunkAvailable); - MediaController.getInstance().scheduleVideoConvert(avatarObject, true); + MediaController.getInstance().scheduleVideoConvert(avatarObject, true, true); uploadingImage = null; if (delegate != null) { delegate.didStartUpload(true); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ImportingAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ImportingAlert.java index 4d093fbddb..567c2ed7d7 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ImportingAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ImportingAlert.java @@ -104,9 +104,8 @@ public void setText(CharSequence text) { textView.setText(text); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + protected int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/InstantCameraView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/InstantCameraView.java index 3ce0428abf..c4540eac88 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/InstantCameraView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/InstantCameraView.java @@ -69,14 +69,17 @@ import androidx.core.graphics.ColorUtils; import com.google.android.exoplayer2.ExoPlayer; +import com.google.android.exoplayer2.util.Log; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.AutoDeleteMediaTask; import org.telegram.messenger.BuildVars; import org.telegram.messenger.DispatchQueue; import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; +import org.telegram.messenger.ImageLoader; import org.telegram.messenger.ImageReceiver; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MediaController; @@ -94,6 +97,7 @@ import org.telegram.messenger.video.MP4Builder; import org.telegram.messenger.video.Mp4Movie; import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.NativeByteBuffer; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.ChatActivity; @@ -101,6 +105,7 @@ import java.io.File; import java.io.FileOutputStream; +import java.io.IOException; import java.lang.ref.WeakReference; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -123,9 +128,11 @@ @TargetApi(18) public class InstantCameraView extends FrameLayout implements NotificationCenter.NotificationCenterDelegate { + public boolean WRITE_TO_FILE_IN_BACKGROUND; + private int currentAccount = UserConfig.selectedAccount; private InstantViewCameraContainer cameraContainer; - private ChatActivity baseFragment; + private Delegate delegate; private Paint paint; private RectF rect; private ImageView switchCameraButton; @@ -226,6 +233,7 @@ public class InstantCameraView extends FrameLayout implements NotificationCenter private final Theme.ResourcesProvider resourcesProvider; private final static int audioSampleRate = 48000; + public boolean drawBlur = true; private static final int[] ALLOW_BIG_CAMERA_WHITELIST = { 285904780, // XIAOMI (Redmi Note 7) @@ -234,15 +242,16 @@ public class InstantCameraView extends FrameLayout implements NotificationCenter @SuppressLint("ClickableViewAccessibility") - public InstantCameraView(Context context, ChatActivity parentFragment, Theme.ResourcesProvider resourcesProvider) { + public InstantCameraView(Context context, Delegate delegate, Theme.ResourcesProvider resourcesProvider) { super(context); + WRITE_TO_FILE_IN_BACKGROUND = false;//SharedConfig.deviceIsAboveAverage(); this.resourcesProvider = resourcesProvider; - parentView = parentFragment.getFragmentView(); + parentView = delegate.getFragmentView(); setWillNotDraw(false); - baseFragment = parentFragment; - recordingGuid = baseFragment.getClassGuid(); - isSecretChat = baseFragment.getCurrentEncryptedChat() != null; + this.delegate = delegate; + recordingGuid = delegate.getClassGuid(); + isSecretChat = delegate.isSecretChat(); paint = new Paint(Paint.ANTI_ALIAS_FLAG) { @Override public void setAlpha(int a) { @@ -487,7 +496,9 @@ public void destroy(boolean async, final Runnable beforeDestroyRunnable) { @Override protected void onDraw(Canvas canvas) { - blurBehindDrawable.draw(canvas); + if (drawBlur) { + blurBehindDrawable.draw(canvas); + } float x = cameraContainer.getX(); float y = cameraContainer.getY(); @@ -506,20 +517,6 @@ protected void onDraw(Canvas canvas) { canvas.drawArc(rect, -90, 360 * progress, false, paint); canvas.restore(); } - if (Theme.chat_roundVideoShadow != null) { - int x1 = (int) x - AndroidUtilities.dp(3); - int y1 = (int) y - AndroidUtilities.dp(2); - canvas.save(); - if (isMessageTransition) { - canvas.scale(cameraContainer.getScaleX(), cameraContainer.getScaleY(), x, y); - } else { - canvas.scale(cameraContainer.getScaleX(), cameraContainer.getScaleY(), x + textureViewSize / 2f, y + textureViewSize / 2f); - } - Theme.chat_roundVideoShadow.setAlpha((int) (cameraContainer.getAlpha() * 255)); - Theme.chat_roundVideoShadow.setBounds(x1, y1, x1 + textureViewSize + AndroidUtilities.dp(6), y1 + textureViewSize + AndroidUtilities.dp(6)); - Theme.chat_roundVideoShadow.draw(canvas); - canvas.restore(); - } } @Override @@ -598,7 +595,13 @@ public void showCamera() { if (!initCamera()) { return; } - MediaController.getInstance().pauseMessage(MediaController.getInstance().getPlayingMessageObject()); + if (MediaController.getInstance().getPlayingMessageObject() != null) { + if (MediaController.getInstance().getPlayingMessageObject().isVideo() || MediaController.getInstance().getPlayingMessageObject().isRoundVideo()) { + MediaController.getInstance().cleanupPlayer(true, true); + } else { + MediaController.getInstance().pauseMessage(MediaController.getInstance().getPlayingMessageObject()); + } + } cameraFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_DOCUMENT), System.currentTimeMillis() + "_" + SharedConfig.getLastLocalId() + ".mp4") { @Override @@ -614,7 +617,7 @@ public boolean delete() { AutoDeleteMediaTask.lockFile(cameraFile); if (BuildVars.LOGS_ENABLED) { - FileLog.d("show round camera " + cameraFile.getAbsolutePath()); + FileLog.d("InstantCamera show round camera " + cameraFile.getAbsolutePath()); } textureView = new TextureView(getContext()); @@ -622,14 +625,14 @@ public boolean delete() { @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { if (BuildVars.LOGS_ENABLED) { - FileLog.d("camera surface available"); + FileLog.d("InstantCamera camera surface available"); } if (cameraThread == null && surface != null) { if (cancelled) { return; } if (BuildVars.LOGS_ENABLED) { - FileLog.d("start create thread"); + FileLog.d("InstantCamera start create thread"); } cameraThread = new CameraGLThread(surface, width, height); } @@ -800,7 +803,7 @@ public void send(int state, boolean notify, int scheduleDate) { videoEditedInfo.encryptedFile = encryptedFile; videoEditedInfo.key = key; videoEditedInfo.iv = iv; - baseFragment.sendMedia(new MediaController.PhotoEntry(0, 0, 0, cameraFile.getAbsolutePath(), 0, true, 0, 0, 0), videoEditedInfo, notify, scheduleDate, false); + delegate.sendMedia(new MediaController.PhotoEntry(0, 0, 0, cameraFile.getAbsolutePath(), 0, true, 0, 0, 0), videoEditedInfo, notify, scheduleDate, false); if (scheduleDate != 0) { startAnimation(false); } @@ -999,7 +1002,7 @@ private boolean initCamera() { } } if (BuildVars.LOGS_ENABLED) { - FileLog.d("preview w = " + previewSize.mWidth + " h = " + previewSize.mHeight); + FileLog.d("InstantCamera preview w = " + previewSize.mWidth + " h = " + previewSize.mHeight); } return true; } @@ -1025,9 +1028,9 @@ private Size chooseOptimalSize(ArrayList previewSizes) { sizes = previewSizes; } if (Build.MANUFACTURER.equalsIgnoreCase("Xiaomi")) { - return CameraController.chooseOptimalSize(sizes, 640, 480, aspectRatio); + return CameraController.chooseOptimalSize(sizes, 640, 480, aspectRatio, false); } else { - return CameraController.chooseOptimalSize(sizes, 480, 270, aspectRatio); + return CameraController.chooseOptimalSize(sizes, 480, 270, aspectRatio, false); } } Collections.sort(sortedSizes, (o1, o2) -> { @@ -1084,7 +1087,7 @@ private void createCamera(final SurfaceTexture surfaceTexture) { return; } if (BuildVars.LOGS_ENABLED) { - FileLog.d("create camera session"); + FileLog.d("InstantCamera create camera session"); } surfaceTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight()); @@ -1097,7 +1100,7 @@ private void createCamera(final SurfaceTexture surfaceTexture) { Camera.Size size = cameraSession.getCurrentPreviewSize(); if (size.width != previewSize.getWidth() || size.height != previewSize.getHeight()) { previewSize = new Size(size.width, size.height); - FileLog.d("change preview size to w = " + previewSize.getWidth() + " h = " + previewSize.getHeight()); + FileLog.d("InstantCamera change preview size to w = " + previewSize.getWidth() + " h = " + previewSize.getHeight()); } } catch (Exception e) { FileLog.e(e); @@ -1107,14 +1110,14 @@ private void createCamera(final SurfaceTexture surfaceTexture) { Camera.Size size = cameraSession.getCurrentPictureSize(); if (size.width != pictureSize.getWidth() || size.height != pictureSize.getHeight()) { pictureSize = new Size(size.width, size.height); - FileLog.d("change picture size to w = " + pictureSize.getWidth() + " h = " + pictureSize.getHeight()); + FileLog.d("InstantCamera change picture size to w = " + pictureSize.getWidth() + " h = " + pictureSize.getHeight()); updateScale = true; } } catch (Exception e) { FileLog.e(e); } if (BuildVars.LOGS_ENABLED) { - FileLog.d("camera initied"); + FileLog.d("InstantCamera camera initied"); } cameraSession.setInitied(); if (updateScale) { @@ -1215,6 +1218,10 @@ public void setIsMessageTransition(boolean isMessageTransition) { this.isMessageTransition = isMessageTransition; } + public void resetCameraFile() { + cameraFile = null; + } + public class CameraGLThread extends DispatchQueue { private final static int EGL_CONTEXT_CLIENT_VERSION = 0x3098; @@ -1279,20 +1286,20 @@ private void updateScale() { scaleX = height / (float) surfaceWidth; scaleY = 1.0f; } - FileLog.d("camera scaleX = " + scaleX + " scaleY = " + scaleY); + FileLog.d("InstantCamera camera scaleX = " + scaleX + " scaleY = " + scaleY); } private boolean initGL() { if (BuildVars.LOGS_ENABLED) { - FileLog.d("start init gl"); + FileLog.d("InstantCamera start init gl"); } egl10 = (EGL10) EGLContext.getEGL(); eglDisplay = egl10.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); if (eglDisplay == EGL10.EGL_NO_DISPLAY) { if (BuildVars.LOGS_ENABLED) { - FileLog.e("eglGetDisplay failed " + GLUtils.getEGLErrorString(egl10.eglGetError())); + FileLog.e("InstantCamera eglGetDisplay failed " + GLUtils.getEGLErrorString(egl10.eglGetError())); } finish(); return false; @@ -1301,7 +1308,7 @@ private boolean initGL() { int[] version = new int[2]; if (!egl10.eglInitialize(eglDisplay, version)) { if (BuildVars.LOGS_ENABLED) { - FileLog.e("eglInitialize failed " + GLUtils.getEGLErrorString(egl10.eglGetError())); + FileLog.e("InstantCamera eglInitialize failed " + GLUtils.getEGLErrorString(egl10.eglGetError())); } finish(); return false; @@ -1322,7 +1329,7 @@ private boolean initGL() { EGLConfig eglConfig; if (!egl10.eglChooseConfig(eglDisplay, configSpec, configs, 1, configsCount)) { if (BuildVars.LOGS_ENABLED) { - FileLog.e("eglChooseConfig failed " + GLUtils.getEGLErrorString(egl10.eglGetError())); + FileLog.e("InstantCamera eglChooseConfig failed " + GLUtils.getEGLErrorString(egl10.eglGetError())); } finish(); return false; @@ -1330,7 +1337,7 @@ private boolean initGL() { eglConfig = configs[0]; } else { if (BuildVars.LOGS_ENABLED) { - FileLog.e("eglConfig not initialized"); + FileLog.e("InstantCamera eglConfig not initialized"); } finish(); return false; @@ -1340,7 +1347,7 @@ private boolean initGL() { eglContext = egl10.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list); if (eglContext == null) { if (BuildVars.LOGS_ENABLED) { - FileLog.e("eglCreateContext failed " + GLUtils.getEGLErrorString(egl10.eglGetError())); + FileLog.e("InstantCamera eglCreateContext failed " + GLUtils.getEGLErrorString(egl10.eglGetError())); } finish(); return false; @@ -1355,14 +1362,14 @@ private boolean initGL() { if (eglSurface == null || eglSurface == EGL10.EGL_NO_SURFACE) { if (BuildVars.LOGS_ENABLED) { - FileLog.e("createWindowSurface failed " + GLUtils.getEGLErrorString(egl10.eglGetError())); + FileLog.e("InstantCamera createWindowSurface failed " + GLUtils.getEGLErrorString(egl10.eglGetError())); } finish(); return false; } if (!egl10.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) { if (BuildVars.LOGS_ENABLED) { - FileLog.e("eglMakeCurrent failed " + GLUtils.getEGLErrorString(egl10.eglGetError())); + FileLog.e("InstantCamera eglMakeCurrent failed " + GLUtils.getEGLErrorString(egl10.eglGetError())); } finish(); return false; @@ -1404,7 +1411,7 @@ private boolean initGL() { GLES20.glGetProgramiv(drawProgram, GLES20.GL_LINK_STATUS, linkStatus, 0); if (linkStatus[0] == 0) { if (BuildVars.LOGS_ENABLED) { - FileLog.e("failed link shader"); + FileLog.e("InstantCamera failed link shader"); } GLES20.glDeleteProgram(drawProgram); drawProgram = 0; @@ -1416,7 +1423,7 @@ private boolean initGL() { } } else { if (BuildVars.LOGS_ENABLED) { - FileLog.e("failed creating shader"); + FileLog.e("InstantCamera failed creating shader"); } finish(); return false; @@ -1435,7 +1442,7 @@ private boolean initGL() { cameraSurface.setOnFrameAvailableListener(surfaceTexture -> requestRender()); createCamera(cameraSurface); if (BuildVars.LOGS_ENABLED) { - FileLog.e("gl initied"); + FileLog.e("InstantCamera gl initied"); } return true; @@ -1450,6 +1457,19 @@ public void reinitForNewCamera() { } public void finish() { + if (cameraSurface != null) { + cameraSurface.release(); + cameraSurface = null; + } + if (eglSurface != null && eglContext != null) { + if (!eglContext.equals(egl10.eglGetCurrentContext()) || !eglSurface.equals(egl10.eglGetCurrentSurface(EGL10.EGL_DRAW))) { + egl10.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext); + } + if (cameraTexture[0] != 0) { + GLES20.glDeleteTextures(1, cameraTexture, 0); + cameraTexture[0] = 0; + } + } if (eglSurface != null) { egl10.eglMakeCurrent(eglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); egl10.eglDestroySurface(eglDisplay, eglSurface); @@ -1552,7 +1572,7 @@ public void handleMessage(Message inputMessage) { case DO_REINIT_MESSAGE: { if (!egl10.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) { if (BuildVars.LOGS_ENABLED) { - FileLog.d("eglMakeCurrent failed " + GLUtils.getEGLErrorString(egl10.eglGetError())); + FileLog.d("InstantCamera eglMakeCurrent failed " + GLUtils.getEGLErrorString(egl10.eglGetError())); } return; } @@ -1572,8 +1592,8 @@ public void handleMessage(Message inputMessage) { GLES20.glGenTextures(1, cameraTexture, 0); GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, cameraTexture[0]); - GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); - GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST); + GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); + GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); @@ -1599,7 +1619,7 @@ public void handleMessage(Message inputMessage) { } case DO_SETSESSION_MESSAGE: { if (BuildVars.LOGS_ENABLED) { - FileLog.d("set gl rednderer session"); + FileLog.d("InstantCamera set gl rednderer session"); } CameraSession newSession = (CameraSession) inputMessage.obj; if (currentSession == newSession) { @@ -1657,7 +1677,7 @@ public void handleMessage(Message inputMessage) { case MSG_START_RECORDING: { try { if (BuildVars.LOGS_ENABLED) { - FileLog.e("start encoder"); + FileLog.e("InstantCamera start encoder"); } encoder.prepareEncoder(); } catch (Exception e) { @@ -1669,7 +1689,7 @@ public void handleMessage(Message inputMessage) { } case MSG_STOP_RECORDING: { if (BuildVars.LOGS_ENABLED) { - FileLog.e("stop encoder"); + FileLog.e("InstantCamera stop encoder"); } encoder.handleStopRecording(inputMessage.arg1); break; @@ -1717,6 +1737,8 @@ private class VideoRecorder implements Runnable { private static final int IFRAME_INTERVAL = 1; private File videoFile; + private File fileToWrite; + private boolean writingToDifferentFile; private int videoWidth; private int videoHeight; private int videoBitrate; @@ -1782,6 +1804,8 @@ private class VideoRecorder implements Runnable { private DispatchQueue generateKeyframeThumbsQueue; private int frameCount; + DispatchQueue fileWriteQueue; + private Runnable recorderRunnable = new Runnable() { @RequiresApi(api = Build.VERSION_CODES.N) @@ -1806,7 +1830,12 @@ public void run() { } AudioBufferInfo buffer; if (buffers.isEmpty()) { - buffer = new AudioBufferInfo(); + try { + buffer = new AudioBufferInfo(); + } catch (OutOfMemoryError error) { + System.gc(); + buffer = new AudioBufferInfo(); + } } else { buffer = buffers.poll(); } @@ -1879,6 +1908,9 @@ public void run() { public void startRecording(File outputFile, android.opengl.EGLContext sharedContext) { int resolution = MessagesController.getInstance(currentAccount).roundVideoSize; int bitrate = MessagesController.getInstance(currentAccount).roundVideoBitrate * 1024; + AndroidUtilities.runOnUIThread(() -> { + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.stopAllHeavyOperations, 512); + }); videoFile = outputFile; videoWidth = resolution; @@ -1902,6 +1934,12 @@ public void startRecording(File outputFile, android.opengl.EGLContext sharedCont } } } + + if (WRITE_TO_FILE_IN_BACKGROUND) { + fileWriteQueue = new DispatchQueue("IVR_FileWriteQueue"); + fileWriteQueue.setPriority(Thread.MAX_PRIORITY); + } + keyframeThumbs.clear(); frameCount = 0; if (generateKeyframeThumbsQueue != null) { @@ -1914,8 +1952,12 @@ public void startRecording(File outputFile, android.opengl.EGLContext sharedCont public void stopRecording(int send) { handler.sendMessage(handler.obtainMessage(MSG_STOP_RECORDING, send, 0)); + AndroidUtilities.runOnUIThread(() -> { + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.stopAllHeavyOperations, 512); + }); } + long prevTimestamp; public void frameAvailable(SurfaceTexture st, Integer cameraId, long timestampInternal) { synchronized (sync) { if (!ready) { @@ -1928,7 +1970,7 @@ public void frameAvailable(SurfaceTexture st, Integer cameraId, long timestampIn zeroTimeStamps++; if (zeroTimeStamps > 1) { if (BuildVars.LOGS_ENABLED) { - FileLog.d("fix timestamp enabled"); + FileLog.d("InstantCamera fix timestamp enabled"); } timestamp = timestampInternal; } else { @@ -1937,7 +1979,7 @@ public void frameAvailable(SurfaceTexture st, Integer cameraId, long timestampIn } else { zeroTimeStamps = 0; } - + prevTimestamp = timestamp; handler.sendMessage(handler.obtainMessage(MSG_VIDEOFRAME_AVAILABLE, (int) (timestamp >> 32), (int) timestamp, cameraId)); } @@ -1964,7 +2006,7 @@ private void handleAudioFrameAvailable(AudioBufferInfo input) { if (audioFirst == -1) { if (videoFirst == -1) { if (BuildVars.LOGS_ENABLED) { - FileLog.d("video record not yet started"); + FileLog.d("InstantCamera video record not yet started"); } return; } @@ -1976,7 +2018,7 @@ private void handleAudioFrameAvailable(AudioBufferInfo input) { audioFirst = input.offset[a]; ok = true; if (BuildVars.LOGS_ENABLED) { - FileLog.d("detected desync between audio and video " + desyncTime); + FileLog.d("InstantCamera detected desync between audio and video " + desyncTime); } break; } @@ -1985,18 +2027,18 @@ private void handleAudioFrameAvailable(AudioBufferInfo input) { audioFirst = input.offset[a]; ok = true; if (BuildVars.LOGS_ENABLED) { - FileLog.d("found first audio frame at " + a + " timestamp = " + input.offset[a]); + FileLog.d("InstantCamera found first audio frame at " + a + " timestamp = " + input.offset[a]); } break; } else { if (BuildVars.LOGS_ENABLED) { - FileLog.d("ignore first audio frame at " + a + " timestamp = " + input.offset[a]); + FileLog.d("InstantCamera ignore first audio frame at " + a + " timestamp = " + input.offset[a]); } } } if (!ok) { if (BuildVars.LOGS_ENABLED) { - FileLog.d("first audio frame not found, removing buffers " + input.results); + FileLog.d("InstantCamera first audio frame not found, removing buffers " + input.results); } buffersToWrite.remove(input); } else { @@ -2037,9 +2079,15 @@ private void handleAudioFrameAvailable(AudioBufferInfo input) { long startWriteTime = input.offset[input.lastWroteBuffer]; for (int a = input.lastWroteBuffer; a <= input.results; a++) { if (a < input.results) { - if (!running && input.offset[a] >= videoLast - desyncTime) { + long totalTime = input.offset[a] - audioStartTime; + if (!running && (input.offset[a] >= videoLast - desyncTime || totalTime >= 60_000000)) { if (BuildVars.LOGS_ENABLED) { - FileLog.d("stop audio encoding because of stoped video recording at " + input.offset[a] + " last video " + videoLast); + if (totalTime >= 60_000000) { + FileLog.d("InstantCamera stop audio encoding because recorded time more than 60s"); + } else { + FileLog.d("InstantCamera stop audio encoding because of stoped video recording at " + input.offset[a] + " last video " + videoLast); + } + } audioStopedByTime = true; isLast = true; @@ -2083,18 +2131,31 @@ private void handleVideoFrameAvailable(long timestampNanos, Integer cameraId) { FileLog.e(e); } long dt, alphaDt; + boolean cameraChanged = false; if (!lastCameraId.equals(cameraId)) { - lastTimestamp = -1; + cameraChanged = true; lastCameraId = cameraId; } - if (lastTimestamp == -1) { - lastTimestamp = timestampNanos; + if (cameraChanged || lastTimestamp == -1) { if (currentTimestamp != 0) { - dt = (System.currentTimeMillis() - lastCommitedFrameTime) * 1000000; + //real dt lead to asynchron aduio and video + //surface may return wrong measured timestamp so big or negative + // `\_(._.)_/` + long dtTimestamps = (timestampNanos - lastTimestamp); + long dtReal = (System.currentTimeMillis() - lastCommitedFrameTime) * 1000000; + if (dtTimestamps < 0 || Math.abs(dtReal - dtTimestamps) > 100_000_000) { + dt = dtReal; + } else { + dt = dtTimestamps; + } + if (dt < 0) { + dt = 0; + } alphaDt = 0; } else { alphaDt = dt = 0; } + lastTimestamp = timestampNanos; } else { alphaDt = dt = (timestampNanos - lastTimestamp); lastTimestamp = timestampNanos; @@ -2111,7 +2172,7 @@ private void handleVideoFrameAvailable(long timestampNanos, Integer cameraId) { if (videoFirst == -1) { videoFirst = timestampNanos / 1000; if (BuildVars.LOGS_ENABLED) { - FileLog.d("first video frame was at " + videoFirst); + FileLog.d("InstantCamera first video frame was at " + videoFirst); } } videoLast = timestampNanos; @@ -2120,7 +2181,7 @@ private void handleVideoFrameAvailable(long timestampNanos, Integer cameraId) { FloatBuffer vertexBuffer = InstantCameraView.this.vertexBuffer; FloatBuffer oldTextureBuffer = oldTextureTextureBuffer; if (textureBuffer == null || vertexBuffer == null) { - FileLog.d("handleVideoFrameAvailable skip frame " + textureBuffer + " " + vertexBuffer); + FileLog.d("InstantCamera handleVideoFrameAvailable skip frame " + textureBuffer + " " + vertexBuffer); return; } @@ -2220,11 +2281,13 @@ public void run() { private void handleStopRecording(final int send) { if (running) { + FileLog.d("InstantCamera handleStopRecording running=false"); sendWhenDone = send; running = false; return; } try { + FileLog.d("InstantCamera handleStopRecording drain encoders"); drainEncoder(true); } catch (Exception e) { FileLog.e(e); @@ -2249,11 +2312,42 @@ private void handleStopRecording(final int send) { FileLog.e(e); } } + if (mediaMuxer != null) { - try { - mediaMuxer.finishMovie(); - } catch (Exception e) { - FileLog.e(e); + if (WRITE_TO_FILE_IN_BACKGROUND) { + CountDownLatch countDownLatch = new CountDownLatch(1); + fileWriteQueue.postRunnable(() -> { + try { + mediaMuxer.finishMovie(); + } catch (Exception e) { + e.printStackTrace(); + } + countDownLatch.countDown(); + }); + try { + countDownLatch.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } else { + try { + mediaMuxer.finishMovie(); + } catch (Exception e) { + FileLog.e(e); + } + } + FileLog.d("InstantCamera handleStopRecording finish muxer"); + if (writingToDifferentFile) { + if (!fileToWrite.renameTo(videoFile)) { + FileLog.e("InstantCamera unable to rename file, try move file"); + try { + AndroidUtilities.copyFile(fileToWrite, videoFile); + fileToWrite.delete(); + } catch (IOException e) { + FileLog.e(e); + FileLog.e("InstantCamera unable to move file"); + } + } } } if (generateKeyframeThumbsQueue != null) { @@ -2261,6 +2355,7 @@ private void handleStopRecording(final int send) { generateKeyframeThumbsQueue.recycle(); generateKeyframeThumbsQueue = null; } + FileLog.d("InstantCamera handleStopRecording send " + send); if (send != 0) { AndroidUtilities.runOnUIThread(() -> { videoEditedInfo = new VideoEditedInfo(); @@ -2277,15 +2372,15 @@ private void handleStopRecording(final int send) { videoEditedInfo.resultHeight = videoEditedInfo.originalHeight = 360; videoEditedInfo.originalPath = videoFile.getAbsolutePath(); if (send == 1) { - if (baseFragment.isInScheduleMode()) { - AlertsCreator.createScheduleDatePickerDialog(baseFragment.getParentActivity(), baseFragment.getDialogId(), (notify, scheduleDate) -> { - baseFragment.sendMedia(new MediaController.PhotoEntry(0, 0, 0, videoFile.getAbsolutePath(), 0, true, 0, 0, 0), videoEditedInfo, notify, scheduleDate, false); + if (delegate.isInScheduleMode()) { + AlertsCreator.createScheduleDatePickerDialog(delegate.getParentActivity(), delegate.getDialogId(), (notify, scheduleDate) -> { + delegate.sendMedia(new MediaController.PhotoEntry(0, 0, 0, videoFile.getAbsolutePath(), 0, true, 0, 0, 0), videoEditedInfo, notify, scheduleDate, false); startAnimation(false); }, () -> { startAnimation(false); }, resourcesProvider); } else { - baseFragment.sendMedia(new MediaController.PhotoEntry(0, 0, 0, videoFile.getAbsolutePath(), 0, true, 0, 0, 0), videoEditedInfo, true, 0, false); + delegate.sendMedia(new MediaController.PhotoEntry(0, 0, 0, videoFile.getAbsolutePath(), 0, true, 0, 0, 0), videoEditedInfo, true, 0, false); } } else { videoPlayer = new VideoPlayer(); @@ -2347,7 +2442,16 @@ public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { }); } else { FileLoader.getInstance(currentAccount).cancelFileUpload(videoFile.getAbsolutePath(), false); - videoFile.delete(); + try { + fileToWrite.delete(); + } catch (Throwable ignore) { + + } + try { + videoFile.delete(); + } catch (Throwable ignore) { + + } } EGL14.eglDestroySurface(eglDisplay, eglSurface); eglSurface = EGL14.EGL_NO_SURFACE; @@ -2404,7 +2508,7 @@ private void prepareEncoder() { audioRecorder = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, audioSampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize); audioRecorder.startRecording(); if (BuildVars.LOGS_ENABLED) { - FileLog.d("initied audio record with channels " + audioRecorder.getChannelCount() + " sample rate = " + audioRecorder.getSampleRate() + " bufferSize = " + bufferSize); + FileLog.d("InstantCamera initied audio record with channels " + audioRecorder.getChannelCount() + " sample rate = " + audioRecorder.getSampleRate() + " bufferSize = " + bufferSize); } Thread thread = new Thread(recorderRunnable); thread.setPriority(Thread.MAX_PRIORITY); @@ -2444,11 +2548,26 @@ private void prepareEncoder() { surface = videoEncoder.createInputSurface(); videoEncoder.start(); + boolean isSdCard = ImageLoader.isSdCardPath(videoFile); + fileToWrite = videoFile; + if (isSdCard) { + try { + fileToWrite = new File(ApplicationLoader.getFilesDirFixed(), "camera_tmp.mp4"); + if (fileToWrite.exists()) { + fileToWrite.delete(); + } + writingToDifferentFile = true; + } catch (Throwable e) { + FileLog.e(e); + fileToWrite = videoFile; + writingToDifferentFile = false; + } + } Mp4Movie movie = new Mp4Movie(); - movie.setCacheFile(videoFile); + movie.setCacheFile(fileToWrite); movie.setRotation(0); movie.setSize(videoWidth, videoHeight); - mediaMuxer = new MP4Builder().createMovie(movie, isSecretChat); + mediaMuxer = new MP4Builder().createMovie(movie, isSecretChat, false); AndroidUtilities.runOnUIThread(() -> { if (cancelled) { @@ -2461,7 +2580,7 @@ private void prepareEncoder() { } } - AndroidUtilities.lockOrientation(baseFragment.getParentActivity()); + AndroidUtilities.lockOrientation(delegate.getParentActivity()); recording = true; recordStartTime = System.currentTimeMillis(); invalidate(); @@ -2638,9 +2757,29 @@ public void drainEncoder(boolean endOfStream) throws Exception { } firstEncode = false; } - long availableSize = mediaMuxer.writeSampleData(videoTrackIndex, encodedData, videoBufferInfo, true); - if (availableSize != 0) { - didWriteData(videoFile, availableSize, false); + if (WRITE_TO_FILE_IN_BACKGROUND) { + MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); + bufferInfo.size = videoBufferInfo.size; + bufferInfo.offset = videoBufferInfo.offset; + bufferInfo.flags = videoBufferInfo.flags; + bufferInfo.presentationTimeUs = videoBufferInfo.presentationTimeUs; + ByteBuffer byteBuffer = AndroidUtilities.cloneByteBuffer(encodedData); + fileWriteQueue.postRunnable(() -> { + long availableSize = 0; + try { + availableSize = mediaMuxer.writeSampleData(videoTrackIndex, byteBuffer, bufferInfo, true); + } catch (Exception e) { + e.printStackTrace(); + } + if (availableSize != 0 && !writingToDifferentFile) { + didWriteData(videoFile, availableSize, false); + } + }); + } else { + long availableSize = mediaMuxer.writeSampleData(videoTrackIndex, encodedData, videoBufferInfo, true); + if (availableSize != 0 && !writingToDifferentFile) { + didWriteData(videoFile, availableSize, false); + } } } else if (videoTrackIndex == -5) { byte[] csd = new byte[videoBufferInfo.size]; @@ -2681,7 +2820,6 @@ public void drainEncoder(boolean endOfStream) throws Exception { if (Build.VERSION.SDK_INT < 21) { encoderOutputBuffers = audioEncoder.getOutputBuffers(); } - boolean encoderOutputAvailable = true; while (true) { int encoderStatus = audioEncoder.dequeueOutputBuffer(audioBufferInfo, 0); if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) { @@ -2711,12 +2849,39 @@ public void drainEncoder(boolean endOfStream) throws Exception { audioBufferInfo.size = 0; } if (audioBufferInfo.size != 0) { - long availableSize = mediaMuxer.writeSampleData(audioTrackIndex, encodedData, audioBufferInfo, false); - if (availableSize != 0) { - didWriteData(videoFile, availableSize, false); + if (WRITE_TO_FILE_IN_BACKGROUND) { + MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); + bufferInfo.size = audioBufferInfo.size; + bufferInfo.offset = audioBufferInfo.offset; + bufferInfo.flags = audioBufferInfo.flags; + bufferInfo.presentationTimeUs = audioBufferInfo.presentationTimeUs; + ByteBuffer byteBuffer = AndroidUtilities.cloneByteBuffer(encodedData); + fileWriteQueue.postRunnable(() -> { + long availableSize = 0; + try { + availableSize = mediaMuxer.writeSampleData(audioTrackIndex, byteBuffer, bufferInfo, false); + } catch (Exception e) { + e.printStackTrace(); + } + if (availableSize != 0 && !writingToDifferentFile) { + didWriteData(videoFile, availableSize, false); + } + }); + if (audioEncoder != null) { + audioEncoder.releaseOutputBuffer(encoderStatus, false); + } + } else { + long availableSize = mediaMuxer.writeSampleData(audioTrackIndex, encodedData, audioBufferInfo, false); + if (availableSize != 0 && !writingToDifferentFile) { + didWriteData(videoFile, availableSize, false); + } + if (audioEncoder != null) { + audioEncoder.releaseOutputBuffer(encoderStatus, false); + } } + } else if (audioEncoder != null) { + audioEncoder.releaseOutputBuffer(encoderStatus, false); } - audioEncoder.releaseOutputBuffer(encoderStatus, false); if ((audioBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { break; } @@ -2726,6 +2891,10 @@ public void drainEncoder(boolean endOfStream) throws Exception { @Override protected void finalize() throws Throwable { + if (fileWriteQueue != null) { + fileWriteQueue.recycle(); + fileWriteQueue = null; + } try { if (eglDisplay != EGL14.EGL_NO_DISPLAY) { EGL14.eglMakeCurrent(eglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT); @@ -2743,7 +2912,7 @@ protected void finalize() throws Throwable { } private String createFragmentShader(Size previewSize) { - if (!allowBigSizeCamera() || Math.max(previewSize.getHeight(), previewSize.getWidth()) * 0.7f < MessagesController.getInstance(currentAccount).roundVideoSize) { + if (SharedConfig.deviceIsLow() || !allowBigSizeCamera() || Math.max(previewSize.getHeight(), previewSize.getWidth()) * 0.7f < MessagesController.getInstance(currentAccount).roundVideoSize) { return "#extension GL_OES_EGL_image_external : require\n" + "precision highp float;\n" + "varying vec2 vTextureCoord;\n" + @@ -2761,12 +2930,12 @@ private String createFragmentShader(Size previewSize) { " gl_FragColor = vec4(color * alpha, alpha);\n" + "}\n"; } - //apply box blur + //apply bilinear filtering return "#extension GL_OES_EGL_image_external : require\n" + "precision highp float;\n" + - "varying vec2 vTextureCoord;\n" + - "uniform vec2 resolution;\n" + - "uniform vec2 preview;\n" + + "varying vec2 vTextureCoord;\n" + //uv + "uniform vec2 resolution;\n" + //rendering texture + "uniform vec2 preview;\n" + //original texture size "uniform float alpha;\n" + "uniform samplerExternalOES sTexture;\n" + @@ -2776,16 +2945,22 @@ private String createFragmentShader(Size previewSize) { " float d = length(coord - gl_FragCoord.xy) - radius;\n" + " float t = clamp(d, 0.0, 1.0);\n" + " if (t == 0.0) {\n" + - " float pixelSizeX = 1.0 / preview.x;\n" + - " float pixelSizeY = 1.0 / preview.y;\n" + - " vec3 accumulation = vec3(0);\n" + - " for (float x = 0.0; x < 2.0; x++){\n" + - " for (float y = 0.0; y < 2.0; y++){\n" + - " accumulation += texture2D(sTexture, vTextureCoord + vec2(x * pixelSizeX, y * pixelSizeY)).xyz;\n" + - " }\n" + - " }\n" + - " vec4 textColor = vec4(accumulation / vec3(4, 4, 4), 1);\n" + - " gl_FragColor = textColor * alpha;\n" + + " vec2 c_textureSize = preview;\n" + + " vec2 c_onePixel = (1.0 / c_textureSize);\n" + + " vec2 uv = vTextureCoord;\n" + + " vec2 pixel = uv * c_textureSize + 0.5;\n" + + + " vec2 frac = fract(pixel);\n" + + " pixel = (floor(pixel) / c_textureSize) - vec2(c_onePixel);\n" + + + " vec4 tl = texture2D(sTexture, pixel + vec2(0.0 , 0.0));\n" + + " vec4 tr = texture2D(sTexture, pixel + vec2(c_onePixel.x, 0.0));\n" + + " vec4 bl = texture2D(sTexture, pixel + vec2(0.0 , c_onePixel.y));\n" + + " vec4 br = texture2D(sTexture, pixel + vec2(c_onePixel.x, c_onePixel.y));\n" + + + " vec4 x1 = mix(tl, tr, frac.x);\n" + + " vec4 x2 = mix(bl, br, frac.x);\n" + + " gl_FragColor = mix(x1, x2, frac.y) * alpha;" + " } else {\n" + " gl_FragColor = vec4(1, 1, 1, alpha);\n" + " }\n" + @@ -2839,7 +3014,7 @@ protected void dispatchDraw(Canvas canvas) { @Override public boolean onTouchEvent(MotionEvent ev) { - if (ev.getAction() == MotionEvent.ACTION_DOWN && baseFragment != null) { + if (ev.getAction() == MotionEvent.ACTION_DOWN && delegate != null) { if (videoPlayer != null) { boolean mute = !videoPlayer.isMuted(); videoPlayer.setMute(mute); @@ -2940,4 +3115,21 @@ public void onAnimationEnd(Animator animation) { finishZoomTransition.start(); } } + + public interface Delegate { + + View getFragmentView(); + void sendMedia(MediaController.PhotoEntry entry, VideoEditedInfo videoEditedInfo, boolean b, int i, boolean b1); + Activity getParentActivity(); + int getClassGuid(); + long getDialogId(); + + default boolean isSecretChat() { + return false; + } + + default boolean isInScheduleMode() { + return false; + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/InviteLinkBottomSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/InviteLinkBottomSheet.java index 72dfbaa38e..5c6f4a9f07 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/InviteLinkBottomSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/InviteLinkBottomSheet.java @@ -6,7 +6,6 @@ import android.animation.ObjectAnimator; import android.content.Context; import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.RectF; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; @@ -48,7 +47,6 @@ import org.telegram.ui.ProfileActivity; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -399,7 +397,7 @@ private void updateColorForView(View view) { } else if (view instanceof LinkActionView) { ((LinkActionView) view).updateColors(); } else if (view instanceof TextInfoPrivacyCell) { - CombinedDrawable combinedDrawable = new CombinedDrawable(new ColorDrawable(Theme.getColor(Theme.key_windowBackgroundGray)), Theme.getThemedDrawable(view.getContext(), R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + CombinedDrawable combinedDrawable = new CombinedDrawable(new ColorDrawable(Theme.getColor(Theme.key_windowBackgroundGray)), Theme.getThemedDrawableByKey(view.getContext(), R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); combinedDrawable.setFullsize(true); view.setBackground(combinedDrawable); ((TextInfoPrivacyCell) view).setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText4)); @@ -409,13 +407,13 @@ private void updateColorForView(View view) { RecyclerView.ViewHolder holder = listView.getChildViewHolder(view); if (holder != null) { if (holder.getItemViewType() == 7) { - Drawable shadowDrawable = Theme.getThemedDrawable(view.getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow); + Drawable shadowDrawable = Theme.getThemedDrawableByKey(view.getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow); Drawable background = new ColorDrawable(Theme.getColor(Theme.key_windowBackgroundGray)); CombinedDrawable combinedDrawable = new CombinedDrawable(background, shadowDrawable, 0, 0); combinedDrawable.setFullsize(true); view.setBackgroundDrawable(combinedDrawable); } else if (holder.getItemViewType() == 2) { - Drawable shadowDrawable = Theme.getThemedDrawable(view.getContext(), R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow); + Drawable shadowDrawable = Theme.getThemedDrawableByKey(view.getContext(), R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow); Drawable background = new ColorDrawable(Theme.getColor(Theme.key_windowBackgroundGray)); CombinedDrawable combinedDrawable = new CombinedDrawable(background, shadowDrawable, 0, 0); combinedDrawable.setFullsize(true); @@ -543,7 +541,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int default: case 0: HeaderCell headerCell = new HeaderCell(context, Theme.key_windowBackgroundWhiteBlueHeader, 21, 15, true); - headerCell.getTextView2().setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText)); + headerCell.getTextView2().setTextColor(Theme.getColor(Theme.key_text_RedRegular)); headerCell.getTextView2().setTextSize(15); headerCell.getTextView2().setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); view = headerCell; @@ -653,7 +651,7 @@ public void removeLink() { break; case 4: view = new TimerPrivacyCell(context); - CombinedDrawable combinedDrawable = new CombinedDrawable(new ColorDrawable(Theme.getColor(Theme.key_windowBackgroundGray)), Theme.getThemedDrawable(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + CombinedDrawable combinedDrawable = new CombinedDrawable(new ColorDrawable(Theme.getColor(Theme.key_windowBackgroundGray)), Theme.getThemedDrawableByKey(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); combinedDrawable.setFullsize(true); view.setBackground(combinedDrawable); break; @@ -675,7 +673,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { break; case 7: view = new ShadowSectionCell(context, 12); - Drawable shadowDrawable = Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow); + Drawable shadowDrawable = Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow); Drawable background = new ColorDrawable(Theme.getColor(Theme.key_windowBackgroundGray)); combinedDrawable = new CombinedDrawable(background, shadowDrawable, 0, 0); combinedDrawable.setFullsize(true); @@ -791,7 +789,7 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi privacyCell.setText(LocaleController.getString("LinkIsExpiredLimitReached", R.string.LinkIsExpiredLimitReached)); } else { privacyCell.setText(LocaleController.getString("LinkIsExpired", R.string.LinkIsExpired)); - privacyCell.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText)); + privacyCell.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); } } else if (invite.expire_date > 0) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ItemOptions.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ItemOptions.java new file mode 100644 index 0000000000..b5e22efca9 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ItemOptions.java @@ -0,0 +1,519 @@ +package org.telegram.ui.Components; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.view.WindowManager; +import android.widget.LinearLayout; +import android.widget.PopupWindow; +import android.widget.ScrollView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.graphics.ColorUtils; + +import com.google.android.exoplayer2.util.Consumer; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.R; +import org.telegram.ui.ActionBar.ActionBarMenuSubItem; +import org.telegram.ui.ActionBar.ActionBarPopupWindow; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Cells.UserCell; +import org.telegram.ui.ProfileActivity; + +public class ItemOptions { + + public static ItemOptions makeOptions(@NonNull BaseFragment fragment, @NonNull View scrimView) { + return new ItemOptions(fragment, scrimView); + } + + public static ItemOptions makeOptions(@NonNull ViewGroup container, @NonNull View scrimView) { + return makeOptions(container, null, scrimView); + } + + public static ItemOptions makeOptions(@NonNull ViewGroup container, @Nullable Theme.ResourcesProvider resourcesProvider, @NonNull View scrimView) { + return new ItemOptions(container, resourcesProvider, scrimView); + } + + private ViewGroup container; + private BaseFragment fragment; + private Theme.ResourcesProvider resourcesProvider; + + private Context context; + private View scrimView; + private Drawable scrimViewBackground; + private int gravity = Gravity.RIGHT; + + private ActionBarPopupWindow actionBarPopupWindow; + private final float[] point = new float[2]; + + private float translateX, translateY; + private int dimAlpha; + + private View dimView; + private ViewTreeObserver.OnPreDrawListener preDrawListener; + + private android.graphics.Rect viewAdditionalOffsets = new android.graphics.Rect(); + private ViewGroup layout; + private ActionBarPopupWindow.ActionBarPopupWindowLayout lastLayout; + + private ItemOptions(BaseFragment fragment, View scrimView) { + if (fragment.getContext() == null) { + return; + } + + this.fragment = fragment; + this.resourcesProvider = fragment.getResourceProvider(); + this.context = fragment.getContext(); + this.scrimView = scrimView; + this.dimAlpha = AndroidUtilities.computePerceivedBrightness(Theme.getColor(Theme.key_windowBackgroundWhite, resourcesProvider)) > .705 ? 0x66 : 0x33; + + if (fragment.getFragmentView() != null) { + //discard all scrolls/gestures + fragment.getFragmentView().getRootView().dispatchTouchEvent(AndroidUtilities.emptyMotionEvent()); + } + init(); + } + + private ItemOptions(ViewGroup container, Theme.ResourcesProvider resourcesProvider, View scrimView) { + if (container.getContext() == null) { + return; + } + + this.container = container; + this.resourcesProvider = resourcesProvider; + this.context = container.getContext(); + this.scrimView = scrimView; + this.dimAlpha = AndroidUtilities.computePerceivedBrightness(Theme.getColor(Theme.key_windowBackgroundWhite, resourcesProvider)) > .705 ? 0x66 : 0x33; + + init(); + } + + private void init() { + lastLayout = new ActionBarPopupWindow.ActionBarPopupWindowLayout(context, resourcesProvider); + lastLayout.setDispatchKeyEventListener(keyEvent -> { + if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_BACK && keyEvent.getRepeatCount() == 0 && actionBarPopupWindow != null && actionBarPopupWindow.isShowing()) { + actionBarPopupWindow.dismiss(); + } + }); + layout = lastLayout; + } + + public ItemOptions addIf(boolean condition, int iconResId, CharSequence text, Runnable onClickListener) { + if (!condition) { + return this; + } + return add(iconResId, text, onClickListener); + } + + public ItemOptions addIf(boolean condition, int iconResId, CharSequence text, boolean isRed, Runnable onClickListener) { + if (!condition) { + return this; + } + return add(iconResId, text, isRed, onClickListener); + } + + public ItemOptions addIf(boolean condition, int iconResId, CharSequence text, Runnable onClickListener, Consumer onVewCreated) { + if (!condition) { + return this; + } + return add(iconResId, text, Theme.key_actionBarDefaultSubmenuItemIcon, Theme.key_actionBarDefaultSubmenuItem, onClickListener, onVewCreated); + } + + public ItemOptions add(int iconResId, CharSequence text, Runnable onClickListener) { + return add(iconResId, text, false, onClickListener); + } + + public ItemOptions add(int iconResId, CharSequence text, boolean isRed, Runnable onClickListener) { + return add(iconResId, text, isRed ? Theme.key_text_RedRegular : Theme.key_actionBarDefaultSubmenuItemIcon, isRed ? Theme.key_text_RedRegular : Theme.key_actionBarDefaultSubmenuItem, onClickListener, null); + } + + public ItemOptions add(int iconResId, CharSequence text, int color, Runnable onClickListener) { + return add(iconResId, text, color, color, onClickListener, null); + } + + public ItemOptions add(int iconResId, CharSequence text, int iconColorKey, int textColorKey, Runnable onClickListener, Consumer onViewCreated) { + if (context == null) { + return this; + } + + ActionBarMenuSubItem subItem = new ActionBarMenuSubItem(context, false, false, resourcesProvider); + subItem.setPadding(AndroidUtilities.dp(18), 0, AndroidUtilities.dp(18 + (LocaleController.isRTL ? 0 : 8)), 0); + subItem.setTextAndIcon(text, iconResId); + + subItem.setColors(Theme.getColor(textColorKey, resourcesProvider), Theme.getColor(iconColorKey, resourcesProvider)); + subItem.setSelectorColor(Theme.multAlpha(Theme.getColor(textColorKey, resourcesProvider), .12f)); + + subItem.setOnClickListener(view1 -> { + if (actionBarPopupWindow != null) { + actionBarPopupWindow.dismiss(); + } + if (onClickListener != null) { + onClickListener.run(); + } + }); + if (minWidthDp > 0) { + subItem.setMinimumWidth(AndroidUtilities.dp(minWidthDp)); + } + if (onViewCreated != null) { + onViewCreated.accept(subItem); + } + + lastLayout.addView(subItem, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48)); + + return this; + } + + public ItemOptions putPremiumLock(Runnable onLockClick) { + if (onLockClick == null || context == null || lastLayout.getItemsCount() <= 0) { + return this; + } + View lastChild = lastLayout.getItemAt(lastLayout.getItemsCount() - 1); + if (!(lastChild instanceof ActionBarMenuSubItem)) { + return this; + } + ActionBarMenuSubItem lastSubItem = (ActionBarMenuSubItem) lastChild; + lastSubItem.setRightIcon(R.drawable.msg_mini_lock3); + lastSubItem.getRightIcon().setAlpha(.4f); + lastSubItem.setOnClickListener(view1 -> { + if (onLockClick != null) { + onLockClick.run(); + } + }); + return this; + } + + public ItemOptions addGap() { + ActionBarPopupWindow.GapView gap = new ActionBarPopupWindow.GapView(context, resourcesProvider); + gap.setTag(R.id.fit_width_tag, 1); + lastLayout.addView(gap, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 8)); + return this; + } + + public ItemOptions addSpaceGap() { + if (layout == lastLayout) { + layout = new LinearLayout(context); + ((LinearLayout) layout).setOrientation(LinearLayout.VERTICAL); + layout.addView(lastLayout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + } + layout.addView(new View(context), LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 8)); + lastLayout = new ActionBarPopupWindow.ActionBarPopupWindowLayout(context, resourcesProvider); + lastLayout.setDispatchKeyEventListener(keyEvent -> { + if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_BACK && keyEvent.getRepeatCount() == 0 && actionBarPopupWindow != null && actionBarPopupWindow.isShowing()) { + actionBarPopupWindow.dismiss(); + } + }); + layout.addView(lastLayout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + return this; + } + + public ItemOptions addView() { + return this; + } + + public ItemOptions addText(CharSequence text, int textSizeDp) { + final TextView textView = new TextView(context); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, textSizeDp); + textView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider)); + textView.setPadding(AndroidUtilities.dp(13), AndroidUtilities.dp(8), AndroidUtilities.dp(13), AndroidUtilities.dp(8)); + textView.setText(text); + textView.setTag(R.id.fit_width_tag, 1); + textView.setMaxWidth(AndroidUtilities.dp(200)); + lastLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + return this; + } + + public ItemOptions setScrimViewBackground(Drawable drawable) { + this.scrimViewBackground = drawable; + return this; + } + + public ItemOptions setGravity(int gravity) { + this.gravity = gravity; + return this; + } + + public ItemOptions translate(float x, float y) { + this.translateX += x; + this.translateY += y; + return this; + } + + private int minWidthDp; + + public ItemOptions setMinWidth(int minWidthDp) { + this.minWidthDp = minWidthDp; + return this; + } + + public ItemOptions setDimAlpha(int dimAlpha) { + this.dimAlpha = dimAlpha; + return this; + } + + public int getItemsCount() { + if (lastLayout == layout) { + return lastLayout.getItemsCount(); + } else { + int itemsCount = 0; + for (int j = 0; j < layout.getChildCount() - 1; ++j) { + View child = j == layout.getChildCount() - 1 ? lastLayout : layout.getChildAt(j); + if (child instanceof ActionBarPopupWindow.ActionBarPopupWindowLayout) { + ActionBarPopupWindow.ActionBarPopupWindowLayout popupLayout = (ActionBarPopupWindow.ActionBarPopupWindowLayout) child; + itemsCount += popupLayout.getItemsCount(); + } + } + return itemsCount; + } + } + + public ItemOptions show() { + if (actionBarPopupWindow != null) { + return this; + } + + if (getItemsCount() <= 0) { + return this; + } + + for (int j = 0; j < layout.getChildCount() - 1; ++j) { + View child = j == layout.getChildCount() - 1 ? lastLayout : layout.getChildAt(j); + if (child instanceof ActionBarPopupWindow.ActionBarPopupWindowLayout) { + ActionBarPopupWindow.ActionBarPopupWindowLayout popupLayout = (ActionBarPopupWindow.ActionBarPopupWindowLayout) child; + if (popupLayout.getItemsCount() <= 0) { + continue; + } + View first = popupLayout.getItemAt(0), last = popupLayout.getItemAt(popupLayout.getItemsCount() - 1); + if (first instanceof ActionBarMenuSubItem) { + ((ActionBarMenuSubItem) first).updateSelectorBackground(true, first == last); + } + if (last instanceof ActionBarMenuSubItem) { + ((ActionBarMenuSubItem) last).updateSelectorBackground(last == first, true); + } + } + } + + if (minWidthDp > 0) { + for (int j = 0; j < layout.getChildCount() - 1; ++j) { + View child = j == layout.getChildCount() - 1 ? lastLayout : layout.getChildAt(j); + if (child instanceof ActionBarPopupWindow.ActionBarPopupWindowLayout) { + ActionBarPopupWindow.ActionBarPopupWindowLayout popupLayout = (ActionBarPopupWindow.ActionBarPopupWindowLayout) child; + for (int i = 0; i < popupLayout.getItemsCount(); ++i) { + popupLayout.getItemAt(i).setMinimumWidth(AndroidUtilities.dp(minWidthDp)); + } + } + } + } + + ViewGroup container = this.container == null ? fragment.getParentLayout().getOverlayContainerView() : this.container; + + if (context == null || container == null) { + return this; + } + + float x = 0; + float y = AndroidUtilities.displaySize.y / 2f; + if (scrimView != null) { + getPointOnScreen(scrimView, container, point); + y = point[1]; + } + + final Bitmap cachedBitmap; + final Paint cachedBitmapPaint; + if (scrimView instanceof UserCell && fragment instanceof ProfileActivity) { + cachedBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + cachedBitmap = Bitmap.createBitmap(scrimView.getWidth() + viewAdditionalOffsets.width(), scrimView.getHeight() + viewAdditionalOffsets.height(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(cachedBitmap); + canvas.translate(viewAdditionalOffsets.left, viewAdditionalOffsets.top); + scrimView.draw(canvas); + } else { + cachedBitmapPaint = null; + cachedBitmap = null; + } + + final float clipTop; + if (scrimView != null && scrimView.getParent() instanceof View) { + clipTop = ((View) scrimView.getParent()).getY() + scrimView.getY(); + } else { + clipTop = 0; + } + + final int dim = ColorUtils.setAlphaComponent(0x00000000, dimAlpha); + + + View dimViewLocal = dimView = new View(context) { + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + canvas.drawColor(dim); + + if (cachedBitmap != null && scrimView.getParent() instanceof View) { + canvas.save(); + if (clipTop < 1) { + canvas.clipRect(-viewAdditionalOffsets.left, -viewAdditionalOffsets.top + point[1] - clipTop + 1, getMeasuredWidth() + viewAdditionalOffsets.right, getMeasuredHeight() + viewAdditionalOffsets.bottom); + } + canvas.translate(point[0], point[1]); + + if (scrimViewBackground != null) { + scrimViewBackground.setBounds( -viewAdditionalOffsets.left, -viewAdditionalOffsets.top, scrimView.getWidth() + viewAdditionalOffsets.right, scrimView.getHeight() + viewAdditionalOffsets.bottom); + scrimViewBackground.draw(canvas); + } + canvas.drawBitmap(cachedBitmap, -viewAdditionalOffsets.left, -viewAdditionalOffsets.top, cachedBitmapPaint); + canvas.restore(); + } else if (scrimView != null && scrimView.getParent() instanceof View) { + canvas.save(); + if (clipTop < 1) { + canvas.clipRect(-viewAdditionalOffsets.left, -viewAdditionalOffsets.top + point[1] - clipTop + 1, getMeasuredWidth() + viewAdditionalOffsets.right, getMeasuredHeight() + viewAdditionalOffsets.bottom); + } + canvas.translate(point[0], point[1]); + + if (scrimViewBackground != null) { + scrimViewBackground.setBounds( -viewAdditionalOffsets.left, -viewAdditionalOffsets.top, scrimView.getWidth() + viewAdditionalOffsets.right, scrimView.getHeight() + viewAdditionalOffsets.bottom); + scrimViewBackground.draw(canvas); + } + scrimView.draw(canvas); + canvas.restore(); + } + } + }; + + preDrawListener = () -> { + dimViewLocal.invalidate(); + return true; + }; + container.getViewTreeObserver().addOnPreDrawListener(preDrawListener); + container.addView(dimView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + dimView.setAlpha(0); + dimView.animate().alpha(1f).setDuration(150); + layout.measure(View.MeasureSpec.makeMeasureSpec(container.getMeasuredWidth(), View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(container.getMeasuredHeight(), View.MeasureSpec.UNSPECIFIED)); + + actionBarPopupWindow = new ActionBarPopupWindow(layout, LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT) { + @Override + public void dismiss() { + super.dismiss(); + ItemOptions.this.dismissDim(container); + } + }; + actionBarPopupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() { + @Override + public void onDismiss() { + actionBarPopupWindow = null; + dismissDim(container); + } + }); + actionBarPopupWindow.setOutsideTouchable(true); + actionBarPopupWindow.setFocusable(true); + actionBarPopupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); + actionBarPopupWindow.setAnimationStyle(R.style.PopupContextAnimation); + actionBarPopupWindow.setInputMethodMode(ActionBarPopupWindow.INPUT_METHOD_NOT_NEEDED); + actionBarPopupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED); + + if (AndroidUtilities.isTablet()) { + y += container.getPaddingTop(); + x -= container.getPaddingLeft(); + } + int X; + if (scrimView != null) { + if (gravity == Gravity.RIGHT) { + X = (int) (point[0] + scrimView.getMeasuredWidth() - layout.getMeasuredWidth() + container.getX()); + } else { + X = (int) (container.getX() + point[0]); + } + } else { + X = (container.getWidth() - layout.getMeasuredWidth()) / 2; // at the center + } + int Y; + if (scrimView != null) { + if (y + layout.getMeasuredHeight() + AndroidUtilities.dp(16) > AndroidUtilities.displaySize.y) { + // put above scrimView + y -= scrimView.getMeasuredHeight(); + y -= layout.getMeasuredHeight(); + } + Y = (int) (y + scrimView.getMeasuredHeight() + container.getY()); // under scrimView + } else { + Y = (container.getHeight() - layout.getMeasuredHeight()) / 2; // at the center + } + actionBarPopupWindow.showAtLocation( + container, + 0, + (int) (X + this.translateX), + (int) (Y + this.translateY) + ); + return this; + } + + private void dismissDim(ViewGroup container) { + if (dimView == null) { + return; + } + View dimViewFinal = dimView; + dimView = null; + dimViewFinal.animate().cancel(); + dimViewFinal.animate().alpha(0).setDuration(150).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + AndroidUtilities.removeFromParent(dimViewFinal); + container.getViewTreeObserver().removeOnPreDrawListener(preDrawListener); + } + }); + } + + public void updateColors() { + // TODO + } + + public boolean isShown() { + return actionBarPopupWindow != null && actionBarPopupWindow.isShowing(); + } + + public void dismiss() { + if (actionBarPopupWindow != null) { + actionBarPopupWindow.dismiss(); + } + } + + public static void getPointOnScreen(View v, ViewGroup finalContainer, float[] point) { + float x = 0; + float y = 0; + while (v != finalContainer) { + y += v.getY(); + x += v.getX(); + if (v instanceof ScrollView) { + x -= v.getScrollX(); + y -= v.getScrollY(); + } + if (!(v.getParent() instanceof View)) { + break; + } + v = (View) v.getParent(); + if (!(v instanceof ViewGroup)) { + return; + } + } + x -= finalContainer.getPaddingLeft(); + y -= finalContainer.getPaddingTop(); + point[0] = x; + point[1] = y; + } + + public ItemOptions setViewAdditionalOffsets(int left, int top, int right, int bottom) { + viewAdditionalOffsets.set(left, top, right, bottom); + return this; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/JoinCallAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/JoinCallAlert.java index d814ac2113..567f904a1a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/JoinCallAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/JoinCallAlert.java @@ -123,7 +123,7 @@ public BottomSheetCell(Context context, boolean withoutBackground) { background = new View(context); if (hasBackground) { - background.setBackground(Theme.AdaptiveRipple.filledRect(Theme.key_featuredStickers_addButton, 4)); + background.setBackground(Theme.AdaptiveRipple.filledRectByKey(Theme.key_featuredStickers_addButton, 4)); } addView(background, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, 0, 16, withoutBackground ? 0 : 16, 16, 16)); @@ -278,6 +278,10 @@ public static void open(Context context, long did, AccountInstance accountInstan } private static void showAlert(Context context, long dialogId, ArrayList peers, BaseFragment fragment, int type, TLRPC.Peer scheduledPeer, JoinCallAlertDelegate delegate) { + if (type == TYPE_CREATE) { + CreateGroupCallBottomSheet.show(peers, fragment, dialogId, delegate); + return; + } JoinCallAlert alert = new JoinCallAlert(context, dialogId, peers, type, scheduledPeer, delegate); if (fragment != null) { if (fragment.getParentActivity() != null) { @@ -699,7 +703,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType view = new ShareDialogCell(context, ShareDialogCell.TYPE_CREATE, null); view.setLayoutParams(new RecyclerView.LayoutParams(AndroidUtilities.dp(80), AndroidUtilities.dp(100))); } else { - view = new GroupCreateUserCell(context, 2, 0, false, currentType == TYPE_DISPLAY); + view = new GroupCreateUserCell(context, 2, 0, false, currentType == TYPE_DISPLAY, null); } return new RecyclerListView.Holder(view); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/JoinCallByUrlAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/JoinCallByUrlAlert.java index 06927bcd1c..386ffd50a3 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/JoinCallByUrlAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/JoinCallByUrlAlert.java @@ -41,7 +41,7 @@ public BottomSheetCell(Context context) { super(context); background = new View(context); - background.setBackground(Theme.AdaptiveRipple.filledRect(Theme.key_featuredStickers_addButton, 4)); + background.setBackground(Theme.AdaptiveRipple.filledRectByKey(Theme.key_featuredStickers_addButton, 4)); addView(background, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, 0, 16, 16, 16, 16)); textView = new TextView(context); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/JoinGroupAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/JoinGroupAlert.java index 8e5eb0aa8b..b4c1742756 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/JoinGroupAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/JoinGroupAlert.java @@ -14,7 +14,6 @@ import android.util.TypedValue; import android.view.Gravity; import android.view.View; -import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; @@ -34,19 +33,16 @@ import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.BottomSheet; import org.telegram.ui.ActionBar.Theme; -import org.telegram.ui.Cells.JoinSheetUserCell; import org.telegram.ui.ChatActivity; import androidx.core.widget.NestedScrollView; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; public class JoinGroupAlert extends BottomSheet { + private final String hash; + private final BaseFragment fragment; private TLRPC.ChatInvite chatInvite; private TLRPC.Chat currentChat; - private String hash; - private BaseFragment fragment; private TextView requestTextView; private RadialProgressView requestProgressView; @@ -82,15 +78,15 @@ public JoinGroupAlert(final Context context, TLObject obj, String group, BaseFra closeView.setOnClickListener((view) -> dismiss()); int closeViewPadding = AndroidUtilities.dp(8); closeView.setPadding(closeViewPadding, closeViewPadding, closeViewPadding, closeViewPadding); - frameLayout.addView(closeView, LayoutHelper.createFrame(36, 36, Gravity.TOP | Gravity.END, 6, 8, 6, 0)); + frameLayout.addView(closeView, LayoutHelper.createFrame(36, 36, Gravity.TOP | Gravity.END, 6, 8, 8, 0)); String title = null, about = null; - AvatarDrawable avatarDrawable = null; + AvatarDrawable avatarDrawable; int participants_count = 0; BackupImageView avatarImageView = new BackupImageView(context); - avatarImageView.setRoundRadius(AndroidUtilities.dp(35)); - linearLayout.addView(avatarImageView, LayoutHelper.createLinear(70, 70, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, 29, 0, 0)); + avatarImageView.setRoundRadius(AndroidUtilities.dp(45)); + linearLayout.addView(avatarImageView, LayoutHelper.createLinear(90, 90, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, 27, 0, 0)); if (chatInvite != null) { if (chatInvite.chat != null) { @@ -117,29 +113,27 @@ public JoinGroupAlert(final Context context, TLObject obj, String group, BaseFra } TextView textView = new TextView(context); - textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 17); + textView.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); textView.setTextColor(getThemedColor(Theme.key_dialogTextBlack)); textView.setText(title); textView.setSingleLine(true); textView.setEllipsize(TextUtils.TruncateAt.END); - linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 10, 9, 10, participants_count > 0 ? 0 : 20)); + linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 10, 10, 10, participants_count > 0 ? 0 : 20)); final boolean isChannel = chatInvite != null && (chatInvite.channel && !chatInvite.megagroup || ChatObject.isChannelAndNotMegaGroup(chatInvite.chat)) || ChatObject.isChannel(currentChat) && !currentChat.megagroup; boolean hasAbout = !TextUtils.isEmpty(about); - if (participants_count > 0) { - textView = new TextView(context); - textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - textView.setTextColor(getThemedColor(Theme.key_dialogTextGray3)); - textView.setSingleLine(true); - textView.setEllipsize(TextUtils.TruncateAt.END); - if (isChannel) { - textView.setText(LocaleController.formatPluralString("Subscribers", participants_count)); - } else { - textView.setText(LocaleController.formatPluralString("Members", participants_count)); - } - linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 10, 3, 10, hasAbout ? 0 : 20)); - } + + textView = new TextView(context); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); + textView.setTextColor(getThemedColor(Theme.key_dialogTextGray3)); + textView.setSingleLine(true); + textView.setEllipsize(TextUtils.TruncateAt.END); + textView.setText(isChannel + ? LocaleController.getString("ChannelPrivate", R.string.ChannelPrivate).toLowerCase() + : LocaleController.getString("MegaPrivate", R.string.MegaPrivate).toLowerCase() + ); + linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 10, 0, 10, hasAbout ? 0 : 20)); if (hasAbout) { TextView aboutTextView = new TextView(context); @@ -161,14 +155,14 @@ public JoinGroupAlert(final Context context, TLObject obj, String group, BaseFra requestFrameLayout.addView(requestProgressView, LayoutHelper.createFrame(48, 48, Gravity.CENTER)); requestTextView = new TextView(getContext()); - requestTextView.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(6), getThemedColor(Theme.key_featuredStickers_addButton), getThemedColor(Theme.key_featuredStickers_addButtonPressed))); + requestTextView.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(8), getThemedColor(Theme.key_featuredStickers_addButton), getThemedColor(Theme.key_featuredStickers_addButtonPressed))); requestTextView.setEllipsize(TextUtils.TruncateAt.END); requestTextView.setGravity(Gravity.CENTER); requestTextView.setSingleLine(true); requestTextView.setText(isChannel ? LocaleController.getString("RequestToJoinChannel", R.string.RequestToJoinChannel) : LocaleController.getString("RequestToJoinGroup", R.string.RequestToJoinGroup)); requestTextView.setTextColor(getThemedColor(Theme.key_featuredStickers_buttonText)); - requestTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); - requestTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + requestTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + requestTextView.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); requestTextView.setOnClickListener((view) -> { AndroidUtilities.runOnUIThread(() -> { if (!isDismissed()) { @@ -178,20 +172,20 @@ public JoinGroupAlert(final Context context, TLObject obj, String group, BaseFra }, 400); if (chatInvite == null && currentChat != null) { MessagesController.getInstance(currentAccount).addUserToChat( - currentChat.id, - UserConfig.getInstance(currentAccount).getCurrentUser(), - 0, - null, - null, - true, - this::dismiss, - err -> { - if (err != null && "INVITE_REQUEST_SENT".equals(err.text)) { - setOnDismissListener(di -> showBulletin(getContext(), fragment, isChannel)); + currentChat.id, + UserConfig.getInstance(currentAccount).getCurrentUser(), + 0, + null, + null, + true, + this::dismiss, + err -> { + if (err != null && "INVITE_REQUEST_SENT".equals(err.text)) { + setOnDismissListener(di -> showBulletin(getContext(), fragment, isChannel)); + } + dismiss(); + return false; } - dismiss(); - return false; - } ); } else { final TLRPC.TL_messages_importChatInvite request = new TLRPC.TL_messages_importChatInvite(); @@ -213,7 +207,7 @@ public JoinGroupAlert(final Context context, TLObject obj, String group, BaseFra }, ConnectionsManager.RequestFlagFailOnServerErrors); } }); - requestFrameLayout.addView(requestTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48, Gravity.START, 16, 0, 16, 0)); + requestFrameLayout.addView(requestTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48, Gravity.START, 14, 0, 14, 0)); TextView descriptionTextView = new TextView(getContext()); descriptionTextView.setGravity(Gravity.CENTER); @@ -223,38 +217,69 @@ public JoinGroupAlert(final Context context, TLObject obj, String group, BaseFra linearLayout.addView(descriptionTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP, 24, 17, 24, 15)); } else if (chatInvite != null) { if (!chatInvite.participants.isEmpty()) { - RecyclerListView listView = new RecyclerListView(context); - listView.setPadding(0, 0, 0, AndroidUtilities.dp(8)); - listView.setNestedScrollingEnabled(false); - listView.setClipToPadding(false); - listView.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false)); - listView.setHorizontalScrollBarEnabled(false); - listView.setVerticalScrollBarEnabled(false); - listView.setAdapter(new UsersAdapter(context)); - listView.setGlowColor(getThemedColor(Theme.key_dialogScrollGlow)); - linearLayout.addView(listView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 90, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, 0, 0, 7)); - } - - View shadow = new View(context); - shadow.setBackgroundColor(getThemedColor(Theme.key_dialogShadowLine)); - linearLayout.addView(shadow, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, AndroidUtilities.getShadowHeight())); + int participantsCount = chatInvite.participants.size(); + int visibleAvatarsCount = Math.min(participantsCount, 3); + float factor = 0.65f; + int avatarSize = 38; + AvatarsImageView avatarsImageView = new AvatarsImageView(context, false); + avatarsImageView.setAvatarsTextSize(AndroidUtilities.dp(20)); + avatarsImageView.setSize(AndroidUtilities.dp(avatarSize)); + avatarsImageView.setCount(visibleAvatarsCount); + avatarsImageView.setStepFactor(factor); + for (int i = 0; i < visibleAvatarsCount; i++) { + avatarsImageView.setObject(i, UserConfig.selectedAccount, chatInvite.participants.get(i)); + } + avatarsImageView.commitTransition(false); + int avatarContainerWidth = (int) (avatarSize + (visibleAvatarsCount - 1) * (avatarSize * factor + 1)); + linearLayout.addView(avatarsImageView, LayoutHelper.createLinear(avatarContainerWidth, 44, Gravity.CENTER, 0, 2, 0, 4)); + + textView = new TextView(context); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); + textView.setTextColor(getThemedColor(Theme.key_dialogTextGray3)); + textView.setGravity(Gravity.CENTER); + + String txt; + if (visibleAvatarsCount == 1) { + txt = ellipsize(textView, chatInvite, 0).toString(); + } else if (visibleAvatarsCount == 2) { + txt = LocaleController.formatString( + "RequestToJoinMembersTwo", R.string.RequestToJoinMembersTwo, + ellipsize(textView, chatInvite, 0), + ellipsize(textView, chatInvite, 1) + ); + } else if (participants_count == 3) { + txt = LocaleController.formatString( + "RequestToJoinMembersThree", R.string.RequestToJoinMembersThree, + ellipsize(textView, chatInvite, 0), + ellipsize(textView, chatInvite, 1), + ellipsize(textView, chatInvite, 2) + ); + } else { + int count = Math.max(participants_count - visibleAvatarsCount, 2); + txt = String.format( + LocaleController.getPluralString("RequestToJoinMembersAll", count), + ellipsize(textView, chatInvite, 0), + ellipsize(textView, chatInvite, 1), + count + ); + } - PickerBottomLayout pickerBottomLayout = new PickerBottomLayout(context, false, resourcesProvider); - linearLayout.addView(pickerBottomLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT | Gravity.BOTTOM)); - pickerBottomLayout.cancelButton.setPadding(AndroidUtilities.dp(18), 0, AndroidUtilities.dp(18), 0); - pickerBottomLayout.cancelButton.setTextColor(getThemedColor(Theme.key_dialogTextBlue2)); - pickerBottomLayout.cancelButton.setText(LocaleController.getString("Cancel", R.string.Cancel).toUpperCase()); - pickerBottomLayout.cancelButton.setOnClickListener(view -> dismiss()); - pickerBottomLayout.doneButton.setPadding(AndroidUtilities.dp(18), 0, AndroidUtilities.dp(18), 0); - pickerBottomLayout.doneButton.setVisibility(View.VISIBLE); - pickerBottomLayout.doneButtonBadgeTextView.setVisibility(View.GONE); - pickerBottomLayout.doneButtonTextView.setTextColor(getThemedColor(Theme.key_dialogTextBlue2)); - if (chatInvite.channel && !chatInvite.megagroup || ChatObject.isChannel(chatInvite.chat) && !chatInvite.chat.megagroup) { - pickerBottomLayout.doneButtonTextView.setText(LocaleController.getString("ProfileJoinChannel", R.string.ProfileJoinChannel).toUpperCase()); - } else { - pickerBottomLayout.doneButtonTextView.setText(LocaleController.getString("JoinGroup", R.string.JoinGroup)); + textView.setText(txt); + linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 10, 0, 10, 24)); } - pickerBottomLayout.doneButton.setOnClickListener(v -> { + + boolean isJoinToChannel = chatInvite.channel && !chatInvite.megagroup || ChatObject.isChannel(chatInvite.chat) && !chatInvite.chat.megagroup; + TextView joinTextView = new TextView(getContext()); + joinTextView.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(8), getThemedColor(Theme.key_featuredStickers_addButton), getThemedColor(Theme.key_featuredStickers_addButtonPressed))); + joinTextView.setEllipsize(TextUtils.TruncateAt.END); + joinTextView.setGravity(Gravity.CENTER); + joinTextView.setSingleLine(true); + joinTextView.setText(isJoinToChannel ? LocaleController.getString("ProfileJoinChannel", R.string.ProfileJoinChannel) : LocaleController.getString("ProfileJoinGroup", R.string.ProfileJoinGroup)); + joinTextView.setTextColor(getThemedColor(Theme.key_featuredStickers_buttonText)); + joinTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + joinTextView.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + linearLayout.addView(joinTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48, Gravity.START, 14, 0, 14, 14)); + joinTextView.setOnClickListener(v -> { dismiss(); final TLRPC.TL_messages_importChatInvite req = new TLRPC.TL_messages_importChatInvite(); req.hash = hash; @@ -292,6 +317,15 @@ public JoinGroupAlert(final Context context, TLObject obj, String group, BaseFra } public static void showBulletin(Context context, BaseFragment fragment, boolean isChannel) { + if (context == null) { + if (fragment != null) { + context = fragment.getContext(); + } + if (context == null) { + return; + } + return; + } Bulletin.TwoLineLottieLayout layout = new Bulletin.TwoLineLottieLayout(context, fragment.getResourceProvider()); layout.imageView.setAnimation(R.raw.timer_3, 28, 28); layout.titleTextView.setText(LocaleController.getString("RequestToJoinSent", R.string.RequestToJoinSent)); @@ -302,65 +336,11 @@ public static void showBulletin(Context context, BaseFragment fragment, boolean Bulletin.make(fragment, layout, Bulletin.DURATION_LONG).show(); } - private class UsersAdapter extends RecyclerListView.SelectionAdapter { - - private Context context; - - public UsersAdapter(Context context) { - this.context = context; - } - - @Override - public int getItemCount() { - int count = chatInvite.participants.size(); - int participants_count; - if (chatInvite.chat != null) { - participants_count = chatInvite.chat.participants_count; - } else { - participants_count = chatInvite.participants_count; - } - if (count != participants_count) { - count++; - } - return count; - } - - @Override - public long getItemId(int i) { - return i; - } - - @Override - public boolean isEnabled(RecyclerView.ViewHolder holder) { - return false; - } - - @Override - public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - View view = new JoinSheetUserCell(context); - view.setLayoutParams(new RecyclerView.LayoutParams(AndroidUtilities.dp(100), AndroidUtilities.dp(90))); - return new RecyclerListView.Holder(view); - } - - @Override - public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { - JoinSheetUserCell cell = (JoinSheetUserCell) holder.itemView; - if (position < chatInvite.participants.size()) { - cell.setUser(chatInvite.participants.get(position)); - } else { - int participants_count; - if (chatInvite.chat != null) { - participants_count = chatInvite.chat.participants_count; - } else { - participants_count = chatInvite.participants_count; - } - cell.setCount(participants_count - chatInvite.participants.size()); - } - } - - @Override - public int getItemViewType(int i) { - return 0; + private CharSequence ellipsize(TextView textView, TLRPC.ChatInvite chatInvite, int pos) { + String firstName = chatInvite.participants.get(pos).first_name; + if (firstName == null) { + firstName = ""; } + return TextUtils.ellipsize(firstName.trim(), textView.getPaint(), AndroidUtilities.dp(120), TextUtils.TruncateAt.END); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/LineProgressView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/LineProgressView.java index 345edf0140..ba839a9d3e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/LineProgressView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/LineProgressView.java @@ -128,7 +128,6 @@ public void onDraw(Canvas canvas) { } cellFlickerDrawable.setParentWidth(getMeasuredWidth()); cellFlickerDrawable.draw(canvas, rect, getHeight() / 2f, null); - invalidate(); } updateAnimation(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/LinkActionView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/LinkActionView.java index 309f9e24c1..b1e712582a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/LinkActionView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/LinkActionView.java @@ -3,6 +3,7 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.graphics.Canvas; import android.graphics.Color; @@ -69,9 +70,8 @@ public class LinkActionView extends LinearLayout { private QRCodeBottomSheet qrCodeBottomSheet; private boolean hideRevokeOption; private boolean canEdit = true; - private boolean isChannel; - - float[] point = new float[2]; + private final boolean isChannel; + private final float[] point = new float[2]; public LinkActionView(Context context, BaseFragment fragment, BottomSheet bottomSheet, long chatId, boolean permanent, boolean isChannel) { super(context); @@ -82,72 +82,71 @@ public LinkActionView(Context context, BaseFragment fragment, BottomSheet bottom setOrientation(VERTICAL); frameLayout = new FrameLayout(context); linkView = new TextView(context); - linkView.setPadding(AndroidUtilities.dp(20), AndroidUtilities.dp(18), AndroidUtilities.dp(40), AndroidUtilities.dp(18)); + linkView.setPadding(AndroidUtilities.dp(18), AndroidUtilities.dp(13), AndroidUtilities.dp(40), AndroidUtilities.dp(13)); linkView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); linkView.setEllipsize(TextUtils.TruncateAt.MIDDLE); linkView.setSingleLine(true); + int containerPadding = 4; frameLayout.addView(linkView); optionsView = new ImageView(context); optionsView.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_ab_other)); optionsView.setContentDescription(LocaleController.getString("AccDescrMoreOptions", R.string.AccDescrMoreOptions)); optionsView.setScaleType(ImageView.ScaleType.CENTER); frameLayout.addView(optionsView, LayoutHelper.createFrame(40, 48, Gravity.RIGHT | Gravity.CENTER_VERTICAL)); - addView(frameLayout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 4, 0, 4, 0)); + addView(frameLayout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, containerPadding, 0, containerPadding, 0)); LinearLayout linearLayout = new LinearLayout(context); linearLayout.setOrientation(HORIZONTAL); copyView = new TextView(context); - copyView.setGravity(Gravity.CENTER_HORIZONTAL); + copyView.setGravity(Gravity.CENTER); SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(); - spannableStringBuilder.append("..").setSpan(new ColoredImageSpan(ContextCompat.getDrawable(context, R.drawable.msg_copy_filled), Theme.getColor(Theme.key_chats_actionIcon), ColoredImageSpan.ALIGN_DEFAULT), 0, 1, 0); - spannableStringBuilder.setSpan(new DialogCell.FixedWidthSpan(AndroidUtilities.dp(8)), 1, 2, 0); + spannableStringBuilder.append("..").setSpan(new ColoredImageSpan(ContextCompat.getDrawable(context, R.drawable.msg_copy_filled)), 0, 1, 0); + spannableStringBuilder.setSpan(new DialogCell.FixedWidthSpan(AndroidUtilities.dp(6)), 1, 2, 0); spannableStringBuilder.append(LocaleController.getString("LinkActionCopy", R.string.LinkActionCopy)); - spannableStringBuilder.append(".").setSpan(new DialogCell.FixedWidthSpan(AndroidUtilities.dp(5)), spannableStringBuilder.length() - 1, spannableStringBuilder.length(), 0); copyView.setText(spannableStringBuilder); copyView.setContentDescription(LocaleController.getString("LinkActionCopy", R.string.LinkActionCopy)); - copyView.setPadding(AndroidUtilities.dp(10), AndroidUtilities.dp(10), AndroidUtilities.dp(10), AndroidUtilities.dp(10)); + copyView.setPadding(AndroidUtilities.dp(8), 0, AndroidUtilities.dp(8), 0); copyView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - copyView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + copyView.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); copyView.setSingleLine(true); - linearLayout.addView(copyView, LayoutHelper.createLinear(0, 40, 1f, 0, 4, 0, 4, 0)); + linearLayout.addView(copyView, LayoutHelper.createLinear(0, 42, 1f, 0, containerPadding, 0, 4, 0)); shareView = new TextView(context); - shareView.setGravity(Gravity.CENTER_HORIZONTAL); + shareView.setGravity(Gravity.CENTER); spannableStringBuilder = new SpannableStringBuilder(); - spannableStringBuilder.append("..").setSpan(new ColoredImageSpan(ContextCompat.getDrawable(context, R.drawable.msg_share_filled), Theme.getColor(Theme.key_chats_actionIcon), ColoredImageSpan.ALIGN_DEFAULT), 0, 1, 0); - spannableStringBuilder.setSpan(new DialogCell.FixedWidthSpan(AndroidUtilities.dp(8)), 1, 2, 0); + spannableStringBuilder.append("..").setSpan(new ColoredImageSpan(ContextCompat.getDrawable(context, R.drawable.msg_share_filled)), 0, 1, 0); + spannableStringBuilder.setSpan(new DialogCell.FixedWidthSpan(AndroidUtilities.dp(6)), 1, 2, 0); spannableStringBuilder.append(LocaleController.getString("LinkActionShare", R.string.LinkActionShare)); - spannableStringBuilder.append(".").setSpan(new DialogCell.FixedWidthSpan(AndroidUtilities.dp(5)), spannableStringBuilder.length() - 1, spannableStringBuilder.length(), 0); shareView.setText(spannableStringBuilder); shareView.setContentDescription(LocaleController.getString("LinkActionShare", R.string.LinkActionShare)); - shareView.setPadding(AndroidUtilities.dp(10), AndroidUtilities.dp(10), AndroidUtilities.dp(10), AndroidUtilities.dp(10)); - + shareView.setPadding(AndroidUtilities.dp(8), 0, AndroidUtilities.dp(8), 0); shareView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - shareView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + shareView.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); shareView.setSingleLine(true); - linearLayout.addView(shareView, LayoutHelper.createLinear(0, 40, 1f, 4, 0, 4, 0)); + linearLayout.addView(shareView, LayoutHelper.createLinear(0, 42, 1f, 4, 0, containerPadding, 0)); removeView = new TextView(context); - removeView.setGravity(Gravity.CENTER_HORIZONTAL); + removeView.setGravity(Gravity.CENTER); spannableStringBuilder = new SpannableStringBuilder(); spannableStringBuilder.append("..").setSpan(new ColoredImageSpan(ContextCompat.getDrawable(context, R.drawable.msg_delete_filled)), 0, 1, 0); spannableStringBuilder.setSpan(new DialogCell.FixedWidthSpan(AndroidUtilities.dp(8)), 1, 2, 0); spannableStringBuilder.append(LocaleController.getString("DeleteLink", R.string.DeleteLink)); spannableStringBuilder.append(".").setSpan(new DialogCell.FixedWidthSpan(AndroidUtilities.dp(5)), spannableStringBuilder.length() - 1, spannableStringBuilder.length(), 0); removeView.setText(spannableStringBuilder); - removeView.setPadding(AndroidUtilities.dp(10), AndroidUtilities.dp(10), AndroidUtilities.dp(10), AndroidUtilities.dp(10)); + removeView.setPadding(AndroidUtilities.dp(8), 0, AndroidUtilities.dp(8), 0); removeView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - removeView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + removeView.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); removeView.setSingleLine(true); - linearLayout.addView(removeView, LayoutHelper.createLinear(0, LayoutHelper.WRAP_CONTENT, 1f, 4, 0, 4, 0)); + linearLayout.addView(removeView, LayoutHelper.createLinear(0, 42, 1f, containerPadding, 0, containerPadding, 0)); removeView.setVisibility(View.GONE); - addView(linearLayout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 20, 0, 0)); + addView(linearLayout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 12, 0, 0)); avatarsContainer = new AvatarsContainer(context); + avatarsContainer.avatarsImageView.setAvatarsTextSize(AndroidUtilities.dp(18)); addView(avatarsContainer, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 28 + 16, 0, 12, 0, 0)); copyView.setOnClickListener(view -> { try { @@ -229,7 +228,7 @@ public LinkActionView(Context context, BaseFragment fragment, BottomSheet bottom if (!hideRevokeOption) { subItem = new ActionBarMenuSubItem(context, false, true); subItem.setTextAndIcon(LocaleController.getString("RevokeLink", R.string.RevokeLink), R.drawable.msg_delete); - subItem.setColors(Theme.getColor(Theme.key_windowBackgroundWhiteRedText), Theme.getColor(Theme.key_windowBackgroundWhiteRedText)); + subItem.setColors(Theme.getColor(Theme.key_text_RedRegular), Theme.getColor(Theme.key_text_RedRegular)); subItem.setOnClickListener(view1 -> { if (actionBarPopupWindow != null) { actionBarPopupWindow.dismiss(); @@ -358,8 +357,13 @@ private void getPointOnScreen(FrameLayout frameLayout, ViewGroup finalContainer, point[1] = y; } + private String qrText; + public void setQrText(String text) { + qrText = text; + } + private void showQrCode() { - qrCodeBottomSheet = new QRCodeBottomSheet(getContext(), LocaleController.getString("InviteByQRCode", R.string.InviteByQRCode), link, isChannel ? LocaleController.getString("QRCodeLinkHelpChannel", R.string.QRCodeLinkHelpChannel) : LocaleController.getString("QRCodeLinkHelpGroup", R.string.QRCodeLinkHelpGroup), false) { + qrCodeBottomSheet = new QRCodeBottomSheet(getContext(), LocaleController.getString("InviteByQRCode", R.string.InviteByQRCode), link, qrText == null ? (isChannel ? LocaleController.getString("QRCodeLinkHelpChannel", R.string.QRCodeLinkHelpChannel) : LocaleController.getString("QRCodeLinkHelpGroup", R.string.QRCodeLinkHelpGroup)) : qrText, false) { @Override public void dismiss() { super.dismiss(); @@ -377,10 +381,10 @@ public void updateColors() { copyView.setTextColor(Theme.getColor(Theme.key_featuredStickers_buttonText)); shareView.setTextColor(Theme.getColor(Theme.key_featuredStickers_buttonText)); removeView.setTextColor(Theme.getColor(Theme.key_featuredStickers_buttonText)); - copyView.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(6), Theme.getColor(Theme.key_featuredStickers_addButton), Theme.getColor(Theme.key_featuredStickers_addButtonPressed))); - shareView.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(6), Theme.getColor(Theme.key_featuredStickers_addButton), Theme.getColor(Theme.key_featuredStickers_addButtonPressed))); - removeView.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(6), Theme.getColor(Theme.key_chat_attachAudioBackground), ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_windowBackgroundWhite), 120))); - frameLayout.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(6), Theme.getColor(Theme.key_graySection), ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_listSelector), (int) (255 * 0.3f)))); + copyView.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(8), Theme.getColor(Theme.key_featuredStickers_addButton), Theme.getColor(Theme.key_featuredStickers_addButtonPressed))); + shareView.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(8), Theme.getColor(Theme.key_featuredStickers_addButton), Theme.getColor(Theme.key_featuredStickers_addButtonPressed))); + removeView.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(8), Theme.getColor(Theme.key_chat_attachAudioBackground), ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_windowBackgroundWhite), 120))); + frameLayout.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(8), Theme.getColor(Theme.key_graySection), ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_listSelector), (int) (255 * 0.3f)))); linkView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); optionsView.setColorFilter(Theme.getColor(Theme.key_dialogTextGray3)); //optionsView.setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_listSelector), 1)); @@ -469,14 +473,19 @@ private void revokeLink() { return; } AlertDialog.Builder builder = new AlertDialog.Builder(fragment.getParentActivity()); - builder.setMessage(LocaleController.getString("RevokeAlert", R.string.RevokeAlert)); builder.setTitle(LocaleController.getString("RevokeLink", R.string.RevokeLink)); + builder.setMessage(LocaleController.getString("RevokeAlert", R.string.RevokeAlert)); builder.setPositiveButton(LocaleController.getString("RevokeButton", R.string.RevokeButton), (dialogInterface, i) -> { if (delegate != null) { delegate.revokeLink(); } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + AlertDialog dialog = builder.create(); + TextView button = (TextView) dialog.getButton(DialogInterface.BUTTON_POSITIVE); + if (button != null) { + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); + } builder.show(); } @@ -485,6 +494,10 @@ public void setDelegate(Delegate delegate) { } public void setUsers(int usersCount, ArrayList importers) { + setUsers(usersCount, importers, false); + } + + public void setUsers(int usersCount, ArrayList importers, boolean animated) { this.usersCount = usersCount; if (usersCount == 0) { avatarsContainer.setVisibility(View.GONE); @@ -496,48 +509,59 @@ public void setUsers(int usersCount, ArrayList importers) { avatarsContainer.requestLayout(); } if (importers != null) { - for (int i = 0; i < 3; i++) { - if (i < importers.size()) { - MessagesController.getInstance(UserConfig.selectedAccount).putUser(importers.get(i), false); - avatarsContainer.avatarsImageView.setObject(i, UserConfig.selectedAccount, importers.get(i)); - } else { - avatarsContainer.avatarsImageView.setObject(i, UserConfig.selectedAccount, null); - } + for (int i = 0; i < importers.size(); ++i) { + MessagesController.getInstance(UserConfig.selectedAccount).putUser(importers.get(i), false); + } + + final int count = Math.min(3, Math.min(usersCount, importers.size())); + avatarsContainer.avatarsImageView.setCount(count); + for (int i = 0; i < count; ++i) { + avatarsContainer.avatarsImageView.setObject(i, UserConfig.selectedAccount, importers.get(i)); } - avatarsContainer.avatarsImageView.commitTransition(false); + } else { + avatarsContainer.avatarsImageView.setCount(0); } + avatarsContainer.avatarsImageView.commitTransition(animated); } + private String loadedInviteLink; + public void loadUsers(TLRPC.TL_chatInviteExported invite, long chatId) { if (invite == null) { - setUsers(0, null); + setUsers(0, null, false); return; } - setUsers(invite.usage, invite.importers); - if (invite.usage > 0 && invite.importers == null && !loadingImporters) { - TLRPC.TL_messages_getChatInviteImporters req = new TLRPC.TL_messages_getChatInviteImporters(); - req.link = invite.link; - req.peer = MessagesController.getInstance(UserConfig.selectedAccount).getInputPeer(-chatId); - req.offset_user = new TLRPC.TL_inputUserEmpty(); - req.limit = Math.min(invite.usage, 3); - - loadingImporters = true; - ConnectionsManager.getInstance(UserConfig.selectedAccount).sendRequest(req, (response, error) -> { - AndroidUtilities.runOnUIThread(() -> { - loadingImporters = false; - if (error == null) { - TLRPC.TL_messages_chatInviteImporters inviteImporters = (TLRPC.TL_messages_chatInviteImporters) response; - if (invite.importers == null) { - invite.importers = new ArrayList<>(3); - } - invite.importers.clear(); - for (int i = 0; i < inviteImporters.users.size(); i++) { - invite.importers.addAll(inviteImporters.users); + if (!TextUtils.equals(loadedInviteLink, invite.link)) { + setUsers(invite.usage, invite.importers, false); + if (invite.usage > 0 && invite.importers == null && !loadingImporters) { + TLRPC.TL_messages_getChatInviteImporters req = new TLRPC.TL_messages_getChatInviteImporters(); + if (invite.link != null) { + req.flags |= 2; + req.link = invite.link; + } + req.peer = MessagesController.getInstance(UserConfig.selectedAccount).getInputPeer(-chatId); + req.offset_user = new TLRPC.TL_inputUserEmpty(); + req.limit = Math.min(invite.usage, 3); + + loadingImporters = true; + ConnectionsManager.getInstance(UserConfig.selectedAccount).sendRequest(req, (response, error) -> { + AndroidUtilities.runOnUIThread(() -> { + loadingImporters = false; + loadedInviteLink = invite.link; + if (error == null) { + TLRPC.TL_messages_chatInviteImporters inviteImporters = (TLRPC.TL_messages_chatInviteImporters) response; + if (invite.importers == null) { + invite.importers = new ArrayList<>(3); + } + invite.importers.clear(); + for (int i = 0; i < inviteImporters.users.size(); i++) { + invite.importers.addAll(inviteImporters.users); + } + setUsers(invite.usage, invite.importers, true); } - setUsers(invite.usage, invite.importers); - } + }); }); - }); + } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/LinkSpanDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/LinkSpanDrawable.java index 2121c112b0..9c5ea76175 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/LinkSpanDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/LinkSpanDrawable.java @@ -480,9 +480,7 @@ private void invalidate(Object obj, boolean tryParent) { ((View) obj).invalidate(); } else if (obj instanceof ArticleViewer.DrawingText) { ArticleViewer.DrawingText text = (ArticleViewer.DrawingText) obj; - if (text.latestParentView != null) { - text.latestParentView.invalidate(); - } + text.invalidateParent(); } else if (tryParent && mParent != null) { mParent.invalidate(); } @@ -554,7 +552,7 @@ public ClickableSpan hit(int x, int y) { y -= getPaddingTop(); final int line = textLayout.getLineForVertical(y); final int off = textLayout.getOffsetForHorizontal(line, x); - final float left = getLayout().getLineLeft(line); + final float left = textLayout.getLineLeft(line); if (left <= x && left + textLayout.getLineWidth(line) >= x && y >= 0 && y <= textLayout.getHeight()) { Spannable buffer = new SpannableString(textLayout.getText()); ClickableSpan[] spans = buffer.getSpans(off, off, ClickableSpan.class); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/LoadingDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/LoadingDrawable.java index 0b72cd7e45..9b879abcd1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/LoadingDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/LoadingDrawable.java @@ -20,6 +20,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.google.android.exoplayer2.util.Log; + import org.telegram.messenger.AndroidUtilities; import org.telegram.ui.ActionBar.Theme; @@ -34,7 +36,7 @@ public LoadingDrawable(Theme.ResourcesProvider resourcesProvider) { this.resourcesProvider = resourcesProvider; } - public LoadingDrawable(String colorKey1, String colorKey2, Theme.ResourcesProvider resourcesProvider) { + public LoadingDrawable(int colorKey1, int colorKey2, Theme.ResourcesProvider resourcesProvider) { this(); this.colorKey1 = colorKey1; this.colorKey2 = colorKey2; @@ -93,8 +95,8 @@ public long timeToDisappear() { private Matrix matrix = new Matrix(), strokeMatrix = new Matrix(); private int gradientColor1, gradientColor2; private int gradientStrokeColor1, gradientStrokeColor2; - public String colorKey1 = Theme.key_dialogBackground; - public String colorKey2 = Theme.key_dialogBackgroundGray; + public int colorKey1 = Theme.key_dialogBackground; + public int colorKey2 = Theme.key_dialogBackgroundGray; public boolean stroke; public Integer backgroundColor, color1, color2, strokeColor1, strokeColor2; private int gradientWidth; @@ -171,8 +173,27 @@ public void setRadii(float topLeft, float topRight, float bottomRight, float bot } } + public void setRadii(float[] radii) { + if (radii == null || radii.length != 8) { + return; + } + boolean changed = false; + for (int i = 0; i < 8; ++i) { + if (this.radii[i] != radii[i]) { + this.radii[i] = radii[i]; + changed = true; + } + } + if (lastBounds != null && changed) { + path.rewind(); + rectF.set(lastBounds); + path.addRoundRect(rectF, radii, Path.Direction.CW); + } + } + public void setBounds(@NonNull RectF bounds) { super.setBounds((int) bounds.left, (int) bounds.top, (int) bounds.right, (int) bounds.bottom); + lastBounds = null; } public void reset() { @@ -235,6 +256,7 @@ public void draw(@NonNull Canvas canvas) { float appearT = (now - start) / APPEAR_DURATION; float disappearT = disappearStart > 0 ? 1f - CubicBezierInterpolator.EASE_OUT.getInterpolation(Math.min(1, (now - disappearStart) / DISAPPEAR_DURATION)) : 0; + boolean disappearRestore = false; if (isDisappearing()) { int disappearGradientWidthNow = Math.max(AndroidUtilities.dp(200), bounds.width() / 3); @@ -255,8 +277,10 @@ public void draw(@NonNull Canvas canvas) { rectF.set(bounds); rectF.inset(-strokePaint.getStrokeWidth(), -strokePaint.getStrokeWidth()); canvas.saveLayerAlpha(rectF, 255, Canvas.ALL_SAVE_FLAG); + disappearRestore = true; } } + boolean appearRestore = false; if (appearByGradient) { int appearGradientWidthNow = Math.max(AndroidUtilities.dp(200), bounds.width() / 3); @@ -277,6 +301,7 @@ public void draw(@NonNull Canvas canvas) { rectF.set(bounds); rectF.inset(-strokePaint.getStrokeWidth(), -strokePaint.getStrokeWidth()); canvas.saveLayerAlpha(rectF, 255, Canvas.ALL_SAVE_FLAG); + appearRestore = true; } } @@ -305,23 +330,23 @@ public void draw(@NonNull Canvas canvas) { canvas.drawPath(drawPath, strokePaint); } - if (isDisappearing() && disappearT < 1) { + if (appearRestore) { canvas.save(); - float appearOffset = disappearT * (disappearGradientWidth + bounds.width() + disappearGradientWidth) - disappearGradientWidth; - disappearMatrix.setTranslate(bounds.right - appearOffset, 0); - disappearGradient.setLocalMatrix(disappearMatrix); + float appearOffset = appearT * (appearGradientWidth + bounds.width() + appearGradientWidth) - appearGradientWidth; + appearMatrix.setTranslate(bounds.left + appearOffset, 0); + appearGradient.setLocalMatrix(appearMatrix); int inset = (int) strokePaint.getStrokeWidth(); - canvas.drawRect(bounds.left - inset, bounds.top - inset, bounds.right + inset, bounds.bottom + inset, disappearPaint); + canvas.drawRect(bounds.left - inset, bounds.top - inset, bounds.right + inset, bounds.bottom + inset, appearPaint); canvas.restore(); canvas.restore(); } - if (appearByGradient && appearT < 1) { + if (disappearRestore) { canvas.save(); - float appearOffset = appearT * (appearGradientWidth + bounds.width() + appearGradientWidth) - appearGradientWidth; - appearMatrix.setTranslate(bounds.left + appearOffset, 0); - appearGradient.setLocalMatrix(appearMatrix); + float appearOffset = disappearT * (disappearGradientWidth + bounds.width() + disappearGradientWidth) - disappearGradientWidth; + disappearMatrix.setTranslate(bounds.right - appearOffset, 0); + disappearGradient.setLocalMatrix(disappearMatrix); int inset = (int) strokePaint.getStrokeWidth(); - canvas.drawRect(bounds.left - inset, bounds.top - inset, bounds.right + inset, bounds.bottom + inset, appearPaint); + canvas.drawRect(bounds.left - inset, bounds.top - inset, bounds.right + inset, bounds.bottom + inset, disappearPaint); canvas.restore(); canvas.restore(); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/LoadingSpan.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/LoadingSpan.java index 27db8b7ca3..4d9d2dbb16 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/LoadingSpan.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/LoadingSpan.java @@ -24,19 +24,23 @@ public LoadingSpan(View view, int size) { } public LoadingSpan(View view, int size, int yOffset) { + this(view, size, yOffset, null); + } + + public LoadingSpan(View view, int size, int yOffset, Theme.ResourcesProvider resourcesProvider) { this.view = view; this.size = size; this.yOffset = yOffset; - this.drawable = new LoadingDrawable(null); + this.drawable = new LoadingDrawable(resourcesProvider); this.drawable.setRadiiDp(4); } - public void setColorKeys(String colorKey1, String colorKey2) { + public void setColorKeys(int colorKey1, int colorKey2) { this.drawable.colorKey1 = colorKey1; this.drawable.colorKey2 = colorKey2; } - public void setColorKeys(String colorKey1, String colorKey2, Theme.ResourcesProvider resourcesProvider) { + public void setColorKeys(int colorKey1, int colorKey2, Theme.ResourcesProvider resourcesProvider) { this.drawable.resourcesProvider = resourcesProvider; this.drawable.colorKey1 = colorKey1; this.drawable.colorKey2 = colorKey2; @@ -53,11 +57,13 @@ public void setView(View view) { @Override public int getSize(@NonNull Paint paint, CharSequence charSequence, int i, int i1, @Nullable Paint.FontMetricsInt fontMetricsInt) { - if (paint != null) { + if (paint != null && this.drawable.color1 == null && this.drawable.color2 == null) { drawable.setColors( Theme.multAlpha(paint.getColor(), .1f), Theme.multAlpha(paint.getColor(), .25f) ); + } + if (paint != null) { drawable.setAlpha(paint.getAlpha()); } return size; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/LoadingStickerDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/LoadingStickerDrawable.java index 7260f63a8d..4d73c3d4f1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/LoadingStickerDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/LoadingStickerDrawable.java @@ -39,7 +39,7 @@ public LoadingStickerDrawable(View parent, String svg, int w, int h) { placeholderMatrix = new Matrix(); } - public void setColors(String key1, String key2) { + public void setColors(int key1, int key2) { int color0 = Theme.getColor(key1); int color1 = Theme.getColor(key2); if (currentColor0 != color0 || currentColor1 != color1) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/MediaActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/MediaActivity.java index 11885b692a..428f59f09b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/MediaActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/MediaActivity.java @@ -1,35 +1,71 @@ package org.telegram.ui.Components; +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.AndroidUtilities.dpf2; +import static org.telegram.messenger.AndroidUtilities.lerp; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.StateListAnimator; +import android.animation.ValueAnimator; +import android.annotation.SuppressLint; import android.content.Context; +import android.content.DialogInterface; import android.content.res.Configuration; import android.graphics.Canvas; +import android.graphics.Outline; import android.graphics.Paint; import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.RippleDrawable; import android.os.Build; import android.os.Bundle; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; import android.text.TextUtils; +import android.util.SparseArray; import android.util.TypedValue; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; +import android.view.ViewOutlineProvider; import android.view.accessibility.AccessibilityNodeInfo; +import android.view.animation.AccelerateDecelerateInterpolator; import android.widget.FrameLayout; +import android.widget.ImageView; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; import androidx.core.graphics.ColorUtils; +import org.checkerframework.checker.units.qual.Angle; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.BotWebViewVibrationEffect; import org.telegram.messenger.ContactsController; import org.telegram.messenger.DialogObject; import org.telegram.messenger.ImageLocation; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MediaDataController; +import org.telegram.messenger.MessageObject; import org.telegram.messenger.MessagesController; +import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; +import org.telegram.messenger.UserObject; +import org.telegram.messenger.Utilities; import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.ActionBar; +import org.telegram.ui.ActionBar.ActionBarMenu; +import org.telegram.ui.ActionBar.ActionBarMenuItem; +import org.telegram.ui.ActionBar.ActionBarMenuSubItem; +import org.telegram.ui.ActionBar.AlertDialog; import org.telegram.ui.ActionBar.BackDrawable; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.SimpleTextView; @@ -39,21 +75,50 @@ import org.telegram.ui.Components.FloatingDebug.FloatingDebugProvider; import org.telegram.ui.Components.Paint.ShapeDetector; import org.telegram.ui.ProfileActivity; +import org.telegram.ui.Stories.recorder.ButtonWithCounterView; +import org.telegram.ui.Stories.recorder.StoryRecorder; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -public class MediaActivity extends BaseFragment implements SharedMediaLayout.SharedMediaPreloaderDelegate, FloatingDebugProvider { +public class MediaActivity extends BaseFragment implements SharedMediaLayout.SharedMediaPreloaderDelegate, FloatingDebugProvider, NotificationCenter.NotificationCenterDelegate { + + public static final int TYPE_MEDIA = 0; + public static final int TYPE_STORIES = 1; + + private int type; private SharedMediaLayout.SharedMediaPreloader sharedMediaPreloader; private TLRPC.ChatFull currentChatInfo; + private TLRPC.UserFull currentUserInfo; private long dialogId; - private SimpleTextView nameTextView; + private FrameLayout titlesContainer; + private FrameLayout[] titles = new FrameLayout[2]; + private SimpleTextView[] nameTextView = new SimpleTextView[2]; + private AnimatedTextView[] subtitleTextView = new AnimatedTextView[2]; ProfileActivity.AvatarImageView avatarImageView; + private BackDrawable backDrawable; + private AnimatedTextView selectedTextView; + private ActionBarMenuItem optionsItem; + private ActionBarMenuItem deleteItem; + private SparseArray actionModeMessageObjects; + private ActionBarMenuSubItem showPhotosItem, showVideosItem; + private boolean filterPhotos = true, filterVideos = true; + private int shiftDp = -12; + private ActionBarMenuSubItem calendarItem, zoomInItem, zoomOutItem; + + private ImageView floatingButton; + private FrameLayout floatingButtonContainer; + + private StoriesTabsView tabsView; + private FrameLayout buttonContainer; + private ButtonWithCounterView button; + + private Runnable applyBulletin; SharedMediaLayout sharedMediaLayout; - AudioPlayerAlert.ClippingTextViewSwitcher mediaCounterTextView; + private int initialTab; public MediaActivity(Bundle args, SharedMediaLayout.SharedMediaPreloader sharedMediaPreloader) { super(args); @@ -62,7 +127,19 @@ public MediaActivity(Bundle args, SharedMediaLayout.SharedMediaPreloader sharedM @Override public boolean onFragmentCreate() { + type = getArguments().getInt("type", TYPE_MEDIA); dialogId = getArguments().getLong("dialog_id"); + initialTab = getArguments().getInt("start_from", type == TYPE_MEDIA ? SharedMediaLayout.TAB_PHOTOVIDEO : SharedMediaLayout.TAB_STORIES); + getNotificationCenter().addObserver(this, NotificationCenter.userInfoDidLoad); + getNotificationCenter().addObserver(this, NotificationCenter.currentUserPremiumStatusChanged); + getNotificationCenter().addObserver(this, NotificationCenter.storiesEnabledUpdate); + if (DialogObject.isUserDialog(dialogId)) { + TLRPC.User user = getMessagesController().getUser(dialogId); + if (UserObject.isUserSelf(user)) { + getMessagesController().loadUserInfo(user, false, this.classGuid); + currentUserInfo = getMessagesController().getUserFull(dialogId); + } + } if (this.sharedMediaPreloader == null) { this.sharedMediaPreloader = new SharedMediaLayout.SharedMediaPreloader(this); this.sharedMediaPreloader.addDelegate(this); @@ -70,16 +147,76 @@ public boolean onFragmentCreate() { return super.onFragmentCreate(); } + @Override + public void onFragmentDestroy() { + super.onFragmentDestroy(); + getNotificationCenter().removeObserver(this, NotificationCenter.userInfoDidLoad); + getNotificationCenter().removeObserver(this, NotificationCenter.currentUserPremiumStatusChanged); + getNotificationCenter().removeObserver(this, NotificationCenter.storiesEnabledUpdate); + } + + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.userInfoDidLoad) { + long uid = (long) args[0]; + if (uid == dialogId) { + currentUserInfo = (TLRPC.UserFull) args[1]; + if (sharedMediaLayout != null) { + sharedMediaLayout.setUserInfo(currentUserInfo); + } + } + } else if (id == NotificationCenter.currentUserPremiumStatusChanged || id == NotificationCenter.storiesEnabledUpdate) { + if (!getMessagesController().storiesEnabled()) { + hideFloatingButton(true, true); + } + } + } + @Override public View createView(Context context) { - actionBar.setBackButtonDrawable(new BackDrawable(false)); + actionBar.setBackButtonDrawable(backDrawable = new BackDrawable(false)); + backDrawable.setAnimationTime(240); actionBar.setCastShadows(false); actionBar.setAddToContainer(false); actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { @Override public void onItemClick(int id) { if (id == -1) { + if (sharedMediaLayout.closeActionMode(true)) { + return; + } finishFragment(); + } else if (id == 2) { + if (actionModeMessageObjects != null) { + ArrayList storyItems = new ArrayList<>(); + for (int i = 0; i < actionModeMessageObjects.size(); ++i) { + MessageObject messageObject = actionModeMessageObjects.valueAt(i); + if (messageObject.storyItem != null) { + storyItems.add(messageObject.storyItem); + } + } + + if (!storyItems.isEmpty()) { + AlertDialog.Builder builder = new AlertDialog.Builder(getContext(), getResourceProvider()); + builder.setTitle(storyItems.size() > 1 ? LocaleController.getString("DeleteStoriesTitle", R.string.DeleteStoriesTitle) : LocaleController.getString("DeleteStoryTitle", R.string.DeleteStoryTitle)); + builder.setMessage(LocaleController.formatPluralString("DeleteStoriesSubtitle", storyItems.size())); + builder.setPositiveButton(LocaleController.getString("Delete", R.string.Delete), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + getMessagesController().getStoriesController().deleteStories(storyItems); + sharedMediaLayout.closeActionMode(false); + } + }); + builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), (DialogInterface.OnClickListener) (dialog, which) -> { + dialog.dismiss(); + }); + AlertDialog dialog = builder.create(); + dialog.show(); + dialog.redPositive(); + } + } + } else if (id == 10) { + sharedMediaLayout.showMediaCalendar(sharedMediaLayout.getClosestTab(), false); } } }); @@ -96,16 +233,23 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { lp.topMargin = actionBar.getOccupyStatusBar() ? AndroidUtilities.statusBarHeight : 0; lp.height = ActionBar.getCurrentActionBarHeight(); - int textTop = (ActionBar.getCurrentActionBarHeight() / 2 - AndroidUtilities.dp(22)) / 2 + AndroidUtilities.dp(!AndroidUtilities.isTablet() && getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE ? 4 : 5); - lp = (LayoutParams) nameTextView.getLayoutParams(); - lp.topMargin = textTop; + int textTop; + for (int i = 0; i < 2; ++i) { + if (nameTextView[i] != null) { + textTop = (ActionBar.getCurrentActionBarHeight() / 2 - dp(22)) / 2 + dp(!AndroidUtilities.isTablet() && getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE ? 4 : 5); + lp = (LayoutParams) nameTextView[i].getLayoutParams(); + lp.topMargin = textTop; + } - textTop = ActionBar.getCurrentActionBarHeight() / 2 + (ActionBar.getCurrentActionBarHeight() / 2 - AndroidUtilities.dp(19)) / 2 - AndroidUtilities.dp(3); - lp = (LayoutParams) mediaCounterTextView.getLayoutParams(); - lp.topMargin = textTop; + if (subtitleTextView[i] != null) { + textTop = ActionBar.getCurrentActionBarHeight() / 2 + (ActionBar.getCurrentActionBarHeight() / 2 - dp(19)) / 2 - dp(3 + 4); + lp = (LayoutParams) subtitleTextView[i].getLayoutParams(); + lp.topMargin = textTop; + } + } lp = (LayoutParams) avatarImageView.getLayoutParams(); - lp.topMargin = (ActionBar.getCurrentActionBarHeight() - AndroidUtilities.dp(42)) / 2; + lp.topMargin = (ActionBar.getCurrentActionBarHeight() - dp(42)) / 2; super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @@ -128,15 +272,110 @@ protected void drawList(Canvas blurCanvas, boolean top) { fragmentView.needBlur = true; this.fragmentView = fragmentView; - nameTextView = new SimpleTextView(context); + ActionBarMenu menu2 = actionBar.createMenu(); + if (type == TYPE_STORIES) { + FrameLayout menu = new FrameLayout(context); + actionBar.addView(menu, LayoutHelper.createFrame(56, 56, Gravity.RIGHT | Gravity.BOTTOM)); + + deleteItem = new ActionBarMenuItem(context, menu2, getThemedColor(Theme.key_actionBarActionModeDefaultSelector), getThemedColor(Theme.key_windowBackgroundWhiteBlackText)); + deleteItem.setIcon(R.drawable.msg_delete); + deleteItem.setVisibility(View.GONE); + deleteItem.setAlpha(0f); + deleteItem.setOnClickListener(v -> menu2.onItemClick(2)); + menu.addView(deleteItem); + + optionsItem = new ActionBarMenuItem(context, menu2, getThemedColor(Theme.key_actionBarActionModeDefaultSelector), getThemedColor(Theme.key_windowBackgroundWhiteBlackText)); + optionsItem.setIcon(R.drawable.ic_ab_other); + optionsItem.setOnClickListener(v -> optionsItem.toggleSubMenu()); + optionsItem.setVisibility(View.GONE); + optionsItem.setAlpha(0f); + menu.addView(optionsItem); + zoomInItem = optionsItem.addSubItem(8, R.drawable.msg_zoomin, LocaleController.getString("MediaZoomIn", R.string.MediaZoomIn)); + zoomInItem.setOnClickListener(v -> { + boolean canZoomOut, canZoomIn; + canZoomOut = true; + Boolean r = sharedMediaLayout.zoomIn(); + if (r == null) { + return; + } + canZoomIn = r; + zoomOutItem.setEnabled(canZoomOut); + zoomOutItem.animate().alpha(zoomOutItem.isEnabled() ? 1f : .5f).start(); + zoomInItem.setEnabled(canZoomIn); + zoomInItem.animate().alpha(zoomInItem.isEnabled() ? 1f : .5f).start(); + }); + zoomOutItem = optionsItem.addSubItem(9, R.drawable.msg_zoomout, LocaleController.getString("MediaZoomOut", R.string.MediaZoomOut)); + zoomOutItem.setOnClickListener(v -> { + boolean canZoomOut, canZoomIn; + canZoomIn = true; + Boolean r = sharedMediaLayout.zoomOut(); + if (r == null) { + return; + } + canZoomOut = r; + zoomOutItem.setEnabled(canZoomOut); + zoomOutItem.animate().alpha(zoomOutItem.isEnabled() ? 1f : .5f).start(); + zoomInItem.setEnabled(canZoomIn); + zoomInItem.animate().alpha(zoomInItem.isEnabled() ? 1f : .5f).start(); + }); + calendarItem = optionsItem.addSubItem(10, R.drawable.msg_calendar2, LocaleController.getString("Calendar", R.string.Calendar)); + calendarItem.setEnabled(false); + calendarItem.setAlpha(.5f); + optionsItem.addColoredGap(); + showPhotosItem = optionsItem.addSubItem(6, 0, LocaleController.getString("MediaShowPhotos", R.string.MediaShowPhotos), true); + showPhotosItem.setChecked(filterPhotos); + showPhotosItem.setOnClickListener(e -> { + if (filterPhotos && !filterVideos) { + BotWebViewVibrationEffect.APP_ERROR.vibrate(); + AndroidUtilities.shakeViewSpring(showPhotosItem, shiftDp = -shiftDp); + return; + } + showPhotosItem.setChecked(filterPhotos = !filterPhotos); + sharedMediaLayout.setStoriesFilter(filterPhotos, filterVideos); + }); + showVideosItem = optionsItem.addSubItem(7, 0, LocaleController.getString("MediaShowVideos", R.string.MediaShowVideos), true); + showVideosItem.setChecked(filterVideos); + showVideosItem.setOnClickListener(e -> { + if (filterVideos && !filterPhotos) { + BotWebViewVibrationEffect.APP_ERROR.vibrate(); + AndroidUtilities.shakeViewSpring(showVideosItem, shiftDp = -shiftDp); + return; + } + showVideosItem.setChecked(filterVideos = !filterVideos); + sharedMediaLayout.setStoriesFilter(filterPhotos, filterVideos); + }); + } + + boolean hasAvatar = type == TYPE_MEDIA; - nameTextView.setTextSize(18); - nameTextView.setGravity(Gravity.LEFT); - nameTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - nameTextView.setLeftDrawableTopPadding(-AndroidUtilities.dp(1.3f)); - nameTextView.setScrollNonFitText(true); - nameTextView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); - avatarContainer.addView(nameTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 118, 0, 56, 0)); + titlesContainer = new FrameLayout(context); + avatarContainer.addView(titlesContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); + for (int i = 0; i < (type == TYPE_STORIES ? 2 : 1); ++i) { + titles[i] = new FrameLayout(context); + titlesContainer.addView(titles[i], LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); + + nameTextView[i] = new SimpleTextView(context); + nameTextView[i].setPivotX(0); + nameTextView[i].setPivotY(dp(9)); + + nameTextView[i].setTextSize(18); + nameTextView[i].setGravity(Gravity.LEFT); + nameTextView[i].setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + nameTextView[i].setLeftDrawableTopPadding(-dp(1.3f)); + nameTextView[i].setScrollNonFitText(true); + nameTextView[i].setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); + titles[i].addView(nameTextView[i], LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, hasAvatar ? 118 : 72, 0, 56, 0)); + + subtitleTextView[i] = new AnimatedTextView(context, true, true, true); + subtitleTextView[i].setAnimationProperties(.4f, 0, 320, CubicBezierInterpolator.EASE_OUT_QUINT); + subtitleTextView[i].setTextSize(AndroidUtilities.dp(14)); + subtitleTextView[i].setTextColor(Theme.getColor(Theme.key_player_actionBarSubtitle)); + titles[i].addView(subtitleTextView[i], LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, hasAvatar ? 118 : 72, 0, 56, 0)); + + if (i != 0) { + titles[i].setAlpha(0f); + } + } avatarImageView = new ProfileActivity.AvatarImageView(context) { @Override @@ -154,36 +393,165 @@ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { } }; avatarImageView.getImageReceiver().setAllowDecodeSingleFrame(true); - avatarImageView.setRoundRadius(AndroidUtilities.dp(21)); + avatarImageView.setRoundRadius(dp(21)); avatarImageView.setPivotX(0); avatarImageView.setPivotY(0); AvatarDrawable avatarDrawable = new AvatarDrawable(); avatarDrawable.setProfile(true); + avatarImageView.setVisibility(hasAvatar ? View.VISIBLE : View.GONE); avatarImageView.setImageDrawable(avatarDrawable); avatarContainer.addView(avatarImageView, LayoutHelper.createFrame(42, 42, Gravity.TOP | Gravity.LEFT, 64, 0, 0, 0)); - mediaCounterTextView = new AudioPlayerAlert.ClippingTextViewSwitcher(context) { - @Override - protected TextView createTextView() { - TextView textView = new TextView(context); - textView.setTextColor(Theme.getColor(Theme.key_player_actionBarSubtitle)); - textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - textView.setSingleLine(true); - textView.setEllipsize(TextUtils.TruncateAt.END); - textView.setGravity(Gravity.LEFT); - return textView; + selectedTextView = new AnimatedTextView(context, true, true, true); + selectedTextView.setAnimationProperties(.4f, 0, 320, CubicBezierInterpolator.EASE_OUT_QUINT); + selectedTextView.setTextSize(dp(20)); + selectedTextView.setGravity(Gravity.LEFT); + selectedTextView.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteBlackText)); + selectedTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + avatarContainer.addView(selectedTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT, Gravity.FILL_HORIZONTAL | Gravity.CENTER_VERTICAL, 72 + (hasAvatar ? 48 : 0), -2, 72, 0)); + + if (type == TYPE_STORIES) { + tabsView = new StoriesTabsView(context, getResourceProvider()); + tabsView.setOnTabClick(i -> { + sharedMediaLayout.scrollToPage(SharedMediaLayout.TAB_STORIES + i); + }); + + buttonContainer = new FrameLayout(context); + buttonContainer.setPadding(dp(10), dp(8), dp(10), dp(8)); + buttonContainer.setBackgroundColor(getThemedColor(Theme.key_windowBackgroundWhite)); + button = new ButtonWithCounterView(context, getResourceProvider()); + button.setText(LocaleController.getString("SaveToProfile", R.string.SaveToProfile), false); + button.setShowZero(true); + button.setCount(0, false); + button.setEnabled(false); + button.setOnClickListener(v -> { + if (applyBulletin != null) { + applyBulletin.run(); + applyBulletin = null; + } + Bulletin.hideVisible(); + boolean pin = sharedMediaLayout.getClosestTab() == SharedMediaLayout.TAB_ARCHIVED_STORIES; + int count = 0; + ArrayList storyItems = new ArrayList<>(); + if (actionModeMessageObjects != null) { + for (int i = 0; i < actionModeMessageObjects.size(); ++i) { + MessageObject messageObject = actionModeMessageObjects.valueAt(i); + if (messageObject.storyItem != null) { + storyItems.add(messageObject.storyItem); + count++; + } + } + } + sharedMediaLayout.closeActionMode(false); + sharedMediaLayout.disableScroll(false); + if (pin) { + sharedMediaLayout.scrollToPage(SharedMediaLayout.TAB_STORIES); + } + if (storyItems.isEmpty()) { + return; + } + boolean[] pastValues = new boolean[storyItems.size()]; + for (int i = 0; i < storyItems.size(); ++i) { + TLRPC.StoryItem storyItem = storyItems.get(i); + pastValues[i] = storyItem.pinned; + storyItem.pinned = pin; + } + getMessagesController().getStoriesController().updateStoriesInLists(dialogId, storyItems); + final boolean[] undone = new boolean[] { false }; + applyBulletin = () -> { + getMessagesController().getStoriesController().updateStoriesPinned(storyItems, pin, null); + }; + final Runnable undo = () -> { + undone[0] = true; + AndroidUtilities.cancelRunOnUIThread(applyBulletin); + for (int i = 0; i < storyItems.size(); ++i) { + TLRPC.StoryItem storyItem = storyItems.get(i); + storyItem.pinned = pastValues[i]; + } + getMessagesController().getStoriesController().updateStoriesInLists(dialogId, storyItems); + }; + Bulletin bulletin; + if (pin) { + bulletin = BulletinFactory.of(this).createSimpleBulletin(R.raw.contact_check, LocaleController.formatPluralString("StorySavedTitle", count), LocaleController.getString("StorySavedSubtitle"), LocaleController.getString("Undo"), undo).show(); + } else { + bulletin = BulletinFactory.of(this).createSimpleBulletin(R.raw.chats_archived, LocaleController.formatPluralString("StoryArchived", count), LocaleController.getString("Undo"), Bulletin.DURATION_PROLONG, undo).show(); + } + bulletin.setOnHideListener(() -> { + if (!undone[0] && applyBulletin != null) { + applyBulletin.run(); + } + applyBulletin = null; + }); + }); + buttonContainer.addView(button); + buttonContainer.setAlpha(0f); + buttonContainer.setTranslationY(dp(100)); + + Bulletin.addDelegate(this, new Bulletin.Delegate() { + @Override + public int getBottomOffset(int tag) { + return AndroidUtilities.dp(64); + } + }); + + floatingButtonContainer = new FrameLayout(context); + floatingButtonContainer.setVisibility(View.VISIBLE); + floatingButtonContainer.setOnClickListener(v -> { + StoryRecorder.getInstance(getParentActivity(), getCurrentAccount()) + .open(StoryRecorder.SourceView.fromFloatingButton(floatingButtonContainer)); + }); + + floatingButton = new RLottieImageView(context); + floatingButton.setScaleType(ImageView.ScaleType.FIT_CENTER); + floatingButton.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_chats_actionIcon), PorterDuff.Mode.MULTIPLY)); + floatingButton.setImageResource(R.drawable.story_camera); + floatingButtonContainer.setContentDescription(LocaleController.getString("AccDescrCaptureStory", R.string.AccDescrCaptureStory)); +// if (Build.VERSION.SDK_INT >= 21) { +// StateListAnimator animator = new StateListAnimator(); +// animator.addState(new int[]{android.R.attr.state_pressed}, ObjectAnimator.ofFloat(floatingButtonContainer, View.TRANSLATION_Z, AndroidUtilities.dp(2), AndroidUtilities.dp(4)).setDuration(200)); +// animator.addState(new int[]{}, ObjectAnimator.ofFloat(floatingButtonContainer, View.TRANSLATION_Z, AndroidUtilities.dp(4), AndroidUtilities.dp(2)).setDuration(200)); +// floatingButtonContainer.setStateListAnimator(animator); +// floatingButtonContainer.setOutlineProvider(new ViewOutlineProvider() { +// @SuppressLint("NewApi") +// @Override +// public void getOutline(View view, Outline outline) { +// outline.setOval(0, 0, AndroidUtilities.dp(56), AndroidUtilities.dp(56)); +// } +// }); +// } + Drawable drawable = Theme.createSimpleSelectorCircleDrawable(AndroidUtilities.dp(56), Theme.getColor(Theme.key_chats_actionBackground), Theme.getColor(Theme.key_chats_actionPressedBackground)); + if (Build.VERSION.SDK_INT < 21) { + Drawable shadowDrawable = context.getResources().getDrawable(R.drawable.floating_shadow).mutate(); + shadowDrawable.setColorFilter(new PorterDuffColorFilter(0xff000000, PorterDuff.Mode.MULTIPLY)); + CombinedDrawable combinedDrawable = new CombinedDrawable(shadowDrawable, drawable, 0, 0); + combinedDrawable.setIconSize(AndroidUtilities.dp(56), AndroidUtilities.dp(56)); + drawable = combinedDrawable; } - }; - avatarContainer.addView(mediaCounterTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 118, 0, 56, 0)); - sharedMediaLayout = new SharedMediaLayout(context, dialogId, sharedMediaPreloader, 0, null, currentChatInfo, false, this, new SharedMediaLayout.Delegate() { + floatingButtonContainer.addView(floatingButton, LayoutHelper.createFrame(56, 56, Gravity.CENTER)); + if (floatingButtonContainer != null) { + drawable = Theme.createSimpleSelectorCircleDrawable(AndroidUtilities.dp(56), Theme.getColor(Theme.key_chats_actionBackground), Theme.getColor(Theme.key_chats_actionPressedBackground)); + if (Build.VERSION.SDK_INT < 21) { + Drawable shadowDrawable = ContextCompat.getDrawable(getParentActivity(), R.drawable.floating_shadow).mutate(); + shadowDrawable.setColorFilter(new PorterDuffColorFilter(0xff000000, PorterDuff.Mode.MULTIPLY)); + CombinedDrawable combinedDrawable = new CombinedDrawable(shadowDrawable, drawable, 0, 0); + combinedDrawable.setIconSize(AndroidUtilities.dp(56), AndroidUtilities.dp(56)); + drawable = combinedDrawable; + } + floatingButtonContainer.setBackground(drawable); + } + + hideFloatingButton(true, false); + } + + sharedMediaLayout = new SharedMediaLayout(context, dialogId, sharedMediaPreloader, 0, null, currentChatInfo, currentUserInfo, false, this, new SharedMediaLayout.Delegate() { @Override public void scrollToSharedMedia() { } @Override - public boolean onMemberClick(TLRPC.ChatParticipant participant, boolean b, boolean resultOnly) { + public boolean onMemberClick(TLRPC.ChatParticipant participant, boolean b, boolean resultOnly, View vi) { return false; } @@ -218,6 +586,11 @@ protected void onSelectedTabChanged() { updateMediaCount(); } + @Override + protected boolean canShowSearchItem() { + return false; + } + @Override protected void onSearchStateChanged(boolean expanded) { AndroidUtilities.removeAdjustResize(getParentActivity(), classGuid); @@ -233,23 +606,176 @@ protected void drawBackgroundWithBlur(Canvas canvas, float y, Rect rectTmp2, Pai protected void invalidateBlur() { fragmentView.invalidateBlur(); } + + @Override + protected boolean isStoriesView() { + return type == TYPE_STORIES; + } + + @Override + protected boolean includeStories() { + return type == TYPE_STORIES; + } + + @Override + protected int getInitialTab() { + return initialTab; + } + + private AnimatorSet actionModeAnimation; + + @Override + protected void showActionMode(boolean show) { + if (isActionModeShowed == show) { + return; + } + isActionModeShowed = show; + if (actionModeAnimation != null) { + actionModeAnimation.cancel(); + } + if (type == TYPE_STORIES) { + disableScroll(show); + } + if (show) { + selectedTextView.setVisibility(VISIBLE); + if (buttonContainer != null) { + buttonContainer.setVisibility(VISIBLE); + } + } else { + titlesContainer.setVisibility(VISIBLE); + } + backDrawable.setRotation(show ? 1f : 0f, true); + actionModeAnimation = new AnimatorSet(); + ArrayList animators = new ArrayList<>(); + animators.add(ObjectAnimator.ofFloat(selectedTextView, View.ALPHA, show ? 1.0f : 0.0f)); + animators.add(ObjectAnimator.ofFloat(titlesContainer, View.ALPHA, show ? 0.0f : 1.0f)); + if (buttonContainer != null) { + boolean showButton = show; + animators.add(ObjectAnimator.ofFloat(buttonContainer, View.ALPHA, showButton ? 1.0f : 0.0f)); + animators.add(ObjectAnimator.ofFloat(buttonContainer, View.TRANSLATION_Y, showButton ? 0.0f : buttonContainer.getMeasuredHeight())); + } + if (deleteItem != null) { + deleteItem.setVisibility(View.VISIBLE); + animators.add(ObjectAnimator.ofFloat(deleteItem, View.ALPHA, show ? 1.0f : 0.0f)); + } + final boolean empty = getStoriesCount(getClosestTab()) == 0; + if (optionsItem != null) { + optionsItem.setVisibility(View.VISIBLE); + animators.add(ObjectAnimator.ofFloat(optionsItem, View.ALPHA, show || empty ? 0.0f : 1.0f)); + } + if (tabsView != null) { + animators.add(ObjectAnimator.ofFloat(tabsView, View.ALPHA, show ? 0.4f : 1.0f)); + } + actionModeAnimation.playTogether(animators); + actionModeAnimation.setDuration(300); + actionModeAnimation.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + actionModeAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationCancel(Animator animation) { + actionModeAnimation = null; + } + + @Override + public void onAnimationEnd(Animator animation) { + if (actionModeAnimation == null) { + return; + } + actionModeAnimation = null; + if (!show) { + selectedTextView.setVisibility(INVISIBLE); + if (buttonContainer != null) { + buttonContainer.setVisibility(INVISIBLE); + } + if (deleteItem != null) { + deleteItem.setVisibility(View.GONE); + } + if (empty && optionsItem != null) { + optionsItem.setVisibility(View.GONE); + } + } else { + titlesContainer.setVisibility(INVISIBLE); + if (optionsItem != null) { + optionsItem.setVisibility(View.GONE); + } + } + } + }); + actionModeAnimation.start(); + } + + @Override + protected void onActionModeSelectedUpdate(SparseArray messageObjects) { + final int count = messageObjects.size(); + actionModeMessageObjects = messageObjects; + selectedTextView.cancelAnimation(); + selectedTextView.setText(LocaleController.formatPluralString("StoriesSelected", count), !LocaleController.isRTL); + if (button != null) { + button.setEnabled(count > 0); + button.setCount(count, true); + if (sharedMediaLayout.getClosestTab() == SharedMediaLayout.TAB_STORIES) { + button.setText(LocaleController.formatPluralString("ArchiveStories", count), true); + } + } + } + + @Override + protected void onTabProgress(float progress) { + if (type != TYPE_STORIES) + return; + float t = progress - TAB_STORIES; + if (tabsView != null) { + tabsView.setProgress(t); + } + titles[0].setAlpha(1f - t); + titles[0].setTranslationX(AndroidUtilities.dp(-12) * t); + titles[1].setAlpha(t); + titles[1].setTranslationX(AndroidUtilities.dp(12) * (1f - t)); + } + + @Override + protected void onTabScroll(boolean scrolling) { + if (tabsView != null) { + tabsView.setScrolling(scrolling); + } + } }; sharedMediaLayout.setPinnedToTop(true); sharedMediaLayout.getSearchItem().setTranslationY(0); sharedMediaLayout.photoVideoOptionsItem.setTranslationY(0); - fragmentView.addView(sharedMediaLayout); + if (type == TYPE_STORIES) { + fragmentView.addView(sharedMediaLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL, 0, 0, 0, 64)); + } else { + fragmentView.addView(sharedMediaLayout); + } fragmentView.addView(actionBar); fragmentView.addView(avatarContainer); fragmentView.blurBehindViews.add(sharedMediaLayout); + if (type == TYPE_STORIES) { + showSubtitle(0, false, false); + showSubtitle(1, false, false); + } + + if (floatingButtonContainer != null) { + fragmentView.addView(floatingButtonContainer, LayoutHelper.createFrame((Build.VERSION.SDK_INT >= 21 ? 56 : 60), (Build.VERSION.SDK_INT >= 21 ? 56 : 60), (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.BOTTOM, LocaleController.isRTL ? 14 : 0, 0, LocaleController.isRTL ? 0 : 14, 14 + 64)); + } + if (tabsView != null) { + fragmentView.addView(tabsView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM | Gravity.FILL_HORIZONTAL)); + } + if (buttonContainer != null) { + fragmentView.addView(buttonContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 64, Gravity.BOTTOM | Gravity.FILL_HORIZONTAL)); + } TLObject avatarObject = null; - if (DialogObject.isEncryptedDialog(dialogId)) { + if (type == TYPE_STORIES) { + nameTextView[0].setText(LocaleController.getString("ProfileMyStories")); + nameTextView[1].setText(LocaleController.getString("ProfileStoriesArchive")); + } else if (DialogObject.isEncryptedDialog(dialogId)) { TLRPC.EncryptedChat encryptedChat = getMessagesController().getEncryptedChat(DialogObject.getEncryptedChatId(dialogId)); if (encryptedChat != null) { TLRPC.User user = getMessagesController().getUser(encryptedChat.user_id); if (user != null) { - nameTextView.setText(ContactsController.formatName(user.first_name, user.last_name)); + nameTextView[0].setText(ContactsController.formatName(user.first_name, user.last_name)); avatarDrawable.setInfo(user); avatarObject = user; } @@ -258,20 +784,19 @@ protected void invalidateBlur() { TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); if (user != null) { if (user.self) { - nameTextView.setText(LocaleController.getString("SavedMessages", R.string.SavedMessages)); + nameTextView[0].setText(LocaleController.getString("SavedMessages", R.string.SavedMessages)); avatarDrawable.setAvatarType(AvatarDrawable.AVATAR_TYPE_SAVED); avatarDrawable.setScaleSize(.8f); } else { - nameTextView.setText(ContactsController.formatName(user.first_name, user.last_name)); + nameTextView[0].setText(ContactsController.formatName(user.first_name, user.last_name)); avatarDrawable.setInfo(user); avatarObject = user; } } - } else { TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-dialogId); if (chat != null) { - nameTextView.setText(chat.title); + nameTextView[0].setText(chat.title); avatarDrawable.setInfo(chat); avatarObject = chat; } @@ -280,15 +805,14 @@ protected void invalidateBlur() { final ImageLocation thumbLocation = ImageLocation.getForUserOrChat(avatarObject, ImageLocation.TYPE_SMALL); avatarImageView.setImage(thumbLocation, "50_50", avatarDrawable, avatarObject); - - if (TextUtils.isEmpty(nameTextView.getText())) { - nameTextView.setText(LocaleController.getString("SharedContentTitle", R.string.SharedContentTitle)); + if (TextUtils.isEmpty(nameTextView[0].getText())) { + nameTextView[0].setText(LocaleController.getString("SharedContentTitle", R.string.SharedContentTitle)); } - if (sharedMediaLayout.isSearchItemVisible()) { + if (sharedMediaLayout.isSearchItemVisible() && type != TYPE_STORIES) { sharedMediaLayout.getSearchItem().setVisibility(View.VISIBLE); } - if (sharedMediaLayout.isCalendarItemVisible()) { + if (sharedMediaLayout.isCalendarItemVisible() && type != TYPE_STORIES) { sharedMediaLayout.photoVideoOptionsItem.setVisibility(View.VISIBLE); } else { sharedMediaLayout.photoVideoOptionsItem.setVisibility(View.INVISIBLE); @@ -298,9 +822,25 @@ protected void invalidateBlur() { AndroidUtilities.updateViewVisibilityAnimated(avatarContainer, true, 1, false); updateMediaCount(); updateColors(); + + if (type == TYPE_STORIES && initialTab == SharedMediaLayout.TAB_ARCHIVED_STORIES) { + sharedMediaLayout.onTabProgress(9f); + } return fragmentView; } + @Override + public boolean onBackPressed() { + if (closeStoryViewer()) { + return false; + } + if (sharedMediaLayout.isActionModeShown()) { + sharedMediaLayout.closeActionMode(false); + return false; + } + return super.onBackPressed(); + } + @Override public boolean isSwipeBackEnabled(MotionEvent event) { if (!sharedMediaLayout.isSwipeBackEnabled()) { @@ -317,30 +857,103 @@ public boolean canBeginSlide() { return super.canBeginSlide(); } + private int lastTab; private void updateMediaCount() { int id = sharedMediaLayout.getClosestTab(); int[] mediaCount = sharedMediaPreloader.getLastMediaCount(); + final boolean animated = !LocaleController.isRTL; + int i; + if (type != TYPE_STORIES) { + i = 0; + } else { + i = id == SharedMediaLayout.TAB_STORIES ? 0 : 1; + } + if (id == SharedMediaLayout.TAB_STORIES || id == SharedMediaLayout.TAB_ARCHIVED_STORIES) { + if (zoomOutItem != null) { + zoomOutItem.setEnabled(sharedMediaLayout.canZoomOut()); + zoomOutItem.setAlpha(zoomOutItem.isEnabled() ? 1f : .5f); + } + if (zoomInItem != null) { + zoomInItem.setEnabled(sharedMediaLayout.canZoomIn()); + zoomInItem.setAlpha(zoomInItem.isEnabled() ? 1f : .5f); + } + + int count = sharedMediaLayout.getStoriesCount(SharedMediaLayout.TAB_STORIES); + if (count > 0) { + showSubtitle(0, true, true); + subtitleTextView[0].setText(LocaleController.formatPluralString("ProfileMyStoriesCount", count), animated); + } else { + showSubtitle(0, false, true); + } + + if (type == TYPE_STORIES) { + count = sharedMediaLayout.getStoriesCount(SharedMediaLayout.TAB_ARCHIVED_STORIES); + if (count > 0) { + showSubtitle(1, true, true); + subtitleTextView[1].setText(LocaleController.formatPluralString("ProfileStoriesArchiveCount", count), animated); + } else { + showSubtitle(1, false, true); + } + hideFloatingButton(id != SharedMediaLayout.TAB_ARCHIVED_STORIES || sharedMediaLayout.getStoriesCount(SharedMediaLayout.TAB_ARCHIVED_STORIES) > 0, true); + } + + if (optionsItem != null) { + final boolean empty = sharedMediaLayout.getStoriesCount(sharedMediaLayout.getClosestTab()) <= 0; + if (!empty) { + optionsItem.setVisibility(View.VISIBLE); + } + optionsItem.animate().alpha(empty ? 0f : 1f).withEndAction(() -> { + if (empty) { + optionsItem.setVisibility(View.GONE); + } + }).setDuration(220).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).start(); + } + + if (button != null) { + boolean animated2 = animated && lastTab == id; + if (id == SharedMediaLayout.TAB_STORIES) { + button.setText(LocaleController.formatPluralString("ArchiveStories", actionModeMessageObjects == null ? 0 : actionModeMessageObjects.size()), animated2); + } else { + button.setText(LocaleController.getString("SaveToProfile", R.string.SaveToProfile), animated2); + } + lastTab = id; + } + + if (calendarItem != null) { + boolean calendarAvailable = sharedMediaLayout.getStoriesCount(id) > 0; + calendarItem.setEnabled(calendarAvailable); + calendarItem.setAlpha(calendarAvailable ? 1f : .5f); + } + + return; + } if (id < 0 || mediaCount[id] < 0) { return; } - if (id == 0) { + if (id == SharedMediaLayout.TAB_PHOTOVIDEO) { + showSubtitle(i, true, true); if (sharedMediaLayout.getPhotosVideosTypeFilter() == SharedMediaLayout.FILTER_PHOTOS_ONLY) { - mediaCounterTextView.setText(LocaleController.formatPluralString("Photos", mediaCount[MediaDataController.MEDIA_PHOTOS_ONLY])); + subtitleTextView[i].setText(LocaleController.formatPluralString("Photos", mediaCount[MediaDataController.MEDIA_PHOTOS_ONLY]), animated); } else if (sharedMediaLayout.getPhotosVideosTypeFilter() == SharedMediaLayout.FILTER_VIDEOS_ONLY) { - mediaCounterTextView.setText(LocaleController.formatPluralString("Videos", mediaCount[MediaDataController.MEDIA_VIDEOS_ONLY])); + subtitleTextView[i].setText(LocaleController.formatPluralString("Videos", mediaCount[MediaDataController.MEDIA_VIDEOS_ONLY]), animated); } else { - mediaCounterTextView.setText(LocaleController.formatPluralString("Media", mediaCount[MediaDataController.MEDIA_PHOTOVIDEO])); - } - } else if (id == 1) { - mediaCounterTextView.setText(LocaleController.formatPluralString("Files", mediaCount[MediaDataController.MEDIA_FILE])); - } else if (id == 2) { - mediaCounterTextView.setText(LocaleController.formatPluralString("Voice", mediaCount[MediaDataController.MEDIA_AUDIO])); - } else if (id == 3) { - mediaCounterTextView.setText(LocaleController.formatPluralString("Links", mediaCount[MediaDataController.MEDIA_URL])); - } else if (id == 4) { - mediaCounterTextView.setText(LocaleController.formatPluralString("MusicFiles", mediaCount[MediaDataController.MEDIA_MUSIC])); - } else if (id == 5) { - mediaCounterTextView.setText(LocaleController.formatPluralString("GIFs", mediaCount[MediaDataController.MEDIA_GIF])); + subtitleTextView[i].setText(LocaleController.formatPluralString("Media", mediaCount[MediaDataController.MEDIA_PHOTOVIDEO]), animated); + } + } else if (id == SharedMediaLayout.TAB_FILES) { + showSubtitle(i, true, true); + subtitleTextView[i].setText(LocaleController.formatPluralString("Files", mediaCount[MediaDataController.MEDIA_FILE]), animated); + } else if (id == SharedMediaLayout.TAB_VOICE) { + showSubtitle(i, true, true); + subtitleTextView[i].setText(LocaleController.formatPluralString("Voice", mediaCount[MediaDataController.MEDIA_AUDIO]), animated); + } else if (id == SharedMediaLayout.TAB_LINKS) { + showSubtitle(i, true, true); + subtitleTextView[i].setText(LocaleController.formatPluralString("Links", mediaCount[MediaDataController.MEDIA_URL]), animated); + } else if (id == SharedMediaLayout.TAB_AUDIO) { + showSubtitle(i, true, true); + subtitleTextView[i].setText(LocaleController.formatPluralString("MusicFiles", mediaCount[MediaDataController.MEDIA_MUSIC]), animated); + } else if (id == SharedMediaLayout.TAB_GIF) { + showSubtitle(i, true, true); + subtitleTextView[i].setText(LocaleController.formatPluralString("GIFs", mediaCount[MediaDataController.MEDIA_GIF]), animated); } } @@ -352,6 +965,114 @@ public long getDialogId() { return dialogId; } + private float floatingButtonTranslation1; + private float floatingButtonTranslation2; + private void updateFloatingButtonOffset() { + if (floatingButtonContainer == null) { + return; + } + floatingButtonContainer.setTranslationY(floatingButtonTranslation + floatingButtonTranslation1 + floatingButtonTranslation2); + } + + private boolean floatingHidden; + private AnimatorSet floatingAnimator; + private float floatingButtonHideProgress, floatingButtonTranslation; + private void hideFloatingButton(boolean hide, boolean animated) { + if (floatingButtonContainer == null) { + return; + } + if (!getMessagesController().storiesEnabled()) { + hide = true; + } + if (floatingHidden == hide) { + return; + } + floatingHidden = hide; + if (floatingAnimator != null) { + floatingAnimator.cancel(); + } + if (animated) { + floatingButtonContainer.setVisibility(View.VISIBLE); + floatingAnimator = new AnimatorSet(); + ValueAnimator valueAnimator = ValueAnimator.ofFloat(floatingButtonHideProgress, floatingHidden ? 1f : 0f); + valueAnimator.addUpdateListener(animation -> { + floatingButtonHideProgress = (float) animation.getAnimatedValue(); + floatingButtonTranslation = dp(100) * floatingButtonHideProgress; + updateFloatingButtonOffset(); + }); + valueAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (floatingHidden) { + floatingButtonContainer.setVisibility(View.GONE); + } + } + }); + floatingAnimator.playTogether(valueAnimator); + floatingAnimator.setDuration(300); + floatingAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); + floatingButtonContainer.setClickable(!hide); + floatingAnimator.start(); + } else { + floatingButtonHideProgress = hide ? 1f : 0f; + floatingButtonTranslation = dp(100) * floatingButtonHideProgress; + updateFloatingButtonOffset(); + floatingButtonContainer.setVisibility(hide ? View.GONE : View.VISIBLE); + } + } + + private final boolean[] subtitleShown = new boolean[2]; + private final float[] subtitleT = new float[2]; + private final boolean[] firstSubtitleCheck = new boolean[] { true, true }; + private final ValueAnimator[] subtitleAnimator = new ValueAnimator[2]; + private void showSubtitle(int i, boolean show, boolean animated) { + if (subtitleShown[i] == show && !firstSubtitleCheck[i]) { + return; + } + animated = !firstSubtitleCheck[i] && animated; + firstSubtitleCheck[i] = false; + subtitleShown[i] = show; + if (subtitleAnimator[i] != null) { + subtitleAnimator[i].cancel(); + subtitleAnimator[i] = null; + } + if (animated) { + subtitleTextView[i].setVisibility(View.VISIBLE); + subtitleAnimator[i] = ValueAnimator.ofFloat(subtitleT[i], show ? 1f : 0f); + subtitleAnimator[i].addUpdateListener(anm -> { + subtitleT[i] = (float) anm.getAnimatedValue(); + nameTextView[i].setScaleX(lerp(1.111f, 1f, subtitleT[i])); + nameTextView[i].setScaleY(lerp(1.111f, 1f, subtitleT[i])); + nameTextView[i].setTranslationY(lerp(dp(8), 0, subtitleT[i])); + subtitleTextView[i].setAlpha(subtitleT[i]); + }); + subtitleAnimator[i].addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + subtitleT[i] = show ? 1f : 0f; + nameTextView[i].setScaleX(show ? 1f : 1.111f); + nameTextView[i].setScaleY(show ? 1f : 1.111f); + nameTextView[i].setTranslationY(show ? 0 : dp(8)); + subtitleTextView[i].setAlpha(show ? 1f : 0f); + + if (!show) { + subtitleTextView[i].setVisibility(View.GONE); + } + } + }); + subtitleAnimator[i].setDuration(320); + subtitleAnimator[i].setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + subtitleAnimator[i].start(); + } else { + subtitleT[i] = show ? 1f : 0f; + nameTextView[i].setScaleX(show ? 1f : 1.111f); + nameTextView[i].setScaleY(show ? 1f : 1.111f); + nameTextView[i].setTranslationY(show ? 0 : dp(8)); + subtitleTextView[i].setAlpha(show ? 1f : 0f); + subtitleTextView[i].setVisibility(show ? View.VISIBLE : View.GONE); + } + } + @Override public void mediaCountUpdated() { if (sharedMediaLayout != null && sharedMediaPreloader != null) { @@ -363,17 +1084,18 @@ public void mediaCountUpdated() { private void updateColors() { actionBar.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); - actionBar.setItemsColor(Theme.getColor(Theme.key_actionBarActionModeDefaultIcon), false); + actionBar.setItemsColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText), false); actionBar.setItemsBackgroundColor(Theme.getColor(Theme.key_actionBarActionModeDefaultSelector), false); actionBar.setTitleColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); - nameTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + nameTextView[0].setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + if (nameTextView[1] != null) { + nameTextView[1].setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + } } @Override public ArrayList getThemeDescriptions() { - ThemeDescription.ThemeDescriptionDelegate themeDelegate = () -> { - updateColors(); - }; + ThemeDescription.ThemeDescriptionDelegate themeDelegate = this::updateColors; ArrayList arrayList = new ArrayList<>(); arrayList.add(new ThemeDescription(null, 0, null, null, null, themeDelegate, Theme.key_windowBackgroundWhite)); arrayList.add(new ThemeDescription(null, 0, null, null, null, themeDelegate, Theme.key_actionBarActionModeDefaultSelector)); @@ -402,4 +1124,259 @@ public List onGetDebugItems() { ) ); } + + private class StoriesTabsView extends View { + + private final Theme.ResourcesProvider resourcesProvider; + private final Tab[] tabs = new Tab[2]; + + class Tab { + final int i; + final RLottieDrawable drawable; + final Drawable ripple; + + final TextPaint paint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + final StaticLayout layout; + final float layoutWidth, layoutLeft; + + final RectF clickRect = new RectF(); + + final AnimatedFloat nonscrollingT = new AnimatedFloat(StoriesTabsView.this, 0, 200, CubicBezierInterpolator.EASE_OUT_QUINT); + + public Tab(int i, int resId, CharSequence text) { + this.i = i; + + drawable = new RLottieDrawable(resId, "" + resId, dp(29), dp(29)); + drawable.setMasterParent(StoriesTabsView.this); + drawable.setAllowDecodeSingleFrame(true); + drawable.setPlayInDirectionOfCustomEndFrame(true); + drawable.setAutoRepeat(0); + + paint.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + paint.setTextSize(dp(12)); + paint.setColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); + layout = new StaticLayout(text, paint, AndroidUtilities.displaySize.x, Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false); + layoutWidth = layout.getLineCount() > 0 ? layout.getLineWidth(0) : 0; + layoutLeft = layout.getLineCount() > 0 ? layout.getLineLeft(0) : 0; + + ripple = Theme.createSelectorDrawable(Theme.multAlpha(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider), .1f), Theme.RIPPLE_MASK_ROUNDRECT_6DP, dp(16)); + } + + private boolean active; + public void setActive(boolean active, boolean animated) { + if (this.active == active) { + return; + } + + if (i == 0) { + // 0 - 20 + // 20 - 40 + if (active) { + drawable.setCustomEndFrame(20); + if (drawable.getCurrentFrame() >= 38) { + drawable.setCurrentFrame(0, false); + } + if (drawable.getCurrentFrame() <= 20) { + drawable.start(); + } else { + drawable.setCurrentFrame(20); + } + } else { + if (drawable.getCurrentFrame() >= 19) { + drawable.setCustomEndFrame(39); + drawable.start(); + } else { + drawable.setCustomEndFrame(0); + drawable.setCurrentFrame(0); + } + } + } else if (i == 1 && active) { + drawable.setCurrentFrame(0); + if (animated) { + drawable.start(); + } + } + this.active = active; + } + + private int drawableColor = -1; + public void setColor(int color) { + paint.setColor(color); + if (drawableColor != color) { + drawable.setColorFilter(new PorterDuffColorFilter(drawableColor = color, PorterDuff.Mode.SRC_IN)); + } + } + } + + private final Paint selectPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + private float progress; + private int value; + + private boolean scrolling; + private AnimatedFloat scrollingT = new AnimatedFloat(this, 0, 210, CubicBezierInterpolator.EASE_OUT_QUINT); + + public StoriesTabsView(Context context, Theme.ResourcesProvider resourcesProvider) { + super(context); + this.resourcesProvider = resourcesProvider; + + tabs[0] = new Tab(0, R.raw.msg_stories_saved, LocaleController.getString("ProfileMyStoriesTab", R.string.ProfileMyStoriesTab)); + tabs[1] = new Tab(1, R.raw.msg_stories_archive, LocaleController.getString("ProfileStoriesArchiveTab", R.string.ProfileStoriesArchiveTab)); + + setPadding(dp(12), 0, dp(12), 0); + + setProgress(0, false); + } + + public void setScrolling(boolean scrolling) { + if (this.scrolling == scrolling) { + return; + } + this.scrolling = scrolling; + invalidate(); + } + + public void setProgress(float progress) { + setProgress(progress, true); + } + + private void setProgress(float progress, boolean animated) { + this.value = Math.round(this.progress = Utilities.clamp(progress, tabs.length, 0)); + for (int i = 0; i < tabs.length; ++i) { + tabs[i].setActive(Math.abs(value - i) < (tabs[i].active ? .25f : .35f), animated); + } + invalidate(); + } + + private Utilities.Callback onTabClick; + public void setOnTabClick(Utilities.Callback listener) { + onTabClick = listener; + } + + @Override + protected void dispatchDraw(Canvas canvas) { + canvas.drawColor(Theme.getColor(Theme.key_windowBackgroundWhite, resourcesProvider)); + + canvas.drawRect(0, 0, getWidth(), AndroidUtilities.getShadowHeight(), Theme.dividerPaint); + + int tabFullWidth = (getWidth() - getPaddingLeft() - getPaddingRight()) / tabs.length; + int tabWidth = Math.min(dp(64), tabFullWidth); + + float scrollingT = this.scrollingT.set(scrolling); + + if (scrollingT > 0) { + double halfT = .4f + 2 * (1 - .4f) * Math.abs(.5f + Math.floor(progress) - progress); + selectPaint.setColor(ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider), (int) (0x12 * halfT * scrollingT))); + float sx = getPaddingLeft() + lerp(tabFullWidth * (float) Math.floor(progress) + tabFullWidth / 2f, tabFullWidth * (float) Math.ceil(progress) + tabFullWidth / 2f, progress - (int) progress); + AndroidUtilities.rectTmp.set( + sx - tabWidth / 2f, + dp(9), + sx + tabWidth / 2f, + dp(9 + 32) + ); + canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(16), dp(16), selectPaint); + } + + for (int i = 0; i < tabs.length; ++i) { + Tab tab = tabs[i]; + final int x = getPaddingLeft() + i * tabFullWidth; + tab.clickRect.set(x, 0, x + tabFullWidth, getHeight()); + + float t = 1f - Math.min(1, Math.abs(progress - i)); + tab.setColor(ColorUtils.blendARGB(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText6, resourcesProvider), Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider), t)); + + AndroidUtilities.rectTmp2.set( + (int) (tab.clickRect.centerX() - tabWidth / 2f), + dp(9), + (int) (tab.clickRect.centerX() + tabWidth / 2f), + dp(9 + 32) + ); + final float T = tab.nonscrollingT.set(t > .6f); + if (scrollingT < 1) { + selectPaint.setColor(ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider), (int) (0x12 * T * (1f - scrollingT)))); + AndroidUtilities.rectTmp.set(AndroidUtilities.rectTmp2); + canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(16), dp(16), selectPaint); + } + + tab.ripple.setBounds(AndroidUtilities.rectTmp2); + tab.ripple.draw(canvas); + + final int drawableSize = dp(29); + AndroidUtilities.rectTmp2.set( + (int) (tab.clickRect.centerX() - drawableSize / 2f), + (int) (dpf2(24.66f) - drawableSize / 2f), + (int) (tab.clickRect.centerX() + drawableSize / 2f), + (int) (dpf2(24.66f) + drawableSize / 2f) + ); + + tab.drawable.setBounds(AndroidUtilities.rectTmp2); + tab.drawable.draw(canvas); + + canvas.save(); + canvas.translate(tab.clickRect.centerX() - tab.layoutWidth / 2f - tab.layoutLeft, dp(50) - tab.layout.getHeight() / 2f); + tab.layout.draw(canvas); + canvas.restore(); + } + } + + private boolean touchDown; + @Override + public boolean onTouchEvent(MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + touchDown = true; + return true; + } else if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_MOVE) { + int index = -1; + final float x = event.getX(); + for (int i = 0; i < tabs.length; ++i) { + if (tabs[i].clickRect.left < x && tabs[i].clickRect.right > x) { + if (event.getAction() != MotionEvent.ACTION_UP) { + if (touchDown) { + tabs[i].ripple.setState(new int[]{}); + } + tabs[i].ripple.setState(new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled}); + } + index = i; + break; + } + } + for (int i = 0; i < tabs.length; ++i) { + if (i != index || event.getAction() == MotionEvent.ACTION_UP) { + tabs[i].ripple.setState(new int[] {}); + } + } + if (index >= 0 && value != index && onTabClick != null) { + onTabClick.run(index); + } + touchDown = false; + } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + for (int i = 0; i < tabs.length; ++i) { + tabs[i].ripple.setState(new int[] {}); + } + } + touchDown = false; + return true; + } + return super.onTouchEvent(event); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setMeasuredDimension( + MeasureSpec.getSize(widthMeasureSpec), + dp(64) + AndroidUtilities.getShadowHeight() + ); + } + + @Override + protected boolean verifyDrawable(@NonNull Drawable who) { + for (int i = 0; i < tabs.length; ++i) { + if (tabs[i].ripple == who) { + return true; + } + } + return super.verifyDrawable(who); + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/MentionsContainerView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/MentionsContainerView.java index 0a29ae632e..9ca164e803 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/MentionsContainerView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/MentionsContainerView.java @@ -7,6 +7,9 @@ import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.Spanned; import android.view.MotionEvent; import android.view.View; @@ -20,16 +23,24 @@ import androidx.recyclerview.widget.RecyclerView; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ChatObject; import org.telegram.messenger.FileLoader; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.MediaDataController; +import org.telegram.messenger.MessageObject; +import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.SharedConfig; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.UserObject; import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Adapters.MentionsAdapter; import org.telegram.ui.Adapters.PaddedListAdapter; -import org.telegram.ui.ChatActivity; +import org.telegram.ui.Cells.StickerCell; import org.telegram.ui.ContentPreviewViewer; -public class MentionsContainerView extends BlurredFrameLayout { +public class MentionsContainerView extends BlurredFrameLayout implements NotificationCenter.NotificationCenterDelegate { private final SizeNotifierFrameLayout sizeNotifierFrameLayout; private final Theme.ResourcesProvider resourcesProvider; @@ -40,14 +51,16 @@ public class MentionsContainerView extends BlurredFrameLayout { private PaddedListAdapter paddedAdapter; private MentionsAdapter adapter; - ChatActivity chatActivity; + BaseFragment baseFragment; private float containerTop, containerBottom, containerPadding, listViewPadding; + private boolean allowBlur; + private RecyclerListView.OnItemClickListener mentionsOnItemClickListener; - public MentionsContainerView(@NonNull Context context, long dialogId, int threadMessageId, ChatActivity chatActivity, Theme.ResourcesProvider resourcesProvider) { - super(context, chatActivity.contentView); - this.chatActivity = chatActivity; - this.sizeNotifierFrameLayout = chatActivity.contentView; + public MentionsContainerView(@NonNull Context context, long dialogId, int threadMessageId, BaseFragment baseFragment, SizeNotifierFrameLayout container, Theme.ResourcesProvider resourcesProvider) { + super(context, container); + this.baseFragment = baseFragment; + this.sizeNotifierFrameLayout = container; this.resourcesProvider = resourcesProvider; this.drawBlur = false; this.isTopView = false; @@ -177,7 +190,7 @@ public int getSpanSize(int position) { public void onItemCountUpdate(int oldCount, int newCount) { if (listView.getLayoutManager() != gridLayoutManager && shown) { AndroidUtilities.cancelRunOnUIThread(updateVisibilityRunnable); - AndroidUtilities.runOnUIThread(updateVisibilityRunnable, chatActivity.fragmentOpened ? 0 : 100); + AndroidUtilities.runOnUIThread(updateVisibilityRunnable, baseFragment.getFragmentBeginToShow() ? 0 : 100); } } @@ -295,7 +308,7 @@ public void dispatchDraw(Canvas canvas) { boolean topPadding = (adapter.isStickers() || adapter.isBotContext()) && adapter.isMediaLayout() && adapter.getBotContextSwitch() == null && adapter.getBotWebViewSwitch() == null; containerPadding = AndroidUtilities.dp(2 + (topPadding ? 2 : 0)); - float r = AndroidUtilities.dp(4); + float r = AndroidUtilities.dp(6); if (reversed) { int paddingViewTop = paddedAdapter.paddingViewAttached ? paddedAdapter.paddingView.getTop() : getHeight(); float top = Math.max(0, paddingViewTop + listView.getTranslationY()) + containerPadding; @@ -326,7 +339,7 @@ public void dispatchDraw(Canvas canvas) { } paint.setColor(color != null ? color : getThemedColor(Theme.key_chat_messagePanelBackground)); - if (SharedConfig.chatBlurEnabled() && sizeNotifierFrameLayout != null) { + if (allowBlur && SharedConfig.chatBlurEnabled() && sizeNotifierFrameLayout != null) { if (r > 0) { canvas.save(); if (path == null) { @@ -343,8 +356,7 @@ public void dispatchDraw(Canvas canvas) { canvas.restore(); } } else { - AndroidUtilities.rectTmp.set(rect); - canvas.drawRoundRect(AndroidUtilities.rectTmp, r, r, paint); + drawRoundRect(canvas, rect, r); } canvas.save(); canvas.clipRect(rect); @@ -352,6 +364,11 @@ public void dispatchDraw(Canvas canvas) { canvas.restore(); } + public void drawRoundRect(Canvas canvas, Rect rectTmp, float r) { + AndroidUtilities.rectTmp.set(rectTmp); + canvas.drawRoundRect(AndroidUtilities.rectTmp, r, r, paint); + } + private Integer color; public void setOverrideColor(int color) { this.color = color; @@ -401,7 +418,7 @@ public void updateVisibility(boolean show) { if (listViewTranslationAnimator != null) { listViewTranslationAnimator.cancel(); } - AndroidUtilities.runOnUIThread(updateVisibilityRunnable, chatActivity.fragmentOpened ? 0 : 100); + AndroidUtilities.runOnUIThread(updateVisibilityRunnable, baseFragment.getFragmentBeginToShow() ? 0 : 100); if (show) { onOpen(); } else { @@ -508,6 +525,83 @@ private void updateListViewTranslation(boolean forceZeroHeight, boolean animated } } + public void setDialogId(long dialogId) { + adapter.setDialogId(dialogId); + } + + public void withDelegate(Delegate delegate) { + getListView().setOnItemClickListener(mentionsOnItemClickListener = (view, position) -> { + if (position == 0 || getAdapter().isBannedInline()) { + return; + } + position--; + Object object = getAdapter().getItem(position); + int start = getAdapter().getResultStartPosition(); + int len = getAdapter().getResultLength(); + if (object instanceof TLRPC.TL_document) { + MessageObject.SendAnimationData sendAnimationData = null; + if (view instanceof StickerCell) { + sendAnimationData = ((StickerCell) view).getSendAnimationData(); + } + TLRPC.TL_document document = (TLRPC.TL_document) object; + Object parent = getAdapter().getItemParent(position); + String query = MessageObject.findAnimatedEmojiEmoticon(document); + delegate.onStickerSelected(document, query, parent); + } else if (object instanceof TLRPC.Chat) { + TLRPC.Chat chat = (TLRPC.Chat) object; + String username = ChatObject.getPublicUsername(chat); + if (username != null) { + delegate.replaceText(start, len, "@" + username + " " , false); + } + } else if (object instanceof TLRPC.User) { + TLRPC.User user = (TLRPC.User) object; + + if (UserObject.getPublicUsername(user) != null) { + delegate.replaceText(start, len, "@" + UserObject.getPublicUsername(user) + " ", false); + } else { + String name = UserObject.getFirstName(user, false); + Spannable spannable = new SpannableString(name + " "); + spannable.setSpan(new URLSpanUserMention("" + user.id, 3), 0, spannable.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + delegate.replaceText(start, len, spannable, false); + } + } else if (object instanceof String) { + delegate.replaceText(start, len, object + " ", false); + } else if (object instanceof MediaDataController.KeywordResult) { + String code = ((MediaDataController.KeywordResult) object).emoji; + delegate.addEmojiToRecent(code); + if (code != null && code.startsWith("animated_")) { + try { + Paint.FontMetricsInt fontMetrics = null; + try { + fontMetrics = delegate.getFontMetrics(); + // chatActivityEnterView.getEditField().getPaint().getFontMetricsInt(); + } catch (Exception e) { + FileLog.e(e, false); + } + long documentId = Long.parseLong(code.substring(9)); + TLRPC.Document document = AnimatedEmojiDrawable.findDocument(UserConfig.selectedAccount, documentId); + SpannableString emoji = new SpannableString(MessageObject.findAnimatedEmojiEmoticon(document)); + AnimatedEmojiSpan span; + if (document != null) { + span = new AnimatedEmojiSpan(document, fontMetrics); + } else { + span = new AnimatedEmojiSpan(documentId, fontMetrics); + } + emoji.setSpan(span, 0, emoji.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + delegate.replaceText(start, len, emoji, false); + } catch (Exception ignore) { + delegate.replaceText(start, len, code, true); + } + } else { + delegate.replaceText(start, len, code, true); + } + updateVisibility(false); + } + }); + getListView().setOnTouchListener((v, event) -> ContentPreviewViewer.getInstance().onTouch(event, getListView(), 0, mentionsOnItemClickListener, null, resourcesProvider)); + + } + public class MentionsListView extends RecyclerListView { private boolean isScrolling, isDragging; @@ -673,8 +767,42 @@ private Paint getThemedPaint(String paintKey) { return paint != null ? paint : Theme.getThemePaint(paintKey); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } + + public interface Delegate { + + void replaceText(int start, int len, CharSequence replacingString, boolean allowShort); + + Paint.FontMetricsInt getFontMetrics(); + + default void onStickerSelected(TLRPC.TL_document document, String query, Object parent) { + + } + + default void addEmojiToRecent(String code) { + + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.emojiLoaded); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.emojiLoaded); + } + + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.emojiLoaded) { + getListView().invalidateViews(); + } + } + } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/MessageContainsEmojiButton.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/MessageContainsEmojiButton.java index 8cdac15708..f733033efe 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/MessageContainsEmojiButton.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/MessageContainsEmojiButton.java @@ -25,6 +25,7 @@ import org.telegram.messenger.MessageObject; import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; +import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.Theme; @@ -53,6 +54,7 @@ public class MessageContainsEmojiButton extends FrameLayout implements Notificat public final static int EMOJI_TYPE = 0; public final static int REACTIONS_TYPE = 1; + public final static int EMOJI_STICKER_TYPE = 2; int type; private class BoldAndAccent extends CharacterStyle { @@ -65,7 +67,7 @@ public void updateDrawState(TextPaint textPaint) { } } - public MessageContainsEmojiButton(int currentAccount, Context context, Theme.ResourcesProvider resourcesProvider, @NonNull ArrayList inputStickerSets, int type) { + private MessageContainsEmojiButton(int currentAccount, Context context, Theme.ResourcesProvider resourcesProvider, int type) { super(context); this.currentAccount = currentAccount; this.type = type; @@ -75,6 +77,28 @@ public MessageContainsEmojiButton(int currentAccount, Context context, Theme.Res textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); textPaint.setTextSize(AndroidUtilities.dp(13)); textPaint.setColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuItem, resourcesProvider)); + } + + public MessageContainsEmojiButton(int currentAccount, Context context, Theme.ResourcesProvider resourcesProvider, TLObject object) { + this(currentAccount, context, resourcesProvider, EMOJI_STICKER_TYPE); + + String string; + if (type == EMOJI_TYPE) { + string = LocaleController.getString("MessageContainsEmojiPack", R.string.MessageContainsEmojiPack); + } else { + string = LocaleController.getString("MessageContainsReactionsPack", R.string.MessageContainsReactionsPack); + } + String[] parts = string.split("%s"); + mainText = parts[0]; + endText = parts[1]; + loadingDrawable = new LoadingDrawable(resourcesProvider); + loadingDrawable.colorKey1 = Theme.key_actionBarDefaultSubmenuBackground; + loadingDrawable.colorKey2 = Theme.key_listSelector; + loadingDrawable.setRadiiDp(4); + } + + public MessageContainsEmojiButton(int currentAccount, Context context, Theme.ResourcesProvider resourcesProvider, @NonNull ArrayList inputStickerSets, int type) { + this(currentAccount, context, resourcesProvider, type); if (inputStickerSets.size() > 1) { String string; @@ -130,7 +154,7 @@ public void draw(@NonNull Canvas canvas, CharSequence charSequence, int start, i } }, 0, emoji.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); emojiDrawable = AnimatedEmojiDrawable.make(currentAccount, AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES, document); - emojiDrawable.setColorFilter(Theme.chat_animatedEmojiTextColorFilter); + emojiDrawable.setColorFilter(Theme.getAnimatedEmojiColorFilter(resourcesProvider)); emojiDrawable.addView(this); SpannableString stickerPack = new SpannableString(stickerPackName); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/MessageSeenCheckDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/MessageSeenCheckDrawable.java index 2506b974c4..7b2d0307ae 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/MessageSeenCheckDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/MessageSeenCheckDrawable.java @@ -8,10 +8,8 @@ import android.text.Spanned; import android.text.style.DynamicDrawableSpan; import android.text.style.ImageSpan; -import android.util.Log; import org.telegram.messenger.AndroidUtilities; -import org.telegram.messenger.R; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Cells.DialogCell; @@ -23,33 +21,33 @@ public class MessageSeenCheckDrawable { private float lastDensity; private int resId; - private String colorKey; + private int colorKey; private int w = -1, h = -1; private float oy = 4.66f; - public MessageSeenCheckDrawable(int resId, String colorKey) { + public MessageSeenCheckDrawable(int resId, int colorKey) { this.resId = resId; this.colorKey = colorKey; } - public MessageSeenCheckDrawable(int resId, String colorKey, int w, int h) { + public MessageSeenCheckDrawable(int resId, int colorKey, int w, int h) { this(resId, colorKey); this.w = w; this.h = h; } - public MessageSeenCheckDrawable(int resId, String colorKey, int w, int h, float oy) { + public MessageSeenCheckDrawable(int resId, int colorKey, int w, int h, float oy) { this(resId, colorKey); this.w = w; this.h = h; this.oy = oy; } - public CharSequence getSpanned(Context context) { + public CharSequence getSpanned(Context context, Theme.ResourcesProvider resourcesProvider) { if (lastSpanned != null && drawable != null && AndroidUtilities.density == lastDensity) { - if (lastColor != Theme.getColor(colorKey)) { - drawable.setColorFilter(new PorterDuffColorFilter(lastColor = Theme.getColor(colorKey), PorterDuff.Mode.SRC_IN)); + if (lastColor != Theme.getColor(colorKey, resourcesProvider)) { + drawable.setColorFilter(new PorterDuffColorFilter(lastColor = Theme.getColor(colorKey, resourcesProvider), PorterDuff.Mode.SRC_IN)); } return lastSpanned; } @@ -59,7 +57,7 @@ public CharSequence getSpanned(Context context) { SpannableStringBuilder str = new SpannableStringBuilder("v "); lastDensity = AndroidUtilities.density; drawable = context.getResources().getDrawable(resId).mutate(); - drawable.setColorFilter(new PorterDuffColorFilter(lastColor = Theme.getColor(colorKey), PorterDuff.Mode.SRC_IN)); + drawable.setColorFilter(new PorterDuffColorFilter(lastColor = Theme.getColor(colorKey, resourcesProvider), PorterDuff.Mode.SRC_IN)); final int w = this.w <= 0 ? drawable.getIntrinsicWidth() : AndroidUtilities.dp(this.w); final int h = this.h <= 0 ? drawable.getIntrinsicHeight() : AndroidUtilities.dp(this.h); final int oy = AndroidUtilities.dp(this.oy); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/MotionBackgroundDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/MotionBackgroundDrawable.java index b2b5461263..4cade21772 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/MotionBackgroundDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/MotionBackgroundDrawable.java @@ -30,7 +30,6 @@ import org.telegram.messenger.GenericProvider; import org.telegram.messenger.LiteMode; import org.telegram.messenger.NotificationCenter; -import org.telegram.messenger.SharedConfig; import org.telegram.messenger.Utilities; import java.lang.ref.WeakReference; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/MultilineTextCheckCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/MultilineTextCheckCell.java new file mode 100644 index 0000000000..96587de024 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/MultilineTextCheckCell.java @@ -0,0 +1,101 @@ +package org.telegram.ui.Components; + +import android.content.Context; +import android.graphics.Canvas; +import android.text.TextUtils; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.TextView; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.LocaleController; +import org.telegram.ui.ActionBar.Theme; + +import java.util.Locale; + +public class MultilineTextCheckCell extends FrameLayout { + + private LinearLayout textLayout; + private TextView titleTextView; + private TextView subtitleTextView; + private Switch checkBox; + + public MultilineTextCheckCell(Context context) { + this(context, null); + } + + public MultilineTextCheckCell(Context context, Theme.ResourcesProvider resourcesProvider) { + super(context); + + textLayout = new LinearLayout(context); + textLayout.setOrientation(LinearLayout.VERTICAL); + addView(textLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL, LocaleController.isRTL ? 70 : 22, 0, LocaleController.isRTL ? 22 : 70, 0)); + + titleTextView = new TextView(context); + titleTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); + titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + titleTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); + titleTextView.setEllipsize(TextUtils.TruncateAt.END); + textLayout.addView(titleTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + + subtitleTextView = new TextView(context); + subtitleTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2, resourcesProvider)); + subtitleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); + subtitleTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); + subtitleTextView.setEllipsize(TextUtils.TruncateAt.END); + textLayout.addView(subtitleTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.FILL_HORIZONTAL | Gravity.TOP, 0, 4, 0, 1)); + + checkBox = new Switch(context, resourcesProvider); + checkBox.setColors(Theme.key_switchTrack, Theme.key_switchTrackChecked, Theme.key_windowBackgroundWhite, Theme.key_windowBackgroundWhite); + addView(checkBox, LayoutHelper.createFrame(37, 20, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.CENTER_VERTICAL, 22, 0, 22, 0)); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure( + MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), + MeasureSpec.getSize(heightMeasureSpec) < AndroidUtilities.dp(50) ? + MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(50), MeasureSpec.EXACTLY) : + MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.AT_MOST) + ); + } + + public void setChecked(boolean checked) { + checkBox.setChecked(checked, true); + } + + private boolean needDivivider; + + public void setTextAndCheck(CharSequence title, boolean checked, boolean divider) { + titleTextView.setText(title); + subtitleTextView.setVisibility(View.GONE); + checkBox.setChecked(checked, false); + setWillNotDraw(!(needDivivider = divider)); + } + + public void setTextAndSubtextAndCheck(CharSequence title, CharSequence subtitle, boolean checked, boolean divider) { + titleTextView.setText(title); + subtitleTextView.setVisibility(View.VISIBLE); + subtitleTextView.setText(subtitle); + checkBox.setChecked(checked, false); + setWillNotDraw(!(needDivivider = divider)); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + if (needDivivider) { + canvas.drawRect( + LocaleController.isRTL ? 0 : AndroidUtilities.dp(22), + getMeasuredHeight() - 1, + LocaleController.isRTL ? AndroidUtilities.dp(22) : 0, + getMeasuredHeight(), + Theme.dividerPaint + ); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/NumberPicker.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/NumberPicker.java index d2e2bc6f2b..86a9f8452b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/NumberPicker.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/NumberPicker.java @@ -1205,8 +1205,7 @@ public void setDrawDividers(boolean drawDividers) { invalidate(); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/OutlineTextContainerView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/OutlineTextContainerView.java index f422a0b386..2a17821e79 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/OutlineTextContainerView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/OutlineTextContainerView.java @@ -93,9 +93,9 @@ private void setColor(int color) { public void updateColor() { int textSelectionColor = ColorUtils.blendARGB(Theme.getColor(Theme.key_windowBackgroundWhiteHintText), Theme.getColor(Theme.key_windowBackgroundWhiteValueText), forceUseCenter ? 0f : selectionProgress); - textPaint.setColor(ColorUtils.blendARGB(textSelectionColor, Theme.getColor(Theme.key_dialogTextRed), errorProgress)); + textPaint.setColor(ColorUtils.blendARGB(textSelectionColor, Theme.getColor(Theme.key_text_RedBold), errorProgress)); int selectionColor = ColorUtils.blendARGB(Theme.getColor(Theme.key_windowBackgroundWhiteInputField), Theme.getColor(Theme.key_windowBackgroundWhiteInputFieldActivated), forceUseCenter ? 0f : selectionProgress); - setColor(ColorUtils.blendARGB(selectionColor, Theme.getColor(Theme.key_dialogTextRed), errorProgress)); + setColor(ColorUtils.blendARGB(selectionColor, Theme.getColor(Theme.key_text_RedBold), errorProgress)); } public void animateSelection(float newValue) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PagerSlidingTabStrip.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PagerSlidingTabStrip.java index d2ce07a7d9..82a7b7594a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PagerSlidingTabStrip.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PagerSlidingTabStrip.java @@ -27,6 +27,7 @@ import android.widget.LinearLayout; import android.widget.TextView; +import androidx.core.graphics.ColorUtils; import androidx.viewpager.widget.ViewPager; import androidx.viewpager.widget.ViewPager.OnPageChangeListener; @@ -178,42 +179,17 @@ public void setSelected(boolean selected) { } private void addTab(final int position, CharSequence text) { - TextView tab = new TextView(getContext()) { - - @Override - public void setAlpha(float alpha) { - super.setAlpha(alpha); - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - if (pager.getAdapter() instanceof IconTabProvider) { - ((IconTabProvider) pager.getAdapter()).customOnDraw(canvas, this, position); - } - } - - @Override - public void setSelected(boolean selected) { - super.setSelected(selected); - Drawable background = getBackground(); - if (Build.VERSION.SDK_INT >= 21 && background != null) { - int color = getThemedColor(selected ? Theme.key_chat_emojiPanelIconSelected : Theme.key_chat_emojiBottomPanelIcon); - Theme.setSelectorDrawableColor(background, Color.argb(30, Color.red(color), Color.green(color), Color.blue(color)), true); - } - setTextColor(getThemedColor(selected ? Theme.key_chat_emojiPanelIconSelected : Theme.key_chat_emojiPanelBackspace)); - } - }; + TextTab tab = new TextTab(getContext(), position); tab.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); tab.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); tab.setTextColor(getThemedColor(Theme.key_chat_emojiPanelBackspace)); tab.setFocusable(true); tab.setGravity(Gravity.CENTER); - if (Build.VERSION.SDK_INT >= 21) { - RippleDrawable rippleDrawable = (RippleDrawable) Theme.createSelectorDrawable(getThemedColor(Theme.key_chat_emojiBottomPanelIcon), Theme.RIPPLE_MASK_CIRCLE_TO_BOUND_EDGE); - Theme.setRippleDrawableForceSoftware(rippleDrawable); - tab.setBackground(rippleDrawable); - } +// if (Build.VERSION.SDK_INT >= 21) { +// RippleDrawable rippleDrawable = (RippleDrawable) Theme.createSelectorDrawable(getThemedColor(Theme.key_chat_emojiBottomPanelIcon), Theme.RIPPLE_MASK_CIRCLE_TO_BOUND_EDGE); +// Theme.setRippleDrawableForceSoftware(rippleDrawable); +// tab.setBackground(rippleDrawable); +// } tab.setText(text); tab.setOnClickListener(v -> { if (pager.getAdapter() instanceof IconTabProvider) { @@ -278,9 +254,9 @@ private void scrollToChild(int position, int offset) { @Override protected void onDraw(Canvas canvas) { - super.onDraw(canvas); if (isInEditMode() || tabCount == 0) { + super.onDraw(canvas); return; } @@ -307,6 +283,13 @@ protected void onDraw(Canvas canvas) { lineLeftAnimated.set(lineLeft, true); lineRightAnimated.set(lineRight, true); + + if (currentTab instanceof TextTab) { + ((TextTab) currentTab).setSelectedProgress(1f - currentPositionOffset); + } + if (nextTab instanceof TextTab) { + ((TextTab) nextTab).setSelectedProgress(currentPositionOffset); + } } else { lineLeft = lineLeftAnimated.set(lineLeft); lineRight = lineRightAnimated.set(lineRight); @@ -314,10 +297,12 @@ protected void onDraw(Canvas canvas) { if (indicatorHeight != 0) { rectPaint.setColor(indicatorColor); - AndroidUtilities.rectTmp.set(lineLeft, height - indicatorHeight, lineRight, height); - canvas.drawRoundRect(AndroidUtilities.rectTmp, indicatorHeight / 2f, indicatorHeight / 2f, rectPaint); + AndroidUtilities.rectTmp.set(lineLeft - AndroidUtilities.dp(12), AndroidUtilities.dp(6), lineRight + AndroidUtilities.dp(12), height - AndroidUtilities.dp(6)); + canvas.drawRoundRect(AndroidUtilities.rectTmp, AndroidUtilities.rectTmp.height() / 2f, AndroidUtilities.rectTmp.height() / 2f, rectPaint); } } + + super.onDraw(canvas); } private class PageListener implements OnPageChangeListener { @@ -357,9 +342,8 @@ public void onPageSelected(int position) { } } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } public void onSizeChanged(int paramInt1, int paramInt2, int paramInt3, int paramInt4) { @@ -451,4 +435,38 @@ public void setTabPaddingLeftRight(int paddingPx) { public int getTabPaddingLeftRight() { return tabPadding; } + + private class TextTab extends TextView { + + final int position; + public TextTab(Context context, int position) { + super(context); + this.position = position; + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + if (pager.getAdapter() instanceof IconTabProvider) { + ((IconTabProvider) pager.getAdapter()).customOnDraw(canvas, this, position); + } + } + + @Override + public void setSelected(boolean selected) { + super.setSelected(selected); + Drawable background = getBackground(); + if (Build.VERSION.SDK_INT >= 21 && background != null) { + int color = getThemedColor(selected ? Theme.key_chat_emojiPanelIconSelected : Theme.key_chat_emojiBottomPanelIcon); + Theme.setSelectorDrawableColor(background, Color.argb(30, Color.red(color), Color.green(color), Color.blue(color)), true); + } + setTextColor(getThemedColor(selected ? Theme.key_chat_emojiPanelIconSelected : Theme.key_chat_emojiPanelBackspace)); + } + + public void setSelectedProgress(float progress) { + int selectedColor = getThemedColor(Theme.key_chat_emojiPanelIconSelected); + int color = getThemedColor(Theme.key_chat_emojiPanelBackspace); + setTextColor(ColorUtils.blendARGB(color, selectedColor, progress)); + } + }; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/PaintTypeface.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/PaintTypeface.java index 666190d5c5..c80d44ad53 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/PaintTypeface.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/PaintTypeface.java @@ -6,12 +6,16 @@ import android.graphics.fonts.SystemFonts; import android.os.Build; import android.text.TextUtils; +import android.util.Log; +import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.FileLog; +import org.telegram.messenger.GenericProvider; import org.telegram.messenger.LocaleController; +import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.Utilities; import java.io.File; @@ -29,14 +33,14 @@ public class PaintTypeface { private final static boolean SYSTEM_FONTS_ENABLED = true; - public static final PaintTypeface ROBOTO_MEDIUM = new PaintTypeface("roboto", "PhotoEditorTypefaceRoboto", AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); - public static final PaintTypeface ROBOTO_ITALIC = new PaintTypeface("italic", "PhotoEditorTypefaceItalic", AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM_ITALIC)); - public static final PaintTypeface ROBOTO_SERIF = new PaintTypeface("serif", "PhotoEditorTypefaceSerif", Typeface.create("serif", Typeface.BOLD)); - public static final PaintTypeface ROBOTO_MONO = new PaintTypeface ("mono", "PhotoEditorTypefaceMono", AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MONO)); - public static final PaintTypeface MW_BOLD = new PaintTypeface("mw_bold", "PhotoEditorTypefaceMerriweather", AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_MERRIWEATHER_BOLD)); - public static final PaintTypeface COURIER_NEW_BOLD = new PaintTypeface("courier_new_bold", "PhotoEditorTypefaceCourierNew", AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_COURIER_NEW_BOLD)); + public static final PaintTypeface ROBOTO_MEDIUM = new PaintTypeface("roboto", "PhotoEditorTypefaceRoboto", new LazyTypeface(() -> AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM))); + public static final PaintTypeface ROBOTO_ITALIC = new PaintTypeface("italic", "PhotoEditorTypefaceItalic", new LazyTypeface(() -> AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM_ITALIC))); + public static final PaintTypeface ROBOTO_SERIF = new PaintTypeface("serif", "PhotoEditorTypefaceSerif", new LazyTypeface(() -> Typeface.create("serif", Typeface.BOLD))); + public static final PaintTypeface ROBOTO_MONO = new PaintTypeface ("mono", "PhotoEditorTypefaceMono", new LazyTypeface(() -> AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MONO))); + public static final PaintTypeface MW_BOLD = new PaintTypeface("mw_bold", "PhotoEditorTypefaceMerriweather", new LazyTypeface(() -> AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_MERRIWEATHER_BOLD))); + public static final PaintTypeface COURIER_NEW_BOLD = new PaintTypeface("courier_new_bold", "PhotoEditorTypefaceCourierNew", new LazyTypeface(() -> AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_COURIER_NEW_BOLD))); - private final static List BUILT_IN_FONTS = Arrays.asList(ROBOTO_MEDIUM, ROBOTO_ITALIC, ROBOTO_SERIF, ROBOTO_MONO, MW_BOLD, COURIER_NEW_BOLD); + public final static List BUILT_IN_FONTS = Arrays.asList(ROBOTO_MEDIUM, ROBOTO_ITALIC, ROBOTO_SERIF, ROBOTO_MONO, MW_BOLD, COURIER_NEW_BOLD); private static final List preferable = Arrays.asList( "Google Sans", @@ -51,14 +55,44 @@ public class PaintTypeface { private final String nameKey; private final String name; private final Typeface typeface; + private final LazyTypeface lazyTypeface; private final Font font; private Paint paint; + private static class LazyTypeface { + public interface LazyTypefaceLoader { + Typeface load(); + } + + private final LazyTypefaceLoader loader; + private Typeface typeface; + public LazyTypeface(LazyTypefaceLoader loader) { + this.loader = loader; + } + + public Typeface get() { + if (typeface == null) { + typeface = loader.load(); + } + return typeface; + } + } + PaintTypeface(String key, String nameKey, Typeface typeface) { this.key = key; this.nameKey = nameKey; this.name = null; this.typeface = typeface; + this.lazyTypeface = null; + this.font = null; + } + + PaintTypeface(String key, String nameKey, LazyTypeface lazyTypeface) { + this.key = key; + this.nameKey = nameKey; + this.name = null; + this.typeface = null; + this.lazyTypeface = lazyTypeface; this.font = null; } @@ -67,7 +101,8 @@ public class PaintTypeface { this.key = name; this.name = name; this.nameKey = null; - this.typeface = Typeface.createFromFile(font.getFile()); + this.typeface = null; + this.lazyTypeface = new LazyTypeface(() -> Typeface.createFromFile(font.getFile())); this.font = font; } @@ -92,6 +127,9 @@ public String getKey() { } public Typeface getTypeface() { + if (lazyTypeface != null) { + return lazyTypeface.get(); + } return typeface; } @@ -103,9 +141,15 @@ public String getName() { } private static List typefaces; - public static List get() { - if (typefaces == null) { - typefaces = new ArrayList<>(BUILT_IN_FONTS); + public static boolean loadingTypefaces; + + private static void load() { + if (typefaces != null || loadingTypefaces) { + return; + } + loadingTypefaces = true; + Utilities.themeQueue.postRunnable(() -> { + ArrayList typefaces = new ArrayList(BUILT_IN_FONTS); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && SYSTEM_FONTS_ENABLED) { Set fonts = SystemFonts.getAvailableFonts(); Iterator i = fonts.iterator(); @@ -125,23 +169,31 @@ public static List get() { } } -// if (BuildVars.DEBUG_PRIVATE_VERSION) { -// for (Family family : families.values()) { -// if (family != null) { -// FontData regular = family.getRegular(); -// typefaces.add(new PaintTypeface(regular.font, regular.getName())); -// } -// } -// } else { - for (String familyName : preferable) { - Family family = families.get(familyName); - if (family != null) { - FontData regular = family.getRegular(); - typefaces.add(new PaintTypeface(regular.font, regular.getName())); + for (String familyName : preferable) { + Family family = families.get(familyName); + if (family != null) { + FontData font = family.getBold(); + if (font == null) { + font = family.getRegular(); + } + if (font != null) { + typefaces.add(new PaintTypeface(font.font, font.getName())); } } -// } + } } + AndroidUtilities.runOnUIThread(() -> { + PaintTypeface.typefaces = typefaces; + loadingTypefaces = false; + NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.customTypefacesLoaded); + }); + }); + } + + public static List get() { + if (typefaces == null) { + load(); + return BUILT_IN_FONTS; } return typefaces; } @@ -150,12 +202,7 @@ public static PaintTypeface find(String key) { if (key == null || TextUtils.isEmpty(key)) { return null; } - if (typefaces == null) { - get(); - } - if (typefaces == null) { - return null; - } + List typefaces = get(); for (int i = 0; i < typefaces.size(); ++i) { PaintTypeface typeface = typefaces.get(i); if (typeface != null && TextUtils.equals(key, typeface.key)) { @@ -165,17 +212,6 @@ public static PaintTypeface find(String key) { return null; } - public static boolean fetched(Runnable runnable) { - if (typefaces != null || runnable == null) { - return true; - } - Utilities.themeQueue.postRunnable(() -> { - get(); - AndroidUtilities.runOnUIThread(runnable); - }); - return false; - } - static class Family { String family; ArrayList fonts = new ArrayList<>(); @@ -193,6 +229,16 @@ public FontData getRegular() { } return regular; } + + @Nullable + public FontData getBold() { + for (int j = 0; j < fonts.size(); ++j) { + if ("Bold".equalsIgnoreCase(fonts.get(j).subfamily)) { + return fonts.get(j); + } + } + return null; + } } static class FontData { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/PersistColorPalette.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/PersistColorPalette.java index 7f90fc0c9b..073997b104 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/PersistColorPalette.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/PersistColorPalette.java @@ -17,6 +17,8 @@ public class PersistColorPalette { public final static int COLORS_COUNT = 14; private final static List DEFAULT_COLORS = Arrays.asList( + 0xff000000, + 0xffFFFFFF, 0xff1D99FF, 0xff03BCD4, 0xff39BA2B, @@ -28,9 +30,7 @@ public class PersistColorPalette { 0xffAC734C, 0xff90512C, 0xff532E1F, - 0xff000000, - 0xff818181, - 0xffFFFFFF + 0xff818181 ); private final static Integer DEFAULT_MARKER_COLOR = 0xff0a84ff; @@ -143,7 +143,12 @@ private void checkIndex(int i) { public int getColor(int index) { checkIndex(index); - + if (index < 0 || index >= colors.size()) { + if (index >= 0 && index < DEFAULT_COLORS.size()) { + return DEFAULT_COLORS.get(index); + } + return DEFAULT_COLORS.get(0); + } return colors.get(index); } @@ -157,20 +162,35 @@ public void selectColor(int color) { pendingChange.clear(); pendingChange.add(color); pendingChange.addAll(from); - pendingChange.remove(pendingChange.size() - 1); + if (pendingChange.size() < DEFAULT_COLORS.size()) { + for (int j = pendingChange.size(); j < DEFAULT_COLORS.size(); ++j) { + pendingChange.add(DEFAULT_COLORS.get(j)); + } + } else if (pendingChange.size() > DEFAULT_COLORS.size()) { + pendingChange = pendingChange.subList(0, DEFAULT_COLORS.size()); + } } } public void selectColorIndex(int index) { - int color = colors.get(index); + int color = index < 0 || index >= colors.size() ? DEFAULT_COLORS.get(index) : colors.get(index); List from = new ArrayList<>(pendingChange.isEmpty() ? colors : pendingChange); pendingChange.clear(); pendingChange.add(color); for (int i = 0; i < COLORS_COUNT; i++) { - if (from.get(i) != color) { + if (i >= from.size()) { + pendingChange.add(DEFAULT_COLORS.get(i)); + } else if (from.get(i) != color) { pendingChange.add(from.get(i)); } } + if (pendingChange.size() < DEFAULT_COLORS.size()) { + for (int j = pendingChange.size(); j < DEFAULT_COLORS.size(); ++j) { + pendingChange.add(DEFAULT_COLORS.get(j)); + } + } else if (pendingChange.size() > DEFAULT_COLORS.size()) { + pendingChange = pendingChange.subList(0, DEFAULT_COLORS.size()); + } } private void loadColors() { @@ -187,7 +207,7 @@ public void saveColors() { SharedPreferences.Editor editor = mConfig.edit(); for (int i = 0; i < COLORS_COUNT; i++) { - editor.putLong("color_" + i, pendingChange.get(i)); + editor.putLong("color_" + i, i < pendingChange.size() ? pendingChange.get(i) : (long) DEFAULT_COLORS.get(i)); } editor.apply(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/EditTextOutline.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/EditTextOutline.java index ff151a1a68..384255308e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/EditTextOutline.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/EditTextOutline.java @@ -1,10 +1,13 @@ package org.telegram.ui.Components.Paint.Views; +import static org.telegram.messenger.AndroidUtilities.dp; + import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.CornerPathEffect; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PorterDuff; @@ -31,6 +34,8 @@ public class EditTextOutline extends EditTextBoldCursor { private int mFrameColor; private Path path = new Path(); + public boolean betterFraming; + private RectF[] lines; private boolean isFrameDirty; @@ -42,9 +47,17 @@ public EditTextOutline(Context context) { mUpdateCachedBitmap = true; isFrameDirty = true; + setFrameRoundRadius(dp(16)); textPaint.setStyle(Paint.Style.FILL_AND_STROKE); } + private float lastFrameRoundRadius; + private void setFrameRoundRadius(float roundRadius) { + if (Math.abs(lastFrameRoundRadius - roundRadius) > 0.1f) { + paint.setPathEffect(new CornerPathEffect(lastFrameRoundRadius = roundRadius)); + } + } + protected void onTextChanged(CharSequence text, int start, int before, int after) { super.onTextChanged(text, start, before, after); mUpdateCachedBitmap = true; @@ -81,10 +94,11 @@ public void setStrokeColor(int strokeColor) { public void setFrameColor(int frameColor) { if (mFrameColor == 0 && frameColor != 0) { - setPadding(AndroidUtilities.dp(7 + 12), AndroidUtilities.dp(7), AndroidUtilities.dp(7 + 12), AndroidUtilities.dp(7)); - setCursorColor(0xff000000); + setPadding(dp(7 + 12), dp(7), dp(7 + 12), dp(7)); + setCursorColor(0xffffffff); +// setCursorColor(0xff000000); } else if (mFrameColor != 0 && frameColor == 0) { - setPadding(AndroidUtilities.dp(7), AndroidUtilities.dp(7), AndroidUtilities.dp(7), AndroidUtilities.dp(7)); + setPadding(dp(7), dp(7), dp(7), dp(7)); setCursorColor(0xffffffff); } mFrameColor = frameColor; @@ -146,7 +160,10 @@ protected void onDraw(Canvas canvas) { canvas.drawBitmap(mCache, 0, 0, textPaint); } if (mFrameColor != 0) { - // have you heard about CornerPathEffect? + canvas.save(); + if (betterFraming) { + canvas.translate(getPaddingLeft(), getPaddingTop()); + } paint.setColor(mFrameColor); Layout layout = getLayout(); if (layout == null) { @@ -165,96 +182,75 @@ protected void onDraw(Canvas canvas) { } lines[i].set(layout.getLineLeft(i), layout.getLineTop(i), layout.getLineRight(i), layout.getLineBottom(i)); - if (lines[i].width() > AndroidUtilities.dp(1)) { - int pad = AndroidUtilities.dp(6); - lines[i].right += AndroidUtilities.dp(32); - lines[i].bottom += pad; + if (lines[i].width() > dp(1)) { + if (betterFraming) { + lines[i].inset(-getTextSize() / 3f, 0); + lines[i].top += AndroidUtilities.dpf2(1.2f); + lines[i].bottom += AndroidUtilities.dpf2(1); + lines[i].left = Math.max(-getPaddingLeft(), lines[i].left); + lines[i].right = Math.min(getWidth() - getPaddingLeft(), lines[i].right); + } else { + lines[i].right += dp(32); + lines[i].bottom += dp(6); + } } else { lines[i].left = lines[i].right; } + + if (i > 0 && lines[i - 1].width() > 0) { + lines[i - 1].bottom = lines[i].top; + } } } path.rewind(); - - float rad = AndroidUtilities.dp(16); - float cornersOffset = AndroidUtilities.dp(8); - - for (int i = 0; i < lines.length; i++) { + float h = getHeight(); + for (int i = 0; i < lines.length; ++i) { if (lines[i].width() == 0) { continue; } - - if (i != lines.length - 1 && lines[i].left > lines[i + 1].left) { // Left top arc corner - if (lines[i].left - lines[i + 1].left > rad + cornersOffset) { - float bottom = lines[i + 1].top; - AndroidUtilities.rectTmp.set(lines[i].left - rad * 2, bottom - rad * 2, lines[i].left, bottom); - path.moveTo(AndroidUtilities.rectTmp.left, AndroidUtilities.rectTmp.bottom); - path.arcTo(AndroidUtilities.rectTmp, 90, -90); - path.lineTo(AndroidUtilities.rectTmp.right, AndroidUtilities.rectTmp.bottom); - path.lineTo(AndroidUtilities.rectTmp.left, AndroidUtilities.rectTmp.bottom); - } + h = lines[i].bottom - lines[i].top; + } + float r = Math.min(h / 3f, dp(16)), mr = r * 1.5f; + for (int i = 1; i < lines.length; ++i) { + RectF above = lines[i - 1]; + RectF line = lines[i]; + if (above.width() < dp(1) || line.width() < dp(1)) { + continue; } - - if (i != lines.length - 1 && lines[i].right < lines[i + 1].right) { // Right top arc corner - if (lines[i + 1].right - lines[i].right > rad + cornersOffset) { - float bottom = lines[i + 1].top; - AndroidUtilities.rectTmp.set(lines[i].right, bottom - rad * 2, lines[i].right + rad * 2, bottom); - path.moveTo(AndroidUtilities.rectTmp.right, AndroidUtilities.rectTmp.bottom); - path.arcTo(AndroidUtilities.rectTmp, 90, 90); - path.lineTo(AndroidUtilities.rectTmp.left, AndroidUtilities.rectTmp.bottom); - path.lineTo(AndroidUtilities.rectTmp.right, AndroidUtilities.rectTmp.bottom); - } + boolean traceback = false; + if (Math.abs(above.left - line.left) < mr) { + line.left = above.left = Math.min(line.left, above.left); + traceback = true; } - - if (i != 0 && lines[i].left > lines[i - 1].left) { // Left bottom arc corner - if (lines[i].left - lines[i - 1].left > rad + cornersOffset) { - float top = lines[i - 1].bottom; - AndroidUtilities.rectTmp.set(lines[i].left - rad * 2, top, lines[i].left, top + rad * 2); - path.moveTo(AndroidUtilities.rectTmp.left, AndroidUtilities.rectTmp.top); - path.arcTo(AndroidUtilities.rectTmp, -90, 90); - path.lineTo(AndroidUtilities.rectTmp.right, AndroidUtilities.rectTmp.top); - path.lineTo(AndroidUtilities.rectTmp.left, AndroidUtilities.rectTmp.top); - } else { - lines[i].left = lines[i - 1].left; - } + if (Math.abs(above.right - line.right) < mr) { + line.right = above.right = Math.max(line.right, above.right); + traceback = true; } - if (i != 0 && lines[i].right < lines[i - 1].right) { // Right bottom arc corner - if (lines[i - 1].right - lines[i].right > rad + cornersOffset) { - float top = lines[i - 1].bottom; - AndroidUtilities.rectTmp.set(lines[i].right, top, lines[i].right + rad * 2, top + rad * 2); - path.moveTo(AndroidUtilities.rectTmp.right, AndroidUtilities.rectTmp.top); - path.arcTo(AndroidUtilities.rectTmp, -90, -90); - path.lineTo(AndroidUtilities.rectTmp.left, AndroidUtilities.rectTmp.top); - path.lineTo(AndroidUtilities.rectTmp.right, AndroidUtilities.rectTmp.top); - } else { - lines[i].right = lines[i - 1].right; + if (traceback) { + for (int j = i; j >= 1; --j) { + above = lines[j - 1]; + line = lines[j]; + if (above.width() < dp(1) || line.width() < dp(1)) { + continue; + } + if (Math.abs(above.left - line.left) < mr) { + line.left = above.left = Math.min(line.left, above.left); + } + if (Math.abs(above.right - line.right) < mr) { + line.right = above.right = Math.max(line.right, above.right); + } } } } - - float[] radii = new float[8]; - for (int i = 0; i < lines.length; i++) { - Arrays.fill(radii, 0); - - if (i == 0 || lines[i].left < lines[i - 1].left || lines[i - 1].width() == 0) { - radii[0] = radii[1] = rad; // Top left corner - } - if (i == 0 || lines[i].right > lines[i - 1].right || lines[i - 1].width() == 0) { - radii[2] = radii[3] = rad; // Top right corner - } - if (i == lines.length - 1 || lines[i + 1].left > lines[i].left || lines[i + 1].width() == 0) { - radii[6] = radii[7] = rad; // Bottom left corner - } - if (i == lines.length - 1 || lines[i + 1].right < lines[i].right || lines[i + 1].width() == 0) { - radii[4] = radii[5] = rad; // Bottom right corner + for (int i = 0; i < lines.length; ++i) { + if (lines[i].width() == 0) { + continue; } - - AndroidUtilities.rectTmp.set(lines[i]); - path.addRoundRect(AndroidUtilities.rectTmp, radii, Path.Direction.CW); + path.addRect(lines[i], Path.Direction.CW); } - path.close(); - + setFrameRoundRadius(r); canvas.drawPath(path, paint); + canvas.restore(); } super.onDraw(canvas); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/EntityView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/EntityView.java index 8332074a0c..c7954187fa 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/EntityView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/EntityView.java @@ -4,8 +4,10 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.content.Context; +import android.graphics.Canvas; import android.graphics.DashPathEffect; import android.graphics.Paint; +import android.util.Log; import android.view.GestureDetector; import android.view.HapticFeedbackConstants; import android.view.MotionEvent; @@ -13,7 +15,12 @@ import android.view.ViewGroup; import android.widget.FrameLayout; +import com.google.zxing.common.detector.MathUtils; + import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.Utilities; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.ButtonBounce; import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.Point; import org.telegram.ui.Components.Rect; @@ -31,16 +38,24 @@ public class EntityView extends FrameLayout { private final static float STICKY_THRESHOLD_ANGLE = 15; private final static float STICKY_TRIGGER_ANGLE = 5; - private final static float STICKY_THRESHOLD_DP = 48; - private final static float STICKY_TRIGGER_DP = 16; + private final static float STICKY_THRESHOLD_DP = 16; + private final static float STICKY_TRIGGER_DP = 6; + + private ButtonBounce bounce = new ButtonBounce(this); public interface EntityViewDelegate { boolean onEntitySelected(EntityView entityView); boolean onEntityLongClicked(EntityView entityView); boolean allowInteraction(EntityView entityView); int[] getCenterLocation(EntityView entityView); - float[] getTransformedTouch(float x, float y); + float[] getTransformedTouch(MotionEvent e, float x, float y); float getCropRotation(); + + default void onEntityDraggedTop(boolean value) {} + default void onEntityDraggedBottom(boolean value) {} + default void onEntityDragStart() {} + default void onEntityDragEnd(boolean delete) {} + default void onEntityDragTrash(boolean enter) {} } private float previousLocationX; @@ -48,7 +63,9 @@ public interface EntityViewDelegate { private boolean hasPanned = false; private boolean hasReleased = false; private boolean hasTransformed = false; + private boolean announcedDrag = false; private boolean announcedSelection = false; + private boolean announcedTrash = false; private boolean recognizedLongPress = false; private EntityViewDelegate delegate; @@ -148,15 +165,41 @@ private boolean onTouchMove(float x, float y) { ((EntitiesContainerView) getParent()).invalidate(); } + if (!announcedDrag && delegate != null) { + announcedDrag = true; + delegate.onEntityDragStart(); + } + if (!isSelected() && !announcedSelection && delegate != null) { + delegate.onEntitySelected(this); + announcedSelection = true; + } + + if (delegate != null) { + delegate.onEntityDraggedTop(position.y - getHeight() / 2f * scale < AndroidUtilities.dp(66)); + delegate.onEntityDraggedBottom(position.y + getHeight() / 2f * scale > ((View) getParent()).getHeight() - AndroidUtilities.dp(64 + 50)); + } + + updateTrash(MathUtils.distance(x, y, ((View) getParent()).getWidth() / 2f, ((View) getParent()).getHeight() - AndroidUtilities.dp(76)) < AndroidUtilities.dp(32)); + + bounce.setPressed(false); + return true; } return false; } private void onTouchUp() { + if (announcedDrag) { + delegate.onEntityDragEnd(announcedTrash); + announcedDrag = false; + } if (!recognizedLongPress && !hasPanned && !hasTransformed && !announcedSelection && delegate != null) { delegate.onEntitySelected(this); } + if (hasPanned && delegate != null) { + delegate.onEntityDraggedTop(false); + delegate.onEntityDraggedBottom(false); + } recognizedLongPress = false; hasPanned = false; hasTransformed = false; @@ -198,17 +241,13 @@ public boolean onTouchEvent(MotionEvent event) { return false; } - float[] xy = delegate.getTransformedTouch(event.getRawX(), event.getRawY()); + float[] xy = delegate.getTransformedTouch(event, event.getRawX(), event.getRawY()); int action = event.getActionMasked(); boolean handled = false; switch (action) { case MotionEvent.ACTION_POINTER_DOWN: case MotionEvent.ACTION_DOWN: { - if (!isSelected() && delegate != null) { - delegate.onEntitySelected(this); - announcedSelection = true; - } previousLocationX = xy[0]; previousLocationY = xy[1]; handled = true; @@ -217,6 +256,7 @@ public boolean onTouchEvent(MotionEvent event) { if (getParent() instanceof EntitiesContainerView && (hasStickyX || hasStickyY)) { ((EntitiesContainerView) getParent()).invalidate(); } + bounce.setPressed(true); } break; @@ -229,6 +269,7 @@ public boolean onTouchEvent(MotionEvent event) { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: { onTouchUp(); + bounce.setPressed(false); handled = true; } break; @@ -236,7 +277,7 @@ public boolean onTouchEvent(MotionEvent event) { gestureDetector.onTouchEvent(event); - return handled; + return super.onTouchEvent(event) || handled; } private void runStickyXAnimator(float... values) { @@ -287,7 +328,7 @@ public void pan(float tx, float ty) { View parent = (View) getParent(); if (parent != null) { if (!hasStickyX) { - if (Math.abs(position.x - parent.getMeasuredWidth() / 2f) <= AndroidUtilities.dp(STICKY_TRIGGER_DP)) { + if (Math.abs(position.x - parent.getMeasuredWidth() / 2f) <= AndroidUtilities.dp(STICKY_TRIGGER_DP) && position.y < parent.getMeasuredHeight() - AndroidUtilities.dp(112 + 64)) { hasStickyX = true; try { performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); @@ -305,7 +346,7 @@ public void pan(float tx, float ty) { runStickyXAnimator(0, 1); } } else { - if (Math.abs(position.x - parent.getMeasuredWidth() / 2f) > AndroidUtilities.dp(STICKY_THRESHOLD_DP)) { + if (Math.abs(position.x - parent.getMeasuredWidth() / 2f) > AndroidUtilities.dp(STICKY_THRESHOLD_DP) || position.y >= parent.getMeasuredHeight() - AndroidUtilities.dp(112 + 64)) { hasStickyX = false; if (getParent() instanceof EntitiesContainerView) { ((EntitiesContainerView) getParent()).invalidate(); @@ -483,7 +524,7 @@ protected Rect getSelectionBounds() { } public boolean isSelected() { - return selectionView != null; + return selecting; } protected SelectionView createSelectionView() { @@ -496,34 +537,60 @@ public void updateSelectionView() { } } - public void select(ViewGroup selectionContainer) { - SelectionView selectionView = createSelectionView(); - selectionView.setAlpha(0f); - selectionView.setScaleX(0.9f); - selectionView.setScaleY(0.9f); - selectionView.animate().cancel(); - selectionView.animate().alpha(1f).scaleX(1).scaleY(1).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).setListener(null).start(); - this.selectionView = selectionView; - selectionContainer.addView(selectionView); - selectionView.updatePosition(); - } + private float selectT; + private ValueAnimator selectAnimator; + private boolean selecting = false; + private void updateSelect(ViewGroup selectionContainer, boolean select) { + if (selecting != select) { + selecting = select; - public void deselect() { - if (selectionView == null) { - return; - } - if (selectionView.getParent() != null) { - selectionView.animate().cancel(); - selectionView.animate().alpha(0f).scaleX(0.9f).scaleY(0.9f).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).setListener(new AnimatorListenerAdapter() { + if (selectAnimator != null) { + selectAnimator.cancel(); + selectAnimator = null; + } + + if (selectionView == null) { + if (!select && selectionContainer == null) { + return; + } + selectionView = createSelectionView(); + selectionContainer.addView(selectionView); + selectT = 0; + } + selectionView.updatePosition(); + + selectAnimator = ValueAnimator.ofFloat(selectT, select ? 1f : 0f); + selectAnimator.addUpdateListener(anm -> { + selectT = (float) anm.getAnimatedValue(); + if (selectionView != null) { + selectionView.setScaleX(AndroidUtilities.lerp(0.9f, 1f, selectT) * Utilities.clamp(trashScale * 1.25f, 1, 0)); + selectionView.setScaleY(AndroidUtilities.lerp(0.9f, 1f, selectT) * Utilities.clamp(trashScale * 1.25f, 1, 0)); + selectionView.setAlpha(selectT * Math.max(0, trashScale - .8f) * 5); + } + }); + selectAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - AndroidUtilities.removeFromParent(selectionView); - selectionView = null; + if (!selecting) { + AndroidUtilities.removeFromParent(selectionView); + selectionView = null; + } } - }).start(); + }); + selectAnimator.setDuration(280); + selectAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + selectAnimator.start(); } } + public void select(ViewGroup selectionContainer) { + updateSelect(selectionContainer, true); + } + + public void deselect() { + updateSelect(null, false); + } + public void setSelectionVisibility(boolean visible) { if (selectionView == null) { return; @@ -552,13 +619,13 @@ public SelectionView(Context context) { paint.setStrokeWidth(AndroidUtilities.dp(2)); paint.setStrokeCap(Paint.Cap.ROUND); paint.setPathEffect(new DashPathEffect(new float[]{AndroidUtilities.dp(10), AndroidUtilities.dp(10)}, .5f)); - paint.setShadowLayer(AndroidUtilities.dp(0.75f), 0, AndroidUtilities.dp(1), 0x70000000); + paint.setShadowLayer(AndroidUtilities.dpf2(0.75f), 0, 0, 0x50000000); - dotPaint.setColor(0xff3ccaef); + dotPaint.setColor(0xff1A9CFF); dotStrokePaint.setColor(0xffffffff); dotStrokePaint.setStyle(Paint.Style.STROKE); - dotStrokePaint.setStrokeWidth(AndroidUtilities.dp(2)); - dotStrokePaint.setShadowLayer(AndroidUtilities.dp(0.75f), 0, AndroidUtilities.dp(1), 0x70000000); + dotStrokePaint.setStrokeWidth(AndroidUtilities.dpf2(2.66f)); + dotStrokePaint.setShadowLayer(AndroidUtilities.dpf2(0.75f), 0, 0, 0x50000000); } protected void updatePosition() { @@ -583,7 +650,7 @@ public boolean onTouchEvent(MotionEvent event) { float rawX = event.getRawX(); float rawY = event.getRawY(); - float[] xy = delegate.getTransformedTouch(rawX, rawY); + float[] xy = delegate.getTransformedTouch(event, rawX, rawY); float x = xy[0]; float y = xy[1]; switch (action) { @@ -614,23 +681,30 @@ public boolean onTouchEvent(MotionEvent event) { if (hasTransformed || Math.abs(tx) > AndroidUtilities.dp(2) || Math.abs(ty) > AndroidUtilities.dp(2)) { hasTransformed = true; - float radAngle = (float) Math.toRadians(getRotation()); - float delta = (float) (tx * Math.cos(radAngle) + ty * Math.sin(radAngle)); - if (currentHandle == SELECTION_LEFT_HANDLE) { - delta *= -1; - } +// float radAngle = (float) Math.toRadians(getRotation()); +// float delta = (float) (tx * Math.cos(radAngle) + ty * Math.sin(radAngle)); +// if (currentHandle == SELECTION_LEFT_HANDLE) { +// delta *= -1; +// } +// +// if (getMeasuredWidth() != 0) { +// float scaleDelta = 1 + (delta * 2) / getMeasuredWidth(); +// scale(scaleDelta); +// } - if (getMeasuredWidth() != 0) { - float scaleDelta = 1 + (delta * 2) / getMeasuredWidth(); - scale(scaleDelta); + int[] pos = delegate.getCenterLocation(EntityView.this); + float pd = MathUtils.distance(pos[0], pos[1], previousLocationX, previousLocationY); + float d = MathUtils.distance(pos[0], pos[1], x, y); + if (pd > 0) { + float scaleFactor = d / pd; + scale(scaleFactor); } - int[] pos = delegate.getCenterLocation(EntityView.this); float angle = 0; if (currentHandle == SELECTION_LEFT_HANDLE) { - angle = (float) Math.atan2(pos[1] - rawY, pos[0] - rawX); + angle = (float) Math.atan2(pos[1] - y, pos[0] - x); } else if (currentHandle == SELECTION_RIGHT_HANDLE) { - angle = (float) Math.atan2(rawY - pos[1], rawX - pos[0]); + angle = (float) Math.atan2(y - pos[1], x - pos[0]); } rotate((float) Math.toDegrees(angle) - delegate.getCropRotation()); @@ -658,7 +732,50 @@ public boolean onTouchEvent(MotionEvent event) { gestureDetector.onTouchEvent(event); } - return handled; + return super.onTouchEvent(event) || handled; + } + } + + private float trashScale = 1f; + private ValueAnimator trashAnimator; + private void updateTrash(boolean enter) { + if (announcedTrash != enter) { + if (trashAnimator != null) { + trashAnimator.cancel(); + trashAnimator = null; + } + trashAnimator = ValueAnimator.ofFloat(trashScale, enter ? .5f : 1f); + trashAnimator.addUpdateListener(anm -> { + trashScale = (float) anm.getAnimatedValue(); + setAlpha(trashScale); + if (selectionView != null) { + selectionView.setScaleX(AndroidUtilities.lerp(0.9f, 1f, selectT) * Utilities.clamp(trashScale * 1.25f, 1, 0)); + selectionView.setScaleY(AndroidUtilities.lerp(0.9f, 1f, selectT) * Utilities.clamp(trashScale * 1.25f, 1, 0)); + selectionView.setAlpha(selectT * Math.max(0, trashScale - .8f) * 5); + } + invalidate(); + }); + trashAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + trashAnimator.setDuration(280); + trashAnimator.start(); + + announcedTrash = enter; + if (delegate != null) { + delegate.onEntityDragTrash(enter); + } + } + } + + @Override + protected void dispatchDraw(Canvas canvas) { + final float scale = bounce.getScale(.05f); + canvas.save(); + canvas.scale(scale, scale, getWidth() / 2f, getHeight() / 2f); + if (getParent() instanceof View) { + View p = (View) getParent(); + canvas.scale(trashScale, trashScale, p.getWidth() / 2f - getX(), p.getHeight() - AndroidUtilities.dp(76) - getY()); } + super.dispatchDraw(canvas); + canvas.restore(); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/LPhotoPaintView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/LPhotoPaintView.java index d63c96f04b..c1d40e61ba 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/LPhotoPaintView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/LPhotoPaintView.java @@ -52,6 +52,7 @@ import org.telegram.messenger.MediaController; import org.telegram.messenger.MessageObject; import org.telegram.messenger.MessagesController; +import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; import org.telegram.messenger.SharedConfig; import org.telegram.messenger.UserConfig; @@ -91,7 +92,7 @@ import java.util.Arrays; import java.util.List; -public class LPhotoPaintView extends SizeNotifierFrameLayoutPhoto implements IPhotoPaintView, PaintToolsView.Delegate, EntityView.EntityViewDelegate, PaintTextOptionsView.Delegate, SizeNotifierFrameLayoutPhoto.SizeNotifierFrameLayoutPhotoDelegate { +public class LPhotoPaintView extends SizeNotifierFrameLayoutPhoto implements IPhotoPaintView, PaintToolsView.Delegate, EntityView.EntityViewDelegate, PaintTextOptionsView.Delegate, SizeNotifierFrameLayoutPhoto.SizeNotifierFrameLayoutPhotoDelegate, NotificationCenter.NotificationCenterDelegate { private PaintCancelView cancelButton; private PaintDoneView doneButton; private float offsetTranslationY; @@ -198,42 +199,55 @@ public void set(float val) { private Runnable onDoneButtonClickedListener; @SuppressLint("NotifyDataSetChanged") - public LPhotoPaintView(Context context, int currentAccount, Bitmap bitmap, Bitmap originalBitmap, int originalRotation, ArrayList entities, MediaController.CropState cropState, Runnable onInit, Theme.ResourcesProvider resourcesProvider) { - super(context, true); + public LPhotoPaintView(Context context, Activity activity, int currentAccount, Bitmap bitmap, Bitmap originalBitmap, int originalRotation, ArrayList entities, MediaController.CropState cropState, Runnable onInit, Theme.ResourcesProvider resourcesProvider) { + super(context, activity, true); setDelegate(this); this.currentAccount = currentAccount; this.resourcesProvider = key -> { - switch (key) { - case Theme.key_actionBarDefaultSubmenuBackground: return 0xFF282829; - case Theme.key_actionBarDefaultSubmenuItem: return 0xFFFFFFFF; - - case Theme.key_dialogBackground: return -14803426; - case Theme.key_dialogTextBlack: return -592138; - case Theme.key_dialogTextGray3: return -8553091; - - case Theme.key_chat_emojiPanelBackground: return 0xFF000000; - case Theme.key_chat_emojiPanelShadowLine: return -1610612736; - case Theme.key_chat_emojiBottomPanelIcon: return -9539985; - case Theme.key_chat_emojiPanelBackspace: return -9539985; - case Theme.key_chat_emojiPanelIcon: return -9539985; - case Theme.key_chat_emojiPanelIconSelected: return -10177041; - case Theme.key_windowBackgroundWhiteBlackText: return -1; - case Theme.key_featuredStickers_addedIcon: return -11754001; - case Theme.key_listSelector: return 0x1FFFFFFF; - - case Theme.key_profile_tabSelectedText: return 0xFFFFFFFF; - case Theme.key_profile_tabText: return 0xFFFFFFFF; - case Theme.key_profile_tabSelectedLine: return 0xFFFFFFFF; - case Theme.key_profile_tabSelector: return 0x14FFFFFF; - - default: { - if (resourcesProvider != null) { - return resourcesProvider.getColor(key); - } else { - return Theme.getColor(key); - } - } + if (key == Theme.key_actionBarDefaultSubmenuBackground) { + return 0xFF282829; + } else if (key == Theme.key_actionBarDefaultSubmenuItem) { + return 0xFFFFFFFF; + } else if (key == Theme.key_dialogBackground) { + return -14803426; + } else if (key == Theme.key_dialogTextBlack) { + return -592138; + } else if (key == Theme.key_dialogTextGray3) { + return -8553091; + } else if (key == Theme.key_chat_emojiPanelBackground) { + return 0xFF000000; + } else if (key == Theme.key_chat_emojiPanelShadowLine) { + return -1610612736; + } else if (key == Theme.key_chat_emojiBottomPanelIcon) { + return -9539985; + } else if (key == Theme.key_chat_emojiPanelBackspace) { + return -9539985; + } else if (key == Theme.key_chat_emojiPanelIcon) { + return -9539985; + } else if (key == Theme.key_chat_emojiPanelIconSelected) { + return -10177041; + } else if (key == Theme.key_windowBackgroundWhiteBlackText) { + return -1; + } else if (key == Theme.key_featuredStickers_addedIcon) { + return -11754001; + } else if (key == Theme.key_listSelector) { + return 0x1FFFFFFF; + } else if (key == Theme.key_profile_tabSelectedText) { + return 0xFFFFFFFF; + } else if (key == Theme.key_profile_tabText) { + return 0xFFFFFFFF; + } else if (key == Theme.key_profile_tabSelectedLine) { + return 0xFFFFFFFF; + } else if (key == Theme.key_profile_tabSelector) { + return 0x14FFFFFF; + } + + + if (resourcesProvider != null) { + return resourcesProvider.getColor(key); + } else { + return Theme.getColor(key); } }; this.currentCropState = cropState; @@ -422,15 +436,7 @@ protected void onDraw(Canvas canvas) { layoutParams.height = entity.viewHeight; } else if (entity.type == 1) { TextPaintView textPaintView = createText(false); - int type; - if ((entity.subType & 1) != 0) { - type = 0; - } else if ((entity.subType & 4) != 0) { - type = 2; - } else { - type = 1; - } - textPaintView.setType(type); + textPaintView.setType(entity.subType); textPaintView.setTypeface(entity.textTypeface); textPaintView.setBaseFontSize(entity.fontSize); SpannableString text = new SpannableString(entity.text); @@ -692,7 +698,6 @@ protected void onDraw(Canvas canvas) { textOptionsView.setPadding(AndroidUtilities.dp(16), 0, AndroidUtilities.dp(16), 0); textOptionsView.setVisibility(GONE); textOptionsView.setDelegate(this); - textOptionsView.setTypeface(PersistColorPalette.getInstance(currentAccount).getCurrentTypeface()); textOptionsView.setAlignment(PersistColorPalette.getInstance(currentAccount).getCurrentAlignment()); bottomLayout.addView(textOptionsView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48)); @@ -1306,6 +1311,7 @@ public void onDismissAnimationStart() { switchTab(wasSelectedIndex); } }; + stickerMasksAlert.setImageReceiverNumLevel(4 + 8 + 16, 4 + 8 + 16); stickerMasksAlert.setDelegate((parentObject, sticker) -> createSticker(parentObject, sticker, true)); stickerMasksAlert.setOnDismissListener(dialog -> { onOpenCloseStickersAlert(false); @@ -1457,6 +1463,7 @@ private Size getPaintingSize() { @Override public void init() { + textOptionsView.setTypeface(PersistColorPalette.getInstance(currentAccount).getCurrentTypeface()); entitiesView.setVisibility(VISIBLE); renderView.setVisibility(View.VISIBLE); renderInputView.setVisibility(View.VISIBLE); @@ -1497,16 +1504,27 @@ public void onResume() { renderView.redraw(); } + @Override + public void onAnimationStateChanged(boolean isStart) { + if (tabsSelectedIndex == 0) { + weightChooserView.setLayerType(isStart ? LAYER_TYPE_HARDWARE : LAYER_TYPE_NONE, null); + bottomLayout.setLayerType(isStart ? LAYER_TYPE_HARDWARE : LAYER_TYPE_NONE, null); + topLayout.setLayerType(isStart ? LAYER_TYPE_HARDWARE : LAYER_TYPE_NONE, null); + } + } + + @Override + public void setOffsetTranslationX(float x) { + if (tabsSelectedIndex == 0) { + weightChooserView.setTranslationX(x); + } + } + @Override public void setOffsetTranslationY(float y, float progress, int keyboardHeight, boolean isPan) { offsetTranslationY = y; if (!isPan) { topLayout.setTranslationY(-y); - - if (tabsSelectedIndex == 0) { - weightChooserView.setTranslationX(-y); - } - bottomLayout.setTranslationY(y); } else { setTranslationY(0); @@ -1569,8 +1587,10 @@ public Bitmap getBitmap(ArrayList entities, Bitmap[ tlentity.document = document; tlentity.offset = spanned.getSpanStart(span); tlentity.length = spanned.getSpanEnd(span) - tlentity.offset; - if (MessageObject.isVideoSticker(document)) { - tlentity.documentAbsolutePath = FileLoader.getInstance(currentAccount).getPathToAttach(document, true).getAbsolutePath(); + tlentity.documentAbsolutePath = FileLoader.getInstance(currentAccount).getPathToAttach(document, true).getAbsolutePath(); + boolean isAnimatedSticker = MessageObject.isAnimatedStickerDocument(tlentity.document, true); + if (isAnimatedSticker || MessageObject.isVideoStickerDocument(tlentity.document)) { + tlentity.subType |= isAnimatedSticker ? 1 : 4; } mediaEntity.entities.add(tlentity); @@ -1588,12 +1608,7 @@ public Bitmap getBitmap(ArrayList entities, Bitmap[ } } mediaEntity.text = text.toString(); - int type = textPaintView.getType(); - if (type == 0) { - mediaEntity.subType |= 1; - } else if (type == 2) { - mediaEntity.subType |= 4; - } + mediaEntity.subType = (byte) textPaintView.getType(); mediaEntity.color = textPaintView.getSwatch().color; mediaEntity.fontSize = textPaintView.getTextSize(); mediaEntity.textTypeface = textPaintView.getTypeface(); @@ -1666,7 +1681,7 @@ public Bitmap getBitmap(ArrayList entities, Bitmap[ currentCanvas.scale(v.getScaleX(), v.getScaleY()); currentCanvas.rotate(v.getRotation()); currentCanvas.translate(-entity.getWidth() / 2f, -entity.getHeight() / 2f); - if (v instanceof TextPaintView) { + if (v instanceof TextPaintView && v.getHeight() > 0 && v.getWidth() > 0) { Bitmap b = Bitmaps.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888); Canvas c = new Canvas(b); v.draw(c); @@ -2056,7 +2071,23 @@ private void setCurrentSwatch(Swatch swatch, boolean updateInterface) { } @Override - public void onBackPressed() { + public boolean onBackPressed() { + if (isColorListShown) { + showColorList(false); + return true; + } + + if (emojiViewVisible) { + hideEmojiPopup(true); + return true; + } + + if (editingText) { + selectEntity(null); + return true; + } + + return false; } @Override @@ -2066,27 +2097,7 @@ public void onColorPickerSelected() { @Override public void onTextOutlineSelected(View v) { - int[] loc = new int[2]; - v.getLocationInWindow(loc); - - showPopup(() -> { - for (int a = 0; a < 3; a++) { - String text; - int icon; - if (a == 0) { - text = LocaleController.getString("PaintOutlined", R.string.PaintOutlined); - icon = R.drawable.msg_text_outlined; - } else if (a == 1) { - text = LocaleController.getString("PaintRegular", R.string.PaintRegular); - icon = R.drawable.msg_text_regular; - } else { - text = LocaleController.getString("PaintFramed", R.string.PaintFramed); - icon = R.drawable.msg_text_framed; - } - int finalA = a; - popupLayout.addView(buttonForPopup(text, icon, selectedTextType == a, ()->setTextType(finalA)), LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48)); - } - }, this, Gravity.LEFT | Gravity.TOP, 0, getHeight() - emojiPadding); + setTextType((selectedTextType + 1) % 4); } private PopupButton buttonForPopup(String text, int icon, boolean selected, Runnable onClick) { @@ -2352,9 +2363,10 @@ private void showMenuForEntity(final EntityView entityView) { deleteView.setBackground(Theme.getSelectorDrawable(false)); deleteView.setGravity(Gravity.CENTER_VERTICAL); deleteView.setPadding(AndroidUtilities.dp(16), 0, AndroidUtilities.dp(14), 0); - deleteView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + deleteView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); deleteView.setTag(0); deleteView.setText(LocaleController.getString("PaintDelete", R.string.PaintDelete)); + deleteView.setEllipsize(TextUtils.TruncateAt.END); deleteView.setOnClickListener(v -> { removeEntity(entityView); @@ -2370,7 +2382,8 @@ private void showMenuForEntity(final EntityView entityView) { editView.setBackground(Theme.getSelectorDrawable(false)); editView.setGravity(Gravity.CENTER_VERTICAL); editView.setPadding(AndroidUtilities.dp(16), 0, AndroidUtilities.dp(16), 0); - editView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + editView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + editView.setEllipsize(TextUtils.TruncateAt.END); editView.setTag(1); editView.setText(LocaleController.getString("PaintEdit", R.string.PaintEdit)); editView.setOnClickListener(v -> { @@ -2387,8 +2400,9 @@ private void showMenuForEntity(final EntityView entityView) { duplicateView.setTextColor(getThemedColor(Theme.key_actionBarDefaultSubmenuItem)); duplicateView.setBackgroundDrawable(Theme.getSelectorDrawable(false)); duplicateView.setGravity(Gravity.CENTER_VERTICAL); + duplicateView.setEllipsize(TextUtils.TruncateAt.END); duplicateView.setPadding(AndroidUtilities.dp(14), 0, AndroidUtilities.dp(16), 0); - duplicateView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + duplicateView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); duplicateView.setTag(2); duplicateView.setText(LocaleController.getString("PaintDuplicate", R.string.PaintDuplicate)); duplicateView.setOnClickListener(v -> { @@ -2530,9 +2544,8 @@ private void showPopup(Runnable setupRunnable, View parent, int gravity, int x, popupWindow.startAnimation(popupLayout); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } @Override @@ -2641,6 +2654,7 @@ protected void didSetAnimatedSticker(RLottieDrawable drawable) { LPhotoPaintView.this.didSetAnimatedSticker(drawable); } }; + view.centerImage.setLayerNum(4 + 8); if (position.position.x == entitiesView.getMeasuredWidth() / 2f) { view.setHasStickyX(true); } @@ -2698,7 +2712,7 @@ public boolean onEntityLongClicked(EntityView entityView) { private float[] temp = new float[2]; @Override - public float[] getTransformedTouch(float x, float y) { + public float[] getTransformedTouch(MotionEvent e, float x, float y) { float x2 = (x - AndroidUtilities.displaySize.x / 2f); float y2 = (y - AndroidUtilities.displaySize.y / 2f); float rotation = (float) Math.toRadians(-entitiesView.getRotation()); @@ -3058,7 +3072,7 @@ protected void createEmojiView() { if (emojiView != null) { return; } - emojiView = new EmojiView(null, true, false, false, getContext(), false, null, null, resourcesProvider); + emojiView = new EmojiView(null, true, false, false, getContext(), false, null, null, true, resourcesProvider); emojiView.allowEmojisForNonPremium(true); emojiView.setVisibility(GONE); if (AndroidUtilities.isTablet()) { @@ -3138,6 +3152,14 @@ public void onEmojiSelected(String symbol) { try { innerTextChange = 2; CharSequence localCharSequence = Emoji.replaceEmoji(symbol, textPaintView.getFontMetricsInt(), (int) (textPaintView.getFontSize() * .8f), false); + if (localCharSequence instanceof Spanned) { + Emoji.EmojiSpan[] spans = ((Spanned) localCharSequence).getSpans(0, localCharSequence.length(), Emoji.EmojiSpan.class); + if (spans != null) { + for (int a = 0; a < spans.length; ++a) { + spans[a].scale = .85f; + } + } + } editText.setText(editText.getText().insert(i, localCharSequence)); int j = i + localCharSequence.length(); editText.setSelection(j, j); @@ -3240,6 +3262,7 @@ protected void onAttachedToWindow() { super.onAttachedToWindow(); adjustPanLayoutHelper.setResizableView(this); adjustPanLayoutHelper.onAttach(); + NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.customTypefacesLoaded); } @Override @@ -3247,5 +3270,21 @@ protected void onDetachedFromWindow() { destroyed = true; super.onDetachedFromWindow(); adjustPanLayoutHelper.onDetach(); + NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.customTypefacesLoaded); + } + + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.customTypefacesLoaded) { + if (entitiesView == null) { + return; + } + for (int i = 0; i < entitiesView.getChildCount(); ++i) { + View child = entitiesView.getChildAt(i); + if (child instanceof TextPaintView) { + ((TextPaintView) child).updateTypeface(); + } + } + } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/PaintTextOptionsView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/PaintTextOptionsView.java index 20eff62bc7..d96fce7087 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/PaintTextOptionsView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/PaintTextOptionsView.java @@ -7,9 +7,11 @@ import android.graphics.PorterDuffColorFilter; import android.graphics.RectF; import android.graphics.drawable.Drawable; +import android.text.TextUtils; import android.util.TypedValue; import android.view.Gravity; import android.view.View; +import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.Space; @@ -19,6 +21,7 @@ import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.LocaleController; +import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Components.ChatActivityEnterViewAnimatedIconView; @@ -30,7 +33,7 @@ import java.util.Arrays; import java.util.List; -public class PaintTextOptionsView extends LinearLayout { +public class PaintTextOptionsView extends FrameLayout implements NotificationCenter.NotificationCenterDelegate { public final static int ALIGN_LEFT = 0, ALIGN_CENTER = 1, ALIGN_RIGHT = 2; @@ -60,13 +63,11 @@ public class PaintTextOptionsView extends LinearLayout { public PaintTextOptionsView(Context context) { super(context); - setOrientation(LinearLayout.HORIZONTAL); - setGravity(Gravity.CENTER_VERTICAL); setWillNotDraw(false); colorClickableView = new View(context); colorClickableView.setOnClickListener(v -> delegate.onColorPickerSelected()); - addView(colorClickableView, LayoutHelper.createLinear(24, LayoutHelper.MATCH_PARENT, Gravity.TOP, 0, 0, 16, 0)); + addView(colorClickableView, LayoutHelper.createFrame(24, 24, Gravity.TOP, 0, 0, 16, 0)); alignView = new RLottieImageView(context); alignView.setAnimation(R.raw.photo_text_allign, 24, 24); @@ -77,13 +78,13 @@ public PaintTextOptionsView(Context context) { alignView.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)); alignView.setOnClickListener(v -> setAlignment((currentAlign + 1) % 3, true)); alignView.setPadding(AndroidUtilities.dp(2), AndroidUtilities.dp(2), AndroidUtilities.dp(2), AndroidUtilities.dp(2)); - addView(alignView, LayoutHelper.createLinear(28, 28, Gravity.CENTER_VERTICAL, 0, 0, 16, 0)); + addView(alignView, LayoutHelper.createFrame(28, 28, Gravity.CENTER_VERTICAL, 0, 0, 16, 0)); outlineView = new ImageView(context); outlineView.setImageResource(R.drawable.msg_text_outlined); outlineView.setPadding(AndroidUtilities.dp(1), AndroidUtilities.dp(1), AndroidUtilities.dp(1), AndroidUtilities.dp(1)); outlineView.setOnClickListener(v -> delegate.onTextOutlineSelected(v)); - addView(outlineView, LayoutHelper.createLinear(28, 28, Gravity.CENTER_VERTICAL, 0, 0, 16, 0)); + addView(outlineView, LayoutHelper.createFrame(28, 28, Gravity.CENTER_VERTICAL, 0, 0, 16, 0)); plusView = new ImageView(context); plusView.setImageResource(R.drawable.msg_add); @@ -91,14 +92,58 @@ public PaintTextOptionsView(Context context) { plusView.setBackground(Theme.createSelectorDrawable(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR)); plusView.setOnClickListener(v -> delegate.onNewTextSelected()); plusView.setPadding(AndroidUtilities.dp(2), AndroidUtilities.dp(2), AndroidUtilities.dp(2), AndroidUtilities.dp(2)); - addView(plusView, LayoutHelper.createLinear(28, 28, Gravity.CENTER_VERTICAL, 0, 0, 16, 0)); + addView(plusView, LayoutHelper.createFrame(28, 28, Gravity.CENTER_VERTICAL, 0, 0, 16, 0)); - addView(new Space(context), LayoutHelper.createLinear(0, 0, 1f)); +// addView(new Space(context), LayoutHelper.createLinear(0, 0, 1f)); typefaceCell = new TypefaceCell(context); typefaceCell.setCurrent(true); typefaceCell.setOnClickListener(v -> delegate.onTypefaceButtonClicked()); - addView(typefaceCell, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT)); + addView(typefaceCell, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, 0f, Gravity.RIGHT | Gravity.CENTER_VERTICAL)); + } + + private int x; + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + x = getPaddingLeft(); + layoutChild(colorClickableView); + layoutChild(alignView); + layoutChild(outlineView); + layoutChild(plusView); + typefaceCell.layout(getMeasuredWidth() - getPaddingRight() - typefaceCell.getMeasuredWidth(), (getMeasuredHeight() - typefaceCell.getMeasuredHeight()) / 2, getMeasuredWidth() - getPaddingRight(), (getMeasuredHeight() + typefaceCell.getMeasuredHeight()) / 2); + } + + private void layoutChild(View child) { + if (child.getVisibility() != View.GONE) { + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) child.getLayoutParams(); + x += lp.leftMargin; + child.layout(x, (getMeasuredHeight() - lp.height) / 2, x + lp.width, (getMeasuredHeight() + lp.height) / 2); + x += lp.width + lp.rightMargin; + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int w = MeasureSpec.getSize(widthMeasureSpec), h = MeasureSpec.getSize(heightMeasureSpec); + int cw = w - getPaddingLeft() - getPaddingRight(); + for (int i = 0; i < getChildCount(); ++i) { + View child = getChildAt(i); + if (child == typefaceCell) { + typefaceCell.measure( + MeasureSpec.makeMeasureSpec(cw, MeasureSpec.AT_MOST), + MeasureSpec.makeMeasureSpec(h, MeasureSpec.AT_MOST) + ); + } else { + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) child.getLayoutParams(); + child.measure( + MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY) + ); + cw -= child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin; + } + } + setMeasuredDimension(w, h); } public TypefaceCell getTypefaceCell() { @@ -117,11 +162,14 @@ public void getTypefaceCellBounds(RectF out) { out.set(typefaceCell.getLeft() + AndroidUtilities.dp(8), typefaceCell.getTop(), typefaceCell.getRight() + AndroidUtilities.dp(8), typefaceCell.getBottom()); } + private int plusIcon; public void animatePlusToIcon(int icon) { if (icon == 0) { icon = R.drawable.msg_add; } - AndroidUtilities.updateImageViewImageAnimated(plusView, icon); + if (plusIcon != icon) { + AndroidUtilities.updateImageViewImageAnimated(plusView, plusIcon = icon); + } } public ChatActivityEnterViewAnimatedIconView getEmojiButton() { @@ -142,13 +190,16 @@ public void setOutlineType(int type, boolean animate) { switch (type) { default: case 0: - res = R.drawable.msg_text_outlined; + res = R.drawable.msg_photo_text_framed; break; case 1: - res = R.drawable.msg_text_regular; + res = R.drawable.msg_photo_text_framed2; break; case 2: - res = R.drawable.msg_text_framed; + res = R.drawable.msg_photo_text_framed3; + break; + case 3: + res = R.drawable.msg_photo_text_regular; break; } if (animate) { @@ -158,13 +209,12 @@ public void setOutlineType(int type, boolean animate) { } } + private String lastTypefaceKey; public void setTypeface(String key) { + lastTypefaceKey = key; if (typefaceCell == null) { return; } - if (!PaintTypeface.fetched(() -> setTypeface(key))) { - return; - } for (PaintTypeface typeface : PaintTypeface.get()) { if (typeface.getKey().equals(key)) { typefaceCell.bind(typeface); @@ -230,18 +280,23 @@ public TypefaceCell(Context context) { setTextColor(Color.WHITE); setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); setCurrent(false); + setEllipsize(TextUtils.TruncateAt.END); + setSingleLine(); } @Override protected void onDraw(Canvas canvas) { + canvas.save(); + canvas.translate(0, AndroidUtilities.dp(-1)); super.onDraw(canvas); + canvas.restore(); if (isCurrent) { int y = (getHeight() - AndroidUtilities.dp(16)) / 2; if (LocaleController.isRTL) { - expandDrawable.setBounds(AndroidUtilities.dp(12), y, AndroidUtilities.dp(16 + 12), y + AndroidUtilities.dp(16)); + expandDrawable.setBounds(AndroidUtilities.dp(7), y, AndroidUtilities.dp(16 + 7), y + AndroidUtilities.dp(16)); } else { - expandDrawable.setBounds(getWidth() - AndroidUtilities.dp(16 + 12), y, getWidth() - AndroidUtilities.dp(12), y + AndroidUtilities.dp(16)); + expandDrawable.setBounds(getWidth() - AndroidUtilities.dp(16 + 7), y, getWidth() - AndroidUtilities.dp(7), y + AndroidUtilities.dp(16)); } expandDrawable.draw(canvas); } @@ -250,7 +305,7 @@ protected void onDraw(Canvas canvas) { public void setCurrent(boolean current) { isCurrent = current; if (isCurrent) { - setPadding(AndroidUtilities.dp(LocaleController.isRTL ? 38 : 14), AndroidUtilities.dp(6), AndroidUtilities.dp(LocaleController.isRTL ? 14 : 38), AndroidUtilities.dp(6)); + setPadding(AndroidUtilities.dp(LocaleController.isRTL ? 27 : 12), AndroidUtilities.dp(6), AndroidUtilities.dp(LocaleController.isRTL ? 12 : 27), AndroidUtilities.dp(6)); setBackground(Theme.AdaptiveRipple.rect(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR, AndroidUtilities.dp(32))); } else { setPadding(AndroidUtilities.dp(24), AndroidUtilities.dp(14), AndroidUtilities.dp(24), AndroidUtilities.dp(14)); @@ -258,7 +313,7 @@ public void setCurrent(boolean current) { } if (isCurrent && expandDrawable == null) { expandDrawable = ContextCompat.getDrawable(getContext(), R.drawable.photo_expand); - expandDrawable.setColorFilter(new PorterDuffColorFilter(0x99FFFFFF, PorterDuff.Mode.SRC_IN)); + expandDrawable.setColorFilter(new PorterDuffColorFilter(0xFFFFFFFF, PorterDuff.Mode.SRC_IN)); } invalidate(); } @@ -292,4 +347,26 @@ private AlignFramePair(int fromAlign, int toAlign, int fromFrame, int toFrame) { this.toFrame = toFrame; } } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.customTypefacesLoaded); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.customTypefacesLoaded); + } + + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.customTypefacesLoaded) { + if (lastTypefaceKey != null) { + setTypeface(lastTypefaceKey); + lastTypefaceKey = null; + } + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/PaintTypefaceListView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/PaintTypefaceListView.java index 786317e94e..6c4cd8d9b5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/PaintTypefaceListView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/PaintTypefaceListView.java @@ -12,10 +12,11 @@ import androidx.recyclerview.widget.RecyclerView; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.NotificationCenter; import org.telegram.ui.Components.Paint.PaintTypeface; import org.telegram.ui.Components.RecyclerListView; -public class PaintTypefaceListView extends RecyclerListView { +public class PaintTypefaceListView extends RecyclerListView implements NotificationCenter.NotificationCenterDelegate { private Path mask = new Path(); private Consumer maskProvider; @@ -52,6 +53,31 @@ public int getItemCount() { }); setPadding(0, AndroidUtilities.dp(8), 0, AndroidUtilities.dp(8)); + setClipToPadding(false); + } + + @Override + public Integer getSelectorColor(int position) { + return 0x10ffffff; + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.customTypefacesLoaded); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.customTypefacesLoaded); + } + + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.customTypefacesLoaded) { + getAdapter().notifyDataSetChanged(); + } } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/PaintWeightChooserView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/PaintWeightChooserView.java index 6509df55c8..1292af8ebb 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/PaintWeightChooserView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/PaintWeightChooserView.java @@ -1,5 +1,9 @@ package org.telegram.ui.Components.Paint.Views; +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.AndroidUtilities.lerp; +import static org.telegram.messenger.AndroidUtilities.rectTmp; + import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Canvas; @@ -15,6 +19,7 @@ import androidx.core.view.GestureDetectorCompat; import org.telegram.messenger.AndroidUtilities; +import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Components.AnimatedFloat; import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.Paint.RenderView; @@ -97,9 +102,9 @@ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float d } }); colorPaint.setColor(Color.WHITE); - colorPaint.setShadowLayer(AndroidUtilities.dp(4), 0, AndroidUtilities.dp(2), 0x50000000); + colorPaint.setShadowLayer(dp(4), 0, dp(2), 0x50000000); backgroundPaint.setColor(0x40ffffff); - backgroundPaint.setShadowLayer(AndroidUtilities.dp(3), 0, AndroidUtilities.dp(1), 0x26000000); + backgroundPaint.setShadowLayer(dp(3), 0, dp(1), 0x26000000); } public void setShowPreview(boolean showPreview) { @@ -136,9 +141,9 @@ public void updatePanTransition(float y, float progress) { setTranslationY(y); - int contentHeight = AndroidUtilities.lerp(fromContentHeight, newContentHeight, panProgress); + int contentHeight = lerp(fromContentHeight, newContentHeight, panProgress); int height = (int) (contentHeight * 0.3f); - touchRect.set(0, (contentHeight - height) / 2f, AndroidUtilities.dp(32), (contentHeight + height) / 2f); + touchRect.set(0, (contentHeight - height) / 2f, dp(32), (contentHeight + height) / 2f); invalidate(); } @@ -184,7 +189,7 @@ protected void onSizeChanged(int w, int h, int oldw, int oldh) { if (!isPanTransitionInProgress) { int height = (int) (getHeight() * 0.3f); - touchRect.set(0, (getHeight() - height) / 2f, AndroidUtilities.dp(32), (getHeight() + height) / 2f); + touchRect.set(0, (getHeight() - height) / 2f, dp(32), (getHeight() + height) / 2f); } } @@ -219,43 +224,43 @@ protected void onDraw(Canvas canvas) { } float height = touchRect.height(); - int bigRadius = AndroidUtilities.dp(16); - int smallRadius = AndroidUtilities.dp(3); - int smallLine = AndroidUtilities.dp(3); + int bigRadius = dp(16); + int smallRadius = dp(3); + int smallLine = dp(3); path.rewind(); path.moveTo(0, 0); - AndroidUtilities.rectTmp.set(AndroidUtilities.lerp(-smallLine, -bigRadius, showProgress), 0, AndroidUtilities.lerp(smallLine, bigRadius, showProgress), bigRadius * 2); - path.arcTo(AndroidUtilities.rectTmp, -90, 90); - path.lineTo(AndroidUtilities.lerp(smallLine, smallRadius, showProgress), height); - AndroidUtilities.rectTmp.set(AndroidUtilities.lerp(-smallLine, -smallRadius, showProgress), height - smallRadius * 2, AndroidUtilities.lerp(smallLine, smallRadius, showProgress), height); - path.arcTo(AndroidUtilities.rectTmp, 0, 180); + rectTmp.set(lerp(-smallLine, -bigRadius, showProgress), 0, lerp(smallLine, bigRadius, showProgress), lerp(smallLine, bigRadius, showProgress) * 2); + path.arcTo(rectTmp, -90, 90); + path.lineTo(lerp(smallLine, smallRadius, showProgress), height); + rectTmp.set(lerp(-smallLine, -smallRadius, showProgress), height - smallRadius * 2, lerp(smallLine, smallRadius, showProgress), height); + path.arcTo(rectTmp, 0, 180); - path.lineTo(AndroidUtilities.lerp(-smallLine, -bigRadius, showProgress), bigRadius); - AndroidUtilities.rectTmp.set(AndroidUtilities.lerp(-smallLine, -bigRadius, showProgress), 0, AndroidUtilities.lerp(smallLine, bigRadius, showProgress), bigRadius * 2); - path.arcTo(AndroidUtilities.rectTmp, -180, 90); + path.lineTo(lerp(-smallLine, -bigRadius, showProgress), bigRadius); + rectTmp.set(lerp(-smallLine, -bigRadius, showProgress), 0, lerp(smallLine, bigRadius, showProgress), bigRadius * 2); + path.arcTo(rectTmp, -180, 90); path.close(); if (hideProgress != 0f) { - AndroidUtilities.rectTmp.set(0, 0, getWidth(), getHeight()); - canvas.saveLayerAlpha(AndroidUtilities.rectTmp, (int) (0xFF * (1f - hideProgress)), Canvas.ALL_SAVE_FLAG); + rectTmp.set(0, 0, getWidth(), getHeight()); + canvas.saveLayerAlpha(rectTmp, (int) (0xFF * (1f - hideProgress)), Canvas.ALL_SAVE_FLAG); } canvas.save(); - canvas.translate(AndroidUtilities.dp(32) * CubicBezierInterpolator.DEFAULT.getInterpolation(showProgress), touchRect.top); + canvas.translate(dp(32) * CubicBezierInterpolator.DEFAULT.getInterpolation(showProgress), touchRect.top); canvas.drawPath(path, backgroundPaint); canvas.restore(); drawCircleWithShadow( canvas, - AndroidUtilities.dp(32) * CubicBezierInterpolator.DEFAULT.getInterpolation(showProgress), + dp(32) * CubicBezierInterpolator.DEFAULT.getInterpolation(showProgress), MathUtils.clamp( touchRect.top + touchRect.height() * (1f - (weight - min) / (max - min)), touchRect.top + bigRadius, touchRect.bottom - Math.min(smallRadius * 1.5f, bigRadius) ), - AndroidUtilities.lerp(AndroidUtilities.dp(12), AndroidUtilities.lerp(Math.min(smallRadius * 1.5f, bigRadius), bigRadius, (weight - min) / (max - min)), showProgress), + lerp(dp(12), lerp(Math.min(smallRadius * 1.5f, bigRadius), bigRadius, (weight - min) / (max - min)), showProgress), false ); @@ -271,7 +276,7 @@ protected void onDraw(Canvas canvas) { private void drawCircleWithShadow(Canvas canvas, float cx, float cy, float rad, boolean useAlpha) { if (useAlpha) { - AndroidUtilities.rectTmp.set(cx - rad - AndroidUtilities.dp(6), cy - rad - AndroidUtilities.dp(6), cx + rad + AndroidUtilities.dp(6), cy + rad + AndroidUtilities.dp(6)); + AndroidUtilities.rectTmp.set(cx - rad - dp(6), cy - rad - dp(6), cx + rad + dp(6), cy + rad + dp(6)); canvas.saveLayerAlpha(AndroidUtilities.rectTmp, (int) (0xFF * showProgress), Canvas.ALL_SAVE_FLAG); } canvas.drawCircle(cx, cy, rad, colorPaint); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/PhotoView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/PhotoView.java new file mode 100644 index 0000000000..255cee5abf --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/PhotoView.java @@ -0,0 +1,255 @@ +package org.telegram.ui.Components.Paint.Views; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.RectF; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.FileLoader; +import org.telegram.messenger.ImageLocation; +import org.telegram.messenger.ImageReceiver; +import org.telegram.messenger.MediaController; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.Components.AnimatedFileDrawable; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.Point; +import org.telegram.ui.Components.RLottieDrawable; +import org.telegram.ui.Components.Rect; +import org.telegram.ui.Components.Size; + +public class PhotoView extends EntityView { + + private class FrameLayoutDrawer extends FrameLayout { + public FrameLayoutDrawer(Context context) { + super(context); + setWillNotDraw(false); + } + + @Override + protected void onDraw(Canvas canvas) { + PhotoView.this.stickerDraw(canvas); + } + } + + private String path; + private int anchor = -1; + private boolean mirrored = false; + private final AnimatedFloat mirrorT; + private Size baseSize; + + private FrameLayoutDrawer containerView; + public final ImageReceiver centerImage = new ImageReceiver(); + + public PhotoView(Context context, Point position, float angle, float scale, Size baseSize, String path, int orientation, int invert) { + super(context, position); + setRotation(angle); + setScale(scale); + + this.path = path; + this.baseSize = baseSize; + + containerView = new FrameLayoutDrawer(context); + addView(containerView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + + mirrorT = new AnimatedFloat(containerView, 0, 500, CubicBezierInterpolator.EASE_OUT_QUINT); + + centerImage.setAspectFit(true); + centerImage.setInvalidateAll(true); + centerImage.setParentView(containerView); + centerImage.setRoundRadius(AndroidUtilities.dp(12)); + centerImage.setOrientation(orientation, invert, true); + final int side = Math.round(Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * .8f / AndroidUtilities.density); + centerImage.setImage(ImageLocation.getForPath(path), side + "_" + side, null, null, null, 1); + updatePosition(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + centerImage.onDetachedFromWindow(); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + centerImage.onAttachedToWindow(); + } + + public int getAnchor() { + return anchor; + } + + public void mirror() { + mirror(false); + } + + public void mirror(boolean animated) { + mirrored = !mirrored; + if (!animated) { + mirrorT.set(mirrored, true); + } + containerView.invalidate(); + } + + public boolean isMirrored() { + return mirrored; + } + + protected void updatePosition() { + float halfWidth = baseSize.width / 2.0f; + float halfHeight = baseSize.height / 2.0f; + setX(getPositionX() - halfWidth); + setY(getPositionY() - halfHeight); + updateSelectionView(); + } + + protected void stickerDraw(Canvas canvas) { + if (containerView == null) { + return; + } + + canvas.save(); + float mirrorT = this.mirrorT.set(mirrored); + canvas.scale(1 - mirrorT * 2, 1f, baseSize.width / 2f, 0); + canvas.skew(0, 4 * mirrorT * (1f - mirrorT) * .25f); + centerImage.setImageCoords(0, 0, (int) baseSize.width, (int) baseSize.height); + centerImage.draw(canvas); + canvas.restore(); + } + + public long getDuration() { + RLottieDrawable rLottieDrawable = centerImage.getLottieAnimation(); + if (rLottieDrawable != null) { + return rLottieDrawable.getDuration(); + } + AnimatedFileDrawable animatedFileDrawable = centerImage.getAnimation(); + if (animatedFileDrawable != null) { + return animatedFileDrawable.getDurationMs(); + } + return 0; + + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(MeasureSpec.makeMeasureSpec((int) baseSize.width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec((int) baseSize.height, MeasureSpec.EXACTLY)); + } + + @Override + protected Rect getSelectionBounds() { + ViewGroup parentView = (ViewGroup) getParent(); + if (parentView == null) { + return new Rect(); + } + float scale = parentView.getScaleX(); + float width = getMeasuredWidth() * getScale() + AndroidUtilities.dp(64) / scale; + float height = getMeasuredHeight() * getScale() + AndroidUtilities.dp(64) / scale; + float left = (getPositionX() - width / 2.0f) * scale; + float right = left + width * scale; + return new Rect(left, (getPositionY() - height / 2.0f) * scale, right - left, height * scale); + } + + @Override + protected SelectionView createSelectionView() { + return new PhotoViewSelectionView(getContext()); + } + + public String getPath() { + return path; + } + + public Size getBaseSize() { + return baseSize; + } + + public class PhotoViewSelectionView extends SelectionView { + + private final Paint clearPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + public PhotoViewSelectionView(Context context) { + super(context); + clearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + } + + @Override + protected int pointInsideHandle(float x, float y) { + float thickness = AndroidUtilities.dp(1.0f); + float radius = AndroidUtilities.dp(19.5f); + + float inset = radius + thickness; + float width = getMeasuredWidth() - inset * 2; + float height = getMeasuredHeight() - inset * 2; + + float middle = inset + height / 2.0f; + + if (x > inset - radius && y > middle - radius && x < inset + radius && y < middle + radius) { + return SELECTION_LEFT_HANDLE; + } else if (x > inset + width - radius && y > middle - radius && x < inset + width + radius && y < middle + radius) { + return SELECTION_RIGHT_HANDLE; + } + + if (x > inset && x < width && y > inset && y < height) { + return SELECTION_WHOLE_HANDLE; + } + + return 0; + } + + private Path path = new Path(); + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + float thickness = AndroidUtilities.dp(2.0f); + float radius = AndroidUtilities.dpf2(5.66f); + + float inset = radius + thickness + AndroidUtilities.dp(15); + + float width = getMeasuredWidth() - inset * 2; + float height = getMeasuredHeight() - inset * 2; + + AndroidUtilities.rectTmp.set(inset, inset, inset + width, inset + height); + + float R = AndroidUtilities.dp(12); + float rx = Math.min(R, width / 2f), ry = Math.min(R, height / 2f); + + path.rewind(); + AndroidUtilities.rectTmp.set(inset, inset, inset + rx * 2, inset + ry * 2); + path.arcTo(AndroidUtilities.rectTmp, 180, 90); + AndroidUtilities.rectTmp.set(inset + width - rx * 2, inset, inset + width, inset + ry * 2); + path.arcTo(AndroidUtilities.rectTmp, 270, 90); + canvas.drawPath(path, paint); + + path.rewind(); + AndroidUtilities.rectTmp.set(inset, inset + height - ry * 2, inset + rx * 2, inset + height); + path.arcTo(AndroidUtilities.rectTmp, 180, -90); + AndroidUtilities.rectTmp.set(inset + width - rx * 2, inset + height - ry * 2, inset + width, inset + height); + path.arcTo(AndroidUtilities.rectTmp, 90, -90); + canvas.drawPath(path, paint); + + canvas.drawCircle(inset, inset + height / 2.0f, radius, dotStrokePaint); + canvas.drawCircle(inset, inset + height / 2.0f, radius - AndroidUtilities.dp(1) + 1, dotPaint); + + canvas.drawCircle(inset + width, inset + height / 2.0f, radius, dotStrokePaint); + canvas.drawCircle(inset + width, inset + height / 2.0f, radius - AndroidUtilities.dp(1) + 1, dotPaint); + + canvas.saveLayerAlpha(0, 0, getWidth(), getHeight(), 0xFF, Canvas.ALL_SAVE_FLAG); + + canvas.drawLine(inset, inset + ry, inset, inset + height - ry, paint); + canvas.drawLine(inset + width, inset + ry, inset + width, inset + height - ry, paint); + canvas.drawCircle(inset + width, inset + height / 2.0f, radius + AndroidUtilities.dp(1) - 1, clearPaint); + canvas.drawCircle(inset, inset + height / 2.0f, radius + AndroidUtilities.dp(1) - 1, clearPaint); + + canvas.restore(); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/StickerView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/StickerView.java index 3eb72bc30c..cfd89f716e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/StickerView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/StickerView.java @@ -12,6 +12,8 @@ import org.telegram.messenger.ImageReceiver; import org.telegram.tgnet.TLRPC; import org.telegram.ui.Components.AnimatedFileDrawable; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.Point; import org.telegram.ui.Components.RLottieDrawable; @@ -36,10 +38,11 @@ protected void onDraw(Canvas canvas) { private Object parentObject; private int anchor = -1; private boolean mirrored = false; + private final AnimatedFloat mirrorT; private Size baseSize; private FrameLayoutDrawer containerView; - private ImageReceiver centerImage = new ImageReceiver(); + public final ImageReceiver centerImage = new ImageReceiver(); public StickerView(Context context, Point position, Size baseSize, TLRPC.Document sticker, Object parentObject) { this(context, position, 0.0f, 1.0f, baseSize, sticker, parentObject); @@ -67,6 +70,8 @@ public StickerView(Context context, Point position, float angle, float scale, Si containerView = new FrameLayoutDrawer(context); addView(containerView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + mirrorT = new AnimatedFloat(containerView, 0, 500, CubicBezierInterpolator.EASE_OUT_QUINT); + centerImage.setAspectFit(true); centerImage.setInvalidateAll(true); centerImage.setParentView(containerView); @@ -108,7 +113,14 @@ public int getAnchor() { } public void mirror() { + mirror(false); + } + + public void mirror(boolean animated) { mirrored = !mirrored; + if (!animated) { + mirrorT.set(mirrored, true); + } containerView.invalidate(); } @@ -134,10 +146,9 @@ protected void stickerDraw(Canvas canvas) { } canvas.save(); - if (mirrored) { - canvas.scale(-1.0f, 1.0f); - canvas.translate(-baseSize.width, 0); - } + float mirrorT = this.mirrorT.set(mirrored); + canvas.scale(1 - mirrorT * 2, 1f, baseSize.width / 2f, 0); + canvas.skew(0, 4 * mirrorT * (1f - mirrorT) * .25f); centerImage.setImageCoords(0, 0, (int) baseSize.width, (int) baseSize.height); centerImage.draw(canvas); canvas.restore(); @@ -226,7 +237,7 @@ protected void onDraw(Canvas canvas) { super.onDraw(canvas); float thickness = AndroidUtilities.dp(1.0f); - float radius = AndroidUtilities.dp(4.5f); + float radius = AndroidUtilities.dpf2(5.66f); float inset = radius + thickness + AndroidUtilities.dp(15); float mainRadius = getMeasuredWidth() / 2 - inset; @@ -235,11 +246,11 @@ protected void onDraw(Canvas canvas) { canvas.drawArc(arcRect, 0, 180, false, paint); canvas.drawArc(arcRect, 180, 180, false, paint); - canvas.drawCircle(inset, inset + mainRadius, radius, dotPaint); canvas.drawCircle(inset, inset + mainRadius, radius, dotStrokePaint); + canvas.drawCircle(inset, inset + mainRadius, radius - AndroidUtilities.dp(1), dotPaint); - canvas.drawCircle(inset + mainRadius * 2, inset + mainRadius, radius, dotPaint); canvas.drawCircle(inset + mainRadius * 2, inset + mainRadius, radius, dotStrokePaint); + canvas.drawCircle(inset + mainRadius * 2, inset + mainRadius, radius - AndroidUtilities.dp(1), dotPaint); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/TextPaintView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/TextPaintView.java index d01c8c00ff..b708b6615c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/TextPaintView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/TextPaintView.java @@ -5,7 +5,11 @@ import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; import android.graphics.Typeface; +import android.graphics.Xfermode; +import android.graphics.text.LineBreaker; import android.os.Build; import android.text.Editable; import android.text.Layout; @@ -18,9 +22,17 @@ import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; +import androidx.core.graphics.ColorUtils; + +import com.googlecode.mp4parser.authoring.Edit; + import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.Emoji; import org.telegram.messenger.LocaleController; +import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.R; +import org.telegram.messenger.VideoEditedInfo; +import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Components.AnimatedEmojiSpan; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.Paint.PaintTypeface; @@ -44,8 +56,6 @@ public TextPaintView(Context context, Point position, int fontSize, CharSequence baseFontSize = fontSize; editText = new EditTextOutline(context) { - { animatedEmojiOffsetX = AndroidUtilities.dp(8); } - @Override public boolean dispatchTouchEvent(MotionEvent event) { if (selectionView == null || selectionView.getVisibility() != VISIBLE) { @@ -53,7 +63,20 @@ public boolean dispatchTouchEvent(MotionEvent event) { } return super.dispatchTouchEvent(event); } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + updateSelectionView(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + updateSelectionView(); + } }; + NotificationCenter.listenEmojiLoading(editText); editText.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL); editText.setBackgroundColor(Color.TRANSPARENT); editText.setPadding(AndroidUtilities.dp(7), AndroidUtilities.dp(7), AndroidUtilities.dp(7), AndroidUtilities.dp(7)); @@ -63,15 +86,23 @@ public boolean dispatchTouchEvent(MotionEvent event) { editText.setTextSize(TypedValue.COMPLEX_UNIT_PX, baseFontSize); editText.setCursorSize(AndroidUtilities.dp(baseFontSize * 0.4f)); editText.setText(text); + updateHint(); editText.setTextColor(swatch.color); editText.setTypeface(null, Typeface.BOLD); editText.setHorizontallyScrolling(false); - editText.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + editText.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI | EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING); + } else { + editText.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI); + } editText.setFocusableInTouchMode(true); - editText.setInputType(editText.getInputType() | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES); + editText.setInputType(EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES); + editText.setSingleLine(false); addView(editText, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP)); - if (Build.VERSION.SDK_INT >= 23) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + editText.setBreakStrategy(LineBreaker.BREAK_STRATEGY_SIMPLE); + } else if (Build.VERSION.SDK_INT >= 23) { editText.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE); } @@ -103,12 +134,23 @@ public void afterTextChanged(Editable s) { editText.setText(text); editText.setSelection(beforeCursorPosition); } - + + updateHint(); + editText.addTextChangedListener(this); } }); } + private void updateHint() { + if (editText.getText().length() <= 0) { + editText.setHint(LocaleController.getString(R.string.TextPlaceholder)); + editText.setHintTextColor(0x60ffffff); + } else { + editText.setHint(null); + } + } + public TextPaintView(Context context, TextPaintView textPaintView, Point position) { this(context, position, textPaintView.baseFontSize, textPaintView.getText(), textPaintView.getSwatch(), textPaintView.currentType); setRotation(textPaintView.getRotation()); @@ -164,6 +206,7 @@ public void setBaseFontSize(int baseFontSize) { Emoji.EmojiSpan[] spans = spanned.getSpans(0, spanned.length(), Emoji.EmojiSpan.class); for (int i = 0; i < spans.length; ++i) { spans[i].replaceFontMetrics(getFontMetricsInt()); + spans[i].scale = .85f; } AnimatedEmojiSpan[] spans2 = spanned.getSpans(0, spanned.length(), AnimatedEmojiSpan.class); @@ -185,16 +228,30 @@ public int getAlign() { public void setTypeface(PaintTypeface typeface) { this.typeface = typeface; - editText.setTypeface(typeface.getTypeface()); + if (typeface != null) { + editText.setTypeface(typeface.getTypeface()); + } + updateSelectionView(); } + private String lastTypefaceKey; public void setTypeface(String key) { + boolean found = false; for (PaintTypeface typeface : PaintTypeface.get()) { if (typeface.getKey().equals(key)) { setTypeface(typeface); + found = true; break; } } + lastTypefaceKey = found ? null : key; + updateSelectionView(); + } + + public void updateTypeface() { + if (lastTypefaceKey != null) { + setTypeface(lastTypefaceKey); + } } public PaintTypeface getTypeface() { @@ -215,12 +272,19 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto updatePosition(); } + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + updatePosition(); + } + public CharSequence getText() { return editText.getText(); } public void setText(CharSequence text) { editText.setText(text); + updateHint(); } public Paint.FontMetricsInt getFontMetricsInt() { @@ -273,22 +337,22 @@ public int getType() { } public void updateColor() { + editText.setShadowLayer(0, 0, 0, 0); + int textColor = swatch.color; if (currentType == 0) { - editText.setTextColor(0xffffffff); - editText.setStrokeColor(swatch.color); - editText.setFrameColor(0); - editText.setShadowLayer(0, 0, 0, 0); + editText.setFrameColor(swatch.color); + textColor = AndroidUtilities.computePerceivedBrightness(swatch.color) >= .721f ? Color.BLACK : Color.WHITE; } else if (currentType == 1) { - editText.setTextColor(swatch.color); - editText.setStrokeColor(0); - editText.setFrameColor(0); - editText.setShadowLayer(5, 0, 1, 0x66000000); + editText.setFrameColor(AndroidUtilities.computePerceivedBrightness(swatch.color) >= .25f ? 0x99000000 : 0x99ffffff); } else if (currentType == 2) { - editText.setTextColor(0xff000000); - editText.setStrokeColor(0); - editText.setFrameColor(swatch.color); - editText.setShadowLayer(0, 0, 0, 0); + editText.setFrameColor(AndroidUtilities.computePerceivedBrightness(swatch.color) >= .25f ? Color.BLACK : Color.WHITE); + } else { + editText.setFrameColor(0); } + editText.setTextColor(textColor); + editText.setCursorColor(textColor); + editText.setHandlesColor(textColor); + editText.setHighlightColor(Theme.multAlpha(textColor, .4f)); } @Override @@ -300,7 +364,9 @@ protected Rect getSelectionBounds() { float scale = parentView.getScaleX(); float width = getMeasuredWidth() * getScale() + AndroidUtilities.dp(64) / scale; float height = getMeasuredHeight() * getScale() + AndroidUtilities.dp(52) / scale; - return new Rect((getPositionX() - width / 2.0f) * scale, (getPositionY() - height / 2.0f) * scale, width * scale, height * scale); + float left = (getPositionX() - width / 2.0f) * scale; + float right = left + width * scale; + return new Rect(left, (getPositionY() - (height - editText.getExtendedPaddingTop() - AndroidUtilities.dpf2(4f)) / 2f) * scale, right - left, (height - editText.getExtendedPaddingBottom()) * scale); } protected TextViewSelectionView createSelectionView() { @@ -308,9 +374,12 @@ protected TextViewSelectionView createSelectionView() { } public class TextViewSelectionView extends SelectionView { + + private final Paint clearPaint = new Paint(Paint.ANTI_ALIAS_FLAG); public TextViewSelectionView(Context context) { super(context); + clearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); } @Override @@ -331,7 +400,7 @@ protected int pointInsideHandle(float x, float y) { } if (x > inset && x < width && y > inset && y < height) { - return SELECTION_WHOLE_HANDLE; + return 0; } return 0; @@ -344,7 +413,7 @@ protected void onDraw(Canvas canvas) { super.onDraw(canvas); float thickness = AndroidUtilities.dp(2.0f); - float radius = AndroidUtilities.dp(4.5f); + float radius = AndroidUtilities.dpf2(5.66f); float inset = radius + thickness + AndroidUtilities.dp(15); @@ -370,14 +439,20 @@ protected void onDraw(Canvas canvas) { path.arcTo(AndroidUtilities.rectTmp, 90, -90); canvas.drawPath(path, paint); - canvas.drawLine(inset, inset + ry, inset, inset + height - ry, paint); - canvas.drawLine(inset + width, inset + ry, inset + width, inset + height - ry, paint); - - canvas.drawCircle(inset, inset + height / 2.0f, radius, dotPaint); canvas.drawCircle(inset, inset + height / 2.0f, radius, dotStrokePaint); + canvas.drawCircle(inset, inset + height / 2.0f, radius - AndroidUtilities.dp(1) + 1, dotPaint); - canvas.drawCircle(inset + width, inset + height / 2.0f, radius, dotPaint); canvas.drawCircle(inset + width, inset + height / 2.0f, radius, dotStrokePaint); + canvas.drawCircle(inset + width, inset + height / 2.0f, radius - AndroidUtilities.dp(1) + 1, dotPaint); + + canvas.saveLayerAlpha(0, 0, getWidth(), getHeight(), 0xFF, Canvas.ALL_SAVE_FLAG); + + canvas.drawLine(inset, inset + ry, inset, inset + height - ry, paint); + canvas.drawLine(inset + width, inset + ry, inset + width, inset + height - ry, paint); + canvas.drawCircle(inset + width, inset + height / 2.0f, radius + AndroidUtilities.dp(1) - 1, clearPaint); + canvas.drawCircle(inset, inset + height / 2.0f, radius + AndroidUtilities.dp(1) - 1, clearPaint); + + canvas.restore(); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PaintingOverlay.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PaintingOverlay.java index 4c65c242a4..163a8d27ff 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PaintingOverlay.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PaintingOverlay.java @@ -27,6 +27,7 @@ import org.telegram.messenger.LocaleController; import org.telegram.messenger.VideoEditedInfo; import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Components.Paint.Views.EditTextOutline; import org.telegram.ui.Components.Paint.Views.PaintTextOptionsView; @@ -167,6 +168,7 @@ public void setEntities(ArrayList entities, boolean View child = null; if (entity.type == 0) { BackupImageView imageView = new BackupImageView(getContext()); + imageView.setLayerNum(8); imageView.setAspectFit(true); ImageReceiver imageReceiver = imageView.getImageReceiver(); if (isVideo) { @@ -191,8 +193,6 @@ public void setEntities(ArrayList entities, boolean entity.view = child = imageView; } else if (entity.type == 1) { EditTextOutline editText = new EditTextOutline(getContext()) { - { animatedEmojiOffsetX = AndroidUtilities.dp(8); } - @Override public boolean dispatchTouchEvent(MotionEvent event) { return false; @@ -253,22 +253,22 @@ public boolean onTouchEvent(MotionEvent event) { if (Build.VERSION.SDK_INT >= 23) { editText.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE); } - if ((entity.subType & 1) != 0) { - editText.setTextColor(0xffffffff); - editText.setStrokeColor(entity.color); - editText.setFrameColor(0); - editText.setShadowLayer(0, 0, 0, 0); - } else if ((entity.subType & 4) != 0) { - editText.setTextColor(0xff000000); - editText.setStrokeColor(0); + editText.setShadowLayer(0, 0, 0, 0); + int textColor = entity.color; + if (entity.subType == 0) { editText.setFrameColor(entity.color); - editText.setShadowLayer(0, 0, 0, 0); + textColor = AndroidUtilities.computePerceivedBrightness(entity.color) >= .721f ? Color.BLACK : Color.WHITE; + } else if (entity.subType == 1) { + editText.setFrameColor(AndroidUtilities.computePerceivedBrightness(entity.color) >= .25f ? 0x99000000 : 0x99ffffff); + } else if (entity.subType == 2) { + editText.setFrameColor(AndroidUtilities.computePerceivedBrightness(entity.color) >= .25f ? Color.BLACK : Color.WHITE); } else { - editText.setTextColor(entity.color); - editText.setStrokeColor(0); editText.setFrameColor(0); - editText.setShadowLayer(5, 0, 1, 0x66000000); } + editText.setTextColor(textColor); + editText.setCursorColor(textColor); + editText.setHandlesColor(textColor); + editText.setHighlightColor(Theme.multAlpha(textColor, .4f)); entity.view = child = editText; } if (child != null) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PermanentLinkBottomSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PermanentLinkBottomSheet.java index 42e60fd872..d9fd18125e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PermanentLinkBottomSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PermanentLinkBottomSheet.java @@ -1,8 +1,14 @@ package org.telegram.ui.Components; import android.content.Context; +import android.graphics.Color; +import android.os.Build; +import android.text.TextUtils; +import android.util.TypedValue; import android.view.Gravity; import android.view.View; +import android.widget.FrameLayout; +import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; @@ -28,25 +34,38 @@ public class PermanentLinkBottomSheet extends BottomSheet { - TLRPC.ChatFull info; - RLottieDrawable linkIcon; private final TextView titleView; private final TextView subtitle; private final TextView manage; private final RLottieImageView imageView; + private final RLottieDrawable linkIcon; private final LinkActionView linkActionView; - private long chatId; + private final long chatId; private BaseFragment fragment; - private boolean isChannel; public PermanentLinkBottomSheet(Context context, boolean needFocus, BaseFragment fragment, TLRPC.ChatFull info, long chatId, boolean isChannel) { super(context, needFocus); - this.info = info; this.chatId = chatId; - this.isChannel = isChannel; setAllowNestedScroll(true); setApplyBottomPadding(false); + setApplyTopPadding(false); + fixNavigationBar(getThemedColor(Theme.key_windowBackgroundWhite)); + + LinearLayout linearLayout = new LinearLayout(context); + linearLayout.setOrientation(LinearLayout.VERTICAL); + + FrameLayout frameLayout = new FrameLayout(context); + frameLayout.addView(linearLayout); + + ImageView closeView = new ImageView(context); + closeView.setBackground(Theme.createSelectorDrawable(getThemedColor(Theme.key_listSelector))); + closeView.setColorFilter(getThemedColor(Theme.key_sheet_other)); + closeView.setImageResource(R.drawable.ic_layer_close); + closeView.setOnClickListener((view) -> dismiss()); + int closeViewPadding = AndroidUtilities.dp(8); + closeView.setPadding(closeViewPadding, closeViewPadding, closeViewPadding, closeViewPadding); + frameLayout.addView(closeView, LayoutHelper.createFrame(36, 36, Gravity.TOP | Gravity.END, 6, 8, 8, 0)); linkActionView = new LinkActionView(context, fragment, this, chatId, true, isChannel); linkActionView.setPermanent(true); @@ -60,23 +79,30 @@ public PermanentLinkBottomSheet(Context context, boolean needFocus, BaseFragment titleView = new TextView(context); titleView.setText(LocaleController.getString("InviteLink", R.string.InviteLink)); - titleView.setTextSize(24); + titleView.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); titleView.setGravity(Gravity.CENTER_HORIZONTAL); titleView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); subtitle = new TextView(context); subtitle.setText(isChannel ? LocaleController.getString("LinkInfoChannel", R.string.LinkInfoChannel) : LocaleController.getString("LinkInfo", R.string.LinkInfo)); - subtitle.setTextSize(14); + subtitle.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); subtitle.setGravity(Gravity.CENTER_HORIZONTAL); - subtitle.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText)); + subtitle.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); + subtitle.setLineSpacing(subtitle.getLineSpacingExtra(), subtitle.getLineSpacingMultiplier() * 1.1f); manage = new TextView(context); manage.setText(LocaleController.getString("ManageInviteLinks", R.string.ManageInviteLinks)); - manage.setTextSize(14); - manage.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlueText)); - manage.setBackground(Theme.createRadSelectorDrawable(ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_windowBackgroundWhiteBlueText), (int) (255 * 0.3f)), AndroidUtilities.dp(4), AndroidUtilities.dp(4))); - manage.setPadding(AndroidUtilities.dp(12), AndroidUtilities.dp(4), AndroidUtilities.dp(12), AndroidUtilities.dp(4)); - + manage.setGravity(Gravity.CENTER); + manage.setEllipsize(TextUtils.TruncateAt.END); + manage.setSingleLine(true); + manage.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + manage.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + manage.setTextColor(Theme.getColor(Theme.key_featuredStickers_addButton)); + manage.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(8), Color.TRANSPARENT, ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_featuredStickers_addButton), 120))); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + manage.setLetterSpacing(0.025f); + } manage.setOnClickListener(view -> { ManageLinksActivity manageFragment = new ManageLinksActivity(info.id, 0, 0); manageFragment.setInfo(info, info.exported_invite); @@ -84,17 +110,15 @@ public PermanentLinkBottomSheet(Context context, boolean needFocus, BaseFragment dismiss(); }); - LinearLayout linearLayout = new LinearLayout(context); - linearLayout.setOrientation(LinearLayout.VERTICAL); - linearLayout.addView(imageView, LayoutHelper.createLinear(90, 90, Gravity.CENTER_HORIZONTAL, 0, 24, 0, 0)); - linearLayout.addView(titleView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 60, 16, 60, 0)); - linearLayout.addView(subtitle, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 60, 16, 60, 0)); + linearLayout.addView(imageView, LayoutHelper.createLinear(90, 90, Gravity.CENTER_HORIZONTAL, 0, 33, 0, 0)); + linearLayout.addView(titleView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 60, 10, 60, 0)); + linearLayout.addView(subtitle, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 28, 7, 28, 2)); linearLayout.addView(linkActionView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - linearLayout.addView(manage, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 60, 26, 60, 26)); + linearLayout.addView(manage, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48, Gravity.CENTER_HORIZONTAL, 14, -2, 14, 6)); NestedScrollView scrollView = new NestedScrollView(context); scrollView.setVerticalScrollBarEnabled(false); - scrollView.addView(linearLayout); + scrollView.addView(frameLayout); setCustomView(scrollView); @@ -145,6 +169,7 @@ private void generateLink(boolean showDialog) { })); } + @SuppressWarnings("Convert2MethodRef") @Override public void show() { super.show(); @@ -156,8 +181,8 @@ public ArrayList getThemeDescriptions() { ArrayList arrayList = new ArrayList<>(); ThemeDescription.ThemeDescriptionDelegate descriptionDelegate = this::updateColors; arrayList.add(new ThemeDescription(titleView, ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); - arrayList.add(new ThemeDescription(subtitle, ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteGrayText)); - arrayList.add(new ThemeDescription(manage, ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteBlueText)); + arrayList.add(new ThemeDescription(subtitle, ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_dialogTextBlack)); + arrayList.add(new ThemeDescription(manage, ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_featuredStickers_addButton)); arrayList.add(new ThemeDescription(null, 0, null, null, null, descriptionDelegate, Theme.key_featuredStickers_addButton)); arrayList.add(new ThemeDescription(null, 0, null, null, null, descriptionDelegate, Theme.key_featuredStickers_buttonText)); arrayList.add(new ThemeDescription(null, 0, null, null, null, descriptionDelegate, Theme.key_windowBackgroundWhiteBlueText)); @@ -166,7 +191,7 @@ public ArrayList getThemeDescriptions() { private void updateColors() { imageView.setBackground(Theme.createCircleDrawable(AndroidUtilities.dp(90), Theme.getColor(Theme.key_featuredStickers_addButton))); - manage.setBackground(Theme.createRadSelectorDrawable(ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_windowBackgroundWhiteBlueText), (int) (255 * 0.3f)), AndroidUtilities.dp(4), AndroidUtilities.dp(4))); + manage.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(8), Color.TRANSPARENT, ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_featuredStickers_addButton), 120))); int color = Theme.getColor(Theme.key_featuredStickers_buttonText); linkIcon.setLayerColor("Top.**", color); linkIcon.setLayerColor("Bottom.**", color); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoCropView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoCropView.java index 6eda85a425..2b78dbeb31 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoCropView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoCropView.java @@ -402,8 +402,7 @@ public void invalidate() { cropView.invalidate(); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoFilterView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoFilterView.java index 0bef174438..2273e6fda3 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoFilterView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoFilterView.java @@ -12,22 +12,32 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.LinearGradient; +import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; +import android.graphics.Shader; import android.graphics.SurfaceTexture; import android.graphics.drawable.Drawable; import android.os.Build; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.util.Log; import android.util.TypedValue; import android.view.Gravity; +import android.view.HapticFeedbackConstants; import android.view.MotionEvent; import android.view.TextureView; import android.view.View; +import android.view.ViewConfiguration; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; +import androidx.annotation.NonNull; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -35,17 +45,22 @@ import org.telegram.messenger.LocaleController; import org.telegram.messenger.MediaController; import org.telegram.messenger.R; +import org.telegram.messenger.SharedConfig; +import org.telegram.messenger.Utilities; +import org.telegram.tgnet.AbstractSerializedData; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.BubbleActivity; import org.telegram.ui.Cells.PhotoEditRadioCell; import org.telegram.ui.Cells.PhotoEditToolCell; +import org.telegram.ui.Stories.recorder.PreviewView; +import org.telegram.ui.Stories.recorder.StoryRecorder; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; @SuppressLint("NewApi") -public class PhotoFilterView extends FrameLayout implements FilterShaders.FilterShadersDelegate { +public class PhotoFilterView extends FrameLayout implements FilterShaders.FilterShadersDelegate, StoryRecorder.Touchable { private final static int curveGranularity = 100; private final static int curveDataStep = 2; @@ -96,6 +111,7 @@ public class PhotoFilterView extends FrameLayout implements FilterShaders.Filter private TextView cancelTextView; private TextureView textureView; private boolean ownsTextureView; + private boolean ownLayout; private FilterGLThread eglThread; private RecyclerListView recyclerListView; private FrameLayout blurLayout; @@ -228,6 +244,22 @@ public float[] interpolateCurve() { public boolean isDefault() { return Math.abs(blacksLevel - 0) < 0.00001 && Math.abs(shadowsLevel - 25) < 0.00001 && Math.abs(midtonesLevel - 50) < 0.00001 && Math.abs(highlightsLevel - 75) < 0.00001 && Math.abs(whitesLevel - 100) < 0.00001; } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeFloat(blacksLevel); + stream.writeFloat(shadowsLevel); + stream.writeFloat(midtonesLevel); + stream.writeFloat(highlightsLevel); + stream.writeFloat(whitesLevel); + } + + public void readParams(AbstractSerializedData stream, boolean exception) { + blacksLevel = previousBlacksLevel = stream.readFloat(exception); + shadowsLevel = previousShadowsLevel = stream.readFloat(exception); + midtonesLevel = previousMidtonesLevel = stream.readFloat(exception); + highlightsLevel = previousHighlightsLevel = stream.readFloat(exception); + whitesLevel = previousWhitesLevel = stream.readFloat(exception); + } } public static class CurvesToolValue { @@ -268,10 +300,25 @@ public void fillBuffer() { public boolean shouldBeSkipped() { return luminanceCurve.isDefault() && redCurve.isDefault() && greenCurve.isDefault() && blueCurve.isDefault(); } + + public void serializeToStream(AbstractSerializedData stream) { + luminanceCurve.serializeToStream(stream); + redCurve.serializeToStream(stream); + greenCurve.serializeToStream(stream); + blueCurve.serializeToStream(stream); + } + + public void readParams(AbstractSerializedData stream, boolean exception) { + luminanceCurve.readParams(stream, exception); + redCurve.readParams(stream, exception); + greenCurve.readParams(stream, exception); + blueCurve.readParams(stream, exception); + } } - public PhotoFilterView(Context context, VideoEditTextureView videoTextureView, Bitmap bitmap, int rotation, MediaController.SavedFilterState state, PaintingOverlay overlay, int hasFaces, boolean mirror, Theme.ResourcesProvider resourcesProvider) { + public PhotoFilterView(Context context, VideoEditTextureView videoTextureView, Bitmap bitmap, int rotation, MediaController.SavedFilterState state, PaintingOverlay overlay, int hasFaces, boolean mirror, boolean ownLayout, Theme.ResourcesProvider resourcesProvider) { super(context); + this.ownLayout = ownLayout; this.resourcesProvider = resourcesProvider; inBubbleMode = context instanceof BubbleActivity; @@ -346,13 +393,15 @@ public PhotoFilterView(Context context, VideoEditTextureView videoTextureView, B } else { ownsTextureView = true; textureView = new TextureView(context); - addView(textureView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT)); + if (ownLayout) { + addView(textureView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT)); + } textureView.setVisibility(INVISIBLE); textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { if (eglThread == null && surface != null) { - eglThread = new FilterGLThread(surface, bitmapToEdit, orientation, isMirrored); + eglThread = new FilterGLThread(surface, bitmapToEdit, orientation, isMirrored, null, ownLayout); eglThread.setFilterGLThreadDelegate(PhotoFilterView.this); eglThread.setSurfaceTextureSize(width, height); eglThread.requestRender(true, true, false); @@ -390,7 +439,9 @@ public void onSurfaceTextureUpdated(SurfaceTexture surface) { blurControl = new PhotoFilterBlurControl(context); blurControl.setVisibility(INVISIBLE); - addView(blurControl, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP)); + if (ownLayout) { + addView(blurControl, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP)); + } blurControl.setDelegate((centerPoint, falloff, size, angle) -> { blurExcludeSize = size; blurExcludePoint = centerPoint; @@ -408,10 +459,12 @@ public void onSurfaceTextureUpdated(SurfaceTexture surface) { } }); curvesControl.setVisibility(INVISIBLE); - addView(curvesControl, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP)); + if (ownLayout) { + addView(curvesControl, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP)); + } toolsView = new FrameLayout(context); - addView(toolsView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 186, Gravity.LEFT | Gravity.BOTTOM)); + addView(toolsView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 186 + (!ownLayout ? 40 : 0), Gravity.LEFT | Gravity.BOTTOM)); FrameLayout frameLayout = new FrameLayout(context); frameLayout.setBackgroundColor(0xff000000); @@ -483,18 +536,18 @@ public void onSurfaceTextureUpdated(SurfaceTexture surface) { switchMode(); }); - recyclerListView = new RecyclerListView(context); + recyclerListView = new RecyclerListViewWithShadows(context); LinearLayoutManager layoutManager = new LinearLayoutManager(context); layoutManager.setOrientation(LinearLayoutManager.VERTICAL); recyclerListView.setLayoutManager(layoutManager); recyclerListView.setClipToPadding(false); recyclerListView.setOverScrollMode(RecyclerListView.OVER_SCROLL_NEVER); recyclerListView.setAdapter(new ToolsAdapter(context)); - toolsView.addView(recyclerListView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 120, Gravity.LEFT | Gravity.TOP)); + toolsView.addView(recyclerListView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 120 + (!ownLayout ? 60 : 0), Gravity.LEFT | Gravity.TOP)); curveLayout = new FrameLayout(context); curveLayout.setVisibility(INVISIBLE); - toolsView.addView(curveLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 78, Gravity.CENTER_HORIZONTAL, 0, 40, 0, 0)); + toolsView.addView(curveLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 78, Gravity.CENTER_HORIZONTAL, 0, 40 + (!ownLayout ? 40 : 0), 0, 0)); LinearLayout curveTextViewContainer = new LinearLayout(context); curveTextViewContainer.setOrientation(LinearLayout.HORIZONTAL); @@ -547,7 +600,7 @@ public void onSurfaceTextureUpdated(SurfaceTexture surface) { blurLayout = new FrameLayout(context); blurLayout.setVisibility(INVISIBLE); - toolsView.addView(blurLayout, LayoutHelper.createFrame(280, 60, Gravity.CENTER_HORIZONTAL, 0, 40, 0, 0)); + toolsView.addView(blurLayout, LayoutHelper.createFrame(280, 60, Gravity.CENTER_HORIZONTAL, 0, 40 + (!ownLayout ? 40 : 0), 0, 0)); blurOffButton = new TextView(context); blurOffButton.setCompoundDrawablePadding(AndroidUtilities.dp(2)); @@ -598,7 +651,7 @@ public void onSurfaceTextureUpdated(SurfaceTexture surface) { updateSelectedBlurType(); - if (Build.VERSION.SDK_INT >= 21 && !inBubbleMode) { + if (Build.VERSION.SDK_INT >= 21 && !inBubbleMode && ownLayout) { if (ownsTextureView) { ((LayoutParams) textureView.getLayoutParams()).topMargin = AndroidUtilities.statusBarHeight; } @@ -707,7 +760,54 @@ public boolean hasChanges() { } } - public void onTouch(MotionEvent event) { + private static class RecyclerListViewWithShadows extends RecyclerListView { + private final Paint topPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint bottomPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + private boolean top, bottom; + private AnimatedFloat topAlpha = new AnimatedFloat(this); + private AnimatedFloat bottomAlpha = new AnimatedFloat(this); + + public RecyclerListViewWithShadows(Context context) { + super(context); + topPaint.setShader(new LinearGradient(0, 0, 0, AndroidUtilities.dp(8), new int[] {0xff000000, 0x00000000}, new float[] {0, 1}, Shader.TileMode.CLAMP)); + bottomPaint.setShader(new LinearGradient(0, 0, 0, AndroidUtilities.dp(8), new int[] {0x00000000, 0xff000000}, new float[] {0, 1}, Shader.TileMode.CLAMP)); + } + + @Override + public void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + + final float topAlpha = this.topAlpha.set(top ? 1 : 0); + topPaint.setAlpha((int) (0xFF * topAlpha)); + canvas.drawRect(0, 0, getWidth(), AndroidUtilities.dp(8), topPaint); + + final float bottomAlpha = this.bottomAlpha.set(bottom ? 1 : 0); + bottomPaint.setAlpha((int) (0xFF * bottomAlpha)); + canvas.save(); + canvas.translate(0, getHeight() - AndroidUtilities.dp(8)); + canvas.drawRect(0, 0, getWidth(), AndroidUtilities.dp(8), bottomPaint); + canvas.restore(); + } + + @Override + public void onScrolled(int dx, int dy) { + super.onScrolled(dx, dy); + updateAlphas(); + } + + private void updateAlphas() { + final boolean top = canScrollVertically(-1); + final boolean bottom = canScrollVertically(1); + if (top != this.top || bottom != this.bottom) { + this.top = top; + this.bottom = bottom; + invalidate(); + } + } + } + + public boolean onTouch(MotionEvent event) { if (event.getActionMasked() == MotionEvent.ACTION_DOWN || event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) { if (textureView instanceof VideoEditTextureView) { if (((VideoEditTextureView) textureView).containsPoint(event.getX(), event.getY())) { @@ -721,6 +821,7 @@ public void onTouch(MotionEvent event) { } else if (event.getActionMasked() == MotionEvent.ACTION_UP || event.getActionMasked() == MotionEvent.ACTION_POINTER_UP) { setShowOriginal(false); } + return true; } private void setShowOriginal(boolean value) { @@ -776,16 +877,21 @@ public void shutdown() { VideoEditTextureView videoEditTextureView = (VideoEditTextureView) textureView; if (lastState == null) { videoEditTextureView.setDelegate(null); - } else { + } else if (eglThread != null) { eglThread.setFilterGLThreadDelegate(FilterShaders.getFilterShadersDelegate(lastState)); } } } - public void init() { - if (ownsTextureView) { - textureView.setVisibility(VISIBLE); + public TextureView getMyTextureView() { + if (ownsTextureView && !ownLayout) { + return textureView; } + return null; + } + + public void init() { + textureView.setVisibility(VISIBLE); } public Bitmap getBitmap() { @@ -793,6 +899,9 @@ public Bitmap getBitmap() { } private void fixLayout(int viewWidth, int viewHeight) { + if (!ownLayout) { + return; + } viewWidth -= AndroidUtilities.dp(28); viewHeight -= AndroidUtilities.dp(14 + 140 + 60) + (Build.VERSION.SDK_INT >= 21 && !inBubbleMode ? AndroidUtilities.statusBarHeight : 0); @@ -832,10 +941,12 @@ private void fixLayout(int viewWidth, int viewHeight) { layoutParams.width = width; layoutParams.height = height; } + curvesControl.setActualArea(bitmapX, bitmapY - (Build.VERSION.SDK_INT >= 21 && !inBubbleMode ? AndroidUtilities.statusBarHeight : 0), width, height); blurControl.setActualAreaSize(width, height); - LayoutParams layoutParams = (LayoutParams) blurControl.getLayoutParams(); + LayoutParams layoutParams; + layoutParams = (LayoutParams) blurControl.getLayoutParams(); layoutParams.height = viewHeight + AndroidUtilities.dp(38); layoutParams = (LayoutParams) curvesControl.getLayoutParams(); @@ -1005,11 +1116,11 @@ public FrameLayout getToolsView() { return toolsView; } - public View getCurveControl() { + public PhotoFilterCurvesControl getCurveControl() { return curvesControl; } - public View getBlurControl() { + public PhotoFilterBlurControl getBlurControl() { return blurControl; } @@ -1021,9 +1132,22 @@ public TextView getCancelTextView() { return cancelTextView; } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); + } + + public void setEnhanceValue(float value) { + enhanceValue = value * 100f; + for (int i = 0; i < recyclerListView.getChildCount(); ++i) { + View child = recyclerListView.getChildAt(i); + if (child instanceof PhotoEditToolCell && recyclerListView.getChildAdapterPosition(child) == enhanceTool) { + ((PhotoEditToolCell) child).setIconAndTextAndValue(LocaleController.getString("Enhance", R.string.Enhance), enhanceValue, 0, 100); + break; + } + } + if (eglThread != null) { + eglThread.requestRender(true); + } } public class ToolsAdapter extends RecyclerListView.SelectionAdapter { @@ -1157,4 +1281,172 @@ public int getItemViewType(int position) { return 0; } } + + public static class EnhanceView extends View { + + private TextPaint topTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + private TextPaint bottomTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + + private StaticLayout topText; + private float topTextWidth, topTextLeft; + + private StaticLayout bottomText; + private float bottomTextWidth, bottomTextLeft; + + private boolean shown; + private AnimatedFloat showT = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + + private boolean allowTouch; + private PhotoFilterView filterView; + private Runnable requestFilterView; + + public EnhanceView(Context context, Runnable requestFilterView) { + super(context); + this.requestFilterView = requestFilterView; + } + + public void setFilterView(PhotoFilterView filterView) { + this.filterView = filterView; + } + + public void setAllowTouch(boolean allow) { + this.allowTouch = allow; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setMeasuredDimension( + MeasureSpec.getSize(widthMeasureSpec), + MeasureSpec.getSize(heightMeasureSpec) + ); + + topTextPaint.setColor(0xffffffff); + topTextPaint.setShadowLayer(AndroidUtilities.dp(8), 0, 0, 0x30000000); + topTextPaint.setTextSize(AndroidUtilities.dp(34)); + bottomTextPaint.setColor(0xffffffff); + bottomTextPaint.setShadowLayer(AndroidUtilities.dp(12), 0, 0, 0x30000000); + bottomTextPaint.setTextSize(AndroidUtilities.dp(58)); + + if (topText == null) { + topText = new StaticLayout(LocaleController.getString("Enhance", R.string.Enhance), topTextPaint, getMeasuredWidth(), Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false); + topTextWidth = topText.getLineCount() > 0 ? topText.getLineWidth(0) : 0; + topTextLeft = topText.getLineCount() > 0 ? topText.getLineLeft(0) : 0; + } + } + + private void updateBottomText() { + float value = filterView == null ? 0 : filterView.getEnhanceValue(); + bottomText = new StaticLayout("" + Math.round(value * 100), bottomTextPaint, getMeasuredWidth(), Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false); + bottomTextWidth = bottomText.getLineCount() > 0 ? bottomText.getLineWidth(0) : 0; + bottomTextLeft = bottomText.getLineCount() > 0 ? bottomText.getLineLeft(0) : 0; + invalidate(); + } + + private boolean tracking; + private long downTime; + private float lastTouchX; + private float lastTouchY; + private float lastVibrateValue; + + private Runnable hide = () -> { + shown = false; + invalidate(); + }; + + public boolean onTouch(MotionEvent event) { + if (allowTouch && event.getPointerCount() == 1) { + final int action = event.getAction(); + if (action == MotionEvent.ACTION_DOWN) { + tracking = false; + downTime = System.currentTimeMillis(); + lastTouchX = event.getX(); + lastTouchY = event.getY(); + if (filterView != null) { + lastVibrateValue = filterView.getEnhanceValue(); + } + return true; + } else if (action == MotionEvent.ACTION_MOVE) { + final float x = event.getX(), y = event.getY(); + if (!tracking) { + if (System.currentTimeMillis() - downTime <= ViewConfiguration.getLongPressTimeout() && + Math.abs(lastTouchY - y) < Math.abs(lastTouchX - x) && + Math.abs(lastTouchX - x) > AndroidUtilities.touchSlop + ) { + tracking = true; + + AndroidUtilities.cancelRunOnUIThread(hide); + shown = true; + invalidate(); + } + } + + if (tracking) { + float dx = x - lastTouchX; + if (filterView == null) { + requestFilterView.run(); + } + if (filterView == null) { + tracking = false; + return false; + } + final float fullDistance = AndroidUtilities.displaySize.x * .8f; + final float value = filterView.getEnhanceValue(); + final float newValue = Utilities.clamp(value + dx / fullDistance, 1, 0); + int newValueInt = Math.round(newValue * 100), lastValueInt = Math.round(value * 100), lastVibrateValueInt = Math.round(lastVibrateValue * 100); + if (newValueInt != lastValueInt && (newValueInt == 100 || newValueInt == 0)) { + try { + performHapticFeedback(HapticFeedbackConstants.KEYBOARD_PRESS, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); + } catch (Exception ignore) {} + lastVibrateValue = newValue; + } else if (Math.abs(newValueInt - lastVibrateValueInt) > (SharedConfig.getDevicePerformanceClass() == SharedConfig.PERFORMANCE_CLASS_HIGH ? 5 : 10)) { + try { + performHapticFeedback(HapticFeedbackConstants.TEXT_HANDLE_MOVE, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); + } catch (Exception ignore) {} + lastVibrateValue = newValue; + } + filterView.setEnhanceValue(newValue); + updateBottomText(); + } + + lastTouchX = x; + lastTouchY = y; + } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { + tracking = false; + downTime = -1; + if (filterView != null) { + lastVibrateValue = filterView.getEnhanceValue(); + } + AndroidUtilities.runOnUIThread(hide, 600); + return false; + } + } else { + if (shown) { + shown = false; + invalidate(); + } + } + return false; + } + + @Override + protected void onDraw(Canvas canvas) { + float alpha = showT.set(shown); + + if (alpha > 0 && topText != null && bottomText != null) { + canvas.saveLayerAlpha(0, 0, getWidth(), getHeight(), (int) (0xFF * alpha), Canvas.ALL_SAVE_FLAG); + + canvas.save(); + canvas.translate((getWidth() - topTextWidth) / 2f - topTextLeft, getHeight() * .22f); + topText.draw(canvas); + canvas.restore(); + + canvas.save(); + canvas.translate((getWidth() - bottomTextWidth) / 2f - bottomTextLeft, getHeight() * .22f + AndroidUtilities.dp(60)); + bottomText.draw(canvas); + canvas.restore(); + + canvas.restore(); + } + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoPaintView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoPaintView.java index 597cbad4ea..cb7f7b7b42 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoPaintView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoPaintView.java @@ -845,7 +845,7 @@ public boolean onEntityLongClicked(EntityView entityView) { } @Override - public float[] getTransformedTouch(float x, float y) { + public float[] getTransformedTouch(MotionEvent e, float x, float y) { float x2 = (x - AndroidUtilities.displaySize.x / 2); float y2 = (y - AndroidUtilities.displaySize.y / 2); float rotation = (float) Math.toRadians(-entitiesView.getRotation()); @@ -1750,9 +1750,8 @@ private boolean isFaceAnchorOccupied(PhotoFace face, int anchor, long documentId } */ - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } private static class StickerPosition { @@ -1768,7 +1767,9 @@ private static class StickerPosition { } @Override - public void onBackPressed() {} + public boolean onBackPressed() { + return false; + } @Override public void updateZoom(boolean zoomedOut) {} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoViewerCaptionEnterView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoViewerCaptionEnterView.java index 0da2e783c1..e140da6272 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoViewerCaptionEnterView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoViewerCaptionEnterView.java @@ -51,7 +51,6 @@ import org.telegram.messenger.MessagesController; import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; -import org.telegram.messenger.SharedConfig; import org.telegram.messenger.UserConfig; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.AdjustPanLayoutHelper; @@ -62,6 +61,7 @@ import org.telegram.ui.LaunchActivity; import org.telegram.ui.PhotoViewer; import org.telegram.ui.PremiumPreviewFragment; +import org.telegram.ui.Stories.DarkThemeResourceProvider; import tw.nekomimi.nekogram.utils.VibrateUtil; @@ -596,33 +596,47 @@ public int getCursorPosition() { private class DarkTheme implements Theme.ResourcesProvider { @Override - public Integer getColor(String key) { - switch (key) { - case Theme.key_dialogBackground: return -14803426; - case Theme.key_windowBackgroundWhite: return -15198183; - case Theme.key_windowBackgroundWhiteBlackText: return -1; -// case Theme.key_chat_emojiPanelNewTrending: return 0xffff0000; -// case Theme.key_chat_gifSaveHintBackground: return 0xffff0000; -// case Theme.key_chat_gifSaveHintText: return 0xffff0000; - case Theme.key_chat_emojiPanelEmptyText: return -8553090; - case Theme.key_progressCircle: return -10177027; - case Theme.key_chat_emojiSearchIcon: return -9211020; - case Theme.key_chat_emojiPanelStickerPackSelector: - case Theme.key_chat_emojiSearchBackground: return 181267199; -// case Theme.key_chat_emojiPanelStickerSetName: return 0xffff0000; - case Theme.key_chat_emojiPanelIcon: return -9539985; - case Theme.key_chat_emojiBottomPanelIcon: return -9539985; - case Theme.key_chat_emojiPanelIconSelected: return -10177041; - case Theme.key_chat_emojiPanelStickerPackSelectorLine: return -10177041; - case Theme.key_chat_emojiPanelBackground: return -14803425; - case Theme.key_chat_emojiPanelShadowLine: return -1610612736; - case Theme.key_chat_emojiPanelBackspace: return -9539985; -// case Theme.key_featuredStickers_addButton: return 0xffff0000; -// case Theme.key_featuredStickers_removeButtonText: return 0xffff0000; - case Theme.key_listSelector: return 771751936; - case Theme.key_divider: return -16777216; - } - return null; + public int getColor(int key) { + if (key == Theme.key_dialogBackground) { + return -14803426; + } else if (key == Theme.key_windowBackgroundWhite) { + return -15198183; + } else if (key == Theme.key_windowBackgroundWhiteBlackText) { + return -1; + } else if (key == Theme.key_chat_emojiPanelEmptyText) { + return -8553090; + } else if (key == Theme.key_progressCircle) { + return -10177027; + } else if (key == Theme.key_chat_emojiSearchIcon) { + return -9211020; + } else if (key == Theme.key_chat_emojiPanelStickerPackSelector || key == Theme.key_chat_emojiSearchBackground) { + return 181267199; + } else if (key == Theme.key_chat_emojiPanelIcon) { + return -9539985; + } else if (key == Theme.key_chat_emojiBottomPanelIcon) { + return -9539985; + } else if (key == Theme.key_chat_emojiPanelIconSelected) { + return -10177041; + } else if (key == Theme.key_chat_emojiPanelStickerPackSelectorLine) { + return -10177041; + } else if (key == Theme.key_chat_emojiPanelBackground) { + return -14803425; + } else if (key == Theme.key_chat_emojiPanelShadowLine) { + return -1610612736; + } else if (key == Theme.key_chat_emojiPanelBackspace) { + return -9539985; + } else if (key == Theme.key_listSelector) { + return 771751936; + } else if (key == Theme.key_divider) { + return -16777216; + } else if (key == Theme.key_dialogFloatingButton) { + return -10177041; + } else if (key == Theme.key_dialogFloatingIcon) { + return 0xffffffff; + } else if (key == Theme.key_chat_emojiPanelStickerSetName) { + return 0x73ffffff; + } + return 0; } } @@ -634,7 +648,8 @@ private void createEmojiView() { if (emojiView != null) { return; } - emojiView = new EmojiView(null, true, false, false, getContext(), false, null, null, resourcesProvider); + emojiView = new EmojiView(null, true, false, false, getContext(), false, null, null, true, resourcesProvider); + emojiView.emojiCacheType = AnimatedEmojiDrawable.CACHE_TYPE_ALERT_PREVIEW; emojiView.setDelegate(new EmojiView.EmojiViewDelegate() { @Override public boolean onBackspace() { @@ -710,6 +725,7 @@ public void onCustomEmojiSelected(long documentId, TLRPC.Document document, Str } if (!isRecent) { span.fromEmojiKeyboard = true; + span.cacheType = AnimatedEmojiDrawable.CACHE_TYPE_ALERT_PREVIEW; } spannable.setSpan(span, 0, spannable.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); messageEditText.setText(messageEditText.getText().insert(i, spannable)); @@ -981,9 +997,8 @@ public EditTextCaption getMessageEditText() { return messageEditText; } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } public Theme.ResourcesProvider getResourcesProvider() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoViewerWebView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoViewerWebView.java index d765dda31d..713c285913 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoViewerWebView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoViewerWebView.java @@ -384,7 +384,7 @@ public boolean shouldOverrideUrlLoading(WebView view, String url) { errorButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); errorButton.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlueText)); errorButton.setPadding(AndroidUtilities.dp(12), AndroidUtilities.dp(8), AndroidUtilities.dp(12), AndroidUtilities.dp(8)); - errorButton.setBackground(Theme.AdaptiveRipple.rect(Theme.key_windowBackgroundWhiteBlueText, 12)); + errorButton.setBackground(Theme.AdaptiveRipple.rectByKey(Theme.key_windowBackgroundWhiteBlueText, 12)); errorButton.setVisibility(GONE); errorLayout.addView(errorButton, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 0, 8, 0, 0)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PinnedLineView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PinnedLineView.java index b27814aaa4..0ab0ee0a81 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PinnedLineView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PinnedLineView.java @@ -12,8 +12,6 @@ import android.graphics.PorterDuffXfermode; import android.graphics.RectF; import android.graphics.Shader; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.GradientDrawable; import android.view.View; import androidx.core.graphics.ColorUtils; @@ -284,8 +282,7 @@ public void onAnimationEnd(Animator animation) { } } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PipVideoOverlay.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PipVideoOverlay.java index 70c2054bb5..501ca23708 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PipVideoOverlay.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PipVideoOverlay.java @@ -44,6 +44,7 @@ import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.MediaController; import org.telegram.messenger.R; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.LaunchActivity; @@ -315,11 +316,12 @@ public void onAnimationEnd(Animator animation) { controlsAnimator.start(); } - public static void dimissAndDestroy() { + public static void dismissAndDestroy() { if (instance.parentSheet != null) { instance.parentSheet.destroy(); } else if (instance.photoViewer != null) { instance.photoViewer.destroyPhotoViewer(); + MediaController.getInstance().tryResumePausedAudio(); } dismiss(); } @@ -753,14 +755,14 @@ public boolean onDoubleTap(MotionEvent e) { @Override public boolean onSingleTapUp(MotionEvent e) { - if (!hasDoubleTap()) { + if (!hasDoubleTap(e)) { return onSingleTapConfirmed(e); } return super.onSingleTapUp(e); } @Override - public boolean hasDoubleTap() { + public boolean hasDoubleTap(MotionEvent e) { if (photoViewer == null || photoViewer.getVideoPlayer() == null && photoViewerWebView == null || isDismissing || isVideoCompleted || isScrolling || scaleGestureDetector.isInProgress() || !canLongClick) { return false; } @@ -894,7 +896,7 @@ public boolean dispatchTouchEvent(MotionEvent ev) { if (onSideToDismiss) { onSideToDismiss = false; - dimissAndDestroy(); + dismissAndDestroy(); } else { if (!pipXSpring.isRunning()) { pipXSpring.setStartValue(pipX) @@ -1028,7 +1030,7 @@ protected void onDraw(Canvas canvas) { closeButton.setColorFilter(Theme.getColor(Theme.key_voipgroup_actionBarItems), PorterDuff.Mode.MULTIPLY); closeButton.setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_listSelector))); closeButton.setPadding(padding, padding, padding, padding); - closeButton.setOnClickListener(v -> dimissAndDestroy()); + closeButton.setOnClickListener(v -> dismissAndDestroy()); controlsView.addView(closeButton, LayoutHelper.createFrame(buttonSize, buttonSize, Gravity.RIGHT, 0, margin, margin, 0)); ImageView expandButton = new ImageView(context); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PollVotesAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PollVotesAlert.java index 9242fe6703..df01940b9b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PollVotesAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PollVotesAlert.java @@ -40,7 +40,13 @@ import android.widget.FrameLayout; import android.widget.TextView; +import androidx.annotation.Keep; +import androidx.recyclerview.widget.DefaultItemAnimator; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.DialogObject; import org.telegram.messenger.Emoji; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MessageObject; @@ -48,6 +54,7 @@ import org.telegram.messenger.R; import org.telegram.messenger.UserConfig; import org.telegram.messenger.UserObject; +import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.BottomSheet; @@ -66,11 +73,6 @@ import java.util.HashSet; import java.util.Objects; -import androidx.annotation.Keep; -import androidx.recyclerview.widget.DefaultItemAnimator; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - public class PollVotesAlert extends BottomSheet { private RecyclerListView listView; @@ -107,7 +109,7 @@ public class PollVotesAlert extends BottomSheet { private static class VotesList { public int count; - public ArrayList votes; + public ArrayList votes; public ArrayList users; public String next_offset; public byte[] option; @@ -263,6 +265,7 @@ public class UserCell extends FrameLayout { private AvatarDrawable avatarDrawable; private TLRPC.User currentUser; + private TLRPC.Chat currentChat; private String lastName; private int lastStatus; @@ -296,12 +299,22 @@ public UserCell(Context context) { addView(nameTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 20, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 28 : 65, 14, LocaleController.isRTL ? 65 : 28, 0)); } - public void setData(TLRPC.User user, int num, boolean divider) { - currentUser = user; + public void setData(TLObject object, int num, boolean divider) { + if (object instanceof TLRPC.User) { + currentUser = (TLRPC.User) object; + currentChat = null; + } else if (object instanceof TLRPC.Chat){ + currentChat = (TLRPC.Chat) object; + currentUser = null; + } else { + currentUser = null; + currentChat = null; + } + needDivider = divider; - drawPlaceholder = user == null; + drawPlaceholder = object == null; placeholderNum = num; - if (user == null) { + if (object == null) { nameTextView.setText(""); avatarImageView.setImageDrawable(null); } else { @@ -337,6 +350,8 @@ public void update(int mask) { String newName = null; if (currentUser != null && currentUser.photo != null) { photo = currentUser.photo.photo_small; + } else if (currentChat != null && currentChat.photo != null) { + photo = currentChat.photo.photo_small; } if (mask != 0) { @@ -358,6 +373,8 @@ public void update(int mask) { if (!continueUpdate && lastName != null && (mask & MessagesController.UPDATE_MASK_NAME) != 0) { if (currentUser != null) { newName = UserObject.getUserName(currentUser); + } else if (currentChat != null) { + newName = currentChat.title; } if (!newName.equals(lastName)) { continueUpdate = true; @@ -368,22 +385,31 @@ public void update(int mask) { } } - avatarDrawable.setInfo(currentUser); - if (currentUser.status != null) { - lastStatus = currentUser.status.expires; - } else { - lastStatus = 0; + if (currentUser != null) { + avatarDrawable.setInfo(currentUser); + if (currentUser.status != null) { + lastStatus = currentUser.status.expires; + } else { + lastStatus = 0; + } + } else if (currentChat != null) { + avatarDrawable.setInfo(currentChat); } + if (currentUser != null) { lastName = newName == null ? UserObject.getUserName(currentUser) : newName; + } else if (currentChat != null){ + lastName = currentChat.title; } else { lastName = ""; } nameTextView.setText(lastName); lastAvatar = photo; - if (currentUser != null) { + if (currentChat != null) { + avatarImageView.setForUserOrChat(currentChat, avatarDrawable); + } else if (currentUser != null) { avatarImageView.setForUserOrChat(currentUser, avatarDrawable); } else { avatarImageView.setImageDrawable(avatarDrawable); @@ -473,7 +499,7 @@ public PollVotesAlert(ChatActivity parentFragment, MessageObject message) { TLRPC.TL_messages_votesList votesList = new TLRPC.TL_messages_votesList(); int N = answerVoters.voters <= 15 ? answerVoters.voters : 10; for (int b = 0; b < N; b++) { - votesList.votes.add(new TLRPC.TL_messageUserVoteInputOption()); + votesList.votes.add(new TLRPC.TL_messagePeerVoteInputOption()); } votesList.next_offset = N < answerVoters.voters ? "empty" : null; votesList.count = answerVoters.voters; @@ -813,15 +839,24 @@ protected int getExtraLayoutSpace(RecyclerView.State state) { })); } else if (view instanceof UserCell) { UserCell userCell = (UserCell) view; - if (userCell.currentUser == null) { + if (userCell.currentUser == null && userCell.currentChat == null) { return; } - TLRPC.User currentUser = parentFragment.getCurrentUser(); Bundle args = new Bundle(); - args.putLong("user_id", userCell.currentUser.id); + if (userCell.currentUser != null) { + args.putLong("user_id", userCell.currentUser.id); + } else { + args.putLong("chat_id", userCell.currentChat.id); + } dismiss(); ProfileActivity fragment = new ProfileActivity(args); - fragment.setPlayProfileAnimation(currentUser != null && currentUser.id == userCell.currentUser.id ? 1 : 0); + if (userCell.currentUser != null) { + TLRPC.User currentUser = parentFragment.getCurrentUser(); + fragment.setPlayProfileAnimation(currentUser != null && currentUser.id == userCell.currentUser.id ? 1 : 0); + } else { + TLRPC.Chat currentChat = parentFragment.getCurrentChat(); + fragment.setPlayProfileAnimation(currentChat != null && currentChat.id == userCell.currentChat.id ? 1 : 0); + } parentFragment.presentFragment(fragment); } }); @@ -1048,7 +1083,7 @@ public Object getItem(int section, int position) { if (position == 0) { return -928312; } else if (section >= 0 && section < voters.size() && position - 1 < voters.get(section).getCount()) { - return Objects.hash(voters.get(section).votes.get(position - 1).user_id); + return Objects.hash(DialogObject.getPeerDialogId(voters.get(section).votes.get(position - 1).peer)); } else { return -182734; } @@ -1163,7 +1198,7 @@ public void onBindViewHolder(int section, int position, RecyclerView.ViewHolder SectionCell sectionCell = (SectionCell) holder.itemView; section--; VotesList votesList = voters.get(section); - TLRPC.MessageUserVote vote = votesList.votes.get(0); + TLRPC.MessagePeerVote vote = votesList.votes.get(0); for (int a = 0, N = poll.answers.size(); a < N; a++) { TLRPC.TL_pollAnswer answer = poll.answers.get(a); if (Arrays.equals(answer.option, votesList.option)) { @@ -1199,14 +1234,9 @@ public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) { position--; UserCell userCell = (UserCell) holder.itemView; VotesList votesList = voters.get(section); - TLRPC.MessageUserVote vote = votesList.votes.get(position); - TLRPC.User user; - if (vote.user_id != 0) { - user = chatActivity.getMessagesController().getUser(vote.user_id); - } else { - user = null; - } - userCell.setData(user, position, position != votesList.getCount() - 1 || !TextUtils.isEmpty(votesList.next_offset) || votesList.collapsed); + TLRPC.MessagePeerVote vote = votesList.votes.get(position); + TLObject object = chatActivity.getMessagesController().getUserOrChat(DialogObject.getPeerDialogId(vote.peer)); + userCell.setData(object, position, position != votesList.getCount() - 1 || !TextUtils.isEmpty(votesList.next_offset) || votesList.collapsed); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PopupAudioView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PopupAudioView.java index daf0a6159f..1adfff22e9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PopupAudioView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PopupAudioView.java @@ -258,7 +258,7 @@ public void updateProgress() { for (int a = 0; a < currentMessageObject.getDocument().attributes.size(); a++) { TLRPC.DocumentAttribute attribute = currentMessageObject.getDocument().attributes.get(a); if (attribute instanceof TLRPC.TL_documentAttributeAudio) { - duration = attribute.duration; + duration = (int) attribute.duration; break; } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PopupSwipeBackLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PopupSwipeBackLayout.java index 8e893f9ef9..44da634b7d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PopupSwipeBackLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PopupSwipeBackLayout.java @@ -10,7 +10,6 @@ import android.graphics.Path; import android.graphics.Rect; import android.graphics.RectF; -import android.util.Log; import android.util.SparseIntArray; import android.view.GestureDetector; import android.view.Gravity; @@ -24,7 +23,7 @@ import androidx.core.view.GestureDetectorCompat; import org.telegram.messenger.AndroidUtilities; -import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.UserConfig; import org.telegram.ui.ActionBar.ActionBarMenuSlider; import org.telegram.ui.ActionBar.ActionBarPopupWindow; @@ -56,7 +55,7 @@ public class PopupSwipeBackLayout extends FrameLayout { private ValueAnimator foregroundAnimator; private int currentForegroundIndex = -1; - private int notificationIndex; + private AnimationNotificationsLocker notificationsLocker = new AnimationNotificationsLocker(); Theme.ResourcesProvider resourcesProvider; private int lastHeightReported = -1; @@ -294,7 +293,7 @@ private void animateToState(float f, float flingVal) { ValueAnimator val = ValueAnimator.ofFloat(transitionProgress, f).setDuration((long) (DURATION * Math.max(0.5f, Math.abs(transitionProgress - f) - Math.min(0.2f, flingVal)))); val.setInterpolator(CubicBezierInterpolator.DEFAULT); int selectedAccount = UserConfig.selectedAccount; - notificationIndex = NotificationCenter.getInstance(selectedAccount).setAnimationInProgress(notificationIndex, null); + notificationsLocker.lock(); val.addUpdateListener(animation -> { transitionProgress = (float) animation.getAnimatedValue(); invalidateTransforms(); @@ -308,7 +307,7 @@ public void onAnimationStart(Animator animation) { @Override public void onAnimationEnd(Animator animation) { - NotificationCenter.getInstance(selectedAccount).onAnimationFinish(notificationIndex); + notificationsLocker.unlock(); transitionProgress = f; if (f <= 0) { currentForegroundIndex = -1; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/DoubledLimitsBottomSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/DoubledLimitsBottomSheet.java index 947def6fbf..64f331f88f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/DoubledLimitsBottomSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/DoubledLimitsBottomSheet.java @@ -223,8 +223,8 @@ public LimitCell(@NonNull Context context) { public void setData(Limit limit) { title.setText(limit.title); subtitle.setText(limit.subtitle); - previewView.premiumCount.setText(Integer.toString(limit.premiumLimit)); - previewView.defaultCount.setText(Integer.toString(limit.defaultLimit)); + previewView.premiumCount.setText(String.format("%d", limit.premiumLimit)); + previewView.defaultCount.setText(String.format("%d", limit.defaultLimit)); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/GLIcon/GLIconRenderer.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/GLIcon/GLIconRenderer.java index ab7ae9416f..3a6e7ecb24 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/GLIcon/GLIconRenderer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/GLIcon/GLIconRenderer.java @@ -41,8 +41,8 @@ public class GLIconRenderer implements GLSurfaceView.Renderer { int color1; int color2; - public String colorKey1 = Theme.key_premiumStartGradient1; - public String colorKey2 = Theme.key_premiumStartGradient2; + public int colorKey1 = Theme.key_premiumStartGradient1; + public int colorKey2 = Theme.key_premiumStartGradient2; private final int style; public final static int FRAGMENT_STYLE = 0; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/GiftPremiumBottomSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/GiftPremiumBottomSheet.java index 357330752f..e3db8d8d3e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/GiftPremiumBottomSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/GiftPremiumBottomSheet.java @@ -48,7 +48,7 @@ import java.util.List; import java.util.concurrent.atomic.AtomicReference; -public class GiftPremiumBottomSheet extends BottomSheetWithRecyclerListView { +public class GiftPremiumBottomSheet extends BottomSheetWithRecyclerListView implements NotificationCenter.NotificationCenterDelegate { private PremiumGradient.PremiumGradientTools gradientTools; private PremiumGradient.PremiumGradientTools outlineGradient; private PremiumButtonView premiumButtonView; @@ -71,9 +71,10 @@ public class GiftPremiumBottomSheet extends BottomSheetWithRecyclerListView { @SuppressLint("NotifyDataSetChanged") public GiftPremiumBottomSheet(BaseFragment fragment, TLRPC.User user) { super(fragment, false, true); + fixNavigationBar(); this.user = user; - gradientTools = new PremiumGradient.PremiumGradientTools(Theme.key_premiumGradient1, Theme.key_premiumGradient2, null, null); + gradientTools = new PremiumGradient.PremiumGradientTools(Theme.key_premiumGradient1, Theme.key_premiumGradient2, -1, -1); gradientTools.exactly = true; gradientTools.x1 = 0; gradientTools.y1 = 0f; @@ -88,42 +89,7 @@ public GiftPremiumBottomSheet(BaseFragment fragment, TLRPC.User user) { dummyCell = new PremiumGiftTierCell(getContext()); - TLRPC.UserFull userFull = MessagesController.getInstance(currentAccount).getUserFull(user.id); - if (userFull != null) { -// List products = new ArrayList<>(); - long pricePerMonthMax = 0; - for (TLRPC.TL_premiumGiftOption option : userFull.premium_gifts) { - GiftTier giftTier = new GiftTier(option); - giftTiers.add(giftTier); - if (BuildVars.useInvoiceBilling()) { - if (giftTier.getPricePerMonth() > pricePerMonthMax) { - pricePerMonthMax = giftTier.getPricePerMonth(); - } - } else if (giftTier.giftOption.store_product != null && BillingController.getInstance().isReady()) { -// products.add(QueryProductDetailsParams.Product.newBuilder() -// .setProductType(BillingClient.ProductType.INAPP) -// .setProductId(giftTier.giftOption.store_product) -// .build()); - } - } - if (BuildVars.useInvoiceBilling()) { - for (GiftTier tier : giftTiers) { - tier.setPricePerMonthRegular(pricePerMonthMax); - } - } - } - - if (!giftTiers.isEmpty()) { - selectedTierIndex = 0; - updateButtonText(false); - } - - headerRow = rowsCount++; - tiersStartRow = rowsCount; - rowsCount += giftTiers.size(); - tiersEndRow = rowsCount; - footerRow = rowsCount++; - buttonRow = rowsCount++; + initData(); recyclerListView.setOnItemClickListener((view, position) -> { if (view instanceof PremiumGiftTierCell) { @@ -184,6 +150,66 @@ public GiftPremiumBottomSheet(BaseFragment fragment, TLRPC.User user) { }); } + private void initData() { + giftTiers.clear(); + rowsCount = 0; + TLRPC.UserFull userFull = MessagesController.getInstance(currentAccount).getUserFull(user.id); + if (userFull != null) { +// List products = new ArrayList<>(); + long pricePerMonthMax = 0; + for (TLRPC.TL_premiumGiftOption option : userFull.premium_gifts) { + GiftTier giftTier = new GiftTier(option); + giftTiers.add(giftTier); + if (BuildVars.useInvoiceBilling()) { + if (giftTier.getPricePerMonth() > pricePerMonthMax) { + pricePerMonthMax = giftTier.getPricePerMonth(); + } + } else if (giftTier.giftOption.store_product != null && BillingController.getInstance().isReady()) { +// products.add(QueryProductDetailsParams.Product.newBuilder() +// .setProductType(BillingClient.ProductType.INAPP) +// .setProductId(giftTier.giftOption.store_product) +// .build()); + } + } + if (BuildVars.useInvoiceBilling()) { + for (GiftTier tier : giftTiers) { + tier.setPricePerMonthRegular(pricePerMonthMax); + } + } + } + + if (!giftTiers.isEmpty()) { + selectedTierIndex = 0; + updateButtonText(false); + } + + headerRow = rowsCount++; + tiersStartRow = rowsCount; + rowsCount += giftTiers.size(); + tiersEndRow = rowsCount; + footerRow = rowsCount++; + buttonRow = rowsCount++; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.billingProductDetailsUpdated); + } + + @Override + public void dismiss() { + super.dismiss(); + NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.billingProductDetailsUpdated); + } + + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.billingProductDetailsUpdated) { + initData(); + } + } + private void updateButtonText(boolean animated) { if (LocaleController.isRTL) { animated = false; @@ -245,6 +271,7 @@ private void onGiftPremium() { } } Browser.openUrl(getBaseFragment().getParentActivity(), tier.giftOption.bot_url); + dismiss(); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/LimitPreviewView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/LimitPreviewView.java index 84cc7ad269..07620e16e1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/LimitPreviewView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/LimitPreviewView.java @@ -25,6 +25,7 @@ import android.widget.TextView; import androidx.annotation.NonNull; +import androidx.core.math.MathUtils; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.LocaleController; @@ -61,9 +62,14 @@ public class LimitPreviewView extends LinearLayout { LinearLayout limitsContainer; private boolean premiumLocked; - @SuppressLint("SetTextI18n") public LimitPreviewView(@NonNull Context context, int icon, int currentValue, int premiumLimit) { + this(context, icon, currentValue, premiumLimit, .5f); + } + + @SuppressLint("SetTextI18n") + public LimitPreviewView(@NonNull Context context, int icon, int currentValue, int premiumLimit, float inputPercent) { super(context); + final float percent = MathUtils.clamp(inputPercent, 0.1f, 0.9f); this.icon = icon; setOrientation(VERTICAL); setClipChildren(false); @@ -88,7 +94,7 @@ protected void dispatchDraw(Canvas canvas) { canvas.drawRoundRect(AndroidUtilities.rectTmp, AndroidUtilities.dp(6), AndroidUtilities.dp(6), grayPaint); canvas.save(); - canvas.clipRect(getMeasuredWidth() / 2f, 0, getMeasuredWidth(), getMeasuredHeight()); + canvas.clipRect(getMeasuredWidth() * percent, 0, getMeasuredWidth(), getMeasuredHeight()); Paint paint = PremiumGradient.getInstance().getMainGradientPaint(); if (parentVideForGradient != null) { View parent = parentVideForGradient; @@ -129,14 +135,21 @@ protected void dispatchDraw(Canvas canvas) { defaultCount = new TextView(context); defaultCount.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - defaultCount.setText(Integer.toString(premiumLimit)); + defaultCount.setText(String.format("%d", premiumLimit)); defaultCount.setGravity(Gravity.CENTER_VERTICAL); defaultCount.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); - limitLayout.addView(freeTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 30, Gravity.LEFT, 0, 0, 36, 0)); - limitLayout.addView(defaultCount, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 30, Gravity.RIGHT, 0, 0, 12, 0)); + if (percent > .3f) { + if (LocaleController.isRTL) { + limitLayout.addView(freeTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 30, Gravity.RIGHT, 36, 0, 12, 0)); + limitLayout.addView(defaultCount, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 30, Gravity.LEFT, 12, 0, 0, 0)); + } else { + limitLayout.addView(freeTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 30, Gravity.LEFT, 0, 0, 36, 0)); + limitLayout.addView(defaultCount, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 30, Gravity.RIGHT, 0, 0, 12, 0)); + } + } - limitsContainer.addView(limitLayout, LayoutHelper.createLinear(0, 30, 1f)); + limitsContainer.addView(limitLayout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 30, 2f * (1f - percent))); FrameLayout limitLayout2 = new FrameLayout(context); @@ -149,14 +162,21 @@ protected void dispatchDraw(Canvas canvas) { premiumCount = new TextView(context); premiumCount.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - premiumCount.setText(Integer.toString(premiumLimit)); + premiumCount.setText(String.format("%d", premiumLimit)); premiumCount.setGravity(Gravity.CENTER_VERTICAL); premiumCount.setTextColor(Color.WHITE); - limitLayout2.addView(limitTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 30, Gravity.LEFT, 0, 0, 36, 0)); - limitLayout2.addView(premiumCount, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 30, Gravity.RIGHT, 0, 0, 12, 0)); + if (percent < .7f) { + if (LocaleController.isRTL) { + limitLayout2.addView(limitTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 30, Gravity.RIGHT, 36, 0, 12, 0)); + limitLayout2.addView(premiumCount, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 30, Gravity.LEFT, 12, 0, 0, 0)); + } else { + limitLayout2.addView(limitTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 30, Gravity.LEFT, 0, 0, 36, 0)); + limitLayout2.addView(premiumCount, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 30, Gravity.RIGHT, 0, 0, 12, 0)); + } + } - limitsContainer.addView(limitLayout2, LayoutHelper.createLinear(0, 30, 1f)); + limitsContainer.addView(limitLayout2, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 30, 2f * percent)); addView(limitsContainer, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, 14, icon == 0 ? 0 : 12, 14, 0)); } @@ -283,7 +303,7 @@ public void setType(int type) { } public void setBagePosition(float position) { - this.position = position; + this.position = MathUtils.clamp(position, 0.1f, 0.9f); } public void setParentViewForGradien(ViewGroup containerView) { @@ -309,14 +329,6 @@ public void setPremiumLocked() { premiumLocked = true; } - private class LimitTextView extends LinearLayout { - - public LimitTextView(Context context) { - super(context); - } - - } - private class CounterView extends View { Path path = new Path(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/LimitReachedBottomSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/LimitReachedBottomSheet.java index 327e65d10e..1f03b36ff2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/LimitReachedBottomSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/LimitReachedBottomSheet.java @@ -59,10 +59,11 @@ public class LimitReachedBottomSheet extends BottomSheetWithRecyclerListView { public static final int TYPE_GIFS = 9; public static final int TYPE_STICKERS = 10; - public static final int TYPE_ADD_MEMBERS_RESTRICTED = 11; + public static final int TYPE_FOLDER_INVITES = 12; + public static final int TYPE_SHARED_FOLDERS = 13; + private boolean canSendLink; - private TLRPC.TL_webPage linkPreview; public static String limitTypeToServerString(int type) { switch (type) { @@ -84,6 +85,10 @@ public static String limitTypeToServerString(int type) { return "double_limits__saved_gifs"; case TYPE_STICKERS: return "double_limits__stickers_faved"; + case TYPE_FOLDER_INVITES: + return "double_limits__chatlist_invites"; + case TYPE_SHARED_FOLDERS: + return "double_limits__chatlists_joined"; } return null; } @@ -247,7 +252,7 @@ private void sendInviteMessages() { } for (Object obj : selectedChats) { TLRPC.User user = (TLRPC.User) obj; - SendMessagesHelper.getInstance(currentAccount).sendMessage(link, user.id, null, null, linkPreview, false, null, null, null, false, 0, null, false); + SendMessagesHelper.getInstance(currentAccount).sendMessage(SendMessagesHelper.SendMessageParams.of(link, user.id, null, null, null, true, null, null, null, false, 0, null, false)); } AndroidUtilities.runOnUIThread(() -> { BulletinFactory factory = BulletinFactory.global(); @@ -273,7 +278,21 @@ public void updatePremiumButtonText() { premiumButtonView.hideIcon(); } else { premiumButtonView.buttonTextView.setText(LocaleController.getString("IncreaseLimit", R.string.IncreaseLimit)); - premiumButtonView.setIcon(type == TYPE_ACCOUNTS ? R.raw.addone_icon : R.raw.double_icon); + if (limitParams != null) { + if (limitParams.defaultLimit + 1 == limitParams.premiumLimit) { + premiumButtonView.setIcon(R.raw.addone_icon); + } else if ( + limitParams.defaultLimit != 0 && limitParams.premiumLimit != 0 && + limitParams.premiumLimit / (float) limitParams.defaultLimit >= 1.6f && + limitParams.premiumLimit / (float) limitParams.defaultLimit <= 2.5f + ) { + premiumButtonView.setIcon(R.raw.double_icon); + } else { + premiumButtonView.hideIcon(); + } + } else { + premiumButtonView.hideIcon(); + } } } @@ -304,7 +323,7 @@ private void leaveFromSelectedGroups() { alertDialog.show(); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } @@ -336,7 +355,7 @@ private void updateButton() { } private static boolean hasFixedSize(int type) { - if (type == TYPE_PIN_DIALOGS || type == TYPE_FOLDERS || type == TYPE_CHATS_IN_FOLDER || type == TYPE_LARGE_FILE || type == TYPE_ACCOUNTS) { + if (type == TYPE_PIN_DIALOGS || type == TYPE_FOLDERS || type == TYPE_CHATS_IN_FOLDER || type == TYPE_LARGE_FILE || type == TYPE_ACCOUNTS || type == TYPE_FOLDER_INVITES || type == TYPE_SHARED_FOLDERS) { return true; } return false; @@ -500,26 +519,8 @@ public void setRestrictedUsers(TLRPC.Chat chat, ArrayList userRestri } updateRows(); updateButton(); - - TLRPC.ChatFull chatFull = MessagesController.getInstance(currentAccount).getChatFull(fromChat.id); - String link; - if (fromChat.username == null && chatFull != null && chatFull.exported_invite != null) { - link = chatFull.exported_invite.link; - - TLRPC.TL_messages_getWebPage webPagePreview = new TLRPC.TL_messages_getWebPage(); - webPagePreview.url = link; - ConnectionsManager.getInstance(currentAccount).sendRequest(webPagePreview,(response, error) -> AndroidUtilities.runOnUIThread(() -> { - if (response != null) { - if (response instanceof TLRPC.TL_webPage) { - linkPreview = (TLRPC.TL_webPage) response; - } - } - })); - } - } - private class HeaderView extends LinearLayout { @SuppressLint("SetTextI18n") @@ -611,7 +612,7 @@ public HeaderView(Context context) { } } - limitPreviewView = new LimitPreviewView(context, icon, currentValue, premiumLimit); + limitPreviewView = new LimitPreviewView(context, icon, currentValue, premiumLimit, position); limitPreviewView.setBagePosition(position); limitPreviewView.setType(type); limitPreviewView.defaultCount.setVisibility(View.GONE); @@ -659,6 +660,8 @@ public HeaderView(Context context) { description.setGravity(Gravity.CENTER_HORIZONTAL); description.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); addView(description, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, 0, 24, 0, 24, 24)); + + updatePremiumButtonText(); } } @@ -678,6 +681,20 @@ private static LimitParams getLimitParams(int type, int currentAccount) { limitParams.descriptionStr = LocaleController.formatString("LimitReachedPublicLinks", R.string.LimitReachedPublicLinks, limitParams.defaultLimit, limitParams.premiumLimit); limitParams.descriptionStrPremium = LocaleController.formatString("LimitReachedPublicLinksPremium", R.string.LimitReachedPublicLinksPremium, limitParams.premiumLimit); limitParams.descriptionStrLocked = LocaleController.formatString("LimitReachedPublicLinksLocked", R.string.LimitReachedPublicLinksLocked, limitParams.defaultLimit); + } else if (type == TYPE_FOLDER_INVITES) { + limitParams.defaultLimit = MessagesController.getInstance(currentAccount).chatlistInvitesLimitDefault; + limitParams.premiumLimit = MessagesController.getInstance(currentAccount).chatlistInvitesLimitPremium; + limitParams.icon = R.drawable.msg_limit_links; + limitParams.descriptionStr = LocaleController.formatString("LimitReachedFolderLinks", R.string.LimitReachedFolderLinks, limitParams.defaultLimit, limitParams.premiumLimit); + limitParams.descriptionStrPremium = LocaleController.formatString("LimitReachedFolderLinksPremium", R.string.LimitReachedFolderLinksPremium, limitParams.premiumLimit); + limitParams.descriptionStrLocked = LocaleController.formatString("LimitReachedFolderLinksLocked", R.string.LimitReachedFolderLinksLocked, limitParams.defaultLimit); + } else if (type == TYPE_SHARED_FOLDERS) { + limitParams.defaultLimit = MessagesController.getInstance(currentAccount).chatlistJoinedLimitDefault; + limitParams.premiumLimit = MessagesController.getInstance(currentAccount).chatlistJoinedLimitPremium; + limitParams.icon = R.drawable.msg_limit_folder; + limitParams.descriptionStr = LocaleController.formatString("LimitReachedSharedFolders", R.string.LimitReachedSharedFolders, limitParams.defaultLimit, limitParams.premiumLimit); + limitParams.descriptionStrPremium = LocaleController.formatString("LimitReachedSharedFoldersPremium", R.string.LimitReachedSharedFoldersPremium, limitParams.premiumLimit); + limitParams.descriptionStrLocked = LocaleController.formatString("LimitReachedSharedFoldersLocked", R.string.LimitReachedSharedFoldersLocked, limitParams.defaultLimit); } else if (type == TYPE_FOLDERS) { limitParams.defaultLimit = MessagesController.getInstance(currentAccount).dialogFiltersLimitDefault; limitParams.premiumLimit = MessagesController.getInstance(currentAccount).dialogFiltersLimitPremium; @@ -835,7 +852,7 @@ private void revokeLinks(ArrayList channels) { alertDialog.show(); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/PremiumButtonView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/PremiumButtonView.java index 198901b400..9358c9a92c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/PremiumButtonView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/PremiumButtonView.java @@ -70,6 +70,7 @@ public PremiumButtonView(@NonNull Context context, int radius, boolean createOve LinearLayout linearLayout = new LinearLayout(context); linearLayout.setOrientation(LinearLayout.HORIZONTAL); buttonTextView = new AnimatedTextView(context); + buttonTextView.setAnimationProperties(.35f, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); buttonTextView.setGravity(Gravity.CENTER); buttonTextView.setTextColor(Color.WHITE); buttonTextView.setTextSize(AndroidUtilities.dp(14)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/PremiumFeatureBottomSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/PremiumFeatureBottomSheet.java index f525d6ef8c..740bf35856 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/PremiumFeatureBottomSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/PremiumFeatureBottomSheet.java @@ -42,6 +42,7 @@ import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.RLottieDrawable; +import org.telegram.ui.LaunchActivity; import org.telegram.ui.PremiumPreviewFragment; import java.util.ArrayList; @@ -139,7 +140,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setApplyTopPadding(false); setApplyBottomPadding(false); useBackgroundTopPadding = false; - PremiumGradient.PremiumGradientTools gradientTools = new PremiumGradient.PremiumGradientTools(Theme.key_premiumGradientBottomSheet1, Theme.key_premiumGradientBottomSheet2, Theme.key_premiumGradientBottomSheet3, null); + PremiumGradient.PremiumGradientTools gradientTools = new PremiumGradient.PremiumGradientTools(Theme.key_premiumGradientBottomSheet1, Theme.key_premiumGradientBottomSheet2, Theme.key_premiumGradientBottomSheet3, -1); gradientTools.x1 = 0; gradientTools.y1 = 1.1f; gradientTools.x2 = 1.5f; @@ -318,8 +319,15 @@ public void onPageScrollStateChanged(int i) { ((ChatActivity) fragment).chatAttachAlert.dismiss(true); } } - if (fragment != null && fragment.getVisibleDialog() != null) { - fragment.getVisibleDialog().dismiss(); + BaseFragment mainFragment = LaunchActivity.getLastFragment(); + for (int i = 0; i < 2; i++) { + BaseFragment currentFragment = i == 0 ? fragment : mainFragment; + if (currentFragment != null && currentFragment.storyViewer != null && currentFragment.storyViewer.isShown()) { + currentFragment.storyViewer.dismissVisibleDialogs(); + } + if (currentFragment != null && currentFragment.getVisibleDialog() != null) { + currentFragment.getVisibleDialog().dismiss(); + } } if ((onlySelectedType || forceAbout) && fragment != null) { fragment.presentFragment(new PremiumPreviewFragment(PremiumPreviewFragment.featureTypeToServerString(featureData.type))); @@ -609,30 +617,30 @@ void setFeatureDate(PremiumPreviewFragment.PremiumFeatureData featureData) { } else if (onlySelectedType) { if (startType == PremiumPreviewFragment.PREMIUM_FEATURE_REACTIONS) { title.setText(LocaleController.getString("AdditionalReactions", R.string.AdditionalReactions)); - description.setText(LocaleController.getString("AdditionalReactionsDescription", R.string.AdditionalReactionsDescription)); + description.setText(AndroidUtilities.replaceTags(LocaleController.getString("AdditionalReactionsDescription", R.string.AdditionalReactionsDescription))); } else if (startType == PremiumPreviewFragment.PREMIUM_FEATURE_ADS) { title.setText(LocaleController.getString("PremiumPreviewNoAds", R.string.PremiumPreviewNoAds)); - description.setText(LocaleController.getString("PremiumPreviewNoAdsDescription2", R.string.PremiumPreviewNoAdsDescription2)); + description.setText(AndroidUtilities.replaceTags(LocaleController.getString("PremiumPreviewNoAdsDescription2", R.string.PremiumPreviewNoAdsDescription2))); } else if (startType == PremiumPreviewFragment.PREMIUM_FEATURE_APPLICATION_ICONS) { title.setText(LocaleController.getString("PremiumPreviewAppIcon", R.string.PremiumPreviewAppIcon)); - description.setText(LocaleController.getString("PremiumPreviewAppIconDescription2", R.string.PremiumPreviewAppIconDescription2)); + description.setText(AndroidUtilities.replaceTags(LocaleController.getString("PremiumPreviewAppIconDescription2", R.string.PremiumPreviewAppIconDescription2))); } else if (startType == PremiumPreviewFragment.PREMIUM_FEATURE_DOWNLOAD_SPEED) { title.setText(LocaleController.getString(R.string.PremiumPreviewDownloadSpeed)); - description.setText(LocaleController.getString(R.string.PremiumPreviewDownloadSpeedDescription2)); + description.setText(AndroidUtilities.replaceTags(LocaleController.getString(R.string.PremiumPreviewDownloadSpeedDescription2))); } else if (startType == PremiumPreviewFragment.PREMIUM_FEATURE_ADVANCED_CHAT_MANAGEMENT) { title.setText(LocaleController.getString(R.string.PremiumPreviewAdvancedChatManagement)); - description.setText(LocaleController.getString(R.string.PremiumPreviewAdvancedChatManagementDescription2)); + description.setText(AndroidUtilities.replaceTags(LocaleController.getString(R.string.PremiumPreviewAdvancedChatManagementDescription2))); } else if (startType == PremiumPreviewFragment.PREMIUM_FEATURE_VOICE_TO_TEXT) { title.setText(LocaleController.getString(R.string.PremiumPreviewVoiceToText)); - description.setText(LocaleController.getString(R.string.PremiumPreviewVoiceToTextDescription2)); + description.setText(AndroidUtilities.replaceTags(LocaleController.getString(R.string.PremiumPreviewVoiceToTextDescription2))); } else if (startType == PremiumPreviewFragment.PREMIUM_FEATURE_TRANSLATIONS) { title.setText(LocaleController.getString(R.string.PremiumPreviewTranslations)); - description.setText(LocaleController.getString(R.string.PremiumPreviewTranslationsDescription)); + description.setText(AndroidUtilities.replaceTags(LocaleController.getString(R.string.PremiumPreviewTranslationsDescription))); } topViewOnFullHeight = false; } else { title.setText(featureData.title); - description.setText(featureData.description); + description.setText(AndroidUtilities.replaceTags(featureData.description)); topViewOnFullHeight = false; } requestLayout(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/PremiumGiftTierCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/PremiumGiftTierCell.java index 20b178ff34..653d492906 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/PremiumGiftTierCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/PremiumGiftTierCell.java @@ -40,8 +40,8 @@ public class PremiumGiftTierCell extends ViewGroup { protected GiftPremiumBottomSheet.GiftTier tier; protected TextView discountView; - private String colorKey1 = Theme.key_windowBackgroundWhite; - private String colorKey2 = Theme.key_windowBackgroundGray; + private int colorKey1 = Theme.key_windowBackgroundWhite; + private int colorKey2 = Theme.key_windowBackgroundGray; private int gradientWidth; private LinearGradient gradient; private Paint paint = new Paint(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/PremiumGradient.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/PremiumGradient.java index 441bafe95e..77a305ed45 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/PremiumGradient.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/PremiumGradient.java @@ -153,21 +153,21 @@ public static class PremiumGradientTools { Matrix matrix = new Matrix(); public final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); - final String colorKey1, colorKey2, colorKey3, colorKey4, colorKey5; + final int colorKey1, colorKey2, colorKey3, colorKey4, colorKey5; final int colors[] = new int[5]; public boolean exactly; public float x1 = 0f, y1 = 1f, x2 = 1.5f, y2 = 0f; - public PremiumGradientTools(String colorKey1, String colorKey2, String colorKey3) { - this(colorKey1, colorKey2, colorKey3, null, null); + public PremiumGradientTools(int colorKey1, int colorKey2, int colorKey3) { + this(colorKey1, colorKey2, colorKey3, -1, -1); } - public PremiumGradientTools(String colorKey1, String colorKey2, String colorKey3, String colorKey4) { - this(colorKey1, colorKey2, colorKey3, colorKey4, null); + public PremiumGradientTools(int colorKey1, int colorKey2, int colorKey3, int colorKey4) { + this(colorKey1, colorKey2, colorKey3, colorKey4, -1); } - public PremiumGradientTools(String colorKey1, String colorKey2, String colorKey3, String colorKey4, String colorKey5) { + public PremiumGradientTools(int colorKey1, int colorKey2, int colorKey3, int colorKey4, int colorKey5) { this.colorKey1 = colorKey1; this.colorKey2 = colorKey2; this.colorKey3 = colorKey3; @@ -203,9 +203,9 @@ public void gradientMatrix(int x, int y, int x1, int y1, float xOffset, float yO private void chekColors() { int c1 = Theme.getColor(colorKey1); int c2 = Theme.getColor(colorKey2); - int c3 = colorKey3 == null ? 0 : Theme.getColor(colorKey3); - int c4 = colorKey4 == null ? 0 : Theme.getColor(colorKey4); - int c5 = colorKey5 == null ? 0 : Theme.getColor(colorKey5); + int c3 = colorKey3 < 0 ? 0 : Theme.getColor(colorKey3); + int c4 = colorKey4 < 0 ? 0 : Theme.getColor(colorKey4); + int c5 = colorKey5 < 0 ? 0 : Theme.getColor(colorKey5); if (colors[0] != c1 || colors[1] != c2 || colors[2] != c3 || colors[3] != c4 || colors[4] != c5) { colors[0] = c1; colors[1] = c2; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/PremiumLockIconView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/PremiumLockIconView.java index bd43995327..685469bbfd 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/PremiumLockIconView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/PremiumLockIconView.java @@ -30,6 +30,7 @@ public class PremiumLockIconView extends ImageView { StarParticlesView.Drawable starParticles; private boolean locked; private Theme.ResourcesProvider resourcesProvider; + boolean attachedToWindow; public PremiumLockIconView(Context context, int type) { this(context, type, null); @@ -104,7 +105,7 @@ protected void onDraw(Canvas canvas) { if (waitingImage) { if (imageReceiver != null && imageReceiver.getBitmap() != null) { waitingImage = false; - setColor(getDominantColor(imageReceiver.getBitmap())); + setColor(AndroidUtilities.getDominantColor(imageReceiver.getBitmap())); } else { invalidate(); } @@ -164,34 +165,10 @@ public ImageReceiver getImageReceiver() { return imageReceiver; } - public static int getDominantColor(Bitmap bitmap) { - if (bitmap == null) { - return Color.WHITE; - } - float stepH = (bitmap.getHeight() - 1) / 10f; - float stepW = (bitmap.getWidth() - 1) / 10f; - int r = 0, g = 0, b = 0; - int amount = 0; - for (int i = 0; i < 10; i++) { - for (int j = 0; j < 10; j++) { - int x = (int) (stepW * i); - int y = (int) (stepH * j); - int pixel = bitmap.getPixel(x, y); - if (Color.alpha(pixel) > 200) { - r += Color.red(pixel); - g += Color.green(pixel); - b += Color.blue(pixel); - amount++; - } - } - } - if (amount == 0) { - return 0; - } - return Color.argb(255, r / amount, g / amount, b / amount); - } - private void updateGradient() { + if (!attachedToWindow) { + return; + } if (getMeasuredHeight() != 0 && getMeasuredWidth() != 0) { int c1 = currentColor; int c2; @@ -219,6 +196,27 @@ private void updateGradient() { } } + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + attachedToWindow = true; + if (type != TYPE_REACTIONS) { + updateGradient(); + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + attachedToWindow = false; + if (paint != null) { + paint.setShader(null); + paint = null; + } + shader = null; + wasDrawn = false; + } + public void setWaitingImage() { waitingImage = true; wasDrawn = false; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/PremiumNotAvailableBottomSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/PremiumNotAvailableBottomSheet.java index 6877ef8a79..085299bf58 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/PremiumNotAvailableBottomSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/PremiumNotAvailableBottomSheet.java @@ -48,7 +48,7 @@ public PremiumNotAvailableBottomSheet(BaseFragment fragment) { buttonTextView.setTextColor(Theme.getColor(Theme.key_featuredStickers_buttonText)); buttonTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); buttonTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - buttonTextView.setBackground(Theme.AdaptiveRipple.filledRect(Theme.key_featuredStickers_addButton, 8)); + buttonTextView.setBackground(Theme.AdaptiveRipple.filledRectByKey(Theme.key_featuredStickers_addButton, 8)); buttonTextView.setText(LocaleController.getString(R.string.InstallOfficialApp)); buttonTextView.setOnClickListener(v -> { try { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/PremiumTierCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/PremiumTierCell.java index 4c90d1c40c..e873123d73 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/PremiumTierCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/PremiumTierCell.java @@ -43,8 +43,8 @@ public class PremiumTierCell extends ViewGroup { protected PremiumPreviewFragment.SubscriptionTier tier; protected TextView discountView; - private String colorKey1 = Theme.key_windowBackgroundWhite; - private String colorKey2 = Theme.key_windowBackgroundGray; + private int colorKey1 = Theme.key_windowBackgroundWhite; + private int colorKey2 = Theme.key_windowBackgroundGray; private int gradientWidth; private LinearGradient gradient; private Paint paint = new Paint(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/StarParticlesView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/StarParticlesView.java index 7d846a8a25..c00068da42 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/StarParticlesView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/StarParticlesView.java @@ -141,7 +141,7 @@ public static class Drawable { public boolean forceMaxAlpha = false; public boolean roundEffect = true; public int type = -1; - public String colorKey = Theme.key_premiumStartSmallStarsColor; + public int colorKey = Theme.key_premiumStartSmallStarsColor; public boolean svg; public long pausedTime; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/VideoScreenPreview.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/VideoScreenPreview.java index 3e94dfdc8c..9f0ffc17c4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/VideoScreenPreview.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/VideoScreenPreview.java @@ -506,6 +506,7 @@ protected void onDetachedFromWindow() { helloParticlesDrawable.recycle(); helloParticlesDrawable = null; } + stopVideoPlayer(); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ProximitySheet.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ProximitySheet.java index e01ae52ada..ccb60f17b8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ProximitySheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ProximitySheet.java @@ -251,7 +251,7 @@ public CharSequence getAccessibilityClassName() { buttonTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); buttonTextView.setMaxLines(2); buttonTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - buttonTextView.setBackgroundDrawable(Theme.AdaptiveRipple.filledRect(Theme.key_featuredStickers_addButton, 4)); + buttonTextView.setBackgroundDrawable(Theme.AdaptiveRipple.filledRectByKey(Theme.key_featuredStickers_addButton, 4)); buttonContainer.addView(buttonTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48)); buttonTextView.setOnClickListener(v -> { if (buttonTextView.getTag() != null) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ProxyDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ProxyDrawable.java index b8494dd4d2..b488aec41f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ProxyDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ProxyDrawable.java @@ -5,6 +5,7 @@ import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.PixelFormat; +import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.os.SystemClock; @@ -29,13 +30,15 @@ public class ProxyDrawable extends Drawable { private boolean connected; private boolean isEnabled; + private int colorKey = -1; + public ProxyDrawable(Context context) { super(); - emptyDrawable = context.getResources().getDrawable(R.drawable.proxy_off); - fullDrawable = context.getResources().getDrawable(R.drawable.proxy_on); + emptyDrawable = context.getResources().getDrawable(R.drawable.msg2_proxy_off); + fullDrawable = context.getResources().getDrawable(R.drawable.msg2_proxy_on); outerPaint.setStyle(Paint.Style.STROKE); - outerPaint.setStrokeWidth(AndroidUtilities.dp(2)); + outerPaint.setStrokeWidth(AndroidUtilities.dp(1.66f)); outerPaint.setStrokeCap(Paint.Cap.ROUND); lastUpdateTime = SystemClock.elapsedRealtime(); } @@ -57,13 +60,13 @@ public void draw(Canvas canvas) { lastUpdateTime = newTime; if (!isEnabled) { - emptyDrawable.setBounds(getBounds()); + setBounds(emptyDrawable); emptyDrawable.draw(canvas); } else if (!connected || connectedAnimationProgress != 1.0f) { - emptyDrawable.setBounds(getBounds()); + setBounds(emptyDrawable); emptyDrawable.draw(canvas); - outerPaint.setColor(Theme.getColor(Theme.key_contextProgressOuter2)); + outerPaint.setColor(Theme.getColor(colorKey < 0 ? Theme.key_contextProgressOuter2 : colorKey)); outerPaint.setAlpha((int) (255 * (1.0f - connectedAnimationProgress))); radOffset += 360 * dt / 1000.0f; @@ -71,16 +74,17 @@ public void draw(Canvas canvas) { int width = getBounds().width(); int height = getBounds().height(); - int x = width / 2 - AndroidUtilities.dp(3); - int y = height / 2 - AndroidUtilities.dp(3); - cicleRect.set(x, y, x + AndroidUtilities.dp(6), y + AndroidUtilities.dp(6)); + int r = AndroidUtilities.dp(4); + int x = width / 2 - r; + int y = height / 2 - r; + cicleRect.set(x, y, x + r + r, y + r + r); canvas.drawArc(cicleRect, -90 + radOffset, 90, false, outerPaint); invalidateSelf(); } if (isEnabled && (connected || connectedAnimationProgress != 0.0f)) { fullDrawable.setAlpha((int) (255 * connectedAnimationProgress)); - fullDrawable.setBounds(getBounds()); + setBounds(fullDrawable); fullDrawable.draw(canvas); } @@ -99,6 +103,16 @@ public void draw(Canvas canvas) { } } + private void setBounds(Drawable drawable) { + Rect bounds = getBounds(); + drawable.setBounds( + bounds.centerX() - drawable.getIntrinsicWidth() / 2, + bounds.centerY() - drawable.getIntrinsicHeight() / 2, + bounds.centerX() + drawable.getIntrinsicWidth() / 2, + bounds.centerY() + drawable.getIntrinsicHeight() / 2 + ); + } + @Override public void setAlpha(int alpha) { @@ -110,6 +124,10 @@ public void setColorFilter(ColorFilter cf) { fullDrawable.setColorFilter(cf); } + public void setColorKey(int colorKey) { + this.colorKey = colorKey; + } + @Override public int getOpacity() { return PixelFormat.TRANSPARENT; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PullForegroundDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PullForegroundDrawable.java index 6deaefbdcd..05346031bf 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PullForegroundDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PullForegroundDrawable.java @@ -27,6 +27,7 @@ import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.R; import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Stories.recorder.HintView2; import org.telegram.ui.TopicsFragment; public class PullForegroundDrawable { @@ -38,9 +39,9 @@ public class PullForegroundDrawable { public final static long minPullingTime = 200L; public int scrollDy; - private String backgroundColorKey = Theme.key_chats_archivePullDownBackground; - private String backgroundActiveColorKey = Theme.key_chats_archivePullDownBackgroundActive; - private String avatarBackgroundColorKey = Theme.key_avatar_backgroundArchivedHidden; + private int backgroundColorKey = Theme.key_chats_archivePullDownBackground; + private int backgroundActiveColorKey = Theme.key_chats_archivePullDownBackgroundActive; + private int avatarBackgroundColorKey = Theme.key_avatar_backgroundArchivedHidden; private boolean changeAvatarColor = true; private final Paint paintSecondary = new Paint(Paint.ANTI_ALIAS_FLAG); @@ -88,10 +89,15 @@ public class PullForegroundDrawable { public float outImageSize; public float outOverScroll; + private final CharSequence pullTooltipText; private StaticLayout pullTooltipLayout; - private float pullTooltipLayoutWidth; + private float pullTooltipLayoutScale = 1; + private float pullTooltipLayoutLeft, pullTooltipLayoutWidth; + + private final CharSequence releaseTooltipText; private StaticLayout releaseTooltipLayout; - private float releaseTooltipLayoutWidth; + private float releaseTooltipLayoutScale = 1; + private float releaseTooltipLayoutLeft, releaseTooltipLayoutWidth; private boolean willDraw; private boolean isOut; @@ -120,21 +126,68 @@ public PullForegroundDrawable(CharSequence pullText, CharSequence releaseText) { final ViewConfiguration vc = ViewConfiguration.get(ApplicationLoader.applicationContext); touchSlop = vc.getScaledTouchSlop(); - pullTooltipLayout = new StaticLayout(pullText, 0, pullText.length(), tooltipTextPaint, AndroidUtilities.displaySize.x, Layout.Alignment.ALIGN_NORMAL, 1, 0, false); - pullTooltipLayoutWidth = pullTooltipLayout.getLineWidth(0); - releaseTooltipLayout = new StaticLayout(releaseText, 0, releaseText.length(), tooltipTextPaint, AndroidUtilities.displaySize.x, Layout.Alignment.ALIGN_NORMAL, 1, 0, false); - releaseTooltipLayoutWidth = releaseTooltipLayout.getLineWidth(0); + pullTooltipText = pullText; + releaseTooltipText = releaseText; try { generalTopicDrawable = ApplicationLoader.applicationContext.getResources().getDrawable(R.drawable.msg_filled_general).mutate(); } catch (Exception ignore) {} } + private int lastWidth; + private void checkTextLayouts(int width) { + if (width != lastWidth) { + + float textWidth; + int layoutWidth; + + pullTooltipLayout = new StaticLayout(pullTooltipText, tooltipTextPaint, AndroidUtilities.displaySize.x, Layout.Alignment.ALIGN_CENTER, 1, 0, false); + textWidth = 0; + for (int i = 0; i < pullTooltipLayout.getLineCount(); ++i) { + textWidth = Math.max(textWidth, pullTooltipLayout.getLineWidth(i)); + } + pullTooltipLayoutScale = Math.min(1, width / textWidth); + layoutWidth = (int) Math.ceil(textWidth); + if (pullTooltipLayoutScale < .8f) { + pullTooltipLayoutScale = .8f; + layoutWidth = HintView2.cutInFancyHalf(pullTooltipText, tooltipTextPaint); + } + pullTooltipLayout = new StaticLayout(pullTooltipText, tooltipTextPaint, layoutWidth, Layout.Alignment.ALIGN_CENTER, 1, 0, false); + pullTooltipLayoutLeft = layoutWidth; + pullTooltipLayoutWidth = 0; + for (int i = 0; i < pullTooltipLayout.getLineCount(); ++i) { + pullTooltipLayoutLeft = Math.min(pullTooltipLayoutLeft, pullTooltipLayout.getLineLeft(i)); + pullTooltipLayoutWidth = Math.max(pullTooltipLayoutWidth, pullTooltipLayout.getLineWidth(i)); + } + + releaseTooltipLayout = new StaticLayout(releaseTooltipText, tooltipTextPaint, AndroidUtilities.displaySize.x, Layout.Alignment.ALIGN_CENTER, 1, 0, false); + textWidth = 0; + for (int i = 0; i < releaseTooltipLayout.getLineCount(); ++i) { + textWidth = Math.max(textWidth, releaseTooltipLayout.getLineWidth(i)); + } + releaseTooltipLayoutScale = Math.min(1, width / textWidth); + layoutWidth = (int) Math.ceil(textWidth); + if (releaseTooltipLayoutScale < .8f) { + releaseTooltipLayoutScale = .8f; + layoutWidth = HintView2.cutInFancyHalf(releaseTooltipText, tooltipTextPaint); + } + releaseTooltipLayout = new StaticLayout(releaseTooltipText, tooltipTextPaint, layoutWidth, Layout.Alignment.ALIGN_CENTER, 1, 0, false); + releaseTooltipLayoutLeft = layoutWidth; + releaseTooltipLayoutWidth = 0; + for (int i = 0; i < releaseTooltipLayout.getLineCount(); ++i) { + releaseTooltipLayoutLeft = Math.min(releaseTooltipLayoutLeft, releaseTooltipLayout.getLineLeft(i)); + releaseTooltipLayoutWidth = Math.max(releaseTooltipLayoutWidth, releaseTooltipLayout.getLineWidth(i)); + } + + lastWidth = width; + } + } + public static int getMaxOverscroll() { return AndroidUtilities.dp(72); } - public void setColors(String background, String active) { + public void setColors(int background, int active) { backgroundColorKey = background; backgroundActiveColorKey = active; changeAvatarColor = false; @@ -189,6 +242,8 @@ public void draw(Canvas canvas, boolean header) { float bounceP = bounceIn ? (0.07f * bounceProgress) - 0.05f : 0.02f * bounceProgress; + checkTextLayouts(cell.getWidth() - startPadding * 4 - AndroidUtilities.dp(16)); + updateTextProgress(pullProgress); float outProgressHalf = outProgress * 2f; @@ -213,7 +268,7 @@ public void draw(Canvas canvas, boolean header) { canvas.save(); if (header) { - canvas.clipRect(0, 0, listView.getMeasuredWidth(), overscroll + 1); + canvas.clipRect(0, -AndroidUtilities.dp(4) /*fix overscroll*/, listView.getMeasuredWidth(), overscroll + 1); } if (outProgress == 0f) { if (!(accentRevalProgress == 1f || accentRevalProgressOut == 1)) { @@ -306,35 +361,40 @@ public void draw(Canvas canvas, boolean header) { } float textY = cell.getHeight() - ((diameter + smallMargin * 2) / 2f) + AndroidUtilities.dp(6); - float textCx = cell.getWidth() / 2f - AndroidUtilities.dp(2); - - if (textSwappingProgress > 0 && textSwappingProgress < 1f) { - canvas.save(); - float scale = 0.8f + 0.2f * textSwappingProgress; - canvas.scale(scale, scale, textCx, textY + AndroidUtilities.dp(16) * (1f - textSwappingProgress)); - } - canvas.saveLayerAlpha(0, 0, cell.getMeasuredWidth(), cell.getMeasuredHeight(), (int) (255 * textSwappingProgress * startPullProgress * textInProgress), Canvas.ALL_SAVE_FLAG); - canvas.translate(textCx - pullTooltipLayoutWidth / 2f, textY + AndroidUtilities.dp(8) * (1f - textSwappingProgress) - tooltipTextPaint.getTextSize()); - pullTooltipLayout.draw(canvas); - canvas.restore(); + float textCx = (cell.getWidth() + (isTopic ? startPadding * 2 : 0)) / 2f; - if (textSwappingProgress > 0 && textSwappingProgress < 1f) { + if (pullTooltipLayout != null) { + if (textSwappingProgress > 0 && textSwappingProgress < 1f) { + canvas.save(); + float scale = 0.8f + 0.2f * textSwappingProgress; + canvas.scale(scale, scale, textCx, textY + AndroidUtilities.dp(16) * (1f - textSwappingProgress)); + } + canvas.saveLayerAlpha(0, 0, cell.getMeasuredWidth(), cell.getMeasuredHeight(), (int) (255 * textSwappingProgress * startPullProgress * textInProgress), Canvas.ALL_SAVE_FLAG); + canvas.translate(textCx - pullTooltipLayoutLeft - pullTooltipLayoutWidth / 2f, textY + AndroidUtilities.dp(8) * (1f - textSwappingProgress) - pullTooltipLayout.getHeight()); + canvas.scale(pullTooltipLayoutScale, pullTooltipLayoutScale, pullTooltipLayoutLeft + pullTooltipLayoutWidth / 2f, pullTooltipLayout.getHeight()); + pullTooltipLayout.draw(canvas); canvas.restore(); + if (textSwappingProgress > 0 && textSwappingProgress < 1f) { + canvas.restore(); + } } - if (textSwappingProgress > 0 && textSwappingProgress < 1f) { - canvas.save(); - float scale = 0.9f + 0.1f * (1f - textSwappingProgress); - canvas.scale(scale, scale, textCx, textY - AndroidUtilities.dp(8) * (textSwappingProgress)); - } - canvas.saveLayerAlpha(0, 0, cell.getMeasuredWidth(), cell.getMeasuredHeight(), (int) (255 * (1f - textSwappingProgress) * startPullProgress * textInProgress), Canvas.ALL_SAVE_FLAG); - canvas.translate(textCx - releaseTooltipLayoutWidth / 2f, textY + AndroidUtilities.dp(8) * (textSwappingProgress) - tooltipTextPaint.getTextSize()); - releaseTooltipLayout.draw(canvas); - canvas.restore(); - - if (textSwappingProgress > 0 && textSwappingProgress < 1f) { + if (releaseTooltipLayout != null) { + if (textSwappingProgress > 0 && textSwappingProgress < 1f) { + canvas.save(); + float scale = 0.9f + 0.1f * (1f - textSwappingProgress); + canvas.scale(scale, scale, textCx, textY - AndroidUtilities.dp(8) * (textSwappingProgress)); + } + canvas.saveLayerAlpha(0, 0, cell.getMeasuredWidth(), cell.getMeasuredHeight(), (int) (255 * (1f - textSwappingProgress) * startPullProgress * textInProgress), Canvas.ALL_SAVE_FLAG); + canvas.translate(textCx - releaseTooltipLayoutLeft - releaseTooltipLayoutWidth / 2f, textY + AndroidUtilities.dp(8) * (textSwappingProgress) - releaseTooltipLayout.getHeight()); + canvas.scale(releaseTooltipLayoutScale, releaseTooltipLayoutScale, releaseTooltipLayoutLeft + releaseTooltipLayoutWidth / 2f, releaseTooltipLayout.getHeight()); + releaseTooltipLayout.draw(canvas); canvas.restore(); + if (textSwappingProgress > 0 && textSwappingProgress < 1f) { + canvas.restore(); + } } + canvas.restore(); if (!isTopic && changeAvatarColor && outProgress > 0) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/RLottieDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/RLottieDrawable.java index 912577346c..d922d6d772 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/RLottieDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/RLottieDrawable.java @@ -24,6 +24,7 @@ import android.view.HapticFeedbackConstants; import android.view.View; +import com.google.android.exoplayer2.util.Log; import com.google.gson.Gson; import org.telegram.messenger.AndroidUtilities; @@ -54,6 +55,8 @@ public class RLottieDrawable extends BitmapDrawable implements Animatable, Bitma public static native long create(String src, String json, int w, int h, int[] params, boolean precache, int[] colorReplacement, boolean limitFps, int fitzModifier); + public static native long getFramesCount(String src, String json); + protected static native long createWithJson(String json, String name, int[] params, int[] colorReplacement); public static native void destroy(long ptr); @@ -503,6 +506,7 @@ public RLottieDrawable(File file, int w, int h, BitmapsCache.CacheOptions cacheO } else { nativePtr = create(file.getAbsolutePath(), null, w, h, metaData, precache, colorReplacement, shouldLimitFps, fitzModifier); if (nativePtr == 0) { + FileLog.d("RLottieDrawable nativePtr == 0 " + file.getAbsolutePath() + " remove file"); file.delete(); } if (shouldLimitFps && metaData[1] < 60) { @@ -540,6 +544,7 @@ public RLottieDrawable(File file, String json, int w, int h, BitmapsCache.CacheO } else { nativePtr = create(file.getAbsolutePath(), json, w, h, metaData, precache, colorReplacement, shouldLimitFps, fitzModifier); if (nativePtr == 0) { + FileLog.d("RLottieDrawable nativePtr == 0 " + file.getAbsolutePath() + " remove file"); file.delete(); } if (shouldLimitFps && metaData[1] < 60) { @@ -707,6 +712,10 @@ public RLottieDrawable(int rawRes, String name, int w, int h, boolean startDecod } } + public void multiplySpeed(float multiplier) { + timeBetweenFrames *= (1f / multiplier); + } + public static String readRes(File path, int rawRes) { int totalRead = 0; byte[] readBuffer = readBufferLocal.get(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/RLottieImageView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/RLottieImageView.java index 4b461405bd..7de61dc833 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/RLottieImageView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/RLottieImageView.java @@ -135,6 +135,7 @@ protected boolean setImageBitmapByKey(Drawable drawable, String key, int type, b return super.setImageBitmapByKey(drawable, key, type, memCache, guid); } }; + imageReceiver.setAllowLoadingOnAttachedOnly(true); if (onlyLastFrame) { imageReceiver.setImage(ImageLocation.getForDocument(document), w + "_" + h + "_lastframe", null, null, null, null, null, 0, null, document, 1); } else if ("video/webm".equals(document.mime_type)) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/RadialProgress.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/RadialProgress.java index 3bae19cd0f..e05e273b81 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/RadialProgress.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/RadialProgress.java @@ -18,6 +18,8 @@ import android.view.View; import android.view.animation.DecelerateInterpolator; +import com.google.android.exoplayer2.util.Log; + import org.telegram.messenger.AndroidUtilities; import org.telegram.ui.ActionBar.Theme; @@ -65,6 +67,26 @@ public class RadialProgress { private Canvas miniDrawCanvas; private float overrideAlpha = 1.0f; + private Paint overridePaint = null; + private boolean disableUpdate; + + public float getAnimatedProgress() { + return animatedProgressValue; + } + + public void copyParams(RadialProgress radialProgressUpload) { + currentProgress = radialProgressUpload.currentProgress; + animatedProgressValue = radialProgressUpload.animatedProgressValue; + radOffset = radialProgressUpload.radOffset; + lastUpdateTime = System.currentTimeMillis(); +// currentProgressTime = radialProgressUpload.currentProgressTime; +// animationProgressStart = radialProgressUpload.animationProgressStart; + invalidateParent(); + } + + public void disableUpdate(boolean disableUpdate) { + this.disableUpdate = disableUpdate; + } private class CheckDrawable extends Drawable { @@ -173,6 +195,9 @@ public void setAlphaForMiniPrevious(boolean value) { } private void updateAnimation(boolean progress) { + if (disableUpdate) { + return; + } long newTime = System.currentTimeMillis(); long dt = newTime - lastUpdateTime; lastUpdateTime = newTime; @@ -490,18 +515,28 @@ public void draw(Canvas canvas) { } if (currentWithRound || previousWithRound) { - progressPaint.setColor(progressColor); - if (previousWithRound) { - progressPaint.setAlpha((int) (255 * animatedAlphaValue * overrideAlpha)); + Paint finalPaint; + if (overridePaint != null) { + finalPaint = overridePaint; } else { - progressPaint.setAlpha((int) (255 * overrideAlpha)); + progressPaint.setColor(progressColor); + if (previousWithRound) { + progressPaint.setAlpha((int) (255 * animatedAlphaValue * overrideAlpha)); + } else { + progressPaint.setAlpha((int) (255 * overrideAlpha)); + } + finalPaint = progressPaint; } cicleRect.set(progressRect.left + diff, progressRect.top + diff, progressRect.right - diff, progressRect.bottom - diff); - canvas.drawArc(cicleRect, -90 + radOffset, Math.max(4, 360 * animatedProgressValue), false, progressPaint); + canvas.drawArc(cicleRect, -90 + radOffset, Math.max(4, 360 * animatedProgressValue), false, finalPaint); updateAnimation(true); } else { updateAnimation(false); } } } + + public void setPaint(Paint paint) { + overridePaint = paint; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/RadialProgress2.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/RadialProgress2.java index aaf0311da9..0a219c4f38 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/RadialProgress2.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/RadialProgress2.java @@ -45,13 +45,13 @@ public class RadialProgress2 { private int circlePressedColor; private int iconColor; private int iconPressedColor; - private String circleColorKey; - private String circleCrossfadeColorKey; + private int circleColorKey = -1; + private int circleCrossfadeColorKey = -1; private float circleCrossfadeColorProgress; private float circleCheckProgress = 1.0f; - private String circlePressedColorKey; - private String iconColorKey; - private String iconPressedColorKey; + private int circlePressedColorKey = -1; + private int iconColorKey = -1; + private int iconPressedColorKey = -1; private ImageReceiver overlayImageView; private int circleRadius; private boolean isPressed; @@ -147,30 +147,30 @@ public void onDetachedFromWindow() { overlayImageView.onDetachedFromWindow(); } + public void setColorKeys(int circle, int circlePressed, int icon, int iconPressed) { + circleColorKey = circle; + circlePressedColorKey = circlePressed; + iconColorKey = icon; + iconPressedColorKey = iconPressed; + } + public void setColors(int circle, int circlePressed, int icon, int iconPressed) { circleColor = circle; circlePressedColor = circlePressed; iconColor = icon; iconPressedColor = iconPressed; - circleColorKey = null; - circlePressedColorKey = null; - iconColorKey = null; - iconPressedColorKey = null; - } - - public void setColors(String circle, String circlePressed, String icon, String iconPressed) { - circleColorKey = circle; - circlePressedColorKey = circlePressed; - iconColorKey = icon; - iconPressedColorKey = iconPressed; + circleColorKey = -1; + circlePressedColorKey = -1; + iconColorKey = -1; + iconPressedColorKey = -1; } - public void setCircleCrossfadeColor(String color, float progress, float checkProgress) { + public void setCircleCrossfadeColor(int color, float progress, float checkProgress) { circleCrossfadeColorKey = color; circleCrossfadeColorProgress = progress; circleCheckProgress = checkProgress; miniIconScale = 1.0f; - if (color != null) { + if (color >= 0) { initMiniIcons(); } } @@ -317,25 +317,25 @@ public void draw(Canvas canvas) { wholeAlpha = currentIcon != MediaActionDrawable.ICON_NONE ? 1.0f : 1.0f - mediaActionDrawable.getTransitionProgress(); } - if (isPressedMini && circleCrossfadeColorKey == null) { - if (iconPressedColorKey != null) { + if (isPressedMini && circleCrossfadeColorKey < 0) { + if (iconPressedColorKey >= 0) { miniMediaActionDrawable.setColor(getThemedColor(iconPressedColorKey)); } else { miniMediaActionDrawable.setColor(iconPressedColor); } - if (circlePressedColorKey != null) { + if (circlePressedColorKey >= 0) { circleMiniPaint.setColor(getThemedColor(circlePressedColorKey)); } else { circleMiniPaint.setColor(circlePressedColor); } } else { - if (iconColorKey != null) { + if (iconColorKey >= 0) { miniMediaActionDrawable.setColor(getThemedColor(iconColorKey)); } else { miniMediaActionDrawable.setColor(iconColor); } - if (circleColorKey != null) { - if (circleCrossfadeColorKey != null) { + if (circleColorKey >= 0) { + if (circleCrossfadeColorKey >= 0) { circleMiniPaint.setColor(AndroidUtilities.getOffsetColor(getThemedColor(circleColorKey), getThemedColor(circleCrossfadeColorKey), circleCrossfadeColorProgress, circleCheckProgress)); } else { circleMiniPaint.setColor(getThemedColor(circleColorKey)); @@ -347,33 +347,33 @@ public void draw(Canvas canvas) { int color; if (isPressed) { - if (iconPressedColorKey != null) { + if (iconPressedColorKey >= 0) { mediaActionDrawable.setColor(color = getThemedColor(iconPressedColorKey)); mediaActionDrawable.setBackColor(getThemedColor(circlePressedColorKey)); } else { mediaActionDrawable.setColor(color = iconPressedColor); mediaActionDrawable.setBackColor(circlePressedColor); } - if (circlePressedColorKey != null) { + if (circlePressedColorKey >= 0) { circlePaint.setColor(getThemedColor(circlePressedColorKey)); } else { circlePaint.setColor(circlePressedColor); } } else { - if (iconColorKey != null) { + if (iconColorKey >= 0) { mediaActionDrawable.setColor(color = getThemedColor(iconColorKey)); mediaActionDrawable.setBackColor(getThemedColor(circleColorKey)); } else { mediaActionDrawable.setColor(color = iconColor); mediaActionDrawable.setBackColor(circleColor); } - if (circleColorKey != null) { + if (circleColorKey >= 0) { circlePaint.setColor(getThemedColor(circleColorKey)); } else { circlePaint.setColor(circleColor); } } - if ((drawMiniIcon || circleCrossfadeColorKey != null) && miniDrawCanvas != null) { + if ((drawMiniIcon || circleCrossfadeColorKey >= 0) && miniDrawCanvas != null) { miniDrawBitmap.eraseColor(0); } @@ -386,7 +386,7 @@ public void draw(Canvas canvas) { float scale = 1f; int centerX; int centerY; - if ((drawMiniIcon || circleCrossfadeColorKey != null) && miniDrawCanvas != null) { + if ((drawMiniIcon || circleCrossfadeColorKey >= 0) && miniDrawCanvas != null) { centerX = (int) Math.ceil(progressRect.width() / 2); centerY = (int) Math.ceil(progressRect.height() / 2); } else { @@ -419,13 +419,13 @@ public void draw(Canvas canvas) { } int restore = Integer.MIN_VALUE; - if (miniDrawCanvas != null && circleCrossfadeColorKey != null && circleCheckProgress != 1.0f) { + if (miniDrawCanvas != null && circleCrossfadeColorKey >= 0 && circleCheckProgress != 1.0f) { restore = miniDrawCanvas.save(); float scaleMini = 1.0f - 0.1f * (1.0f - circleCheckProgress); miniDrawCanvas.scale(scaleMini, scaleMini, centerX, centerY); } if (drawCircle && drawBackground) { - if ((drawMiniIcon || circleCrossfadeColorKey != null) && miniDrawCanvas != null) { + if ((drawMiniIcon || circleCrossfadeColorKey >= 0) && miniDrawCanvas != null) { miniDrawCanvas.drawCircle(centerX, centerY, circleRadius, circlePaint); } else { if (currentIcon != MediaActionDrawable.ICON_NONE || wholeAlpha != 0) { @@ -440,7 +440,7 @@ public void draw(Canvas canvas) { if (overlayImageView.hasBitmapImage()) { overlayImageView.setAlpha(wholeAlpha * overrideAlpha * overlayImageAlpha); - if ((drawMiniIcon || circleCrossfadeColorKey != null) && miniDrawCanvas != null) { + if ((drawMiniIcon || circleCrossfadeColorKey >= 0) && miniDrawCanvas != null) { overlayImageView.draw(miniDrawCanvas); miniDrawCanvas.drawCircle(centerX, centerY, circleRadius, overlayPaint); } else { @@ -454,7 +454,7 @@ public void draw(Canvas canvas) { } mediaActionDrawable.setBounds(centerX - iconSize, centerY - iconSize, centerX + iconSize, centerY + iconSize); mediaActionDrawable.setHasOverlayImage(overlayImageView.hasBitmapImage()); - if ((drawMiniIcon || circleCrossfadeColorKey != null)) { + if ((drawMiniIcon || circleCrossfadeColorKey >= 0)) { if (miniDrawCanvas != null) { mediaActionDrawable.draw(miniDrawCanvas); } else { @@ -468,7 +468,7 @@ public void draw(Canvas canvas) { miniDrawCanvas.restoreToCount(restore); } - if ((drawMiniIcon || circleCrossfadeColorKey != null)) { + if ((drawMiniIcon || circleCrossfadeColorKey >= 0)) { int offset; int size; float cx; @@ -524,13 +524,12 @@ public void draw(Canvas canvas) { } } - public String getCircleColorKey() { + public int getCircleColorKey() { return circleColorKey; } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } public void setMaxIconSize(int maxSize) { @@ -540,4 +539,8 @@ public void setMaxIconSize(int maxSize) { public void setOverlayImageAlpha(float overlayImageAlpha) { this.overlayImageAlpha = overlayImageAlpha; } + + public float getTransitionProgress() { + return drawMiniIcon ? miniMediaActionDrawable.getTransitionProgress() : mediaActionDrawable.getTransitionProgress(); + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/RadialProgressView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/RadialProgressView.java index 6ee4f3e001..fdc7cbb2f0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/RadialProgressView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/RadialProgressView.java @@ -242,8 +242,7 @@ public boolean isCircle() { return Math.abs(drawingCircleLenght) >= 360; } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ReactedHeaderView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ReactedHeaderView.java index b15634d0e4..aaf07ae7dc 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ReactedHeaderView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ReactedHeaderView.java @@ -5,7 +5,6 @@ import android.graphics.PorterDuffColorFilter; import android.graphics.drawable.Drawable; import android.text.TextUtils; -import android.util.Pair; import android.util.TypedValue; import android.view.Gravity; import android.view.View; @@ -26,6 +25,7 @@ import org.telegram.messenger.MessagesController; import org.telegram.messenger.R; import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.Theme; @@ -58,7 +58,7 @@ public ReactedHeaderView(@NonNull Context context, int currentAccount, MessageOb this.dialogId = dialogId; flickerLoadingView = new FlickerLoadingView(context); - flickerLoadingView.setColors(Theme.key_actionBarDefaultSubmenuBackground, Theme.key_listSelector, null); + flickerLoadingView.setColors(Theme.key_actionBarDefaultSubmenuBackground, Theme.key_listSelector, -1); flickerLoadingView.setViewType(FlickerLoadingView.MESSAGE_SEEN_TYPE); flickerLoadingView.setIsSingleCell(false); addView(flickerLoadingView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT)); @@ -96,15 +96,22 @@ public void setSeenCallback(Consumer> seenCallback) { } public static class UserSeen { - public TLRPC.User user; + public TLObject user; + long dialogId; public int date = 0; public UserSeen(TLRPC.User user) { this.user = user; + dialogId = user.id; } - public UserSeen(TLRPC.User user, int date) { + public UserSeen(TLObject user, int date) { this.user = user; this.date = date; + if (user instanceof TLRPC.User) { + dialogId = ((TLRPC.User) user).id; + } else if (user instanceof TLRPC.Chat) { + dialogId = -((TLRPC.Chat) user).id; + } } } @@ -153,7 +160,7 @@ protected void onAttachedToWindow() { for (UserSeen p : usersRes) { boolean hasSame = false; for (int i = 0; i < users.size(); i++) { - if (users.get(i).user.id == p.user.id) { + if (MessageObject.getObjectPeerId(users.get(i).user) == MessageObject.getObjectPeerId(p.user)) { hasSame = true; if (p.date > 0) { users.get(i).date = p.date; @@ -268,7 +275,21 @@ private void loadReactions() { if (message.messageOwner.from_id != null && u.id != message.messageOwner.from_id.user_id) { boolean hasSame = false; for (int i = 0; i < users.size(); i++) { - if (users.get(i).user.id == u.id) { + if (users.get(i).dialogId == u.id) { + hasSame = true; + break; + } + } + if (!hasSame) { + users.add(new UserSeen(u, 0)); + } + } + } + for (TLRPC.Chat u : list.chats) { + if (message.messageOwner.from_id != null && u.id != message.messageOwner.from_id.user_id) { + boolean hasSame = false; + for (int i = 0; i < users.size(); i++) { + if (users.get(i).dialogId == -u.id) { hasSame = true; break; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ReactedUsersListView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ReactedUsersListView.java index 72035ab066..9e18824f4e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ReactedUsersListView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ReactedUsersListView.java @@ -6,16 +6,10 @@ import android.annotation.SuppressLint; import android.content.Context; import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; import android.os.Build; -import android.text.TextUtils; -import android.util.Log; import android.util.LongSparseArray; -import android.util.Pair; -import android.view.Gravity; import android.view.View; import android.view.ViewGroup; -import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; import android.widget.LinearLayout; @@ -24,21 +18,14 @@ import androidx.recyclerview.widget.RecyclerView; import org.telegram.messenger.AndroidUtilities; -import org.telegram.messenger.DocumentObject; -import org.telegram.messenger.Emoji; -import org.telegram.messenger.ImageLocation; -import org.telegram.messenger.LocaleController; -import org.telegram.messenger.MediaDataController; import org.telegram.messenger.MessageObject; import org.telegram.messenger.MessagesController; import org.telegram.messenger.NotificationCenter; -import org.telegram.messenger.R; -import org.telegram.messenger.SvgHelper; -import org.telegram.messenger.UserObject; import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; -import org.telegram.ui.ActionBar.SimpleTextView; import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Cells.ReactedUserHolderView; import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble; import java.util.ArrayList; @@ -113,7 +100,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int View view = null; switch (viewType) { case USER_VIEW_TYPE: - view = new ReactedUserHolderView(currentAccount, context); + view = new ReactedUserHolderView(ReactedUserHolderView.STYLE_DEFAULT, currentAccount, context, null); break; default: case CUSTOM_EMOJI_VIEW_TYPE: @@ -188,7 +175,7 @@ public int getAdditionalHeight() { return !customReactionsEmoji.isEmpty() && messageContainsEmojiButton != null ? messageContainsEmojiButton.getMeasuredHeight() + AndroidUtilities.dp(8) : 0; } }; - loadingView.setColors(Theme.key_actionBarDefaultSubmenuBackground, Theme.key_listSelector, null); + loadingView.setColors(Theme.key_actionBarDefaultSubmenuBackground, Theme.key_listSelector, -1); loadingView.setIsSingleCell(true); loadingView.setItemsCount(predictiveCount); @@ -205,11 +192,11 @@ public int getAdditionalHeight() { public ReactedUsersListView setSeenUsers(List users) { if (userReactions != null && !userReactions.isEmpty()) { for (ReactedHeaderView.UserSeen p : users) { - TLRPC.User user = p.user; + TLObject user = p.user; if (user != null && p.date > 0) { for (int i = 0; i < userReactions.size(); ++i) { TLRPC.MessagePeerReaction react = userReactions.get(i); - if (react != null && react.date <= 0 && react.peer_id.user_id == user.id) { + if (react != null && react.date <= 0 && MessageObject.getPeerId(react.peer_id) == p.dialogId) { react.date = p.date; react.dateIsSeen = true; break; @@ -220,14 +207,19 @@ public ReactedUsersListView setSeenUsers(List users) } List nr = new ArrayList<>(users.size()); for (ReactedHeaderView.UserSeen p : users) { - ArrayList userReactions = peerReactionMap.get(p.user.id); + ArrayList userReactions = peerReactionMap.get(p.dialogId); if (userReactions != null) { continue; } TLRPC.TL_messagePeerReaction r = new TLRPC.TL_messagePeerReaction(); r.reaction = null; - r.peer_id = new TLRPC.TL_peerUser(); - r.peer_id.user_id = p.user.id; + if (p.user instanceof TLRPC.User) { + r.peer_id = new TLRPC.TL_peerUser(); + r.peer_id.user_id = ((TLRPC.User) p.user).id; + } else if (p.user instanceof TLRPC.Chat) { + r.peer_id = new TLRPC.TL_peerChat(); + r.peer_id.chat_id = ((TLRPC.Chat) p.user).id; + } r.date = p.date; r.dateIsSeen = true; userReactions = new ArrayList<>(); @@ -386,167 +378,6 @@ private int getLoadCount() { return filter == null ? 100 : 50; } - private static final class ReactedUserHolderView extends FrameLayout { - int currentAccount; - - BackupImageView avatarView; - SimpleTextView titleView; - SimpleTextView subtitleView; - BackupImageView reactView; - AvatarDrawable avatarDrawable = new AvatarDrawable(); - View overlaySelectorView; - AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable rightDrawable; - - private static final MessageSeenCheckDrawable seenDrawable = new MessageSeenCheckDrawable(R.drawable.msg_mini_checks, Theme.key_windowBackgroundWhiteGrayText); - private static final MessageSeenCheckDrawable reactDrawable = new MessageSeenCheckDrawable(R.drawable.msg_reactions, Theme.key_windowBackgroundWhiteGrayText, 16, 16, 5.66f); - - ReactedUserHolderView(int currentAccount, @NonNull Context context) { - super(context); - this.currentAccount = currentAccount; - setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, AndroidUtilities.dp(ITEM_HEIGHT_DP))); - - avatarView = new BackupImageView(context); - avatarView.setRoundRadius(AndroidUtilities.dp(34)); - addView(avatarView, LayoutHelper.createFrameRelatively(34, 34, Gravity.START | Gravity.CENTER_VERTICAL, 10, 0, 0, 0)); - - titleView = new SimpleTextView(context) { - @Override - public boolean setText(CharSequence value) { - value = Emoji.replaceEmoji(value, getPaint().getFontMetricsInt(), AndroidUtilities.dp(14), false); - return super.setText(value); - } - }; - NotificationCenter.listenEmojiLoading(titleView); - titleView.setTextSize(16); - titleView.setTextColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuItem)); - titleView.setEllipsizeByGradient(true); - titleView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); - titleView.setRightPadding(AndroidUtilities.dp(30)); - titleView.setTranslationX(LocaleController.isRTL ? AndroidUtilities.dp(30) : 0); - titleView.setRightDrawableOutside(true); - addView(titleView, LayoutHelper.createFrameRelatively(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.FILL_HORIZONTAL | Gravity.TOP, 55, 5.33f, 12, 0)); - - rightDrawable = new AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable(this, AndroidUtilities.dp(18)); - titleView.setDrawablePadding(AndroidUtilities.dp(3)); - titleView.setRightDrawable(rightDrawable); - - subtitleView = new SimpleTextView(context); - subtitleView.setTextSize(13); - subtitleView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText)); - subtitleView.setEllipsizeByGradient(true); - subtitleView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); - subtitleView.setTranslationX(LocaleController.isRTL ? AndroidUtilities.dp(30) : 0); - addView(subtitleView, LayoutHelper.createFrameRelatively(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.FILL_HORIZONTAL | Gravity.TOP, 55, 19f, 20, 0)); - - reactView = new BackupImageView(context); - addView(reactView, LayoutHelper.createFrameRelatively(24, 24, Gravity.END | Gravity.CENTER_VERTICAL, 0, 0, 12, 0)); - - overlaySelectorView = new View(context); - overlaySelectorView.setBackground(Theme.getSelectorDrawable(false)); - addView(overlaySelectorView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); - } - - void setUserReaction(TLRPC.MessagePeerReaction reaction) { - if (reaction == null) { - return; - } - - TLRPC.User u = MessagesController.getInstance(currentAccount).getUser(MessageObject.getPeerId(reaction.peer_id)); - if (u == null) { - return; - } - - Long documentId = UserObject.getEmojiStatusDocumentId(u); - if (documentId == null) { - rightDrawable.set((Drawable) null, false); - } else { - rightDrawable.set(documentId, false); - } - - avatarDrawable.setInfo(u); - titleView.setText(UserObject.getUserName(u)); - - Drawable thumb = avatarDrawable; - if (u.photo != null && u.photo.strippedBitmap != null) { - thumb = u.photo.strippedBitmap; - } - avatarView.setImage(ImageLocation.getForUser(u, ImageLocation.TYPE_SMALL), "50_50", thumb, u); - - String contentDescription; - boolean hasReactImage = false; - if (reaction.reaction != null) { - ReactionsLayoutInBubble.VisibleReaction visibleReaction = ReactionsLayoutInBubble.VisibleReaction.fromTLReaction(reaction.reaction); - if (visibleReaction.emojicon != null) { - TLRPC.TL_availableReaction r = MediaDataController.getInstance(currentAccount).getReactionsMap().get(visibleReaction.emojicon); - if (r != null) { - SvgHelper.SvgDrawable svgThumb = DocumentObject.getSvgThumb(r.static_icon.thumbs, Theme.key_windowBackgroundGray, 1.0f); - reactView.setImage(ImageLocation.getForDocument(r.center_icon), "40_40_lastreactframe", "webp", svgThumb, r); - hasReactImage = true; - } else { - reactView.setImageDrawable(null); - } - } else { - AnimatedEmojiDrawable drawable = new AnimatedEmojiDrawable(AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES, currentAccount, visibleReaction.documentId); - drawable.setColorFilter(Theme.chat_animatedEmojiTextColorFilter); - reactView.setAnimatedEmojiDrawable(drawable); - hasReactImage = true; - } - contentDescription = LocaleController.formatString("AccDescrReactedWith", R.string.AccDescrReactedWith, UserObject.getUserName(u), visibleReaction.emojicon != null ? visibleReaction.emojicon : reaction.reaction); - } else { - reactView.setImageDrawable(null); - contentDescription = LocaleController.formatString("AccDescrPersonHasSeen", R.string.AccDescrPersonHasSeen, UserObject.getUserName(u)); - } - - if (reaction.date != 0) { - contentDescription += " " + LocaleController.formatSeenDate(reaction.date); - } - setContentDescription(contentDescription); - - if (reaction.date != 0) { - subtitleView.setVisibility(View.VISIBLE); - CharSequence icon = reaction.dateIsSeen ? seenDrawable.getSpanned(getContext()) : reactDrawable.getSpanned(getContext()); - subtitleView.setText(TextUtils.concat(icon, LocaleController.formatSeenDate(reaction.date))); - subtitleView.setTranslationY(!reaction.dateIsSeen ? AndroidUtilities.dp(-1) : 0); - titleView.setTranslationY(0); - } else { - subtitleView.setVisibility(View.GONE); - titleView.setTranslationY(AndroidUtilities.dp(9)); - } - - titleView.setRightPadding(AndroidUtilities.dp(hasReactImage ? 30 : 0)); - titleView.setTranslationX(hasReactImage && LocaleController.isRTL ? AndroidUtilities.dp(30) : 0); - ((MarginLayoutParams) subtitleView.getLayoutParams()).rightMargin = AndroidUtilities.dp(hasReactImage && !LocaleController.isRTL ? 12 + 24 : 12); - subtitleView.setTranslationX(hasReactImage && LocaleController.isRTL ? AndroidUtilities.dp(30) : 0); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(ITEM_HEIGHT_DP), MeasureSpec.EXACTLY)); - } - - @Override - public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { - super.onInitializeAccessibilityNodeInfo(info); - info.setEnabled(true); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - if (rightDrawable != null) { - rightDrawable.attach(); - } - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - if (rightDrawable != null) { - rightDrawable.detach(); - } - } - } - public ReactedUsersListView setOnProfileSelectedListener(OnProfileSelectedListener onProfileSelectedListener) { this.onProfileSelectedListener = onProfileSelectedListener; return this; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/ChatSelectionReactionMenuOverlay.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/ChatSelectionReactionMenuOverlay.java index 9926104d16..6024ed7d52 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/ChatSelectionReactionMenuOverlay.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/ChatSelectionReactionMenuOverlay.java @@ -66,7 +66,7 @@ public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { private void checkCreateReactionsLayout() { if (reactionsContainerLayout == null) { - reactionsContainerLayout = new ReactionsContainerLayout(parentFragment, getContext(), parentFragment.getCurrentAccount(), parentFragment.getResourceProvider()) { + reactionsContainerLayout = new ReactionsContainerLayout(ReactionsContainerLayout.TYPE_DEFAULT, parentFragment, getContext(), parentFragment.getCurrentAccount(), parentFragment.getResourceProvider()) { float enabledAlpha = 1f; long lastUpdate; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/CustomEmojiReactionsWindow.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/CustomEmojiReactionsWindow.java index 954eb7cab4..e52e253b2f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/CustomEmojiReactionsWindow.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/CustomEmojiReactionsWindow.java @@ -1,10 +1,13 @@ package org.telegram.ui.Components.Reactions; +import static org.telegram.ui.Components.ReactionsContainerLayout.TYPE_STORY; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Canvas; +import android.graphics.Outline; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PixelFormat; @@ -13,10 +16,12 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; +import android.os.Build; import android.view.Gravity; import android.view.HapticFeedbackConstants; import android.view.KeyEvent; import android.view.View; +import android.view.ViewOutlineProvider; import android.view.WindowManager; import android.view.animation.OvershootInterpolator; import android.widget.FrameLayout; @@ -25,6 +30,7 @@ import androidx.core.content.ContextCompat; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.ImageReceiver; import org.telegram.messenger.LiteMode; import org.telegram.messenger.LocaleController; @@ -40,9 +46,12 @@ import org.telegram.ui.Components.Bulletin; import org.telegram.ui.Components.BulletinFactory; import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.EmojiTabsStrip; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.Premium.PremiumFeatureBottomSheet; import org.telegram.ui.Components.ReactionsContainerLayout; +import org.telegram.ui.Components.StableAnimator; +import org.telegram.ui.LaunchActivity; import org.telegram.ui.PremiumPreviewFragment; import org.telegram.ui.SelectAnimatedEmojiDialog; @@ -66,7 +75,7 @@ public class CustomEmojiReactionsWindow { SelectAnimatedEmojiDialog selectAnimatedEmojiDialog; ReactionsContainerLayout reactionsContainerLayout; - Path pathToClip = new Path(); + private final Path pathToClipApi20 = new Path(); private boolean invalidatePath; List reactions; @@ -82,12 +91,14 @@ public class CustomEmojiReactionsWindow { private int account; private boolean cascadeAnimation; private ValueAnimator valueAnimator; + private final int type; - public CustomEmojiReactionsWindow(BaseFragment baseFragment, List reactions, HashSet selectedReactions, ReactionsContainerLayout reactionsContainerLayout, Theme.ResourcesProvider resourcesProvider) { + public CustomEmojiReactionsWindow(int type, BaseFragment baseFragment, List reactions, HashSet selectedReactions, ReactionsContainerLayout reactionsContainerLayout, Theme.ResourcesProvider resourcesProvider) { + this.type = type; this.reactions = reactions; this.baseFragment = baseFragment; this.resourcesProvider = resourcesProvider; - Context context = baseFragment.getContext(); + Context context = baseFragment != null ? baseFragment.getContext() : reactionsContainerLayout.getContext(); windowView = new FrameLayout(context) { @Override public boolean dispatchKeyEvent(KeyEvent event) { @@ -114,6 +125,24 @@ protected boolean fitSystemWindows(Rect insets) { return super.fitSystemWindows(insets); } + Bulletin.Delegate bulletinDelegate = new Bulletin.Delegate() { + @Override + public int getBottomOffset(int tag) { + return (int) keyboardHeight; + } + }; + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + Bulletin.addDelegate(this, bulletinDelegate); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + Bulletin.removeDelegate(this); + } }; windowView.setOnClickListener(v -> { if (enterTransitionFinished) { @@ -124,7 +153,16 @@ protected boolean fitSystemWindows(Rect insets) { // sizeNotifierFrameLayout.setFitsSystemWindows(true); containerView = new ContainerView(context); - selectAnimatedEmojiDialog = new SelectAnimatedEmojiDialog(baseFragment, context, false, null, SelectAnimatedEmojiDialog.TYPE_REACTIONS, resourcesProvider) { + selectAnimatedEmojiDialog = new SelectAnimatedEmojiDialog(baseFragment, context, false, null, SelectAnimatedEmojiDialog.TYPE_REACTIONS, type != TYPE_STORY, resourcesProvider, 16) { + + @Override + public boolean prevWindowKeyboardVisible() { + if (reactionsContainerLayout.getDelegate() != null) { + return reactionsContainerLayout.getDelegate().needEnterText(); + } + return false; + } + @Override protected void onInputFocus() { if (!wasFocused) { @@ -133,6 +171,9 @@ protected void onInputFocus() { if (baseFragment instanceof ChatActivity) { ((ChatActivity) baseFragment).needEnterText(); } + if (reactionsContainerLayout.getDelegate() != null) { + reactionsContainerLayout.getDelegate().needEnterText(); + } } } @@ -163,6 +204,24 @@ protected void invalidateParent() { containerView.invalidate(); } }; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + selectAnimatedEmojiDialog.setOutlineProvider(new ViewOutlineProvider() { + final Rect rect = new Rect(); + final RectF rectTmp = new RectF(); + final RectF rectF = new RectF(); + + @Override + public void getOutline(View view, Outline outline) { + float radius = AndroidUtilities.lerp(fromRadius, AndroidUtilities.dp(8), enterTransitionProgress); + rectTmp.set(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight()); + AndroidUtilities.lerp(fromRect, rectTmp, enterTransitionProgress, rectF); + rectF.round(rect); + outline.setRoundRect(rect, radius); + } + }); + selectAnimatedEmojiDialog.setClipToOutline(true); + } + selectAnimatedEmojiDialog.setOnLongPressedListener(new SelectAnimatedEmojiDialog.onLongPressedListener() { @Override public void onLongPressed(SelectAnimatedEmojiDialog.ImageViewEmoji view) { @@ -186,12 +245,19 @@ public void onRecentCleared() { containerView.addView(selectAnimatedEmojiDialog, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, 0, 0, 0)); windowView.addView(containerView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP, 16, 16, 16, 16)); windowView.setClipChildren(false); + if (type == TYPE_STORY) { + selectAnimatedEmojiDialog.setBackgroundDelegate((canvas, left, top, right, bottom, x, y) -> { + AndroidUtilities.rectTmp.set(left, top, right, bottom); + reactionsContainerLayout.getDelegate().drawRoundRect(canvas, AndroidUtilities.rectTmp, 0, containerView.getX() + x, containerView.getY() - AndroidUtilities.statusBarHeight + y); + }); + } WindowManager.LayoutParams lp = createLayoutParams(false); - windowManager = baseFragment.getParentActivity().getWindowManager(); + windowManager = AndroidUtilities.findActivity(context).getWindowManager(); windowManager.addView(windowView, lp); this.reactionsContainerLayout = reactionsContainerLayout; + reactionsContainerLayout.setOnSwitchedToLoopView(() -> containerView.invalidate()); //fixed emoji freeze reactionsContainerLayout.prepareAnimation(true); AndroidUtilities.runOnUIThread(() -> { isShowing = true; @@ -207,19 +273,25 @@ private void updateWindowPosition() { return; } float y = yTranslation; - if (y + containerView.getMeasuredHeight() > windowView.getMeasuredHeight() - keyboardHeight - AndroidUtilities.dp(32)) { - y = windowView.getMeasuredHeight() - keyboardHeight - containerView.getMeasuredHeight() - AndroidUtilities.dp(32); + int bottomOffset = AndroidUtilities.dp(32); + if (type == TYPE_STORY) { + bottomOffset = AndroidUtilities.dp(24); + } + if (y + containerView.getMeasuredHeight() > windowView.getMeasuredHeight() - keyboardHeight - bottomOffset) { + y = windowView.getMeasuredHeight() - keyboardHeight - containerView.getMeasuredHeight() - bottomOffset; } if (y < 0) { y = 0; } - containerView.animate().translationY(y).setDuration(250).setInterpolator(CubicBezierInterpolator.DEFAULT).start(); + containerView.animate().translationY(y).setDuration(250).setUpdateListener(animation -> { + containerView.invalidate(); + }).setInterpolator(CubicBezierInterpolator.DEFAULT).start(); } private WindowManager.LayoutParams createLayoutParams(boolean focusable) { WindowManager.LayoutParams lp = new WindowManager.LayoutParams(); lp.width = lp.height = WindowManager.LayoutParams.MATCH_PARENT; - lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; + lp.type = type == ReactionsContainerLayout.TYPE_DEFAULT ? WindowManager.LayoutParams.TYPE_APPLICATION_PANEL : WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; if (focusable) { lp.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; @@ -233,11 +305,16 @@ private WindowManager.LayoutParams createLayoutParams(boolean focusable) { private void showUnlockPremiumAlert() { if (baseFragment instanceof ChatActivity) { baseFragment.showDialog(new PremiumFeatureBottomSheet(baseFragment, PremiumPreviewFragment.PREMIUM_FEATURE_ANIMATED_EMOJI, false)); + } else { + BaseFragment fragment = LaunchActivity.getLastFragment(); + if (fragment != null) { + fragment.showDialog(new PremiumFeatureBottomSheet(baseFragment, PremiumPreviewFragment.PREMIUM_FEATURE_ANIMATED_EMOJI, false)); + } } } int[] location = new int[2]; - int animationIndex; + final AnimationNotificationsLocker notificationsLocker = new AnimationNotificationsLocker(); private void createTransition(boolean enter) { fromRect.set(reactionsContainerLayout.rect); @@ -256,7 +333,12 @@ private void createTransition(boolean enter) { y = AndroidUtilities.dp(16); } - containerView.setTranslationX(location[0] - windowLocation[0] - AndroidUtilities.dp(2)); + if (type == TYPE_STORY) { + containerView.setTranslationX((windowView.getMeasuredWidth() - containerView.getMeasuredWidth()) / 2f - AndroidUtilities.dp(16)); + } else { + containerView.setTranslationX(location[0] - windowLocation[0] - AndroidUtilities.dp(2)); + } + if (!enter) { yTranslation = containerView.getTranslationY(); } else { @@ -275,50 +357,57 @@ private void createTransition(boolean enter) { cascadeAnimation = false; } if (cascadeAnimation) { - updateCascadeEnter(0); + updateCascadeEnter(0, true); } + updateContainersAlpha(); selectAnimatedEmojiDialog.setEnterAnimationInProgress(true); + selectAnimatedEmojiDialog.emojiTabs.showRecentTabStub(enter && cascadeAnimation); account = UserConfig.selectedAccount; - animationIndex = NotificationCenter.getInstance(account).setAnimationInProgress(animationIndex, null); - valueAnimator = ValueAnimator.ofFloat(enterTransitionProgress, enter ? 1f : 0); + notificationsLocker.lock(); + valueAnimator = StableAnimator.ofFloat(enterTransitionProgress, enter ? 1f : 0); valueAnimator.addUpdateListener(animation -> { valueAnimator = null; enterTransitionProgress = (float) animation.getAnimatedValue(); - reactionsContainerLayout.setCustomEmojiEnterProgress(Utilities.clamp(enterTransitionProgress,1f, 0)); + updateContainersAlpha(); + updateContentPosition(); + reactionsContainerLayout.setCustomEmojiEnterProgress(Utilities.clamp(enterTransitionProgress, 1f, 0)); invalidatePath = true; containerView.invalidate(); - + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + selectAnimatedEmojiDialog.invalidateOutline(); + } if (cascadeAnimation) { - updateCascadeEnter(enterTransitionProgress); + updateCascadeEnter(enterTransitionProgress, enter); } }); if (!enter) { - syncReactionFrames(enter); + syncReactionFrames(); } valueAnimator.addListener(new AnimatorListenerAdapter() { + @Override public void onAnimationEnd(Animator animation) { - checkAnimationEnd(); + updateContainersAlpha(); + updateContentPosition(); + checkAnimationEnd(enter); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + selectAnimatedEmojiDialog.invalidateOutline(); + } enterTransitionProgress = enter ? 1f : 0f; if (enter) { enterTransitionFinished = true; - selectAnimatedEmojiDialog.resetBackgroundBitmaps(); - reactionsContainerLayout.onCustomEmojiWindowOpened(); containerView.invalidate(); } reactionsContainerLayout.setCustomEmojiEnterProgress(Utilities.clamp(enterTransitionProgress, 1f, 0f)); - if (enter) { - syncReactionFrames(enter); - } if (!enter) { reactionsContainerLayout.setSkipDraw(false); - } - if (!enter) { removeView(); + Runtime.getRuntime().gc(); //to prevent garbage collection when reopening + reactionsContainerLayout.setCustomEmojiReactionsBackground(true); } } }); - valueAnimator.setStartDelay(30); + if (cascadeAnimation) { valueAnimator.setDuration(450); valueAnimator.setInterpolator(new OvershootInterpolator(0.5f)); @@ -326,15 +415,68 @@ public void onAnimationEnd(Animator animation) { valueAnimator.setDuration(350); valueAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); } - valueAnimator.start(); containerView.invalidate(); + switchLayerType(true); + if (!enter) { + reactionsContainerLayout.isHiddenNextReaction = true; + reactionsContainerLayout.invalidate(); + valueAnimator.setStartDelay(30); + valueAnimator.start(); + } else { + reactionsContainerLayout.setCustomEmojiReactionsBackground(false); + final ValueAnimator finalAnimator = valueAnimator; + HwEmojis.prepare(finalAnimator::start, cascadeAnimation); + } + HwEmojis.enableHw(); + } + + private void updateContainersAlpha() { + if (!cascadeAnimation) { + selectAnimatedEmojiDialog.searchBox.setAlpha(enterTransitionProgress); + selectAnimatedEmojiDialog.emojiGridView.setAlpha(enterTransitionProgress); + selectAnimatedEmojiDialog.emojiSearchGridView.setAlpha(enterTransitionProgress); + selectAnimatedEmojiDialog.emojiTabs.setAlpha(enterTransitionProgress); + selectAnimatedEmojiDialog.emojiTabsShadow.setAlpha(enterTransitionProgress); + } + } + + private void updateContentPosition() { + selectAnimatedEmojiDialog.contentView.setTranslationX(cascadeAnimation ? 0 : containerView.enterTransitionOffsetX); + selectAnimatedEmojiDialog.contentView.setTranslationY(containerView.enterTransitionOffsetY); + selectAnimatedEmojiDialog.contentView.setPivotX(containerView.enterTransitionScalePx); + selectAnimatedEmojiDialog.contentView.setPivotY(containerView.enterTransitionScalePy); + selectAnimatedEmojiDialog.contentView.setScaleX(containerView.enterTransitionScale); + selectAnimatedEmojiDialog.contentView.setScaleY(containerView.enterTransitionScale); + } + + private void switchLayerType(boolean hardware) { + int layerType = hardware ? View.LAYER_TYPE_HARDWARE : View.LAYER_TYPE_NONE; + selectAnimatedEmojiDialog.emojiGridView.setLayerType(layerType, null); + selectAnimatedEmojiDialog.searchBox.setLayerType(layerType, null); + if (cascadeAnimation) { + for (int i = 0; i < Math.min(selectAnimatedEmojiDialog.emojiTabs.contentView.getChildCount(), 16); i++) { + View child = selectAnimatedEmojiDialog.emojiTabs.contentView.getChildAt(i); + child.setLayerType(layerType, null); + } + } else { + selectAnimatedEmojiDialog.emojiTabsShadow.setLayerType(layerType, null); + selectAnimatedEmojiDialog.emojiTabs.setLayerType(layerType, null); + } } HashSet animatingEnterChild = new HashSet<>(); ArrayList animators = new ArrayList<>(); - private void updateCascadeEnter(float progress) { - int fullHeight = selectAnimatedEmojiDialog.contentView.getHeight(); + private void setScaleForChild(View child, float value) { + if (child instanceof SelectAnimatedEmojiDialog.ImageViewEmoji) { + ((SelectAnimatedEmojiDialog.ImageViewEmoji) child).setAnimatedScale(value); + } else if (child instanceof EmojiTabsStrip.EmojiTabButton) { + child.setScaleX(value); + child.setScaleY(value); + } + } + + private void updateCascadeEnter(float progress, boolean enter) { int parentTop = (int) (selectAnimatedEmojiDialog.getY() + selectAnimatedEmojiDialog.contentView.getY() + selectAnimatedEmojiDialog.emojiGridView.getY()); ArrayList animatedViews = null; boolean updated = false; @@ -351,8 +493,7 @@ private void updateCascadeEnter(float progress) { animatedViews.add(child); animatingEnterChild.add(child); } else { - child.setScaleX(0f); - child.setScaleY(0f); + setScaleForChild(child, 0f); updated = true; } } @@ -370,15 +511,12 @@ private void updateCascadeEnter(float progress) { animatedViews.add(child); animatingEnterChild.add(child); } else { - child.setScaleX(0f); - child.setScaleY(0f); + setScaleForChild(child, 0f); updated = true; } } if (updated) { - selectAnimatedEmojiDialog.emojiGridView.invalidate(); - selectAnimatedEmojiDialog.contentView.invalidate(); - selectAnimatedEmojiDialog.emojiTabs.contentView.invalidate(); + selectAnimatedEmojiDialog.emojiGridViewContainer.invalidate(); } if (animatedViews != null) { ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1f); @@ -386,12 +524,10 @@ private void updateCascadeEnter(float progress) { valueAnimator.addUpdateListener(animation -> { float s = (float) animation.getAnimatedValue(); for (int i = 0; i < finalAnimatedViews.size(); i++) { - finalAnimatedViews.get(i).setScaleX(s); - finalAnimatedViews.get(i).setScaleY(s); + View v = finalAnimatedViews.get(i); + setScaleForChild(v, s); } - selectAnimatedEmojiDialog.emojiGridView.invalidate(); - selectAnimatedEmojiDialog.contentView.invalidate(); - selectAnimatedEmojiDialog.emojiTabs.contentView.invalidate(); + selectAnimatedEmojiDialog.emojiGridViewContainer.invalidate(); }); animators.add(valueAnimator); valueAnimator.addListener(new AnimatorListenerAdapter() { @@ -399,7 +535,7 @@ private void updateCascadeEnter(float progress) { public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); animators.remove(valueAnimator); - checkAnimationEnd(); + checkAnimationEnd(enter); } }); valueAnimator.setDuration(350); @@ -408,16 +544,32 @@ public void onAnimationEnd(Animator animation) { } } - private void checkAnimationEnd() { + private void checkAnimationEnd(boolean enter) { if (animators.isEmpty()) { - NotificationCenter.getInstance(account).onAnimationFinish(animationIndex); + switchLayerType(false); + HwEmojis.disableHw(); + notificationsLocker.unlock(); selectAnimatedEmojiDialog.setEnterAnimationInProgress(false); + if (enter) { + selectAnimatedEmojiDialog.emojiTabs.showRecentTabStub(false); + selectAnimatedEmojiDialog.emojiGridView.invalidate(); + selectAnimatedEmojiDialog.emojiGridView.invalidateViews(); + selectAnimatedEmojiDialog.searchBox.checkInitialization(); + if (reactionsContainerLayout.getPullingLeftProgress() > 0) { + reactionsContainerLayout.isHiddenNextReaction = false; + reactionsContainerLayout.onCustomEmojiWindowOpened(); + } else { + reactionsContainerLayout.isHiddenNextReaction = true; + reactionsContainerLayout.onCustomEmojiWindowOpened(); + } + selectAnimatedEmojiDialog.resetBackgroundBitmaps(); + syncReactionFrames(); + containerView.invalidate(); + } } } - private void syncReactionFrames(boolean enter) { - HashMap transitionReactions = new HashMap<>(); - + private void syncReactionFrames() { for (int i = 0; i < selectAnimatedEmojiDialog.emojiGridView.getChildCount(); i++) { if (selectAnimatedEmojiDialog.emojiGridView.getChildAt(i) instanceof SelectAnimatedEmojiDialog.ImageViewEmoji) { SelectAnimatedEmojiDialog.ImageViewEmoji imageViewEmoji = (SelectAnimatedEmojiDialog.ImageViewEmoji) selectAnimatedEmojiDialog.emojiGridView.getChildAt(i); @@ -490,6 +642,33 @@ public void onAnimationEnd(Animator animation) { private int frameDrawCount = 0; + public boolean isShowing() { + return !dismissed; + } + + public void dismissWithAlpha() { + if (dismissed) { + return; + } + Bulletin.hideVisible(); + dismissed = true; + AndroidUtilities.hideKeyboard(windowView); + windowView.animate().alpha(0).setDuration(150).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + checkAnimationEnd(false); + enterTransitionProgress = 0f; + reactionsContainerLayout.setCustomEmojiEnterProgress(Utilities.clamp(enterTransitionProgress, 1f, 0f)); + reactionsContainerLayout.setSkipDraw(false); + windowView.setVisibility(View.GONE); + removeView(); + } + }); + if (wasFocused && baseFragment instanceof ChatActivity) { + ((ChatActivity) baseFragment).onEditTextDialogClose(true, true); + } + } + private class ContainerView extends FrameLayout { Drawable shadow; @@ -506,12 +685,25 @@ public ContainerView(@NonNull Context context) { backgroundPaint.setColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuBackground, resourcesProvider)); } + @Override + public void invalidate() { + super.invalidate(); + if (type == TYPE_STORY) { + selectAnimatedEmojiDialog.invalidateSearchBox(); + } + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int size = Math.min(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec)); - int measuredSize = AndroidUtilities.dp(36) * 8 + AndroidUtilities.dp(12); - if (measuredSize < size) { - size = measuredSize; + int size; + if (type == TYPE_STORY) { + size = reactionsContainerLayout.getMeasuredWidth(); + } else { + size = Math.min(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec)); + int measuredSize = AndroidUtilities.dp(36) * 8 + AndroidUtilities.dp(12); + if (measuredSize < size) { + size = measuredSize; + } } int height = size; // if (height * 1.2 < MeasureSpec.getSize(heightMeasureSpec)) { @@ -522,6 +714,12 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { HashMap transitionReactions = new HashMap<>(); + float enterTransitionOffsetX = 0; + float enterTransitionOffsetY = 0; + float enterTransitionScale = 1f; + float enterTransitionScalePx = 0; + float enterTransitionScalePy = 0; + @Override protected void dispatchDraw(Canvas canvas) { if (!isShowing) { @@ -532,12 +730,16 @@ protected void dispatchDraw(Canvas canvas) { AndroidUtilities.lerp(fromRect, AndroidUtilities.rectTmp, enterTransitionProgress, drawingRect); float radius = AndroidUtilities.lerp(fromRadius, AndroidUtilities.dp(8), enterTransitionProgress); - shadow.setAlpha((int) (Utilities.clamp(progressClpamped / 0.05f, 1f, 0f) * 255)); - shadow.setBounds((int) drawingRect.left - shadowPad.left, (int) drawingRect.top - shadowPad.top, (int) drawingRect.right + shadowPad.right, (int) drawingRect.bottom + shadowPad.bottom); - shadow.draw(canvas); transitionReactions.clear(); - canvas.drawRoundRect(drawingRect, radius, radius, backgroundPaint); + if (type == TYPE_STORY) { + reactionsContainerLayout.getDelegate().drawRoundRect(canvas, drawingRect, radius, getX(), getY() - AndroidUtilities.statusBarHeight); + } else { + shadow.setAlpha((int) (Utilities.clamp(progressClpamped / 0.05f, 1f, 0f) * 255)); + shadow.setBounds((int) drawingRect.left - shadowPad.left, (int) drawingRect.top - shadowPad.top, (int) drawingRect.right + shadowPad.right, (int) drawingRect.bottom + shadowPad.bottom); + shadow.draw(canvas); + canvas.drawRoundRect(drawingRect, radius, radius, backgroundPaint); + } float rightDelta = drawingRect.left - reactionsContainerLayout.rect.left + (drawingRect.width() - reactionsContainerLayout.rect.width()); @@ -547,11 +749,11 @@ protected void dispatchDraw(Canvas canvas) { reactionsContainerLayout.drawBubbles(canvas); canvas.restore(); } - float enterTransitionOffsetX = 0; - float enterTransitionOffsetY = 0; - float enterTransitionScale = 1f; - float enterTransitionScalePx = 0; - float enterTransitionScalePy = 0; + enterTransitionOffsetX = 0; + enterTransitionOffsetY = 0; + enterTransitionScale = 1f; + enterTransitionScalePx = 0; + enterTransitionScalePy = 0; if (reactionsContainerLayout != null) { for (int i = 0; i < selectAnimatedEmojiDialog.emojiGridView.getChildCount(); i++) { @@ -567,7 +769,8 @@ protected void dispatchDraw(Canvas canvas) { canvas.translate(drawingRect.left, drawingRect.top + reactionsContainerLayout.expandSize() * (1f - enterTransitionProgress)); - float alpha = Math.max(selectAnimatedEmojiDialog.emojiGridView.getAlpha(), 1f - enterTransitionProgress); + float a = selectAnimatedEmojiDialog.emojiSearchGridView.getVisibility() == View.VISIBLE ? selectAnimatedEmojiDialog.emojiSearchGridView.getAlpha() : 0; + float alpha = Math.max(1f - a, 1f - enterTransitionProgress); if (alpha != 1f) { canvas.saveLayerAlpha(0, 0, drawingRect.width(), drawingRect.height(), (int) (255 * alpha), Canvas.ALL_SAVE_FLAG); } @@ -576,9 +779,6 @@ protected void dispatchDraw(Canvas canvas) { canvas.clipRect(left, top + AndroidUtilities.dp(36) * enterTransitionProgress, left + selectAnimatedEmojiDialog.emojiGridView.getMeasuredHeight(), top + selectAnimatedEmojiDialog.emojiGridView.getMeasuredWidth()); for (int i = -1; i < reactionsContainerLayout.recyclerListView.getChildCount(); i++) { View child; - if (enterTransitionProgress == 1 && i == -1) { - continue; - } if (i == -1) { child = reactionsContainerLayout.nextRecentReaction; } else { @@ -700,6 +900,9 @@ protected void dispatchDraw(Canvas canvas) { imageReceiver.setAlpha(oldAlpha); } } + if (holderView.loopImageView.getVisibility() != View.VISIBLE) { + invalidate(); + } } else { canvas.translate(child.getX() + drawingRect.width() - reactionsContainerLayout.rect.width(), child.getY() + fromRect.top - drawingRect.top); canvas.saveLayerAlpha(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight(), (int) (255 * (1f - progressClpamped)), Canvas.ALL_SAVE_FLAG); @@ -712,20 +915,20 @@ protected void dispatchDraw(Canvas canvas) { canvas.restoreToCount(restoreCount); } - if (invalidatePath) { - invalidatePath = false; - pathToClip.rewind(); - pathToClip.addRoundRect(drawingRect, radius, radius, Path.Direction.CW); - } - canvas.save(); - canvas.clipPath(pathToClip); - canvas.translate(cascadeAnimation ? 0 : enterTransitionOffsetX, enterTransitionOffsetY); - canvas.scale(enterTransitionScale, enterTransitionScale, enterTransitionScalePx, enterTransitionScalePy); - if (!cascadeAnimation) { - selectAnimatedEmojiDialog.setAlpha(enterTransitionProgress); + boolean beforeLollipop = Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP; + if (beforeLollipop) { + if (invalidatePath) { + invalidatePath = false; + pathToClipApi20.rewind(); + pathToClipApi20.addRoundRect(drawingRect, radius, radius, Path.Direction.CW); + } + canvas.save(); + canvas.clipPath(pathToClipApi20); + super.dispatchDraw(canvas); + canvas.restore(); + } else { + super.dispatchDraw(canvas); } - super.dispatchDraw(canvas); - canvas.restore(); if (frameDrawCount < 5) { if (frameDrawCount == 3) { @@ -738,6 +941,7 @@ protected void dispatchDraw(Canvas canvas) { if (valueAnimator != null) { invalidate(); } + HwEmojis.exec(); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/HwEmojis.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/HwEmojis.java new file mode 100644 index 0000000000..a5d02fb8a1 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/HwEmojis.java @@ -0,0 +1,106 @@ +package org.telegram.ui.Components.Reactions; + +import static org.telegram.messenger.SharedConfig.PERFORMANCE_CLASS_HIGH; +import static org.telegram.messenger.SharedConfig.getDevicePerformanceClass; + +import android.view.View; + +import org.telegram.messenger.ImageLoader; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class HwEmojis { + private static final Set hwViews = new HashSet<>(); + private static volatile boolean hwEnabled = false; + private static Runnable task; + private static boolean firstOpen = true; + private static boolean isPreparing = false; + private static boolean isCascade = false; + private static boolean isBeforePreparing = false; + private static Boolean isWeakDevice; + + public static void prepare(Runnable runnable, boolean cascade) { + isCascade = cascade; + isPreparing = true; + isBeforePreparing = false; + if (firstOpen) { + firstOpen = false; + } + task = runnable; + } + + public static void beforePreparing() { + ImageLoader.getInstance().getCacheOutQueue().pause(); + isBeforePreparing = true; + } + + public static boolean isCascade() { + return isCascade; + } + + public static boolean isPreparing() { + return isPreparing; + } + + public static boolean isFirstOpen() { + return firstOpen; + } + + public static boolean isHwEnabled() { + return hwEnabled; + } + + public static boolean isHwEnabledOrPreparing() { + return hwEnabled || isPreparing || isBeforePreparing; + } + + public static void exec() { + if (task != null) { + task.run(); + task = null; + } + } + + public static boolean grab(View view) { + if (hwEnabled) { + hwViews.add(view); + } + return hwEnabled; + } + + public static boolean grabIfWeakDevice(View... views) { + if (isWeakDevice == null) { + isWeakDevice = getDevicePerformanceClass() != PERFORMANCE_CLASS_HIGH; + } + + if (!isWeakDevice) { + return false; + } + + if (hwEnabled) { + hwViews.addAll(Arrays.asList(views)); + } + return hwEnabled; + } + + public static void enableHw() { + ImageLoader.getInstance().getCacheOutQueue().pause(); + hwEnabled = true; + isPreparing = false; + isBeforePreparing = false; + } + + public static void disableHw() { + ImageLoader.getInstance().getCacheOutQueue().resume(); + hwEnabled = false; + isPreparing = false; + isBeforePreparing = false; + task = null; + for (View view : hwViews) { + view.invalidate(); + } + hwViews.clear(); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/ReactionsEffectOverlay.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/ReactionsEffectOverlay.java index 0c0f8c1ad3..8d7c4928e5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/ReactionsEffectOverlay.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/ReactionsEffectOverlay.java @@ -1,8 +1,11 @@ package org.telegram.ui.Components.Reactions; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.PixelFormat; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; @@ -20,6 +23,7 @@ import org.telegram.messenger.MediaDataController; import org.telegram.messenger.MessageObject; import org.telegram.messenger.MessagesController; +import org.telegram.messenger.R; import org.telegram.messenger.SharedConfig; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.BaseFragment; @@ -55,11 +59,12 @@ public class ReactionsEffectOverlay { private final FrameLayout container; private final BaseFragment fragment; private final int currentAccount; + private ReactionsEffectOverlay nextReactionOverlay; boolean animateIn; float animateInProgress; float animateOutProgress; - FrameLayout windowView; + public FrameLayout windowView; BackupImageView backupImageView; private static int uniqPrefix; @@ -72,27 +77,40 @@ public class ReactionsEffectOverlay { private final ReactionsLayoutInBubble.VisibleReaction reaction; private float lastDrawnToX; private float lastDrawnToY; - private boolean started; + public boolean started; private ReactionsContainerLayout.ReactionHolderView holderView = null; private SelectAnimatedEmojiDialog.ImageViewEmoji holderView2 = null; private boolean wasScrolled; private ChatMessageCell cell; - private boolean finished; private boolean useWindow; private ViewGroup decorView; private static long lastHapticTime; ArrayList avatars = new ArrayList<>(); - long startTime; + public long startTime; + public boolean isStories; + boolean isFinished; - private ReactionsEffectOverlay(Context context, BaseFragment fragment, ReactionsContainerLayout reactionsLayout, ChatMessageCell cell, View fromAnimationView, float x, float y, ReactionsLayoutInBubble.VisibleReaction visibleReaction, int currentAccount, int animationType) { + public ReactionsEffectOverlay(Context context, BaseFragment fragment, ReactionsContainerLayout reactionsLayout, ChatMessageCell cell, View fromAnimationView, float x, float y, ReactionsLayoutInBubble.VisibleReaction visibleReaction, int currentAccount, int animationType, boolean isStories) { this.fragment = fragment; - this.messageId = cell.getMessageObject().getId(); - this.groupId = cell.getMessageObject().getGroupId(); + this.isStories = isStories; + if (cell != null) { + this.messageId = cell.getMessageObject().getId(); + this.groupId = cell.getMessageObject().getGroupId(); + } else { + this.messageId = 0; + this.groupId = 0; + } this.reaction = visibleReaction; this.animationType = animationType; this.currentAccount = currentAccount; this.cell = cell; - ReactionsLayoutInBubble.ReactionButton reactionButton = cell.getReactionButton(visibleReaction); + ReactionsLayoutInBubble.ReactionButton reactionButton = null; + if (cell != null) { + reactionButton = cell.getReactionButton(visibleReaction); + } + if (isStories && animationType == ONLY_MOVE_ANIMATION) { + ReactionsEffectOverlay.currentShortOverlay = nextReactionOverlay = new ReactionsEffectOverlay(context, fragment, reactionsLayout, cell, fromAnimationView, x, y, visibleReaction, currentAccount, SHORT_ANIMATION, true); + } float fromX, fromY, fromHeight; ChatActivity chatActivity = (fragment instanceof ChatActivity) ? (ChatActivity) fragment : null; if (reactionsLayout != null) { @@ -109,7 +127,7 @@ private ReactionsEffectOverlay(Context context, BaseFragment fragment, Reactions if (animationType == SHORT_ANIMATION) { Random random = new Random(); ArrayList recentReactions = null; - if (cell.getMessageObject().messageOwner.reactions != null) { + if (cell != null && cell.getMessageObject().messageOwner.reactions != null) { recentReactions = cell.getMessageObject().messageOwner.reactions.recent_reactions; } if (recentReactions != null && chatActivity != null && chatActivity.getDialogId() < 0) { @@ -210,21 +228,31 @@ private ReactionsEffectOverlay(Context context, BaseFragment fragment, Reactions fromX = loc[0] + cell.reactionsLayoutInBubble.x + reactionButton.x + (reactionButton.imageReceiver == null ? 0 : reactionButton.imageReceiver.getImageX()); fromY = loc[1] + cell.reactionsLayoutInBubble.y + reactionButton.y + (reactionButton.imageReceiver == null ? 0 : reactionButton.imageReceiver.getImageY()); fromHeight = reactionButton.imageReceiver == null ? 0 : reactionButton.imageReceiver.getImageHeight(); - } else { + } else if (cell != null) { ((View) cell.getParent()).getLocationInWindow(loc); fromX = loc[0] + x; fromY = loc[1] + y; fromHeight = 0; + } else { + fromX = x; + fromY = y; + fromHeight = 0; } int size; int sizeForFilter; if (animationType == ONLY_MOVE_ANIMATION) { - size = AndroidUtilities.dp(34); + size = (isStories && SharedConfig.deviceIsHigh()) ? AndroidUtilities.dp(60) : AndroidUtilities.dp(34); sizeForFilter = (int) (2f * size / AndroidUtilities.density); } else if (animationType == SHORT_ANIMATION) { - size = AndroidUtilities.dp(80); - sizeForFilter = sizeForAroundReaction(); + if (isStories) { + size = SharedConfig.deviceIsHigh() ? AndroidUtilities.dp(240) : AndroidUtilities.dp(140); + sizeForFilter = SharedConfig.deviceIsHigh() ? (int) (2f * AndroidUtilities.dp(80) / AndroidUtilities.density) : sizeForAroundReaction(); + } else { + size = AndroidUtilities.dp(80); + sizeForFilter = sizeForAroundReaction(); + } + } else { size = Math.round(Math.min(AndroidUtilities.dp(350), Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y)) * 0.8f); sizeForFilter = sizeForBigReaction(); @@ -276,7 +304,9 @@ protected void dispatchDraw(Canvas canvas) { } float toX, toY, toH; - if (cell.getMessageObject().shouldDrawReactionsInLayout()) { + if (isStories) { + toH = SharedConfig.deviceIsHigh() ? AndroidUtilities.dp(120) : AndroidUtilities.dp(50); + } else if (cell != null && cell.getMessageObject().shouldDrawReactionsInLayout()) { toH = AndroidUtilities.dp(20); } else { toH = AndroidUtilities.dp(14); @@ -299,21 +329,27 @@ protected void dispatchDraw(Canvas canvas) { } lastDrawnToX = toX; lastDrawnToY = toY; + } else if (isStories) { + toX = getMeasuredWidth() / 2f - toH / 2f; + toY = getMeasuredHeight() / 2f - toH / 2f; } else { toX = lastDrawnToX; toY = lastDrawnToY; } - if (fragment.getParentActivity() != null && fragment.getFragmentView() != null && fragment.getFragmentView().getParent() != null && fragment.getFragmentView().getVisibility() == View.VISIBLE && fragment.getFragmentView() != null) { + if (fragment != null && fragment.getParentActivity() != null && fragment.getFragmentView() != null && fragment.getFragmentView().getParent() != null && fragment.getFragmentView().getVisibility() == View.VISIBLE && fragment.getFragmentView() != null) { fragment.getFragmentView().getLocationOnScreen(loc); setAlpha(((View) fragment.getFragmentView().getParent()).getAlpha()); - } else { + } else if (!isStories){ return; } float previewX = toX - (emojiSize - toH) / 2f; float previewY = toY - (emojiSize - toH) / 2f; + if (isStories && animationType == LONG_ANIMATION) { + previewX += AndroidUtilities.dp(40); + } - if (animationType != SHORT_ANIMATION) { + if (animationType != SHORT_ANIMATION && !isStories) { if (previewX < loc[0]) { previewX = loc[0]; } @@ -369,7 +405,14 @@ protected void dispatchDraw(Canvas canvas) { } if (animationType != SHORT_ANIMATION) { - emojiStaticImageView.setAlpha(animateOutProgress > 0.7f ? (animateOutProgress - 0.7f) / 0.3f : 0); + if (!isStories) { + emojiStaticImageView.setAlpha(animateOutProgress > 0.7f ? (animateOutProgress - 0.7f) / 0.3f : 0); + } else { + emojiStaticImageView.setAlpha(1f); + } + } + if (animationType == LONG_ANIMATION && isStories) { + emojiImageView.setAlpha(1f - animateOutProgress); } //emojiImageView.setAlpha(animateOutProgress < 0.5f ? 1f - (animateOutProgress / 0.5f) : 0f); container.setTranslationX(x); @@ -400,12 +443,34 @@ protected void dispatchDraw(Canvas canvas) { float duration = animationType == ONLY_MOVE_ANIMATION ? 350f : 220f; ReactionsEffectOverlay.this.animateOutProgress += 16f / duration; } - if (ReactionsEffectOverlay.this.animateOutProgress > 0.7f && !finished) { - startShortAnimation(); + if (ReactionsEffectOverlay.this.animateOutProgress > 0.7f) { + if (isStories && animationType == ONLY_MOVE_ANIMATION) { + if (!isFinished) { + isFinished = true; + performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + + ViewGroup viewGroup = (ViewGroup) getParent(); + viewGroup.addView(nextReactionOverlay.windowView); + nextReactionOverlay.isStories = true; + nextReactionOverlay.started = true; + nextReactionOverlay.startTime = System.currentTimeMillis(); + nextReactionOverlay.windowView.setTag(R.id.parent_tag, 1); + animate().scaleX(0).scaleY(0).setStartDelay(1000).setDuration(150).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + removeCurrentView(); + } + }); + } + } else { + startShortAnimation(); + } } if (ReactionsEffectOverlay.this.animateOutProgress >= 1f) { if (animationType == LONG_ANIMATION || animationType == ONLY_MOVE_ANIMATION) { - cell.reactionsLayoutInBubble.animateReaction(reaction); + if (cell != null) { + cell.reactionsLayoutInBubble.animateReaction(reaction); + } } ReactionsEffectOverlay.this.animateOutProgress = 1f; if (animationType == SHORT_ANIMATION) { @@ -413,13 +478,19 @@ protected void dispatchDraw(Canvas canvas) { } else { currentOverlay = null; } - cell.invalidate(); - if (cell.getCurrentMessagesGroup() != null && cell.getParent() != null) { - ((View) cell.getParent()).invalidate(); + if (cell != null) { + cell.invalidate(); + if (cell.getCurrentMessagesGroup() != null && cell.getParent() != null) { + ((View) cell.getParent()).invalidate(); + } + } + if (isStories && animationType == ONLY_MOVE_ANIMATION) { + + } else { + AndroidUtilities.runOnUIThread(() -> { + removeCurrentView(); + }); } - AndroidUtilities.runOnUIThread(() -> { - removeCurrentView(); - }); } } } @@ -525,7 +596,7 @@ protected void onDetachedFromWindow() { if ((animationType == SHORT_ANIMATION && LiteMode.isEnabled(LiteMode.FLAG_ANIMATED_EMOJI_CHAT)) || animationType == LONG_ANIMATION) { TLRPC.Document document = animationType == SHORT_ANIMATION ? availableReaction.around_animation : availableReaction.effect_animation; String filer = animationType == SHORT_ANIMATION ? getFilterForAroundAnimation() : sizeForFilter + "_" + sizeForFilter; - effectImageView.getImageReceiver().setUniqKeyPrefix((uniqPrefix++) + "_" + cell.getMessageObject().getId() + "_"); + effectImageView.getImageReceiver().setUniqKeyPrefix((uniqPrefix++) + "_" + messageId + "_"); effectImageView.setImage(ImageLocation.getForDocument(document), filer, null, null, 0, null); effectImageView.getImageReceiver().setAutoRepeat(0); @@ -539,12 +610,12 @@ protected void onDetachedFromWindow() { } if (animationType == ONLY_MOVE_ANIMATION) { - TLRPC.Document document = availableReaction.appear_animation; - emojiImageView.getImageReceiver().setUniqKeyPrefix((uniqPrefix++) + "_" + cell.getMessageObject().getId() + "_"); + TLRPC.Document document = isStories ? availableReaction.select_animation : availableReaction.appear_animation; + emojiImageView.getImageReceiver().setUniqKeyPrefix((uniqPrefix++) + "_" + messageId + "_"); emojiImageView.setImage(ImageLocation.getForDocument(document), emojiSizeForFilter + "_" + emojiSizeForFilter, null, null, 0, null); } else if (animationType == LONG_ANIMATION) { TLRPC.Document document = availableReaction.activate_animation; - emojiImageView.getImageReceiver().setUniqKeyPrefix((uniqPrefix++) + "_" + cell.getMessageObject().getId() + "_"); + emojiImageView.getImageReceiver().setUniqKeyPrefix((uniqPrefix++) + "_" + messageId + "_"); emojiImageView.setImage(ImageLocation.getForDocument(document), emojiSizeForFilter + "_" + emojiSizeForFilter, null, null, 0, null); } } else { @@ -555,12 +626,17 @@ protected void onDetachedFromWindow() { } if (animationType == LONG_ANIMATION || animationType == SHORT_ANIMATION) { AnimatedEmojiDrawable animatedEmojiDrawable = new AnimatedEmojiDrawable(AnimatedEmojiDrawable.CACHE_TYPE_KEYBOARD, currentAccount, visibleReaction.documentId); - int color = Theme.getColor( - cell.getMessageObject().shouldDrawWithoutBackground() ? - cell.getMessageObject().isOutOwner() ? Theme.key_chat_outReactionButtonBackground : Theme.key_chat_inReactionButtonBackground : - cell.getMessageObject().isOutOwner() ? Theme.key_chat_outReactionButtonTextSelected : Theme.key_chat_inReactionButtonTextSelected, - fragment.getResourceProvider() - ); + int color; + if (cell != null) { + color = Theme.getColor( + cell.getMessageObject().shouldDrawWithoutBackground() ? + cell.getMessageObject().isOutOwner() ? Theme.key_chat_outReactionButtonBackground : Theme.key_chat_inReactionButtonBackground : + cell.getMessageObject().isOutOwner() ? Theme.key_chat_outReactionButtonTextSelected : Theme.key_chat_inReactionButtonTextSelected, + fragment != null ? fragment.getResourceProvider() : null + ); + } else { + color = Color.WHITE; + } animatedEmojiDrawable.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN)); effectImageView.setAnimatedEmojiEffect(AnimatedEmojiEffect.createFrom(animatedEmojiDrawable, animationType == LONG_ANIMATION, true)); windowView.setClipChildren(false); @@ -592,7 +668,7 @@ protected void onDetachedFromWindow() { ((FrameLayout.LayoutParams) emojiImageView.getLayoutParams()).topMargin = topOffset; ((FrameLayout.LayoutParams) emojiImageView.getLayoutParams()).leftMargin = leftOffset; - if (animationType != SHORT_ANIMATION) { + if (animationType != SHORT_ANIMATION && !isStories) { if (availableReaction != null) { emojiStaticImageView.getImageReceiver().setImage(ImageLocation.getForDocument(availableReaction.center_icon), "40_40_lastreactframe", null, "webp", availableReaction, 1); } @@ -636,7 +712,7 @@ private void removeCurrentView() { if (useWindow) { windowManager.removeView(windowView); } else { - decorView.removeView(windowView); + AndroidUtilities.removeFromParent(windowView); } } catch (Exception e) { @@ -655,7 +731,7 @@ public static void show(BaseFragment baseFragment, ReactionsContainerLayout reac show(baseFragment, null, cell, fromAnimationView, 0, 0, visibleReaction, currentAccount, SHORT_ANIMATION); } - ReactionsEffectOverlay reactionsEffectOverlay = new ReactionsEffectOverlay(baseFragment.getParentActivity(), baseFragment, reactionsLayout, cell, fromAnimationView, x, y, visibleReaction, currentAccount, animationType); + ReactionsEffectOverlay reactionsEffectOverlay = new ReactionsEffectOverlay(baseFragment.getParentActivity(), baseFragment, reactionsLayout, cell, fromAnimationView, x, y, visibleReaction, currentAccount, animationType, false); if (animationType == SHORT_ANIMATION) { currentShortOverlay = reactionsEffectOverlay; } else { @@ -772,7 +848,7 @@ protected void onDraw(Canvas canvas) { wasPlaying = true; } if (!wasPlaying && getImageReceiver().getLottieAnimation() != null && !getImageReceiver().getLottieAnimation().isRunning()) { - if (animationType == ONLY_MOVE_ANIMATION) { + if (animationType == ONLY_MOVE_ANIMATION && !isStories) { getImageReceiver().getLottieAnimation().setCurrentFrame(getImageReceiver().getLottieAnimation().getFramesCount() - 1, false); } else { getImageReceiver().getLottieAnimation().setCurrentFrame(0, false); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/ReactionsLayoutInBubble.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/ReactionsLayoutInBubble.java index bfc413a1fc..b3798b2336 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/ReactionsLayoutInBubble.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/ReactionsLayoutInBubble.java @@ -26,6 +26,7 @@ import org.telegram.messenger.SvgHelper; import org.telegram.messenger.UserConfig; import org.telegram.messenger.UserObject; +import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Cells.ChatMessageCell; @@ -91,7 +92,17 @@ public class ReactionsLayoutInBubble { private static int pointer = 1; - private final static Comparator usersComparator = (user1, user2) -> (int) (user1.id - user2.id); + private final static Comparator usersComparator = (user1, user2) -> (int) (getPeerId(user1) - getPeerId(user2)); + + private static long getPeerId(TLObject object) { + if (object instanceof TLRPC.User) { + return ((TLRPC.User) object).id; + } + if (object instanceof TLRPC.Chat) { + return ((TLRPC.Chat) object).id; + } + return 0; + } public ReactionsLayoutInBubble(ChatMessageCell parentView) { this.parentView = parentView; @@ -140,7 +151,7 @@ public void setMessage(MessageObject messageObject, boolean isSmall, Theme.Resou ReactionButton button = new ReactionButton(old, reactionCount, isSmall); reactionButtons.add(button); if (!isSmall && messageObject.messageOwner.reactions.recent_reactions != null) { - ArrayList users = null; + ArrayList users = null; if (messageObject.getDialogId() > 0 && !UserObject.isReplyUser(messageObject.getDialogId())) { users = new ArrayList<>(); @@ -174,14 +185,12 @@ public void setMessage(MessageObject messageObject, boolean isSmall, Theme.Resou TLRPC.MessagePeerReaction recent = messageObject.messageOwner.reactions.recent_reactions.get(j); VisibleReaction visibleReactionPeer = VisibleReaction.fromTLReaction(recent.reaction); VisibleReaction visibleReactionCount = VisibleReaction.fromTLReaction(reactionCount.reaction); - if (visibleReactionPeer.equals(visibleReactionCount) && MessagesController.getInstance(currentAccount).getUser(MessageObject.getPeerId(recent.peer_id)) != null) { - TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(MessageObject.getPeerId(recent.peer_id)); - if (user != null) { - if (users == null) { - users = new ArrayList<>(); - } - users.add(user); + TLObject object = MessagesController.getInstance(currentAccount).getUserOrChat(MessageObject.getPeerId(recent.peer_id)); + if (visibleReactionPeer.equals(visibleReactionCount) && object != null) { + if (users == null) { + users = new ArrayList<>(); } + users.add(object); } } button.setUsers(users); @@ -439,14 +448,14 @@ public boolean animateChange() { return changed; } - private boolean equalsUsersList(ArrayList users, ArrayList users1) { + private boolean equalsUsersList(ArrayList users, ArrayList users1) { if (users == null || users1 == null || users.size() != users1.size()) { return false; } for (int i = 0; i < users.size(); i++) { - TLRPC.User user1 = users.get(i); - TLRPC.User user2 = users1.get(i); - if (user1 == null || user2 == null || user1.id != user2.id) { + TLObject user1 = users.get(i); + TLObject user2 = users1.get(i); + if (user1 == null || user2 == null || getPeerId(user1) != getPeerId(user2)) { return false; } } @@ -522,7 +531,7 @@ public class ReactionButton { int lastDrawnBackgroundColor; boolean isSelected; AvatarsDrawable avatarsDrawable; - ArrayList users; + ArrayList users; public ReactionButton(ReactionButton reuseFrom, TLRPC.ReactionCount reactionCount, boolean isSmall) { if (reuseFrom != null) { @@ -724,7 +733,7 @@ private void drawImage(Canvas canvas, float alpha) { } } - public void setUsers(ArrayList users) { + public void setUsers(ArrayList users) { this.users = users; if (users != null) { Collections.sort(users, usersComparator); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ReactionsContainerLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ReactionsContainerLayout.java index 3bc275c0a3..2d5235ef91 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ReactionsContainerLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ReactionsContainerLayout.java @@ -51,6 +51,7 @@ import org.telegram.messenger.MessageObject; import org.telegram.messenger.MessagesController; import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.R; import org.telegram.messenger.SharedConfig; import org.telegram.messenger.SvgHelper; @@ -60,10 +61,12 @@ import org.telegram.ui.ActionBar.AlertDialog; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Cells.ReactedUserHolderView; import org.telegram.ui.Components.ListView.AdapterWithDiffUtils; import org.telegram.ui.Components.Premium.PremiumFeatureBottomSheet; import org.telegram.ui.Components.Premium.PremiumLockIconView; import org.telegram.ui.Components.Reactions.CustomEmojiReactionsWindow; +import org.telegram.ui.Components.Reactions.HwEmojis; import org.telegram.ui.Components.Reactions.ReactionsEffectOverlay; import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble; import org.telegram.ui.Components.Reactions.ReactionsUtils; @@ -89,6 +92,8 @@ public void set(ReactionsContainerLayout object, Float value) { } }; + public final static int TYPE_DEFAULT = 0; + public final static int TYPE_STORY = 1; private final static int ALPHA_DURATION = 150; private final static float SIDE_SCALE = 0.6f; @@ -166,9 +171,15 @@ public void set(ReactionsContainerLayout object, Float value) { private Paint selectedPaint; ChatScrimPopupContainerLayout parentLayout; private boolean animatePopup; + final AnimationNotificationsLocker notificationsLocker = new AnimationNotificationsLocker(); + private final int type; + public boolean skipEnterAnimation; + public boolean isHiddenNextReaction = true; + private Runnable onSwitchedToLoopView; - public ReactionsContainerLayout(BaseFragment fragment, @NonNull Context context, int currentAccount, Theme.ResourcesProvider resourcesProvider) { + public ReactionsContainerLayout(int type, BaseFragment fragment, @NonNull Context context, int currentAccount, Theme.ResourcesProvider resourcesProvider) { super(context); + this.type = type; durationScale = Settings.Global.getFloat(context.getContentResolver(), Settings.Global.ANIMATOR_DURATION_SCALE, 1.0f); selectedPaint = new Paint(Paint.ANTI_ALIAS_FLAG); selectedPaint.setColor(Theme.getColor(Theme.key_listSelector, resourcesProvider)); @@ -190,6 +201,7 @@ public ReactionsContainerLayout(BaseFragment fragment, @NonNull Context context, shadow.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_chat_messagePanelShadow), PorterDuff.Mode.MULTIPLY)); recyclerListView = new RecyclerListView(context) { + @Override public boolean drawChild(Canvas canvas, View child, long drawingTime) { if (pressedReaction != null && (child instanceof ReactionHolderView) && ((ReactionHolderView) child).currentReaction.equals(pressedReaction)) { @@ -330,7 +342,11 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int customEmojiReactionsIconView = new InternalImageView(context); customEmojiReactionsIconView.setImageResource(R.drawable.msg_reactions_expand); customEmojiReactionsIconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE); - customEmojiReactionsIconView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_dialogBackground), PorterDuff.Mode.MULTIPLY)); + if (type == TYPE_STORY) { + customEmojiReactionsIconView.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.MULTIPLY)); + } else { + customEmojiReactionsIconView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_dialogBackground), PorterDuff.Mode.MULTIPLY)); + } customEmojiReactionsIconView.setBackground(Theme.createSimpleSelectorCircleDrawable(AndroidUtilities.dp(28), Color.TRANSPARENT, ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_listSelector), 40))); customEmojiReactionsIconView.setPadding(AndroidUtilities.dp(2), AndroidUtilities.dp(2), AndroidUtilities.dp(2), AndroidUtilities.dp(2)); customEmojiReactionsIconView.setContentDescription(LocaleController.getString(R.string.AccDescrExpandPanel)); @@ -373,6 +389,7 @@ public int getItemViewType(int position) { private static final int VIEW_TYPE_REACTION = 0; private static final int VIEW_TYPE_PREMIUM_BUTTON = 1; private static final int VIEW_TYPE_CUSTOM_EMOJI_BUTTON = 2; + @Override public void notifyDataSetChanged() { oldItems.clear(); @@ -500,6 +517,10 @@ private void animatePullingBack() { } } + public void setOnSwitchedToLoopView(Runnable onSwitchedToLoopView) { + this.onSwitchedToLoopView = onSwitchedToLoopView; + } + public void dismissWindow() { reactionsWindow.dismiss(); } @@ -512,13 +533,27 @@ private void showCustomEmojiReactionDialog() { if (reactionsWindow != null) { return; } - reactionsWindow = new CustomEmojiReactionsWindow(fragment, allReactionsList, selectedReactions, this, resourcesProvider); + reactionsWindow = new CustomEmojiReactionsWindow(type, fragment, allReactionsList, selectedReactions, this, resourcesProvider); + invalidateLoopViews(); reactionsWindow.onDismissListener(() -> { reactionsWindow = null; + invalidateLoopViews(); + if (delegate != null) { + delegate.onEmojiWindowDismissed(); + } }); //animatePullingBack(); } + private void invalidateLoopViews() { + for (int i = 0; i < recyclerListView.getChildCount(); i++) { + View child = recyclerListView.getChildAt(i); + if (child instanceof ReactionHolderView) { + ((ReactionHolderView) child).loopImageView.invalidate(); + } + } + } + public boolean showCustomEmojiReaction() { return !MessagesController.getInstance(currentAccount).premiumLocked && allReactionsAvailable; } @@ -658,9 +693,12 @@ protected void dispatchDraw(Canvas canvas) { float transitionLeftOffset = (getWidth() - getPaddingRight()) * Math.min(1f, lt); rect.set(getPaddingLeft() + transitionLeftOffset, getPaddingTop() + recyclerListView.getMeasuredHeight() * (1f - otherViewsScale) - expandSize, (getWidth() - getPaddingRight()) * rt, getHeight() - getPaddingBottom() + expandSize); radius = (rect.height() - expandSize * 2f) / 2f; - shadow.setAlpha((int) (Utilities.clamp(1f - (customEmojiReactionsEnterProgress / 0.05f), 1f, 0f) * 255)); - shadow.setBounds((int) (getPaddingLeft() + (getWidth() - getPaddingRight() + shadowPad.right) * lt - shadowPad.left), getPaddingTop() - shadowPad.top - (int) expandSize, (int) ((getWidth() - getPaddingRight() + shadowPad.right) * rt), getHeight() - getPaddingBottom() + shadowPad.bottom + (int) expandSize); - shadow.draw(canvas); + + if (type != TYPE_STORY) { + shadow.setAlpha((int) (Utilities.clamp(1f - (customEmojiReactionsEnterProgress / 0.05f), 1f, 0f) * 255)); + shadow.setBounds((int) (getPaddingLeft() + (getWidth() - getPaddingRight() + shadowPad.right) * lt - shadowPad.left), getPaddingTop() - shadowPad.top - (int) expandSize, (int) ((getWidth() - getPaddingRight() + shadowPad.right) * rt), getHeight() - getPaddingBottom() + shadowPad.bottom + (int) expandSize); + shadow.draw(canvas); + } canvas.restoreToCount(s); @@ -670,7 +708,12 @@ protected void dispatchDraw(Canvas canvas) { float sc = transitionProgress; canvas.scale(sc, sc, pivotX, getHeight() / 2f); } - canvas.drawRoundRect(rect, radius, radius, bgPaint); + if (type == TYPE_STORY) { + delegate.drawRoundRect(canvas, rect, radius, getX(), getY()); + } else { + canvas.drawRoundRect(rect, radius, radius, bgPaint); + } + canvas.restoreToCount(s); } @@ -698,7 +741,7 @@ protected void dispatchDraw(Canvas canvas) { if (child.getLeft() > lastReactionX) { lastReactionX = child.getLeft(); } - if (view.hasEnterAnimation && view.enterImageView.getImageReceiver().getLottieAnimation() == null) { + if (skipEnterAnimation || (view.hasEnterAnimation && view.enterImageView.getImageReceiver().getLottieAnimation() == null)) { continue; } if (view.getX() + view.getMeasuredWidth() / 2f > 0 && view.getX() + view.getMeasuredWidth() / 2f < recyclerListView.getWidth()) { @@ -744,9 +787,10 @@ protected void dispatchDraw(Canvas canvas) { } if (pullingLeftOffsetProgress > 0) { float progress = getPullingLeftProgress(); - int left = lastReactionX + AndroidUtilities.dp(32); - float leftProgress = Utilities.clamp(left / (float) (getMeasuredWidth() - AndroidUtilities.dp(34)), 1f, 0f); - float pullingOffsetX = leftProgress * progress * AndroidUtilities.dp(32); + int reactionSize = nextRecentReaction.getMeasuredWidth() - AndroidUtilities.dp(2); + int left = lastReactionX + reactionSize; + float leftProgress = Utilities.clamp(left / (float) (getMeasuredWidth() - nextRecentReaction.getMeasuredWidth()), 1f, 0f); + float pullingOffsetX = leftProgress * progress * reactionSize; if (nextRecentReaction.getTag() == null) { nextRecentReaction.setTag(1f); @@ -756,10 +800,20 @@ protected void dispatchDraw(Canvas canvas) { float scale = Utilities.clamp(progress, 1f, 0f); nextRecentReaction.setScaleX(scale); nextRecentReaction.setScaleY(scale); - nextRecentReaction.setTranslationX(recyclerListView.getX() + left - pullingOffsetX - AndroidUtilities.dp(20)); - nextRecentReaction.setVisibility(View.VISIBLE); + float additionalOffset = 0; + if (type != TYPE_STORY) { + additionalOffset = - AndroidUtilities.dp(20); + } else { + additionalOffset = - AndroidUtilities.dp(8); + } + nextRecentReaction.setTranslationX(recyclerListView.getX() + left - pullingOffsetX + additionalOffset); + if (nextRecentReaction.getVisibility() != View.VISIBLE) { + nextRecentReaction.setVisibility(View.VISIBLE); + } } else { - nextRecentReaction.setVisibility(View.GONE); + if (nextRecentReaction.getVisibility() != View.GONE && isHiddenNextReaction) { + nextRecentReaction.setVisibility(View.GONE); + } if (nextRecentReaction.getTag() != null) { nextRecentReaction.setTag(null); } @@ -774,20 +828,25 @@ protected void dispatchDraw(Canvas canvas) { return; } - canvas.clipPath(mPath); + boolean showCustomEmojiReaction = showCustomEmojiReaction(); + if (!showCustomEmojiReaction) { + canvas.clipPath(mPath); + } canvas.translate((LocaleController.isRTL || mirrorX ? -1 : 1) * getWidth() * (1f - transitionProgress), 0); recyclerListView.setTranslationX(-transitionLeftOffset); super.dispatchDraw(canvas); - if (leftShadowPaint != null) { - float p = Utilities.clamp(leftAlpha * transitionProgress, 1f, 0f); - leftShadowPaint.setAlpha((int) (p * 0xFF)); - canvas.drawRect(rect, leftShadowPaint); - } - if (rightShadowPaint != null) { - float p = Utilities.clamp(rightAlpha * transitionProgress, 1f, 0f); - rightShadowPaint.setAlpha((int) (p * 0xFF)); - canvas.drawRect(rect, rightShadowPaint); + if (!showCustomEmojiReaction) { + if (leftShadowPaint != null) { + float p = Utilities.clamp(leftAlpha * transitionProgress, 1f, 0f); + leftShadowPaint.setAlpha((int) (p * 0xFF)); + canvas.drawRect(rect, leftShadowPaint); + } + if (rightShadowPaint != null) { + float p = Utilities.clamp(rightAlpha * transitionProgress, 1f, 0f); + rightShadowPaint.setAlpha((int) (p * 0xFF)); + canvas.drawRect(rect, rightShadowPaint); + } } canvas.restoreToCount(s); @@ -803,8 +862,10 @@ public void drawBubbles(Canvas canvas) { } private void drawBubbles(Canvas canvas, float br, float cPr, float sr, int alpha) { + if (type == TYPE_STORY) { + return; + } canvas.save(); - float scale = transitionProgress; canvas.clipRect(0, AndroidUtilities.lerp(rect.bottom, 0, CubicBezierInterpolator.DEFAULT.getInterpolation(flipVerticalProgress)) - (int) Math.ceil(rect.height() / 2f * (1f - transitionProgress)), getMeasuredWidth(), AndroidUtilities.lerp(getMeasuredHeight() + AndroidUtilities.dp(8), getPaddingTop() - expandSize(), CubicBezierInterpolator.DEFAULT.getInterpolation(flipVerticalProgress))); float cx = LocaleController.isRTL || mirrorX ? bigCircleOffset : getWidth() - bigCircleOffset; float cy = getHeight() - getPaddingBottom() + expandSize(); @@ -917,7 +978,7 @@ private void checkPressedProgress(Canvas canvas, ReactionHolderView view) { } } - private float getPullingLeftProgress() { + public float getPullingLeftProgress() { return Utilities.clamp(pullingLeftOffset / AndroidUtilities.dp(42), 2f, 0f); } @@ -951,7 +1012,7 @@ public void setMessage(MessageObject message, TLRPC.ChatFull chatFull) { this.messageObject = message; TLRPC.ChatFull reactionsChat = chatFull; List visibleReactions = new ArrayList<>(); - if (message.isForwardedChannelPost()) { + if (message != null && message.isForwardedChannelPost()) { reactionsChat = MessagesController.getInstance(currentAccount).getChatFull(-message.getFromChatId()); if (reactionsChat == null) { waitingLoadingChatId = -message.getFromChatId(); @@ -994,7 +1055,7 @@ public void setMessage(MessageObject message, TLRPC.ChatFull chatFull) { filterReactions(visibleReactions); setVisibleReactionsList(visibleReactions); - if (message.messageOwner.reactions != null && message.messageOwner.reactions.results != null) { + if (message != null && message.messageOwner.reactions != null && message.messageOwner.reactions.results != null) { for (int i = 0; i < message.messageOwner.reactions.results.size(); i++) { if (message.messageOwner.reactions.results.get(i).chosen) { selectedReactions.add(ReactionsLayoutInBubble.VisibleReaction.fromTLReaction(message.messageOwner.reactions.results.get(i).reaction)); @@ -1080,15 +1141,23 @@ public void startEnterAnimation(boolean animatePopup) { this.animatePopup = animatePopup; setTransitionProgress(0); setAlpha(1f); + notificationsLocker.lock(); + ObjectAnimator animator; if (allowSmoothEnterTransition()) { - ObjectAnimator animator = ObjectAnimator.ofFloat(this, ReactionsContainerLayout.TRANSITION_PROGRESS_VALUE, 0f, 1f).setDuration(250); + animator = ObjectAnimator.ofFloat(this, ReactionsContainerLayout.TRANSITION_PROGRESS_VALUE, 0f, 1f).setDuration(250); animator.setInterpolator(new OvershootInterpolator(0.5f)); - animator.start(); } else { - ObjectAnimator animator = ObjectAnimator.ofFloat(this, ReactionsContainerLayout.TRANSITION_PROGRESS_VALUE, 0f, 1f).setDuration(250); + animator = ObjectAnimator.ofFloat(this, ReactionsContainerLayout.TRANSITION_PROGRESS_VALUE, 0f, 1f).setDuration(250); animator.setInterpolator(new OvershootInterpolator(0.5f)); - animator.start(); } + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + notificationsLocker.unlock(); + } + }); + animator.start(); } public int getTotalWidth() { @@ -1132,6 +1201,14 @@ public void prepareAnimation(boolean b) { invalidate(); } + public void setCustomEmojiReactionsBackground(boolean isNeed) { + if (isNeed) { + customEmojiReactionsIconView.setBackground(Theme.createSimpleSelectorCircleDrawable(AndroidUtilities.dp(28), Color.TRANSPARENT, ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_listSelector), 40))); + } else { + customEmojiReactionsIconView.setBackground(null); + } + } + boolean skipDraw; public void setSkipDraw(boolean b) { @@ -1157,7 +1234,11 @@ public void setSkipDraw(boolean b) { } public void onCustomEmojiWindowOpened() { - animatePullingBack(); + pullingLeftOffset = 0f; + if (customReactionsContainer != null) { + customReactionsContainer.invalidate(); + } + invalidate(); } public void clearRecentReactions() { @@ -1177,7 +1258,7 @@ public void clearRecentReactions() { alertDialog.show(); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } @@ -1187,6 +1268,37 @@ public void setChatScrimView(ChatScrimPopupContainerLayout chatScrimPopupContain this.chatScrimPopupContainerLayout = chatScrimPopupContainerLayout; } + public ReactionsContainerDelegate getDelegate() { + return delegate; + } + + public void setCurrentAccount(int currentAccount) { + this.currentAccount = currentAccount; + } + + public void setFragment(BaseFragment lastFragment) { + fragment = lastFragment; + } + + public void reset() { + pressedReactionPosition = 0; + pressedProgress = 0; + pressedReaction = null; + clicked = false; + AndroidUtilities.forEachViews(recyclerListView, view -> { + if (view instanceof ReactionHolderView) { + ReactionHolderView reactionHolderView = (ReactionHolderView) view; + reactionHolderView.pressed = false; + reactionHolderView.loopImageView.setAlpha(1f); + reactionHolderView.loopImageView.setScaleX(1f); + reactionHolderView.loopImageView.setScaleY(1f); + } + + }); + recyclerListView.invalidate(); + invalidate(); + } + private final class LeftRightShadowsListener extends RecyclerView.OnScrollListener { private boolean leftVisible, rightVisible; private ValueAnimator leftAnimator, rightAnimator; @@ -1278,6 +1390,20 @@ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { this.recyclerReaction = recyclerReaction; enterImageView = new BackupImageView(context) { + @Override + protected ImageReceiver createImageReciever() { + return new ImageReceiver(this) { + @Override + protected boolean setImageBitmapByKey(Drawable drawable, String key, int type, boolean memCache, int guid) { + if (drawable instanceof RLottieDrawable) { + RLottieDrawable rLottieDrawable = (RLottieDrawable) drawable; + rLottieDrawable.setCurrentFrame(0, false, true); + } + return super.setImageBitmapByKey(drawable, key, type, memCache, guid); + } + }; + } + @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); @@ -1288,6 +1414,9 @@ protected void dispatchDraw(Canvas canvas) { switchedToLoopView = true; loopImageView.imageReceiver.getLottieAnimation().setCurrentFrame(0, false, true); loopImageView.setVisibility(View.VISIBLE); + if (onSwitchedToLoopView != null) { + onSwitchedToLoopView.run(); + } AndroidUtilities.runOnUIThread(() -> { enterImageView.setVisibility(View.INVISIBLE); }); @@ -1297,21 +1426,97 @@ protected void dispatchDraw(Canvas canvas) { @Override public void invalidate() { + if (HwEmojis.grabIfWeakDevice(this, ReactionsContainerLayout.this)) { + return; + } super.invalidate(); ReactionsContainerLayout.this.invalidate(); } @Override public void invalidate(Rect dirty) { + if (HwEmojis.grabIfWeakDevice(this, ReactionsContainerLayout.this)) { + return; + } super.invalidate(dirty); ReactionsContainerLayout.this.invalidate(); } + + @Override + public void invalidate(int l, int t, int r, int b) { + if (HwEmojis.grabIfWeakDevice(this)) { + return; + } + super.invalidate(l, t, r, b); + } + }; + loopImageView = new BackupImageView(context) { + + @Override + protected void onDraw(Canvas canvas) { + ImageReceiver imageReceiver = animatedEmojiDrawable != null ? animatedEmojiDrawable.getImageReceiver() : this.imageReceiver; + if (imageReceiver != null && imageReceiver.getLottieAnimation() != null) { + if (reactionsWindow != null || pressed) { + imageReceiver.getLottieAnimation().start(); + } else { + if (imageReceiver.getLottieAnimation().getCurrentFrame() <= 2) { + imageReceiver.getLottieAnimation().stop(); + } + } + } + super.onDraw(canvas); + + } + + @Override + protected ImageReceiver createImageReciever() { + return new ImageReceiver(this) { + + @Override + protected boolean setImageBitmapByKey(Drawable drawable, String key, int type, boolean memCache, int guid) { + boolean rez = super.setImageBitmapByKey(drawable, key, type, memCache, guid); + if (rez) { + if (drawable instanceof RLottieDrawable) { + RLottieDrawable rLottieDrawable = (RLottieDrawable) drawable; + rLottieDrawable.setCurrentFrame(0, false, true); + rLottieDrawable.stop(); + } + } + return rez; + } + }; + } + + @Override + public void invalidate() { + if (HwEmojis.grabIfWeakDevice(this)) { + return; + } + super.invalidate(); + } + + @Override + public void invalidate(int l, int t, int r, int b) { + if (HwEmojis.grabIfWeakDevice(this)) { + return; + } + super.invalidate(l, t, r, b); + } }; - loopImageView = new BackupImageView(context); enterImageView.getImageReceiver().setAutoRepeat(0); enterImageView.getImageReceiver().setAllowStartLottieAnimation(false); pressedBackupImageView = new BackupImageView(context) { + + @Override + protected void onDraw(Canvas canvas) { + ImageReceiver imageReceiver = animatedEmojiDrawable != null ? animatedEmojiDrawable.getImageReceiver() : this.imageReceiver; + if (imageReceiver != null && imageReceiver.getLottieAnimation() != null) { + imageReceiver.getLottieAnimation().start(); + } + super.onDraw(canvas); + } + @Override public void invalidate() { super.invalidate(); @@ -1323,6 +1528,9 @@ public void invalidate() { addView(loopImageView, LayoutHelper.createFrame(34, 34, Gravity.CENTER)); enterImageView.setLayerNum(Integer.MAX_VALUE); loopImageView.setLayerNum(Integer.MAX_VALUE); + loopImageView.imageReceiver.setAutoRepeat(0); + loopImageView.imageReceiver.setAllowStartAnimation(false); + loopImageView.imageReceiver.setAllowStartLottieAnimation(false); pressedBackupImageView.setLayerNum(Integer.MAX_VALUE); } @@ -1347,14 +1555,14 @@ private void setReaction(ReactionsLayoutInBubble.VisibleReaction react, int posi pressedBackupImageView.getImageReceiver().clearImage(); loopImageView.getImageReceiver().clearImage(); AnimatedEmojiDrawable pressedDrawable = new AnimatedEmojiDrawable(AnimatedEmojiDrawable.CACHE_TYPE_ALERT_PREVIEW_LARGE, currentAccount, currentReaction.documentId); - pressedDrawable.setColorFilter(Theme.chat_animatedEmojiTextColorFilter); + pressedDrawable.setColorFilter(Theme.getAnimatedEmojiColorFilter(resourcesProvider)); pressedBackupImageView.setAnimatedEmojiDrawable(pressedDrawable); AnimatedEmojiDrawable loopDrawable = new AnimatedEmojiDrawable(AnimatedEmojiDrawable.CACHE_TYPE_ALERT_PREVIEW, currentAccount, currentReaction.documentId); - loopDrawable.setColorFilter(Theme.chat_animatedEmojiTextColorFilter); + loopDrawable.setColorFilter(Theme.getAnimatedEmojiColorFilter(resourcesProvider)); loopImageView.setAnimatedEmojiDrawable(loopDrawable); } setFocusable(true); - shouldSwitchToLoopView = hasEnterAnimation && showCustomEmojiReaction(); + shouldSwitchToLoopView = hasEnterAnimation && !allReactionsIsDefault; if (!hasEnterAnimation) { enterImageView.setVisibility(View.GONE); loopImageView.setVisibility(View.VISIBLE); @@ -1389,6 +1597,9 @@ private void updateImage(ReactionsLayoutInBubble.VisibleReaction react) { enterImageView.getImageReceiver().setImage(ImageLocation.getForDocument(defaultReaction.appear_animation), ReactionsUtils.APPEAR_ANIMATION_FILTER, null, null, svgThumb, 0, "tgs", react, 0); loopImageView.getImageReceiver().setImage(ImageLocation.getForDocument(defaultReaction.select_animation), ReactionsUtils.SELECT_ANIMATION_FILTER, null, null, hasEnterAnimation ? null : svgThumb, 0, "tgs", currentReaction, 0); } + if (enterImageView.getImageReceiver().getLottieAnimation() != null) { + enterImageView.getImageReceiver().getLottieAnimation().setCurrentFrame(0, false, true); + } pressedBackupImageView.getImageReceiver().setImage(ImageLocation.getForDocument(defaultReaction.select_animation), ReactionsUtils.SELECT_ANIMATION_FILTER, null, null, svgThumb, 0, "tgs", react, 0); preloadImageReceiver.setAllowStartLottieAnimation(false); @@ -1473,8 +1684,13 @@ public void resetAnimation() { loopImageView.setScaleX(1f); } else { loopImageView.animate().cancel(); - loopImageView.setScaleY(0); - loopImageView.setScaleX(0); + if (skipEnterAnimation) { + loopImageView.setScaleY(1f); + loopImageView.setScaleX(1f); + } else { + loopImageView.setScaleY(0); + loopImageView.setScaleX(0); + } } isEnter = false; } @@ -1581,6 +1797,18 @@ public interface ReactionsContainerDelegate { void onReactionClicked(View view, ReactionsLayoutInBubble.VisibleReaction visibleReaction, boolean longpress, boolean addToRecent); void hideMenu(); + + default void drawRoundRect(Canvas canvas, RectF rect, float radius, float offsetX, float offsetY) { + + } + + default boolean needEnterText() { + return false; + } + + default void onEmojiWindowDismissed() { + + } } @Override @@ -1692,8 +1920,12 @@ public CustomReactionsContainer(Context context) { @Override protected void dispatchDraw(Canvas canvas) { - super.dispatchDraw(canvas); - int color = ColorUtils.blendARGB(Theme.getColor(Theme.key_actionBarDefaultSubmenuItemIcon, resourcesProvider), Theme.getColor(Theme.key_dialogBackground, resourcesProvider), 0.7f); + int color; + if (type == TYPE_STORY) { + color = ColorUtils.setAlphaComponent(Color.WHITE, 30); + } else { + color = ColorUtils.blendARGB(Theme.getColor(Theme.key_actionBarDefaultSubmenuItemIcon, resourcesProvider), Theme.getColor(Theme.key_dialogBackground, resourcesProvider), 0.7f); + } backgroundPaint.setColor(color); float cy = getMeasuredHeight() / 2f; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/RecyclerAnimationScrollHelper.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/RecyclerAnimationScrollHelper.java index cd2c283619..3298dfd131 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/RecyclerAnimationScrollHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/RecyclerAnimationScrollHelper.java @@ -34,6 +34,8 @@ public class RecyclerAnimationScrollHelper { public SparseArray positionToOldView = new SparseArray<>(); private HashMap oldStableIds = new HashMap<>(); + public boolean forceUseStableId; + public boolean isDialogs; public RecyclerAnimationScrollHelper(RecyclerListView recyclerView, LinearLayoutManager layoutManager) { this.recyclerView = recyclerView; @@ -78,8 +80,18 @@ public void scrollToPosition(int position, int offset, final boolean bottom, boo oldViews.add(child); int childPosition = layoutManager.getPosition(child); positionToOldView.put(childPosition, child); - if (adapter != null && adapter.hasStableIds()) { - long itemId = ((RecyclerView.LayoutParams) child.getLayoutParams()).mViewHolder.getItemId(); + if (adapter != null && (adapter.hasStableIds() || forceUseStableId)) { + long itemId; + if (forceUseStableId) { + int adapterPosition = ((RecyclerView.LayoutParams) child.getLayoutParams()).mViewHolder.getAdapterPosition(); + if (adapterPosition < 0) { + continue; + } + itemId = adapter.getItemId(adapterPosition); + } else { + itemId = ((RecyclerView.LayoutParams) child.getLayoutParams()).mViewHolder.getItemId(); + } + oldStableIds.put(itemId, child); } if (child instanceof ChatMessageCell) { @@ -108,6 +120,7 @@ public void scrollToPosition(int position, int offset, final boolean bottom, boo recyclerView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { @Override public void onLayoutChange(View v, int l, int t, int r, int b, int ol, int ot, int or, int ob) { + recyclerView.removeOnLayoutChangeListener(this); final ArrayList incomingViews = new ArrayList<>(); recyclerView.stopScroll(); @@ -128,7 +141,7 @@ public void onLayoutChange(View v, int l, int t, int r, int b, int ol, int ot, i ((ChatMessageCell) child).setAnimationRunning(true, false); } - if (adapter != null && adapter.hasStableIds()) { + if (adapter != null && (adapter.hasStableIds() || forceUseStableId)) { long stableId = adapter.getItemId(recyclerView.getChildAdapterPosition(child)); if (oldStableIds.containsKey(stableId)) { View view = oldStableIds.get(stableId); @@ -288,21 +301,36 @@ public void onAnimationEnd(Animator animation) { } }); - recyclerView.removeOnLayoutChangeListener(this); long duration; - if (hasSameViews) { - duration = 600; + if (isDialogs) { + if (hasSameViews) { + duration = 150; + animator.setDuration(duration); + animator.setInterpolator(CubicBezierInterpolator.EASE_OUT); + } else { + duration = (long) (((scrollLength / (float) recyclerView.getMeasuredHeight()) + 1f) * 200L); + if (duration < 300) { + duration = 300; + } + duration = Math.min(duration, 1300); + animator.setDuration(duration); + animator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + } } else { - duration = (long) (((scrollLength / (float) recyclerView.getMeasuredHeight()) + 1f) * 200L); - if (duration < 300) { - duration = 300; + if (hasSameViews) { + duration = 600; + } else { + duration = (long) (((scrollLength / (float) recyclerView.getMeasuredHeight()) + 1f) * 200L); + if (duration < 300) { + duration = 300; + } + duration = Math.min(duration, 1300); } - duration = Math.min(duration, 1300); - } - animator.setDuration(duration); - animator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + animator.setDuration(duration); + animator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + } animator.start(); } }); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/RecyclerListView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/RecyclerListView.java index b3b9f8939e..427365926d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/RecyclerListView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/RecyclerListView.java @@ -53,6 +53,7 @@ import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.FileLog; +import org.telegram.messenger.GenericProvider; import org.telegram.messenger.LocaleController; import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; @@ -75,6 +76,7 @@ public class RecyclerListView extends RecyclerView { SECTIONS_TYPE_STICKY_HEADERS = 1, SECTIONS_TYPE_DATE = 2, SECTIONS_TYPE_FAST_SCROLL_ONLY = 3; + private boolean drawSelection = true; @Retention(RetentionPolicy.SOURCE) @IntDef({ @@ -135,6 +137,7 @@ public class RecyclerListView extends RecyclerView { protected View selectorView; protected android.graphics.Rect selectorRect = new android.graphics.Rect(); private boolean isChildViewEnabled; + private boolean translateSelector; private boolean selfOnLayout; @@ -303,7 +306,7 @@ public abstract static class SectionsAdapter extends FastScrollAdapter { private ArrayList hashes = new ArrayList<>(); - private void cleanupCache() { + public void cleanupCache() { if (sectionCache == null) { sectionCache = new SparseIntArray(); sectionPositionCache = new SparseIntArray(); @@ -418,17 +421,8 @@ public int getPositionInSectionForPosition(int position) { } public void update(boolean diff) { - cleanupCache(); - ArrayList oldHashes = new ArrayList<>(hashes); - hashes.clear(); - - for (int i = 0, N = internalGetSectionCount(); i < N; i++) { - int count = internalGetCountForSection(i); - for (int j = 0; j < count; ++j) { - hashes.add(Objects.hash(i * -49612, getItem(i, j))); - } - } + updateHashes(); if (diff) { DiffUtil.calculateDiff(new DiffUtil.Callback() { @@ -457,6 +451,23 @@ public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { } } + public void updateHashes() { + cleanupCache(); + + hashes.clear(); + + for (int i = 0, N = internalGetSectionCount(); i < N; i++) { + int count = internalGetCountForSection(i); + for (int j = 0; j < count; ++j) { + hashes.add(getHash(i, j)); + } + } + } + + public int getHash(int section, int position) { + return Objects.hash(section * -49612, getItem(section, position)); + } + public abstract int getSectionCount(); public abstract int getCountForSection(int section); @@ -483,6 +494,7 @@ public class FastScroll extends View { public static final int LETTER_TYPE = 0; public static final int DATE_TYPE = 1; + public boolean usePadding = true; private RectF rect = new RectF(); private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); @@ -516,11 +528,12 @@ public class FastScroll extends View { private boolean floatingDateVisible; private float floatingDateProgress; private int[] positionWithOffset = new int[2]; - boolean isVisible; + public boolean isVisible; float touchSlop; Drawable fastScrollShadowDrawable; Drawable fastScrollBackgroundDrawable; boolean isRtl; + public int topOffset; Runnable hideFloatingDateRunnable = new Runnable() { @Override @@ -545,9 +558,9 @@ public FastScroll(Context context, int type) { isRtl = false; letterPaint.setTextSize(AndroidUtilities.dp(13)); letterPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - paint2.setColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + paint2.setColor(Theme.getColor(Theme.key_windowBackgroundWhite, resourcesProvider)); fastScrollBackgroundDrawable = ContextCompat.getDrawable(context, R.drawable.calendar_date).mutate(); - fastScrollBackgroundDrawable.setColorFilter(new PorterDuffColorFilter(ColorUtils.blendARGB(Theme.getColor(Theme.key_windowBackgroundWhite), Color.WHITE, 0.1f), PorterDuff.Mode.MULTIPLY)); + fastScrollBackgroundDrawable.setColorFilter(new PorterDuffColorFilter(ColorUtils.blendARGB(Theme.getColor(Theme.key_windowBackgroundWhite, resourcesProvider), Color.WHITE, 0.1f), PorterDuff.Mode.MULTIPLY)); } for (int a = 0; a < 8; a++) { radii[a] = AndroidUtilities.dp(44); @@ -562,14 +575,14 @@ public FastScroll(Context context, int type) { } private void updateColors() { - inactiveColor = type == LETTER_TYPE ? Theme.getColor(Theme.key_fastScrollInactive) : ColorUtils.setAlphaComponent(Color.BLACK, (int) (255 * 0.4f)); - activeColor = Theme.getColor(Theme.key_fastScrollActive); + inactiveColor = type == LETTER_TYPE ? Theme.getColor(Theme.key_fastScrollInactive, resourcesProvider) : ColorUtils.setAlphaComponent(Color.BLACK, (int) (255 * 0.4f)); + activeColor = Theme.getColor(Theme.key_fastScrollActive, resourcesProvider); paint.setColor(inactiveColor); if (type == LETTER_TYPE) { - letterPaint.setColor(Theme.getColor(Theme.key_fastScrollText)); + letterPaint.setColor(Theme.getColor(Theme.key_fastScrollText, resourcesProvider)); } else { - letterPaint.setColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + letterPaint.setColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); } invalidate(); } @@ -578,7 +591,7 @@ private void updateColors() { boolean isMoving; long startTime; float visibilityAlpha; - float viewAlpha; + float viewAlpha = 1f; @Override public boolean onTouchEvent(MotionEvent event) { @@ -747,20 +760,21 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { @Override protected void onDraw(Canvas canvas) { - int y = getPaddingTop() + (int) Math.ceil((getMeasuredHeight() - getPaddingTop() - AndroidUtilities.dp(24 + 30)) * progress); + int topPadding = usePadding ? getPaddingTop() : topOffset; + int y = topPadding + (int) Math.ceil((getMeasuredHeight() - topPadding - AndroidUtilities.dp(24 + 30)) * progress); rect.set(scrollX, AndroidUtilities.dp(12) + y, scrollX + AndroidUtilities.dp(5), AndroidUtilities.dp(12 + 30) + y); if (type == LETTER_TYPE) { paint.setColor(ColorUtils.blendARGB(inactiveColor, activeColor, bubbleProgress)); canvas.drawRoundRect(rect, AndroidUtilities.dp(2), AndroidUtilities.dp(2), paint); } else { - paint.setColor(ColorUtils.blendARGB(Theme.getColor(Theme.key_windowBackgroundWhite), Color.WHITE, 0.1f)); + paint.setColor(ColorUtils.blendARGB(Theme.getColor(Theme.key_windowBackgroundWhite, resourcesProvider), Color.WHITE, 0.1f)); float cy = y + AndroidUtilities.dp(12 + 15); fastScrollShadowDrawable.setBounds(getMeasuredWidth() - fastScrollShadowDrawable.getIntrinsicWidth(), (int) (cy - fastScrollShadowDrawable.getIntrinsicHeight() / 2), getMeasuredWidth(), (int) (cy + fastScrollShadowDrawable.getIntrinsicHeight() / 2)); fastScrollShadowDrawable.draw(canvas); canvas.drawCircle(scrollX + AndroidUtilities.dp(8), y + AndroidUtilities.dp(12 + 15), AndroidUtilities.dp(24), paint); - paint.setColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + paint.setColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); canvas.save(); canvas.translate(scrollX + AndroidUtilities.dp(4), y + AndroidUtilities.dp(12 + 15 + 2 + 5) + AndroidUtilities.dp(2) * bubbleProgress); canvas.drawPath(arrowPath, paint); @@ -1041,7 +1055,9 @@ private void onPressItem(View cv, MotionEvent e) { final View view = cv; final int position = currentChildPosition; if (instantClick && position != -1) { - view.playSoundEffect(SoundEffectConstants.CLICK); + try { + view.playSoundEffect(SoundEffectConstants.CLICK); + } catch (Exception ignore) {} view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); if (onItemClickListener != null) { onItemClickListener.onItemClick(view, position); @@ -1058,7 +1074,9 @@ public void run() { if (view != null) { onChildPressed(view, 0, 0, false); if (!instantClick) { - view.playSoundEffect(SoundEffectConstants.CLICK); + try { + view.playSoundEffect(SoundEffectConstants.CLICK); + } catch (Exception ignore) {} view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); if (position != -1) { if (onItemClickListener != null) { @@ -1112,7 +1130,7 @@ public boolean onDown(MotionEvent e) { } @Override - public boolean hasDoubleTap() { + public boolean hasDoubleTap(MotionEvent e) { return onItemLongClickListenerExtended != null; } }); @@ -1455,6 +1473,10 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { private Paint backgroundPaint; protected void drawSectionBackground(Canvas canvas, int fromAdapterPosition, int toAdapterPosition, int color) { + if (toAdapterPosition < fromAdapterPosition) { + return; + } + int top = Integer.MAX_VALUE; int bottom = Integer.MIN_VALUE; @@ -1570,7 +1592,8 @@ public void setVerticalScrollBarEnabled(boolean verticalScrollBarEnabled) { protected void onMeasure(int widthSpec, int heightSpec) { super.onMeasure(widthSpec, heightSpec); if (fastScroll != null && fastScroll.getLayoutParams() != null) { - int height = getMeasuredHeight() - getPaddingTop() - getPaddingBottom(); + int topPadding = fastScroll.usePadding ? getPaddingTop() : fastScroll.topOffset; + int height = getMeasuredHeight() - topPadding - getPaddingBottom(); fastScroll.getLayoutParams().height = height; fastScroll.measure(MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(132), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); } @@ -1582,7 +1605,8 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); if (fastScroll != null) { selfOnLayout = true; - t += getPaddingTop(); + int topPadding = fastScroll.usePadding ? getPaddingTop() : fastScroll.topOffset; + t += topPadding; if (fastScroll.isRtl) { fastScroll.layout(0, t, fastScroll.getMeasuredWidth(), t + fastScroll.getMeasuredHeight()); } else { @@ -1849,14 +1873,22 @@ public void checkSection(boolean force) { } } - public void setListSelectorColor(int color) { - Theme.setSelectorDrawableColor(selectorDrawable, color, true); + public void setListSelectorColor(Integer color) { + Theme.setSelectorDrawableColor(selectorDrawable, color == null ? getThemedColor(Theme.key_listSelector) : color, true); } + private GenericProvider getSelectorColor; public Integer getSelectorColor(int position) { + if (getSelectorColor != null) { + return getSelectorColor.provide(position); + } return null; } + public void setItemSelectorColorProvider(GenericProvider getSelectorColor) { + this.getSelectorColor = getSelectorColor; + } + public void setOnItemClickListener(OnItemClickListener listener) { onItemClickListener = listener; } @@ -2264,7 +2296,7 @@ private void positionSelector(int position, View sel, boolean manageHotspot, flo Theme.setMaskDrawableRad(selectorDrawable, position == 0 ? topBottomSelectorRadius : 0, position == getAdapter().getItemCount() - 2 ? topBottomSelectorRadius : 0); } selectorRect.set(sel.getLeft(), sel.getTop(), sel.getRight(), sel.getBottom() - bottomPadding); - selectorRect.offset((int) sel.getTranslationX(), (int) sel.getTranslationY()); +// selectorRect.offset((int) sel.getTranslationX(), (int) sel.getTranslationY()); final boolean enabled = sel.isEnabled(); if (isChildViewEnabled != enabled) { @@ -2275,10 +2307,7 @@ private void positionSelector(int position, View sel, boolean manageHotspot, flo selectorDrawable.setVisible(false, false); selectorDrawable.setState(StateSet.NOTHING); } - Integer color = getSelectorColor(position); - if (color != null) { - setListSelectorColor(color); - } + setListSelectorColor(getSelectorColor(position)); selectorDrawable.setBounds(selectorRect); if (positionChanged) { if (getVisibility() == VISIBLE) { @@ -2492,28 +2521,38 @@ public Rect getSelectorRect() { return selectorRect; } + public void setTranslateSelector(boolean value) { + translateSelector = value; + } + @Override protected void dispatchDraw(Canvas canvas) { if (itemsEnterAnimator != null) { itemsEnterAnimator.dispatchDraw(); } - if (drawSelectorBehind && !selectorRect.isEmpty()) { + if (drawSelection && drawSelectorBehind && !selectorRect.isEmpty()) { selectorDrawable.setBounds(selectorRect); canvas.save(); - if (selectorTransformer != null) { + if (translateSelector && selectorTransformer != null) { selectorTransformer.accept(canvas); } + if (translateSelector && selectorView != null) { + canvas.translate(selectorView.getX() - selectorRect.left, selectorView.getY() - selectorRect.top); + } selectorDrawable.draw(canvas); canvas.restore(); } super.dispatchDraw(canvas); - if (!drawSelectorBehind && !selectorRect.isEmpty()) { + if (drawSelection && !drawSelectorBehind && !selectorRect.isEmpty()) { selectorDrawable.setBounds(selectorRect); canvas.save(); - if (selectorTransformer != null) { + if (translateSelector && selectorTransformer != null) { selectorTransformer.accept(canvas); } + if (translateSelector && selectorView != null) { + canvas.translate(selectorView.getX() - selectorRect.left, selectorView.getY() - selectorRect.top); + } selectorDrawable.draw(canvas); canvas.restore(); } @@ -2848,9 +2887,8 @@ public boolean isMultiselect() { return multiSelectionGesture; } - protected int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + protected int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } protected Drawable getThemedDrawable(String key) { @@ -2883,4 +2921,8 @@ public void setAccessibilityEnabled(boolean accessibilityEnabled) { public void setAllowStopHeaveOperations(boolean allowStopHeaveOperations) { this.allowStopHeaveOperations = allowStopHeaveOperations; } + + public void setDrawSelection(boolean drawSelection) { + this.drawSelection = drawSelection; + } } \ No newline at end of file diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ReplaceableIconDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ReplaceableIconDrawable.java index 6f9cab685b..3601c85101 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ReplaceableIconDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ReplaceableIconDrawable.java @@ -9,6 +9,7 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.view.View; +import android.widget.ImageView; import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; @@ -199,7 +200,9 @@ public void onAnimationRepeat(Animator animation) { } public void addView(View view) { - parentViews.add(view); + if (!parentViews.contains(view)) { + parentViews.add(view); + } } @Override @@ -211,4 +214,8 @@ public void invalidateSelf() { } } } + + public void removeView(View view) { + parentViews.remove(view); + } } \ No newline at end of file diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ReportAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ReportAlert.java index b3165e509c..cd3197bcb0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ReportAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ReportAlert.java @@ -37,11 +37,11 @@ public static class BottomSheetCell extends FrameLayout { private TextView textView; private LinearLayout linearLayout; - public BottomSheetCell(Context context) { + public BottomSheetCell(Context context, Theme.ResourcesProvider resourcesProvider) { super(context); background = new View(context); - background.setBackground(Theme.AdaptiveRipple.filledRect(Theme.key_featuredStickers_addButton, 4)); + background.setBackground(Theme.AdaptiveRipple.filledRectByKey(Theme.key_featuredStickers_addButton, 4)); addView(background, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, 0, 16, 16, 16, 16)); textView = new TextView(context); @@ -50,7 +50,7 @@ public BottomSheetCell(Context context) { textView.setGravity(Gravity.CENTER_HORIZONTAL); textView.setEllipsize(TextUtils.TruncateAt.END); textView.setGravity(Gravity.CENTER); - textView.setTextColor(Theme.getColor(Theme.key_featuredStickers_buttonText)); + textView.setTextColor(Theme.getColor(Theme.key_featuredStickers_buttonText, resourcesProvider)); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); addView(textView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); @@ -66,8 +66,8 @@ public void setText(CharSequence text) { } } - public ReportAlert(final Context context, int type) { - super(context, true); + public ReportAlert(final Context context, int type, Theme.ResourcesProvider resourcesProvider) { + super(context, true, resourcesProvider); setApplyBottomPadding(false); setApplyTopPadding(false); @@ -86,7 +86,7 @@ public ReportAlert(final Context context, int type) { TextView percentTextView = new TextView(context); percentTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); percentTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 24); - percentTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); + percentTextView.setTextColor(getThemedColor(Theme.key_dialogTextBlack)); if (type == AlertsCreator.REPORT_TYPE_SPAM) { percentTextView.setText(LocaleController.getString("ReportTitleSpam", R.string.ReportTitleSpam)); } else if (type == AlertsCreator.REPORT_TYPE_FAKE_ACCOUNT) { @@ -104,17 +104,17 @@ public ReportAlert(final Context context, int type) { TextView infoTextView = new TextView(context); infoTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - infoTextView.setTextColor(Theme.getColor(Theme.key_dialogTextGray3)); + infoTextView.setTextColor(getThemedColor(Theme.key_dialogTextGray3)); infoTextView.setGravity(Gravity.CENTER_HORIZONTAL); infoTextView.setText(LocaleController.getString("ReportInfo", R.string.ReportInfo)); frameLayout.addView(infoTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 30, 235, 30, 44)); editText = new EditTextBoldCursor(context); editText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); - editText.setHintTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteHintText)); - editText.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + editText.setHintTextColor(getThemedColor(Theme.key_windowBackgroundWhiteHintText)); + editText.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteBlackText)); editText.setBackgroundDrawable(null); - editText.setLineColors(getThemedColor(Theme.key_windowBackgroundWhiteInputField), getThemedColor(Theme.key_windowBackgroundWhiteInputFieldActivated), getThemedColor(Theme.key_windowBackgroundWhiteRedText3)); + editText.setLineColors(getThemedColor(Theme.key_windowBackgroundWhiteInputField), getThemedColor(Theme.key_windowBackgroundWhiteInputFieldActivated), getThemedColor(Theme.key_text_RedRegular)); editText.setMaxLines(1); editText.setLines(1); editText.setPadding(0, 0, 0, 0); @@ -123,7 +123,7 @@ public ReportAlert(final Context context, int type) { editText.setInputType(InputType.TYPE_TEXT_FLAG_CAP_SENTENCES | InputType.TYPE_TEXT_FLAG_MULTI_LINE | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT); editText.setImeOptions(EditorInfo.IME_ACTION_DONE); editText.setHint(LocaleController.getString("ReportHint", R.string.ReportHint)); - editText.setCursorColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + editText.setCursorColor(getThemedColor(Theme.key_windowBackgroundWhiteBlackText)); editText.setCursorSize(AndroidUtilities.dp(20)); editText.setCursorWidth(1.5f); editText.setOnEditorActionListener((textView, i, keyEvent) -> { @@ -135,7 +135,7 @@ public ReportAlert(final Context context, int type) { }); frameLayout.addView(editText, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 36, Gravity.LEFT | Gravity.TOP, 17, 305, 17, 0)); - clearButton = new BottomSheetCell(context); + clearButton = new BottomSheetCell(context, resourcesProvider); clearButton.setBackground(null); clearButton.setText(LocaleController.getString("ReportSend", R.string.ReportSend)); clearButton.background.setOnClickListener(v -> { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/RoundVideoPlayingDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/RoundVideoPlayingDrawable.java index f49f6c10d9..07624ecbb3 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/RoundVideoPlayingDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/RoundVideoPlayingDrawable.java @@ -144,8 +144,7 @@ public int getIntrinsicHeight() { return AndroidUtilities.dp(12); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ScaleStateListAnimator.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ScaleStateListAnimator.java new file mode 100644 index 0000000000..160df0ebe9 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ScaleStateListAnimator.java @@ -0,0 +1,43 @@ +package org.telegram.ui.Components; + +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.StateListAnimator; +import android.os.Build; +import android.view.View; +import android.view.animation.OvershootInterpolator; + +public class ScaleStateListAnimator { + + public static void apply(View view) { + apply(view, .1f, 1.5f); + } + + public static void apply(View view, float scale, float tension) { + if (view == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + return; + } + + AnimatorSet pressedAnimator = new AnimatorSet(); + pressedAnimator.playTogether( + ObjectAnimator.ofFloat(view, View.SCALE_X, 1f - scale), + ObjectAnimator.ofFloat(view, View.SCALE_Y, 1f - scale) + ); + pressedAnimator.setDuration(80); + + AnimatorSet defaultAnimator = new AnimatorSet(); + defaultAnimator.playTogether( + ObjectAnimator.ofFloat(view, View.SCALE_X, 1f), + ObjectAnimator.ofFloat(view, View.SCALE_Y, 1f) + ); + defaultAnimator.setInterpolator(new OvershootInterpolator(tension)); + defaultAnimator.setDuration(350); + + StateListAnimator scaleStateListAnimator = new StateListAnimator(); + + scaleStateListAnimator.addState(new int[]{android.R.attr.state_pressed}, pressedAnimator); + scaleStateListAnimator.addState(new int[0], defaultAnimator); + + view.setStateListAnimator(scaleStateListAnimator); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ScrollSlidingTabStrip.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ScrollSlidingTabStrip.java index c6881f24e6..4db532954d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ScrollSlidingTabStrip.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ScrollSlidingTabStrip.java @@ -26,7 +26,6 @@ import android.transition.TransitionSet; import android.transition.TransitionValues; import android.util.SparseArray; -import android.util.TypedValue; import android.view.Gravity; import android.view.HapticFeedbackConstants; import android.view.MotionEvent; @@ -37,7 +36,8 @@ import android.widget.HorizontalScrollView; import android.widget.ImageView; import android.widget.LinearLayout; -import android.widget.TextView; + +import androidx.core.graphics.ColorUtils; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.DocumentObject; @@ -47,7 +47,6 @@ import org.telegram.messenger.LiteMode; import org.telegram.messenger.MessageObject; import org.telegram.messenger.R; -import org.telegram.messenger.SharedConfig; import org.telegram.messenger.SvgHelper; import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; @@ -59,6 +58,8 @@ public class ScrollSlidingTabStrip extends HorizontalScrollView { + private int imageReceiversPlayingNum = 1; + public interface ScrollSlidingTabStripDelegate { void onPageSelected(int page); } @@ -349,7 +350,7 @@ public void addStickerTab(TLRPC.Chat chat) { avatarDrawable.setInfo(chat); BackupImageView imageView = stickerTabView.imageView; - imageView.setLayerNum(1); + imageView.setLayerNum(imageReceiversPlayingNum); imageView.setForUserOrChat(chat, avatarDrawable); imageView.setAspectFit(true); @@ -407,6 +408,7 @@ public View addStickerTab(TLObject thumb, TLRPC.Document sticker, TLRPC.TL_messa tabsContainer.addView(tab, position); } + tab.imageView.setLayerNum(imageReceiversPlayingNum); tab.isChatSticker = false; tab.setTag(thumb); tab.setTag(R.id.index_tag, position); @@ -785,7 +787,7 @@ protected void dispatchDraw(Canvas canvas) { h *= AndroidUtilities.lerp(1f, 0.55f, expandProgressInterpolated); tabBounds.set(cx - w / 2, cy - h / 2, cx + w / 2, cy + h / 2); - selectorPaint.setColor(0x2effffff & getThemedColor(Theme.key_chat_emojiPanelIcon)); + selectorPaint.setColor(ColorUtils.setAlphaComponent(getThemedColor(Theme.key_chat_emojiPanelIcon), 0x2e)); selectorPaint.setAlpha((int) (selectorPaint.getAlpha() * selectedAlpha)); canvas.drawRoundRect(tabBounds, AndroidUtilities.dp(8), AndroidUtilities.dp(8), selectorPaint); } @@ -1064,8 +1066,12 @@ public void setDragEnabled(boolean enabled) { dragEnabled = enabled; } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); + } + + + public void setImageReceiversLayerNum(int playingImages) { + imageReceiversPlayingNum = playingImages; } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ScrollSlidingTextTabStrip.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ScrollSlidingTextTabStrip.java index 3b2143ffd6..e3011ff288 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ScrollSlidingTextTabStrip.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ScrollSlidingTextTabStrip.java @@ -69,10 +69,10 @@ default void onSamePageSelected() { private GradientDrawable selectorDrawable; - private String tabLineColorKey = Theme.key_actionBarTabLine; - private String activeTextColorKey = Theme.key_actionBarTabActiveText; - private String unactiveTextColorKey = Theme.key_actionBarTabUnactiveText; - private String selectorColorKey = Theme.key_actionBarTabSelector; + private int tabLineColorKey = Theme.key_actionBarTabLine; + private int activeTextColorKey = Theme.key_actionBarTabActiveText; + private int unactiveTextColorKey = Theme.key_actionBarTabUnactiveText; + private int selectorColorKey = Theme.key_actionBarTabSelector; private CubicBezierInterpolator interpolator = CubicBezierInterpolator.EASE_OUT_QUINT; @@ -91,6 +91,8 @@ default void onSamePageSelected() { private float indicatorXAnimationDx; private float indicatorWidthAnimationDx; + public long animationDuration = 200; + private Runnable animationRunnable = new Runnable() { @Override public void run() { @@ -102,7 +104,7 @@ public void run() { if (dt > 17) { dt = 17; } - animationTime += dt / 200.0f; + animationTime += dt / (float) animationDuration; setAnimationIdicatorProgress(interpolator.getInterpolation(animationTime)); if (animationTime > 1.0f) { animationTime = 1.0f; @@ -276,50 +278,17 @@ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setSelected(selectedTabId == id); } - }; + }; tab.setWillNotDraw(false); tab.setGravity(Gravity.CENTER); - tab.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.getColor(selectorColorKey, resourcesProvider), 3)); + tab.setBackground(Theme.createSelectorDrawable(Theme.multAlpha(Theme.getColor(activeTextColorKey, resourcesProvider), .15f), 3)); tab.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); tab.setSingleLine(true); tab.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); tab.setPadding(AndroidUtilities.dp(16), 0, AndroidUtilities.dp(16), 0); tab.setOnClickListener(v -> { int position1 = tabsContainer.indexOfChild(v); - if (position1 < 0) { - return; - } - if (position1 == currentPosition && delegate != null) { - delegate.onSamePageSelected(); - return; - } - boolean scrollingForward = currentPosition < position1; - scrollingToChild = -1; - previousPosition = currentPosition; - currentPosition = position1; - selectedTabId = id; - - if (animatingIndicator) { - AndroidUtilities.cancelRunOnUIThread(animationRunnable); - animatingIndicator = false; - } - - animationTime = 0; - animatingIndicator = true; - animateIndicatorStartX = indicatorX; - animateIndicatorStartWidth = indicatorWidth; - - TextView nextChild = (TextView) v; - animateIndicatorToWidth = getChildWidth(nextChild); - animateIndicatorToX = nextChild.getLeft() + (nextChild.getMeasuredWidth() - animateIndicatorToWidth) / 2; - setEnabled(false); - - AndroidUtilities.runOnUIThread(animationRunnable, 16); - - if (delegate != null) { - delegate.onPageSelected(id, scrollingForward); - } - scrollToChild(position1); + scrollTo(id, position1, v); }); } tab.setText(text); @@ -332,6 +301,49 @@ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { } } + public void scrollTo(int pageId, int position1, View v) { + if (position1 < 0 || v == null && animatingIndicator) { + return; + } + if (position1 == currentPosition && delegate != null) { + delegate.onSamePageSelected(); + return; + } + boolean scrollingForward = currentPosition < position1; + scrollingToChild = -1; + previousPosition = currentPosition; + currentPosition = position1; + selectedTabId = pageId; + + if (animatingIndicator) { + AndroidUtilities.cancelRunOnUIThread(animationRunnable); + animatingIndicator = false; + } + + animationTime = 0; + animatingIndicator = true; + animateIndicatorStartX = indicatorX; + animateIndicatorStartWidth = indicatorWidth; + + if (v != null) { + TextView nextChild = (TextView) v; + animateIndicatorToWidth = getChildWidth(nextChild); + animateIndicatorToX = nextChild.getLeft() + (nextChild.getMeasuredWidth() - animateIndicatorToWidth) / 2; + } + setEnabled(false); + + AndroidUtilities.runOnUIThread(animationRunnable, 16); + + if (delegate != null) { + delegate.onPageSelected(pageId, scrollingForward); + } + scrollToChild(position1); + } + + public void scrollTo(int pageId) { + scrollTo(pageId, idToPosition.get(pageId), null); + } + public void finishAddingTabs() { int count = tabsContainer.getChildCount(); for (int a = 0; a < count; a++) { @@ -344,7 +356,7 @@ public void finishAddingTabs() { } } - public void setColors(String line, String active, String unactive, String selector) { + public void setColors(int line, int active, int unactive, int selector) { tabLineColorKey = line; activeTextColorKey = active; unactiveTextColorKey = unactive; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SearchCounterView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SearchCounterView.java index a693c5be92..8c418cea82 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/SearchCounterView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SearchCounterView.java @@ -43,7 +43,7 @@ public class SearchCounterView extends View { private int countWidth; private int textColor; - private String textColorKey = Theme.key_chat_searchPanelText; + private int textColorKey = Theme.key_chat_searchPanelText; int lastH; int gravity = Gravity.CENTER; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SearchDownloadsContainer.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SearchDownloadsContainer.java index 17d16234ad..f85753b618 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/SearchDownloadsContainer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SearchDownloadsContainer.java @@ -3,21 +3,13 @@ import android.app.Activity; import android.content.Context; import android.graphics.Canvas; -import android.graphics.Color; -import android.os.Build; import android.text.TextUtils; -import android.util.TypedValue; -import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; -import android.widget.LinearLayout; -import android.widget.TextView; import androidx.annotation.NonNull; -import androidx.core.graphics.ColorUtils; -import androidx.core.widget.NestedScrollView; import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.DiffUtil; import androidx.recyclerview.widget.ItemTouchHelper; @@ -38,9 +30,7 @@ import org.telegram.messenger.Utilities; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.BaseFragment; -import org.telegram.ui.ActionBar.BottomSheet; import org.telegram.ui.ActionBar.Theme; -import org.telegram.ui.CacheControlActivity; import org.telegram.ui.Cells.GraySectionCell; import org.telegram.ui.Cells.SharedAudioCell; import org.telegram.ui.Cells.SharedDocumentCell; @@ -530,12 +520,9 @@ public void onClick(View view) { }); } } else if (position == recentFilesHeader) { - graySectionCell.setText(LocaleController.getString("RecentlyDownloaded", R.string.RecentlyDownloaded), LocaleController.getString("Settings", R.string.Settings), new OnClickListener() { - @Override - public void onClick(View view) { - showSettingsDialog(); - } - }); + graySectionCell.setText(LocaleController.getString("RecentlyDownloaded", R.string.RecentlyDownloaded), LocaleController.getString("Settings", R.string.Settings), + view -> DownloadsInfoBottomSheet.show(parentActivity, parentFragment) + ); } } else { MessageObject messageObject = getMessage(position); @@ -596,82 +583,6 @@ public boolean isEnabled(RecyclerView.ViewHolder holder) { } } - private void showSettingsDialog() { - if (parentFragment == null || parentActivity == null) { - return; - } - BottomSheet bottomSheet = new BottomSheet(parentActivity, false); - Context context = parentFragment.getParentActivity(); - LinearLayout linearLayout = new LinearLayout(context); - linearLayout.setOrientation(LinearLayout.VERTICAL); - - StickerImageView imageView = new StickerImageView(context, currentAccount); - imageView.setStickerNum(9); - imageView.getImageReceiver().setAutoRepeat(1); - linearLayout.addView(imageView, LayoutHelper.createLinear(144, 144, Gravity.CENTER_HORIZONTAL, 0, 16, 0, 0)); - - TextView title = new TextView(context); - title.setGravity(Gravity.CENTER_HORIZONTAL); - title.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); - title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 24); - title.setText(LocaleController.getString("DownloadedFiles", R.string.DownloadedFiles)); - linearLayout.addView(title, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 21, 30, 21, 0)); - - TextView description = new TextView(context); - description.setGravity(Gravity.CENTER_HORIZONTAL); - description.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); - description.setTextColor(Theme.getColor(Theme.key_dialogTextHint)); - description.setText(LocaleController.formatString("DownloadedFilesMessage", R.string.DownloadedFilesMessage)); - linearLayout.addView(description, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 21, 15, 21, 16)); - - - TextView buttonTextView = new TextView(context); - buttonTextView.setPadding(AndroidUtilities.dp(34), 0, AndroidUtilities.dp(34), 0); - buttonTextView.setGravity(Gravity.CENTER); - buttonTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - buttonTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - buttonTextView.setText(LocaleController.getString("ManageDeviceStorage", R.string.ManageDeviceStorage)); - - buttonTextView.setTextColor(Theme.getColor(Theme.key_featuredStickers_buttonText)); - buttonTextView.setBackgroundDrawable(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(6), Theme.getColor(Theme.key_featuredStickers_addButton), ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_windowBackgroundWhite), 120))); - - linearLayout.addView(buttonTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, 0, 16, 15, 16, 16)); - - - TextView buttonTextView2 = new TextView(context); - buttonTextView2.setPadding(AndroidUtilities.dp(34), 0, AndroidUtilities.dp(34), 0); - buttonTextView2.setGravity(Gravity.CENTER); - buttonTextView2.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - buttonTextView2.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - buttonTextView2.setText(LocaleController.getString("ClearDownloadsList", R.string.ClearDownloadsList)); - - buttonTextView2.setTextColor(Theme.getColor(Theme.key_featuredStickers_addButton)); - buttonTextView2.setBackgroundDrawable(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(6), Color.TRANSPARENT, ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_featuredStickers_addButton), 120))); - - linearLayout.addView(buttonTextView2, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, 0, 16, 0, 16, 16)); - - NestedScrollView scrollView = new NestedScrollView(context); - scrollView.addView(linearLayout); - bottomSheet.setCustomView(scrollView); - bottomSheet.show(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - AndroidUtilities.setLightStatusBar(bottomSheet.getWindow(), !Theme.isCurrentThemeDark()); - AndroidUtilities.setLightNavigationBar(bottomSheet.getWindow(), !Theme.isCurrentThemeDark()); - } - - buttonTextView.setOnClickListener(view -> { - bottomSheet.dismiss(); - if (parentFragment != null) { - parentFragment.presentFragment(new CacheControlActivity()); - } - }); - buttonTextView2.setOnClickListener(view -> { - bottomSheet.dismiss(); - DownloadController.getInstance(currentAccount).clearRecentDownloadedFiles(); - }); - //parentFragment.showDialog(bottomSheet); - } - @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SearchField.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SearchField.java index 140b1057f5..1b73f95e6c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/SearchField.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SearchField.java @@ -198,8 +198,7 @@ public void getThemeDescriptions(List descriptions) { descriptions.add(new ThemeDescription(searchEditText, ThemeDescription.FLAG_CURSORCOLOR, null, null, null, null, Theme.key_featuredStickers_addedIcon)); } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SearchViewPager.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SearchViewPager.java index 41442d18ce..9816d28362 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/SearchViewPager.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SearchViewPager.java @@ -31,6 +31,7 @@ import org.telegram.messenger.MessageObject; import org.telegram.messenger.MessagesController; import org.telegram.messenger.R; +import org.telegram.messenger.SendMessagesHelper; import org.telegram.messenger.UserConfig; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.ActionBarMenu; @@ -500,7 +501,7 @@ public void onActionBarItemClick(int id) { AlertDialog alertDialog = builder.show(); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } else if (id == speedItemId) { @@ -535,7 +536,7 @@ public void onActionBarItemClick(int id) { for (int a = 0; a < dids.size(); a++) { long did = dids.get(a).dialogId; if (message != null) { - AccountInstance.getInstance(currentAccount).getSendMessagesHelper().sendMessage(message.toString(), did, null, null, null, true, null, null, null, true, 0, null, false); + AccountInstance.getInstance(currentAccount).getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of(message.toString(), did, null, null, null, true, null, null, null, true, 0, null, false)); } AccountInstance.getInstance(currentAccount).getSendMessagesHelper().sendMessage(fmessages, did, false,false, true, 0); } @@ -948,7 +949,7 @@ public String getItemTitle(int position) { } else if (items.get(position).type == DOWNLOADS_TYPE) { return LocaleController.getString("DownloadsTabs", R.string.DownloadsTabs); } else { - return FiltersView.filters[items.get(position).filterIndex].title; + return FiltersView.filters[items.get(position).filterIndex].getTitle(); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SeekBarView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SeekBarView.java index a6bfff9e53..901d641101 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/SeekBarView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SeekBarView.java @@ -805,8 +805,7 @@ public SeekBarAccessibilityDelegate getSeekBarAccessibilityDelegate() { return seekBarAccessibilityDelegate; } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ShareAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ShareAlert.java index 894caa3825..1c46e98c8e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ShareAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ShareAlert.java @@ -187,6 +187,11 @@ public class ShareAlert extends BottomSheet implements NotificationCenter.Notifi private ArrayList recentSearchObjects = new ArrayList<>(); private LongSparseArray recentSearchObjectsById = new LongSparseArray<>(); private final Theme.ResourcesProvider resourcesProvider; + TLRPC.StoryItem storyItem; + + public void setStoryToShare(TLRPC.StoryItem storyItem) { + this.storyItem = storyItem; + } public interface ShareAlertDelegate { default void didShare() { @@ -1203,7 +1208,7 @@ public void getItemOffsets(android.graphics.Rect outRect, View view, RecyclerVie FlickerLoadingView flickerLoadingView = new FlickerLoadingView(context, resourcesProvider); flickerLoadingView.setViewType(FlickerLoadingView.SHARE_ALERT_TYPE); if (darkTheme) { - flickerLoadingView.setColors(Theme.key_voipgroup_inviteMembersBackground, Theme.key_voipgroup_searchBackground, null); + flickerLoadingView.setColors(Theme.key_voipgroup_inviteMembersBackground, Theme.key_voipgroup_searchBackground, -1); } searchEmptyView = new StickerEmptyView(context, flickerLoadingView, StickerEmptyView.STICKER_TYPE_SEARCH, resourcesProvider); searchEmptyView.addView(flickerLoadingView, 0); @@ -1398,7 +1403,7 @@ protected void onLineCountChanged(int oldLineCount, int newLineCount) { protected void showPopup(int show) { super.showPopup(show); if (darkTheme) { - navBarColorKey = null; + navBarColorKey = -1; AndroidUtilities.setNavigationBarColor(ShareAlert.this.getWindow(), ShareAlert.this.getThemedColor(Theme.key_windowBackgroundGray), true, color -> { ShareAlert.this.setOverlayNavBarColor(navBarColor = color); }); @@ -1409,7 +1414,7 @@ protected void showPopup(int show) { public void hidePopup(boolean byBackButton) { super.hidePopup(byBackButton); if (darkTheme) { - navBarColorKey = null; + navBarColorKey = -1; AndroidUtilities.setNavigationBarColor(ShareAlert.this.getWindow(), ShareAlert.this.getThemedColor(Theme.key_voipgroup_inviteMembersBackground), true, color -> { ShareAlert.this.setOverlayNavBarColor(navBarColor = color); }); @@ -1973,7 +1978,7 @@ protected void sendInternal(boolean withSound) { result = SendMessagesHelper.getInstance(currentAccount).sendMessage(sendingMessageObjects, key, !showSendersName, false, withSound, 0); } if (frameLayout2.getTag() != null && commentTextView.length() > 0) { - SendMessagesHelper.getInstance(currentAccount).sendMessage(text[0] == null ? null : text[0].toString(), key, replyTopMsg, replyTopMsg, null, true, entities, null, null, withSound, 0, null, false); + SendMessagesHelper.getInstance(currentAccount).sendMessage(SendMessagesHelper.SendMessageParams.of(text[0] == null ? null : text[0].toString(), key, replyTopMsg, replyTopMsg, null, true, entities, null, null, withSound, 0, null, false)); } if (!NekoConfig.sendCommentAfterForward.Bool()) { // send fwd message second. @@ -2007,19 +2012,41 @@ protected void sendInternal(boolean withSound) { } else { num = 0; } - if (sendingText[num] != null) { + if (storyItem != null) { + for (int a = 0; a < selectedDialogs.size(); a++) { + long key = selectedDialogs.keyAt(a); + TLRPC.TL_forumTopic topic = selectedDialogTopics.get(selectedDialogs.get(key)); + MessageObject replyTopMsg = topic != null ? new MessageObject(currentAccount, topic.topicStartMessage, false, false) : null; + + SendMessagesHelper.SendMessageParams params; + if (storyItem == null) { + if (frameLayout2.getTag() != null && commentTextView.length() > 0) { + params = SendMessagesHelper.SendMessageParams.of(text[0] == null ? null : text[0].toString(), key, null, replyTopMsg, null, true, entities, null, null, withSound, 0, null, false); + } else { + params = SendMessagesHelper.SendMessageParams.of(sendingText[num], key, null, replyTopMsg, null, true, null, null, null, withSound, 0, null, false); + } + } else { + if (frameLayout2.getTag() != null && commentTextView.length() > 0 && text[0] != null) { + SendMessagesHelper.getInstance(currentAccount).sendMessage(SendMessagesHelper.SendMessageParams.of(text[0].toString(), key, null, replyTopMsg, null, true, null, null, null, withSound, 0, null, false)); + } + params = SendMessagesHelper.SendMessageParams.of(null, key, null, replyTopMsg, null, true, null, null, null, withSound, 0, null, false); + params.sendingStory = storyItem; + } + SendMessagesHelper.getInstance(currentAccount).sendMessage(params); + } + } else if (sendingText[num] != null) { for (int a = 0; a < selectedDialogs.size(); a++) { long key = selectedDialogs.keyAt(a); TLRPC.TL_forumTopic topic = selectedDialogTopics.get(selectedDialogs.get(key)); MessageObject replyTopMsg = topic != null ? new MessageObject(currentAccount, topic.topicStartMessage, false, false) : null; if (NekoConfig.sendCommentAfterForward.Bool()) { - SendMessagesHelper.getInstance(currentAccount).sendMessage(sendingText[num], key, null, null, null, true, null, null, null, true, 0, null, false); + SendMessagesHelper.getInstance(currentAccount).sendMessage(SendMessagesHelper.SendMessageParams.of(sendingText[num], key, null, replyTopMsg, null, true, null, null, null, withSound, 0, null, false)); } if (frameLayout2.getTag() != null && commentTextView.length() > 0) { - SendMessagesHelper.getInstance(currentAccount).sendMessage(text[0] == null ? null : text[0].toString(), key, null, replyTopMsg, null, true, entities, null, null, true, 0, null, false); + SendMessagesHelper.getInstance(currentAccount).sendMessage(SendMessagesHelper.SendMessageParams.of(text[0] == null ? null : text[0].toString(), key, null, replyTopMsg, null, true, entities, null, null, withSound, 0, null, false)); } if (!NekoConfig.sendCommentAfterForward.Bool()) { - SendMessagesHelper.getInstance(currentAccount).sendMessage(sendingText[num], key, null, replyTopMsg, null, true, null, null, null, true, 0, null, false); + SendMessagesHelper.getInstance(currentAccount).sendMessage(SendMessagesHelper.SendMessageParams.of(sendingText[num], key, null, replyTopMsg, null, true, null, null, null, withSound, 0, null, false)); } } onSend(selectedDialogs, 1, selectedDialogTopics.get(selectedDialogs.valueAt(0))); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SharedMediaLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SharedMediaLayout.java index 5360196dfa..7f65fde82b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/SharedMediaLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SharedMediaLayout.java @@ -21,8 +21,11 @@ import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; +import android.text.Layout; import android.text.SpannableStringBuilder; import android.text.Spanned; +import android.text.StaticLayout; +import android.text.TextPaint; import android.text.TextUtils; import android.text.style.ForegroundColorSpan; import android.transition.ChangeBounds; @@ -57,6 +60,7 @@ import androidx.recyclerview.widget.RecyclerView; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.ChatObject; import org.telegram.messenger.DialogObject; @@ -71,6 +75,7 @@ import org.telegram.messenger.MessagesStorage; import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; +import org.telegram.messenger.SendMessagesHelper; import org.telegram.messenger.SharedConfig; import org.telegram.messenger.UserObject; import org.telegram.messenger.Utilities; @@ -111,6 +116,10 @@ import org.telegram.ui.DialogsActivity; import org.telegram.ui.PhotoViewer; import org.telegram.ui.ProfileActivity; +import org.telegram.ui.Stories.StoriesController; +import org.telegram.ui.Stories.StoriesListPlaceProvider; +import org.telegram.ui.Stories.StoriesUtilities; +import org.telegram.ui.Stories.UserListPoller; import java.util.ArrayList; import java.util.Collections; @@ -126,6 +135,17 @@ @SuppressWarnings("unchecked") public class SharedMediaLayout extends FrameLayout implements NotificationCenter.NotificationCenterDelegate { + public static final int TAB_PHOTOVIDEO = 0; + public static final int TAB_FILES = 1; + public static final int TAB_VOICE = 2; + public static final int TAB_LINKS = 3; + public static final int TAB_AUDIO = 4; + public static final int TAB_GIF = 5; + public static final int TAB_COMMON_GROUPS = 6; + public static final int TAB_GROUPUSERS = 7; + public static final int TAB_STORIES = 8; + public static final int TAB_ARCHIVED_STORIES = 9; + public static final int FILTER_PHOTOS_AND_VIDEOS = 0; public static final int FILTER_PHOTOS_ONLY = 1; public static final int FILTER_VIDEOS_ONLY = 2; @@ -173,7 +193,8 @@ public boolean dispatchFastScrollEvent(MotionEvent ev) { private UndoView undoView; public boolean checkPinchToZoom(MotionEvent ev) { - if (mediaPages[0].selectedType != 0 || getParent() == null) { + final int selectedType = mediaPages[0].selectedType; + if (selectedType != TAB_PHOTOVIDEO && selectedType != TAB_STORIES && selectedType != TAB_ARCHIVED_STORIES || getParent() == null) { return false; } if (photoVideoChangeColumnsAnimation && !isInPinchToZoomTouchMode) { @@ -242,13 +263,21 @@ public boolean checkPinchToZoom(MotionEvent ev) { } if (photoVideoChangeColumnsProgress == 1f || photoVideoChangeColumnsProgress == 0f) { + final RecyclerView.Adapter adapter; + if (changeColumnsTab == TAB_STORIES) { + adapter = storiesAdapter; + } else if (changeColumnsTab == TAB_ARCHIVED_STORIES) { + adapter = archivedStoriesAdapter; + } else { + adapter = photoVideoAdapter; + } if (photoVideoChangeColumnsProgress == 1f) { int newRow = (int) Math.ceil(pinchCenterPosition / (float) animateToColumnsCount); int columnWidth = (int) (mediaPages[0].listView.getMeasuredWidth() / (float) animateToColumnsCount); int newColumn = (int) ((startedTrackingX / (float) (mediaPages[0].listView.getMeasuredWidth() - columnWidth)) * (animateToColumnsCount - 1)); int newPosition = newRow * animateToColumnsCount + newColumn; - if (newPosition >= photoVideoAdapter.getItemCount()) { - newPosition = photoVideoAdapter.getItemCount() - 1; + if (newPosition >= adapter.getItemCount()) { + newPosition = adapter.getItemCount() - 1; } pinchCenterPosition = newPosition; } @@ -294,7 +323,8 @@ private void selectPinchPosition(int pinchCenterX, int pinchCenterY) { if (delegate.canSearchMembers()) { if (pinchCenterPosition == -1) { float x = Math.min(1, Math.max(pinchCenterX / (float) mediaPages[0].listView.getMeasuredWidth(), 0)); - pinchCenterPosition = (int) (mediaPages[0].layoutManager.findFirstVisibleItemPosition() + (mediaColumnsCount - 1) * x); + final int ci = mediaPages[0].selectedType == TAB_STORIES || mediaPages[0].selectedType == TAB_ARCHIVED_STORIES ? 1 : 0; + pinchCenterPosition = (int) (mediaPages[0].layoutManager.findFirstVisibleItemPosition() + (mediaColumnsCount[ci] - 1) * x); pinchCenterOffset = 0; } } @@ -354,8 +384,8 @@ private static class MediaPage extends FrameLayout { public long lastCheckScrollTime; public boolean fastScrollEnabled; public ObjectAnimator fastScrollAnimator; - private BlurredRecyclerView listView; - private BlurredRecyclerView animationSupportingListView; + private InternalListView listView; + private InternalListView animationSupportingListView; private GridLayoutManager animationSupportingLayoutManager; private FlickerLoadingView progressView; private StickerEmptyView emptyView; @@ -393,6 +423,9 @@ protected void dispatchDraw(Canvas canvas) { RecyclerListView.FastScroll fastScroll = listView.getFastScroll(); if (fastScroll != null) { float y = fastScroll.getScrollBarY() + AndroidUtilities.dp(36); + if (selectedType == TAB_ARCHIVED_STORIES) { + y += AndroidUtilities.dp(64); + } float x = (getMeasuredWidth() - fastScrollHintView.getMeasuredWidth() - AndroidUtilities.dp(16)); fastScrollHintView.setPivotX(fastScrollHintView.getMeasuredWidth()); fastScrollHintView.setPivotY(0); @@ -408,6 +441,15 @@ protected void dispatchDraw(Canvas canvas) { } + public float getPhotoVideoOptionsAlpha(float progress) { + float alpha = 0; + if (mediaPages[1] != null && (mediaPages[1].selectedType == TAB_PHOTOVIDEO || mediaPages[1].selectedType == TAB_STORIES || mediaPages[1].selectedType == TAB_ARCHIVED_STORIES)) + alpha += progress; + if (mediaPages[0] != null && (mediaPages[0].selectedType == TAB_PHOTOVIDEO || mediaPages[0].selectedType == TAB_STORIES || mediaPages[0].selectedType == TAB_ARCHIVED_STORIES)) + alpha += 1f - progress; + return alpha; + } + public void updateFastScrollVisibility(MediaPage mediaPage, boolean animated) { boolean show = mediaPage.fastScrollEnabled && isPinnedToTop; View view = mediaPage.listView.getFastScroll(); @@ -455,6 +497,10 @@ public void updateFastScrollVisibility(MediaPage mediaPage, boolean animated) { private GifAdapter gifAdapter; private CommonGroupsAdapter commonGroupsAdapter; private ChatUsersAdapter chatUsersAdapter; + private StoriesAdapter storiesAdapter; + private StoriesAdapter animationSupportingStoriesAdapter; + private StoriesAdapter archivedStoriesAdapter; + private StoriesAdapter animationSupportingArchivedStoriesAdapter; private MediaSearchAdapter documentsSearchAdapter; private MediaSearchAdapter audioSearchAdapter; private MediaSearchAdapter linksSearchAdapter; @@ -503,6 +549,7 @@ public void updateFastScrollVisibility(MediaPage mediaPage, boolean animated) { private boolean scrolling; private long mergeDialogId; private TLRPC.ChatFull info; + private TLRPC.UserFull userInfo; private AnimatorSet tabsAnimation; private boolean tabsAnimationInProgress; @@ -511,9 +558,13 @@ public void updateFastScrollVisibility(MediaPage mediaPage, boolean animated) { private long dialog_id; public boolean scrollingByUser; - private int mediaColumnsCount = 3; + private boolean allowStoriesSingleColumn = false; + private boolean storiesColumnsCountSet = false; + private int mediaColumnsCount[] = new int[] { 3, 3 }; private float photoVideoChangeColumnsProgress; private boolean photoVideoChangeColumnsAnimation; + private int changeColumnsTab; + private int animationSupportingSortedCellsOffset; private ArrayList animationSupportingSortedCells = new ArrayList<>(); private int animateToColumnsCount; @@ -528,10 +579,10 @@ public interface SharedMediaPreloaderDelegate { public static class SharedMediaPreloader implements NotificationCenter.NotificationCenterDelegate { - private int[] mediaCount = new int[]{-1, -1, -1, -1, -1, -1, -1, -1}; - private int[] mediaMergeCount = new int[]{-1, -1, -1, -1, -1, -1, -1, -1}; - private int[] lastMediaCount = new int[]{-1, -1, -1, -1, -1, -1, -1, -1}; - private int[] lastLoadMediaCount = new int[]{-1, -1, -1, -1, -1, -1, -1, -1}; + private int[] mediaCount = new int[]{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + private int[] mediaMergeCount = new int[]{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + private int[] lastMediaCount = new int[]{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + private int[] lastLoadMediaCount = new int[]{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; private SharedMediaData[] sharedMediaData; private long dialogId; private int topicId; @@ -573,6 +624,7 @@ public SharedMediaPreloader(BaseFragment fragment) { notificationCenter.addObserver(this, NotificationCenter.replaceMessagesObjects); notificationCenter.addObserver(this, NotificationCenter.chatInfoDidLoad); notificationCenter.addObserver(this, NotificationCenter.fileLoaded); + notificationCenter.addObserver(this, NotificationCenter.storiesListUpdated); } public void addDelegate(SharedMediaPreloaderDelegate delegate) { @@ -598,6 +650,7 @@ public void onDestroy(BaseFragment fragment) { notificationCenter.removeObserver(this, NotificationCenter.replaceMessagesObjects); notificationCenter.removeObserver(this, NotificationCenter.chatInfoDidLoad); notificationCenter.removeObserver(this, NotificationCenter.fileLoaded); + notificationCenter.removeObserver(this, NotificationCenter.storiesListUpdated); } public int[] getLastMediaCount() { @@ -1016,6 +1069,8 @@ public PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageObj } }; + private float shiftDp = -5; + public static class SharedMediaData { public ArrayList messages = new ArrayList<>(); public SparseArray[] messagesDict = new SparseArray[]{new SparseArray<>(), new SparseArray<>()}; @@ -1187,14 +1242,18 @@ public Period(TLRPC.TL_searchResultPosition calendarPeriod) { private int startedTrackingY; private VelocityTracker velocityTracker; - private boolean isActionModeShowed; + protected boolean isActionModeShowed; private static boolean skipPhotos = false; final Delegate delegate; private HintView fwdRestrictedHint; private Theme.ResourcesProvider resourcesProvider; - public SharedMediaLayout(Context context, long did, SharedMediaPreloader preloader, int commonGroupsCount, ArrayList sortedUsers, TLRPC.ChatFull chatInfo, boolean membersFirst, BaseFragment parent, Delegate delegate, int viewType, Theme.ResourcesProvider resourcesProvider) { + public boolean hasInternet() { + return profileActivity.getConnectionsManager().getConnectionState() == ConnectionsManager.ConnectionStateConnected; + } + + public SharedMediaLayout(Context context, long did, SharedMediaPreloader preloader, int commonGroupsCount, ArrayList sortedUsers, TLRPC.ChatFull chatInfo, TLRPC.UserFull userInfo, boolean membersFirst, BaseFragment parent, Delegate delegate, int viewType, Theme.ResourcesProvider resourcesProvider) { super(context); this.viewType = viewType; this.resourcesProvider = resourcesProvider; @@ -1207,8 +1266,10 @@ public SharedMediaLayout(Context context, long did, SharedMediaPreloader preload int[] mediaCount = preloader.getLastMediaCount(); topicId = sharedMediaPreloader.topicId; hasMedia = new int[]{mediaCount[0], mediaCount[1], mediaCount[2], mediaCount[3], mediaCount[4], mediaCount[5], topicId == 0 ? commonGroupsCount : 0}; - if (membersFirst && topicId == 0) { - initialTab = 7; + if (userInfo != null && userInfo.stories_pinned_available || isStoriesView()) { + initialTab = getInitialTab(); + } else if (membersFirst && topicId == 0) { + initialTab = TAB_GROUPUSERS; } else { for (int a = 0; a < hasMedia.length; a++) { if (hasMedia[a] == -1 || hasMedia[a] > 0) { @@ -1218,6 +1279,7 @@ public SharedMediaLayout(Context context, long did, SharedMediaPreloader preload } } info = chatInfo; + this.userInfo = userInfo; if (info != null) { mergeDialogId = -info.migrated_from_chat_id; } @@ -1235,7 +1297,8 @@ public SharedMediaLayout(Context context, long did, SharedMediaPreloader preload profileActivity = parent; actionBar = profileActivity.getActionBar(); - mediaColumnsCount = SharedConfig.mediaColumnsCount; + mediaColumnsCount[0] = SharedConfig.mediaColumnsCount; + mediaColumnsCount[1] = SharedConfig.storiesColumnsCount; profileActivity.getNotificationCenter().addObserver(this, NotificationCenter.mediaDidLoad); profileActivity.getNotificationCenter().addObserver(this, NotificationCenter.messagesDeleted); @@ -1244,6 +1307,7 @@ public SharedMediaLayout(Context context, long did, SharedMediaPreloader preload profileActivity.getNotificationCenter().addObserver(this, NotificationCenter.messagePlayingDidReset); profileActivity.getNotificationCenter().addObserver(this, NotificationCenter.messagePlayingPlayStateChanged); profileActivity.getNotificationCenter().addObserver(this, NotificationCenter.messagePlayingDidStart); + profileActivity.getNotificationCenter().addObserver(this, NotificationCenter.storiesListUpdated); for (int a = 0; a < 10; a++) { //cellCache.add(new SharedPhotoVideoCell(context)); @@ -1329,22 +1393,22 @@ public void onTextChanged(EditText editText) { searchWas = false; } switchToCurrentSelectedMode(false); - if (mediaPages[0].selectedType == 1) { + if (mediaPages[0].selectedType == TAB_FILES) { if (documentsSearchAdapter == null) { return; } documentsSearchAdapter.search(text, true); - } else if (mediaPages[0].selectedType == 3) { + } else if (mediaPages[0].selectedType == TAB_LINKS) { if (linksSearchAdapter == null) { return; } linksSearchAdapter.search(text, true); - } else if (mediaPages[0].selectedType == 4) { + } else if (mediaPages[0].selectedType == TAB_AUDIO) { if (audioSearchAdapter == null) { return; } audioSearchAdapter.search(text, true); - } else if (mediaPages[0].selectedType == 7) { + } else if (mediaPages[0].selectedType == TAB_GROUPUSERS) { if (groupUsersSearchAdapter == null) { return; } @@ -1361,7 +1425,7 @@ public void onLayout(int l, int t, int r, int b) { searchItem.setTranslationY(AndroidUtilities.dp(10)); searchItem.setSearchFieldHint(LocaleController.getString("Search", R.string.Search)); searchItem.setContentDescription(LocaleController.getString("Search", R.string.Search)); - searchItem.setVisibility(View.INVISIBLE); + searchItem.setVisibility(isStoriesView() ? View.GONE : View.INVISIBLE); photoVideoOptionsItem = new ImageView(context); photoVideoOptionsItem.setContentDescription(LocaleController.getString("AccDescrMoreOptions", R.string.AccDescrMoreOptions)); @@ -1377,7 +1441,7 @@ public void onLayout(int l, int t, int r, int b) { @Override public void onClick(View view) { View dividerView = new DividerCell(context); - ActionBarPopupWindow.ActionBarPopupWindowLayout popupLayout = new ActionBarPopupWindow.ActionBarPopupWindowLayout(context) { + ActionBarPopupWindow.ActionBarPopupWindowLayout popupLayout = new ActionBarPopupWindow.ActionBarPopupWindowLayout(context, resourcesProvider) { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (dividerView.getParent() != null) { @@ -1392,71 +1456,42 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } }; - ActionBarMenuSubItem mediaZoomInItem = new ActionBarMenuSubItem(context, true, false); - ActionBarMenuSubItem mediaZoomOutItem = new ActionBarMenuSubItem(context, false, false); + int tab = getClosestTab(); + boolean isStories = tab == TAB_STORIES || tab == TAB_ARCHIVED_STORIES; + + mediaZoomInItem = new ActionBarMenuSubItem(context, true, false, resourcesProvider); + mediaZoomOutItem = new ActionBarMenuSubItem(context, false, false, resourcesProvider); mediaZoomInItem.setTextAndIcon(LocaleController.getString("MediaZoomIn", R.string.MediaZoomIn), R.drawable.msg_zoomin); - mediaZoomInItem.setOnClickListener(view1 -> { - if (photoVideoChangeColumnsAnimation) { - return; - } - int newColumnsCount = getNextMediaColumnsCount(mediaColumnsCount, true); - if (newColumnsCount == getNextMediaColumnsCount(newColumnsCount, true)) { - mediaZoomInItem.setEnabled(false); - mediaZoomInItem.animate().alpha(0.5f).start(); - } - if (mediaColumnsCount != newColumnsCount) { - if (!mediaZoomOutItem.isEnabled()) { - mediaZoomOutItem.setEnabled(true); - mediaZoomOutItem.animate().alpha(1f).start(); - } - SharedConfig.setMediaColumnsCount(newColumnsCount); - animateToMediaColumnsCount(newColumnsCount); - } - }); + mediaZoomInItem.setOnClickListener(view1 -> zoomIn()); popupLayout.addView(mediaZoomInItem); mediaZoomOutItem.setTextAndIcon(LocaleController.getString("MediaZoomOut", R.string.MediaZoomOut), R.drawable.msg_zoomout); - mediaZoomOutItem.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View view) { - if (photoVideoChangeColumnsAnimation) { - return; - } - int newColumnsCount = getNextMediaColumnsCount(mediaColumnsCount, false); - if (newColumnsCount == getNextMediaColumnsCount(newColumnsCount, false)) { - mediaZoomOutItem.setEnabled(false); - mediaZoomOutItem.animate().alpha(0.5f).start(); - } - if (mediaColumnsCount != newColumnsCount) { - if (!mediaZoomInItem.isEnabled()) { - mediaZoomInItem.setEnabled(true); - mediaZoomInItem.animate().alpha(1f).start(); - } - SharedConfig.setMediaColumnsCount(newColumnsCount); - animateToMediaColumnsCount(newColumnsCount); - } - } - }); + mediaZoomOutItem.setOnClickListener(view1 -> zoomOut()); + popupLayout.addView(mediaZoomOutItem); - if (mediaColumnsCount == 2) { + if (isStories && allowStoriesSingleColumn) { + mediaZoomInItem.setEnabled(false); + mediaZoomInItem.setAlpha(0.5f); + mediaZoomOutItem.setEnabled(false); + mediaZoomOutItem.setAlpha(0.5f); + } else if (mediaColumnsCount[isStories ? 1 : 0] == 2) { mediaZoomInItem.setEnabled(false); mediaZoomInItem.setAlpha(0.5f); - } else if (mediaColumnsCount == 9) { + } else if (mediaColumnsCount[isStories ? 1 : 0] == 9) { mediaZoomOutItem.setEnabled(false); mediaZoomOutItem.setAlpha(0.5f); } - popupLayout.addView(mediaZoomOutItem); - boolean hasDifferentTypes = (sharedMediaData[0].hasPhotos && sharedMediaData[0].hasVideos) || !sharedMediaData[0].endReached[0] || !sharedMediaData[0].endReached[1] || !sharedMediaData[0].startReached; + boolean hasDifferentTypes = isStories || (sharedMediaData[0].hasPhotos && sharedMediaData[0].hasVideos) || !sharedMediaData[0].endReached[0] || !sharedMediaData[0].endReached[1] || !sharedMediaData[0].startReached; if (!DialogObject.isEncryptedDialog(dialog_id)) { - ActionBarMenuSubItem calendarItem = new ActionBarMenuSubItem(context, false, false); + ActionBarMenuSubItem calendarItem = new ActionBarMenuSubItem(context, false, false, resourcesProvider); calendarItem.setTextAndIcon(LocaleController.getString("Calendar", R.string.Calendar), R.drawable.msg_calendar2); popupLayout.addView(calendarItem); calendarItem.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { - showMediaCalendar(false); + showMediaCalendar(tab, false); if (optionsWindow != null) { optionsWindow.dismiss(); } @@ -1465,53 +1500,83 @@ public void onClick(View view) { if (hasDifferentTypes) { popupLayout.addView(dividerView); - ActionBarMenuSubItem showPhotosItem = new ActionBarMenuSubItem(context, true, false, false); - ActionBarMenuSubItem showVideosItem = new ActionBarMenuSubItem(context, true, false, true); + ActionBarMenuSubItem showPhotosItem = new ActionBarMenuSubItem(context, true, false, false, resourcesProvider); + ActionBarMenuSubItem showVideosItem = new ActionBarMenuSubItem(context, true, false, true, resourcesProvider); showPhotosItem.setTextAndIcon(LocaleController.getString("MediaShowPhotos", R.string.MediaShowPhotos), 0); - showPhotosItem.setChecked(sharedMediaData[0].filterType == FILTER_PHOTOS_AND_VIDEOS || sharedMediaData[0].filterType == FILTER_PHOTOS_ONLY); - showPhotosItem.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View view) { + popupLayout.addView(showPhotosItem); + + showVideosItem.setTextAndIcon(LocaleController.getString("MediaShowVideos", R.string.MediaShowVideos), 0); + popupLayout.addView(showVideosItem); + + if (isStories) { + StoriesAdapter adapter = tab == TAB_STORIES ? storiesAdapter : archivedStoriesAdapter; + showPhotosItem.setChecked(adapter.storiesList.showPhotos()); + showVideosItem.setChecked(adapter.storiesList.showVideos()); + showPhotosItem.setOnClickListener(v -> { if (changeTypeAnimation) { return; } if (!showVideosItem.getCheckView().isChecked() && showPhotosItem.getCheckView().isChecked()) { + AndroidUtilities.shakeViewSpring(v, shiftDp = -shiftDp); return; } - showPhotosItem.setChecked(!showPhotosItem.getCheckView().isChecked()); - if (showPhotosItem.getCheckView().isChecked() && showVideosItem.getCheckView().isChecked()) { - sharedMediaData[0].filterType = FILTER_PHOTOS_AND_VIDEOS; - } else { - sharedMediaData[0].filterType = FILTER_VIDEOS_ONLY; - } - changeMediaFilterType(); - } - }); - popupLayout.addView(showPhotosItem); - - - showVideosItem.setTextAndIcon(LocaleController.getString("MediaShowVideos", R.string.MediaShowVideos), 0); - showVideosItem.setChecked(sharedMediaData[0].filterType == FILTER_PHOTOS_AND_VIDEOS || sharedMediaData[0].filterType == FILTER_VIDEOS_ONLY); - showVideosItem.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View view) { + showPhotosItem.getCheckView().setChecked(!showPhotosItem.getCheckView().isChecked(), true); + adapter.storiesList.updateFilters(showPhotosItem.getCheckView().isChecked(), showVideosItem.getCheckView().isChecked()); + }); + showVideosItem.setOnClickListener(v -> { if (changeTypeAnimation) { return; } if (!showPhotosItem.getCheckView().isChecked() && showVideosItem.getCheckView().isChecked()) { + AndroidUtilities.shakeViewSpring(v, shiftDp = -shiftDp); return; } - showVideosItem.setChecked(!showVideosItem.getCheckView().isChecked()); - if (showPhotosItem.getCheckView().isChecked() && showVideosItem.getCheckView().isChecked()) { - sharedMediaData[0].filterType = FILTER_PHOTOS_AND_VIDEOS; - } else { - sharedMediaData[0].filterType = FILTER_PHOTOS_ONLY; + showVideosItem.getCheckView().setChecked(!showVideosItem.getCheckView().isChecked(), true); + adapter.storiesList.updateFilters(showPhotosItem.getCheckView().isChecked(), showVideosItem.getCheckView().isChecked()); + }); + } else { + showPhotosItem.setChecked(sharedMediaData[0].filterType == FILTER_PHOTOS_AND_VIDEOS || sharedMediaData[0].filterType == FILTER_PHOTOS_ONLY); + showPhotosItem.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + if (changeTypeAnimation) { + return; + } + if (!showVideosItem.getCheckView().isChecked() && showPhotosItem.getCheckView().isChecked()) { + AndroidUtilities.shakeViewSpring(showPhotosItem, shiftDp = -shiftDp); + return; + } + showPhotosItem.setChecked(!showPhotosItem.getCheckView().isChecked()); + if (showPhotosItem.getCheckView().isChecked() && showVideosItem.getCheckView().isChecked()) { + sharedMediaData[0].filterType = FILTER_PHOTOS_AND_VIDEOS; + } else { + sharedMediaData[0].filterType = FILTER_VIDEOS_ONLY; + } + changeMediaFilterType(); } - changeMediaFilterType(); - } - }); - popupLayout.addView(showVideosItem); + }); + showVideosItem.setChecked(sharedMediaData[0].filterType == FILTER_PHOTOS_AND_VIDEOS || sharedMediaData[0].filterType == FILTER_VIDEOS_ONLY); + showVideosItem.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + if (changeTypeAnimation) { + return; + } + if (!showPhotosItem.getCheckView().isChecked() && showVideosItem.getCheckView().isChecked()) { + AndroidUtilities.shakeViewSpring(showVideosItem, shiftDp = -shiftDp); + return; + } + showVideosItem.setChecked(!showVideosItem.getCheckView().isChecked()); + if (showPhotosItem.getCheckView().isChecked() && showVideosItem.getCheckView().isChecked()) { + sharedMediaData[0].filterType = FILTER_PHOTOS_AND_VIDEOS; + } else { + sharedMediaData[0].filterType = FILTER_PHOTOS_ONLY; + } + changeMediaFilterType(); + } + }); + } } } @@ -1612,6 +1677,34 @@ public void notifyDataSetChanged() { chatUsersAdapter.sortedUsers = sortedUsers; chatUsersAdapter.chatInfo = membersFirst ? chatInfo : null; } + storiesAdapter = new StoriesAdapter(context, false) { + @Override + public void notifyDataSetChanged() { + super.notifyDataSetChanged(); + MediaPage mediaPage = getMediaPage(TAB_STORIES); + if (mediaPage != null && mediaPage.animationSupportingListView.getVisibility() == View.VISIBLE) { + animationSupportingStoriesAdapter.notifyDataSetChanged(); + } + if (mediaPage != null) { + mediaPage.emptyView.showProgress(storiesList != null && (storiesList.isLoading() || hasInternet() && storiesList.getCount() > 0)); + } + } + }; + animationSupportingStoriesAdapter = new StoriesAdapter(context, false); + archivedStoriesAdapter = new StoriesAdapter(context, true) { + @Override + public void notifyDataSetChanged() { + super.notifyDataSetChanged(); + MediaPage mediaPage = getMediaPage(TAB_ARCHIVED_STORIES); + if (mediaPage != null && mediaPage.animationSupportingListView.getVisibility() == View.VISIBLE) { + animationSupportingArchivedStoriesAdapter.notifyDataSetChanged(); + } + if (mediaPage != null) { + mediaPage.emptyView.showProgress(storiesList != null && (storiesList.isLoading() || hasInternet() && storiesList.getCount() > 0)); + } + } + }; + animationSupportingArchivedStoriesAdapter = new StoriesAdapter(context, true); linksAdapter = new SharedLinksAdapter(context); setWillNotDraw(false); @@ -1650,15 +1743,9 @@ public void setTranslationX(float translationX) { searchItem.setAlpha(scrollProgress); } - float photoVideoOptionsAlpha = 0f; - if (mediaPages[1] != null && mediaPages[1].selectedType == 0) { - photoVideoOptionsAlpha = scrollProgress; - } - if (mediaPages[0].selectedType == 0) { - photoVideoOptionsAlpha = 1f - scrollProgress; - } + float photoVideoOptionsAlpha = getPhotoVideoOptionsAlpha(scrollProgress); photoVideoOptionsItem.setAlpha(photoVideoOptionsAlpha); - photoVideoOptionsItem.setVisibility((photoVideoOptionsAlpha == 0 || !canShowSearchItem()) ? INVISIBLE : View.VISIBLE); + photoVideoOptionsItem.setVisibility((photoVideoOptionsAlpha == 0 || !canShowSearchItem()) ? INVISIBLE : View.VISIBLE); } else { searchItem.setAlpha(0.0f); } @@ -1668,7 +1755,10 @@ public void setTranslationX(float translationX) { invalidateBlur(); } }; - addView(mediaPage, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 0, 48, 0, 0)); + addView(mediaPage, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 0, isStoriesView() ? 0 : 48, 0, 0)); + if (a == 1) { + mediaPage.setTranslationX(AndroidUtilities.displaySize.x); + } mediaPages[a] = mediaPage; final ExtendedGridLayoutManager layoutManager = mediaPages[a].layoutManager = new ExtendedGridLayoutManager(context, 100) { @@ -1683,9 +1773,9 @@ public boolean supportsPredictiveItemAnimations() { @Override protected void calculateExtraLayoutSpace(RecyclerView.State state, int[] extraLayoutSpace) { super.calculateExtraLayoutSpace(state, extraLayoutSpace); - if (mediaPage.selectedType == 0) { + if (mediaPage.selectedType == TAB_PHOTOVIDEO || mediaPage.selectedType == TAB_STORIES || mediaPage.selectedType == TAB_ARCHIVED_STORIES) { extraLayoutSpace[1] = Math.max(extraLayoutSpace[1], SharedPhotoVideoCell.getItemSize(1) * 2); - } else if (mediaPage.selectedType == 1) { + } else if (mediaPage.selectedType == TAB_FILES) { extraLayoutSpace[1] = Math.max(extraLayoutSpace[1], AndroidUtilities.dp(56f) * 2); } } @@ -1739,9 +1829,20 @@ public void onInitializeAccessibilityNodeInfoForItem(RecyclerView.Recycler recyc layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { + final int columnsCount = mediaColumnsCount[mediaPage.selectedType == TAB_STORIES || mediaPage.selectedType == TAB_ARCHIVED_STORIES ? 1 : 0]; if (mediaPage.listView.getAdapter() == photoVideoAdapter) { if (photoVideoAdapter.getItemViewType(position) == 2) { - return mediaColumnsCount; + return columnsCount; + } + return 1; + } else if (mediaPage.listView.getAdapter() == storiesAdapter) { + if (storiesAdapter.getItemViewType(position) == 2) { + return columnsCount; + } + return 1; + } else if (mediaPage.listView.getAdapter() == archivedStoriesAdapter) { + if (storiesAdapter.getItemViewType(position) == 2) { + return columnsCount; } return 1; } @@ -1754,12 +1855,12 @@ public int getSpanSize(int position) { return mediaPage.layoutManager.getSpanSizeForItem(position); } }); - mediaPages[a].listView = new BlurredRecyclerView(context) { + mediaPages[a].listView = new InternalListView(context) { - HashSet excludeDrawViews = new HashSet<>(); - ArrayList drawingViews = new ArrayList<>(); - ArrayList drawingViews2 = new ArrayList<>(); - ArrayList drawingViews3 = new ArrayList<>(); + final HashSet excludeDrawViews = new HashSet<>(); + final ArrayList drawingViews = new ArrayList<>(); + final ArrayList drawingViews2 = new ArrayList<>(); + final ArrayList drawingViews3 = new ArrayList<>(); @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { @@ -1770,9 +1871,64 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) { } } + private TextPaint archivedHintPaint; + private StaticLayout archivedHintLayout; + private float archivedHintLayoutWidth, archivedHintLayoutLeft; + + UserListPoller poller; + @Override protected void dispatchDraw(Canvas canvas) { - if (getAdapter() == photoVideoAdapter) { + if (getAdapter() == archivedStoriesAdapter && getChildCount() > 0) { + View topChild = getChildAt(0); + if (topChild != null && getChildAdapterPosition(topChild) == 0) { + int top = topChild.getTop(); + if (photoVideoChangeColumnsAnimation && changeColumnsTab == TAB_ARCHIVED_STORIES && mediaPage.animationSupportingListView.getChildCount() > 0) { + View supportingTopChild = mediaPage.animationSupportingListView.getChildAt(0); + if (supportingTopChild != null && mediaPage.animationSupportingListView.getChildAdapterPosition(supportingTopChild) == 0) { + top = AndroidUtilities.lerp(top, supportingTopChild.getTop(), photoVideoChangeColumnsProgress); + } + } + if (archivedHintPaint == null) { + archivedHintPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + archivedHintPaint.setTextSize(AndroidUtilities.dp(14)); + archivedHintPaint.setColor(getThemedColor(Theme.key_windowBackgroundWhiteGrayText2)); + } + int width = getMeasuredWidth() - AndroidUtilities.dp(60); + if (archivedHintLayout == null || archivedHintLayout.getWidth() != width) { + archivedHintLayout = new StaticLayout(LocaleController.getString("ProfileStoriesArchiveHint", R.string.ProfileStoriesArchiveHint), archivedHintPaint, width, Layout.Alignment.ALIGN_CENTER, 1f, 0f, false); + archivedHintLayoutWidth = 0; + archivedHintLayoutLeft = width; + for (int i = 0; i < archivedHintLayout.getLineCount(); ++i) { + archivedHintLayoutWidth = Math.max(archivedHintLayoutWidth, archivedHintLayout.getLineWidth(i)); + archivedHintLayoutLeft = Math.min(archivedHintLayoutLeft, archivedHintLayout.getLineLeft(i)); + } + } + + canvas.save(); + canvas.translate( + (getWidth() - archivedHintLayoutWidth) / 2f - archivedHintLayoutLeft, + top - (AndroidUtilities.dp(64) + archivedHintLayout.getHeight()) / 2f + ); + archivedHintLayout.draw(canvas); + canvas.restore(); + } + } + SharedPhotoVideoAdapter movingAdapter, supportingMovingAdapter; + int ci = 0; + if (changeColumnsTab == TAB_STORIES) { + movingAdapter = storiesAdapter; + supportingMovingAdapter = animationSupportingStoriesAdapter; + ci = 1; + } else if (changeColumnsTab == TAB_ARCHIVED_STORIES) { + movingAdapter = archivedStoriesAdapter; + supportingMovingAdapter = animationSupportingArchivedStoriesAdapter; + ci = 1; + } else { + movingAdapter = photoVideoAdapter; + supportingMovingAdapter = animationSupportingPhotoVideoAdapter; + } + if (this == mediaPage.listView && getAdapter() == movingAdapter) { int firstVisibleItemPosition = 0; int firstVisibleItemPosition2 = 0; int lastVisibleItemPosition = 0; @@ -1812,24 +1968,25 @@ protected void dispatchDraw(Canvas canvas) { lastVisibleItemPosition2 = max; if (firstVisibleItemPosition >= 0 && firstVisibleItemPosition2 >= 0 && pinchCenterPosition >= 0) { - int rowsCount1 = (int) Math.ceil(photoVideoAdapter.getItemCount() / (float) mediaColumnsCount); - int rowsCount2 = (int) Math.ceil(photoVideoAdapter.getItemCount() / (float) animateToColumnsCount); - rowsOffset = (pinchCenterPosition / animateToColumnsCount - firstVisibleItemPosition2 / animateToColumnsCount) - (pinchCenterPosition / mediaColumnsCount - firstVisibleItemPosition / mediaColumnsCount); - if ((firstVisibleItemPosition / mediaColumnsCount - rowsOffset < 0 && animateToColumnsCount < mediaColumnsCount) || (firstVisibleItemPosition2 / animateToColumnsCount + rowsOffset < 0 && animateToColumnsCount > mediaColumnsCount)) { + int rowsCount1 = (int) Math.ceil((movingAdapter.getItemCount()) / (float) mediaColumnsCount[ci]); + int rowsCount2 = (int) Math.ceil((movingAdapter.getItemCount()) / (float) animateToColumnsCount); + rowsOffset = ((pinchCenterPosition) / animateToColumnsCount - firstVisibleItemPosition2 / animateToColumnsCount) - ((pinchCenterPosition - movingAdapter.getTopOffset()) / mediaColumnsCount[ci] - firstVisibleItemPosition / mediaColumnsCount[ci]); + if ((firstVisibleItemPosition / mediaColumnsCount[ci] - rowsOffset < 0 && animateToColumnsCount < mediaColumnsCount[ci]) || (firstVisibleItemPosition2 / animateToColumnsCount + rowsOffset < 0 && animateToColumnsCount > mediaColumnsCount[ci])) { rowsOffset = 0; } - if ((lastVisibleItemPosition2 / mediaColumnsCount + rowsOffset >= rowsCount1 && animateToColumnsCount > mediaColumnsCount) || (lastVisibleItemPosition / animateToColumnsCount - rowsOffset >= rowsCount2 && animateToColumnsCount < mediaColumnsCount)) { + if ((lastVisibleItemPosition2 / mediaColumnsCount[ci] + rowsOffset >= rowsCount1 && animateToColumnsCount > mediaColumnsCount[ci]) || (lastVisibleItemPosition / animateToColumnsCount - rowsOffset >= rowsCount2 && animateToColumnsCount < mediaColumnsCount[ci])) { rowsOffset = 0; } - float k = (pinchCenterPosition % mediaColumnsCount) / (float) (mediaColumnsCount - 1); - columnsOffset = (int) ((animateToColumnsCount - mediaColumnsCount) * k); + float k = (pinchCenterPosition % mediaColumnsCount[ci]) / (float) (mediaColumnsCount[ci] - 1); + columnsOffset = (int) ((animateToColumnsCount - mediaColumnsCount[ci]) * k); } animationSupportingSortedCells.clear(); excludeDrawViews.clear(); drawingViews.clear(); drawingViews2.clear(); drawingViews3.clear(); + animationSupportingSortedCellsOffset = 0; for (int i = 0; i < mediaPage.animationSupportingListView.getChildCount(); i++) { View child = mediaPage.animationSupportingListView.getChildAt(i); if (child.getTop() > getMeasuredHeight() || child.getBottom() < 0) { @@ -1837,15 +1994,17 @@ protected void dispatchDraw(Canvas canvas) { } if (child instanceof SharedPhotoVideoCell2) { animationSupportingSortedCells.add((SharedPhotoVideoCell2) child); + } else if (child instanceof TextView) { + animationSupportingSortedCellsOffset++; } } drawingViews.addAll(animationSupportingSortedCells); FastScroll fastScroll = getFastScroll(); if (fastScroll != null && fastScroll.getTag() != null) { - float p1 = photoVideoAdapter.getScrollProgress(mediaPage.listView); - float p2 = animationSupportingPhotoVideoAdapter.getScrollProgress(mediaPage.animationSupportingListView); - float a1 = photoVideoAdapter.fastScrollIsVisible(mediaPage.listView) ? 1f : 0f; - float a2 = animationSupportingPhotoVideoAdapter.fastScrollIsVisible(mediaPage.animationSupportingListView) ? 1f : 0f; + float p1 = movingAdapter.getScrollProgress(mediaPage.listView); + float p2 = supportingMovingAdapter.getScrollProgress(mediaPage.animationSupportingListView); + float a1 = movingAdapter.fastScrollIsVisible(mediaPage.listView) ? 1f : 0f; + float a2 = supportingMovingAdapter.fastScrollIsVisible(mediaPage.animationSupportingListView) ? 1f : 0f; fastScroll.setProgress(p1 * (1f - photoVideoChangeColumnsProgress) + p2 * photoVideoChangeColumnsProgress); fastScroll.setVisibilityAlpha(a1 * (1f - photoVideoChangeColumnsProgress) + a2 * photoVideoChangeColumnsProgress); } @@ -1893,9 +2052,9 @@ protected void dispatchDraw(Canvas canvas) { if (photoVideoChangeColumnsAnimation) { float fromScale = 1f; - int currentColumn = ((GridLayoutManager.LayoutParams) cell.getLayoutParams()).getViewAdapterPosition() % mediaColumnsCount + columnsOffset; - int currentRow = (((GridLayoutManager.LayoutParams) cell.getLayoutParams()).getViewAdapterPosition() - firstVisibleItemPosition) / mediaColumnsCount + rowsOffset; - int toIndex = currentRow * animateToColumnsCount + currentColumn; + int currentColumn = (((GridLayoutManager.LayoutParams) cell.getLayoutParams()).getViewAdapterPosition()) % mediaColumnsCount[ci] + columnsOffset; + int currentRow = ((((GridLayoutManager.LayoutParams) cell.getLayoutParams()).getViewAdapterPosition()) - firstVisibleItemPosition) / mediaColumnsCount[ci] + rowsOffset; + int toIndex = currentRow * animateToColumnsCount + currentColumn + animationSupportingSortedCellsOffset; if (currentColumn >= 0 && currentColumn < animateToColumnsCount && toIndex >= 0 && toIndex < animationSupportingSortedCells.size()) { inAnimation = true; float toScale = (animationSupportingSortedCells.get(toIndex).getMeasuredWidth() - AndroidUtilities.dpf2(2)) / (float) (cell.getMeasuredWidth() - AndroidUtilities.dpf2(2)); @@ -1937,15 +2096,18 @@ protected void dispatchDraw(Canvas canvas) { } if (photoVideoChangeColumnsAnimation && !drawingViews.isEmpty()) { - float toScale = animateToColumnsCount / (float) mediaColumnsCount; + float toScale = animateToColumnsCount / (float) mediaColumnsCount[ci]; float scale = toScale * (1f - photoVideoChangeColumnsProgress) + photoVideoChangeColumnsProgress; - float sizeToScale = ((getMeasuredWidth() / (float) mediaColumnsCount) - AndroidUtilities.dpf2(2)) / ((getMeasuredWidth() / (float) animateToColumnsCount) - AndroidUtilities.dpf2(2)); + float sizeToScale = ((getMeasuredWidth() / (float) mediaColumnsCount[ci]) - AndroidUtilities.dpf2(2)) / ((getMeasuredWidth() / (float) animateToColumnsCount) - AndroidUtilities.dpf2(2)); float scaleSize = sizeToScale * (1f - photoVideoChangeColumnsProgress) + photoVideoChangeColumnsProgress; - float fromSize = getMeasuredWidth() / (float) mediaColumnsCount; + float fromSize = getMeasuredWidth() / (float) mediaColumnsCount[ci]; float toSize = (getMeasuredWidth() / (float) animateToColumnsCount); float size1 = (float) ((Math.ceil((getMeasuredWidth() / (float) animateToColumnsCount)) - AndroidUtilities.dpf2(2)) * scaleSize + AndroidUtilities.dpf2(2)); + if (changeColumnsTab == TAB_STORIES || changeColumnsTab == TAB_ARCHIVED_STORIES) { + size1 *= 1.25f; + } for (int i = 0; i < drawingViews.size(); i++) { SharedPhotoVideoCell2 view = drawingViews.get(i); @@ -1953,16 +2115,16 @@ protected void dispatchDraw(Canvas canvas) { continue; } view.setCrossfadeView(null, 0, 0); - int fromColumn = ((GridLayoutManager.LayoutParams) view.getLayoutParams()).getViewAdapterPosition() % animateToColumnsCount; + int fromColumn = (((GridLayoutManager.LayoutParams) view.getLayoutParams()).getViewAdapterPosition()) % animateToColumnsCount; int toColumn = fromColumn - columnsOffset; - int currentRow = (((GridLayoutManager.LayoutParams) view.getLayoutParams()).getViewAdapterPosition() - firstVisibleItemPosition2) / animateToColumnsCount; + int currentRow = ((((GridLayoutManager.LayoutParams) view.getLayoutParams()).getViewAdapterPosition()) - firstVisibleItemPosition2) / animateToColumnsCount; currentRow -= rowsOffset; canvas.save(); canvas.translate(toColumn * fromSize * (1f - photoVideoChangeColumnsProgress) + toSize * fromColumn * photoVideoChangeColumnsProgress, minY + size1 * currentRow); view.setImageScale(scaleSize, !photoVideoChangeColumnsAnimation); - if (toColumn < mediaColumnsCount) { - canvas.saveLayerAlpha(0, 0, view.getMeasuredWidth() * scale, view.getMeasuredWidth() * scale, (int) (photoVideoChangeColumnsProgress * 255), Canvas.ALL_SAVE_FLAG); + if (toColumn < mediaColumnsCount[ci]) { + canvas.saveLayerAlpha(0, 0, view.getMeasuredWidth() * scale, view.getMeasuredHeight() * scale, (int) (photoVideoChangeColumnsProgress * 255), Canvas.ALL_SAVE_FLAG); view.draw(canvas); canvas.restore(); } else { @@ -1975,20 +2137,23 @@ protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); if (photoVideoChangeColumnsAnimation) { - float toScale = mediaColumnsCount / (float) animateToColumnsCount; + float toScale = mediaColumnsCount[ci] / (float) animateToColumnsCount; float scale = toScale * photoVideoChangeColumnsProgress + (1f - photoVideoChangeColumnsProgress); - float sizeToScale = ((getMeasuredWidth() / (float) animateToColumnsCount) - AndroidUtilities.dpf2(2)) / ((getMeasuredWidth() / (float) mediaColumnsCount) - AndroidUtilities.dpf2(2)); + float sizeToScale = ((getMeasuredWidth() / (float) animateToColumnsCount) - AndroidUtilities.dpf2(2)) / ((getMeasuredWidth() / (float) mediaColumnsCount[ci]) - AndroidUtilities.dpf2(2)); float scaleSize = sizeToScale * photoVideoChangeColumnsProgress + (1f - photoVideoChangeColumnsProgress); - float size1 = (float) ((Math.ceil((getMeasuredWidth() / (float) mediaColumnsCount)) - AndroidUtilities.dpf2(2)) * scaleSize + AndroidUtilities.dpf2(2)); - float fromSize = getMeasuredWidth() / (float) mediaColumnsCount; + float size1 = (float) ((Math.ceil((getMeasuredWidth() / (float) mediaColumnsCount[ci])) - AndroidUtilities.dpf2(2)) * scaleSize + AndroidUtilities.dpf2(2)); + if (changeColumnsTab == TAB_STORIES || changeColumnsTab == TAB_ARCHIVED_STORIES) { + size1 *= 1.25f; + } + float fromSize = getMeasuredWidth() / (float) mediaColumnsCount[ci]; float toSize = getMeasuredWidth() / (float) animateToColumnsCount; for (int i = 0; i < drawingViews2.size(); i++) { SharedPhotoVideoCell2 view = drawingViews2.get(i); - int fromColumn = ((GridLayoutManager.LayoutParams) view.getLayoutParams()).getViewAdapterPosition() % mediaColumnsCount; - int currentRow = (((GridLayoutManager.LayoutParams) view.getLayoutParams()).getViewAdapterPosition() - firstVisibleItemPosition) / mediaColumnsCount; + int fromColumn = (((GridLayoutManager.LayoutParams) view.getLayoutParams()).getViewAdapterPosition()) % mediaColumnsCount[ci]; + int currentRow = ((((GridLayoutManager.LayoutParams) view.getLayoutParams()).getViewAdapterPosition()) - firstVisibleItemPosition) / mediaColumnsCount[ci]; currentRow += rowsOffset; int toColumn = fromColumn + columnsOffset; @@ -1997,7 +2162,7 @@ protected void dispatchDraw(Canvas canvas) { view.setImageScale(scaleSize, !photoVideoChangeColumnsAnimation); canvas.translate(fromColumn * fromSize * (1f - photoVideoChangeColumnsProgress) + toSize * toColumn * photoVideoChangeColumnsProgress, minY + size1 * currentRow); if (toColumn < animateToColumnsCount) { - canvas.saveLayerAlpha(0, 0, view.getMeasuredWidth() * scale, view.getMeasuredWidth() * scale, (int) ((1f - photoVideoChangeColumnsProgress) * 255), Canvas.ALL_SAVE_FLAG); + canvas.saveLayerAlpha(0, 0, view.getMeasuredWidth() * scale, view.getMeasuredHeight() * scale, (int) ((1f - photoVideoChangeColumnsProgress) * 255), Canvas.ALL_SAVE_FLAG); view.draw(canvas); canvas.restore(); } else { @@ -2043,12 +2208,23 @@ protected void dispatchDraw(Canvas canvas) { } invalidate(); } - + if (poller == null) { + poller = UserListPoller.getInstance(profileActivity.getCurrentAccount()); + } + poller.checkList(this); } @Override public boolean drawChild(Canvas canvas, View child, long drawingTime) { - if (getAdapter() == photoVideoAdapter) { + SharedPhotoVideoAdapter movingAdapter; + if (changeColumnsTab == TAB_STORIES) { + movingAdapter = storiesAdapter; + } else if (changeColumnsTab == TAB_ARCHIVED_STORIES) { + movingAdapter = archivedStoriesAdapter; + } else { + movingAdapter = photoVideoAdapter; + } + if (mediaPage.listView == this && getAdapter() == movingAdapter) { if (photoVideoChangeColumnsAnimation && child instanceof SharedPhotoVideoCell2) { return true; } @@ -2067,7 +2243,7 @@ public boolean drawChild(Canvas canvas, View child, long drawingTime) { mediaPages[a].listView.setSectionsType(RecyclerListView.SECTIONS_TYPE_DATE); mediaPages[a].listView.setLayoutManager(layoutManager); mediaPages[a].addView(mediaPages[a].listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); - mediaPages[a].animationSupportingListView = new BlurredRecyclerView(context); + mediaPages[a].animationSupportingListView = new InternalListView(context); mediaPages[a].animationSupportingListView.setLayoutManager(mediaPages[a].animationSupportingLayoutManager = new GridLayoutManager(context, 3) { @Override @@ -2085,6 +2261,26 @@ public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerVi }); mediaPages[a].addView(mediaPages[a].animationSupportingListView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); mediaPages[a].animationSupportingListView.setVisibility(View.GONE); + mediaPages[a].animationSupportingListView.addItemDecoration(new RecyclerView.ItemDecoration() { + @Override + public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { + if (view instanceof SharedPhotoVideoCell2) { + SharedPhotoVideoCell2 cell = (SharedPhotoVideoCell2) view; + final int position = mediaPage.animationSupportingListView.getChildAdapterPosition(cell), spanCount = mediaPage.animationSupportingLayoutManager.getSpanCount(); + cell.isFirst = position % spanCount == 0; + cell.isLast = position % spanCount == spanCount - 1; + outRect.left = 0; + outRect.top = 0; + outRect.bottom = 0; + outRect.right = 0; + } else { + outRect.left = 0; + outRect.top = 0; + outRect.bottom = 0; + outRect.right = 0; + } + } + }); mediaPages[a].listView.addItemDecoration(new RecyclerView.ItemDecoration() { @@ -2100,6 +2296,15 @@ public void getItemOffsets(android.graphics.Rect outRect, View view, RecyclerVie outRect.top = 0; } outRect.right = mediaPage.layoutManager.isLastInRow(position) ? 0 : AndroidUtilities.dp(2); + } else if (view instanceof SharedPhotoVideoCell2) { + SharedPhotoVideoCell2 cell = (SharedPhotoVideoCell2) view; + final int position = mediaPage.listView.getChildAdapterPosition(cell), spanCount = mediaPage.layoutManager.getSpanCount(); + cell.isFirst = position % spanCount == 0; + cell.isLast = position % spanCount == spanCount - 1; + outRect.left = 0; + outRect.top = 0; + outRect.bottom = 0; + outRect.right = 0; } else { outRect.left = 0; outRect.top = 0; @@ -2109,15 +2314,20 @@ public void getItemOffsets(android.graphics.Rect outRect, View view, RecyclerVie } }); mediaPages[a].listView.setOnItemClickListener((view, position, x, y) -> { - if (mediaPage.selectedType == 7) { + if (mediaPage.selectedType == TAB_GROUPUSERS) { if (view instanceof UserCell) { TLRPC.ChatParticipant participant; + final int i; if (!chatUsersAdapter.sortedUsers.isEmpty()) { - participant = chatUsersAdapter.chatInfo.participants.participants.get(chatUsersAdapter.sortedUsers.get(position)); + i = chatUsersAdapter.sortedUsers.get(position); } else { - participant = chatUsersAdapter.chatInfo.participants.participants.get(position); + i = position; + } + participant = chatUsersAdapter.chatInfo.participants.participants.get(i); + if (i < 0 || i >= chatUsersAdapter.chatInfo.participants.participants.size()) { + return; } - onMemberClick(participant, false); + onMemberClick(participant, false, view); } else if (mediaPage.listView.getAdapter() == groupUsersSearchAdapter) { long user_id; TLObject object = groupUsersSearchAdapter.getItem(position); @@ -2138,7 +2348,7 @@ public void getItemOffsets(android.graphics.Rect outRect, View view, RecyclerVie args.putLong("user_id", user_id); profileActivity.presentFragment(new ProfileActivity(args)); } - } else if (mediaPage.selectedType == 6 && view instanceof ProfileSearchCell) { + } else if (mediaPage.selectedType == TAB_COMMON_GROUPS && view instanceof ProfileSearchCell) { TLRPC.Chat chat = ((ProfileSearchCell) view).getChat(); Bundle args = new Bundle(); args.putLong("chat_id", chat.id); @@ -2146,15 +2356,15 @@ public void getItemOffsets(android.graphics.Rect outRect, View view, RecyclerVie return; } profileActivity.presentFragment(new ChatActivity(args)); - } else if (mediaPage.selectedType == 1 && view instanceof SharedDocumentCell) { + } else if (mediaPage.selectedType == TAB_FILES && view instanceof SharedDocumentCell) { onItemClick(position, view, ((SharedDocumentCell) view).getMessage(), 0, mediaPage.selectedType); - } else if (mediaPage.selectedType == 3 && view instanceof SharedLinkCell) { + } else if (mediaPage.selectedType == TAB_LINKS && view instanceof SharedLinkCell) { onItemClick(position, view, ((SharedLinkCell) view).getMessage(), 0, mediaPage.selectedType); - } else if ((mediaPage.selectedType == 2 || mediaPage.selectedType == 4) && view instanceof SharedAudioCell) { + } else if ((mediaPage.selectedType == TAB_VOICE || mediaPage.selectedType == TAB_AUDIO) && view instanceof SharedAudioCell) { onItemClick(position, view, ((SharedAudioCell) view).getMessage(), 0, mediaPage.selectedType); - } else if (mediaPage.selectedType == 5 && view instanceof ContextLinkCell) { + } else if (mediaPage.selectedType == TAB_GIF && view instanceof ContextLinkCell) { onItemClick(position, view, (MessageObject) ((ContextLinkCell) view).getParentObject(), 0, mediaPage.selectedType); - } else if (mediaPage.selectedType == 0 && view instanceof SharedPhotoVideoCell2) { + } else if (mediaPage.selectedType == TAB_PHOTOVIDEO && view instanceof SharedPhotoVideoCell2) { SharedPhotoVideoCell2 cell = (SharedPhotoVideoCell2) view; if (cell.canRevealSpoiler()) { cell.startRevealMedia(x, y); @@ -2164,6 +2374,12 @@ public void getItemOffsets(android.graphics.Rect outRect, View view, RecyclerVie if (messageObject != null) { onItemClick(position, view, messageObject, 0, mediaPage.selectedType); } + } else if ((mediaPage.selectedType == TAB_STORIES || mediaPage.selectedType == TAB_ARCHIVED_STORIES) && view instanceof SharedPhotoVideoCell2) { + SharedPhotoVideoCell2 cell = (SharedPhotoVideoCell2) view; + MessageObject messageObject = cell.getMessageObject(); + if (messageObject != null) { + onItemClick(position, view, messageObject, 0, mediaPage.selectedType); + } } }); mediaPages[a].listView.setOnScrollListener(new RecyclerView.OnScrollListener() { @@ -2178,7 +2394,7 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { if (dy != 0 && (mediaPages[0].selectedType == 0 || mediaPages[0].selectedType == 5) && !sharedMediaData[0].messages.isEmpty()) { showFloatingDateView(); } - if (dy != 0 && mediaPage.selectedType == 0) { + if (dy != 0 && (mediaPage.selectedType == 0 || mediaPage.selectedType == TAB_STORIES || mediaPage.selectedType == TAB_ARCHIVED_STORIES)) { showFastScrollHint(mediaPage, sharedMediaData, true); } mediaPage.listView.checkSection(true); @@ -2193,13 +2409,10 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { return false; } if (isActionModeShowed) { - RecyclerListView.OnItemClickListener onItemClickListener = mediaPage.listView.getOnItemClickListener(); - if (onItemClickListener != null) { - onItemClickListener.onItemClick(view, position); - } + mediaPage.listView.clickItem(view, position); return true; } - if (mediaPage.selectedType == 7 && view instanceof UserCell) { + if (mediaPage.selectedType == TAB_GROUPUSERS && view instanceof UserCell) { final TLRPC.ChatParticipant participant; int index = position; if (!chatUsersAdapter.sortedUsers.isEmpty()) { @@ -2212,19 +2425,27 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { return false; } participant = chatUsersAdapter.chatInfo.participants.participants.get(index); - return onMemberClick(participant, true); - } else if (mediaPage.selectedType == 1 && view instanceof SharedDocumentCell) { + RecyclerListView listView = (RecyclerListView) view.getParent(); + for (int i = 0; i < listView.getChildCount(); ++i) { + View child = listView.getChildAt(i); + if (listView.getChildAdapterPosition(child) == position) { + view = child; + break; + } + } + return onMemberClick(participant, true, view); + } else if (mediaPage.selectedType == TAB_FILES && view instanceof SharedDocumentCell) { return onItemLongClick(((SharedDocumentCell) view).getMessage(), view, 0); - } else if (mediaPage.selectedType == 3 && view instanceof SharedLinkCell) { + } else if (mediaPage.selectedType == TAB_LINKS && view instanceof SharedLinkCell) { return onItemLongClick(((SharedLinkCell) view).getMessage(), view, 0); - } else if ((mediaPage.selectedType == 2 || mediaPage.selectedType == 4) && view instanceof SharedAudioCell) { + } else if ((mediaPage.selectedType == TAB_VOICE || mediaPage.selectedType == TAB_AUDIO) && view instanceof SharedAudioCell) { return onItemLongClick(((SharedAudioCell) view).getMessage(), view, 0); - } else if (mediaPage.selectedType == 5 && view instanceof ContextLinkCell) { + } else if (mediaPage.selectedType == TAB_GIF && view instanceof ContextLinkCell) { return onItemLongClick((MessageObject) ((ContextLinkCell) view).getParentObject(), view, 0); - } else if (mediaPage.selectedType == 0 && view instanceof SharedPhotoVideoCell2) { + } else if ((mediaPage.selectedType == TAB_PHOTOVIDEO || mediaPage.selectedType == TAB_ARCHIVED_STORIES || mediaPage.selectedType == TAB_STORIES && isStoriesView()) && view instanceof SharedPhotoVideoCell2) { MessageObject messageObject = ((SharedPhotoVideoCell2) view).getMessageObject(); if (messageObject != null) { - return onItemLongClick(messageObject, view, 0); + return onItemLongClick(messageObject, view, mediaPage.selectedType); } } return false; @@ -2249,29 +2470,31 @@ public void invalidate() { @Override public int getColumnsCount() { - return mediaColumnsCount; + return mediaColumnsCount[mediaPage.selectedType == TAB_STORIES || mediaPage.selectedType == TAB_ARCHIVED_STORIES ? 1 : 0]; } @Override public int getViewType() { setIsSingleCell(false); - if (mediaPage.selectedType == 0 || mediaPage.selectedType == 5) { - return 2; - } else if (mediaPage.selectedType == 1) { - return 3; - } else if (mediaPage.selectedType == 2 || mediaPage.selectedType == 4) { - return 4; - } else if (mediaPage.selectedType == 3) { - return 5; - } else if (mediaPage.selectedType == 7) { + if (mediaPage.selectedType == TAB_PHOTOVIDEO || mediaPage.selectedType == TAB_GIF) { + return FlickerLoadingView.PHOTOS_TYPE; + } else if (mediaPage.selectedType == TAB_FILES) { + return FlickerLoadingView.FILES_TYPE; + } else if (mediaPage.selectedType == TAB_VOICE || mediaPage.selectedType == TAB_AUDIO) { + return FlickerLoadingView.USERS_TYPE; + } else if (mediaPage.selectedType == TAB_LINKS) { + return FlickerLoadingView.LINKS_TYPE; + } else if (mediaPage.selectedType == TAB_GROUPUSERS) { return FlickerLoadingView.USERS_TYPE; - } else if (mediaPage.selectedType == 6) { + } else if (mediaPage.selectedType == TAB_COMMON_GROUPS) { if (scrollSlidingTextTabStrip.getTabsCount() == 1) { setIsSingleCell(true); } - return 1; + return FlickerLoadingView.DIALOG_TYPE; + } else if (mediaPage.selectedType == TAB_STORIES || mediaPage.selectedType == TAB_ARCHIVED_STORIES) { + return FlickerLoadingView.STORIES_TYPE; } - return 1; + return FlickerLoadingView.DIALOG_TYPE; } @Override @@ -2316,13 +2539,15 @@ protected void onDraw(Canvas canvas) { } }); - addView(scrollSlidingTextTabStrip, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT | Gravity.TOP)); - addView(actionModeLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT | Gravity.TOP)); + if (!isStoriesView()) { + addView(scrollSlidingTextTabStrip, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT | Gravity.TOP)); + addView(actionModeLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT | Gravity.TOP)); + } shadowLine = new View(context); shadowLine.setBackgroundColor(getThemedColor(Theme.key_divider)); FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1); - layoutParams.topMargin = AndroidUtilities.dp(48) - 1; + layoutParams.topMargin = isStoriesView() ? 0 : AndroidUtilities.dp(48) - 1; addView(shadowLine, layoutParams); updateTabs(false); @@ -2332,6 +2557,27 @@ protected void onDraw(Canvas canvas) { } } + protected boolean isStoriesView() { + return false; + } + + protected boolean includeStories() { + return true; + } + + protected int getInitialTab() { + return 0; + } + + public void setStoriesFilter(boolean photos, boolean videos) { + if (storiesAdapter != null) { + storiesAdapter.storiesList.updateFilters(photos, videos); + } + if (archivedStoriesAdapter != null) { + archivedStoriesAdapter.storiesList.updateFilters(photos, videos); + } + } + protected void invalidateBlur() { } @@ -2459,17 +2705,20 @@ public void onAnimationEnd(Animator animation) { private MediaPage getMediaPage(int type) { for (int i = 0; i < mediaPages.length; i++) { - if (mediaPages[i].selectedType == 0) { + if (mediaPages[i] != null && mediaPages[i].selectedType == type) { return mediaPages[i]; } } return null; } - private void showMediaCalendar(boolean fromFastScroll) { + public void showMediaCalendar(int page, boolean fromFastScroll) { if (fromFastScroll && SharedMediaLayout.this.getY() != 0 && viewType == VIEW_TYPE_PROFILE_ACTIVITY) { return; } + if (fromFastScroll && (page == TAB_STORIES || page == TAB_ARCHIVED_STORIES) && getStoriesCount(page) <= 0) { + return; + } Bundle bundle = new Bundle(); bundle.putLong("dialog_id", dialog_id); bundle.putInt("topic_id", topicId); @@ -2498,7 +2747,13 @@ private void showMediaCalendar(boolean fromFastScroll) { } } } - bundle.putInt("type", CalendarActivity.TYPE_MEDIA_CALENDAR); + if (page == TAB_ARCHIVED_STORIES) { + bundle.putInt("type", CalendarActivity.TYPE_ARCHIVED_STORIES); + } else if (page == TAB_STORIES) { + bundle.putInt("type", CalendarActivity.TYPE_PROFILE_STORIES); + } else { + bundle.putInt("type", CalendarActivity.TYPE_MEDIA_CALENDAR); + } CalendarActivity calendarActivity = new CalendarActivity(bundle, sharedMediaData[0].filterType, date); calendarActivity.setCallback(new CalendarActivity.Callback() { @Override @@ -2530,28 +2785,70 @@ private void startPinchToMediaColumnsCount(boolean pinchScaleUp) { } MediaPage mediaPage = null; for (int i = 0; i < mediaPages.length; i++) { - if (mediaPages[i].selectedType == 0) { + if (mediaPages[i].selectedType == TAB_PHOTOVIDEO || mediaPages[i].selectedType == TAB_STORIES || mediaPages[i].selectedType == TAB_ARCHIVED_STORIES) { mediaPage = mediaPages[i]; break; } } if (mediaPage != null) { - int newColumnsCount = getNextMediaColumnsCount(mediaColumnsCount, pinchScaleUp); + changeColumnsTab = mediaPage.selectedType; + final int ci = changeColumnsTab == TAB_STORIES || changeColumnsTab == TAB_ARCHIVED_STORIES ? 1 : 0; + + int newColumnsCount = getNextMediaColumnsCount(ci, mediaColumnsCount[ci], pinchScaleUp); animateToColumnsCount = newColumnsCount; - if (animateToColumnsCount == mediaColumnsCount) { + if (animateToColumnsCount == mediaColumnsCount[ci] || allowStoriesSingleColumn) { return; } mediaPage.animationSupportingListView.setVisibility(View.VISIBLE); - mediaPage.animationSupportingListView.setAdapter(animationSupportingPhotoVideoAdapter); + if (changeColumnsTab == TAB_STORIES) { + mediaPage.animationSupportingListView.setAdapter(animationSupportingStoriesAdapter); + } else if (changeColumnsTab == TAB_ARCHIVED_STORIES) { + mediaPage.animationSupportingListView.setAdapter(animationSupportingArchivedStoriesAdapter); + } else { + mediaPage.animationSupportingListView.setAdapter(animationSupportingPhotoVideoAdapter); + } + mediaPage.animationSupportingListView.setPadding( + mediaPage.animationSupportingListView.getPaddingLeft(), + changeColumnsTab == TAB_ARCHIVED_STORIES ? AndroidUtilities.dp(2 + 64) : AndroidUtilities.dp(2), + mediaPage.animationSupportingListView.getPaddingRight(), + mediaPage.animationSupportingListView.getPaddingBottom() + ); + mediaPage.animationSupportingLayoutManager.setSpanCount(newColumnsCount); + mediaPage.animationSupportingListView.invalidateItemDecorations(); + final MediaPage finalMediaPage = mediaPage; + mediaPage.animationSupportingLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { + @Override + public int getSpanSize(int position) { + if (finalMediaPage.animationSupportingListView.getAdapter() == animationSupportingPhotoVideoAdapter) { + if (animationSupportingPhotoVideoAdapter.getItemViewType(position) == 2) { + return finalMediaPage.animationSupportingLayoutManager.getSpanCount(); + } + return 1; + } else if (finalMediaPage.animationSupportingListView.getAdapter() == animationSupportingStoriesAdapter) { + if (animationSupportingStoriesAdapter.getItemViewType(position) == 2) { + return finalMediaPage.animationSupportingLayoutManager.getSpanCount(); + } + return 1; + } else if (finalMediaPage.animationSupportingListView.getAdapter() == animationSupportingArchivedStoriesAdapter) { + if (animationSupportingArchivedStoriesAdapter.getItemViewType(position) == 2) { + return finalMediaPage.animationSupportingLayoutManager.getSpanCount(); + } + return 1; + } + return 1; + } + }); AndroidUtilities.updateVisibleRows(mediaPage.listView); photoVideoChangeColumnsAnimation = true; - sharedMediaData[0].setListFrozen(true); + if (changeColumnsTab == TAB_PHOTOVIDEO) { + sharedMediaData[0].setListFrozen(true); + } photoVideoChangeColumnsProgress = 0; if (pinchCenterPosition >= 0) { for (int k = 0; k < mediaPages.length; k++) { - if (mediaPages[k].selectedType == 0) { + if (mediaPages[k].selectedType == changeColumnsTab) { mediaPages[k].animationSupportingLayoutManager.scrollToPositionWithOffset(pinchCenterPosition, pinchCenterOffset - mediaPages[k].animationSupportingListView.getPaddingTop()); } } @@ -2565,30 +2862,45 @@ private void finishPinchToMediaColumnsCount() { if (photoVideoChangeColumnsAnimation) { MediaPage mediaPage = null; for (int i = 0; i < mediaPages.length; i++) { - if (mediaPages[i].selectedType == 0) { + if (mediaPages[i].selectedType == changeColumnsTab) { mediaPage = mediaPages[i]; break; } } if (mediaPage != null) { + final int ci = mediaPage.selectedType == TAB_STORIES || mediaPage.selectedType == TAB_ARCHIVED_STORIES ? 1 : 0; if (photoVideoChangeColumnsProgress == 1f) { - int oldItemCount = photoVideoAdapter.getItemCount(); photoVideoChangeColumnsAnimation = false; - sharedMediaData[0].setListFrozen(false); - mediaPage.animationSupportingListView.setVisibility(View.GONE); - mediaColumnsCount = animateToColumnsCount; - SharedConfig.setMediaColumnsCount(animateToColumnsCount); - mediaPage.layoutManager.setSpanCount(mediaColumnsCount); - mediaPage.listView.invalidate(); - if (photoVideoAdapter.getItemCount() == oldItemCount) { - AndroidUtilities.updateVisibleRows(mediaPage.listView); - } else { - photoVideoAdapter.notifyDataSetChanged(); + mediaColumnsCount[ci] = animateToColumnsCount; + if (ci == 0) { + SharedConfig.setMediaColumnsCount(animateToColumnsCount); + } else if (getStoriesCount(mediaPage.selectedType) >= 5) { + SharedConfig.setStoriesColumnsCount(animateToColumnsCount); + } + for (int i = 0; i < mediaPages.length; ++i) { + if (mediaPages[i] != null && mediaPages[i].listView != null && isTabZoomable(mediaPages[i].selectedType)) { + RecyclerView.Adapter adapter = mediaPages[i].listView.getAdapter(); + if (adapter == null) { + continue; + } + int oldItemCount = adapter.getItemCount(); + if (i == TAB_PHOTOVIDEO) { + sharedMediaData[0].setListFrozen(false); + } + mediaPages[i].animationSupportingListView.setVisibility(View.GONE); + mediaPages[i].layoutManager.setSpanCount(mediaColumnsCount[ci]); + mediaPages[i].listView.invalidateItemDecorations(); + mediaPages[i].listView.invalidate(); + if (adapter.getItemCount() == oldItemCount) { + AndroidUtilities.updateVisibleRows(mediaPages[i].listView); + } else { + adapter.notifyDataSetChanged(); + } + } } - if (pinchCenterPosition >= 0) { for (int k = 0; k < mediaPages.length; k++) { - if (mediaPages[k].selectedType == 0) { + if (mediaPages[k].selectedType == changeColumnsTab) { View view = mediaPages[k].animationSupportingLayoutManager.findViewByPosition(pinchCenterPosition); if (view != null) { pinchCenterOffset = view.getTop(); @@ -2603,7 +2915,9 @@ private void finishPinchToMediaColumnsCount() { } if (photoVideoChangeColumnsProgress == 0) { photoVideoChangeColumnsAnimation = false; - sharedMediaData[0].setListFrozen(false); + if (changeColumnsTab == TAB_PHOTOVIDEO) { + sharedMediaData[0].setListFrozen(false); + } mediaPage.animationSupportingListView.setVisibility(View.GONE); mediaPage.listView.invalidate(); return; @@ -2621,25 +2935,40 @@ public void onAnimationUpdate(ValueAnimator valueAnimator) { animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - int oldItemCount = photoVideoAdapter.getItemCount(); photoVideoChangeColumnsAnimation = false; - sharedMediaData[0].setListFrozen(false); if (forward) { - mediaColumnsCount = animateToColumnsCount; - SharedConfig.setMediaColumnsCount(animateToColumnsCount); - finalMediaPage.layoutManager.setSpanCount(mediaColumnsCount); + mediaColumnsCount[ci] = animateToColumnsCount; + if (ci == 0) { + SharedConfig.setMediaColumnsCount(animateToColumnsCount); + } else if (getStoriesCount(finalMediaPage.selectedType) >= 5) { + SharedConfig.setStoriesColumnsCount(animateToColumnsCount); + } } - if (forward) { - if (photoVideoAdapter.getItemCount() == oldItemCount) { - AndroidUtilities.updateVisibleRows(finalMediaPage.listView); - } else { - photoVideoAdapter.notifyDataSetChanged(); + for (int i = 0; i < mediaPages.length; ++i) { + if (mediaPages[i] != null && mediaPages[i].listView != null && isTabZoomable(mediaPages[i].selectedType)) { + RecyclerView.Adapter adapter = mediaPages[i].listView.getAdapter(); + if (adapter == null) { + continue; + } + int oldItemCount = adapter.getItemCount(); + if (i == TAB_PHOTOVIDEO) { + sharedMediaData[0].setListFrozen(false); + } + if (forward) { + mediaPages[i].layoutManager.setSpanCount(mediaColumnsCount[ci]); + mediaPages[i].listView.invalidateItemDecorations(); + if (adapter.getItemCount() == oldItemCount) { + AndroidUtilities.updateVisibleRows(mediaPages[i].listView); + } else { + adapter.notifyDataSetChanged(); + } + } + mediaPages[i].animationSupportingListView.setVisibility(View.GONE); } } - finalMediaPage.animationSupportingListView.setVisibility(View.GONE); if (pinchCenterPosition >= 0) { for (int k = 0; k < mediaPages.length; k++) { - if (mediaPages[k].selectedType == 0) { + if (mediaPages[k].selectedType == changeColumnsTab) { if (forward) { View view = mediaPages[k].animationSupportingLayoutManager.findViewByPosition(pinchCenterPosition); if (view != null) { @@ -2662,27 +2991,46 @@ public void onAnimationEnd(Animator animation) { } } - int animationIndex; + AnimationNotificationsLocker notificationsLocker = new AnimationNotificationsLocker(); private void animateToMediaColumnsCount(int newColumnsCount) { - MediaPage mediaPage = getMediaPage(0); + MediaPage mediaPage = getMediaPage(changeColumnsTab); pinchCenterPosition = -1; if (mediaPage != null) { mediaPage.listView.stopScroll(); animateToColumnsCount = newColumnsCount; mediaPage.animationSupportingListView.setVisibility(View.VISIBLE); - mediaPage.animationSupportingListView.setAdapter(animationSupportingPhotoVideoAdapter); + if (changeColumnsTab == TAB_STORIES) { + mediaPage.animationSupportingListView.setAdapter(animationSupportingStoriesAdapter); + } else if (changeColumnsTab == TAB_ARCHIVED_STORIES) { + mediaPage.animationSupportingListView.setAdapter(animationSupportingArchivedStoriesAdapter); + } else { + mediaPage.animationSupportingListView.setAdapter(animationSupportingPhotoVideoAdapter); + } + mediaPage.animationSupportingListView.setPadding( + mediaPage.animationSupportingListView.getPaddingLeft(), + AndroidUtilities.dp(2) + (mediaPage.animationSupportingListView.hintPaddingTop = (changeColumnsTab == TAB_ARCHIVED_STORIES ? AndroidUtilities.dp(64) : 0)), + mediaPage.animationSupportingListView.getPaddingRight(), + mediaPage.animationSupportingListView.getPaddingBottom() + ); mediaPage.animationSupportingLayoutManager.setSpanCount(newColumnsCount); - AndroidUtilities.updateVisibleRows(mediaPage.listView); + mediaPage.animationSupportingListView.invalidateItemDecorations(); + for (int i = 0; i < mediaPages.length; ++i) { + if (mediaPages[i] != null && isTabZoomable(mediaPages[i].selectedType)) { + AndroidUtilities.updateVisibleRows(mediaPages[i].listView); + } + } photoVideoChangeColumnsAnimation = true; - sharedMediaData[0].setListFrozen(true); + if (changeColumnsTab == TAB_PHOTOVIDEO) { + sharedMediaData[0].setListFrozen(true); + } photoVideoChangeColumnsProgress = 0; saveScrollPosition(); ValueAnimator animator = ValueAnimator.ofFloat(0, 1f); MediaPage finalMediaPage = mediaPage; - animationIndex = NotificationCenter.getInstance(profileActivity.getCurrentAccount()).setAnimationInProgress(animationIndex, null); + notificationsLocker.lock(); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { @@ -2690,21 +3038,33 @@ public void onAnimationUpdate(ValueAnimator valueAnimator) { finalMediaPage.listView.invalidate(); } }); + final int ci = mediaPage.selectedType == TAB_STORIES || mediaPage.selectedType == TAB_ARCHIVED_STORIES ? 1 : 0; animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - NotificationCenter.getInstance(profileActivity.getCurrentAccount()).onAnimationFinish(animationIndex); - int oldItemCount = photoVideoAdapter.getItemCount(); + notificationsLocker.unlock(); photoVideoChangeColumnsAnimation = false; - sharedMediaData[0].setListFrozen(false); - mediaColumnsCount = newColumnsCount; - finalMediaPage.layoutManager.setSpanCount(mediaColumnsCount); - if (photoVideoAdapter.getItemCount() == oldItemCount) { - AndroidUtilities.updateVisibleRows(finalMediaPage.listView); - } else { - photoVideoAdapter.notifyDataSetChanged(); + mediaColumnsCount[ci] = newColumnsCount; + for (int i = 0; i < mediaPages.length; ++i) { + if (mediaPages[i] != null && mediaPages[i].listView != null && isTabZoomable(mediaPages[i].selectedType)) { + RecyclerView.Adapter adapter = mediaPages[i].listView.getAdapter(); + if (adapter == null) { + continue; + } + int oldItemCount = adapter.getItemCount(); + if (i == TAB_PHOTOVIDEO) { + sharedMediaData[0].setListFrozen(false); + } + mediaPages[i].layoutManager.setSpanCount(mediaColumnsCount[ci]); + mediaPages[i].listView.invalidateItemDecorations(); + if (adapter.getItemCount() == oldItemCount) { + AndroidUtilities.updateVisibleRows(mediaPages[i].listView); + } else { + adapter.notifyDataSetChanged(); + } + mediaPages[i].animationSupportingListView.setVisibility(View.GONE); + } } - finalMediaPage.animationSupportingListView.setVisibility(View.GONE); saveScrollPosition(); } }); @@ -2773,16 +3133,11 @@ public void onPageScrolled(float progress) { mediaPages[0].setTranslationX(progress * mediaPages[0].getMeasuredWidth()); mediaPages[1].setTranslationX(progress * mediaPages[0].getMeasuredWidth() - mediaPages[0].getMeasuredWidth()); } + onTabProgress(getTabProgress()); - float photoVideoOptionsAlpha = 0f; - if (mediaPages[0].selectedType == 0) { - photoVideoOptionsAlpha = 1f - progress; - } - if (mediaPages[1].selectedType == 0) { - photoVideoOptionsAlpha = progress; - } + float photoVideoOptionsAlpha = getPhotoVideoOptionsAlpha(progress); photoVideoOptionsItem.setAlpha(photoVideoOptionsAlpha); - photoVideoOptionsItem.setVisibility((photoVideoOptionsAlpha == 0 || !canShowSearchItem()) ? INVISIBLE : View.VISIBLE); + photoVideoOptionsItem.setVisibility((photoVideoOptionsAlpha == 0 || !canShowSearchItem()) ? INVISIBLE : View.VISIBLE); if (canShowSearchItem()) { if (searchItemState == 1) { searchItem.setAlpha(progress); @@ -2790,7 +3145,7 @@ public void onPageScrolled(float progress) { searchItem.setAlpha(1.0f - progress); } } else { - searchItem.setVisibility(INVISIBLE); + searchItem.setVisibility(isStoriesView() ? View.GONE : View.INVISIBLE); searchItem.setAlpha(0.0f); } if (progress == 1) { @@ -2799,7 +3154,7 @@ public void onPageScrolled(float progress) { mediaPages[1] = tempPage; mediaPages[1].setVisibility(View.GONE); if (searchItemState == 2) { - searchItem.setVisibility(View.INVISIBLE); + searchItem.setVisibility(isStoriesView() ? View.GONE : View.INVISIBLE); } searchItemState = 0; startStopVisibleGifs(); @@ -2897,7 +3252,7 @@ private void scrollToTop() { } int scrollDistance; if (mediaPages[0].selectedType == 0) { - scrollDistance = mediaPages[0].layoutManager.findFirstVisibleItemPosition() / mediaColumnsCount * height; + scrollDistance = mediaPages[0].layoutManager.findFirstVisibleItemPosition() / mediaColumnsCount[0] * height; } else { scrollDistance = mediaPages[0].layoutManager.findFirstVisibleItemPosition() * height; } @@ -2920,7 +3275,7 @@ private void checkLoadMoreScroll(MediaPage mediaPage, RecyclerListView recyclerV return; } mediaPage.lastCheckScrollTime = currentTime; - if (searching && searchWas || mediaPage.selectedType == 7) { + if (searching && searchWas || mediaPage.selectedType == TAB_GROUPUSERS) { return; } int firstVisibleItem = layoutManager.findFirstVisibleItemPosition(); @@ -2932,7 +3287,7 @@ private void checkLoadMoreScroll(MediaPage mediaPage, RecyclerListView recyclerV if (sharedMediaData[type].fastScrollDataLoaded && sharedMediaData[type].fastScrollPeriods.size() > 2 && mediaPage.selectedType == 0 && sharedMediaData[type].messages.size() != 0) { int columnsCount = 1; if (type == 0) { - columnsCount = mediaColumnsCount; + columnsCount = mediaColumnsCount[0]; } int jumpToTreshold = (int) ((recyclerView.getMeasuredHeight() / ((float) (recyclerView.getMeasuredWidth() / (float) columnsCount))) * columnsCount * 1.5f); if (jumpToTreshold < 100) { @@ -2953,6 +3308,14 @@ private void checkLoadMoreScroll(MediaPage mediaPage, RecyclerListView recyclerV if (mediaPage.selectedType == 7) { + } else if (mediaPage.selectedType == TAB_STORIES) { + if (storiesAdapter.storiesList != null && firstVisibleItem + visibleItemCount > storiesAdapter.storiesList.getLoadedCount() - mediaColumnsCount[1]) { + storiesAdapter.load(false); + } + } else if (mediaPage.selectedType == TAB_ARCHIVED_STORIES) { + if (archivedStoriesAdapter.storiesList != null && firstVisibleItem + visibleItemCount > archivedStoriesAdapter.storiesList.getLoadedCount() - mediaColumnsCount[1]) { + archivedStoriesAdapter.load(false); + } } else if (mediaPage.selectedType == 6) { if (visibleItemCount > 0) { if (!commonGroupsAdapter.endReached && !commonGroupsAdapter.loading && !commonGroupsAdapter.chats.isEmpty() && firstVisibleItem + visibleItemCount >= totalItemCount - 5) { @@ -3052,14 +3415,25 @@ public ActionBarMenuItem getSearchItem() { } public boolean isSearchItemVisible() { - if (mediaPages[0].selectedType == 7) { + if (mediaPages[0].selectedType == TAB_GROUPUSERS) { return delegate.canSearchMembers(); } - return mediaPages[0].selectedType != 0 && mediaPages[0].selectedType != 2 && mediaPages[0].selectedType != 5 && mediaPages[0].selectedType != 6; + return ( + mediaPages[0].selectedType != TAB_PHOTOVIDEO && + mediaPages[0].selectedType != TAB_STORIES && + mediaPages[0].selectedType != TAB_ARCHIVED_STORIES && + mediaPages[0].selectedType != TAB_VOICE && + mediaPages[0].selectedType != TAB_GIF && + mediaPages[0].selectedType != TAB_COMMON_GROUPS + ); + } + + public boolean isTabZoomable(int type) { + return type == TAB_PHOTOVIDEO || type == TAB_STORIES || type == TAB_ARCHIVED_STORIES; } public boolean isCalendarItemVisible() { - return mediaPages[0].selectedType == 0; + return mediaPages[0].selectedType == TAB_PHOTOVIDEO || mediaPages[0].selectedType == TAB_STORIES || mediaPages[0].selectedType == TAB_ARCHIVED_STORIES; } public int getSelectedTab() { @@ -3089,7 +3463,7 @@ protected void onSearchStateChanged(boolean expanded) { } - protected boolean onMemberClick(TLRPC.ChatParticipant participant, boolean isLong) { + protected boolean onMemberClick(TLRPC.ChatParticipant participant, boolean isLong, View view) { return false; } @@ -3101,6 +3475,14 @@ public void onDestroy() { profileActivity.getNotificationCenter().removeObserver(this, NotificationCenter.messagePlayingDidReset); profileActivity.getNotificationCenter().removeObserver(this, NotificationCenter.messagePlayingPlayStateChanged); profileActivity.getNotificationCenter().removeObserver(this, NotificationCenter.messagePlayingDidStart); + profileActivity.getNotificationCenter().removeObserver(this, NotificationCenter.storiesListUpdated); + + if (storiesAdapter != null && storiesAdapter.storiesList != null) { + storiesAdapter.destroy(); + } + if (archivedStoriesAdapter != null && archivedStoriesAdapter.storiesList != null) { + archivedStoriesAdapter.destroy(); + } } private void checkCurrentTabValid() { @@ -3305,11 +3687,11 @@ public void onActionBarItemClick(View v, int id) { showActionMode(false); if (dids.size() > 1 || dids.get(0).dialogId == profileActivity.getUserConfig().getClientUserId() || message != null) { - updateRowsSelection(); + updateRowsSelection(true); for (int a = 0; a < dids.size(); a++) { long did = dids.get(a).dialogId; if (message != null) { - profileActivity.getSendMessagesHelper().sendMessage(message.toString(), did, null, null, null, true, null, null, null, true, 0, null, false); + profileActivity.getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of(message.toString(), did, null, null, null, true, null, null, null, true, 0, null, false)); } if (id == forward_noquote) { //profileActivity.getMessageHelper().processForwardFromMyName(fmessages, did, true, 0); @@ -3402,12 +3784,12 @@ private boolean prepareForMoving(MotionEvent ev, boolean forward) { searchItem.setAlpha(1.0f); } else if (searchItemState == 1) { searchItem.setAlpha(0.0f); - searchItem.setVisibility(View.INVISIBLE); + searchItem.setVisibility(isStoriesView() ? View.GONE : View.INVISIBLE); } searchItemState = 0; } } else { - searchItem.setVisibility(INVISIBLE); + searchItem.setVisibility(isStoriesView() ? View.GONE : View.INVISIBLE); searchItem.setAlpha(0.0f); } @@ -3415,6 +3797,7 @@ private boolean prepareForMoving(MotionEvent ev, boolean forward) { hideFloatingDateView(true); maybeStartTracking = false; startedTracking = true; + onTabScroll(true); startedTrackingX = (int) ev.getX(); actionBar.setEnabled(false); scrollSlidingTextTabStrip.setEnabled(false); @@ -3427,6 +3810,7 @@ private boolean prepareForMoving(MotionEvent ev, boolean forward) { } else { mediaPages[1].setTranslationX(-mediaPages[0].getMeasuredWidth()); } + onTabProgress(getTabProgress()); return true; } @@ -3467,7 +3851,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } if (child instanceof MediaPage) { measureChildWithMargins(child, widthMeasureSpec, 0, MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY), 0); - ((MediaPage) child).listView.setPadding(0, 0, 0, topPadding); + ((MediaPage) child).listView.setPadding(0, ((MediaPage) child).listView.topPadding, 0, topPadding); } else { measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); } @@ -3495,6 +3879,7 @@ public boolean checkTabsAnimationInProgress() { } tabsAnimationInProgress = false; } + onTabProgress(getTabProgress()); return tabsAnimationInProgress; } return false; @@ -3513,8 +3898,13 @@ public RecyclerListView getCurrentListView() { return mediaPages[0].listView; } + private boolean disableScrolling; + @Override public boolean onTouchEvent(MotionEvent ev) { + if (disableScrolling) { + return false; + } if (profileActivity.getParentLayout() != null && !profileActivity.getParentLayout().checkTransitionAnimation() && !checkTabsAnimationInProgress() && !isInPinchToZoomTouchMode) { if (ev != null) { if (velocityTracker == null) { @@ -3539,9 +3929,11 @@ public boolean onTouchEvent(MotionEvent ev) { if (!prepareForMoving(ev, dx < 0)) { maybeStartTracking = true; startedTracking = false; + onTabScroll(false); mediaPages[0].setTranslationX(0); mediaPages[1].setTranslationX(animatingForward ? mediaPages[0].getMeasuredWidth() : -mediaPages[0].getMeasuredWidth()); scrollSlidingTextTabStrip.selectTabWithId(mediaPages[1].selectedType, 0); + onTabProgress(getTabProgress()); } } if (maybeStartTracking && !startedTracking) { @@ -3564,152 +3956,178 @@ public boolean onTouchEvent(MotionEvent ev) { searchItem.setAlpha(scrollProgress); } - float photoVideoOptionsAlpha = 0f; - if (mediaPages[1] != null && mediaPages[1].selectedType == 0) { - photoVideoOptionsAlpha = scrollProgress; - } - if (mediaPages[0].selectedType == 0) { - photoVideoOptionsAlpha = 1f - scrollProgress; - } + float photoVideoOptionsAlpha = getPhotoVideoOptionsAlpha(scrollProgress); photoVideoOptionsItem.setAlpha(photoVideoOptionsAlpha); photoVideoOptionsItem.setVisibility((photoVideoOptionsAlpha == 0 || !canShowSearchItem()) ? INVISIBLE : View.VISIBLE); } else { searchItem.setAlpha(0.0f); } scrollSlidingTextTabStrip.selectTabWithId(mediaPages[1].selectedType, scrollProgress); + onTabProgress(getTabProgress()); onSelectedTabChanged(); } } else if (ev == null || ev.getPointerId(0) == startedTrackingPointerId && (ev.getAction() == MotionEvent.ACTION_CANCEL || ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_POINTER_UP)) { - velocityTracker.computeCurrentVelocity(1000, maximumVelocity); - float velX; - float velY; - if (ev != null && ev.getAction() != MotionEvent.ACTION_CANCEL) { - velX = velocityTracker.getXVelocity(); - velY = velocityTracker.getYVelocity(); - if (!startedTracking) { - if (Math.abs(velX) >= 3000 && Math.abs(velX) > Math.abs(velY)) { - prepareForMoving(ev, velX < 0); - } - } - } else { - velX = 0; - velY = 0; - } - if (startedTracking) { - float x = mediaPages[0].getX(); - tabsAnimation = new AnimatorSet(); - backAnimation = Math.abs(x) < mediaPages[0].getMeasuredWidth() / 3.0f && (Math.abs(velX) < 3500 || Math.abs(velX) < Math.abs(velY)); - float distToMove; - float dx; - if (backAnimation) { - dx = Math.abs(x); - if (animatingForward) { - tabsAnimation.playTogether( - ObjectAnimator.ofFloat(mediaPages[0], View.TRANSLATION_X, 0), - ObjectAnimator.ofFloat(mediaPages[1], View.TRANSLATION_X, mediaPages[1].getMeasuredWidth()) - ); - } else { - tabsAnimation.playTogether( - ObjectAnimator.ofFloat(mediaPages[0], View.TRANSLATION_X, 0), - ObjectAnimator.ofFloat(mediaPages[1], View.TRANSLATION_X, -mediaPages[1].getMeasuredWidth()) - ); - } - } else { - dx = mediaPages[0].getMeasuredWidth() - Math.abs(x); - if (animatingForward) { - tabsAnimation.playTogether( - ObjectAnimator.ofFloat(mediaPages[0], View.TRANSLATION_X, -mediaPages[0].getMeasuredWidth()), - ObjectAnimator.ofFloat(mediaPages[1], View.TRANSLATION_X, 0) - ); - } else { - tabsAnimation.playTogether( - ObjectAnimator.ofFloat(mediaPages[0], View.TRANSLATION_X, mediaPages[0].getMeasuredWidth()), - ObjectAnimator.ofFloat(mediaPages[1], View.TRANSLATION_X, 0) - ); - } - } - tabsAnimation.setInterpolator(interpolator); + stopScroll(ev); + } + return startedTracking; + } + return false; + } - int width = getMeasuredWidth(); - int halfWidth = width / 2; - float distanceRatio = Math.min(1.0f, 1.0f * dx / (float) width); - float distance = (float) halfWidth + (float) halfWidth * AndroidUtilities.distanceInfluenceForSnapDuration(distanceRatio); - velX = Math.abs(velX); - int duration; - if (velX > 0) { - duration = 4 * Math.round(1000.0f * Math.abs(distance / velX)); - } else { - float pageDelta = dx / getMeasuredWidth(); - duration = (int) ((pageDelta + 1.0f) * 100.0f); - } - duration = Math.max(150, Math.min(duration, 600)); + public void scrollToPage(int page) { + if (disableScrolling || scrollSlidingTextTabStrip == null) { + return; + } + scrollSlidingTextTabStrip.scrollTo(page); + } - tabsAnimation.setDuration(duration); - tabsAnimation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animator) { - tabsAnimation = null; - if (backAnimation) { - mediaPages[1].setVisibility(View.GONE); - if (canShowSearchItem()) { - if (searchItemState == 2) { - searchItem.setAlpha(1.0f); - } else if (searchItemState == 1) { - searchItem.setAlpha(0.0f); - searchItem.setVisibility(View.INVISIBLE); - } - } else { - searchItem.setVisibility(INVISIBLE); - searchItem.setAlpha(0.0f); - } - searchItemState = 0; - } else { - MediaPage tempPage = mediaPages[0]; - mediaPages[0] = mediaPages[1]; - mediaPages[1] = tempPage; - mediaPages[1].setVisibility(View.GONE); - if (searchItemState == 2) { - searchItem.setVisibility(View.INVISIBLE); - } - searchItemState = 0; - scrollSlidingTextTabStrip.selectTabWithId(mediaPages[0].selectedType, 1.0f); - onSelectedTabChanged(); - startStopVisibleGifs(); + private void stopScroll(MotionEvent ev) { + if (velocityTracker == null) { + return; + } + velocityTracker.computeCurrentVelocity(1000, maximumVelocity); + float velX; + float velY; + if (ev != null && ev.getAction() != MotionEvent.ACTION_CANCEL) { + velX = velocityTracker.getXVelocity(); + velY = velocityTracker.getYVelocity(); + if (!startedTracking) { + if (Math.abs(velX) >= 3000 && Math.abs(velX) > Math.abs(velY)) { + prepareForMoving(ev, velX < 0); + } + } + } else { + velX = 0; + velY = 0; + } + if (startedTracking) { + float x = mediaPages[0].getX(); + tabsAnimation = new AnimatorSet(); + backAnimation = Math.abs(x) < mediaPages[0].getMeasuredWidth() / 3.0f && (Math.abs(velX) < 3500 || Math.abs(velX) < Math.abs(velY)); + float dx; + ValueAnimator invalidate = ValueAnimator.ofFloat(0, 1); + invalidate.addUpdateListener(anm -> onTabProgress(getTabProgress())); + if (backAnimation) { + dx = Math.abs(x); + if (animatingForward) { + tabsAnimation.playTogether( + ObjectAnimator.ofFloat(mediaPages[0], View.TRANSLATION_X, 0), + ObjectAnimator.ofFloat(mediaPages[1], View.TRANSLATION_X, mediaPages[1].getMeasuredWidth()), + invalidate + ); + } else { + tabsAnimation.playTogether( + ObjectAnimator.ofFloat(mediaPages[0], View.TRANSLATION_X, 0), + ObjectAnimator.ofFloat(mediaPages[1], View.TRANSLATION_X, -mediaPages[1].getMeasuredWidth()), + invalidate + ); + } + } else { + dx = mediaPages[0].getMeasuredWidth() - Math.abs(x); + if (animatingForward) { + tabsAnimation.playTogether( + ObjectAnimator.ofFloat(mediaPages[0], View.TRANSLATION_X, -mediaPages[0].getMeasuredWidth()), + ObjectAnimator.ofFloat(mediaPages[1], View.TRANSLATION_X, 0), + invalidate + ); + } else { + tabsAnimation.playTogether( + ObjectAnimator.ofFloat(mediaPages[0], View.TRANSLATION_X, mediaPages[0].getMeasuredWidth()), + ObjectAnimator.ofFloat(mediaPages[1], View.TRANSLATION_X, 0), + invalidate + ); + } + } + tabsAnimation.setInterpolator(interpolator); + + int width = getMeasuredWidth(); + int halfWidth = width / 2; + float distanceRatio = Math.min(1.0f, 1.0f * dx / (float) width); + float distance = (float) halfWidth + (float) halfWidth * AndroidUtilities.distanceInfluenceForSnapDuration(distanceRatio); + velX = Math.abs(velX); + int duration; + if (velX > 0) { + duration = 4 * Math.round(1000.0f * Math.abs(distance / velX)); + } else { + float pageDelta = dx / getMeasuredWidth(); + duration = (int) ((pageDelta + 1.0f) * 100.0f); + } + duration = Math.max(150, Math.min(duration, 600)); + + tabsAnimation.setDuration(duration); + tabsAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animator) { + tabsAnimation = null; + if (backAnimation) { + mediaPages[1].setVisibility(View.GONE); + if (canShowSearchItem()) { + if (searchItemState == 2) { + searchItem.setAlpha(1.0f); + } else if (searchItemState == 1) { + searchItem.setAlpha(0.0f); + searchItem.setVisibility(isStoriesView() ? View.GONE : View.INVISIBLE); } - tabsAnimationInProgress = false; - maybeStartTracking = false; - startedTracking = false; - actionBar.setEnabled(true); - scrollSlidingTextTabStrip.setEnabled(true); + } else { + searchItem.setVisibility(isStoriesView() ? View.GONE : INVISIBLE); + searchItem.setAlpha(0.0f); } - }); - tabsAnimation.start(); - tabsAnimationInProgress = true; - startedTracking = false; - onSelectedTabChanged(); - } else { + searchItemState = 0; + } else { + MediaPage tempPage = mediaPages[0]; + mediaPages[0] = mediaPages[1]; + mediaPages[1] = tempPage; + mediaPages[1].setVisibility(View.GONE); + if (searchItemState == 2) { + searchItem.setVisibility(isStoriesView() ? View.GONE : View.INVISIBLE); + } + searchItemState = 0; + scrollSlidingTextTabStrip.selectTabWithId(mediaPages[0].selectedType, 1.0f); + onSelectedTabChanged(); + startStopVisibleGifs(); + } + tabsAnimationInProgress = false; maybeStartTracking = false; + startedTracking = false; + onTabScroll(false); actionBar.setEnabled(true); scrollSlidingTextTabStrip.setEnabled(true); } - if (velocityTracker != null) { - velocityTracker.recycle(); - velocityTracker = null; - } - } - return startedTracking; + }); + tabsAnimation.start(); + tabsAnimationInProgress = true; + startedTracking = false; + onSelectedTabChanged(); + } else { + maybeStartTracking = false; + actionBar.setEnabled(true); + scrollSlidingTextTabStrip.setEnabled(true); + } + if (velocityTracker != null) { + velocityTracker.recycle(); + velocityTracker = null; } - return false; + } + + public void disableScroll(boolean disable) { + if (disable) { + stopScroll(null); + } + disableScrolling = disable; } public boolean closeActionMode() { + return closeActionMode(true); + } + + public boolean closeActionMode(boolean uncheckAnimated) { if (isActionModeShowed) { for (int a = 1; a >= 0; a--) { selectedFiles[a].clear(); } cantDeleteMessagesCount = 0; showActionMode(false); - updateRowsSelection(); + updateRowsSelection(uncheckAnimated); return true; } else { return false; @@ -3725,9 +4143,17 @@ public void setVisibleHeight(int height) { } } + protected void onActionModeSelectedUpdate(SparseArray messageObjects) { + + } + private AnimatorSet actionModeAnimation; - private void showActionMode(boolean show) { + public boolean isActionModeShown() { + return isActionModeShowed; + } + + protected void showActionMode(boolean show) { if (isActionModeShowed == show) { return; } @@ -4087,6 +4513,30 @@ public void didReceivedNotification(int id, int account, Object... args) { } } } + } else if (id == NotificationCenter.storiesListUpdated) { + StoriesController.StoriesList list = (StoriesController.StoriesList) args[0]; + if (storiesAdapter != null && list == storiesAdapter.storiesList) { + MediaPage page = getMediaPage(TAB_STORIES); + if (page != null && page.fastScrollEnabled != (list.getCount() > 0)) { + page.fastScrollEnabled = list.getCount() > 0; + updateFastScrollVisibility(page, true); + } + storiesAdapter.notifyDataSetChanged(); + if (delegate != null) { + delegate.updateSelectedMediaTabText(); + } + } + if (archivedStoriesAdapter != null && list == archivedStoriesAdapter.storiesList) { + MediaPage page = getMediaPage(TAB_ARCHIVED_STORIES); + if (page != null && page.fastScrollEnabled != (list.getCount() > 0)) { + page.fastScrollEnabled = list.getCount() > 0; + updateFastScrollVisibility(page, true); + } + archivedStoriesAdapter.notifyDataSetChanged(); + if (delegate != null) { + delegate.updateSelectedMediaTabText(); + } + } } } @@ -4118,18 +4568,30 @@ private void saveScrollPosition() { } } if (messageId != 0) { - int index = -1; - if (mediaPages[k].selectedType < 0 || mediaPages[k].selectedType >= sharedMediaData.length) { - continue; - } - for (int i = 0; i < sharedMediaData[mediaPages[k].selectedType].messages.size(); i++) { - if (messageId == sharedMediaData[mediaPages[k].selectedType].messages.get(i).getId()) { - index = i; - break; + int index = -1, position = -1; + final int type = mediaPages[k].selectedType; + if (type == TAB_STORIES || type == TAB_ARCHIVED_STORIES) { + StoriesAdapter adapter = type == TAB_STORIES ? storiesAdapter : archivedStoriesAdapter; + if (adapter.storiesList != null) { + for (int i = 0; i < adapter.storiesList.messageObjects.size(); ++i) { + if (messageId == adapter.storiesList.messageObjects.get(i).getId()) { + index = i; + break; + } + } } + position = index; + } else if (type >= 0 && type < sharedMediaData.length) { + for (int i = 0; i < sharedMediaData[type].messages.size(); i++) { + if (messageId == sharedMediaData[type].messages.get(i).getId()) { + index = i; + break; + } + } + position = sharedMediaData[type].startOffset + index; + } else { + continue; } - - int position = sharedMediaData[mediaPages[k].selectedType].startOffset + index; if (index >= 0) { ((LinearLayoutManager) listView.getLayoutManager()).scrollToPositionWithOffset(position, -mediaPages[k].listView.getPaddingTop() + offset); if (photoVideoChangeColumnsAnimation) { @@ -4274,19 +4736,36 @@ public void setChatInfo(TLRPC.ChatFull chatInfo) { } } + public void setUserInfo(TLRPC.UserFull userInfo) { + boolean stories_pinned_available = this.userInfo != null && this.userInfo.stories_pinned_available; + this.userInfo = userInfo; + updateTabs(true); + if (userInfo != null && (stories_pinned_available != userInfo.stories_pinned_available)) { + scrollToPage(TAB_STORIES); + } + } + public void setChatUsers(ArrayList sortedUsers, TLRPC.ChatFull chatInfo) { + for (int a = 0; a < mediaPages.length; a++) { + if (mediaPages[a].selectedType == TAB_GROUPUSERS) { + if (mediaPages[a].listView.getAdapter().getItemCount() != 0 && profileActivity.getMessagesController().getStoriesController().hasLoadingStories()) { + return; + } + } + } if (topicId == 0) { chatUsersAdapter.chatInfo = chatInfo; chatUsersAdapter.sortedUsers = sortedUsers; } updateTabs(true); for (int a = 0; a < mediaPages.length; a++) { - if (mediaPages[a].selectedType == 7) { + if (mediaPages[a].selectedType == TAB_GROUPUSERS) { mediaPages[a].listView.getAdapter().notifyDataSetChanged(); } } } + public void updateAdapters() { if (photoVideoAdapter != null) { photoVideoAdapter.notifyDataSetChanged(); @@ -4306,23 +4785,26 @@ public void updateAdapters() { if (gifAdapter != null) { gifAdapter.notifyDataSetChanged(); } + if (storiesAdapter != null) { + storiesAdapter.notifyDataSetChanged(); + } } - private void updateRowsSelection() { + private void updateRowsSelection(boolean animated) { for (int i = 0; i < mediaPages.length; i++) { int count = mediaPages[i].listView.getChildCount(); for (int a = 0; a < count; a++) { View child = mediaPages[i].listView.getChildAt(a); if (child instanceof SharedDocumentCell) { - ((SharedDocumentCell) child).setChecked(false, true); + ((SharedDocumentCell) child).setChecked(false, animated); } else if (child instanceof SharedPhotoVideoCell2) { - ((SharedPhotoVideoCell2) child).setChecked(false, true); + ((SharedPhotoVideoCell2) child).setChecked(false, animated); } else if (child instanceof SharedLinkCell) { - ((SharedLinkCell) child).setChecked(false, true); + ((SharedLinkCell) child).setChecked(false, animated); } else if (child instanceof SharedAudioCell) { - ((SharedAudioCell) child).setChecked(false, true); + ((SharedAudioCell) child).setChecked(false, animated); } else if (child instanceof ContextLinkCell) { - ((ContextLinkCell) child).setChecked(false, true); + ((ContextLinkCell) child).setChecked(false, animated); } } } @@ -4340,35 +4822,40 @@ private void updateTabs(boolean animated) { animated = false; } int changed = 0; - if ((chatUsersAdapter.chatInfo == null) == scrollSlidingTextTabStrip.hasTab(7)) { - changed++; - } - if ((hasMedia[0] <= 0) == scrollSlidingTextTabStrip.hasTab(0)) { - changed++; - } - if ((hasMedia[1] <= 0) == scrollSlidingTextTabStrip.hasTab(1)) { + if ((DialogObject.isUserDialog(dialog_id) && !DialogObject.isEncryptedDialog(dialog_id) && (userInfo != null && userInfo.stories_pinned_available || isStoriesView())) != scrollSlidingTextTabStrip.hasTab(TAB_STORIES)) { changed++; } - if (!DialogObject.isEncryptedDialog(dialog_id)) { - if ((hasMedia[3] <= 0) == scrollSlidingTextTabStrip.hasTab(3)) { + if (!isStoriesView()) { + if ((chatUsersAdapter.chatInfo == null) == scrollSlidingTextTabStrip.hasTab(TAB_GROUPUSERS)) { changed++; } - if ((hasMedia[4] <= 0) == scrollSlidingTextTabStrip.hasTab(4)) { + if ((hasMedia[0] <= 0) == scrollSlidingTextTabStrip.hasTab(TAB_PHOTOVIDEO)) { changed++; } - } else { - if ((hasMedia[4] <= 0) == scrollSlidingTextTabStrip.hasTab(4)) { + if ((hasMedia[1] <= 0) == scrollSlidingTextTabStrip.hasTab(TAB_FILES)) { + changed++; + } + if (!DialogObject.isEncryptedDialog(dialog_id)) { + if ((hasMedia[3] <= 0) == scrollSlidingTextTabStrip.hasTab(TAB_LINKS)) { + changed++; + } + if ((hasMedia[4] <= 0) == scrollSlidingTextTabStrip.hasTab(TAB_AUDIO)) { + changed++; + } + } else { + if ((hasMedia[4] <= 0) == scrollSlidingTextTabStrip.hasTab(TAB_AUDIO)) { + changed++; + } + } + if ((hasMedia[2] <= 0) == scrollSlidingTextTabStrip.hasTab(TAB_VOICE)) { + changed++; + } + if ((hasMedia[5] <= 0) == scrollSlidingTextTabStrip.hasTab(TAB_GIF)) { + changed++; + } + if ((hasMedia[6] <= 0) == scrollSlidingTextTabStrip.hasTab(TAB_COMMON_GROUPS)) { changed++; } - } - if ((hasMedia[2] <= 0) == scrollSlidingTextTabStrip.hasTab(2)) { - changed++; - } - if ((hasMedia[5] <= 0) == scrollSlidingTextTabStrip.hasTab(5)) { - changed++; - } - if ((hasMedia[6] <= 0) == scrollSlidingTextTabStrip.hasTab(6)) { - changed++; } if (changed > 0) { if (animated && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { @@ -4409,88 +4896,110 @@ public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues sta if (changed > 3) { idToView = null; } - if (chatUsersAdapter.chatInfo != null) { - if (!scrollSlidingTextTabStrip.hasTab(7)) { - scrollSlidingTextTabStrip.addTextTab(7, LocaleController.getString("GroupMembers", R.string.GroupMembers), idToView); - } - } - if (hasMedia[0] > 0) { - if (!scrollSlidingTextTabStrip.hasTab(0)) { - OnLongClickListener longClickListener = view -> { - ArrayList entries = new ArrayList<>(); - entries.add(LocaleController.getString("SharedPhotosAndVideos", R.string.SharedPhotosAndVideos)); - entries.add(LocaleController.getString("AllVideos", R.string.AllVideos)); - AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); - builder.setTitle(LocaleController.getString("SharedMediaTabFull2", R.string.SharedMediaTabFull2)); - final LinearLayout linearLayout = new LinearLayout(getContext()); - linearLayout.setOrientation(LinearLayout.VERTICAL); - builder.setView(linearLayout); - - for (int a = 0; a < entries.size(); a++) { - RadioColorCell cell = new RadioColorCell(getContext()); - cell.setPadding(AndroidUtilities.dp(4), 0, AndroidUtilities.dp(4), 0); - cell.setTag(a); - cell.setCheckColor(Theme.getColor(Theme.key_radioBackground), Theme.getColor(Theme.key_dialogRadioBackgroundChecked)); - cell.setTextAndValue(entries.get(a), (a == 0) != skipPhotos); - linearLayout.addView(cell); - cell.setOnClickListener(v -> { - Integer which = (Integer) v.getTag(); - skipPhotos = which == 1; - sharedMediaData[0] = new SharedMediaData(); - sharedMediaData[0].max_id[0] = ((int) dialog_id) == 0 ? Integer.MIN_VALUE : Integer.MAX_VALUE; - switchToCurrentSelectedMode(false); - builder.getDismissRunnable().run(); - }); + if (DialogObject.isUserDialog(dialog_id) && !DialogObject.isEncryptedDialog(dialog_id) && (userInfo != null && userInfo.stories_pinned_available || isStoriesView()) && includeStories()) { + if (!scrollSlidingTextTabStrip.hasTab(TAB_STORIES)) { + scrollSlidingTextTabStrip.addTextTab(TAB_STORIES, LocaleController.getString("ProfileStories", R.string.ProfileStories), idToView); + } + if (isStoriesView()) { + if (!scrollSlidingTextTabStrip.hasTab(TAB_ARCHIVED_STORIES)) { + scrollSlidingTextTabStrip.addTextTab(TAB_ARCHIVED_STORIES, LocaleController.getString("ProfileStories", R.string.ProfileStories), idToView); + } + scrollSlidingTextTabStrip.animationDuration = 420; + } + } + if (!isStoriesView()) { + if (chatUsersAdapter.chatInfo != null) { + if (!scrollSlidingTextTabStrip.hasTab(7)) { + scrollSlidingTextTabStrip.addTextTab(7, LocaleController.getString("GroupMembers", R.string.GroupMembers), idToView); + } + } + if (hasMedia[0] > 0) { + if (!scrollSlidingTextTabStrip.hasTab(0)) { + OnLongClickListener longClickListener = view -> { + ArrayList entries = new ArrayList<>(); + entries.add(LocaleController.getString("SharedPhotosAndVideos", R.string.SharedPhotosAndVideos)); + entries.add(LocaleController.getString("AllVideos", R.string.AllVideos)); + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder.setTitle(LocaleController.getString("SharedMediaTabFull2", R.string.SharedMediaTabFull2)); + final LinearLayout linearLayout = new LinearLayout(getContext()); + linearLayout.setOrientation(LinearLayout.VERTICAL); + builder.setView(linearLayout); + + for (int a = 0; a < entries.size(); a++) { + RadioColorCell cell = new RadioColorCell(getContext()); + cell.setPadding(AndroidUtilities.dp(4), 0, AndroidUtilities.dp(4), 0); + cell.setTag(a); + cell.setCheckColor(Theme.getColor(Theme.key_radioBackground), Theme.getColor(Theme.key_dialogRadioBackgroundChecked)); + cell.setTextAndValue(entries.get(a), (a == 0) != skipPhotos); + linearLayout.addView(cell); + cell.setOnClickListener(v -> { + Integer which = (Integer) v.getTag(); + skipPhotos = which == 1; + sharedMediaData[0] = new SharedMediaData(); + sharedMediaData[0].max_id[0] = ((int) dialog_id) == 0 ? Integer.MIN_VALUE : Integer.MAX_VALUE; + switchToCurrentSelectedMode(false); + builder.getDismissRunnable().run(); + }); + } + builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + builder.show(); + return true; + }; + if (hasMedia[1] == 0 && hasMedia[2] == 0 && hasMedia[3] == 0 && hasMedia[4] == 0 && hasMedia[5] == 0 && hasMedia[6] == 0 && chatUsersAdapter.chatInfo == null) { + scrollSlidingTextTabStrip.addTextTab(0, LocaleController.getString("SharedMediaTabFull2", R.string.SharedMediaTabFull2), idToView, longClickListener); + } else { + scrollSlidingTextTabStrip.addTextTab(0, LocaleController.getString("SharedMediaTab2", R.string.SharedMediaTab2), idToView, longClickListener); + } + } + if (hasMedia[0] > 0) { + if (!scrollSlidingTextTabStrip.hasTab(0)) { + if (hasMedia[1] == 0 && hasMedia[2] == 0 && hasMedia[3] == 0 && hasMedia[4] == 0 && hasMedia[5] == 0 && hasMedia[6] == 0 && chatUsersAdapter.chatInfo == null) { + scrollSlidingTextTabStrip.addTextTab(0, LocaleController.getString("SharedMediaTabFull2", R.string.SharedMediaTabFull2), idToView); + } else { + scrollSlidingTextTabStrip.addTextTab(0, LocaleController.getString("SharedMediaTab2", R.string.SharedMediaTab2), idToView); + } + } + } + if (hasMedia[1] > 0) { + if (!scrollSlidingTextTabStrip.hasTab(1)) { + scrollSlidingTextTabStrip.addTextTab(1, LocaleController.getString("SharedFilesTab2", R.string.SharedFilesTab2), idToView); + } + } + if (!DialogObject.isEncryptedDialog(dialog_id)) { + if (hasMedia[3] > 0) { + if (!scrollSlidingTextTabStrip.hasTab(3)) { + scrollSlidingTextTabStrip.addTextTab(3, LocaleController.getString("SharedLinksTab2", R.string.SharedLinksTab2), idToView); + } + } + if (hasMedia[4] > 0) { + if (!scrollSlidingTextTabStrip.hasTab(4)) { + scrollSlidingTextTabStrip.addTextTab(4, LocaleController.getString("SharedMusicTab2", R.string.SharedMusicTab2), idToView); + } } - builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - builder.show(); - return true; - }; - if (hasMedia[1] == 0 && hasMedia[2] == 0 && hasMedia[3] == 0 && hasMedia[4] == 0 && hasMedia[5] == 0 && hasMedia[6] == 0 && chatUsersAdapter.chatInfo == null) { - scrollSlidingTextTabStrip.addTextTab(0, LocaleController.getString("SharedMediaTabFull2", R.string.SharedMediaTabFull2), idToView, longClickListener); } else { - scrollSlidingTextTabStrip.addTextTab(0, LocaleController.getString("SharedMediaTab2", R.string.SharedMediaTab2), idToView, longClickListener); + if (hasMedia[4] > 0) { + if (!scrollSlidingTextTabStrip.hasTab(4)) { + scrollSlidingTextTabStrip.addTextTab(4, LocaleController.getString("SharedMusicTab2", R.string.SharedMusicTab2), idToView); + } + } } - } - } - if (hasMedia[1] > 0) { - if (!scrollSlidingTextTabStrip.hasTab(1)) { - scrollSlidingTextTabStrip.addTextTab(1, LocaleController.getString("SharedFilesTab2", R.string.SharedFilesTab2), idToView); - } - } - if (!DialogObject.isEncryptedDialog(dialog_id)) { - if (hasMedia[3] > 0) { - if (!scrollSlidingTextTabStrip.hasTab(3)) { - scrollSlidingTextTabStrip.addTextTab(3, LocaleController.getString("SharedLinksTab2", R.string.SharedLinksTab2), idToView); + if (hasMedia[2] > 0) { + if (!scrollSlidingTextTabStrip.hasTab(2)) { + scrollSlidingTextTabStrip.addTextTab(2, LocaleController.getString("SharedVoiceTab2", R.string.SharedVoiceTab2), idToView); + } } - } - if (hasMedia[4] > 0) { - if (!scrollSlidingTextTabStrip.hasTab(4)) { - scrollSlidingTextTabStrip.addTextTab(4, LocaleController.getString("SharedMusicTab2", R.string.SharedMusicTab2), idToView); + if (hasMedia[5] > 0) { + if (!scrollSlidingTextTabStrip.hasTab(5)) { + scrollSlidingTextTabStrip.addTextTab(5, LocaleController.getString("SharedGIFsTab2", R.string.SharedGIFsTab2), idToView); + } } - } - } else { - if (hasMedia[4] > 0) { - if (!scrollSlidingTextTabStrip.hasTab(4)) { - scrollSlidingTextTabStrip.addTextTab(4, LocaleController.getString("SharedMusicTab2", R.string.SharedMusicTab2), idToView); + if (hasMedia[6] > 0) { + if (!scrollSlidingTextTabStrip.hasTab(6)) { + scrollSlidingTextTabStrip.addTextTab(6, LocaleController.getString("SharedGroupsTab2", R.string.SharedGroupsTab2), idToView); + } } } } - if (hasMedia[2] > 0) { - if (!scrollSlidingTextTabStrip.hasTab(2)) { - scrollSlidingTextTabStrip.addTextTab(2, LocaleController.getString("SharedVoiceTab2", R.string.SharedVoiceTab2), idToView); - } - } - if (hasMedia[5] > 0) { - if (!scrollSlidingTextTabStrip.hasTab(5)) { - scrollSlidingTextTabStrip.addTextTab(5, LocaleController.getString("SharedGIFsTab2", R.string.SharedGIFsTab2), idToView); - } - } - if (hasMedia[6] > 0) { - if (!scrollSlidingTextTabStrip.hasTab(6)) { - scrollSlidingTextTabStrip.addTextTab(6, LocaleController.getString("SharedGroupsTab2", R.string.SharedGroupsTab2), idToView); - } - } } int id = scrollSlidingTextTabStrip.getCurrentTabId(); if (id >= 0) { @@ -4604,8 +5113,14 @@ private void switchToCurrentSelectedMode(boolean animated) { } } else { mediaPages[a].listView.setPinnedHeaderShadowDrawable(null); - - if (mediaPages[a].selectedType == 0) { + mediaPages[a].listView.setPadding( + mediaPages[a].listView.getPaddingLeft(), + AndroidUtilities.dp(2) + (mediaPages[a].listView.hintPaddingTop = mediaPages[a].selectedType == TAB_ARCHIVED_STORIES ? AndroidUtilities.dp(64) : 0), + mediaPages[a].listView.getPaddingRight(), + mediaPages[a].listView.getPaddingBottom() + ); + + if (mediaPages[a].selectedType == TAB_PHOTOVIDEO) { if (currentAdapter != photoVideoAdapter) { recycleAdapter(currentAdapter); mediaPages[a].listView.setAdapter(photoVideoAdapter); @@ -4614,13 +5129,13 @@ private void switchToCurrentSelectedMode(boolean animated) { if (sharedMediaData[0].fastScrollDataLoaded && !sharedMediaData[0].fastScrollPeriods.isEmpty()) { fastScrollVisible = true; } - spanCount = mediaColumnsCount; + spanCount = mediaColumnsCount[0]; mediaPages[a].listView.setPinnedHeaderShadowDrawable(pinnedHeaderShadowDrawable); if (sharedMediaData[0].recycledViewPool == null) { sharedMediaData[0].recycledViewPool = new RecyclerView.RecycledViewPool(); } viewPool = sharedMediaData[0].recycledViewPool; - } else if (mediaPages[a].selectedType == 1) { + } else if (mediaPages[a].selectedType == TAB_FILES) { if (sharedMediaData[1].fastScrollDataLoaded && !sharedMediaData[1].fastScrollPeriods.isEmpty()) { fastScrollVisible = true; } @@ -4628,7 +5143,7 @@ private void switchToCurrentSelectedMode(boolean animated) { recycleAdapter(currentAdapter); mediaPages[a].listView.setAdapter(documentsAdapter); } - } else if (mediaPages[a].selectedType == 2) { + } else if (mediaPages[a].selectedType == TAB_VOICE) { if (sharedMediaData[2].fastScrollDataLoaded && !sharedMediaData[2].fastScrollPeriods.isEmpty()) { fastScrollVisible = true; } @@ -4636,12 +5151,12 @@ private void switchToCurrentSelectedMode(boolean animated) { recycleAdapter(currentAdapter); mediaPages[a].listView.setAdapter(voiceAdapter); } - } else if (mediaPages[a].selectedType == 3) { + } else if (mediaPages[a].selectedType == TAB_LINKS) { if (currentAdapter != linksAdapter) { recycleAdapter(currentAdapter); mediaPages[a].listView.setAdapter(linksAdapter); } - } else if (mediaPages[a].selectedType == 4) { + } else if (mediaPages[a].selectedType == TAB_AUDIO) { if (sharedMediaData[4].fastScrollDataLoaded && !sharedMediaData[4].fastScrollPeriods.isEmpty()) { fastScrollVisible = true; } @@ -4649,28 +5164,40 @@ private void switchToCurrentSelectedMode(boolean animated) { recycleAdapter(currentAdapter); mediaPages[a].listView.setAdapter(audioAdapter); } - } else if (mediaPages[a].selectedType == 5) { + } else if (mediaPages[a].selectedType == TAB_GIF) { if (currentAdapter != gifAdapter) { recycleAdapter(currentAdapter); mediaPages[a].listView.setAdapter(gifAdapter); } - } else if (mediaPages[a].selectedType == 6) { + } else if (mediaPages[a].selectedType == TAB_COMMON_GROUPS) { if (currentAdapter != commonGroupsAdapter) { recycleAdapter(currentAdapter); mediaPages[a].listView.setAdapter(commonGroupsAdapter); } - } else if (mediaPages[a].selectedType == 7) { + } else if (mediaPages[a].selectedType == TAB_GROUPUSERS) { if (currentAdapter != chatUsersAdapter) { recycleAdapter(currentAdapter); mediaPages[a].listView.setAdapter(chatUsersAdapter); } + } else if (mediaPages[a].selectedType == TAB_STORIES) { + if (currentAdapter != storiesAdapter) { + recycleAdapter(currentAdapter); + mediaPages[a].listView.setAdapter(storiesAdapter); + } + spanCount = mediaColumnsCount[1]; + } else if (mediaPages[a].selectedType == TAB_ARCHIVED_STORIES) { + if (currentAdapter != archivedStoriesAdapter) { + recycleAdapter(currentAdapter); + mediaPages[a].listView.setAdapter(archivedStoriesAdapter); + } + spanCount = mediaColumnsCount[1]; } - if (mediaPages[a].selectedType == 0 || mediaPages[a].selectedType == 2 || mediaPages[a].selectedType == 5 || mediaPages[a].selectedType == 6 || mediaPages[a].selectedType == 7 && !delegate.canSearchMembers()) { + if (mediaPages[a].selectedType == TAB_PHOTOVIDEO || mediaPages[a].selectedType == TAB_STORIES || mediaPages[a].selectedType == TAB_ARCHIVED_STORIES || mediaPages[a].selectedType == TAB_VOICE || mediaPages[a].selectedType == TAB_GIF || mediaPages[a].selectedType == TAB_COMMON_GROUPS || mediaPages[a].selectedType == TAB_GROUPUSERS && !delegate.canSearchMembers()) { if (animated) { searchItemState = 2; } else { searchItemState = 0; - searchItem.setVisibility(View.INVISIBLE); + searchItem.setVisibility(isStoriesView() ? View.GONE : View.INVISIBLE); } } else { if (animated) { @@ -4679,7 +5206,7 @@ private void switchToCurrentSelectedMode(boolean animated) { searchItemState = 1; searchItem.setVisibility(View.VISIBLE); } else { - searchItem.setVisibility(INVISIBLE); + searchItem.setVisibility(isStoriesView() ? View.GONE : View.INVISIBLE); } searchItem.setAlpha(0.0f); } else { @@ -4691,7 +5218,7 @@ private void switchToCurrentSelectedMode(boolean animated) { searchItem.setAlpha(1.0f); searchItem.setVisibility(View.VISIBLE); } else { - searchItem.setVisibility(INVISIBLE); + searchItem.setVisibility(isStoriesView() ? View.GONE : View.INVISIBLE); searchItem.setAlpha(0.0f); } } @@ -4702,6 +5229,16 @@ private void switchToCurrentSelectedMode(boolean animated) { } } else if (mediaPages[a].selectedType == 7) { + } else if (mediaPages[a].selectedType == TAB_STORIES) { + StoriesController.StoriesList storiesList = storiesAdapter.storiesList; + storiesAdapter.load(false); + mediaPages[a].emptyView.showProgress(storiesList != null && (storiesList.isLoading() || hasInternet() && storiesList.getCount() > 0), animated); + fastScrollVisible = storiesList != null && storiesList.getCount() > 0; + } else if (mediaPages[a].selectedType == TAB_ARCHIVED_STORIES) { + StoriesController.StoriesList storiesList = archivedStoriesAdapter.storiesList; + archivedStoriesAdapter.load(false); + mediaPages[a].emptyView.showProgress(storiesList != null && (storiesList.isLoading() || hasInternet() && storiesList.getCount() > 0), animated); + fastScrollVisible = storiesList != null && storiesList.getCount() > 0; } else { if (!sharedMediaData[mediaPages[a].selectedType].loading && !sharedMediaData[mediaPages[a].selectedType].endReached[0] && sharedMediaData[mediaPages[a].selectedType].messages.isEmpty()) { sharedMediaData[mediaPages[a].selectedType].loading = true; @@ -4717,11 +5254,25 @@ private void switchToCurrentSelectedMode(boolean animated) { profileActivity.getMediaDataController().loadMedia(dialog_id, 50, 0, 0, type, topicId, 1, profileActivity.getClassGuid(), sharedMediaData[mediaPages[a].selectedType].requestIndex, skipPhotos); } } + if (mediaPages[a].selectedType == TAB_STORIES) { + mediaPages[a].emptyView.setStickerType(StickerEmptyView.STICKER_TYPE_ALBUM); + mediaPages[a].emptyView.title.setText(isStoriesView() ? LocaleController.getString(R.string.NoPublicStoriesTitle) : LocaleController.getString(R.string.NoStoriesTitle)); + mediaPages[a].emptyView.subtitle.setText(isStoriesView() ? LocaleController.getString("NoStoriesSubtitle") : ""); + } else if (mediaPages[a].selectedType == TAB_ARCHIVED_STORIES) { + mediaPages[a].emptyView.setStickerType(StickerEmptyView.STICKER_TYPE_ALBUM); + mediaPages[a].emptyView.title.setText(LocaleController.getString("NoArchivedStoriesTitle")); + mediaPages[a].emptyView.subtitle.setText(isStoriesView() ? LocaleController.getString("NoArchivedStoriesSubtitle") : ""); + } else { + mediaPages[a].emptyView.setStickerType(StickerEmptyView.STICKER_TYPE_SEARCH); + mediaPages[a].emptyView.title.setText(LocaleController.getString("NoResult", R.string.NoResult)); + mediaPages[a].emptyView.subtitle.setText(LocaleController.getString("SearchEmptyViewFilteredSubtitle2", R.string.SearchEmptyViewFilteredSubtitle2)); + } mediaPages[a].listView.setVisibility(View.VISIBLE); } mediaPages[a].fastScrollEnabled = fastScrollVisible; updateFastScrollVisibility(mediaPages[a], false); mediaPages[a].layoutManager.setSpanCount(spanCount); + mediaPages[a].listView.invalidateItemDecorations(); mediaPages[a].listView.setRecycledViewPool(viewPool); mediaPages[a].animationSupportingListView.setRecycledViewPool(viewPool); @@ -4730,7 +5281,7 @@ private void switchToCurrentSelectedMode(boolean animated) { actionBar.closeSearchField(); searchItemState = 0; searchItem.setAlpha(0.0f); - searchItem.setVisibility(View.INVISIBLE); + searchItem.setVisibility(isStoriesView() ? View.GONE : View.INVISIBLE); } } @@ -4775,6 +5326,7 @@ private boolean onItemLongClick(MessageObject item, View view, int a) { if (!isActionModeShowed) { showActionMode(true); } + onActionModeSelectedUpdate(selectedFiles[0]); updateForwardItem(); return true; } @@ -4784,6 +5336,9 @@ private void onItemClick(int index, View view, MessageObject message, int a, int return; } if (isActionModeShowed) { + if (selectedMode == TAB_STORIES && !isStoriesView()) { + return; + } int loadIndex = message.getDialogId() == dialog_id ? 0 : 1; if (selectedFiles[loadIndex].indexOfKey(message.getId()) >= 0) { selectedFiles[loadIndex].remove(message.getId()); @@ -4799,6 +5354,7 @@ private void onItemClick(int index, View view, MessageObject message, int a, int cantDeleteMessagesCount++; } } + onActionModeSelectedUpdate(selectedFiles[0]); if (selectedFiles[0].size() == 0 && selectedFiles[1].size() == 0) { showActionMode(false); } else { @@ -4896,6 +5452,9 @@ private void onItemClick(int index, View view, MessageObject message, int a, int } catch (Exception e) { FileLog.e(e); } + } else if (selectedMode == TAB_STORIES || selectedMode == TAB_ARCHIVED_STORIES) { + StoriesController.StoriesList storiesList = (selectedMode == TAB_STORIES ? storiesAdapter : archivedStoriesAdapter).storiesList; + profileActivity.getOrCreateStoryViewer().open(getContext(), message.getId(), storiesList, StoriesListPlaceProvider.of(mediaPages[a].listView)); } } updateForwardItem(); @@ -5434,14 +5993,18 @@ public void requestLayout() { private class SharedPhotoVideoAdapter extends RecyclerListView.FastScrollAdapter { - private Context mContext; - private boolean inFastScrollMode; + protected Context mContext; + protected boolean inFastScrollMode; SharedPhotoVideoCell2.SharedResources sharedResources; public SharedPhotoVideoAdapter(Context context) { mContext = context; } + public int getTopOffset() { + return 0; + } + public int getPositionForIndex(int i) { return sharedMediaData[0].startOffset + i; } @@ -5498,9 +6061,12 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType if (sharedResources == null) { sharedResources = new SharedPhotoVideoCell2.SharedResources(parent.getContext(), resourcesProvider); } - view = new SharedPhotoVideoCell2(mContext, sharedResources, profileActivity.getCurrentAccount()); - SharedPhotoVideoCell2 cell = (SharedPhotoVideoCell2) view; + SharedPhotoVideoCell2 cell = new SharedPhotoVideoCell2(mContext, sharedResources, profileActivity.getCurrentAccount()); cell.setGradientView(globalGradientView); + if (this == storiesAdapter || this == archivedStoriesAdapter) { + cell.isStory = true; + } + view = cell; break; default: case 2: @@ -5520,7 +6086,14 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { SharedPhotoVideoCell2 cell = (SharedPhotoVideoCell2) holder.itemView; int oldMessageId = cell.getMessageId(); - int parentCount = this == photoVideoAdapter ? mediaColumnsCount : animateToColumnsCount; + int parentCount; + if (this == photoVideoAdapter) { + parentCount = mediaColumnsCount[0]; + } else if (this == storiesAdapter || this == archivedStoriesAdapter) { + parentCount = mediaColumnsCount[1]; + } else { + parentCount = animateToColumnsCount; + } if (index >= 0 && index < messageObjects.size()) { MessageObject messageObject = messageObjects.get(index); boolean animated = messageObject.getId() == oldMessageId; @@ -5571,9 +6144,21 @@ public String getLetter(int position) { @Override public void getPositionForScrollProgress(RecyclerListView listView, float progress, int[] position) { int viewHeight = listView.getChildAt(0).getMeasuredHeight(); - int totalHeight = (int) (Math.ceil(getTotalItemsCount() / (float) mediaColumnsCount) * viewHeight); + int columnsCount; + if (this == animationSupportingPhotoVideoAdapter || this == animationSupportingStoriesAdapter || this == animationSupportingArchivedStoriesAdapter) { + columnsCount = animateToColumnsCount; + } else if (this == storiesAdapter || this == archivedStoriesAdapter) { + columnsCount = mediaColumnsCount[1]; + } else { + columnsCount = mediaColumnsCount[0]; + } + int totalHeight = (int) (Math.ceil(getTotalItemsCount() / (float) columnsCount) * viewHeight); int listHeight = listView.getMeasuredHeight() - listView.getPaddingTop(); - position[0] = (int) ((progress * (totalHeight -listHeight)) / viewHeight) * mediaColumnsCount; + if (viewHeight == 0) { + position[0] = position[1] = 0; + return; + } + position[0] = (int) ((progress * (totalHeight - listHeight)) / viewHeight) * columnsCount; position[1] = (int) (progress * (totalHeight - listHeight)) % viewHeight; } @@ -5616,8 +6201,15 @@ public int getTotalItemsCount() { @Override public float getScrollProgress(RecyclerListView listView) { - int parentCount = this == photoVideoAdapter ? mediaColumnsCount : animateToColumnsCount; - int cellCount = (int) Math.ceil(getTotalItemsCount() / (float) parentCount); + int columnsCount; + if (this == animationSupportingPhotoVideoAdapter || this == animationSupportingStoriesAdapter || this == animationSupportingArchivedStoriesAdapter) { + columnsCount = animateToColumnsCount; + } else if (this == storiesAdapter || this == archivedStoriesAdapter) { + columnsCount = mediaColumnsCount[1]; + } else { + columnsCount = mediaColumnsCount[0]; + } + int cellCount = (int) Math.ceil(getTotalItemsCount() / (float) columnsCount); if (listView.getChildCount() == 0) { return 0; } @@ -5629,12 +6221,12 @@ public float getScrollProgress(RecyclerListView listView) { } float childTop = firstChild.getTop() - listView.getPaddingTop(); float listH = listView.getMeasuredHeight() - listView.getPaddingTop(); - float scrollY = (firstPosition / parentCount) * cellHeight - childTop; + float scrollY = (firstPosition / columnsCount) * cellHeight - childTop; return scrollY / (((float) cellCount) * cellHeight - listH); } public boolean fastScrollIsVisible(RecyclerListView listView) { - int parentCount = this == photoVideoAdapter ? mediaColumnsCount : animateToColumnsCount; + int parentCount = this == photoVideoAdapter || this == storiesAdapter || this == archivedStoriesAdapter ? mediaColumnsCount[0] : animateToColumnsCount; int cellCount = (int) Math.ceil(getTotalItemsCount() / (float) parentCount); if (listView.getChildCount() == 0) { return false; @@ -5645,7 +6237,7 @@ public boolean fastScrollIsVisible(RecyclerListView listView) { @Override public void onFastScrollSingleTap() { - showMediaCalendar(true); + showMediaCalendar(0, true); } } @@ -6237,6 +6829,174 @@ public int getItemViewType(int i) { } } + public int getStoriesCount(int tab) { + if (tab == TAB_STORIES) { + return storiesAdapter.storiesList.getCount(); + } else if (tab == TAB_ARCHIVED_STORIES) { + return archivedStoriesAdapter.storiesList.getCount(); + } + return 0; + } + + private class StoriesAdapter extends SharedPhotoVideoAdapter { + + private final boolean isArchive; + public StoriesController.StoriesList storiesList; + private StoriesAdapter supportingAdapter; + private int id; + + public StoriesAdapter(Context context, boolean isArchive) { + super(context); + this.isArchive = isArchive; + storiesList = profileActivity.getMessagesController().getStoriesController().getStoriesList(dialog_id, isArchive ? StoriesController.StoriesList.TYPE_ARCHIVE : StoriesController.StoriesList.TYPE_PINNED); + id = storiesList.link(); + checkColumns(); + } + + public StoriesAdapter makeSupporting() { + StoriesAdapter adapter = new StoriesAdapter(getContext(), isArchive, storiesList); + this.supportingAdapter = adapter; + return adapter; + } + + public void destroy() { + if (storiesList != null) { + storiesList.unlink(id); + } + } + + private StoriesAdapter(Context context, boolean isArchive, StoriesController.StoriesList list) { + super(context); + this.isArchive = isArchive; + this.storiesList = list; + } + + private void checkColumns() { + if ((!storiesColumnsCountSet || allowStoriesSingleColumn && storiesList.getCount() > 1) && storiesList.getCount() > 0 && !isStoriesView()) { + if (storiesList.getCount() < 5) { + mediaColumnsCount[1] = storiesList.getCount(); + if (mediaPages != null && mediaPages[0] != null && mediaPages[1] != null && mediaPages[0].listView != null && mediaPages[1].listView != null) { + switchToCurrentSelectedMode(false); + } + allowStoriesSingleColumn = mediaColumnsCount[1] == 1; + } else if (allowStoriesSingleColumn) { + allowStoriesSingleColumn = false; + mediaColumnsCount[1] = Math.max(2, SharedConfig.storiesColumnsCount); + if (mediaPages != null && mediaPages[0] != null && mediaPages[1] != null && mediaPages[0].listView != null && mediaPages[1].listView != null) { + switchToCurrentSelectedMode(false); + } + } + storiesColumnsCountSet = true; + } + } + + @Override + public void notifyDataSetChanged() { + super.notifyDataSetChanged(); + if (supportingAdapter != null) { + supportingAdapter.notifyDataSetChanged(); + } + checkColumns(); + } + + public int columnsCount() { + if (this == photoVideoAdapter) { + return mediaColumnsCount[0]; + } else if (this == storiesAdapter || this == archivedStoriesAdapter) { + return mediaColumnsCount[1]; + } else { + return animateToColumnsCount; + } + } + + @Override + public int getTopOffset() { + return 0; + } + + @Override + public int getItemCount() { + return storiesList.isOnlyCache() && hasInternet() ? 0 : storiesList.getCount(); + } + + @Override + public int getTotalItemsCount() { + return getItemCount(); + } + + @Override + public int getPositionForIndex(int i) { + if (isArchive) { + return getTopOffset() + i; + } + return i; + } + + @Override + public boolean isEnabled(RecyclerView.ViewHolder holder) { + return false; + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + RecyclerView.ViewHolder holder = super.onCreateViewHolder(parent, viewType); + if (holder.itemView instanceof SharedPhotoVideoCell2) { + ((SharedPhotoVideoCell2) holder.itemView).isStory = true; + } + return holder; + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + int viewType = holder.getItemViewType(); + if (viewType == 0) { + SharedPhotoVideoCell2 cell = (SharedPhotoVideoCell2) holder.itemView; + cell.isStory = true; + position -= getTopOffset(); + if (position < 0 || position >= storiesList.messageObjects.size()) { + cell.setMessageObject(null, columnsCount()); + return; + } + MessageObject messageObject = storiesList.messageObjects.get(position); + cell.setMessageObject(messageObject, columnsCount()); + if (isActionModeShowed && messageObject != null) { + cell.setChecked(selectedFiles[messageObject.getDialogId() == dialog_id ? 0 : 1].indexOfKey(messageObject.getId()) >= 0, false); + } else { + cell.setChecked(false, false); + } + } + } + + public void load(boolean force) { + final int columnCount = columnsCount(); + final int count = Math.min(100, Math.max(1, columnCount / 2) * columnCount * columnCount); + storiesList.load(force, count); + } + + @Override + public int getItemViewType(int i) { + return 0; + } + + @Override + public String getLetter(int position) { + position -= getTopOffset(); + if (position < 0 || position >= storiesList.messageObjects.size()) { + return null; + } + MessageObject messageObject = storiesList.messageObjects.get(position); + if (messageObject == null || messageObject.storyItem == null) { + return null; + } + return LocaleController.formatYearMont(messageObject.storyItem.date, true); + } + + @Override + public void onFastScrollSingleTap() { + showMediaCalendar(isArchive ? TAB_ARCHIVED_STORIES : TAB_STORIES, true); + } + } + private class ChatUsersAdapter extends RecyclerListView.SelectionAdapter { private Context mContext; @@ -6341,7 +7101,7 @@ public GroupUsersSearchAdapter(Context context) { searchCount--; if (searchCount == 0) { for (int a = 0; a < mediaPages.length; a++) { - if (mediaPages[a].selectedType == 7) { + if (mediaPages[a].selectedType == TAB_GROUPUSERS) { if (getItemCount() == 0) { mediaPages[a].emptyView.showProgress(false, true); } else { @@ -6355,7 +7115,7 @@ public GroupUsersSearchAdapter(Context context) { currentChat = delegate.getCurrentChat(); } - private boolean createMenuForParticipant(TLObject participant, boolean resultOnly) { + private boolean createMenuForParticipant(TLObject participant, boolean resultOnly, View view) { if (participant instanceof TLRPC.ChannelParticipant) { TLRPC.ChannelParticipant channelParticipant = (TLRPC.ChannelParticipant) participant; TLRPC.TL_chatChannelParticipant p = new TLRPC.TL_chatChannelParticipant(); @@ -6365,7 +7125,7 @@ private boolean createMenuForParticipant(TLObject participant, boolean resultOnl p.date = channelParticipant.date; participant = p; } - return delegate.onMemberClick((TLRPC.ChatParticipant) participant, true, resultOnly); + return delegate.onMemberClick((TLRPC.ChatParticipant) participant, true, resultOnly, view); } public void search(final String query, boolean animated) { @@ -6536,7 +7296,13 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType TLObject object = getItem((Integer) cell.getTag()); if (object instanceof TLRPC.ChannelParticipant) { TLRPC.ChannelParticipant participant = (TLRPC.ChannelParticipant) object; - return createMenuForParticipant(participant, !click); + +// int index = searchAdapterHelper.getGroupSearch().indexOf(object); +// if (index >= 0) { +// for () +// } + + return createMenuForParticipant(participant, !click, cell); } else { return false; } @@ -6727,35 +7493,83 @@ public ArrayList getThemeDescriptions() { return arrayList; } - public int getNextMediaColumnsCount(int mediaColumnsCount, boolean up) { - int newColumnsCount = mediaColumnsCount; - if (!up) { - if (mediaColumnsCount == 2) { - newColumnsCount = 3; - } else if (mediaColumnsCount == 3) { - newColumnsCount = 4; - } else if (mediaColumnsCount == 4) { - newColumnsCount = 5; - } else if (mediaColumnsCount == 5) { - newColumnsCount = 6; - } else if (mediaColumnsCount == 6) { - newColumnsCount = 9; + public int getNextMediaColumnsCount(int i, int mediaColumnsCount, boolean up) { + int newColumnsCount = mediaColumnsCount + (!up ? 1 : -1); + if (newColumnsCount > 6) { + newColumnsCount = !up ? 9 : 6; + } + return Utilities.clamp(newColumnsCount, 9, allowStoriesSingleColumn && i == 1 ? 1 : 2); + } + + private ActionBarMenuSubItem mediaZoomInItem; + private ActionBarMenuSubItem mediaZoomOutItem; + + public Boolean zoomIn() { + if (photoVideoChangeColumnsAnimation || mediaPages[0] == null) { + return null; + } + changeColumnsTab = mediaPages[0].selectedType; + final int ci = changeColumnsTab == TAB_STORIES || changeColumnsTab == TAB_ARCHIVED_STORIES ? 1 : 0; + int newColumnsCount = getNextMediaColumnsCount(ci, mediaColumnsCount[ci], true); + if (mediaZoomInItem != null && newColumnsCount == getNextMediaColumnsCount(ci, newColumnsCount, true)) { + mediaZoomInItem.setEnabled(false); + mediaZoomInItem.animate().alpha(0.5f).start(); + } + if (mediaColumnsCount[ci] != newColumnsCount) { + if (mediaZoomOutItem != null && !mediaZoomOutItem.isEnabled()) { + mediaZoomOutItem.setEnabled(true); + mediaZoomOutItem.animate().alpha(1f).start(); } - } else { - if (mediaColumnsCount == 9) { - newColumnsCount = 6; - } else if (mediaColumnsCount == 6) { - newColumnsCount = 5; - } else if (mediaColumnsCount == 5) { - newColumnsCount = 4; - } else if (mediaColumnsCount == 4) { - newColumnsCount = 3; - } else if (mediaColumnsCount == 3) { - newColumnsCount = 2; + if (ci == 0) { + SharedConfig.setMediaColumnsCount(newColumnsCount); + } else if (getStoriesCount(mediaPages[0].selectedType) >= 5) { + SharedConfig.setStoriesColumnsCount(newColumnsCount); } + animateToMediaColumnsCount(newColumnsCount); } + return newColumnsCount != getNextMediaColumnsCount(ci, newColumnsCount, true); + } - return newColumnsCount; + public Boolean zoomOut() { + if (photoVideoChangeColumnsAnimation || mediaPages[0] == null || allowStoriesSingleColumn) { + return null; + } + changeColumnsTab = mediaPages[0].selectedType; + final int ci = changeColumnsTab == TAB_STORIES || changeColumnsTab == TAB_ARCHIVED_STORIES ? 1 : 0; + int newColumnsCount = getNextMediaColumnsCount(ci, mediaColumnsCount[ci], false); + if (mediaZoomOutItem != null && newColumnsCount == getNextMediaColumnsCount(ci, newColumnsCount, false)) { + mediaZoomOutItem.setEnabled(false); + mediaZoomOutItem.animate().alpha(0.5f).start(); + } + if (mediaColumnsCount[ci] != newColumnsCount) { + if (mediaZoomInItem != null && !mediaZoomInItem.isEnabled()) { + mediaZoomInItem.setEnabled(true); + mediaZoomInItem.animate().alpha(1f).start(); + } + if (ci == 0) { + SharedConfig.setMediaColumnsCount(newColumnsCount); + } else if (getStoriesCount(mediaPages[0].selectedType) >= 5) { + SharedConfig.setStoriesColumnsCount(newColumnsCount); + } + animateToMediaColumnsCount(newColumnsCount); + } + return newColumnsCount != getNextMediaColumnsCount(ci, newColumnsCount, false); + } + + public boolean canZoomIn() { + if (mediaPages == null || mediaPages[0] == null) { + return false; + } + final int ci = mediaPages[0].selectedType == TAB_STORIES || mediaPages[0].selectedType == TAB_ARCHIVED_STORIES ? 1 : 0; + return mediaColumnsCount[ci] != getNextMediaColumnsCount(ci, mediaColumnsCount[ci], true); + } + + public boolean canZoomOut() { + if (mediaPages == null || mediaPages[0] == null || allowStoriesSingleColumn) { + return false; + } + final int ci = mediaPages[0].selectedType == TAB_STORIES || mediaPages[0].selectedType == TAB_ARCHIVED_STORIES ? 1 : 0; + return mediaColumnsCount[ci] != getNextMediaColumnsCount(ci, mediaColumnsCount[ci], false); } @Override @@ -6798,15 +7612,17 @@ public void setBackgroundColor(int color) { } } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + if (resourcesProvider != null) { + return resourcesProvider.getColor(key); + } + return Theme.getColor(key); } public interface Delegate { void scrollToSharedMedia(); - boolean onMemberClick(TLRPC.ChatParticipant participant, boolean b, boolean resultOnly); + boolean onMemberClick(TLRPC.ChatParticipant participant, boolean b, boolean resultOnly, View view); TLRPC.Chat getCurrentChat(); @@ -6818,4 +7634,32 @@ public interface Delegate { void updateSelectedMediaTabText(); } + + public float getTabProgress() { + float progress = 0; + for (int i = 0; i < mediaPages.length; ++i) { + if (mediaPages[i] != null) { + progress += mediaPages[i].selectedType * (1f - Math.abs(mediaPages[i].getTranslationX() / getWidth())); + } + } + return progress; + } + + protected void onTabProgress(float progress) {} + protected void onTabScroll(boolean scrolling) {} + + public static class InternalListView extends BlurredRecyclerView implements StoriesListPlaceProvider.ClippedView { + + public int hintPaddingTop; + + public InternalListView(Context context) { + super(context); + } + + @Override + public void updateClip(int[] clip) { + clip[0] = getPaddingTop() - AndroidUtilities.dp(2) - hintPaddingTop; + clip[1] = getMeasuredHeight() - getPaddingBottom(); + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SharingLocationsAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SharingLocationsAlert.java index 750152a3d8..bf9063c0db 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/SharingLocationsAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SharingLocationsAlert.java @@ -178,7 +178,7 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { pickerBottomLayout.setBackgroundColor(getThemedColor(Theme.key_dialogBackground)); containerView.addView(pickerBottomLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT | Gravity.BOTTOM)); pickerBottomLayout.cancelButton.setPadding(AndroidUtilities.dp(18), 0, AndroidUtilities.dp(18), 0); - pickerBottomLayout.cancelButton.setTextColor(getThemedColor(Theme.key_dialogTextRed)); + pickerBottomLayout.cancelButton.setTextColor(getThemedColor(Theme.key_text_RedBold)); pickerBottomLayout.cancelButton.setText(LocaleController.getString("StopAllLocationSharings", R.string.StopAllLocationSharings)); pickerBottomLayout.cancelButton.setOnClickListener(view -> { for (int a : SharedConfig.activeAccounts) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SimpleThemeDescription.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SimpleThemeDescription.java index 7c79b6cc4e..8a9c7b82ba 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/SimpleThemeDescription.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SimpleThemeDescription.java @@ -7,19 +7,19 @@ public class SimpleThemeDescription { private SimpleThemeDescription() {} - public static ThemeDescription createThemeDescription(ThemeDescription.ThemeDescriptionDelegate del, String key) { + public static ThemeDescription createThemeDescription(ThemeDescription.ThemeDescriptionDelegate del, int key) { return new ThemeDescription(null, 0, null, null, null, del, key); } - public static ArrayList createThemeDescriptions(ThemeDescription.ThemeDescriptionDelegate del, String... keys) { + public static ArrayList createThemeDescriptions(ThemeDescription.ThemeDescriptionDelegate del, int... keys) { ArrayList l = new ArrayList<>(keys.length); - for (String k : keys) { + for (int k : keys) { l.add(createThemeDescription(del, k)); } return l; } - public static void add(ArrayList descriptions, Runnable upd, String... keys) { + public static void add(ArrayList descriptions, Runnable upd, int... keys) { descriptions.addAll(SimpleThemeDescription.createThemeDescriptions(upd::run, keys)); } } \ No newline at end of file diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierFrameLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierFrameLayout.java index cdcaca27eb..03fd862ec8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierFrameLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierFrameLayout.java @@ -39,6 +39,7 @@ import org.telegram.ui.ActionBar.INavigationLayout; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.BlurSettingsBottomSheet; +import org.telegram.ui.ChatBackgroundDrawable; import java.util.ArrayList; @@ -49,6 +50,10 @@ public class SizeNotifierFrameLayout extends FrameLayout { private Rect rect = new Rect(); private Drawable backgroundDrawable; + private boolean backgroundMotion; + private Drawable oldBackgroundDrawable; + private boolean oldBackgroundMotion; + protected int keyboardHeight; private int bottomClip; private SizeNotifierFrameLayoutDelegate delegate; @@ -60,7 +65,6 @@ public class SizeNotifierFrameLayout extends FrameLayout { private float parallaxScale = 1.0f; private int backgroundTranslationY; private boolean paused = true; - private Drawable oldBackgroundDrawable; private INavigationLayout parentLayout; public AdjustPanLayoutHelper adjustPanLayoutHelper; private int emojiHeight; @@ -69,6 +73,7 @@ public class SizeNotifierFrameLayout extends FrameLayout { private boolean skipBackgroundDrawing; SnowflakesEffect snowflakesEffect; protected View backgroundView; + boolean attached; //blur variables @@ -101,6 +106,7 @@ public class SizeNotifierFrameLayout extends FrameLayout { int times; int count2; int times2; + private float themeAnimationValue = 1f; // public void invalidateBlur() { @@ -108,6 +114,16 @@ public void invalidateBlur() { invalidate(); } + public void invalidateBackground() { + if (backgroundView != null) { + backgroundView.invalidate(); + } + } + + public int getBottomPadding() { + return 0; + } + public interface SizeNotifierFrameLayoutDelegate { void onSizeChanged(int keyboardHeight, boolean isWidthGreater); @@ -122,8 +138,6 @@ public SizeNotifierFrameLayout(Context context, INavigationLayout layout) { setWillNotDraw(false); parentLayout = layout; adjustPanLayoutHelper = createAdjustPanLayoutHelper(); - addView(backgroundView = new BackgroundView(context), LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); - checkLayerType(); } private class BackgroundView extends View { @@ -137,17 +151,25 @@ protected void onDraw(Canvas canvas) { return; } Drawable newDrawable = getNewDrawable(); + boolean newMotion = getNewDrawableMotion(); if (newDrawable != backgroundDrawable && newDrawable != null) { if (Theme.isAnimatingColor()) { oldBackgroundDrawable = backgroundDrawable; + oldBackgroundMotion = backgroundMotion; } if (newDrawable instanceof MotionBackgroundDrawable) { MotionBackgroundDrawable motionBackgroundDrawable = (MotionBackgroundDrawable) newDrawable; motionBackgroundDrawable.setParentView(backgroundView); } backgroundDrawable = newDrawable; + if (attached && backgroundDrawable instanceof ChatBackgroundDrawable) { + ((ChatBackgroundDrawable) backgroundDrawable).onAttachedToWindow(); + } + backgroundMotion = newMotion; + themeAnimationValue = 0f; + checkMotion(); } - float themeAnimationValue = parentLayout != null ? parentLayout.getThemeAnimationValue() : 1.0f; + themeAnimationValue = Utilities.clamp(themeAnimationValue + AndroidUtilities.screenRefreshTime / 200, 1f, 0); for (int a = 0; a < 2; a++) { Drawable drawable = a == 0 ? oldBackgroundDrawable : backgroundDrawable; if (drawable == null) { @@ -158,6 +180,20 @@ protected void onDraw(Canvas canvas) { } else { drawable.setAlpha(255); } + float parallaxScale; + float translationX; + float translationY; + boolean drawMotion = a == 0 ? oldBackgroundMotion : backgroundMotion; + if (drawMotion) { + parallaxScale = SizeNotifierFrameLayout.this.parallaxScale; + translationX = SizeNotifierFrameLayout.this.translationX; + translationY = SizeNotifierFrameLayout.this.translationY; + } else { + parallaxScale = 1.0f; + translationX = 0; + translationY = 0; + + } if (drawable instanceof MotionBackgroundDrawable) { MotionBackgroundDrawable motionBackgroundDrawable = (MotionBackgroundDrawable) drawable; if (motionBackgroundDrawable.hasPattern()) { @@ -243,12 +279,41 @@ protected void onDraw(Canvas canvas) { checkSnowflake(canvas); canvas.restore(); } + } else { + if (bottomClip != 0) { + canvas.save(); + canvas.clipRect(0, 0, getMeasuredWidth(), getRootView().getMeasuredHeight() - bottomClip); + } + if (drawable instanceof ChatBackgroundDrawable) { + ((ChatBackgroundDrawable) drawable).setParent(this); + } + float x = -getMeasuredWidth() * (parallaxScale - 1f) / 2f + translationX; + float y = -getRootView().getMeasuredHeight() * (parallaxScale - 1f) / 2f + translationY; + drawable.setBounds( + (int) x, + (int) (backgroundTranslationY + y), + (int) (getMeasuredWidth() * parallaxScale + x), + (int) (backgroundTranslationY + getRootView().getMeasuredHeight() * parallaxScale + y) + ); + drawable.draw(canvas); + checkSnowflake(canvas); + if (bottomClip != 0) { + canvas.restore(); + } } if (a == 0 && oldBackgroundDrawable != null && themeAnimationValue >= 1.0f) { + if (attached && oldBackgroundDrawable instanceof ChatBackgroundDrawable) { + ((ChatBackgroundDrawable) oldBackgroundDrawable).onDetachedFromWindow(); + } oldBackgroundDrawable = null; + oldBackgroundMotion = false; + checkMotion(); backgroundView.invalidate(); } } + if (themeAnimationValue != 1f) { + backgroundView.invalidate(); + } } } @@ -256,11 +321,28 @@ public void setBackgroundImage(Drawable bitmap, boolean motion) { if (backgroundDrawable == bitmap) { return; } + if (backgroundView == null) { + addView(backgroundView = new BackgroundView(getContext()), 0, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + checkLayerType(); + } if (bitmap instanceof MotionBackgroundDrawable) { MotionBackgroundDrawable motionBackgroundDrawable = (MotionBackgroundDrawable) bitmap; motionBackgroundDrawable.setParentView(backgroundView); } + if (attached && backgroundDrawable instanceof ChatBackgroundDrawable) { + ((ChatBackgroundDrawable) backgroundDrawable).onDetachedFromWindow(); + } backgroundDrawable = bitmap; + if (attached && backgroundDrawable instanceof ChatBackgroundDrawable) { + ((ChatBackgroundDrawable) backgroundDrawable).onAttachedToWindow(); + } + checkMotion(); + backgroundView.invalidate(); + checkLayerType(); + } + + private void checkMotion() { + boolean motion = oldBackgroundMotion || backgroundMotion; if (motion) { if (parallaxEffect == null) { parallaxEffect = new WallpaperParallaxEffect(getContext()); @@ -268,7 +350,9 @@ public void setBackgroundImage(Drawable bitmap, boolean motion) { translationX = offsetX; translationY = offsetY; bgAngle = angle; - backgroundView.invalidate(); + if (backgroundView != null) { + backgroundView.invalidate(); + } }); if (getMeasuredWidth() != 0 && getMeasuredHeight() != 0) { parallaxScale = parallaxEffect.getScale(getMeasuredWidth(), getMeasuredHeight()); @@ -284,8 +368,6 @@ public void setBackgroundImage(Drawable bitmap, boolean motion) { translationX = 0; translationY = 0; } - backgroundView.invalidate(); - checkLayerType(); } private void checkLayerType() { @@ -360,14 +442,18 @@ public void notifyHeightChanged() { public void setBottomClip(int value) { if (value != bottomClip) { bottomClip = value; - backgroundView.invalidate(); + if (backgroundView != null) { + backgroundView.invalidate(); + } } } public void setBackgroundTranslation(int translation) { if (translation != backgroundTranslationY) { backgroundTranslationY = translation; - backgroundView.invalidate(); + if (backgroundView != null) { + backgroundView.invalidate(); + } } } @@ -409,7 +495,9 @@ public int getHeightWithKeyboard() { public void setEmojiKeyboardHeight(int height) { if (emojiHeight != height) { emojiHeight = height; - backgroundView.invalidate(); + if (backgroundView != null) { + backgroundView.invalidate(); + } } } @@ -417,12 +505,14 @@ public void setEmojiOffset(boolean animInProgress, float offset) { if (emojiOffset != offset || animationInProgress != animInProgress) { emojiOffset = offset; animationInProgress = animInProgress; - backgroundView.invalidate(); + if (backgroundView != null) { + backgroundView.invalidate(); + } } } private void checkSnowflake(Canvas canvas) { - if (((Theme.canStartHolidayAnimation() && LiteMode.isEnabled(LiteMode.FLAG_CHAT_BACKGROUND)) || NaConfig.INSTANCE.getChatDecoration().Int() == 1) && NaConfig.INSTANCE.getChatDecoration().Int() != 2) { + if (((backgroundView != null && Theme.canStartHolidayAnimation() && LiteMode.isEnabled(LiteMode.FLAG_CHAT_BACKGROUND)) || NaConfig.INSTANCE.getChatDecoration().Int() == 1) && NaConfig.INSTANCE.getChatDecoration().Int() != 2) { if (snowflakesEffect == null) { snowflakesEffect = new SnowflakesEffect(1); } @@ -441,7 +531,9 @@ protected AdjustPanLayoutHelper createAdjustPanLayoutHelper() { public void setSkipBackgroundDrawing(boolean skipBackgroundDrawing) { if (this.skipBackgroundDrawing != skipBackgroundDrawing) { this.skipBackgroundDrawing = skipBackgroundDrawing; - backgroundView.invalidate(); + if (backgroundView != null) { + backgroundView.invalidate(); + } } } @@ -449,6 +541,10 @@ protected Drawable getNewDrawable() { return Theme.getCachedWallpaperNonBlocking(); } + protected boolean getNewDrawableMotion() { + return Theme.isWallpaperMotion(); + } + @Override protected boolean verifyDrawable(Drawable who) { return who == getBackgroundImage() || super.verifyDrawable(who); @@ -675,15 +771,23 @@ protected void dispatchDraw(Canvas canvas) { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); + attached = true; if (needBlur && !blurIsRunning) { blurIsRunning = true; invalidateBlur = true; } + if (backgroundDrawable instanceof ChatBackgroundDrawable) { + ((ChatBackgroundDrawable) backgroundDrawable).onAttachedToWindow(); + } + if (oldBackgroundDrawable instanceof ChatBackgroundDrawable) { + ((ChatBackgroundDrawable) oldBackgroundDrawable).onAttachedToWindow(); + } } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); + attached = false; blurPaintTop.setShader(null); blurPaintTop2.setShader(null); blurPaintBottom.setShader(null); @@ -702,6 +806,13 @@ protected void onDetachedFromWindow() { } unusedBitmaps.clear(); blurIsRunning = false; + + if (backgroundDrawable instanceof ChatBackgroundDrawable) { + ((ChatBackgroundDrawable) backgroundDrawable).onDetachedFromWindow(); + } + if (oldBackgroundDrawable instanceof ChatBackgroundDrawable) { + ((ChatBackgroundDrawable) oldBackgroundDrawable).onDetachedFromWindow(); + } } public boolean blurWasDrawn() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierFrameLayoutPhoto.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierFrameLayoutPhoto.java index 3434c3d4fc..618ddab43a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierFrameLayoutPhoto.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierFrameLayoutPhoto.java @@ -19,6 +19,7 @@ public class SizeNotifierFrameLayoutPhoto extends FrameLayout { + private Activity activity; private Rect rect = new Rect(); private int keyboardHeight; private SizeNotifierFrameLayoutPhotoDelegate delegate; @@ -30,11 +31,16 @@ public interface SizeNotifierFrameLayoutPhotoDelegate { void onSizeChanged(int keyboardHeight, boolean isWidthGreater); } - public SizeNotifierFrameLayoutPhoto(Context context, boolean smoothKeyboard) { + public SizeNotifierFrameLayoutPhoto(Context context, Activity activity, boolean smoothKeyboard) { super(context); + setActivity(activity); useSmoothKeyboard = smoothKeyboard; } + public void setActivity(Activity activity) { + this.activity = activity; + } + public void setDelegate(SizeNotifierFrameLayoutPhotoDelegate sizeNotifierFrameLayoutPhotoDelegate) { delegate = sizeNotifierFrameLayoutPhotoDelegate; } @@ -60,7 +66,7 @@ public int measureKeyboardHeight() { int usableViewHeight = rootView.getHeight() - (rect.top != 0 ? AndroidUtilities.statusBarHeight : 0) - AndroidUtilities.getViewInset(rootView); return usableViewHeight - (rect.bottom - rect.top); } else { - int size = ((Activity) rootView.getContext()).getWindow().getDecorView().getHeight() - AndroidUtilities.getViewInset(rootView) - rootView.getBottom(); + int size = activity.getWindow().getDecorView().getHeight() - AndroidUtilities.getViewInset(rootView) - rootView.getBottom(); if (size <= Math.max(AndroidUtilities.dp(10), AndroidUtilities.statusBarHeight)) { size = 0; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SlideChooseView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SlideChooseView.java index 4d036a89e6..195aee15f5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/SlideChooseView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SlideChooseView.java @@ -258,9 +258,8 @@ public int getSelectedIndex() { return selectedIndex; } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SnowflakesEffect.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SnowflakesEffect.java index a7c16946f6..91cc0e56e4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/SnowflakesEffect.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SnowflakesEffect.java @@ -16,7 +16,6 @@ import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.LiteMode; -import org.telegram.messenger.SharedConfig; import org.telegram.messenger.Utilities; import org.telegram.ui.ActionBar.Theme; @@ -27,7 +26,7 @@ public class SnowflakesEffect { private Paint particlePaint; private Paint particleThinPaint; private Paint bitmapPaint = new Paint(); - private String colorKey = Theme.key_actionBarDefaultTitle; + private int colorKey = Theme.key_actionBarDefaultTitle; private int viewType; Bitmap particleBitmap; @@ -123,7 +122,7 @@ public SnowflakesEffect(int viewType) { } } - public void setColorKey(String key) { + public void setColorKey(int key) { colorKey = key; updateColors(); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/StableAnimator.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/StableAnimator.java new file mode 100644 index 0000000000..03adb6dc43 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/StableAnimator.java @@ -0,0 +1,68 @@ +package org.telegram.ui.Components; + +import android.animation.TimeAnimator; + +import org.telegram.messenger.AndroidUtilities; + +/** + * Good for animations with a jank at the beginning. + */ +public class StableAnimator extends TimeAnimator { + private int times = 0; + private int totalTimes = 0; + private AnimatorUpdateListener updateListener; + private Object animatedValue; + private float[] floatValues; + + public static StableAnimator ofFloat(float... values) { + StableAnimator anim = new StableAnimator(); + anim.setFloatValues(values); + return anim; + } + + @Override + public void setFloatValues(float[] floatValues) { + super.setFloatValues(floatValues); + this.floatValues = floatValues; + } + + @Override + public void addUpdateListener(AnimatorUpdateListener listener) { + updateListener = listener; + } + + @Override + public Object getAnimatedValue() { + return animatedValue; + } + + @Override + public void end() { + updateListener = null; + super.end(); + } + + @Override + public void start() { + setTimeListener((animation, totalTime, deltaTime) -> { + if (times > 0 && totalTimes > 0) { + times--; + if (updateListener != null) { + if (floatValues != null && floatValues.length == 2) { + float percent = (float) times / (float) totalTimes; + float fraction = getInterpolator().getInterpolation(1f - percent); + animatedValue = floatValues[0] + ((floatValues[1] - floatValues[0]) * fraction); + updateListener.onAnimationUpdate(this); + } else { + end(); + } + } + } else { + end(); + } + }); + this.times = (int) (getDuration() / AndroidUtilities.screenRefreshTime); + this.totalTimes = this.times; + super.start(); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/StaticLayoutEx.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/StaticLayoutEx.java index 5434d6cc0e..7de5f6732c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/StaticLayoutEx.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/StaticLayoutEx.java @@ -18,6 +18,8 @@ import android.text.TextUtils; import android.text.style.CharacterStyle; +import com.google.android.exoplayer2.util.Log; + import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.FileLog; @@ -133,6 +135,10 @@ public static StaticLayout createStaticLayout(CharSequence source, int bufstart, }*/ try { if (maxLines == 1) { + int index = TextUtils.indexOf(source, "\n") - 1; + if (index > 0) { + source = SpannableStringBuilder.valueOf(source.subSequence(0, index)).append("…"); + } CharSequence text = TextUtils.ellipsize(source, paint, ellipsisWidth, TextUtils.TruncateAt.END); return new StaticLayout(text, 0, text.length(), paint, outerWidth, align, spacingMult, spacingAdd, includePad); } else { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/StickerCategoriesListView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/StickerCategoriesListView.java index 3aae30f676..2a957d7ff0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/StickerCategoriesListView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/StickerCategoriesListView.java @@ -5,6 +5,7 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; +import android.annotation.SuppressLint; import android.content.Context; import android.content.res.Configuration; import android.graphics.Canvas; @@ -28,12 +29,12 @@ import org.telegram.SQLite.SQLiteDatabase; import org.telegram.SQLite.SQLitePreparedStatement; import org.telegram.messenger.AndroidUtilities; -import org.telegram.messenger.Fetcher; +import org.telegram.messenger.CacheFetcher; import org.telegram.messenger.FileLog; import org.telegram.messenger.LiteMode; import org.telegram.messenger.MessagesStorage; +import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; -import org.telegram.messenger.SharedConfig; import org.telegram.messenger.UserConfig; import org.telegram.messenger.Utilities; import org.telegram.tgnet.ConnectionsManager; @@ -60,7 +61,7 @@ public class StickerCategoriesListView extends RecyclerListView { private float shownButtonsAtStart = 6.5f; private static EmojiGroupFetcher fetcher = new EmojiGroupFetcher(); - public static Fetcher search = new EmojiSearch(); + public static CacheFetcher search = new EmojiSearch(); private EmojiCategory[] categories = null; private Adapter adapter; @@ -115,6 +116,7 @@ public StickerCategoriesListView(Context context, EmojiCategory[] additionalCate this(context, additionalCategories, categoriesType, null); } + @SuppressLint("NotifyDataSetChanged") public StickerCategoriesListView(Context context, EmojiCategory[] additionalCategories, @CategoriesType int categoriesType, Theme.ResourcesProvider resourcesProvider) { super(context, resourcesProvider); @@ -125,11 +127,10 @@ public StickerCategoriesListView(Context context, EmojiCategory[] additionalCate setLayoutManager(layoutManager = new LinearLayoutManager(context)); layoutManager.setOrientation(HORIZONTAL); -// setSelectorRadius(dp(15)); -// setSelectorType(Theme.RIPPLE_MASK_CIRCLE_20DP); -// setSelectorDrawableColor(getThemedColor(Theme.key_listSelector)); + setSelectorRadius(dp(15)); + setSelectorType(Theme.RIPPLE_MASK_CIRCLE_20DP); + setSelectorDrawableColor(getThemedColor(Theme.key_listSelector)); selectedPaint.setColor(getThemedColor(Theme.key_listSelector)); - setSelectorDrawableColor(0); setWillNotDraw(false); @@ -138,19 +139,22 @@ public StickerCategoriesListView(Context context, EmojiCategory[] additionalCate long start = System.currentTimeMillis(); fetcher.fetch(UserConfig.selectedAccount, categoriesType, (emojiGroups) -> { if (emojiGroups != null) { - categories = new EmojiCategory[(additionalCategories == null ? 0 : additionalCategories.length) + emojiGroups.groups.size()]; - int i = 0; - if (additionalCategories != null) { - for (; i < additionalCategories.length; ++i) { - categories[i] = additionalCategories[i]; + Runnable action = () -> { + categories = new EmojiCategory[(additionalCategories == null ? 0 : additionalCategories.length) + emojiGroups.groups.size()]; + int i = 0; + if (additionalCategories != null) { + for (; i < additionalCategories.length; ++i) { + categories[i] = additionalCategories[i]; + } } - } - for (int j = 0; j < emojiGroups.groups.size(); ++j) { - categories[i + j] = EmojiCategory.remote(emojiGroups.groups.get(j)); - } - adapter.notifyDataSetChanged(); - setCategoriesShownT(0); - updateCategoriesShown(categoriesShouldShow, System.currentTimeMillis() - start > 16); + for (int j = 0; j < emojiGroups.groups.size(); ++j) { + categories[i + j] = EmojiCategory.remote(emojiGroups.groups.get(j)); + } + adapter.notifyDataSetChanged(); + setCategoriesShownT(0); + updateCategoriesShown(categoriesShouldShow, System.currentTimeMillis() - start > 16); + }; + NotificationCenter.getInstance(UserConfig.selectedAccount).doOnIdle(action); } }); } @@ -196,6 +200,12 @@ public void scrollToStart() { smoothScrollBy(-getScrollToStartWidth(), 0, CubicBezierInterpolator.EASE_OUT_QUINT); } + public void scrollToSelected() { + final int dx = -getScrollToStartWidth() - Math.max(0, dontOccupyWidth) + selectedCategoryIndex * dp(34); + scrollBy(dx, 0); + post(() -> onScrolled(dx, 0)); + } + public void selectCategory(EmojiCategory category) { int index = -1; if (categories != null) { @@ -210,7 +220,7 @@ public void selectCategory(EmojiCategory category) { } public void selectCategory(int categoryIndex) { - if (selectedCategoryIndex < 0) { + if (selectedCategoryIndex < 0 && categoryIndex >= 0) { selectedIndex.set(categoryIndex, true); } this.selectedCategoryIndex = categoryIndex; @@ -231,6 +241,10 @@ public EmojiCategory getSelectedCategory() { return categories[selectedCategoryIndex]; } + public int getCategoryIndex() { + return selectedCategoryIndex; + } + @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); @@ -602,8 +616,6 @@ public CategoryButton(Context context) { setImageColor(getThemedColor(Theme.key_chat_emojiPanelIcon)); setScaleType(ScaleType.CENTER); - setBackground(Theme.createSelectorDrawable(getThemedColor(Theme.key_listSelector), Theme.RIPPLE_MASK_CIRCLE_20DP, dp(15))); - setLayerNum(layerNum); } @@ -849,10 +861,10 @@ public static EmojiCategory remote(TLRPC.TL_emojiGroup group) { } } - private static class EmojiGroupFetcher extends Fetcher { + private static class EmojiGroupFetcher extends CacheFetcher { @Override - protected void getRemote(int currentAccount, @CategoriesType Integer type, long hash, Utilities.Callback3 onResult) { + protected void getRemote(int currentAccount, @CategoriesType Integer type, long hash, Utilities.Callback4 onResult) { TLObject req; if (type == CategoriesType.STATUS) { req = new TLRPC.TL_messages_getEmojiStatusGroups(); @@ -867,12 +879,12 @@ protected void getRemote(int currentAccount, @CategoriesType Integer type, long ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> { if (res instanceof TLRPC.TL_messages_emojiGroupsNotModified) { - onResult.run(true, null, 0L); + onResult.run(true, null, 0L, true); } else if (res instanceof TLRPC.TL_messages_emojiGroups) { TLRPC.TL_messages_emojiGroups result = (TLRPC.TL_messages_emojiGroups) res; - onResult.run(false, result, (long) result.hash); + onResult.run(false, result, (long) result.hash, true); } else { - onResult.run(false, null, 0L); + onResult.run(false, null, 0L, true); } }); } @@ -939,20 +951,20 @@ protected void setLocal(int currentAccount, Integer type, TLRPC.TL_messages_emoj } } - private static class EmojiSearch extends Fetcher { + private static class EmojiSearch extends CacheFetcher { @Override - protected void getRemote(int currentAccount, String query, long hash, Utilities.Callback3 onResult) { + protected void getRemote(int currentAccount, String query, long hash, Utilities.Callback4 onResult) { TLRPC.TL_messages_searchCustomEmoji req = new TLRPC.TL_messages_searchCustomEmoji(); req.emoticon = query; req.hash = hash; ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> { if (res instanceof TLRPC.TL_emojiListNotModified) { - onResult.run(true, null, 0L); + onResult.run(true, null, 0L, true); } else if (res instanceof TLRPC.TL_emojiList) { TLRPC.TL_emojiList list = (TLRPC.TL_emojiList) res; - onResult.run(false, list, list.hash); + onResult.run(false, list, list.hash, true); } else { - onResult.run(false, null, 0L); + onResult.run(false, null, 0L, true); } }); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/StickerEmptyView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/StickerEmptyView.java index 2784390c57..49b113f0ac 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/StickerEmptyView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/StickerEmptyView.java @@ -3,6 +3,7 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.content.Context; +import android.util.Log; import android.util.TypedValue; import android.view.Gravity; import android.view.View; @@ -21,16 +22,17 @@ import org.telegram.messenger.SvgHelper; import org.telegram.messenger.UserConfig; import org.telegram.tgnet.TLRPC; -import org.telegram.ui.ActionBar.AdjustPanLayoutHelper; import org.telegram.ui.ActionBar.Theme; public class StickerEmptyView extends FrameLayout implements NotificationCenter.NotificationCenterDelegate { public final static int STICKER_TYPE_NO_CONTACTS = 0; public final static int STICKER_TYPE_SEARCH = 1; - public final static int STICKER_TYPE_DONE = 2; + public final static int STICKER_TYPE_ALBUM = 11; + public final static int STICKER_TYPE_PRIVACY = 12; + public final static int STICKER_TYPE_DONE = 16; - private LinearLayout linearLayout; + public LinearLayout linearLayout; public BackupImageView stickerView; private RadialProgressView progressBar; public final TextView title; @@ -142,9 +144,9 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto lastH = getMeasuredHeight(); } - String colorKey1 = Theme.key_emptyListPlaceholder; + int colorKey1 = Theme.key_emptyListPlaceholder; - public void setColors(String titleKey, String subtitleKey, String key1, String key2) { + public void setColors(int titleKey, int subtitleKey, int key1, int key2) { title.setTag(titleKey); title.setTextColor(getThemedColor(titleKey)); @@ -159,6 +161,7 @@ public void setVisibility(int visibility) { if (visibility == VISIBLE) { if (progressShowing) { linearLayout.animate().alpha(0f).scaleY(0.8f).scaleX(0.8f).setDuration(150).start(); + progressView.animate().setListener(null).cancel(); progressView.setVisibility(VISIBLE); progressView.setAlpha(1f); //showProgressRunnable.run(); @@ -232,7 +235,7 @@ private void setSticker() { if (set == null) { set = MediaDataController.getInstance(currentAccount).getStickerSetByEmojiOrName(AndroidUtilities.STICKERS_PLACEHOLDER_PACK_NAME); } - if (set != null && stickerType >= 0 && stickerType < set.documents.size() ) { + if (set != null && stickerType >= 0 && stickerType < set.documents.size()) { document = set.documents.get(stickerType); } imageFilter = "130_130"; @@ -369,9 +372,8 @@ public void setPreventMoving(boolean preventMoving) { } } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } public void setStickerType(int stickerType) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/StickerMasksAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/StickerMasksAlert.java index b4a078ca12..d6e74e7862 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/StickerMasksAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/StickerMasksAlert.java @@ -313,7 +313,7 @@ private void showShadow(boolean show, boolean animated) { public StickerMasksAlert(Context context, boolean isVideo, Theme.ResourcesProvider resourcesProvider) { super(context, true, resourcesProvider); - behindKeyboardColorKey = null; + behindKeyboardColorKey = -1; behindKeyboardColor = 0xff252525; useLightStatusBar = false; fixNavigationBar(0xff252525); @@ -1381,10 +1381,12 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType View view = null; switch (viewType) { case -1: - view = new ImageViewEmoji(context); + ImageViewEmoji imageViewEmoji = new ImageViewEmoji(context); + imageViewEmoji.getImageReceiver().setLayerNum(playingImagesLayerNum); + view = imageViewEmoji; break; case 0: - view = new StickerEmojiCell(context, false) { + StickerEmojiCell stickerEmojiCell = new StickerEmojiCell(context, false, resourcesProvider) { public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (currentType == MediaDataController.TYPE_EMOJIPACKS) { super.onMeasure(widthMeasureSpec, widthMeasureSpec); @@ -1393,6 +1395,8 @@ public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } } }; + stickerEmojiCell.getImageView().setLayerNum(playingImagesLayerNum); + view = stickerEmojiCell; break; case 1: view = new EmptyCell(context); @@ -1907,10 +1911,12 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType View view = null; switch (viewType) { case -1: - view = new ImageViewEmoji(context); + ImageViewEmoji imageViewEmoji = new ImageViewEmoji(context); + imageViewEmoji.getImageReceiver().setLayerNum(playingImagesLayerNum); + view = imageViewEmoji; break; case 0: - view = new StickerEmojiCell(context, false) { + StickerEmojiCell cell = new StickerEmojiCell(context, false, resourcesProvider) { public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (currentType == MediaDataController.TYPE_EMOJIPACKS) { super.onMeasure(widthMeasureSpec, widthMeasureSpec); @@ -1919,6 +1925,8 @@ public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } } }; + cell.getImageView().setLayerNum(playingImagesLayerNum); + view = cell; break; case 1: view = new EmptyCell(context); @@ -2111,4 +2119,10 @@ public void notifyDataSetChanged() { super.notifyDataSetChanged(); } } + + @Override + public void setImageReceiverNumLevel(int playingImages, int onShowing) { + super.setImageReceiverNumLevel(playingImages, onShowing); + stickersTab.setImageReceiversLayerNum(playingImages); + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/StickerSetBulletinLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/StickerSetBulletinLayout.java index e911a5c4c7..4e7d346a41 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/StickerSetBulletinLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/StickerSetBulletinLayout.java @@ -171,6 +171,9 @@ public StickerSetBulletinLayout(@NonNull Context context, TLObject setObject, in if (stickerSet.masks) { titleTextView.setText(LocaleController.getString("MasksArchived", R.string.MasksArchived)); subtitleTextView.setText(LocaleController.formatString("MasksArchivedInfo", R.string.MasksArchivedInfo, stickerSet.title)); + } else if (stickerSet.emojis) { + titleTextView.setText(LocaleController.getString("EmojiArchived", R.string.EmojiArchived)); + subtitleTextView.setText(LocaleController.formatString("EmojiArchivedInfo", R.string.EmojiArchivedInfo, stickerSet.title)); } else { titleTextView.setText(LocaleController.getString("StickersArchived", R.string.StickersArchived)); subtitleTextView.setText(LocaleController.formatString("StickersArchivedInfo", R.string.StickersArchivedInfo, stickerSet.title)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/StickersAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/StickersAlert.java index d3d84c407b..e6bd70033f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/StickersAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/StickersAlert.java @@ -8,6 +8,8 @@ package org.telegram.ui.Components; +import static org.telegram.messenger.AndroidUtilities.dp; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; @@ -124,9 +126,9 @@ public interface StickersAlertInstallDelegate { } public interface StickersAlertCustomButtonDelegate { - String getCustomButtonTextColorKey(); - String getCustomButtonRippleColorKey(); - String getCustomButtonColorKey(); + int getCustomButtonTextColorKey(); + int getCustomButtonRippleColorKey(); + int getCustomButtonColorKey(); String getCustomButtonText(); boolean onCustomButtonPressed(); } @@ -181,7 +183,7 @@ public interface StickersAlertCustomButtonDelegate { private boolean clearsInputField; private boolean showTooltipWhenToggle = true; - private String buttonTextColorKey; + private int buttonTextColorKey; private ContentPreviewViewer.ContentPreviewViewerDelegate previewDelegate = new ContentPreviewViewer.ContentPreviewViewerDelegate() { @Override @@ -244,8 +246,8 @@ public long getDialogId() { public StickersAlert(Context context, Object parentObject, TLObject object, Theme.ResourcesProvider resourcesProvider) { super(context, false, resourcesProvider); - fixNavigationBar(); this.resourcesProvider = resourcesProvider; + fixNavigationBar(); parentActivity = (Activity) context; final TLRPC.TL_messages_getAttachedStickers req = new TLRPC.TL_messages_getAttachedStickers(); if (object instanceof TLRPC.Photo) { @@ -308,6 +310,33 @@ public StickersAlert(Context context, Object parentObject, TLObject object, Them init(context); } + public StickersAlert(Context context, TLRPC.Vector vector, Theme.ResourcesProvider resourcesProvider) { + super(context, false, resourcesProvider); + this.resourcesProvider = resourcesProvider; + fixNavigationBar(); + parentActivity = (Activity) context; + if (vector.objects.isEmpty()) { + return; + } else if (vector.objects.size() == 1) { + TLRPC.StickerSetCovered set = (TLRPC.StickerSetCovered) vector.objects.get(0); + inputStickerSet = new TLRPC.TL_inputStickerSetID(); + inputStickerSet.id = set.set.id; + inputStickerSet.access_hash = set.set.access_hash; + loadStickerSet(); + init(context); + } else { + stickerSetCovereds = new ArrayList<>(); + for (int a = 0; a < vector.objects.size(); a++) { + stickerSetCovereds.add((TLRPC.StickerSetCovered) vector.objects.get(a)); + } + init(context); + gridView.setLayoutParams(LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT, 0, 0, 0, 48)); + titleTextView.setVisibility(View.GONE); + shadow[0].setVisibility(View.GONE); + adapter.notifyDataSetChanged(); + } + } + public StickersAlert(Context context, String software, ArrayList uris, ArrayList emoji, Theme.ResourcesProvider resourcesProvider) { super(context, false, resourcesProvider); fixNavigationBar(); @@ -540,23 +569,23 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (width == 0) { width = AndroidUtilities.displaySize.x; } - adapter.stickersPerRow = Math.max(1, width / AndroidUtilities.dp(AndroidUtilities.isTablet() ? 60 : 45)); - itemSize = (int) ((MeasureSpec.getSize(widthMeasureSpec) - AndroidUtilities.dp(36)) / adapter.stickersPerRow); + adapter.stickersPerRow = Math.max(1, width / dp(AndroidUtilities.isTablet() ? 60 : 45)); + itemSize = (int) ((MeasureSpec.getSize(widthMeasureSpec) - dp(36)) / adapter.stickersPerRow); itemHeight = itemSize; } else { adapter.stickersPerRow = 5; - itemSize = (int) ((MeasureSpec.getSize(widthMeasureSpec) - AndroidUtilities.dp(36)) / adapter.stickersPerRow); - itemHeight = AndroidUtilities.dp(82); + itemSize = (int) ((MeasureSpec.getSize(widthMeasureSpec) - dp(36)) / adapter.stickersPerRow); + itemHeight = dp(82); } float spansCount = adapter.stickersPerRow; int contentSize; MarginLayoutParams params = (MarginLayoutParams) gridView.getLayoutParams(); if (importingStickers != null) { - contentSize = AndroidUtilities.dp(48) + params.bottomMargin + Math.max(3, (int) Math.ceil(importingStickers.size() / spansCount)) * itemHeight + backgroundPaddingTop + AndroidUtilities.statusBarHeight; + contentSize = dp(48) + params.bottomMargin + Math.max(3, (int) Math.ceil(importingStickers.size() / spansCount)) * itemHeight + backgroundPaddingTop + AndroidUtilities.statusBarHeight; } else if (stickerSetCovereds != null) { - contentSize = AndroidUtilities.dp(8) + params.bottomMargin + AndroidUtilities.dp(60) * stickerSetCovereds.size() + adapter.stickersRowCount * itemHeight + backgroundPaddingTop + AndroidUtilities.dp(24); + contentSize = dp(8) + params.bottomMargin + dp(60) * stickerSetCovereds.size() + adapter.stickersRowCount * itemHeight + backgroundPaddingTop + dp(24); } else { - contentSize = AndroidUtilities.dp(48) + params.bottomMargin + (Math.max(isEmoji() ? 2 : 3, (stickerSet != null ? (int) Math.ceil(stickerSet.documents.size() / spansCount) : 0))) * itemHeight + backgroundPaddingTop + AndroidUtilities.statusBarHeight; + contentSize = dp(48) + params.bottomMargin + (Math.max(isEmoji() ? 2 : 3, (stickerSet != null ? (int) Math.ceil(stickerSet.documents.size() / spansCount) : 0))) * itemHeight + backgroundPaddingTop + AndroidUtilities.statusBarHeight; } if (isEmoji()) { contentSize += itemHeight * .15f; @@ -573,14 +602,14 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { padding = backgroundPaddingTop; } if (descriptionTextView != null) { - padding += AndroidUtilities.dp(32) + descriptionTextView.getMeasuredHeight(); + padding += dp(32) + descriptionTextView.getMeasuredHeight(); } if (stickerSetCovereds != null) { - padding += AndroidUtilities.dp(8); + padding += dp(8); } if (gridView.getPaddingTop() != padding) { ignoreLayout = true; - gridView.setPadding(AndroidUtilities.dp(10), padding, AndroidUtilities.dp(10), AndroidUtilities.dp(8)); + gridView.setPadding(dp(10), padding, dp(10), dp(8)); emptyView.setPadding(0, padding, 0, 0); ignoreLayout = false; } @@ -672,7 +701,7 @@ protected void onDraw(Canvas canvas) { containerView.setPadding(backgroundPaddingLeft, 0, backgroundPaddingLeft, 0); FrameLayout.LayoutParams frameLayoutParams = new FrameLayout.LayoutParams(LayoutHelper.MATCH_PARENT, AndroidUtilities.getShadowHeight(), Gravity.TOP | Gravity.LEFT); - frameLayoutParams.topMargin = AndroidUtilities.dp(48); + frameLayoutParams.topMargin = dp(48); shadow[0] = new View(context); shadow[0].setBackgroundColor(getThemedColor(Theme.key_dialogShadowLine)); shadow[0].setAlpha(0.0f); @@ -722,7 +751,7 @@ public void getItemOffsets(Rect outRect, View view, RecyclerView parent, Recycle outRect.top = 0; } }); - gridView.setPadding(AndroidUtilities.dp(10), 0, AndroidUtilities.dp(10), 0); + gridView.setPadding(dp(10), 0, dp(10), 0); gridView.setClipToPadding(false); gridView.setEnabled(true); gridView.setGlowColor(getThemedColor(Theme.key_dialogScrollGlow)); @@ -737,11 +766,15 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { if (stickerSetCovereds != null) { TLRPC.StickerSetCovered pack = adapter.positionsToSets.get(position); if (pack != null) { + ignoreMasterDismiss = true; dismiss(); TLRPC.TL_inputStickerSetID inputStickerSetID = new TLRPC.TL_inputStickerSetID(); inputStickerSetID.access_hash = pack.set.access_hash; inputStickerSetID.id = pack.set.id; StickersAlert alert = new StickersAlert(parentActivity, parentFragment, inputStickerSetID, null, null, resourcesProvider); + if (masterDismissListener != null) { + alert.setOnDismissListener(di -> masterDismissListener.run()); + } alert.show(); } } else if (importingStickersPaths != null) { @@ -752,7 +785,7 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { if (!selectedStickerPath.validated) { return; } - stickerEmojiTextView.setText(Emoji.replaceEmoji(selectedStickerPath.emoji, stickerEmojiTextView.getPaint().getFontMetricsInt(), AndroidUtilities.dp(30), false)); + stickerEmojiTextView.setText(Emoji.replaceEmoji(selectedStickerPath.emoji, stickerEmojiTextView.getPaint().getFontMetricsInt(), dp(30), false)); stickerImageView.setImage(ImageLocation.getForPath(selectedStickerPath.path), null, null, null, null, null, selectedStickerPath.animated ? "tgs" : null, 0, null); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) stickerPreviewLayout.getLayoutParams(); layoutParams.topMargin = scrollOffsetY; @@ -773,14 +806,14 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { TLRPC.DocumentAttribute attribute = selectedSticker.attributes.get(a); if (attribute instanceof TLRPC.TL_documentAttributeSticker) { if (attribute.alt != null && attribute.alt.length() > 0) { - stickerEmojiTextView.setText(attribute.alt); + stickerEmojiTextView.setText(Emoji.replaceEmoji(attribute.alt, stickerEmojiTextView.getPaint().getFontMetricsInt(), dp(30), false)); set = true; } break; } } if (!set) { - stickerEmojiTextView.setText(MediaDataController.getInstance(currentAccount).getEmojiForSticker(selectedSticker.id)); + stickerEmojiTextView.setText(Emoji.replaceEmoji(MediaDataController.getInstance(currentAccount).getEmojiForSticker(selectedSticker.id), stickerEmojiTextView.getPaint().getFontMetricsInt(), dp(30), false)); } if ((stickerSet == null || stickerSet.set == null || !stickerSet.set.emojis) && !ContentPreviewViewer.getInstance().showMenuFor(view)) { @@ -820,7 +853,7 @@ public void requestLayout() { titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); titleTextView.setLinkTextColor(getThemedColor(Theme.key_dialogTextLink)); titleTextView.setEllipsize(TextUtils.TruncateAt.END); - titleTextView.setPadding(AndroidUtilities.dp(18), AndroidUtilities.dp(6), AndroidUtilities.dp(18), AndroidUtilities.dp(6)); + titleTextView.setPadding(dp(18), dp(6), dp(18), dp(6)); titleTextView.setGravity(Gravity.CENTER_VERTICAL); titleTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); containerView.addView(titleTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 50, Gravity.LEFT | Gravity.TOP, 0, 0, 40, 0)); @@ -845,7 +878,7 @@ public void requestLayout() { emptyView.addView(progressView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); frameLayoutParams = new FrameLayout.LayoutParams(LayoutHelper.MATCH_PARENT, AndroidUtilities.getShadowHeight(), Gravity.BOTTOM | Gravity.LEFT); - frameLayoutParams.bottomMargin = AndroidUtilities.dp(48); + frameLayoutParams.bottomMargin = dp(48); shadow[1] = new View(context); shadow[1].setBackgroundColor(getThemedColor(Theme.key_dialogShadowLine)); containerView.addView(shadow[1], frameLayoutParams); @@ -854,7 +887,7 @@ public void requestLayout() { pickerBottomLayout.setBackground(Theme.createSelectorWithBackgroundDrawable(getThemedColor(Theme.key_dialogBackground), getThemedColor(Theme.key_listSelector))); pickerBottomLayout.setTextColor(getThemedColor(buttonTextColorKey = Theme.key_dialogTextBlue2)); pickerBottomLayout.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - pickerBottomLayout.setPadding(AndroidUtilities.dp(18), 0, AndroidUtilities.dp(18), 0); + pickerBottomLayout.setPadding(dp(18), 0, dp(18), 0); pickerBottomLayout.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); pickerBottomLayout.setGravity(Gravity.CENTER); @@ -888,7 +921,7 @@ public void requestLayout() { previewSendButton.setTextColor(getThemedColor(Theme.key_dialogTextBlue2)); previewSendButton.setBackground(Theme.createSelectorWithBackgroundDrawable(getThemedColor(Theme.key_dialogBackground), getThemedColor(Theme.key_listSelector))); previewSendButton.setGravity(Gravity.CENTER); - previewSendButton.setPadding(AndroidUtilities.dp(29), 0, AndroidUtilities.dp(29), 0); + previewSendButton.setPadding(dp(29), 0, dp(29), 0); previewSendButton.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); stickerPreviewLayout.addView(previewSendButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.BOTTOM | Gravity.LEFT)); previewSendButton.setOnClickListener(v -> { @@ -903,7 +936,7 @@ public void requestLayout() { }); frameLayoutParams = new FrameLayout.LayoutParams(LayoutHelper.MATCH_PARENT, AndroidUtilities.getShadowHeight(), Gravity.BOTTOM | Gravity.LEFT); - frameLayoutParams.bottomMargin = AndroidUtilities.dp(48); + frameLayoutParams.bottomMargin = dp(48); previewSendButtonShadow = new View(context); previewSendButtonShadow.setBackgroundColor(getThemedColor(Theme.key_dialogShadowLine)); stickerPreviewLayout.addView(previewSendButtonShadow, frameLayoutParams); @@ -939,7 +972,7 @@ private void updateSendButton() { int size = (int) (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) / 2 / AndroidUtilities.density); if (importingStickers != null) { previewSendButton.setText(LocaleController.getString("ImportStickersRemove", R.string.ImportStickersRemove)); - previewSendButton.setTextColor(getThemedColor(Theme.key_dialogTextRed)); + previewSendButton.setTextColor(getThemedColor(Theme.key_text_RedBold)); stickerImageView.setLayoutParams(LayoutHelper.createFrame(size, size, Gravity.CENTER, 0, 0, 0, 30)); stickerEmojiTextView.setLayoutParams(LayoutHelper.createFrame(size, size, Gravity.CENTER, 0, 0, 0, 30)); previewSendButton.setVisibility(View.VISIBLE); @@ -1072,7 +1105,7 @@ private void updateFields() { SpannableStringBuilder stringBuilder = null; CharSequence title = stickerSet.set.title; - title = Emoji.replaceEmoji(title, titleTextView.getPaint().getFontMetricsInt(), AndroidUtilities.dp(18), false); + title = Emoji.replaceEmoji(title, titleTextView.getPaint().getFontMetricsInt(), dp(18), false); try { if (urlPattern == null) { urlPattern = Pattern.compile("@[a-zA-Z\\d_]{1,32}"); @@ -1109,7 +1142,7 @@ public void onClick(View widget) { if (width == 0) { width = AndroidUtilities.displaySize.x; } - adapter.stickersPerRow = Math.max(1, width / AndroidUtilities.dp(AndroidUtilities.isTablet() ? 60 : 45)); + adapter.stickersPerRow = Math.max(1, width / dp(AndroidUtilities.isTablet() ? 60 : 45)); } else { adapter.stickersPerRow = 5; } @@ -1130,7 +1163,7 @@ public void onClick(View widget) { premiumButtonView.setVisibility(View.VISIBLE); pickerBottomLayout.setBackground(null); - setButton(null, null, null); + setButton(null, null, -1); premiumButtonView.setButton(LocaleController.getString("UnlockPremiumEmoji", R.string.UnlockPremiumEmoji), e -> { if (parentFragment != null) { new PremiumFeatureBottomSheet(parentFragment, PremiumPreviewFragment.PREMIUM_FEATURE_ANIMATED_EMOJI, false).show(); @@ -1227,7 +1260,7 @@ public void onClick(View widget) { } dismiss(); MediaDataController.getInstance(currentAccount).toggleStickerSet(getContext(), stickerSet, 1, parentFragment, true, showTooltipWhenToggle); - }, text, Theme.key_dialogTextRed); + }, text, Theme.key_text_RedBold); } else { setButton(v -> { if (installDelegate != null) { @@ -1235,7 +1268,7 @@ public void onClick(View widget) { } dismiss(); MediaDataController.getInstance(currentAccount).toggleStickerSet(getContext(), stickerSet, 0, parentFragment, true, showTooltipWhenToggle); - }, text, Theme.key_dialogTextRed); + }, text, Theme.key_text_RedBold); } } adapter.notifyDataSetChanged(); @@ -1285,12 +1318,12 @@ private void showNameEnterAlert() { textView.setSingleLine(true); textView.setVisibility(View.INVISIBLE); textView.setImeOptions(EditorInfo.IME_ACTION_DONE); - textView.setPadding(0, AndroidUtilities.dp(4), 0, 0); + textView.setPadding(0, dp(4), 0, 0); fieldLayout.addView(textView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 36, Gravity.TOP | Gravity.LEFT)); EditTextBoldCursor editText = new EditTextBoldCursor(context); editText.setBackground(null); - editText.setLineColors(Theme.getColor(Theme.key_dialogInputField), Theme.getColor(Theme.key_dialogInputFieldActivated), Theme.getColor(Theme.key_dialogTextRed)); + editText.setLineColors(Theme.getColor(Theme.key_dialogInputField), Theme.getColor(Theme.key_dialogInputFieldActivated), Theme.getColor(Theme.key_text_RedBold)); editText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); editText.setTextColor(getThemedColor(Theme.key_dialogTextBlack)); editText.setMaxLines(1); @@ -1300,9 +1333,9 @@ private void showNameEnterAlert() { editText.setSingleLine(true); editText.setImeOptions(EditorInfo.IME_ACTION_NEXT); editText.setCursorColor(getThemedColor(Theme.key_windowBackgroundWhiteBlackText)); - editText.setCursorSize(AndroidUtilities.dp(20)); + editText.setCursorSize(dp(20)); editText.setCursorWidth(1.5f); - editText.setPadding(0, AndroidUtilities.dp(4), 0, 0); + editText.setPadding(0, dp(4), 0, 0); editText.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { @@ -1336,7 +1369,7 @@ public void afterTextChanged(Editable s) { message.setText(AndroidUtilities.replaceTags(LocaleController.getString("ImportStickersEnterNameInfo", R.string.ImportStickersEnterNameInfo))); message.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - message.setPadding(AndroidUtilities.dp(23), AndroidUtilities.dp(12), AndroidUtilities.dp(23), AndroidUtilities.dp(6)); + message.setPadding(dp(23), dp(12), dp(23), dp(6)); message.setTextColor(getThemedColor(Theme.key_dialogTextGray2)); linearLayout.addView(message, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); @@ -1367,7 +1400,7 @@ public void afterTextChanged(Editable s) { } } textView.setVisibility(View.VISIBLE); - editText.setPadding(textView.getMeasuredWidth(), AndroidUtilities.dp(4), 0, 0); + editText.setPadding(textView.getMeasuredWidth(), dp(4), 0, 0); if (!set) { editText.setText(""); } @@ -1420,26 +1453,26 @@ private void checkUrlAvailable(TextView message, String text, boolean forceAvail if (text != null) { if (text.startsWith("_") || text.endsWith("_")) { message.setText(LocaleController.getString("ImportStickersLinkInvalid", R.string.ImportStickersLinkInvalid)); - message.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteRedText4)); + message.setTextColor(getThemedColor(Theme.key_text_RedRegular)); return; } for (int a = 0, N = text.length(); a < N; a++) { char ch = text.charAt(a); if (!(ch >= '0' && ch <= '9' || ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch == '_')) { message.setText(LocaleController.getString("ImportStickersEnterUrlInfo", R.string.ImportStickersEnterUrlInfo)); - message.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteRedText4)); + message.setTextColor(getThemedColor(Theme.key_text_RedRegular)); return; } } } if (text == null || text.length() < 5) { message.setText(LocaleController.getString("ImportStickersLinkInvalidShort", R.string.ImportStickersLinkInvalidShort)); - message.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteRedText4)); + message.setTextColor(getThemedColor(Theme.key_text_RedRegular)); return; } if (text.length() > 32) { message.setText(LocaleController.getString("ImportStickersLinkInvalidLong", R.string.ImportStickersLinkInvalidLong)); - message.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteRedText4)); + message.setTextColor(getThemedColor(Theme.key_text_RedRegular)); return; } @@ -1458,7 +1491,7 @@ private void checkUrlAvailable(TextView message, String text, boolean forceAvail lastNameAvailable = true; } else { message.setText(LocaleController.getString("ImportStickersLinkTaken", R.string.ImportStickersLinkTaken)); - message.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteRedText4)); + message.setTextColor(getThemedColor(Theme.key_text_RedRegular)); lastNameAvailable = false; } } @@ -1581,6 +1614,9 @@ public void setOnDismissListener(Runnable onDismissListener) { @Override public void dismiss() { super.dismiss(); + if (!ignoreMasterDismiss && masterDismissListener != null) { + masterDismissListener.run(); + } if (onDismissListener != null) { onDismissListener.run(); } @@ -1679,12 +1715,12 @@ public void didReceivedNotification(int id, int account, Object... args) { } } - private void setButton(View.OnClickListener onClickListener, String title, String colorKey) { - setButton(onClickListener, title, colorKey, null, null); + private void setButton(View.OnClickListener onClickListener, String title, int colorKey) { + setButton(onClickListener, title, colorKey, -1, -1); } - private void setButton(View.OnClickListener onClickListener, String title, String colorKey, String backgroundColorKey, String backgroundSelectorColorKey) { - if (colorKey != null) { + private void setButton(View.OnClickListener onClickListener, String title, int colorKey, int backgroundColorKey, int backgroundSelectorColorKey) { + if (colorKey >= 0) { pickerBottomLayout.setTextColor(getThemedColor(buttonTextColorKey = colorKey)); } pickerBottomLayout.setText(title); @@ -1694,16 +1730,16 @@ private void setButton(View.OnClickListener onClickListener, String title, Strin ViewGroup.MarginLayoutParams shadowParams = (ViewGroup.MarginLayoutParams) shadow[1].getLayoutParams(); ViewGroup.MarginLayoutParams gridParams = (ViewGroup.MarginLayoutParams) gridView.getLayoutParams(); ViewGroup.MarginLayoutParams emptyParams = (ViewGroup.MarginLayoutParams) emptyView.getLayoutParams(); - if (backgroundColorKey != null && backgroundSelectorColorKey != null) { - pickerBottomLayout.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(6), getThemedColor(backgroundColorKey), getThemedColor(backgroundSelectorColorKey))); + if (backgroundColorKey >= 0 && backgroundSelectorColorKey >= 0) { + pickerBottomLayout.setBackground(Theme.createSimpleSelectorRoundRectDrawable(dp(6), getThemedColor(backgroundColorKey), getThemedColor(backgroundSelectorColorKey))); pickerBottomFrameLayout.setBackgroundColor(getThemedColor(Theme.key_dialogBackground)); - params.leftMargin = params.topMargin = params.rightMargin = params.bottomMargin = AndroidUtilities.dp(8); - emptyParams.bottomMargin = gridParams.bottomMargin = shadowParams.bottomMargin = AndroidUtilities.dp(64); + params.leftMargin = params.topMargin = params.rightMargin = params.bottomMargin = dp(8); + emptyParams.bottomMargin = gridParams.bottomMargin = shadowParams.bottomMargin = dp(64); } else { - pickerBottomLayout.setBackground(Theme.createSelectorWithBackgroundDrawable(getThemedColor(Theme.key_dialogBackground), Theme.multAlpha(getThemedColor(Theme.key_dialogTextRed), .1f))); + pickerBottomLayout.setBackground(Theme.createSelectorWithBackgroundDrawable(getThemedColor(Theme.key_dialogBackground), Theme.multAlpha(getThemedColor(Theme.key_text_RedBold), .1f))); pickerBottomFrameLayout.setBackgroundColor(Color.TRANSPARENT); params.leftMargin = params.topMargin = params.rightMargin = params.bottomMargin = 0; - emptyParams.bottomMargin = gridParams.bottomMargin = shadowParams.bottomMargin = AndroidUtilities.dp(48); + emptyParams.bottomMargin = gridParams.bottomMargin = shadowParams.bottomMargin = dp(48); } containerView.requestLayout(); } @@ -1835,7 +1871,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType View view = null; switch (viewType) { case 0: - StickerEmojiCell cell = new StickerEmojiCell(context, false) { + StickerEmojiCell cell = new StickerEmojiCell(context, false, resourcesProvider) { public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(MeasureSpec.makeMeasureSpec(itemSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(itemSize/*AndroidUtilities.dp(82)*/, MeasureSpec.EXACTLY)); } @@ -1863,7 +1899,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { ((StickerEmojiCell) holder.itemView).setSticker(sticker, positionsToSets.get(position), false); break; case 1: - ((EmptyCell) holder.itemView).setHeight(AndroidUtilities.dp(82)); + ((EmptyCell) holder.itemView).setHeight(dp(82)); break; case 2: TLRPC.StickerSetCovered stickerSetCovered = stickerSetCovereds.get((Integer) cache.get(position)); @@ -1885,7 +1921,7 @@ public void notifyDataSetChanged() { if (width == 0) { width = AndroidUtilities.displaySize.x; } - stickersPerRow = width / AndroidUtilities.dp(72); + stickersPerRow = width / dp(72); layoutManager.setSpanCount(stickersPerRow); cache.clear(); positionsToSets.clear(); @@ -1893,16 +1929,19 @@ public void notifyDataSetChanged() { stickersRowCount = 0; for (int a = 0; a < stickerSetCovereds.size(); a++) { TLRPC.StickerSetCovered pack = stickerSetCovereds.get(a); - ArrayList documents; + List documents; if (pack instanceof TLRPC.TL_stickerSetFullCovered) { documents = ((TLRPC.TL_stickerSetFullCovered) pack).documents; } else { documents = pack.covers; } + if (documents != null) { + documents = documents.subList(0, Math.min(documents.size(), stickersPerRow)); + } if (documents == null || documents.isEmpty() && pack.cover == null) { continue; } - stickersRowCount += Math.ceil(stickerSetCovereds.size() / (float) stickersPerRow); + stickersRowCount++; positionsToSets.put(totalItems, pack); cache.put(totalItems++, a); int startRow = totalItems / stickersPerRow; @@ -1963,4 +2002,10 @@ public void onBackPressed() { } super.onBackPressed(); } + + private boolean ignoreMasterDismiss; + private Runnable masterDismissListener; + public void setOnMasterDismiss(Runnable listener) { + masterDismissListener = listener; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/StorageDiagramView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/StorageDiagramView.java index e36918ef49..e06b4b0759 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/StorageDiagramView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/StorageDiagramView.java @@ -209,7 +209,7 @@ protected void onDraw(Canvas canvas) { if (a > 0) { a = 0; } - data[i].paint.setColor(Theme.getColor(data[i].color)); + data[i].paint.setColor(Theme.getColor(data[i].colorKey)); data[i].paint.setAlpha(255); float r = (rectF.width() / 2); float len = (float) ((Math.PI * r / 180) * a); @@ -241,7 +241,7 @@ protected void onDraw(Canvas canvas) { if (a > 0) { a = 0; } - data[i].paint.setColor(Theme.getColor(data[i].color)); + data[i].paint.setColor(Theme.getColor(data[i].colorKey)); data[i].paint.setAlpha(255); float r = (rectF.width() / 2); float len = (float) ((Math.PI * r / 180) * a); @@ -291,7 +291,7 @@ protected void onDraw(Canvas canvas) { public static class ClearViewData { - public String color; + public int colorKey; Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); public boolean clear = true; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SuggestEmojiView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SuggestEmojiView.java index f904d3772d..7b67a41208 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/SuggestEmojiView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SuggestEmojiView.java @@ -90,7 +90,7 @@ public void sendEmoji(TLRPC.Document emoji) { } @Override - public boolean needCopy() { + public boolean needCopy(TLRPC.Document document) { return UserConfig.getInstance(UserConfig.selectedAccount).isPremium(); } @@ -350,7 +350,7 @@ private void update() { } CharSequence text = enterView.getFieldText(); Emoji.EmojiSpan[] emojiSpans = (text instanceof Spanned) ? ((Spanned) text).getSpans(Math.max(0, selectionEnd - 24), selectionEnd, Emoji.EmojiSpan.class) : null; - if (emojiSpans != null && emojiSpans.length > 0 && SharedConfig.suggestAnimatedEmoji) { + if (emojiSpans != null && emojiSpans.length > 0 && SharedConfig.suggestAnimatedEmoji && UserConfig.getInstance(currentAccount).isPremium()) { Emoji.EmojiSpan lastEmoji = emojiSpans[emojiSpans.length - 1]; if (lastEmoji != null) { int emojiStart = ((Spanned) text).getSpanStart(lastEmoji); @@ -398,6 +398,23 @@ private void update() { private int lastQueryId; private String[] lastLang; private Runnable searchRunnable; + private long lastLangChangedTime = 0; + + /** + * The user needs time to change the locale. We estimate this time to be at least 360 ms. + */ + private String[] detectKeyboardLangThrottleFirstWithDelay() { + long currentTime = System.currentTimeMillis(); + int delay = 360; + if (lastLang == null || Math.abs(currentTime - lastLangChangedTime) > delay) { + lastLangChangedTime = currentTime; + return AndroidUtilities.getCurrentKeyboardLanguage(); + } else { + lastLangChangedTime = currentTime; + } + return lastLang; + } + private void searchKeywords(String query) { if (query == null) { return; @@ -412,7 +429,7 @@ private void searchKeywords(String query) { } final int id = ++lastQueryId; - String[] lang = AndroidUtilities.getCurrentKeyboardLanguage(); + String[] lang = detectKeyboardLangThrottleFirstWithDelay(); if (lastLang == null || !Arrays.equals(lang, lastLang)) { MediaDataController.getInstance(currentAccount).fetchNewEmojiKeywords(lang); } @@ -445,6 +462,7 @@ private void searchKeywords(String query) { adapter.notifyDataSetChanged(); } } else { + keywordResults = null; clear = true; forceClose(); } @@ -480,7 +498,7 @@ private void searchAnimated(String emoji) { searchRunnable = () -> { ArrayList standard = new ArrayList<>(1); standard.add(new MediaDataController.KeywordResult(emoji, null)); - MediaDataController.getInstance(currentAccount).fillWithAnimatedEmoji(standard, 15, false, () -> { + MediaDataController.getInstance(currentAccount).fillWithAnimatedEmoji(standard, 15, false, false, () -> { if (id == lastQueryId) { lastQuery = emoji; lastQueryType = 2; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SwipeGestureSettingsView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SwipeGestureSettingsView.java index 3557a41cd1..51e5138936 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/SwipeGestureSettingsView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SwipeGestureSettingsView.java @@ -44,7 +44,7 @@ public class SwipeGestureSettingsView extends FrameLayout { private NumberPicker picker; String[] strings = new String[6]; - String[] backgroundKeys = new String[6]; + int[] backgroundKeys = new int[6]; RLottieDrawable[] icons = new RLottieDrawable[6]; int currentIconIndex; @@ -54,7 +54,7 @@ public class SwipeGestureSettingsView extends FrameLayout { float progressToSwipeFolders; float colorProgress = 1f; int fromColor; - String currentColorKey; + int currentColorKey; public SwipeGestureSettingsView(Context context, int currentAccount) { @@ -212,12 +212,12 @@ protected void onDraw(Canvas canvas) { int color; - if (currentColorKey == null) { + if (currentColorKey < 0) { currentColorKey = backgroundKeys[picker.getValue()]; colorProgress = 1f; color = ColorUtils.blendARGB(Theme.getColor(Theme.key_windowBackgroundWhite), Theme.getColor(currentColorKey), 0.9f); fromColor = color; - } else if (!backgroundKeys[picker.getValue()].equals(currentColorKey)) { + } else if (backgroundKeys[picker.getValue()] != currentColorKey) { fromColor = ColorUtils.blendARGB(fromColor, ColorUtils.blendARGB(Theme.getColor(Theme.key_windowBackgroundWhite), Theme.getColor(currentColorKey), 0.9f), colorProgress); colorProgress = 0; currentColorKey = backgroundKeys[picker.getValue()]; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Switch.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Switch.java index 756e43f1da..b38484d471 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Switch.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Switch.java @@ -57,10 +57,10 @@ public class Switch extends View { private OnCheckedChangeListener onCheckedChangeListener; - private String trackColorKey = Theme.key_switch2Track; - private String trackCheckedColorKey = Theme.key_switch2TrackChecked; - private String thumbColorKey = Theme.key_windowBackgroundWhite; - private String thumbCheckedColorKey = Theme.key_windowBackgroundWhite; + private int trackColorKey = Theme.key_fill_RedNormal; + private int trackCheckedColorKey = Theme.key_switch2TrackChecked; + private int thumbColorKey = Theme.key_windowBackgroundWhite; + private int thumbCheckedColorKey = Theme.key_windowBackgroundWhite; private Drawable iconDrawable; private int lastIconColor; @@ -224,7 +224,7 @@ protected boolean verifyDrawable(Drawable who) { return super.verifyDrawable(who) || rippleDrawable != null && who == rippleDrawable; } - public void setColors(String track, String trackChecked, String thumb, String thumbChecked) { + public void setColors(int track, int trackChecked, int thumb, int thumbChecked) { trackColorKey = track; trackCheckedColorKey = trackChecked; thumbColorKey = thumb; @@ -233,7 +233,7 @@ public void setColors(String track, String trackChecked, String thumb, String th private void animateToCheckedState(boolean newCheckedState) { checkAnimator = ObjectAnimator.ofFloat(this, "progress", newCheckedState ? 1 : 0); - checkAnimator.setDuration(semHaptics ? 150 : 250); + checkAnimator.setDuration(200); checkAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -245,7 +245,7 @@ public void onAnimationEnd(Animator animation) { private void animateIcon(boolean newCheckedState) { iconAnimator = ObjectAnimator.ofFloat(this, "iconProgress", newCheckedState ? 1 : 0); - iconAnimator.setDuration(semHaptics ? 150 : 250); + iconAnimator.setDuration(200); iconAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -279,7 +279,6 @@ public void setChecked(boolean checked, int iconType, boolean animated) { if (checked != isChecked) { isChecked = checked; if (attachedToWindow && animated) { - vibrateChecked(checked); animateToCheckedState(checked); } else { cancelCheckAnimator(); @@ -546,15 +545,4 @@ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { //info.setContentDescription(isChecked ? LocaleController.getString("NotificationsOn", R.string.NotificationsOn) : LocaleController.getString("NotificationsOff", R.string.NotificationsOff)); } - private boolean semHaptics = false; - - private void vibrateChecked(boolean toCheck) { - try { - if (isHapticFeedbackEnabled() && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) { - VibrationEffect vibrationEffect = VibrationEffect.createWaveform(new long[]{75,10,5,10}, new int[] {5,20,110,20}, -1); - VibrateUtil.vibrate(200L, vibrationEffect); - semHaptics = true; - } - } catch (Exception ignore) {} - } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/TextSelectionHint.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/TextSelectionHint.java index 0986a7c783..0d1e81a60d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/TextSelectionHint.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/TextSelectionHint.java @@ -363,8 +363,7 @@ public float getPrepareProgress() { return prepareProgress; } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/TextStyleSpan.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/TextStyleSpan.java index 84562c1216..3aa2712827 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/TextStyleSpan.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/TextStyleSpan.java @@ -99,6 +99,7 @@ public Typeface getTypeface() { public final static int FLAG_STYLE_URL = 128; public final static int FLAG_STYLE_SPOILER = 256; public final static int FLAG_STYLE_SPOILER_REVEALED = 512; + public final static int FLAG_STYLE_TEXT_URL = 1024; public TextStyleSpan(TextStyleRun run) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ThemeEditorView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ThemeEditorView.java index 2ccc6f5f41..2f5626cd98 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ThemeEditorView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ThemeEditorView.java @@ -70,6 +70,7 @@ import org.telegram.ui.ActionBar.BottomSheet; import org.telegram.ui.ActionBar.INavigationLayout; import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.ActionBar.ThemeColors; import org.telegram.ui.ActionBar.ThemeDescription; import org.telegram.ui.Cells.TextColorThemeCell; import org.telegram.ui.LaunchActivity; @@ -344,7 +345,7 @@ public ColorPicker(Context context) { colorEditText[a].setCursorWidth(1.5f); colorEditText[a].setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); colorEditText[a].setBackground(null); - colorEditText[a].setLineColors(Theme.getColor(Theme.key_dialogInputField), Theme.getColor(Theme.key_dialogInputFieldActivated), Theme.getColor(Theme.key_dialogTextRed)); + colorEditText[a].setLineColors(Theme.getColor(Theme.key_dialogInputField), Theme.getColor(Theme.key_dialogInputFieldActivated), Theme.getColor(Theme.key_text_RedBold)); colorEditText[a].setMaxLines(1); colorEditText[a].setTag(a); colorEditText[a].setGravity(Gravity.CENTER); @@ -578,8 +579,8 @@ public boolean onTouchEvent(MotionEvent event) { int color = getColor(); for (int a = 0; a < currentThemeDesription.size(); a++) { ThemeDescription description = currentThemeDesription.get(a); - String key = description.getCurrentKey(); - if (a == 0 && key.equals(Theme.key_chat_wallpaper) || key.equals(Theme.key_chat_wallpaper_gradient_to1) || key.equals(Theme.key_chat_wallpaper_gradient_to2) || key.equals(Theme.key_chat_wallpaper_gradient_to3) || key.equals(Theme.key_windowBackgroundWhite) || key.equals(Theme.key_windowBackgroundGray)) { + int key = description.getCurrentKey(); + if (a == 0 && key == Theme.key_chat_wallpaper || key == Theme.key_chat_wallpaper_gradient_to1 || key == Theme.key_chat_wallpaper_gradient_to2 || key == Theme.key_chat_wallpaper_gradient_to3 || key == Theme.key_windowBackgroundWhite || key == Theme.key_windowBackgroundGray) { color = 0xff000000 | color; } currentThemeDesription.get(a).setColor(color, false); @@ -801,7 +802,7 @@ protected boolean allowSelectChildAtPosition(float x, float y) { currentThemeDesriptionPosition = position; for (int a = 0; a < currentThemeDesription.size(); a++) { ThemeDescription description = currentThemeDesription.get(a); - if (description.getCurrentKey().equals(Theme.key_chat_wallpaper)) { + if (description.getCurrentKey() == Theme.key_chat_wallpaper) { wallpaperUpdater.showAlert(true); return; } @@ -1175,9 +1176,8 @@ private void searchDialogsInternal(final String query, final int searchId) { ArrayList names = new ArrayList<>(); for (int a = 0, N = listAdapter.items.size(); a < N; a++) { ArrayList themeDescriptions = listAdapter.items.get(a); - String key = themeDescriptions.get(0).getCurrentKey(); + String key = ThemeColors.getStringName(themeDescriptions.get(0).getCurrentKey()); String name = key.toLowerCase(); - int found = 0; for (String q : search) { if (name.contains(q)) { searchResults.add(themeDescriptions); @@ -1279,7 +1279,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { ArrayList arrayList = searchResult.get(position - 1); ThemeDescription description = arrayList.get(0); int color; - if (description.getCurrentKey().equals(Theme.key_chat_wallpaper)) { + if (description.getCurrentKey() == Theme.key_chat_wallpaper) { color = 0; } else { color = description.getSetColor(); @@ -1305,10 +1305,10 @@ private class ListAdapter extends RecyclerListView.SelectionAdapter { public ListAdapter(Context context, ArrayList descriptions) { this.context = context; - HashMap> itemsMap = new HashMap<>(); + HashMap> itemsMap = new HashMap<>(); for (int a = 0, N = descriptions.size(); a < N; a++) { ThemeDescription description = descriptions.get(a); - String key = description.getCurrentKey(); + int key = description.getCurrentKey(); ArrayList arrayList = itemsMap.get(key); if (arrayList == null) { arrayList = new ArrayList<>(); @@ -1364,7 +1364,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { ArrayList arrayList = items.get(position - 1); ThemeDescription description = arrayList.get(0); int color; - if (description.getCurrentKey().equals(Theme.key_chat_wallpaper)) { + if (description.getCurrentKey() == Theme.key_chat_wallpaper) { color = 0; } else { color = description.getSetColor(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ThemePreviewDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ThemePreviewDrawable.java index 2848c9fcdb..660e6eea83 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ThemePreviewDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ThemePreviewDrawable.java @@ -10,6 +10,7 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.util.SparseIntArray; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ApplicationLoader; @@ -20,7 +21,6 @@ import org.telegram.ui.ActionBar.Theme; import java.io.File; -import java.util.HashMap; public class ThemePreviewDrawable extends BitmapDrawable { @@ -42,8 +42,8 @@ private static Bitmap createPreview(File pattern, DocumentObject.ThemeDocument t Bitmap bitmap = Bitmaps.createBitmap(560, 678, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); - HashMap baseColors = Theme.getThemeFileValues(null, themeDocument.baseTheme.assetName, null); - HashMap colors = new HashMap<>(baseColors); + SparseIntArray baseColors = Theme.getThemeFileValues(null, themeDocument.baseTheme.assetName, null); + SparseIntArray colors = baseColors.clone(); themeDocument.accent.fillAccentColors(baseColors, colors); int actionBarColor = Theme.getPreviewColor(colors, Theme.key_actionBarDefault); @@ -76,7 +76,7 @@ private static Bitmap createPreview(File pattern, DocumentObject.ThemeDocument t for (int a = 0; a < 2; a++) { messageDrawable[a] = new Theme.MessageDrawable(Theme.MessageDrawable.TYPE_PREVIEW, a == 1, false) { @Override - protected int getColor(String key) { + protected int getColor(int key) { Integer color = colors.get(key); if (color == null) { return Theme.getColor(key); @@ -85,7 +85,7 @@ protected int getColor(String key) { } @Override - protected Integer getCurrentColor(String key) { + protected int getCurrentColor(int key) { return colors.get(key); } }; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ThemeSmallPreviewView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ThemeSmallPreviewView.java index 9c24e29ef1..29db2ecf2d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ThemeSmallPreviewView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ThemeSmallPreviewView.java @@ -40,6 +40,7 @@ import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.EmojiThemes; import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.ChatBackgroundDrawable; import java.util.List; @@ -80,6 +81,8 @@ public class ThemeSmallPreviewView extends FrameLayout implements NotificationCe private int currentType; int patternColor; private float selectionProgress; + ChatBackgroundDrawable chatBackgroundDrawable; + boolean attached; public ThemeSmallPreviewView(Context context, int currentAccount, Theme.ResourcesProvider resourcesProvider, int currentType) { super(context); @@ -141,6 +144,13 @@ protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); return; } + if (chatBackgroundDrawable != null) { + canvas.save(); + canvas.clipPath(clipPath); + chatBackgroundDrawable.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight()); + chatBackgroundDrawable.draw(canvas); + canvas.restore(); + } if (changeThemeProgress != 1 && animateOutThemeDrawable != null) { animateOutThemeDrawable.drawBackground(canvas, 1f); } @@ -193,6 +203,21 @@ public void setItem(ChatThemeBottomSheet.ChatThemeItem item, boolean animated) { thumb = Emoji.getEmojiDrawable(item.chatTheme.getEmoticon()); } backupImageView.setImage(ImageLocation.getForDocument(document), "50_50", thumb, null); + if (item.chatTheme.wallpaper != null) { + if (attached && chatBackgroundDrawable != null) { + chatBackgroundDrawable.onDetachedFromWindow(); + } + chatBackgroundDrawable = new ChatBackgroundDrawable(item.chatTheme.wallpaper, false, true); + chatBackgroundDrawable.setParent(this); + if (attached) { + chatBackgroundDrawable.onAttachedToWindow(); + } + } else { + if (attached && chatBackgroundDrawable != null) { + chatBackgroundDrawable.onDetachedFromWindow(); + } + chatBackgroundDrawable = null; + } } if (itemChanged || darkModeChanged) { @@ -498,9 +523,8 @@ private StaticLayout getNoThemeStaticLayout() { return textLayout; } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } @@ -591,7 +615,7 @@ public void draw(Canvas canvas, float alpha) { inBubblePaint.setAlpha((int) (255 * alpha)); rectF.set(INNER_RECT_SPACE, INNER_RECT_SPACE, getWidth() - INNER_RECT_SPACE, getHeight() - INNER_RECT_SPACE); - if (chatThemeItem.chatTheme == null || chatThemeItem.chatTheme.showAsDefaultStub) { + if (chatThemeItem.chatTheme == null || (chatThemeItem.chatTheme.showAsDefaultStub && chatThemeItem.chatTheme.wallpaper == null)) { canvas.drawRoundRect(rectF, INNER_RADIUS, INNER_RADIUS, backgroundFillPaint); canvas.save(); StaticLayout textLayout = getNoThemeStaticLayout(); @@ -654,12 +678,20 @@ public void draw(Canvas canvas, float alpha) { protected void onAttachedToWindow() { super.onAttachedToWindow(); NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.emojiLoaded); + attached = true; + if (chatBackgroundDrawable != null) { + chatBackgroundDrawable.onAttachedToWindow(); + } } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.emojiLoaded); + attached = false; + if (chatBackgroundDrawable != null) { + chatBackgroundDrawable.onDetachedFromWindow(); + } } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/TranslateButton.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/TranslateButton.java index 80e4847ccd..d37c2843ac 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/TranslateButton.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/TranslateButton.java @@ -44,6 +44,7 @@ public class TranslateButton extends FrameLayout { private Theme.ResourcesProvider resourcesProvider; private AnimatedTextView textView; + private final Drawable translateDrawable; public final SpannableString translateIcon; private ImageView menuView; @@ -64,19 +65,17 @@ public TranslateButton(Context context, int currentAccount, long dialogId, BaseF textView = new AnimatedTextView(context, true, true, false); textView.setAnimationProperties(.3f, 0, 450, CubicBezierInterpolator.EASE_OUT_QUINT); - textView.setTextColor(Theme.getColor(Theme.key_chat_addContact, resourcesProvider)); + textView.setTextSize(AndroidUtilities.dp(15)); textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); textView.setPadding(AndroidUtilities.dp(4), 0, AndroidUtilities.dp(4), 0); textView.setGravity(Gravity.CENTER_HORIZONTAL); textView.setIgnoreRTL(!LocaleController.isRTL); textView.adaptWidth = false; - textView.setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_chat_addContact, resourcesProvider) & 0x19ffffff, 3)); textView.setOnClickListener(e -> onButtonClick()); addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); - final Drawable translateDrawable = getContext().getResources().getDrawable(R.drawable.msg_translate).mutate(); - translateDrawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_chat_addContact, resourcesProvider), PorterDuff.Mode.MULTIPLY)); + translateDrawable = getContext().getResources().getDrawable(R.drawable.msg_translate).mutate(); translateDrawable.setBounds(0, AndroidUtilities.dp(-8), AndroidUtilities.dp(20), AndroidUtilities.dp(20 - 8)); translateIcon = new SpannableString("x"); translateIcon.setSpan(new ImageSpan(translateDrawable, DynamicDrawableSpan.ALIGN_BOTTOM), 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); @@ -84,8 +83,6 @@ public TranslateButton(Context context, int currentAccount, long dialogId, BaseF menuView = new ImageView(context); menuView.setScaleType(ImageView.ScaleType.CENTER); menuView.setImageResource(R.drawable.msg_mini_customize); - menuView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_chat_addContact, resourcesProvider), PorterDuff.Mode.MULTIPLY)); - menuView.setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_chat_addContact, resourcesProvider) & 0x19ffffff, Theme.RIPPLE_MASK_ROUNDRECT_6DP)); menuView.setOnClickListener(e -> { if (UserConfig.getInstance(currentAccount).isPremium()) { onMenuClick(); @@ -94,6 +91,16 @@ public TranslateButton(Context context, int currentAccount, long dialogId, BaseF } }); addView(menuView, LayoutHelper.createFrame(32, 32, Gravity.RIGHT | Gravity.CENTER_VERTICAL, 0, 0, 8, 0)); + + updateColors(); + } + + public void updateColors() { + textView.setTextColor(Theme.getColor(Theme.key_chat_addContact, resourcesProvider)); + textView.setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_chat_addContact, resourcesProvider) & 0x19ffffff, 3)); + menuView.setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_chat_addContact, resourcesProvider) & 0x19ffffff, Theme.RIPPLE_MASK_ROUNDRECT_6DP)); + menuView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_chat_addContact, resourcesProvider), PorterDuff.Mode.MULTIPLY)); + translateDrawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_chat_addContact, resourcesProvider), PorterDuff.Mode.MULTIPLY)); } protected void onButtonClick() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/TrendingStickersLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/TrendingStickersLayout.java index 7c71e784f2..b84b3dbdef 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/TrendingStickersLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/TrendingStickersLayout.java @@ -692,7 +692,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType View view = null; switch (viewType) { case 0: - StickerEmojiCell stickerCell = new StickerEmojiCell(context, false) { + StickerEmojiCell stickerCell = new StickerEmojiCell(context, false, resourcesProvider) { public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(82), MeasureSpec.EXACTLY)); } @@ -1001,8 +1001,7 @@ public void getThemeDescriptions(List descriptions, RecyclerLi } } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/TypefaceSpan.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/TypefaceSpan.java index eb70c01eb8..c44f4dd535 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/TypefaceSpan.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/TypefaceSpan.java @@ -21,7 +21,7 @@ public class TypefaceSpan extends MetricAffectingSpan { private Typeface typeface; private int textSize; private int color; - private String colorKey; + private int colorKey = -1; Theme.ResourcesProvider resourcesProvider; public TypefaceSpan(Typeface tf) { @@ -41,7 +41,7 @@ public TypefaceSpan(Typeface tf, int size, int textColor) { color = textColor; } - public TypefaceSpan(Typeface tf, int size, String colorKey, Theme.ResourcesProvider resourcesProvider) { + public TypefaceSpan(Typeface tf, int size, int colorKey, Theme.ResourcesProvider resourcesProvider) { typeface = tf; if (size > 0) { textSize = size; @@ -84,7 +84,7 @@ public void updateMeasureState(TextPaint p) { @Override public void updateDrawState(TextPaint tp) { - if (colorKey != null) { + if (colorKey >= 0) { color = Theme.getColor(colorKey, resourcesProvider); } if (typeface != null) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/UndoView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/UndoView.java index 600ef657b2..c6ab3aa967 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/UndoView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/UndoView.java @@ -78,6 +78,7 @@ public class UndoView extends FrameLayout { private BaseFragment parentFragment; private Object currentInfoObject; + private Object currentInfoObject2; private int currentAccount = UserConfig.selectedAccount; @@ -186,6 +187,7 @@ public class UndoView extends FrameLayout { public final static int ACTION_PREMIUM_ALL_FOLDER = 86; public final static int ACTION_PROXY_ADDED = 87; + public final static int ACTION_SHARED_FOLDER_DELETED = 88; private CharSequence infoText; private int hideAnimationType = 1; @@ -343,7 +345,8 @@ private boolean isTooltipAction() { private boolean hasSubInfo() { return currentAction == ACTION_QR_SESSION_ACCEPTED || currentAction == ACTION_PROXIMITY_SET || currentAction == ACTION_ARCHIVE_HIDDEN || currentAction == ACTION_ARCHIVE_HINT || currentAction == ACTION_ARCHIVE_FEW_HINT || currentAction == ACTION_QUIZ_CORRECT || currentAction == ACTION_QUIZ_INCORRECT || - currentAction == ACTION_REPORT_SENT || currentAction == ACTION_ARCHIVE_PINNED && MessagesController.getInstance(currentAccount).dialogFilters.isEmpty() || currentAction == ACTION_RINGTONE_ADDED || currentAction == ACTION_HINT_SWIPE_TO_REPLY; + currentAction == ACTION_REPORT_SENT || currentAction == ACTION_ARCHIVE_PINNED && MessagesController.getInstance(currentAccount).dialogFilters.isEmpty() || currentAction == ACTION_RINGTONE_ADDED || currentAction == ACTION_HINT_SWIPE_TO_REPLY || + currentAction == ACTION_SHARED_FOLDER_DELETED && currentInfoObject2 != null && ((Integer) currentInfoObject2) > 0; } public boolean isMultilineSubInfo() { @@ -367,6 +370,7 @@ public void hide(boolean apply, int animated) { return; } currentInfoObject = null; + currentInfoObject2 = null; isShown = false; if (currentActionRunnable != null) { if (apply) { @@ -467,8 +471,9 @@ public void showWithAction(ArrayList dialogIds, int action, Object infoObj return; } + currentInfoObject2 = infoObject2; lastUpdateTime = SystemClock.elapsedRealtime(); - undoTextView.setText(LocaleController.getString("Undo", R.string.Undo).toUpperCase()); + undoTextView.setText(LocaleController.getString("Undo", R.string.Undo)); undoImageView.setVisibility(VISIBLE); leftImageView.setPadding(0, 0, 0, 0); leftImageView.setScaleX(1); @@ -477,7 +482,12 @@ public void showWithAction(ArrayList dialogIds, int action, Object infoObj avatarImageView.setVisibility(GONE); infoTextView.setGravity(Gravity.LEFT | Gravity.TOP); - FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) infoTextView.getLayoutParams(); + FrameLayout.LayoutParams layoutParams; + + layoutParams = (FrameLayout.LayoutParams) subinfoTextView.getLayoutParams(); + layoutParams.leftMargin = AndroidUtilities.dp(58); + + layoutParams = (FrameLayout.LayoutParams) infoTextView.getLayoutParams(); layoutParams.height = LayoutHelper.WRAP_CONTENT; layoutParams.topMargin = AndroidUtilities.dp(13); layoutParams.bottomMargin = 0; @@ -1193,7 +1203,7 @@ public void showWithAction(ArrayList dialogIds, int action, Object infoObj infoTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); infoTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - undoTextView.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteRedText2)); + undoTextView.setTextColor(getThemedColor(Theme.key_text_RedRegular)); undoImageView.setVisibility(GONE); undoButton.setVisibility(VISIBLE); leftImageView.setVisibility(VISIBLE); @@ -1411,7 +1421,7 @@ public void showWithAction(ArrayList dialogIds, int action, Object infoObj if (photoEntry.thumbPath != null) { avatarImageView.setImage(photoEntry.thumbPath, null, Theme.chat_attachEmptyDrawable); } else if (photoEntry.path != null) { - avatarImageView.setOrientation(photoEntry.orientation, true); + avatarImageView.setOrientation(photoEntry.orientation, photoEntry.invert, true); if (photoEntry.isVideo) { avatarImageView.setImage("vthumb://" + photoEntry.imageId + ":" + photoEntry.path, null, Theme.chat_attachEmptyDrawable); } else { @@ -1431,7 +1441,30 @@ public void showWithAction(ArrayList dialogIds, int action, Object infoObj subinfoTextView.setVisibility(GONE); leftImageView.setVisibility(GONE); - if (currentAction == ACTION_CLEAR_DATES || currentAction == ACTION_CLEAR || currentAction == ACTION_CLEAR_FEW) { + if (currentAction == ACTION_SHARED_FOLDER_DELETED) { + String folderName = (String) infoObject; + int chatsCount = (Integer) infoObject2; + if (chatsCount > 0) { + int margin = (int) Math.ceil(undoTextView.getPaint().measureText(undoTextView.getText().toString())) + AndroidUtilities.dp(26); + + layoutParams.leftMargin = AndroidUtilities.dp(48); + layoutParams.rightMargin = margin; + layoutParams.topMargin = AndroidUtilities.dp(6); + + layoutParams = (FrameLayout.LayoutParams) subinfoTextView.getLayoutParams(); + layoutParams.leftMargin = AndroidUtilities.dp(48); + layoutParams.rightMargin = margin; + + infoTextView.setText(LocaleController.formatString("FolderLinkDeletedTitle", R.string.FolderLinkDeletedTitle, folderName)); + infoTextView.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + + subinfoTextView.setVisibility(VISIBLE); + subinfoTextView.setText(LocaleController.formatPluralString("FolderLinkDeletedSubtitle", chatsCount)); + } else { + infoTextView.setTypeface(Typeface.DEFAULT); + infoTextView.setText(AndroidUtilities.replaceTags(LocaleController.formatString("FolderLinkDeleted", R.string.FolderLinkDeleted, (folderName == null ? "" : folderName).replace('*', '✱')))); + } + } else if (currentAction == ACTION_CLEAR_DATES || currentAction == ACTION_CLEAR || currentAction == ACTION_CLEAR_FEW) { infoTextView.setText(LocaleController.getString("HistoryClearedUndo", R.string.HistoryClearedUndo)); } else if (currentAction == ACTION_DELETE_FEW) { infoTextView.setText(LocaleController.getString("ChatsDeletedUndo", R.string.ChatsDeletedUndo)); @@ -1550,7 +1583,7 @@ protected void onDraw(Canvas canvas) { backgroundDrawable.draw(canvas); } - if (currentAction == ACTION_DELETE || currentAction == ACTION_CLEAR || currentAction == ACTION_DELETE_FEW || currentAction == ACTION_CLEAR_FEW || currentAction == ACTION_CLEAR_DATES) { + if (currentAction == ACTION_DELETE || currentAction == ACTION_CLEAR || currentAction == ACTION_DELETE_FEW || currentAction == ACTION_CLEAR_FEW || currentAction == ACTION_CLEAR_DATES || currentAction == ACTION_SHARED_FOLDER_DELETED) { int newSeconds = timeLeft > 0 ? (int) Math.ceil(timeLeft / 1000.0f) : 0; if (prevSeconds != newSeconds) { prevSeconds = newSeconds; @@ -1656,8 +1689,7 @@ public Drawable getBackground() { return backgroundDrawable; } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/UnreadCounterTextView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/UnreadCounterTextView.java index b249327b2d..6d2a5b5b0d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/UnreadCounterTextView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/UnreadCounterTextView.java @@ -46,7 +46,7 @@ public class UnreadCounterTextView extends View { int counterColor; CharSequence lastText; - String textColorKey = Theme.key_chat_fieldOverlayText; + int textColorKey = Theme.key_chat_fieldOverlayText; public UnreadCounterTextView(Context context) { super(context); @@ -301,7 +301,7 @@ end, getMeasuredHeight() / 2 + contentWidth / 2 } } - public void setTextColorKey(String textColorKey) { + public void setTextColorKey(int textColorKey) { this.textColorKey = textColorKey; invalidate(); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/UpdateAppAlertDialog.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/UpdateAppAlertDialog.java index 985f53cf00..f6f287bedf 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/UpdateAppAlertDialog.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/UpdateAppAlertDialog.java @@ -75,7 +75,7 @@ public BottomSheetCell(Context context, boolean withoutBackground) { background = new View(context); if (hasBackground) { - background.setBackground(Theme.AdaptiveRipple.filledRect(Theme.key_featuredStickers_addButton, 4)); + background.setBackground(Theme.AdaptiveRipple.filledRectByKey(Theme.key_featuredStickers_addButton, 4)); } addView(background, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, 0, 16, withoutBackground ? 0 : 16, 16, 16)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/UsersAlertBase.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/UsersAlertBase.java index 0094c5be7d..98a7179ae4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/UsersAlertBase.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/UsersAlertBase.java @@ -27,6 +27,7 @@ import android.view.inputmethod.EditorInfo; import android.widget.FrameLayout; import android.widget.ImageView; +import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.LinearLayoutManager; @@ -44,6 +45,7 @@ public class UsersAlertBase extends BottomSheet { + private TextView titleView; protected FrameLayout frameLayout; protected RecyclerListView listView; protected RecyclerView.Adapter searchListViewAdapter; @@ -65,24 +67,26 @@ public class UsersAlertBase extends BottomSheet { protected boolean needSnapToTop = true; protected boolean isEmptyViewVisible = true; - protected String keyScrollUp = Theme.key_sheet_scrollUp; - protected String keyListSelector = Theme.key_listSelector; - protected String keySearchBackground = Theme.key_dialogSearchBackground; - protected String keyInviteMembersBackground = Theme.key_windowBackgroundWhite; - protected String keyListViewBackground = Theme.key_windowBackgroundWhite; - protected String keyActionBarUnscrolled = Theme.key_windowBackgroundWhite; - protected String keyNameText = Theme.key_windowBackgroundWhiteBlackText; - protected String keyLastSeenText = Theme.key_windowBackgroundWhiteGrayText; - protected String keyLastSeenTextUnscrolled = Theme.key_windowBackgroundWhiteGrayText; - protected String keySearchPlaceholder = Theme.key_dialogSearchHint; - protected String keySearchText = Theme.key_dialogSearchText; - protected String keySearchIcon = Theme.key_dialogSearchIcon; - protected String keySearchIconUnscrolled = Theme.key_dialogSearchIcon; + protected int keyScrollUp = Theme.key_sheet_scrollUp; + protected int keyListSelector = Theme.key_listSelector; + protected int keySearchBackground = Theme.key_dialogSearchBackground; + protected int keyInviteMembersBackground = Theme.key_windowBackgroundWhite; + protected int keyListViewBackground = Theme.key_windowBackgroundWhite; + protected int keyActionBarUnscrolled = Theme.key_windowBackgroundWhite; + protected int keyNameText = Theme.key_windowBackgroundWhiteBlackText; + protected int keyLastSeenText = Theme.key_windowBackgroundWhiteGrayText; + protected int keyLastSeenTextUnscrolled = Theme.key_windowBackgroundWhiteGrayText; + protected int keySearchPlaceholder = Theme.key_dialogSearchHint; + protected int keySearchText = Theme.key_dialogSearchText; + protected int keySearchIcon = Theme.key_dialogSearchIcon; + protected int keySearchIconUnscrolled = Theme.key_dialogSearchIcon; protected final FillLastLinearLayoutManager layoutManager; + private boolean drawTitle = true; public UsersAlertBase(Context context, boolean needFocus, int account, Theme.ResourcesProvider resourcesProvider) { super(context, needFocus, resourcesProvider); + this.resourcesProvider = resourcesProvider; updateColorKeys(); setDimBehindAlpha(75); @@ -116,7 +120,7 @@ public UsersAlertBase(Context context, boolean needFocus, int account, Theme.Res emptyView.setColors(keyNameText, keyLastSeenText, keyInviteMembersBackground, keySearchBackground); containerView.addView(emptyView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 0, 58 + 4, 0, 0)); - listView = new RecyclerListView(context) { + listView = new RecyclerListView(context, resourcesProvider) { @Override public void setTranslationY(float translationY) { @@ -133,11 +137,12 @@ public boolean emptyViewIsVisible() { return isEmptyViewVisible && getAdapter().getItemCount() <= 2; } }; + listView.setOverScrollMode(View.OVER_SCROLL_NEVER); listView.setTag(13); listView.setPadding(0, 0, 0, AndroidUtilities.dp(48)); listView.setClipToPadding(false); listView.setHideIfEmpty(false); - listView.setSelectorDrawableColor(Theme.getColor(keyListSelector)); + listView.setSelectorDrawableColor(Theme.getColor(keyListSelector, resourcesProvider)); layoutManager = new FillLastLinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false, AndroidUtilities.dp(8), listView); layoutManager.setBind(false); listView.setLayoutManager(layoutManager); @@ -207,13 +212,13 @@ public SearchField(Context context) { super(context); searchBackground = new View(context); - searchBackground.setBackgroundDrawable(Theme.createRoundRectDrawable(AndroidUtilities.dp(18), Theme.getColor(keySearchBackground))); + searchBackground.setBackgroundDrawable(Theme.createRoundRectDrawable(AndroidUtilities.dp(18), Theme.getColor(keySearchBackground, resourcesProvider))); addView(searchBackground, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 36, Gravity.LEFT | Gravity.TOP, 14, 11, 14, 0)); searchIconImageView = new ImageView(context); searchIconImageView.setScaleType(ImageView.ScaleType.CENTER); searchIconImageView.setImageResource(R.drawable.smiles_inputsearch); - searchIconImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(keySearchPlaceholder), PorterDuff.Mode.MULTIPLY)); + searchIconImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(keySearchPlaceholder, resourcesProvider), PorterDuff.Mode.MULTIPLY)); addView(searchIconImageView, LayoutHelper.createFrame(36, 36, Gravity.LEFT | Gravity.TOP, 16, 11, 0, 0)); clearSearchImageView = new ImageView(context); @@ -348,7 +353,7 @@ private float getColorProgress() { protected void setColorProgress(float progress) { colorProgress = progress; - backgroundColor = AndroidUtilities.getOffsetColor(Theme.getColor(keyInviteMembersBackground), Theme.getColor(keyListViewBackground), progress, 1.0f); + backgroundColor = AndroidUtilities.getOffsetColor(Theme.getColor(keyInviteMembersBackground, resourcesProvider), Theme.getColor(keyListViewBackground, resourcesProvider), progress, 1.0f); shadowDrawable.setColorFilter(new PorterDuffColorFilter(backgroundColor, PorterDuff.Mode.MULTIPLY)); frameLayout.setBackgroundColor(backgroundColor); fixNavigationBar(backgroundColor); @@ -542,7 +547,7 @@ public void onAnimationEnd(Animator animation) { setTranslationY(snapToTopOffset); } } else { - padding = availableHeight - (availableHeight / 5 * 3) + AndroidUtilities.dp(8); + padding = measurePadding(availableHeight); setAllowNestedScroll(true); } if (listView.getPaddingTop() != padding) { @@ -608,17 +613,18 @@ protected void onDraw(Canvas canvas) { shadowDrawable.setBounds(0, top, getMeasuredWidth(), height); shadowDrawable.draw(canvas); - if (radProgress != 1.0f) { - Theme.dialogs_onlineCirclePaint.setColor(backgroundColor); - rect.set(backgroundPaddingLeft, backgroundPaddingTop + top, getMeasuredWidth() - backgroundPaddingLeft, backgroundPaddingTop + top + AndroidUtilities.dp(24)); - canvas.drawRoundRect(rect, AndroidUtilities.dp(12) * radProgress, AndroidUtilities.dp(12) * radProgress, Theme.dialogs_onlineCirclePaint); - } - - int w = AndroidUtilities.dp(36); - rect.set((getMeasuredWidth() - w) / 2, y, (getMeasuredWidth() + w) / 2, y + AndroidUtilities.dp(4)); - Theme.dialogs_onlineCirclePaint.setColor(Theme.getColor(keyScrollUp)); - canvas.drawRoundRect(rect, AndroidUtilities.dp(2), AndroidUtilities.dp(2), Theme.dialogs_onlineCirclePaint); + if(!drawTitle) { + if (radProgress != 1.0f) { + Theme.dialogs_onlineCirclePaint.setColor(backgroundColor); + rect.set(backgroundPaddingLeft, backgroundPaddingTop + top, getMeasuredWidth() - backgroundPaddingLeft, backgroundPaddingTop + top + AndroidUtilities.dp(24)); + canvas.drawRoundRect(rect, AndroidUtilities.dp(12) * radProgress, AndroidUtilities.dp(12) * radProgress, Theme.dialogs_onlineCirclePaint); + } + int w = AndroidUtilities.dp(36); + rect.set((getMeasuredWidth() - w) / 2, y, (getMeasuredWidth() + w) / 2, y + AndroidUtilities.dp(4)); + Theme.dialogs_onlineCirclePaint.setColor(Theme.getColor(keyScrollUp)); + canvas.drawRoundRect(rect, AndroidUtilities.dp(2), AndroidUtilities.dp(2), Theme.dialogs_onlineCirclePaint); + } if (statusBarHeight > 0) { Theme.dialogs_onlineCirclePaint.setColor(backgroundColor); canvas.drawRect(backgroundPaddingLeft, AndroidUtilities.statusBarHeight - statusBarHeight - getTranslationY(), getMeasuredWidth() - backgroundPaddingLeft, AndroidUtilities.statusBarHeight - getTranslationY(), Theme.dialogs_onlineCirclePaint); @@ -645,6 +651,35 @@ protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); canvas.restore(); } + } + + protected int measurePadding(int availableHeight) { + return availableHeight - (availableHeight / 5 * 3) + AndroidUtilities.dp(8); + } + + @Override + public void setTitle(CharSequence title) { + if (titleView == null) { + titleView = new TextView(getContext()); + titleView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider)); + titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); + titleView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + titleView.setLines(1); + titleView.setMaxLines(1); + titleView.setSingleLine(true); + titleView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); + titleView.setEllipsize(TextUtils.TruncateAt.END); + frameLayout.addView(titleView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 36, Gravity.TOP | Gravity.LEFT, 16, 0, 0, 0)); + + FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) searchView.getLayoutParams(); + layoutParams.topMargin = AndroidUtilities.dp(30); + frameLayout.getLayoutParams().height = AndroidUtilities.dp(58 + 36); + } + titleView.setText(title); + } + public void showSearch(boolean show) { + searchView.setVisibility(show ? View.VISIBLE : View.GONE); + frameLayout.getLayoutParams().height = titleView == null ? 0 : AndroidUtilities.dp(36); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoEditTextureView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoEditTextureView.java index e4f67414d0..914015cea6 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoEditTextureView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoEditTextureView.java @@ -2,9 +2,14 @@ import android.content.Context; import android.graphics.SurfaceTexture; +import android.util.Log; import android.view.Surface; import android.view.TextureView; +import androidx.annotation.Nullable; + +import org.telegram.ui.Stories.recorder.StoryEntry; + public class VideoEditTextureView extends TextureView implements TextureView.SurfaceTextureListener { private VideoPlayer currentVideoPlayer; @@ -14,6 +19,14 @@ public class VideoEditTextureView extends TextureView implements TextureView.Sur private int videoWidth; private int videoHeight; + public StoryEntry.HDRInfo hdrInfo; + public void setHDRInfo(StoryEntry.HDRInfo hdrInfo) { + this.hdrInfo = hdrInfo; + if (eglThread != null) { + eglThread.updateHDRInfo(this.hdrInfo); + } + } + private VideoEditTextureViewDelegate delegate; public interface VideoEditTextureViewDelegate { @@ -64,7 +77,7 @@ public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int hei } Surface s = new Surface(surfaceTexture); currentVideoPlayer.setSurface(s); - }); + }, hdrInfo); if (videoWidth != 0 && videoHeight != 0) { eglThread.setVideoSize(videoWidth, videoHeight); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoPlayer.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoPlayer.java index e254b82c2f..c1b3e0392f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoPlayer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoPlayer.java @@ -8,14 +8,18 @@ package org.telegram.ui.Components; +import static com.google.android.exoplayer2.C.TRACK_TYPE_AUDIO; + import android.annotation.SuppressLint; import android.content.Context; import android.graphics.SurfaceTexture; import android.media.AudioManager; +import android.media.MediaFormat; import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.view.Surface; +import android.view.SurfaceView; import android.view.TextureView; import android.view.ViewGroup; @@ -24,24 +28,20 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.DefaultLoadControl; import com.google.android.exoplayer2.DefaultRenderersFactory; -import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayer; +import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.PlaybackException; import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Player; -import com.google.android.exoplayer2.Renderer; -import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.analytics.AnalyticsListener; import com.google.android.exoplayer2.audio.AudioAttributes; import com.google.android.exoplayer2.audio.AudioCapabilities; import com.google.android.exoplayer2.audio.AudioProcessor; -import com.google.android.exoplayer2.audio.AudioRendererEventListener; import com.google.android.exoplayer2.audio.AudioSink; import com.google.android.exoplayer2.audio.DefaultAudioSink; import com.google.android.exoplayer2.audio.TeeAudioProcessor; import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer; -import com.google.android.exoplayer2.mediacodec.MediaCodecSelector; import com.google.android.exoplayer2.source.LoopingMediaSource; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.ProgressiveMediaSource; @@ -52,23 +52,33 @@ import com.google.android.exoplayer2.trackselection.MappingTrackSelector; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DefaultAllocator; -import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; +import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.video.SurfaceNotValidException; import com.google.android.exoplayer2.video.VideoListener; import com.google.android.exoplayer2.video.VideoSize; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.DispatchQueue; +import org.telegram.messenger.FileLoader; import org.telegram.messenger.FourierTransform; import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.secretmedia.ExtendedDefaultDataSourceFactory; +import org.telegram.ui.Stories.recorder.StoryEntry; import java.nio.ByteBuffer; -import java.util.ArrayList; +import java.nio.ByteOrder; @SuppressLint("NewApi") public class VideoPlayer implements Player.Listener, VideoListener, AnalyticsListener, NotificationCenter.NotificationCenterDelegate { + private DispatchQueue workerQueue; + private boolean isStory; + + public boolean createdWithAudioTrack() { + return !audioDisabled; + } + public interface VideoPlayerDelegate { void onStateChanged(boolean playWhenReady, int playbackState); void onError(VideoPlayer player, Exception e); @@ -92,11 +102,12 @@ public interface AudioVisualizerDelegate { boolean needUpdate(); } - private ExoPlayer player; + public ExoPlayer player; private ExoPlayer audioPlayer; private MappingTrackSelector trackSelector; private DataSource.Factory mediaDataSourceFactory; private TextureView textureView; + private SurfaceView surfaceView; private Surface surface; private boolean isStreaming; private boolean autoplay; @@ -129,18 +140,26 @@ public interface AudioVisualizerDelegate { Handler audioUpdateHandler = new Handler(Looper.getMainLooper()); + boolean audioDisabled; + public VideoPlayer() { - this(true); + this(true, false); } - public VideoPlayer(boolean pauseOther) { + static int playerCounter = 0; + public VideoPlayer(boolean pauseOther, boolean audioDisabled) { + this.audioDisabled = audioDisabled; mediaDataSourceFactory = new ExtendedDefaultDataSourceFactory(ApplicationLoader.applicationContext, "Mozilla/5.0 (X11; Linux x86_64; rv:10.0) Gecko/20150101 Firefox/47.0 (Chrome)"); trackSelector = new DefaultTrackSelector(ApplicationLoader.applicationContext); + if (audioDisabled) { + trackSelector.setParameters(trackSelector.getParameters().buildUpon().setTrackTypeDisabled(TRACK_TYPE_AUDIO, true).build()); + } lastReportedPlaybackState = ExoPlayer.STATE_IDLE; shouldPauseOther = pauseOther; if (pauseOther) { NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.playerDidStartPlaying); } + playerCounter++; } @Override @@ -154,16 +173,30 @@ public void didReceivedNotification(int id, int account, Object... args) { } private void ensurePlayerCreated() { - DefaultLoadControl loadControl = new DefaultLoadControl( - new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE), - DefaultLoadControl.DEFAULT_MIN_BUFFER_MS, - DefaultLoadControl.DEFAULT_MAX_BUFFER_MS, - 100, - DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS, - DefaultLoadControl.DEFAULT_TARGET_BUFFER_BYTES, - DefaultLoadControl.DEFAULT_PRIORITIZE_TIME_OVER_SIZE_THRESHOLDS, - DefaultLoadControl.DEFAULT_BACK_BUFFER_DURATION_MS, - DefaultLoadControl.DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME); + DefaultLoadControl loadControl; + if (isStory) { + loadControl = new DefaultLoadControl( + new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE), + DefaultLoadControl.DEFAULT_MIN_BUFFER_MS, + DefaultLoadControl.DEFAULT_MAX_BUFFER_MS, + 1000, + 1000, + DefaultLoadControl.DEFAULT_TARGET_BUFFER_BYTES, + DefaultLoadControl.DEFAULT_PRIORITIZE_TIME_OVER_SIZE_THRESHOLDS, + DefaultLoadControl.DEFAULT_BACK_BUFFER_DURATION_MS, + DefaultLoadControl.DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME); + } else { + loadControl = new DefaultLoadControl( + new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE), + DefaultLoadControl.DEFAULT_MIN_BUFFER_MS, + DefaultLoadControl.DEFAULT_MAX_BUFFER_MS, + 100, + DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS, + DefaultLoadControl.DEFAULT_TARGET_BUFFER_BYTES, + DefaultLoadControl.DEFAULT_PRIORITIZE_TIME_OVER_SIZE_THRESHOLDS, + DefaultLoadControl.DEFAULT_BACK_BUFFER_DURATION_MS, + DefaultLoadControl.DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME); + } if (player == null) { DefaultRenderersFactory factory; if (audioVisualizerDelegate != null) { @@ -271,6 +304,10 @@ private MediaSource mediaSourceFromUri(Uri uri, String type) { } public void preparePlayer(Uri uri, String type) { + preparePlayer(uri, type, FileLoader.PRIORITY_HIGH); + } + + public void preparePlayer(Uri uri, String type, int priority) { this.videoUri = uri; this.videoType = type; this.audioUri = null; @@ -304,6 +341,7 @@ public void releasePlayer(boolean async) { if (shouldPauseOther) { NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.playerDidStartPlaying); } + playerCounter--; } @Override @@ -338,6 +376,17 @@ public void setTextureView(TextureView texture) { player.setVideoTextureView(textureView); } + public void setSurfaceView(SurfaceView surfaceView) { + if (this.surfaceView == surfaceView) { + return; + } + this.surfaceView = surfaceView; + if (player == null) { + return; + } + player.setVideoSurfaceView(surfaceView); + } + public void setSurface(Surface s) { if (surface == s) { return; @@ -569,14 +618,29 @@ public void onPlayerError(PlaybackException error) { parent.removeView(textureView); parent.addView(textureView, i); } - player.clearVideoTextureView(textureView); - player.setVideoTextureView(textureView); - if (loopingMediaSource) { - preparePlayerLoop(videoUri, videoType, audioUri, audioType); + if (workerQueue != null) { + workerQueue.postRunnable(() -> { + if (player != null) { + player.clearVideoTextureView(textureView); + player.setVideoTextureView(textureView); + if (loopingMediaSource) { + preparePlayerLoop(videoUri, videoType, audioUri, audioType); + } else { + preparePlayer(videoUri, videoType); + } + play(); + } + }); } else { - preparePlayer(videoUri, videoType); + player.clearVideoTextureView(textureView); + player.setVideoTextureView(textureView); + if (loopingMediaSource) { + preparePlayerLoop(videoUri, videoType, audioUri, audioType); + } else { + preparePlayer(videoUri, videoType); + } + play(); } - play(); } } else { delegate.onError(this, error); @@ -584,6 +648,10 @@ public void onPlayerError(PlaybackException error) { }); } + public VideoSize getVideoSize() { + return player != null ? player.getVideoSize() : null; + } + @Override public void onVideoSizeChanged(VideoSize videoSize) { delegate.onVideoSizeChanged(videoSize.width, videoSize.height, videoSize.unappliedRotationDegrees, videoSize.pixelWidthHeightRatio); @@ -760,4 +828,49 @@ public void handleBuffer(ByteBuffer buffer) { } } } + + public boolean isHDR() { + if (player == null) { + return false; + } + try { + Format format = player.getVideoFormat(); + if (format == null || format.colorInfo == null) { + return false; + } + return ( + format.colorInfo.colorTransfer == C.COLOR_TRANSFER_ST2084 || + format.colorInfo.colorTransfer == C.COLOR_TRANSFER_HLG + ); + } catch (Exception ignore) {} + return false; + } + + + public StoryEntry.HDRInfo getHDRStaticInfo(StoryEntry.HDRInfo hdrInfo) { + if (hdrInfo == null) { + hdrInfo = new StoryEntry.HDRInfo(); + } + try { + MediaFormat mediaFormat = ((MediaCodecRenderer) player.getRenderer(0)).codecOutputMediaFormat; + ByteBuffer byteBuffer = mediaFormat.getByteBuffer(MediaFormat.KEY_HDR_STATIC_INFO); + byteBuffer.order(ByteOrder.LITTLE_ENDIAN); + if (byteBuffer.get() == 0) { + hdrInfo.maxlum = byteBuffer.getShort(17); + hdrInfo.minlum = byteBuffer.getShort(19) * 0.0001f; + } + } catch (Exception ignore) { + hdrInfo.maxlum = hdrInfo.minlum = 0; + } + return hdrInfo; + } + + public void setWorkerQueue(DispatchQueue dispatchQueue) { + workerQueue = dispatchQueue; + player.setWorkerQueue(dispatchQueue); + } + + public void setIsStory() { + isStory = true; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoTimelinePlayView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoTimelinePlayView.java index d0086b471c..2e0a39a61a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoTimelinePlayView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoTimelinePlayView.java @@ -8,16 +8,21 @@ package org.telegram.ui.Components; +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.AndroidUtilities.dpf2; + import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; +import android.graphics.Path; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.media.MediaMetadataRetriever; +import android.net.Uri; import android.os.AsyncTask; import android.os.Build; import android.view.MotionEvent; @@ -26,6 +31,7 @@ import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.FileLog; import org.telegram.messenger.R; +import org.telegram.ui.ActionBar.Theme; import java.util.ArrayList; @@ -36,6 +42,7 @@ public class VideoTimelinePlayView extends View { private float progressRight = 1; private Paint paint; private Paint paint2; + private Paint paint3; private boolean pressedLeft; private boolean pressedRight; private boolean pressedPlay; @@ -58,7 +65,7 @@ public class VideoTimelinePlayView extends View { private int lastWidth; private int currentMode = MODE_VIDEO; - Paint bitmapPaint = new Paint(); + Paint bitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); private ArrayList exclusionRects = new ArrayList<>(); private android.graphics.Rect exclustionRect = new Rect(); @@ -83,11 +90,13 @@ public VideoTimelinePlayView(Context context) { paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor(0xffffffff); paint2 = new Paint(); - paint2.setColor(0x7f000000); - drawableLeft = context.getResources().getDrawable(R.drawable.video_cropleft); - drawableLeft.setColorFilter(new PorterDuffColorFilter(0xff000000, PorterDuff.Mode.SRC_IN)); - drawableRight = context.getResources().getDrawable(R.drawable.video_cropright); - drawableRight.setColorFilter(new PorterDuffColorFilter(0xff000000, PorterDuff.Mode.SRC_IN)); + paint2.setColor(0x4d000000); + paint3 = new Paint(); + paint3.setColor(0xff000000); +// drawableLeft = context.getResources().getDrawable(R.drawable.video_cropleft); +// drawableLeft.setColorFilter(new PorterDuffColorFilter(0xff000000, PorterDuff.Mode.MULTIPLY)); +// drawableRight = context.getResources().getDrawable(R.drawable.video_cropright); +// drawableRight.setColorFilter(new PorterDuffColorFilter(0xff000000, PorterDuff.Mode.MULTIPLY)); exclusionRects.add(exclustionRect); } @@ -103,6 +112,17 @@ public float getRightProgress() { return progressRight; } + public float getProgressOf(int type) { + if (type == TYPE_LEFT) { + return getLeftProgress(); + } else if (type == TYPE_PROGRESS) { + return getProgress(); + } else if (type == TYPE_RIGHT) { + return getRightProgress(); + } + return 0; + } + public void setMinProgressDiff(float value) { minProgressDiff = value; } @@ -140,18 +160,18 @@ public boolean onTouchEvent(MotionEvent event) { float x = event.getX(); float y = event.getY(); - int width = getMeasuredWidth() - AndroidUtilities.dp(32); - int startX = (int) (width * progressLeft) + AndroidUtilities.dp(16); - int playX = (int) (width * playProgress) + AndroidUtilities.dp(16); - int endX = (int) (width * progressRight) + AndroidUtilities.dp(16); + int width = getMeasuredWidth() - dp(32); + int startX = (int) (width * progressLeft) + dp(16); + int playX = (int) (width * playProgress) + dp(16); + int endX = (int) (width * progressRight) + dp(16); if (event.getAction() == MotionEvent.ACTION_DOWN) { getParent().requestDisallowInterceptTouchEvent(true); if (mediaMetadataRetriever == null) { return false; } - int additionWidth = AndroidUtilities.dp(16); - int additionWidthPlay = AndroidUtilities.dp(8); + int additionWidth = dp(16); + int additionWidthPlay = dp(8); if (endX != startX && playX - additionWidthPlay <= x && x <= playX + additionWidthPlay && y >= 0 && y <= getMeasuredHeight()) { if (delegate != null) { delegate.didStartDragging(TYPE_PROGRESS); @@ -181,7 +201,7 @@ public boolean onTouchEvent(MotionEvent event) { delegate.didStartDragging(TYPE_PROGRESS); } pressedPlay = true; - playProgress = (x - AndroidUtilities.dp(16)) / width; + playProgress = (x - dp(16)) / width; if (delegate != null) { delegate.onPlayProgressChanged(playProgress); } @@ -212,7 +232,7 @@ public boolean onTouchEvent(MotionEvent event) { } else if (event.getAction() == MotionEvent.ACTION_MOVE) { if (pressedPlay) { playX = (int) (x - pressDx); - playProgress = (float) (playX - AndroidUtilities.dp(16)) / (float) width; + playProgress = (float) (playX - dp(16)) / (float) width; if (playProgress < progressLeft) { playProgress = progressLeft; } else if (playProgress > progressRight) { @@ -225,12 +245,12 @@ public boolean onTouchEvent(MotionEvent event) { return true; } else if (pressedLeft) { startX = (int) (x - pressDx); - if (startX < AndroidUtilities.dp(16)) { - startX = AndroidUtilities.dp(16); + if (startX < dp(16)) { + startX = dp(16); } else if (startX > endX) { startX = endX; } - progressLeft = (float) (startX - AndroidUtilities.dp(16)) / (float) width; + progressLeft = (float) (startX - dp(16)) / (float) width; if (progressRight - progressLeft > maxProgressDiff) { progressRight = progressLeft + maxProgressDiff; } else if (minProgressDiff != 0 && progressRight - progressLeft < minProgressDiff) { @@ -253,10 +273,10 @@ public boolean onTouchEvent(MotionEvent event) { endX = (int) (x - pressDx); if (endX < startX) { endX = startX; - } else if (endX > width + AndroidUtilities.dp(16)) { - endX = width + AndroidUtilities.dp(16); + } else if (endX > width + dp(16)) { + endX = width + dp(16); } - progressRight = (float) (endX - AndroidUtilities.dp(16)) / (float) width; + progressRight = (float) (endX - dp(16)) / (float) width; if (progressRight - progressLeft > maxProgressDiff) { progressLeft = progressRight - maxProgressDiff; } else if (minProgressDiff != 0 && progressRight - progressLeft < minProgressDiff) { @@ -295,6 +315,10 @@ public void setVideoPath(String path, float left, float right) { invalidate(); } + public long getLength() { + return Math.max(1, videoLength); + } + public void setRightProgress(float value) { progressRight = value; if (delegate != null) { @@ -309,6 +333,12 @@ public void setRightProgress(float value) { invalidate(); } + public void setLeftRightProgress(float left, float right) { + progressRight = right; + progressLeft = left; + invalidate(); + } + public void setDelegate(VideoTimelineViewDelegate delegate) { this.delegate = delegate; } @@ -318,9 +348,9 @@ private void reloadFrames(int frameNum) { return; } if (frameNum == 0) { - frameHeight = AndroidUtilities.dp(40); - framesToLoad = Math.max(1, (getMeasuredWidth() - AndroidUtilities.dp(16)) / frameHeight); - frameWidth = (int) Math.ceil((float) (getMeasuredWidth() - AndroidUtilities.dp(16)) / (float) framesToLoad); + frameHeight = dp(40); + framesToLoad = Math.max(1, (getMeasuredWidth() - dp(16)) / frameHeight); + frameWidth = (int) Math.ceil((float) (getMeasuredWidth() - dp(16)) / (float) framesToLoad); frameTimeOffset = videoLength / framesToLoad; } currentTask = new AsyncTask() { @@ -347,7 +377,7 @@ protected Bitmap doInBackground(Integer... objects) { int w = (int) (bitmap.getWidth() * scale); int h = (int) (bitmap.getHeight() * scale); Rect srcRect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); - Rect destRect = new Rect((frameWidth - w) / 2, (frameHeight - h) / 2, w, h); + Rect destRect = new Rect((frameWidth - w) / 2, (frameHeight - h) / 2, (frameWidth + w) / 2, (frameHeight + h) / 2); canvas.drawBitmap(bitmap, srcRect, destRect, null); bitmap.recycle(); bitmap = result; @@ -408,7 +438,7 @@ public void setProgress(float value) { public void clearFrames() { for (int a = 0; a < frames.size(); a++) { BitmapFrame frame = frames.get(a); - if (frame != null) { + if (frame != null && frame.bitmap != null) { frame.bitmap.recycle(); } } @@ -430,31 +460,43 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } } + private Path clipPath = new Path(); + @Override protected void onDraw(Canvas canvas) { - int width = getMeasuredWidth() - AndroidUtilities.dp(32); - int startX = (int) (width * progressLeft) + AndroidUtilities.dp(16); - int endX = (int) (width * progressRight) + AndroidUtilities.dp(16); + int width = getMeasuredWidth() - dp(32); + int startX = (int) (width * progressLeft) + dp(16); + int endX = (int) (width * progressRight) + dp(16); + + int top = dp(2 + 4); + int end = dp(48); canvas.save(); - canvas.clipRect(AndroidUtilities.dp(16), AndroidUtilities.dp(4), width + AndroidUtilities.dp(20), AndroidUtilities.dp(48)); + canvas.clipRect(dp(16), dp(4), width + dp(20), dp(48)); if (frames.isEmpty() && currentTask == null) { + canvas.drawRect(dp(16), top, dp(16) + width + dp(4), dp(46), paint2); reloadFrames(0); } else { + canvas.save(); + clipPath.rewind(); + AndroidUtilities.rectTmp.set(dp(16), dp(6), width + dp(20), dp(46)); + clipPath.addRoundRect(AndroidUtilities.rectTmp, dp(6), dp(6), Path.Direction.CW); + canvas.clipPath(clipPath); + canvas.drawRect(startX, top, endX, dp(46), paint2); int offset = 0; for (int a = 0; a < frames.size(); a++) { BitmapFrame bitmap = frames.get(a); if (bitmap.bitmap != null) { - int x = AndroidUtilities.dp(16) + offset * frameWidth; - int y = AndroidUtilities.dp(2 + 4); + int x = dp(16) + offset * frameWidth; + int y = dp(2 + 4); if (bitmap.alpha != 1f) { - bitmap.alpha += 16f / 100f; + bitmap.alpha += 16f / 350f; if (bitmap.alpha > 1f) { bitmap.alpha = 1f; } else { invalidate(); } - bitmapPaint.setAlpha((int) (255 * bitmap.alpha)); + bitmapPaint.setAlpha((int) (255 * CubicBezierInterpolator.EASE_OUT_QUINT.getInterpolation(bitmap.alpha))); canvas.drawBitmap(bitmap.bitmap, x, y, bitmapPaint); } else { canvas.drawBitmap(bitmap.bitmap, x, y, null); @@ -462,38 +504,35 @@ protected void onDraw(Canvas canvas) { } offset++; } + canvas.drawRect(dp(16), top, startX, dp(46), paint2); + canvas.drawRect(endX + dp(4), top, dp(16) + width + dp(4), dp(46), paint2); + canvas.restore(); } - int top = AndroidUtilities.dp(2 + 4); - int end = AndroidUtilities.dp(48); - - canvas.drawRect(AndroidUtilities.dp(16), top, startX, AndroidUtilities.dp(46), paint2); - canvas.drawRect(endX + AndroidUtilities.dp(4), top, AndroidUtilities.dp(16) + width + AndroidUtilities.dp(4), AndroidUtilities.dp(46), paint2); - - canvas.drawRect(startX, AndroidUtilities.dp(4), startX + AndroidUtilities.dp(2), end, paint); - canvas.drawRect(endX + AndroidUtilities.dp(2), AndroidUtilities.dp(4), endX + AndroidUtilities.dp(4), end, paint); - canvas.drawRect(startX + AndroidUtilities.dp(2), AndroidUtilities.dp(4), endX + AndroidUtilities.dp(4), top, paint); - canvas.drawRect(startX + AndroidUtilities.dp(2), end - AndroidUtilities.dp(2), endX + AndroidUtilities.dp(4), end, paint); + canvas.drawRect(startX, dp(4), startX + dp(2), end, paint); + canvas.drawRect(endX + dp(2), dp(4), endX + dp(4), end, paint); + canvas.drawRect(startX + dp(2), dp(4), endX + dp(4), top, paint); + canvas.drawRect(startX + dp(2), end - dp(2), endX + dp(4), end, paint); canvas.restore(); - rect3.set(startX - AndroidUtilities.dp(8), AndroidUtilities.dp(4), startX + AndroidUtilities.dp(2), end); - canvas.drawRoundRect(rect3, AndroidUtilities.dp(2), AndroidUtilities.dp(2), paint); - drawableLeft.setBounds(startX - AndroidUtilities.dp(8), AndroidUtilities.dp(4) + (AndroidUtilities.dp(44) - AndroidUtilities.dp(18)) / 2, startX + AndroidUtilities.dp(2), (AndroidUtilities.dp(44) - AndroidUtilities.dp(18)) / 2 + AndroidUtilities.dp(18 + 4)); - drawableLeft.draw(canvas); + rect3.set(startX - dp(8), dp(4), startX + dp(2), end); + canvas.drawRoundRect(rect3, dp(3), dp(3), paint); + rect3.set(startX - dpf2(2), dp(21.17f), startX - dpf2(2 + 2), dp(30.83f)); + canvas.drawRoundRect(rect3, dp(3), dp(3), paint3); - rect3.set(endX + AndroidUtilities.dp(2), AndroidUtilities.dp(4), endX + AndroidUtilities.dp(12), end); - canvas.drawRoundRect(rect3, AndroidUtilities.dp(2), AndroidUtilities.dp(2), paint); - drawableRight.setBounds(endX + AndroidUtilities.dp(2), AndroidUtilities.dp(4) + (AndroidUtilities.dp(44) - AndroidUtilities.dp(18)) / 2, endX + AndroidUtilities.dp(12), (AndroidUtilities.dp(44) - AndroidUtilities.dp(18)) / 2 + AndroidUtilities.dp(18 + 4)); - drawableRight.draw(canvas); + rect3.set(endX + dp(2), dp(4), endX + dp(12), end); + canvas.drawRoundRect(rect3, dp(3), dp(3), paint); + rect3.set(endX + dpf2(6), dp(21.17f), endX + dpf2(6 + 2), dp(30.83f)); + canvas.drawRoundRect(rect3, dp(3), dp(3), paint3); - float cx = AndroidUtilities.dp(18) + width * playProgress; - rect3.set(cx - AndroidUtilities.dp(1.5f), AndroidUtilities.dp(2), cx + AndroidUtilities.dp(1.5f), AndroidUtilities.dp(50)); - canvas.drawRoundRect(rect3, AndroidUtilities.dp(1), AndroidUtilities.dp(1), paint2); - canvas.drawCircle(cx, AndroidUtilities.dp(52), AndroidUtilities.dp(3.5f), paint2); + float cx = dp(18) + width * playProgress; + rect3.set(cx - dp(2), dp(2), cx + dp(2), dp(50)); + canvas.drawRoundRect(rect3, dp(1), dp(1), paint2); +// canvas.drawCircle(cx, dp(52), dp(3.5f), paint2); - rect3.set(cx - AndroidUtilities.dp(1), AndroidUtilities.dp(2), cx + AndroidUtilities.dp(1), AndroidUtilities.dp(50)); - canvas.drawRoundRect(rect3, AndroidUtilities.dp(1), AndroidUtilities.dp(1), paint); - canvas.drawCircle(cx, AndroidUtilities.dp(52), AndroidUtilities.dp(3), paint); + rect3.set(cx - dpf2(1.5f), dp(2), cx + dpf2(1.5f), dp(50)); + canvas.drawRoundRect(rect3, dp(1), dp(1), paint); +// canvas.drawCircle(cx, dp(52), dp(3), paint); } private static class BitmapFrame { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoTimelineView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoTimelineView.java index ce80e38c96..c6c1b7c204 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoTimelineView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoTimelineView.java @@ -13,6 +13,7 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; +import android.graphics.Path; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.PorterDuffXfermode; @@ -66,8 +67,10 @@ public class VideoTimelineView extends View { private ArrayList keyframes = new ArrayList<>(); private boolean framesLoaded; private TimeHintView timeHintView; + Path path; Paint thumbPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + boolean useClip; public void setKeyframes(ArrayList keyframes) { this.keyframes.clear(); @@ -395,8 +398,28 @@ public void clearFrames() { invalidate(); } + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + if (useClip) { + if (path == null) { + path = new Path(); + } + path.rewind(); + int topOffset = (getMeasuredHeight() - AndroidUtilities.dp(32)) >> 1; + AndroidUtilities.rectTmp.set(0, topOffset, getMeasuredWidth(), getMeasuredHeight() - topOffset); + path.addRoundRect(AndroidUtilities.rectTmp, AndroidUtilities.dp(7), AndroidUtilities.dp(7), Path.Direction.CCW); + } + } + @Override protected void onDraw(Canvas canvas) { + if (useClip) { + canvas.save(); + if (path != null) { + canvas.clipPath(path); + } + } int width = getMeasuredWidth() - AndroidUtilities.dp(24); int startX = (int) (width * progressLeft) + AndroidUtilities.dp(12); int endX = (int) (width * progressRight) + AndroidUtilities.dp(12); @@ -425,6 +448,9 @@ protected void onDraw(Canvas canvas) { offset++; } } else { + if (useClip) { + canvas.restore(); + } return; } @@ -434,7 +460,11 @@ protected void onDraw(Canvas canvas) { canvas.drawLine(startX - AndroidUtilities.dp(4), topOffset + AndroidUtilities.dp(10), startX - AndroidUtilities.dp(4), getMeasuredHeight() - AndroidUtilities.dp(10) - topOffset, thumbPaint); canvas.drawLine(endX + AndroidUtilities.dp(4), topOffset + AndroidUtilities.dp(10), endX + AndroidUtilities.dp(4), getMeasuredHeight() - AndroidUtilities.dp(10) - topOffset, thumbPaint); - drawCorners(canvas, getMeasuredHeight() - topOffset * 2, getMeasuredWidth(), 0, topOffset); + if (useClip) { + canvas.restore(); + } else { + drawCorners(canvas, getMeasuredHeight() - topOffset * 2, getMeasuredWidth(), 0, topOffset); + } } private void drawCorners(Canvas canvas, int height, int width, int left, int top) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ViewPagerFixed.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ViewPagerFixed.java index dade1b05cf..56bf41362f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ViewPagerFixed.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ViewPagerFixed.java @@ -20,6 +20,7 @@ import android.text.TextPaint; import android.text.TextUtils; import android.transition.TransitionManager; +import android.util.Log; import android.util.SparseArray; import android.util.SparseIntArray; import android.view.MotionEvent; @@ -45,6 +46,7 @@ import org.telegram.messenger.MessagesController; import org.telegram.messenger.MessagesStorage; import org.telegram.messenger.UserConfig; +import org.telegram.messenger.Utilities; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.Theme; @@ -94,11 +96,35 @@ public void onAnimationUpdate(ValueAnimator valueAnimator) { tabsView.selectTab(nextPosition, currentPosition, 1f - scrollProgress); } } + onTabAnimationUpdate(); } }; private Rect rect = new Rect(); private boolean allowDisallowInterceptTouch = true; + protected void onTabAnimationUpdate() { + + } + + public float getPositionAnimated() { + float position = 0; + if (viewPages[0] != null && viewPages[0].getVisibility() == View.VISIBLE) { + final float t = Utilities.clamp(1f - Math.abs(viewPages[0].getTranslationX() / (float) AndroidUtilities.displaySize.x), 1, 0); + position += currentPosition * t; + } + if (viewPages[1] != null && viewPages[1].getVisibility() == View.VISIBLE) { + final float t = Utilities.clamp(1f - Math.abs(viewPages[1].getTranslationX() / (float) AndroidUtilities.displaySize.x), 1, 0); + position += nextPosition * t; + } + return position; + } + + protected boolean canScroll(MotionEvent e) { + return true; + } + + protected void onScrollEnd() {} + public ViewPagerFixed(@NonNull Context context) { this(context, null); } @@ -133,6 +159,64 @@ protected int tabMarginDp() { return 16; } + public boolean isManualScrolling() { + return manualScrolling != null && manualScrolling.isRunning(); + } + + private ValueAnimator manualScrolling; + public void scrollToPosition(int page) { + boolean forward = currentPosition < page; + animatingForward = forward; + nextPosition = page; + updateViewForIndex(1); + + onTabPageSelected(page); + int trasnlationX = viewPages[0] != null ? viewPages[0].getMeasuredWidth() : 0; + if (forward) { + viewPages[1].setTranslationX(trasnlationX); + } else { + viewPages[1].setTranslationX(-trasnlationX); + } + + if (manualScrolling != null) { + manualScrolling.cancel(); + manualScrolling = null; + } + + manualScrolling = ValueAnimator.ofFloat(0, 1); + manualScrolling.addUpdateListener(anm -> { + float progress = (float) anm.getAnimatedValue(); + if (viewPages[1] == null) { + return; + } + if (animatingForward) { + viewPages[1].setTranslationX(viewPages[0].getMeasuredWidth() * (1f - progress)); + viewPages[0].setTranslationX(-viewPages[0].getMeasuredWidth() * progress); + } else { + viewPages[1].setTranslationX(-viewPages[0].getMeasuredWidth() * (1f - progress)); + viewPages[0].setTranslationX(viewPages[0].getMeasuredWidth() * progress); + } + onTabAnimationUpdate(); + }); + manualScrolling.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (viewPages[1] != null) { + swapViews(); + viewsByType.put(viewTypes[1], viewPages[1]); + removeView(viewPages[1]); + viewPages[0].setTranslationX(0); + viewPages[1] = null; + } + manualScrolling = null; + onTabAnimationUpdate(); + } + }); + manualScrolling.setDuration(540); + manualScrolling.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + manualScrolling.start(); + } + public TabsView createTabsView(boolean hasStableIds, int selectorType) { tabsView = new TabsView(getContext(), hasStableIds, selectorType, resourcesProvider) { @Override @@ -205,6 +289,9 @@ protected void invalidateBlur() { } + public boolean isTouch() { + return startedTracking; + } private void updateViewForIndex(int index) { int adapterPosition = index == 0 ? currentPosition : nextPosition; @@ -273,7 +360,10 @@ protected void fillTabs(boolean animated) { } private boolean prepareForMoving(MotionEvent ev, boolean forward) { - if ((!forward && currentPosition == 0 && !onBackProgress(backProgress = 0)) || (forward && currentPosition == adapter.getItemCount() - 1)) { + if ((!forward && currentPosition == 0 && !onBackProgress(backProgress = 0)) || (forward && currentPosition == adapter.getItemCount() - 1) || manualScrolling != null) { + return false; + } + if (!canScroll(ev)) { return false; } @@ -295,6 +385,7 @@ private boolean prepareForMoving(MotionEvent ev, boolean forward) { viewPages[1].setTranslationX(-viewPages[0].getMeasuredWidth()); } } + onTabAnimationUpdate(); return true; } @@ -381,6 +472,7 @@ public boolean onTouchEvent(MotionEvent ev) { if (tabsView != null) { tabsView.selectTab(currentPosition, 0, 0); } + onTabAnimationUpdate(); } } if (maybeStartTracking && !startedTracking) { @@ -405,6 +497,7 @@ public boolean onTouchEvent(MotionEvent ev) { if (tabsView != null) { tabsView.selectTab(nextPosition, currentPosition, 1f - scrollProgress); } + onTabAnimationUpdate(); } } else if (ev == null || ev.getPointerId(0) == startedTrackingPointerId && (ev.getAction() == MotionEvent.ACTION_CANCEL || ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_POINTER_UP)) { if (velocityTracker != null) { @@ -522,11 +615,16 @@ public void onAnimationEnd(Animator animator) { if (tabsView != null) { tabsView.setEnabled(true); } + + onTabAnimationUpdate(); + onScrollEnd(); } }); tabsAnimation.start(); tabsAnimationInProgress = true; startedTracking = false; + + onTabAnimationUpdate(); } else { maybeStartTracking = false; if (tabsView != null) { @@ -875,12 +973,12 @@ protected void onDraw(Canvas canvas) { canvas.translate(AndroidUtilities.dp(0.66f) * p, 0); canvas.rotate(p, getMeasuredWidth() / 2, getMeasuredHeight() / 2); } - String key; - String animateToKey; - String otherKey; - String animateToOtherKey; - String unreadKey; - String unreadOtherKey; + int key; + int animateToKey; + int otherKey; + int animateToOtherKey; + int unreadKey; + int unreadOtherKey; int id1; int id2; if (manualScrollingToId != -1) { @@ -1037,11 +1135,11 @@ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { private int scrollingToChild = -1; private GradientDrawable selectorDrawable; - private String tabLineColorKey = Theme.key_profile_tabSelectedLine; - private String activeTextColorKey = Theme.key_profile_tabSelectedText; - private String unactiveTextColorKey = Theme.key_profile_tabText; - private String selectorColorKey = Theme.key_profile_tabSelector; - private String backgroundColorKey = Theme.key_actionBarDefault; + private int tabLineColorKey = Theme.key_profile_tabSelectedLine; + private int activeTextColorKey = Theme.key_profile_tabSelectedText; + private int unactiveTextColorKey = Theme.key_profile_tabText; + private int selectorColorKey = Theme.key_profile_tabSelector; + private int backgroundColorKey = Theme.key_actionBarDefault; private int prevLayoutWidth; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/WallpaperCheckBoxView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/WallpaperCheckBoxView.java index 682f25ab88..e16dfce990 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/WallpaperCheckBoxView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/WallpaperCheckBoxView.java @@ -17,6 +17,7 @@ public class WallpaperCheckBoxView extends View { + Theme.ResourcesProvider resourcesProvider; private Paint eraserPaint; private Paint checkPaint; private TextPaint textPaint; @@ -52,8 +53,9 @@ public Float get(WallpaperCheckBoxView object) { } }; - public WallpaperCheckBoxView(Context context, boolean check, View parent) { + public WallpaperCheckBoxView(Context context, boolean check, View parent, Theme.ResourcesProvider resourcesProvider) { super(context); + // this.resourcesProvider = resourcesProvider; rect = new RectF(); if (check) { @@ -107,14 +109,15 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { @Override protected void onDraw(Canvas canvas) { rect.set(0, 0, getMeasuredWidth(), getMeasuredHeight()); - Theme.applyServiceShaderMatrixForView(this, parentView); - canvas.drawRoundRect(rect, getMeasuredHeight() / 2, getMeasuredHeight() / 2, Theme.chat_actionBackgroundPaint); - if (Theme.hasGradientService()) { + Theme.applyServiceShaderMatrixForView(this, parentView, resourcesProvider); + canvas.drawRoundRect(rect, getMeasuredHeight() / 2, getMeasuredHeight() / 2, getThemedPaint(Theme.key_paint_chatActionBackground)); + boolean hasGradient = resourcesProvider == null ? Theme.hasGradientService() : resourcesProvider.hasGradientService(); + if (hasGradient) { canvas.drawRoundRect(rect, getMeasuredHeight() / 2, getMeasuredHeight() / 2, Theme.chat_actionBackgroundGradientDarkenPaint); } - textPaint.setColor(Theme.getColor(Theme.key_chat_serviceText)); + textPaint.setColor(Theme.getColor(Theme.key_chat_serviceText, resourcesProvider)); int x = (getMeasuredWidth() - currentTextSize - AndroidUtilities.dp(28)) / 2; canvas.drawText(currentText, x + AndroidUtilities.dp(28), AndroidUtilities.dp(21), textPaint); @@ -134,7 +137,7 @@ protected void onDraw(Canvas canvas) { rect.set(bounce, bounce, AndroidUtilities.dp(18) - bounce, AndroidUtilities.dp(18) - bounce); drawBitmap.eraseColor(0); - backgroundPaint.setColor(Theme.getColor(Theme.key_chat_serviceText)); + backgroundPaint.setColor(Theme.getColor(Theme.key_chat_serviceText, resourcesProvider)); drawCanvas.drawRoundRect(rect, rect.width() / 2, rect.height() / 2, backgroundPaint); if (checkProgress != 1) { @@ -219,4 +222,9 @@ public void setChecked(boolean checked, boolean animated) { public boolean isChecked() { return isChecked; } + + private Paint getThemedPaint(String paintKey) { + Paint paint = resourcesProvider != null ? resourcesProvider.getPaint(paintKey) : null; + return paint != null ? paint : Theme.getThemePaint(paintKey); + } } \ No newline at end of file diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ZoomControlView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ZoomControlView.java index fb71af2078..11029ba8e5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ZoomControlView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ZoomControlView.java @@ -48,6 +48,8 @@ public class ZoomControlView extends View { private ZoomControlViewDelegate delegate; + public boolean enabledTouch = true; + public interface ZoomControlViewDelegate { void didSetZoom(float zoom); } @@ -108,6 +110,9 @@ public void setDelegate(ZoomControlViewDelegate zoomControlViewDelegate) { @Override public boolean onTouchEvent(MotionEvent event) { + if (!enabledTouch) { + return false; + } float x = event.getX(); float y = event.getY(); int action = event.getAction(); @@ -197,6 +202,10 @@ public boolean onTouchEvent(MotionEvent event) { return handled || pressed || knobPressed || super.onTouchEvent(event); } + public boolean isTouch() { + return pressed || knobPressed; + } + private boolean animateToZoom(float zoom) { if (zoom < 0 || zoom > 1.0f) { return false; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/spoilers/SpoilerEffect.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/spoilers/SpoilerEffect.java index b7134697ca..02fc80ccea 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/spoilers/SpoilerEffect.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/spoilers/SpoilerEffect.java @@ -24,10 +24,8 @@ import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.StaticLayout; -import android.text.TextPaint; import android.text.style.ForegroundColorSpan; import android.text.style.ReplacementSpan; -import android.text.style.URLSpan; import android.view.View; import android.widget.TextView; @@ -40,7 +38,6 @@ import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.Emoji; import org.telegram.messenger.LiteMode; -import org.telegram.messenger.LocaleController; import org.telegram.messenger.SharedConfig; import org.telegram.messenger.Utilities; import org.telegram.ui.ActionBar.Theme; @@ -303,7 +300,6 @@ public void draw(@NonNull Canvas canvas) { int dt = (int) Math.min(curTime - lastDrawTime, renderDelayMs); boolean hasAnimator = false; - lastDrawTime = curTime; int left = getBounds().left, top = getBounds().top, right = getBounds().right, bottom = getBounds().bottom; @@ -528,7 +524,8 @@ public void setMaxParticlesCount(int maxParticles) { * @param spoilers Spoilers list to populate */ public static void addSpoilers(TextView tv, @Nullable Stack spoilersPool, List spoilers) { - addSpoilers(tv, tv.getLayout(), (Spanned) tv.getText(), spoilersPool, spoilers); + int width = tv.getMeasuredWidth(); + addSpoilers(tv, tv.getLayout(), 0, width > 0 ? width : -2, (Spanned) tv.getText(), spoilersPool, spoilers); } /** @@ -541,34 +538,58 @@ public static void addSpoilers(TextView tv, @Nullable Stack spoil */ public static void addSpoilers(@Nullable View v, Layout textLayout, @Nullable Stack spoilersPool, List spoilers) { if (NekoConfig.showSpoilersDirectly.Bool()) return; - if (textLayout.getText() instanceof Spanned){ + if (textLayout.getText() instanceof Spanned) { addSpoilers(v, textLayout, (Spanned) textLayout.getText(), spoilersPool, spoilers); } } + public static void addSpoilers(@Nullable View v, Layout textLayout, int left, int right, @Nullable Stack spoilersPool, List spoilers) { + if (textLayout.getText() instanceof Spanned) { + addSpoilers(v, textLayout, left, right, (Spanned) textLayout.getText(), spoilersPool, spoilers); + } + } + + public static void addSpoilers(@Nullable View v, Layout textLayout, Spanned spannable, @Nullable Stack spoilersPool, List spoilers) { + if (textLayout == null) { + return; + } + addSpoilers(v, textLayout, -1, -1, spannable, spoilersPool, spoilers); + } + /** * Parses spoilers from spannable * * @param v View to use as a parent view * @param textLayout Text layout to measure + * @param layoutLeft The minimum left bound to limit spoilers in + * @param layoutRight The maximum right bound to limit spoilers in. Use -1 when + * needed calculation, use -2 (or any other negative) when + * you don't want to limit anyway * @param spannable Text to parse * @param spoilersPool Cached spoilers pool, could be null, but highly recommended * @param spoilers Spoilers list to populate */ - public static void addSpoilers(@Nullable View v, Layout textLayout, Spanned spannable, @Nullable Stack spoilersPool, List spoilers) { - for (int line = 0; line < textLayout.getLineCount(); line++) { - float l = textLayout.getLineLeft(line), t = textLayout.getLineTop(line), r = textLayout.getLineRight(line), b = textLayout.getLineBottom(line); - int start = textLayout.getLineStart(line), end = textLayout.getLineEnd(line); - - for (TextStyleSpan span : spannable.getSpans(start, end, TextStyleSpan.class)) { - if (span.isSpoiler()) { - int ss = spannable.getSpanStart(span), se = spannable.getSpanEnd(span); - int realStart = Math.max(start, ss), realEnd = Math.min(end, se); - - int len = realEnd - realStart; - if (len == 0) continue; - addSpoilersInternal(v, spannable, textLayout, start, end, l, t, r, b, realStart, realEnd, spoilersPool, spoilers); + public static void addSpoilers(@Nullable View v, Layout textLayout, int layoutLeft, int layoutRight, Spanned spannable, @Nullable Stack spoilersPool, List spoilers) { + if (textLayout == null) { + return; + } + TextStyleSpan[] spans = spannable.getSpans(0, textLayout.getText().length(), TextStyleSpan.class); + for (int i = 0; i < spans.length; ++i) { + if (spans[i].isSpoiler()) { + int start = spannable.getSpanStart(spans[i]); + int end = spannable.getSpanEnd(spans[i]); + int left = layoutLeft, right = layoutRight; + if (left == -1 && right == -1) { + left = Integer.MAX_VALUE; + right = Integer.MIN_VALUE; + int linestart = textLayout.getLineForOffset(start); + int lineend = textLayout.getLineForOffset(end); + for (int l = linestart; l <= lineend; ++l) { + left = Math.min(left, (int) textLayout.getLineLeft(l)); + right = Math.max(right, (int) textLayout.getLineRight(l)); + } } + addSpoilerRangesInternal(v, textLayout, left, right, start, end, spoilersPool, spoilers); } } if (v instanceof TextView && spoilersPool != null) { @@ -576,59 +597,25 @@ public static void addSpoilers(@Nullable View v, Layout textLayout, Spanned span } } - @SuppressLint("WrongConstant") - private static void addSpoilersInternal(View v, Spanned spannable, Layout textLayout, int lineStart, - int lineEnd, float lineLeft, float lineTop, float lineRight, - float lineBottom, int realStart, int realEnd, Stack spoilersPool, - List spoilers) { - SpannableStringBuilder vSpan = SpannableStringBuilder.valueOf(AndroidUtilities.replaceNewLines(new SpannableStringBuilder(spannable, realStart, realEnd))); - for (TextStyleSpan styleSpan : vSpan.getSpans(0, vSpan.length(), TextStyleSpan.class)) - vSpan.removeSpan(styleSpan); - for (URLSpan urlSpan : vSpan.getSpans(0, vSpan.length(), URLSpan.class)) - vSpan.removeSpan(urlSpan); - int tLen = vSpan.toString().trim().length(); - if (tLen == 0) return; - int width = textLayout.getEllipsizedWidth() > 0 ? textLayout.getEllipsizedWidth() : textLayout.getWidth(); - TextPaint measurePaint = new TextPaint(textLayout.getPaint()); - measurePaint.setColor(Color.BLACK); - StaticLayout newLayout; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - newLayout = StaticLayout.Builder.obtain(vSpan, 0, vSpan.length(), measurePaint, width) - .setBreakStrategy(StaticLayout.BREAK_STRATEGY_HIGH_QUALITY) - .setHyphenationFrequency(StaticLayout.HYPHENATION_FREQUENCY_NONE) - .setAlignment(Layout.Alignment.ALIGN_NORMAL) - .setLineSpacing(textLayout.getSpacingAdd(), textLayout.getSpacingMultiplier()) - .build(); - } else - newLayout = new StaticLayout(vSpan, measurePaint, width, Layout.Alignment.ALIGN_NORMAL, textLayout.getSpacingMultiplier(), textLayout.getSpacingAdd(), false); - boolean rtlInNonRTL = (LocaleController.isRTLCharacter(vSpan.charAt(0)) || LocaleController.isRTLCharacter(vSpan.charAt(vSpan.length() - 1))) && !LocaleController.isRTL; + private static void addSpoilerRangesInternal(@Nullable View v, @NonNull Layout textLayout, int mostleft, int mostright, int start, int end, @Nullable Stack spoilersPool, List spoilers) { + textLayout.getSelectionPath(start, end, new Path() { + @Override + public void addRect(float left, float top, float right, float bottom, @NonNull Direction dir) { + addSpoilerRangeInternal(v, textLayout, left, top, right, bottom, spoilersPool, spoilers, mostleft, mostright); + } + }); + } + + private static void addSpoilerRangeInternal(@Nullable View v, @NonNull Layout textLayout, float left, float top, float right, float bottom, @Nullable Stack spoilersPool, List spoilers, int mostleft, int mostright) { SpoilerEffect spoilerEffect = spoilersPool == null || spoilersPool.isEmpty() ? new SpoilerEffect() : spoilersPool.remove(0); spoilerEffect.setRippleProgress(-1); - float ps = realStart == lineStart ? lineLeft : textLayout.getPrimaryHorizontal(realStart), - pe = realEnd == lineEnd || rtlInNonRTL && realEnd == lineEnd - 1 && spannable.charAt(lineEnd - 1) == '\u2026' ? lineRight : textLayout.getPrimaryHorizontal(realEnd); - spoilerEffect.setBounds((int) Math.min(ps, pe), (int) lineTop, (int) Math.max(ps, pe), (int) lineBottom); + spoilerEffect.setBounds((int) Math.max(left, mostleft), (int) top, (int) Math.min(right, mostright <= 0 ? Integer.MAX_VALUE : mostright), (int) bottom); spoilerEffect.setColor(textLayout.getPaint().getColor()); spoilerEffect.setRippleInterpolator(Easings.easeInQuad); spoilerEffect.updateMaxParticles(); if (v != null) { spoilerEffect.setParentView(v); } - spoilerEffect.spaces.clear(); - for (int i = 0; i < vSpan.length(); i++) { - if (vSpan.charAt(i) == ' ') { - RectF r = new RectF(); - int off = realStart + i; - int line = textLayout.getLineForOffset(off); - r.top = textLayout.getLineTop(line); - r.bottom = textLayout.getLineBottom(line); - float lh = textLayout.getPrimaryHorizontal(off), rh = textLayout.getPrimaryHorizontal(off + 1); - r.left = (int) Math.min(lh, rh); // RTL - r.right = (int) Math.max(lh, rh); - if (Math.abs(lh - rh) <= AndroidUtilities.dp(20)) { - spoilerEffect.spaces.add(r); - } - } - } spoilers.add(spoilerEffect); } @@ -637,7 +624,8 @@ private static void addSpoilersInternal(View v, Spanned spannable, Layout textLa */ public static void clipOutCanvas(Canvas canvas, List spoilers) { tempPath.rewind(); - for (SpoilerEffect eff : spoilers) { + for (int i = 0; i < spoilers.size(); i++) { + SpoilerEffect eff = spoilers.get(i); Rect b = eff.getBounds(); tempPath.addRect(b.left, b.top, b.right, b.bottom, Path.Direction.CW); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/spoilers/SpoilerEffectBitmapFactory.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/spoilers/SpoilerEffectBitmapFactory.java index 6a5ae2f155..7b6a3a4735 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/spoilers/SpoilerEffectBitmapFactory.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/spoilers/SpoilerEffectBitmapFactory.java @@ -7,6 +7,7 @@ import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Shader; +import android.os.Process; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.DispatchQueue; @@ -25,7 +26,7 @@ public static SpoilerEffectBitmapFactory getInstance() { return factory; } - final DispatchQueue dispatchQueue = new DispatchQueue("SpoilerEffectBitmapFactory"); + final DispatchQueue dispatchQueue = new DispatchQueue("SpoilerEffectBitmapFactory", true, 3 * Process.THREAD_PRIORITY_LESS_FAVORABLE); private Bitmap shaderBitmap; Bitmap bufferBitmap; Bitmap backgroundBitmap; @@ -38,17 +39,17 @@ public static SpoilerEffectBitmapFactory getInstance() { int size; private SpoilerEffectBitmapFactory() { - int maxSize = SharedConfig.getDevicePerformanceClass() == SharedConfig.PERFORMANCE_CLASS_HIGH ? AndroidUtilities.dp(200) : AndroidUtilities.dp(150); + int maxSize = SharedConfig.getDevicePerformanceClass() == SharedConfig.PERFORMANCE_CLASS_HIGH ? AndroidUtilities.dp(150) : AndroidUtilities.dp(100); size = (int) Math.min(Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.5f, maxSize); - if (size < AndroidUtilities.dp(100)) { - size = AndroidUtilities.dp(100); + if (size < AndroidUtilities.dp(80)) { + size = AndroidUtilities.dp(80); } } Paint getPaint() { if (shaderBitmap == null) { - shaderBitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); + shaderBitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ALPHA_8); shaderCanvas = new Canvas(shaderBitmap); shaderPaint = new Paint(); shaderSpoilerEffects = new ArrayList<>(10 * 10); @@ -88,10 +89,10 @@ public void checkUpdate() { dispatchQueue.postRunnable(() -> { Bitmap bitmap = bufferBitmapFinall; if (bitmap == null) { - bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); + bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ALPHA_8); } if (backgroundBitmap == null) { - backgroundBitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); + backgroundBitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ALPHA_8); } else { backgroundBitmap.eraseColor(Color.TRANSPARENT); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/spoilers/SpoilersTextView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/spoilers/SpoilersTextView.java index 5c2e1e3dec..82118d6796 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/spoilers/SpoilersTextView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/spoilers/SpoilersTextView.java @@ -10,10 +10,12 @@ import android.graphics.Region; import android.text.Layout; import android.text.Spanned; +import android.text.StaticLayout; import android.view.MotionEvent; import android.widget.TextView; import org.telegram.messenger.AndroidUtilities; +import org.telegram.ui.Cells.TextSelectionHelper; import java.util.ArrayList; import java.util.List; @@ -21,13 +23,14 @@ import tw.nekomimi.nekogram.NekoConfig; -public class SpoilersTextView extends TextView { +public class SpoilersTextView extends TextView implements TextSelectionHelper.SimpleSelectabeleView { private SpoilersClickDetector clickDetector; - private List spoilers = new ArrayList<>(); + protected List spoilers = new ArrayList<>(); private Stack spoilersPool = new Stack<>(); private boolean isSpoilersRevealed; private Path path = new Path(); private Paint xRefPaint; + public boolean allowClickSpoilers = true; public SpoilersTextView(Context context) { this(context, true); @@ -52,7 +55,7 @@ public SpoilersTextView(Context context, boolean revealOnClick) { @Override public boolean dispatchTouchEvent(MotionEvent event) { - if (clickDetector.onTouchEvent(event)) + if (allowClickSpoilers && clickDetector.onTouchEvent(event)) return true; return super.dispatchTouchEvent(event); } @@ -148,4 +151,9 @@ private void invalidateSpoilers() { } invalidate(); } + + @Override + public Layout getStaticTextLayout() { + return getLayout(); + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/CellFlickerDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/CellFlickerDrawable.java index 75d08fdf11..0374ea202b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/CellFlickerDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/CellFlickerDrawable.java @@ -96,33 +96,28 @@ public void draw(Canvas canvas, Path path, View view) { } private void update(View view) { - if (progress > 1f && !repeatEnabled) { - return; - } - if (view != null) { - view.invalidate(); - } - long currentTime = System.currentTimeMillis(); - if (lastUpdateTime != 0) { - long dt = currentTime - lastUpdateTime; - if (dt > 10) { - progress += (dt / 1200f) * animationSpeedScale; - if (progress > repeatProgress) { - progress = 0; - if (onRestartCallback != null) { - onRestartCallback.run(); + if (repeatEnabled || progress < 1) { + if (view != null) { + view.invalidate(); + } + long currentTime = System.currentTimeMillis(); + if (lastUpdateTime != 0) { + long dt = currentTime - lastUpdateTime; + if (dt > 10) { + progress += (dt / 1200f) * animationSpeedScale; + if (progress > repeatProgress) { + progress = 0; + if (onRestartCallback != null) { + onRestartCallback.run(); + } } + lastUpdateTime = currentTime; } + } else { lastUpdateTime = currentTime; } - } else { - lastUpdateTime = currentTime; } -// if (progress > 1f) { -// return; -// } - float x = (parentWidth + size * 2) * progress - size; matrix.reset(); matrix.setTranslate(x, 0); @@ -181,6 +176,11 @@ public void setOnRestartCallback(Runnable runnable) { onRestartCallback = runnable; } + public void setAlpha(int alpha) { + paint.setAlpha(alpha); + paintOutline.setAlpha(alpha); + } + public class DrawableInterface extends Drawable { public float radius; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/DarkTheme.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/DarkTheme.java deleted file mode 100644 index 3a6b7e7082..0000000000 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/DarkTheme.java +++ /dev/null @@ -1,508 +0,0 @@ -package org.telegram.ui.Components.voip; - -import android.content.Context; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffColorFilter; -import android.graphics.drawable.Drawable; - -import org.telegram.messenger.FileLog; -import org.telegram.ui.ActionBar.Theme; - -/** - * Created by grishka on 29.09.2017. - */ - -public class DarkTheme{ - - public static int getColor(String key){ - switch(key){ - case "avatar_subtitleInProfilePink": - return 0xFF8A8A8A; - case "chat_emojiPanelTrendingDescription": - return 0xFF717171; - case "chat_inFileBackground": - return 0xFF5D6F80; - case "chat_emojiPanelIconSelected": - return 0xFF5598DB; - case "actionBarActionModeDefaultSelector": - return 0x7A0F1923; - case "chats_menuItemIcon": - return 0xFF828282; - case "chat_inTimeText": - return 0xD98091A0; - case "windowBackgroundGray": - return 0xFF0D0D0D; - case "windowBackgroundWhiteGreenText2": - return 0xFF42C366; - case "chat_emojiPanelBackspace": - return 0xFF727272; - case "chat_inBubble": - return 0xFF253442; - case "chat_outFileInfoSelectedText": - return 0xFFFFFFFF; - case "chat_outLoaderSelected": - return 0xFFFFFFFF; - case "chat_emojiPanelIcon": - return 0xFF717171; - case "chat_selectedBackground": - return 0x4C0F99ED; - case "chats_pinnedIcon": - return 0xFF787878; - case "player_actionBarTitle": - return 0xFFE7E7E7; - case "chat_muteIcon": - return 0xFF7E7E7E; - case "chat_mediaMenu": - return 0xFFFFFFFF; - case "chat_addContact": - return 0xFF55A3DB; - case "chat_outMenu": - return 0xFF6D9ACE; - case "actionBarActionModeDefault": - return 0xFF253442; - case "chat_emojiPanelShadowLine": - return 0x0EFFFFFF; - case "dialogBackground": - return 0xFF212426; - case "chat_inPreviewInstantText": - return 0xFF55A2DB; - case "chat_outVoiceSeekbarSelected": - return 0xFFEBF3FF; - case "chat_outForwardedNameText": - return 0xFFD1EBFF; - case "chat_outFileProgressSelected": - return 0xFFFFFFFF; - case "player_progressBackground": - return 0x8A000000; - case "avatar_actionBarSelectorRed": - return 0xFF495154; - case "player_button": - return 0xFF868686; - case "chat_inVoiceSeekbar": - return 0xFF5D6F80; - case "switchThumb": - return 0xFF3C3C3C; - case "chats_tabletSelectedOverlay": - return 0x0FFFFFFF; - case "chats_menuItemText": - return 0xFFF0F0F0; - case "chat_outFileNameText": - return 0xFFD2EBFF; - case "divider": - return 0x17FFFFFF; - case "chat_outViews": - return 0xFF82B2DC; - case "avatar_actionBarSelectorBlue": - return 0xFF495054; - case "chats_actionMessage": - return 0xFF5491C6; - case "groupcreate_spanBackground": - return 0xFF282E33; - case "chat_messageTextIn": - return 0xFFFAFAFA; - case "chat_serviceBackgroundSelected": - return 0x60495154; - case "inappPlayerBackground": - return 0xD82B2B2B; - case "chat_topPanelLine": - return 0xFF5680A9; - case "chat_outFileInfoText": - return 0xFFAACFEE; - case "chat_unreadMessagesStartArrowIcon": - return 0xFF5A6B7A; - case "chat_outAudioProgress": - return 0xFF3873A4; - case "chat_outBubbleShadow": - return 0xFF000000; - case "chat_inMenuSelected": - return 0x82A9CFEE; - case "chat_inContactIcon": - return 0xFF253542; - case "chat_messageTextOut": - return 0xFFFAFAFA; - case "chat_outAudioTitleText": - return 0xFFD1EBFF; - case "inappPlayerPerformer": - return 0xFFFAFAFA; - case "actionBarActionModeDefaultTop": - return 0xA4000000; - case "avatar_subtitleInProfileCyan": - return 0xFF8A8A8A; - case "profile_actionBackground": - return 0xFF383E42; - case "chat_outSentClockSelected": - return 0xFFFFFFFF; - case "avatar_nameInMessageGreen": - return 0xFF6CB55B; - case "chat_outAudioSeekbarFill": - return 0xFFC4E1F7; - case "chat_inReplyNameText": - return 0xFF55A2DB; - case "chat_messagePanelIcons": - return 0xFF696969; - case "graySection": - return 0xFF222222; - case "avatar_backgroundActionBarViolet": - return 0xFF212426; - case "chat_outPreviewInstantText": - return 0xFFD1EBFF; - case "chat_emojiPanelTrendingTitle": - return 0xFFF4F4F4; - case "chat_inFileInfoSelectedText": - return 0xFFA9CFEE; - case "avatar_subtitleInProfileRed": - return 0xFF8A8A8A; - case "chat_outLocationIcon": - return 0xFF669ABF; - case "chat_inAudioPerfomerText": - return 0xFF798897; - case "chats_attachMessage": - return 0xFF5491C6; - case "chat_messageLinkIn": - return 0xFF56A3DB; - case "chats_unreadCounter": - return 0xFF2794DE; - case "windowBackgroundWhiteGrayText": - return 0xFF656565; - case "windowBackgroundWhiteGrayText3": - return 0xFF707070; - case "actionBarDefaultSubmenuBackground": - return 0xFB1E2022; - case "chat_outSentCheckSelected": - return 0xFFFFFFFF; - case "chat_outTimeSelectedText": - return 0xFFFFFFFF; - case "chats_secretIcon": - return 0xFF71D756; - case "dialogIcon": - return 0xFF7A848D; - case "chat_outAudioPerfomerText": - return 0xFF94C0E2; - case "chats_pinnedOverlay": - return 0x09FFFFFF; - case "chat_outContactIcon": - return 0xFFACCDFF; - case "windowBackgroundWhiteBlueHeader": - return 0xFF69ABF3; - case "actionBarDefaultSelector": - return 0xFF495154; - case "chat_emojiPanelEmptyText": - return 0xFF5D5D5D; - case "chat_inViews": - return 0xFF798997; - case "listSelector": - return 0x2E000000; - case "chat_messagePanelBackground": - return 0xFF1E1E1E; - case "chats_secretName": - return 0xFF71D756; - case "chat_inReplyLine": - return 0xFF54A2DB; - case "actionBarDefaultSubtitle": - return 0xFF8F8F8F; - case "switchThumbChecked": - return 0xFF3078A8; - case "chat_inReplyMessageText": - return 0xFFFFFFFF; - case "avatar_actionBarSelectorGreen": - return 0xFF495154; - case "chat_inAudioTitleText": - return 0xFF56A3DB; - case "chat_inAudioDurationSelectedText": - return 0xFFA9CFEE; - case "chat_outSentClock": - return 0xFF82B2DC; - case "actionBarDefault": - return 0xFF242728; - case "chat_goDownButton": - return 0xFF4D4D4D; - case "chat_inAudioSelectedProgress": - return 0xFF1C4163; - case "profile_actionPressedBackground": - return 0xFF495054; - case "chat_outContactPhoneText": - return 0xFFB6DFFF; - case "chat_inVenueInfoText": - return 0xFF5D6F80; - case "chat_outAudioDurationText": - return 0xFFD1EBFF; - case "windowBackgroundWhiteLinkText": - return 0xFF3D92D2; - case "chat_outSiteNameText": - return 0xFFD1EBFF; - case "chat_inBubbleSelected": - return 0xFF1C4063; - case "chats_date": - return 0xFF5E5E5E; - case "chat_outFileProgress": - return 0xFF72A5D0; - case "chat_outBubbleSelected": - return 0xFF2C83CB; - case "progressCircle": - return 0xFF364044; - case "chats_unreadCounterMuted": - return 0xFF444444; - case "stickers_menu": - return 0xFF4D5053; - case "chat_outAudioSeekbarSelected": - return 0xFFFFFFFF; - case "chat_inSiteNameText": - return 0xFF55A2DB; - case "chat_inFileProgressSelected": - return 0xFFA6CFEE; - case "chat_topPanelMessage": - return 0xFF6A6A6A; - case "chat_outVoiceSeekbar": - return 0xFF72A5D0; - case "chat_topPanelBackground": - return 0xFA1C1C1C; - case "chat_outVenueInfoSelectedText": - return 0xFFFFFFFF; - case "chats_menuTopShadow": - return 0xFF101010; - case "dialogTextBlack": - return 0xFFF9F9F9; - case "player_actionBarItems": - return 0xFFFFFFFF; - case "files_folderIcon": - return 0xFFA6A6A6; - case "chat_inReplyMediaMessageSelectedText": - return 0xFF6DA8DF; - case "chat_inViewsSelected": - return 0xFFA9CFEE; - case "chat_outAudioDurationSelectedText": - return 0xFFFFFFFF; - case "avatar_backgroundActionBarGreen": - return 0xFF212426; - case "profile_verifiedCheck": - return 0xFFFFFFFF; - case "chat_outViewsSelected": - return 0xFFFFFFFF; - case "switchTrackChecked": - return 0xFF164A72; - case "chat_serviceBackground": - return 0x6628323D; - case "windowBackgroundWhiteGrayText2": - return 0xFF797979; - case "profile_actionIcon": - return 0xFFFFFFFF; - case "chat_secretChatStatusText": - return 0xFF686868; - case "chat_emojiPanelBackground": - return 0xFF232323; - case "chat_inPreviewLine": - return 0xFF54A2DB; - case "chat_unreadMessagesStartBackground": - return 0xFF253442; - case "avatar_backgroundActionBarBlue": - return 0xFF212426; - case "chat_inViaBotNameText": - return 0xFF55A2DB; - case "avatar_actionBarSelectorCyan": - return 0xFF495154; - case "avatar_nameInMessageOrange": - return 0xFFDC8859; - case "windowBackgroundWhiteGrayText4": - return 0xFF6E6E6E; - case "files_folderIconBackground": - return 0xFF303030; - case "profile_verifiedBackground": - return 0xFF51CBF8; - case "chat_outFileBackground": - return 0xFF72A5D0; - case "chat_inLoaderPhoto": - return 0xFF243442; - case "dialogTextLink": - return 0xFF3984D1; - case "chat_inForwardedNameText": - return 0xFF55A2DB; - case "chat_inSentClock": - return 0xFF5D6F80; - case "chat_inAudioSeekbarSelected": - return 0xFFA9CFEE; - case "chats_name": - return 0xFFE6E6E6; - case "chats_nameMessage": - return 0xFF4D87B6; - case "key_chats_menuTopShadow": - return 0x000C0C0C; - case "windowBackgroundWhite": - return 0xFF171819; - case "chat_outBubble": - return 0xFF3872A4; - case "chats_menuBackground": - return 0xFF1D2023; - case "chat_messagePanelHint": - return 0xFF4C4C4C; - case "chat_replyPanelLine": - return 0xFF1D1D1D; - case "chat_inReplyMediaMessageText": - return 0xFF798897; - case "chat_outReplyMediaMessageText": - return 0xFFD1EBFF; - case "avatar_backgroundActionBarPink": - return 0xFF212426; - case "chat_outLoader": - return 0xFF8EBFE8; - case "chat_outReplyNameText": - return 0xFFD1EBFF; - case "avatar_subtitleInProfileViolet": - return 0xFF8A8A8A; - case "chat_outAudioSelectedProgress": - return 0xFF2782CB; - case "chat_inSentClockSelected": - return 0xFFA9D0EE; - case "chat_inBubbleShadow": - return 0xFF000000; - case "chat_inFileInfoText": - return 0xFF798997; - case "windowBackgroundWhiteGrayIcon": - return 0xFF828282; - case "chat_inAudioSeekbar": - return 0xFF516170; - case "chat_inContactPhoneText": - return 0xFF798897; - case "avatar_backgroundInProfileBlue": - return 0xFF549CDD; - case "chat_outInstantSelected": - return 0xFFFFFFFF; - case "chat_outAudioSeekbar": - return 0x9672A5D0; - case "windowBackgroundWhiteRedText5": - return 0xFFFF4C56; - case "avatar_actionBarSelectorViolet": - return 0xFF495154; - case "chats_menuPhone": - return 0x60FFFFFF; - case "chat_outVoiceSeekbarFill": - return 0xFFC4E1F7; - case "actionBarDefaultSubmenuItem": - return 0xFFF5F5F5; - case "chat_outPreviewLine": - return 0xFFD1EBFF; - case "chats_sentCheck": - return 0xFF5EA4E0; - case "chat_inMenu": - return 0x795C6F80; - case "chats_sentClock": - return 0xFF6082BD; - case "chat_messageLinkOut": - return 0xFFB6DEFF; - case "chat_unreadMessagesStartText": - return 0xDAFFFFFF; - case "inappPlayerClose": - return 0xFF585858; - case "chat_inAudioProgress": - return 0xFF253542; - case "chat_outFileBackgroundSelected": - return 0xFFFFFFFF; - case "chat_outInstant": - return 0xFFB6DFFF; - case "chat_outReplyMessageText": - return 0xFFFFFFFF; - case "chat_outContactBackground": - return 0xFF5985C2; - case "chat_inAudioDurationText": - return 0xFF7A8897; - case "listSelectorSDK21": - return 0x11FFFFFF; - case "chat_goDownButtonIcon": - return 0xFFE4E4E4; - case "windowBackgroundWhiteBlueText4": - return 0xFF4A8FCD; - case "chat_inContactNameText": - return 0xFF56A3DB; - case "chat_topPanelTitle": - return 0xFF55A3DB; - case "avatar_actionBarSelectorPink": - return 0xFF495154; - case "chat_outContactNameText": - return 0xFFD1EBFF; - case "player_actionBarSubtitle": - return 0xFF5F5F5F; - case "chat_wallpaper": - return 0xFF131617; - case "chat_emojiPanelStickerPackSelector": - return 0x0CFAFEFF; - case "chats_menuPhoneCats": - return 0xFF8E8E8E; - case "chat_reportSpam": - return 0xFFE96461; - case "avatar_subtitleInProfileGreen": - return 0xFF8A8A8A; - case "inappPlayerTitle": - return 0xFF9C9C9C; - case "chat_outViaBotNameText": - return 0xFFD1EBFF; - case "avatar_backgroundActionBarRed": - return 0xFF212426; - case "windowBackgroundWhiteValueText": - return 0xFF459DE1; - case "avatar_backgroundActionBarOrange": - return 0xFF212426; - case "chat_inFileBackgroundSelected": - return 0xFFFFFFFF; - case "avatar_actionBarSelectorOrange": - return 0xFF495154; - case "chat_inVenueInfoSelectedText": - return 0xFFA9CFEE; - case "actionBarActionModeDefaultIcon": - return 0xFFFFFFFF; - case "chats_message": - return 0xFF686868; - case "avatar_subtitleInProfileBlue": - return 0xFF8A8A8A; - case "chat_outVenueNameText": - return 0xFFD1EBFF; - case "emptyListPlaceholder": - return 0xFF515151; - case "chat_inFileProgress": - return 0xFF5D6F80; - case "chats_muteIcon": - return 0xFF5B5B5B; - case "groupcreate_spanText": - return 0xFFF5F5F5; - case "windowBackgroundWhiteBlackText": - return 0xFFF2F2F2; - case "windowBackgroundWhiteBlueText": - return 0xFF4295D9; - case "chat_outReplyMediaMessageSelectedText": - return 0xFFFFFFFF; - case "avatar_backgroundActionBarCyan": - return 0xFF212426; - case "chat_topPanelClose": - return 0xFF555555; - case "chat_outSentCheck": - return 0xFF97C3EA; - case "chat_outMenuSelected": - return 0xFFFFFFFF; - case "chat_messagePanelText": - return 0xFFEEEEEE; - case "chat_outReplyLine": - return 0xFFD1EBFF; - case "dialogBackgroundGray": - return 0xFF4B555D; - case "dialogButtonSelector": - return 0x14FFFFFF; - case "chat_outVenueInfoText": - return 0xFFB6DFFF; - case "chat_outTimeText": - return 0xD6A8CFEE; - case "chat_inTimeSelectedText": - return 0xFFAACFEE; - case "switchTrack": - return 0xFF2B2B2B; - case "avatar_subtitleInProfileOrange": - return 0xFF8A8A8A; - } - FileLog.w("returning color for key "+key+" from current theme"); - return Theme.getColor(key); - } - - public static Drawable getThemedDrawable(Context context, int resId, String key) { - Drawable drawable = context.getResources().getDrawable(resId).mutate(); - drawable.setColorFilter(new PorterDuffColorFilter(getColor(key), PorterDuff.Mode.SRC_IN)); - return drawable; - } - -} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/GroupCallMiniTextureView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/GroupCallMiniTextureView.java index 8e1a921645..e8e7a52d06 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/GroupCallMiniTextureView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/GroupCallMiniTextureView.java @@ -170,7 +170,7 @@ public GroupCallMiniTextureView(GroupCallRenderersContainer parentContainer, Arr super(parentContainer.getContext()); this.call = call; this.currentAccount = activity.getCurrentAccount(); - pausedVideoDrawable = new CrossOutDrawable(parentContainer.getContext(), R.drawable.calls_video, null); + pausedVideoDrawable = new CrossOutDrawable(parentContainer.getContext(), R.drawable.calls_video, -1); pausedVideoDrawable.setCrossOut(true, false); pausedVideoDrawable.setOffsets(-AndroidUtilities.dp(4), AndroidUtilities.dp(6), AndroidUtilities.dp(6)); pausedVideoDrawable.setStrokeWidth(AndroidUtilities.dpf2(3.4f)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/GroupCallRenderersContainer.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/GroupCallRenderersContainer.java index 4e16e3fe64..92a41c6406 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/GroupCallRenderersContainer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/GroupCallRenderersContainer.java @@ -32,6 +32,7 @@ import androidx.recyclerview.widget.RecyclerView; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.ChatObject; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MessageObject; @@ -78,7 +79,7 @@ public class GroupCallRenderersContainer extends FrameLayout { private LongSparseIntArray attachedPeerIds = new LongSparseIntArray(); - int animationIndex; + AnimationNotificationsLocker notificationsLocker = new AnimationNotificationsLocker(); public GroupCallMiniTextureView fullscreenTextureView; private GroupCallMiniTextureView outFullscreenTextureView; @@ -254,7 +255,7 @@ protected void dispatchDraw(Canvas canvas) { addView(pinContainer); - pinDrawable = new CrossOutDrawable(context, R.drawable.msg_pin_filled, null); + pinDrawable = new CrossOutDrawable(context, R.drawable.msg_pin_filled, -1); pinDrawable.setOffsets(-AndroidUtilities.dp(1), AndroidUtilities.dp(2), AndroidUtilities.dp(1)); pinButton.setImageDrawable(pinDrawable); pinButton.setPadding(AndroidUtilities.dp(16), 0, AndroidUtilities.dp(16), 0); @@ -956,11 +957,11 @@ public void onAnimationEnd(Animator animation) { textureViewFinal.animateToFullscreen = true; int currentAccount = groupCallActivity.getCurrentAccount(); swipedBack = swipeToBackGesture; - animationIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(animationIndex, null); + notificationsLocker.lock(); fullscreenAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - NotificationCenter.getInstance(currentAccount).onAnimationFinish(animationIndex); + notificationsLocker.unlock(); fullscreenAnimator = null; textureViewFinal.animateToFullscreen = false; if (!inFullscreenMode) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPHelper.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPHelper.java index 3b274472db..88f690fefb 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPHelper.java @@ -501,17 +501,17 @@ public static void showRateAlert(final Context context, final Runnable onDismiss alertView.addView(problemsWrap, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, -8, 0, -8, 0)); problemsWrap.setVisibility(View.GONE); - final EditTextBoldCursor commentBox = new EditTextBoldCursor(context); - commentBox.setHint(LocaleController.getString("VoipFeedbackCommentHint", R.string.VoipFeedbackCommentHint)); - commentBox.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES | InputType.TYPE_TEXT_FLAG_MULTI_LINE); - commentBox.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); - commentBox.setHintTextColor(Theme.getColor(Theme.key_dialogTextHint)); - commentBox.setBackground(null); - commentBox.setLineColors(Theme.getColor(Theme.key_dialogInputField), Theme.getColor(Theme.key_dialogInputFieldActivated), Theme.getColor(Theme.key_dialogTextRed)); - commentBox.setPadding(0, AndroidUtilities.dp(4), 0, AndroidUtilities.dp(4)); - commentBox.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); - commentBox.setVisibility(View.GONE); - alertView.addView(commentBox, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 8, 8, 8, 0)); + final EditTextBoldCursor commentBox = new EditTextBoldCursor(context); + commentBox.setHint(LocaleController.getString("VoipFeedbackCommentHint", R.string.VoipFeedbackCommentHint)); + commentBox.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES | InputType.TYPE_TEXT_FLAG_MULTI_LINE); + commentBox.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); + commentBox.setHintTextColor(Theme.getColor(Theme.key_dialogTextHint)); + commentBox.setBackground(null); + commentBox.setLineColors(Theme.getColor(Theme.key_dialogInputField), Theme.getColor(Theme.key_dialogInputFieldActivated), Theme.getColor(Theme.key_text_RedBold)); + commentBox.setPadding(0, AndroidUtilities.dp(4), 0, AndroidUtilities.dp(4)); + commentBox.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); + commentBox.setVisibility(View.GONE); + alertView.addView(commentBox, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 8, 8, 8, 0)); final boolean[] includeLogs = {true}; final CheckBoxCell checkbox = new CheckBoxCell(context, 1); @@ -586,46 +586,46 @@ public static void showRateAlert(final Context context, final Runnable onDismiss problemTags.add("#" + check.getTag()); } - if (req.rating < 5) { - req.comment = commentBox.getText().toString(); - } else { - req.comment = ""; - } - if (!problemTags.isEmpty() && !includeLogs[0]) { - req.comment += " " + TextUtils.join(" ", problemTags); - } - req.peer = new TLRPC.TL_inputPhoneCall(); - req.peer.access_hash = accessHash; - req.peer.id = callID; - req.user_initiative = userInitiative; - ConnectionsManager.getInstance(account).sendRequest(req, (response, error) -> { - if (response instanceof TLRPC.TL_updates) { - TLRPC.TL_updates updates = (TLRPC.TL_updates) response; - MessagesController.getInstance(currentAccount).processUpdates(updates, false); - } - if (includeLogs[0] && log.exists() && req.rating < 4) { - AccountInstance accountInstance = AccountInstance.getInstance(UserConfig.selectedAccount); - SendMessagesHelper.prepareSendingDocument(accountInstance, log.getAbsolutePath(), log.getAbsolutePath(), null, TextUtils.join(" ", problemTags), "text/plain", VOIP_SUPPORT_ID, null, null, null, null, true, 0); - Toast.makeText(context, LocaleController.getString("CallReportSent", R.string.CallReportSent), Toast.LENGTH_LONG).show(); - } - }); - alert.dismiss(); - } else { - page[0] = 1; - bar.setVisibility(View.GONE); - //text.setText(LocaleController.getString("CallReportHint", R.string.CallReportHint)); - text.setVisibility(View.GONE); - alert.setTitle(LocaleController.getString("CallReportHint", R.string.CallReportHint)); - commentBox.setVisibility(View.VISIBLE); - if (log.exists()) { - checkbox.setVisibility(View.VISIBLE); - logsText.setVisibility(View.VISIBLE); - } - problemsWrap.setVisibility(View.VISIBLE); - ((TextView) btn).setText(LocaleController.getString("Send", R.string.Send).toUpperCase()); - } - }); - } + if (req.rating < 5) { + req.comment = commentBox.getText().toString(); + } else { + req.comment = ""; + } + if (!problemTags.isEmpty() && !includeLogs[0]) { + req.comment += " " + TextUtils.join(" ", problemTags); + } + req.peer = new TLRPC.TL_inputPhoneCall(); + req.peer.access_hash = accessHash; + req.peer.id = callID; + req.user_initiative = userInitiative; + ConnectionsManager.getInstance(account).sendRequest(req, (response, error) -> { + if (response instanceof TLRPC.TL_updates) { + TLRPC.TL_updates updates = (TLRPC.TL_updates) response; + MessagesController.getInstance(currentAccount).processUpdates(updates, false); + } + if (includeLogs[0] && log.exists() && req.rating < 4) { + AccountInstance accountInstance = AccountInstance.getInstance(UserConfig.selectedAccount); + SendMessagesHelper.prepareSendingDocument(accountInstance, log.getAbsolutePath(), log.getAbsolutePath(), null, TextUtils.join(" ", problemTags), "text/plain", VOIP_SUPPORT_ID, null, null, null, null, true, 0, null); + Toast.makeText(context, LocaleController.getString("CallReportSent", R.string.CallReportSent), Toast.LENGTH_LONG).show(); + } + }); + alert.dismiss(); + } else { + page[0] = 1; + bar.setVisibility(View.GONE); + //text.setText(LocaleController.getString("CallReportHint", R.string.CallReportHint)); + text.setVisibility(View.GONE); + alert.setTitle(LocaleController.getString("CallReportHint", R.string.CallReportHint)); + commentBox.setVisibility(View.VISIBLE); + if (log.exists()) { + checkbox.setVisibility(View.VISIBLE); + logsText.setVisibility(View.VISIBLE); + } + problemsWrap.setVisibility(View.VISIBLE); + ((TextView) btn).setText(LocaleController.getString("Send", R.string.Send).toUpperCase()); + } + }); + } private static File getLogFile(long callID) { if (BuildVars.DEBUG_VERSION) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPWindowView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPWindowView.java index 1d3bc859f0..e40acd01c8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPWindowView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPWindowView.java @@ -15,6 +15,7 @@ import android.widget.FrameLayout; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.UserConfig; import org.telegram.ui.Components.CubicBezierInterpolator; @@ -26,7 +27,7 @@ public class VoIPWindowView extends FrameLayout { protected boolean lockOnScreen; private int orientationBefore; - private int animationIndex = -1; + private AnimationNotificationsLocker notificationsLocker = new AnimationNotificationsLocker(); VelocityTracker velocityTracker; @@ -137,11 +138,11 @@ public void finish(long animDuration) { } } else { int account = UserConfig.selectedAccount; - animationIndex = NotificationCenter.getInstance(account).setAnimationInProgress(animationIndex, null); + notificationsLocker.lock(); animate().translationX(getMeasuredWidth()).setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - NotificationCenter.getInstance(account).onAnimationFinish(animationIndex); + notificationsLocker.unlock(); if (getParent() != null) { activity.setRequestedOrientation(orientationBefore); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ContactAddActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ContactAddActivity.java index c1c41aab2c..e169552f52 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ContactAddActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ContactAddActivity.java @@ -182,6 +182,8 @@ public void onItemClick(int id) { TLRPC.User user = getMessagesController().getUser(user_id); user.first_name = firstNameField.getText().toString(); user.last_name = lastNameField.getText().toString(); + user.contact = true; + getMessagesController().putUser(user, false); getContactsController().addContact(user, checkBoxCell != null && checkBoxCell.isChecked()); SharedPreferences preferences = MessagesController.getNotificationsSettings(currentAccount); preferences.edit().putInt("dialog_bar_vis3" + user_id, 3).commit(); @@ -266,7 +268,7 @@ protected Theme.ResourcesProvider getResourcesProvider() { firstNameField.setHintTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteHintText, resourcesProvider)); firstNameField.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); firstNameField.setBackgroundDrawable(null); - firstNameField.setLineColors(getThemedColor(Theme.key_windowBackgroundWhiteInputField), getThemedColor(Theme.key_windowBackgroundWhiteInputFieldActivated), getThemedColor(Theme.key_windowBackgroundWhiteRedText3)); + firstNameField.setLineColors(getThemedColor(Theme.key_windowBackgroundWhiteInputField), getThemedColor(Theme.key_windowBackgroundWhiteInputFieldActivated), getThemedColor(Theme.key_text_RedRegular)); firstNameField.setMaxLines(1); firstNameField.setLines(1); firstNameField.setSingleLine(true); @@ -308,7 +310,7 @@ protected Theme.ResourcesProvider getResourcesProvider() { lastNameField.setHintTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteHintText, resourcesProvider)); lastNameField.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); lastNameField.setBackgroundDrawable(null); - lastNameField.setLineColors(getThemedColor(Theme.key_windowBackgroundWhiteInputField), getThemedColor(Theme.key_windowBackgroundWhiteInputFieldActivated), getThemedColor(Theme.key_windowBackgroundWhiteRedText3)); + lastNameField.setLineColors(getThemedColor(Theme.key_windowBackgroundWhiteInputField), getThemedColor(Theme.key_windowBackgroundWhiteInputFieldActivated), getThemedColor(Theme.key_text_RedRegular)); lastNameField.setMaxLines(1); lastNameField.setLines(1); lastNameField.setSingleLine(true); @@ -367,7 +369,6 @@ protected Theme.ResourcesProvider getResourcesProvider() { suggestPhoto.setBackgroundDrawable(Theme.getSelectorDrawable(false)); suggestPhoto.setColors(Theme.key_windowBackgroundWhiteBlueIcon, Theme.key_windowBackgroundWhiteBlueButton); RLottieDrawable suggestDrawable = new RLottieDrawable(R.raw.photo_suggest_icon, "" + R.raw.photo_suggest_icon, AndroidUtilities.dp(50), AndroidUtilities.dp(50), false, null); - suggestPhoto.imageView.setTranslationY(-AndroidUtilities.dp(9)); suggestPhoto.imageView.setTranslationX(-AndroidUtilities.dp(8)); suggestPhoto.imageView.setAnimation(suggestDrawable); suggestPhoto.setOnClickListener(v -> { @@ -396,7 +397,6 @@ protected Theme.ResourcesProvider getResourcesProvider() { setAvatarCell.setBackgroundDrawable(Theme.getSelectorDrawable(false)); setAvatarCell.setColors(Theme.key_windowBackgroundWhiteBlueIcon, Theme.key_windowBackgroundWhiteBlueButton); RLottieDrawable cameraDrawable = new RLottieDrawable(R.raw.camera_outline, "" + R.raw.camera_outline, AndroidUtilities.dp(50), AndroidUtilities.dp(50), false, null); - setAvatarCell.imageView.setTranslationY(-AndroidUtilities.dp(9)); setAvatarCell.imageView.setTranslationX(-AndroidUtilities.dp(8)); setAvatarCell.imageView.setAnimation(cameraDrawable); setAvatarCell.setOnClickListener(v -> { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ContactsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ContactsActivity.java index 46095d2d9d..43c53bcb3c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ContactsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ContactsActivity.java @@ -24,6 +24,7 @@ import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.res.Configuration; +import android.graphics.Canvas; import android.graphics.Outline; import android.graphics.Paint; import android.graphics.PorterDuff; @@ -52,6 +53,7 @@ import android.widget.ImageView; import androidx.collection.LongSparseArray; +import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -65,6 +67,7 @@ import org.telegram.messenger.MessagesController; import org.telegram.messenger.MessagesStorage; import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.NotificationsController; import org.telegram.messenger.R; import org.telegram.messenger.SecretChatHelper; import org.telegram.messenger.SharedConfig; @@ -87,17 +90,21 @@ import org.telegram.ui.Cells.TextCell; import org.telegram.ui.Cells.UserCell; import org.telegram.ui.Components.AlertsCreator; +import org.telegram.ui.Components.Bulletin; import org.telegram.ui.Components.BulletinFactory; import org.telegram.ui.Components.CombinedDrawable; import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.EditTextBoldCursor; import org.telegram.ui.Components.FlickerLoadingView; +import org.telegram.ui.Components.ItemOptions; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.RLottieImageView; import org.telegram.ui.Components.RecyclerListView; import org.telegram.ui.Components.StickerEmptyView; +import org.telegram.ui.Stories.StoriesListPlaceProvider; import java.util.ArrayList; +import java.util.Arrays; public class ContactsActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { @@ -166,6 +173,7 @@ public ContactsActivity(Bundle args) { public boolean onFragmentCreate() { super.onFragmentCreate(); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.contactsDidLoad); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.storiesUpdated); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.updateInterfaces); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.encryptedChatCreated); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.closeChats); @@ -195,6 +203,7 @@ public boolean onFragmentCreate() { getContactsController().checkInviteText(); getContactsController().reloadContactsStatusesMaybe(); + MessagesController.getInstance(currentAccount).getStoriesController().loadHiddenStories(); return true; @@ -204,6 +213,7 @@ public boolean onFragmentCreate() { public void onFragmentDestroy() { super.onFragmentDestroy(); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.contactsDidLoad); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.storiesUpdated); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.updateInterfaces); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.encryptedChatCreated); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.closeChats); @@ -278,7 +288,8 @@ public void onSearchCollapse() { listViewAdapter.notifyDataSetChanged(); listView.setFastScrollVisible(true); listView.setVerticalScrollBarEnabled(false); - // emptyView.setText(LocaleController.getString("NoContacts", R.string.NoContacts)); + listView.getFastScroll().topOffset = AndroidUtilities.dp(90); + // emptyView.setText(LocaleController.getString("NoContacts", R.string.NoContacts)); if (floatingButtonContainer != null) { floatingButtonContainer.setVisibility(View.VISIBLE); floatingHidden = true; @@ -330,6 +341,7 @@ protected void onSearchProgressChanged() { } showItemsAnimated(); } + }; int inviteViaLink; if (chatId != 0) { @@ -346,14 +358,14 @@ protected void onSearchProgressChanged() { } catch (Throwable e) { hasGps = false; } - listViewAdapter = new ContactsAdapter(context, onlyUsers ? 1 : 0, needPhonebook, ignoreUsers, inviteViaLink, hasGps) { + listViewAdapter = new ContactsAdapter(context, this, onlyUsers ? 1 : 0, needPhonebook, ignoreUsers, inviteViaLink, hasGps) { @Override public void notifyDataSetChanged() { super.notifyDataSetChanged(); if (listView != null && listView.getAdapter() == this) { int count = super.getItemCount(); if (needPhonebook) { - // emptyView.setVisibility(count == 2 ? View.VISIBLE : View.GONE); + // emptyView.setVisibility(count == 2 ? View.VISIBLE : View.GONE); listView.setFastScrollVisible(count != 2); } else { //emptyView.setVisibility(count == 0 ? View.VISIBLE : View.GONE); @@ -366,6 +378,26 @@ public void notifyDataSetChanged() { listViewAdapter.setDisableSections(disableSections); fragmentView = new FrameLayout(context) { + + Paint actionBarPaint = new Paint(); + + @Override + protected void dispatchDraw(Canvas canvas) { + actionBarPaint.setColor(Theme.getColor(Theme.key_actionBarDefault)); + float actionBarBottom = actionBar.getMeasuredHeight(); + canvas.drawRect(0, 0, getMeasuredWidth(), actionBar.getMeasuredHeight(), actionBarPaint); + parentLayout.drawHeaderShadow(canvas, (int) actionBarBottom); + super.dispatchDraw(canvas); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + measureChildWithMargins(actionBar, widthMeasureSpec, 0, heightMeasureSpec, 0); + ((MarginLayoutParams) emptyView.getLayoutParams()).topMargin = actionBar.getMeasuredHeight(); + ((MarginLayoutParams) listView.getLayoutParams()).topMargin = actionBar.getMeasuredHeight(); + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); @@ -401,6 +433,11 @@ public void setPadding(int left, int top, int right, int bottom) { } } }; + DefaultItemAnimator defaultItemAnimator = new DefaultItemAnimator(); + defaultItemAnimator.setDelayAnimations(false); + defaultItemAnimator.setDurations(150); + defaultItemAnimator.setSupportsChangeAnimations(false); + listView.setItemAnimator(defaultItemAnimator); listView.setSectionsType(RecyclerListView.SECTIONS_TYPE_STICKY_HEADERS); listView.setVerticalScrollBarEnabled(false); listView.setFastScrollEnabled(RecyclerListView.FastScroll.LETTER_TYPE); @@ -411,7 +448,7 @@ public void setPadding(int left, int top, int right, int bottom) { listView.setEmptyView(emptyView); listView.setAnimateEmptyView(true, RecyclerListView.EMPTY_VIEW_ANIMATION_TYPE_ALPHA); - listView.setOnItemClickListener((view, position) -> { + listView.setOnItemClickListener((view, position, x, y) -> { if (listView.getAdapter() == searchListViewAdapter) { Object object = searchListViewAdapter.getItem(position); if (object instanceof TLRPC.User) { @@ -456,9 +493,21 @@ public void setPadding(int left, int top, int right, int bottom) { } else { int section = listViewAdapter.getSectionForPosition(position); int row = listViewAdapter.getPositionInSectionForPosition(position); + if (row < 0 || section < 0) { return; } + if (listViewAdapter.hasStories && section == 1) { + if (!(view instanceof UserCell)) { + return; + } + UserCell userCell = (UserCell) view; + long dialogId = userCell.getDialogId(); + getOrCreateStoryViewer().open(getContext(), dialogId, StoriesListPlaceProvider.of(listView)); + return; + } else if (listViewAdapter.hasStories && section > 1) { + section--; + } if ((!onlyUsers || inviteViaLink != 0) && section == 0) { if (needPhonebook) { if (row == 0) { @@ -520,6 +569,8 @@ public void setPadding(int left, int top, int right, int bottom) { } } } else { + section = listViewAdapter.getSectionForPosition(position); + row = listViewAdapter.getPositionInSectionForPosition(position); Object item1 = listViewAdapter.getItem(section, row); if (item1 instanceof TLRPC.User) { @@ -569,6 +620,92 @@ public void setPadding(int left, int top, int right, int bottom) { } } }); + listView.setOnItemLongClickListener(new RecyclerListView.OnItemLongClickListener() { + @Override + public boolean onItemClick(View view, int position) { + int section = listViewAdapter.getSectionForPosition(position); + int row = listViewAdapter.getPositionInSectionForPosition(position); + if (Bulletin.getVisibleBulletin() != null) { + Bulletin.getVisibleBulletin().hide(); + } + if (row < 0 || section < 0) { + return false; + } + if (listViewAdapter.hasStories && section == 1 && view instanceof UserCell) { + UserCell userCell = (UserCell) view; + long dialogId = userCell.getDialogId(); + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); + final String key = NotificationsController.getSharedPrefKey(dialogId, 0); + boolean muted = !NotificationsCustomSettingsActivity.areStoriesNotMuted(currentAccount, dialogId); + ItemOptions filterOptions = ItemOptions.makeOptions(ContactsActivity.this, view) + //.setViewAdditionalOffsets(0, AndroidUtilities.dp(8), 0, 0) + .setScrimViewBackground(Theme.createRoundRectDrawable(0, 0, Theme.getColor(Theme.key_windowBackgroundWhite))) + .add(R.drawable.msg_discussion, LocaleController.getString("SendMessage", R.string.SendMessage), () -> { + presentFragment(ChatActivity.of(dialogId)); + }) + .add(R.drawable.msg_openprofile, LocaleController.getString("OpenProfile", R.string.OpenProfile), () -> { + presentFragment(ProfileActivity.of(dialogId)); + }) + .addIf(!muted, R.drawable.msg_mute, LocaleController.getString("NotificationsStoryMute", R.string.NotificationsStoryMute), () -> { + MessagesController.getNotificationsSettings(currentAccount).edit().putBoolean("stories_" + key, false).apply(); + getNotificationsController().updateServerNotificationsSettings(dialogId, 0); + String name = user == null ? "" : user.first_name.trim(); + int index = name.indexOf(" "); + if (index > 0) { + name = name.substring(0, index); + } + BulletinFactory.of(ContactsActivity.this).createUsersBulletin(Arrays.asList(user), AndroidUtilities.replaceTags(LocaleController.formatString("NotificationsStoryMutedHint", R.string.NotificationsStoryMutedHint, name))).show(); + }) + .addIf(muted, R.drawable.msg_unmute, LocaleController.getString("NotificationsStoryUnmute", R.string.NotificationsStoryUnmute), () -> { + MessagesController.getNotificationsSettings(currentAccount).edit().putBoolean("stories_" + key, true).apply(); + getNotificationsController().updateServerNotificationsSettings(dialogId, 0); + String name = user == null ? "" : user.first_name.trim(); + int index = name.indexOf(" "); + if (index > 0) { + name = name.substring(0, index); + } + BulletinFactory.of(ContactsActivity.this).createUsersBulletin(Arrays.asList(user), AndroidUtilities.replaceTags(LocaleController.formatString("NotificationsStoryUnmutedHint", R.string.NotificationsStoryUnmutedHint, name))).show(); + }); + // if (user.stories_hidden) { + filterOptions.add(R.drawable.msg_viewintopic, LocaleController.getString("ShowInChats", R.string.ShowInChats), () -> { + // listViewAdapter.removeStory(dialogId); + getMessagesController().getStoriesController().toggleHidden(dialogId, false, false, true); + BulletinFactory.UndoObject undoObject = new BulletinFactory.UndoObject(); + undoObject.onUndo = () -> { + getMessagesController().getStoriesController().toggleHidden(dialogId, true, false, true); + }; + undoObject.onAction = () -> { + getMessagesController().getStoriesController().toggleHidden(dialogId, false, true, true); + }; + BulletinFactory.global().createUsersBulletin( + Arrays.asList(user), + AndroidUtilities.replaceTags(LocaleController.formatString("StoriesMovedToDialogs", R.string.StoriesMovedToDialogs, ContactsController.formatName(user.first_name, null, 20))), + null, + undoObject + ).show(); + + }); +// } else { +// filterOptions.add(R.drawable.msg_cancel, LocaleController.getString("Hide", R.string.Hide), () -> { +// BulletinFactory.global().createUndoBulletin( +// AndroidUtilities.replaceTags(LocaleController.formatString("StoriesMovedToContacts", R.string.StoriesMovedToContacts, user.first_name)), +// () -> { +// //undo +// getMessagesController().getStoriesController().toggleHidden(dialogId, false, false, true); +// }, () -> { +// //action +// getMessagesController().getStoriesController().toggleHidden(dialogId, true, true, true); +// }).show(); +// }); +// } + + filterOptions.setGravity(Gravity.RIGHT) + .show(); + return true; + } + return false; + } + }); listView.setOnScrollListener(new RecyclerView.OnScrollListener() { @@ -644,7 +781,11 @@ public void dismissInternal() { floatingButton.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_chats_actionIcon), PorterDuff.Mode.SRC_IN)); SharedPreferences preferences = MessagesController.getGlobalMainSettings(); boolean configAnimationsEnabled = preferences.getBoolean("view_animations", true); - floatingButton.setAnimation(configAnimationsEnabled ? R.raw.write_contacts_fab_icon : R.raw.write_contacts_fab_icon_reverse, 52, 52); + if (getMessagesController().storiesEnabled()) { + floatingButton.setAnimation(configAnimationsEnabled ? R.raw.write_contacts_fab_icon_camera : R.raw.write_contacts_fab_icon_reverse_camera, 56, 56); + } else { + floatingButton.setAnimation(configAnimationsEnabled ? R.raw.write_contacts_fab_icon : R.raw.write_contacts_fab_icon_reverse, 52, 52); + } floatingButtonContainer.setContentDescription(LocaleController.getString("CreateNewContact", R.string.CreateNewContact)); if (Build.VERSION.SDK_INT >= 21) { StateListAnimator animator = new StateListAnimator(); @@ -667,9 +808,21 @@ public void getOutline(View view, Outline outline) { initialSearchString = null; } + ((FrameLayout) fragmentView).addView(actionBar); + + listViewAdapter.setStories(getMessagesController().storiesController.getHiddenList(), false); + return fragmentView; } + @Override + public ActionBar createActionBar(Context context) { + ActionBar actionBar = super.createActionBar(context); + actionBar.setBackground(null); + actionBar.setAddToContainer(false); + return actionBar; + } + private void didSelectResult(final TLRPC.User user, boolean useAlert, String param) { if (useAlert && selectAlertString != null) { if (getParentActivity() == null) { @@ -913,7 +1066,10 @@ public void onPause() { @Override public void didReceivedNotification(int id, int account, Object... args) { - if (id == NotificationCenter.contactsDidLoad) { + if (id == NotificationCenter.storiesUpdated) { + listViewAdapter.setStories(getMessagesController().getStoriesController().getHiddenList(), true); + MessagesController.getInstance(currentAccount).getStoriesController().loadHiddenStories(); + } else if (id == NotificationCenter.contactsDidLoad) { if (listViewAdapter != null) { if (!sortByName) { listViewAdapter.setSortType(2, true); @@ -926,7 +1082,7 @@ public void didReceivedNotification(int id, int account, Object... args) { updateVisibleRows(mask); } if ((mask & MessagesController.UPDATE_MASK_STATUS) != 0 && !sortByName && listViewAdapter != null) { - listViewAdapter.sortOnlineContacts(); + scheduleSort(); } } else if (id == NotificationCenter.encryptedChatCreated) { if (createSecretChat && creatingChat) { @@ -943,6 +1099,23 @@ public void didReceivedNotification(int id, int account, Object... args) { } } + boolean scheduled; + Runnable sortContactsRunnable = new Runnable() { + @Override + public void run() { + listViewAdapter.sortOnlineContacts(); + scheduled = false; + } + }; + + private void scheduleSort() { + if (!scheduled) { + scheduled = true; + AndroidUtilities.cancelRunOnUIThread(sortContactsRunnable); + AndroidUtilities.runOnUIThread(sortContactsRunnable, 5000); + } + } + private void updateVisibleRows(int mask) { if (listView != null) { int count = listView.getChildCount(); @@ -961,7 +1134,7 @@ private void hideFloatingButton(boolean hide) { } floatingHidden = hide; AnimatorSet animatorSet = new AnimatorSet(); - animatorSet.playTogether(ObjectAnimator.ofFloat(floatingButtonContainer, View.TRANSLATION_Y, (floatingHidden ? AndroidUtilities.dp(100) : 0))); + animatorSet.playTogether(ObjectAnimator.ofFloat(floatingButtonContainer, View.TRANSLATION_Y, (floatingHidden ? AndroidUtilities.dp(100) : 0))); animatorSet.setDuration(300); animatorSet.setInterpolator(floatingInterpolator); floatingButtonContainer.setClickable(!hide); @@ -1020,10 +1193,15 @@ public AnimatorSet onCustomTransitionAnimation(boolean isOpen, Runnable callback if (dialogsActivity == null) { return null; } + final boolean stories = dialogsActivity.storiesEnabled; RLottieImageView previousFab = dialogsActivity.getFloatingButton(); View previousFabContainer = previousFab.getParent() != null ? (View) previousFab.getParent() : null; if (floatingButtonContainer == null || previousFabContainer == null || previousFab.getVisibility() != View.VISIBLE || Math.abs(previousFabContainer.getTranslationY()) > AndroidUtilities.dp(4) || Math.abs(floatingButtonContainer.getTranslationY()) > AndroidUtilities.dp(4)) { - floatingButton.setAnimation(R.raw.write_contacts_fab_icon, 52, 52); + if (stories) { + floatingButton.setAnimation(R.raw.write_contacts_fab_icon_camera, 56, 56); + } else { + floatingButton.setAnimation(R.raw.write_contacts_fab_icon, 52, 52); + } floatingButton.getAnimatedDrawable().setCurrentFrame(floatingButton.getAnimatedDrawable().getFramesCount() - 1); return null; } @@ -1057,7 +1235,11 @@ public void onAnimationEnd(Animator animation) { previousFabContainer.setVisibility(View.VISIBLE); if (!isOpen) { - previousFab.setAnimation(R.raw.write_contacts_fab_icon_reverse, 52, 52); + if (stories) { + previousFab.setAnimation(R.raw.write_contacts_fab_icon_reverse_camera, 56, 56); + } else { + previousFab.setAnimation(R.raw.write_contacts_fab_icon_reverse, 52, 52); + } previousFab.getAnimatedDrawable().setCurrentFrame(floatingButton.getAnimatedDrawable().getCurrentFrame()); previousFab.playAnimation(); } @@ -1067,15 +1249,22 @@ public void onAnimationEnd(Animator animation) { }); animatorSet.playTogether(valueAnimator); AndroidUtilities.runOnUIThread(() -> { - animationIndex = getNotificationCenter().setAnimationInProgress(animationIndex, new int[] {NotificationCenter.diceStickersDidLoad}, false); + animationIndex = getNotificationCenter().setAnimationInProgress(animationIndex, new int[]{NotificationCenter.diceStickersDidLoad}, false); animatorSet.start(); if (isOpen) { - floatingButton.setAnimation(R.raw.write_contacts_fab_icon, 52, 52); - floatingButton.playAnimation(); + if (stories) { + floatingButton.setAnimation(R.raw.write_contacts_fab_icon_camera, 56, 56); + } else { + floatingButton.setAnimation(R.raw.write_contacts_fab_icon, 52, 52); + } } else { - floatingButton.setAnimation(R.raw.write_contacts_fab_icon_reverse, 52, 52); - floatingButton.playAnimation(); + if (stories) { + floatingButton.setAnimation(R.raw.write_contacts_fab_icon_reverse_camera, 56, 56); + } else { + floatingButton.setAnimation(R.raw.write_contacts_fab_icon_reverse, 52, 52); + } } + floatingButton.playAnimation(); if (bounceIconAnimator != null) { bounceIconAnimator.cancel(); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ContentPreviewViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/ContentPreviewViewer.java index a5c543713c..01d98ac5eb 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ContentPreviewViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ContentPreviewViewer.java @@ -138,7 +138,7 @@ default Boolean canSetAsStatus(TLRPC.Document document) { } default void setAsEmojiStatus(TLRPC.Document document, Integer until) {} - default boolean needCopy() { + default boolean needCopy(TLRPC.Document document) { return false; } default void copyEmoji(TLRPC.Document document) {} @@ -393,7 +393,7 @@ public void dismiss() { actions.add(2); } } - if (delegate.needCopy()) { + if (delegate.needCopy(currentDocument)) { items.add(LocaleController.getString("CopyEmojiPreview", R.string.CopyEmojiPreview)); icons.add(R.drawable.msg_copy); actions.add(3); @@ -403,6 +403,12 @@ public void dismiss() { icons.add(R.drawable.msg_delete); actions.add(4); } + final boolean inFavs = MediaDataController.getInstance(currentAccount).isStickerInFavorites(currentDocument); + if (!MessageObject.isAnimatedEmoji(currentDocument) && !MessageObject.isMaskDocument(currentDocument) && (inFavs || MediaDataController.getInstance(currentAccount).canAddStickerToFavorites() && MessageObject.isStickerHasSet(currentDocument))) { + items.add(inFavs ? LocaleController.getString("DeleteFromFavorites", R.string.DeleteFromFavorites) : LocaleController.getString("AddToFavorites", R.string.AddToFavorites)); + icons.add(inFavs ? R.drawable.msg_unfave : R.drawable.msg_fave); + actions.add(5); + } if (items.isEmpty()) { return; } @@ -430,6 +436,8 @@ public void dismiss() { delegate.copyEmoji(currentDocument); } else if (action == 4) { delegate.removeFromRecent(currentDocument); + } else if (action == 5) { + MediaDataController.getInstance(currentAccount).addRecentSticker(MediaDataController.TYPE_FAVE, parentObject, currentDocument, (int) (System.currentTimeMillis() / 1000), inFavs); } if (popupWindow != null) { popupWindow.dismiss(); @@ -439,8 +447,8 @@ public void dismiss() { for (int i = 0; i < items.size(); i++) { ActionBarMenuSubItem item = ActionBarMenuItem.addItem(i == 0, i == items.size() - 1, previewMenu, icons.get(i), items.get(i), false, resourcesProvider); if (actions.get(i) == 4) { - item.setIconColor(getThemedColor(Theme.key_dialogRedIcon)); - item.setTextColor(getThemedColor(Theme.key_dialogTextRed)); + item.setIconColor(getThemedColor(Theme.key_text_RedRegular)); + item.setTextColor(getThemedColor(Theme.key_text_RedBold)); } item.setTag(i); item.setOnClickListener(onItemClickListener); @@ -582,7 +590,7 @@ public void dismiss() { item.setOnClickListener(onItemClickListener); if (canDelete && i == items.size() - 1) { - item.setColors(getThemedColor(Theme.key_dialogTextRed), getThemedColor(Theme.key_dialogRedIcon)); + item.setColors(getThemedColor(Theme.key_text_RedBold), getThemedColor(Theme.key_text_RedRegular)); } } popupWindow = new ActionBarPopupWindow(previewMenu, LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT) { @@ -719,7 +727,6 @@ public void reset() { public boolean onTouch(MotionEvent event, final RecyclerListView listView, final int height, final Object listener, ContentPreviewViewerDelegate contentPreviewViewerDelegate, Theme.ResourcesProvider resourcesProvider) { delegate = contentPreviewViewerDelegate; - this.resourcesProvider = resourcesProvider; if (delegate != null && !delegate.can()) { return false; } @@ -839,7 +846,7 @@ public boolean onTouch(MotionEvent event, final RecyclerListView listView, final AndroidUtilities.updateViewVisibilityAnimated(unlockPremiumView, false); if (currentPreviewCell instanceof StickerEmojiCell) { StickerEmojiCell stickerEmojiCell = (StickerEmojiCell) currentPreviewCell; - open(stickerEmojiCell.getSticker(), stickerEmojiCell.getStickerPath(), stickerEmojiCell.getEmoji(), delegate != null ? delegate.getQuery(false) : null, null, contentType, stickerEmojiCell.isRecent(), stickerEmojiCell.getParentObject(), resourcesProvider); + open(stickerEmojiCell.getSticker(), stickerEmojiCell.getStickerPath(), MessageObject.findAnimatedEmojiEmoticon(stickerEmojiCell.getSticker(), null, currentAccount), delegate != null ? delegate.getQuery(false) : null, null, contentType, stickerEmojiCell.isRecent(), stickerEmojiCell.getParentObject(), resourcesProvider); stickerEmojiCell.setScaled(true); } else if (currentPreviewCell instanceof StickerCell) { StickerCell stickerCell = (StickerCell) currentPreviewCell; @@ -856,7 +863,7 @@ public boolean onTouch(MotionEvent event, final RecyclerListView listView, final EmojiPacksAlert.EmojiImageView imageView = (EmojiPacksAlert.EmojiImageView) currentPreviewCell; TLRPC.Document document = imageView.getDocument(); if (document != null) { - open(document, null, MessageObject.findAnimatedEmojiEmoticon(document, null), null, null, contentType, false, null, resourcesProvider); + open(document, null, MessageObject.findAnimatedEmojiEmoticon(document, null, currentAccount), null, null, contentType, false, null, resourcesProvider); } } else if (currentPreviewCell instanceof EmojiView.ImageViewEmoji) { EmojiView.ImageViewEmoji imageView = (EmojiView.ImageViewEmoji) currentPreviewCell; @@ -869,7 +876,7 @@ public boolean onTouch(MotionEvent event, final RecyclerListView listView, final } } if (document != null) { - open(document, null, MessageObject.findAnimatedEmojiEmoticon(document, null), null, null, contentType, false, null, resourcesProvider); + open(document, null, MessageObject.findAnimatedEmojiEmoticon(document, null, currentAccount), null, null, contentType, false, null, resourcesProvider); } else { return false; } @@ -883,7 +890,7 @@ public boolean onTouch(MotionEvent event, final RecyclerListView listView, final if (document == null) { return false; } - open(document, null, MessageObject.findAnimatedEmojiEmoticon(document, null), null, null, contentType, false, null, resourcesProvider); + open(document, null, MessageObject.findAnimatedEmojiEmoticon(document, null, currentAccount), null, null, contentType, false, null, resourcesProvider); } runSmoothHaptic(); @@ -921,7 +928,6 @@ protected void runSmoothHaptic() { public boolean onInterceptTouchEvent(MotionEvent event, final RecyclerListView listView, final int height, ContentPreviewViewerDelegate contentPreviewViewerDelegate, Theme.ResourcesProvider resourcesProvider) { delegate = contentPreviewViewerDelegate; - this.resourcesProvider = resourcesProvider; if (delegate != null && !delegate.can()) { return false; } @@ -1000,7 +1006,7 @@ public boolean onInterceptTouchEvent(MotionEvent event, final RecyclerListView l clearsInputField = false; if (currentPreviewCell instanceof StickerEmojiCell) { StickerEmojiCell stickerEmojiCell = (StickerEmojiCell) currentPreviewCell; - open(stickerEmojiCell.getSticker(), stickerEmojiCell.getStickerPath(), stickerEmojiCell.getEmoji(), delegate != null ? delegate.getQuery(false) : null, null, contentTypeFinal, stickerEmojiCell.isRecent(), stickerEmojiCell.getParentObject(), resourcesProvider); + open(stickerEmojiCell.getSticker(), stickerEmojiCell.getStickerPath(), MessageObject.findAnimatedEmojiEmoticon(stickerEmojiCell.getSticker(), null, currentAccount), delegate != null ? delegate.getQuery(false) : null, null, contentTypeFinal, stickerEmojiCell.isRecent(), stickerEmojiCell.getParentObject(), this.resourcesProvider); opened = true; stickerEmojiCell.setScaled(true); } else if (currentPreviewCell instanceof StickerCell) { @@ -1020,7 +1026,7 @@ public boolean onInterceptTouchEvent(MotionEvent event, final RecyclerListView l EmojiPacksAlert.EmojiImageView imageView = (EmojiPacksAlert.EmojiImageView) currentPreviewCell; TLRPC.Document document = imageView.getDocument(); if (document != null) { - open(document, null, MessageObject.findAnimatedEmojiEmoticon(document, null), null, null, contentTypeFinal, false, null, resourcesProvider); + open(document, null, MessageObject.findAnimatedEmojiEmoticon(document, null, currentAccount), null, null, contentTypeFinal, false, null, resourcesProvider); opened = true; } } else if (currentPreviewCell instanceof EmojiView.ImageViewEmoji) { @@ -1034,7 +1040,7 @@ public boolean onInterceptTouchEvent(MotionEvent event, final RecyclerListView l } } if (document != null) { - open(document, null, MessageObject.findAnimatedEmojiEmoticon(document, null), null, null, contentTypeFinal, false, null, resourcesProvider); + open(document, null, MessageObject.findAnimatedEmojiEmoticon(document, null, currentAccount), null, null, contentTypeFinal, false, null, resourcesProvider); opened = true; } } else if (currentPreviewCell instanceof SuggestEmojiView.EmojiImageView) { @@ -1045,7 +1051,7 @@ public boolean onInterceptTouchEvent(MotionEvent event, final RecyclerListView l document = ((AnimatedEmojiDrawable) drawable).getDocument(); } if (document != null) { - open(document, null, MessageObject.findAnimatedEmojiEmoticon(document, null), null, null, contentTypeFinal, false, null, resourcesProvider); + open(document, null, MessageObject.findAnimatedEmojiEmoticon(document, null, currentAccount), null, null, contentTypeFinal, false, null, resourcesProvider); opened = true; } } @@ -1151,7 +1157,8 @@ public void open(TLRPC.Document document, SendMessagesHelper.ImportingSticker st this.resourcesProvider = resourcesProvider; isRecentSticker = isRecent; stickerEmojiLayout = null; - backgroundDrawable.setColor(Theme.getActiveTheme().isDark() ? 0x71000000 : 0x64E6E6E6); + boolean isDark = AndroidUtilities.isDarkColor(Theme.getColor(Theme.key_windowBackgroundWhite, resourcesProvider)); + backgroundDrawable.setColor(isDark ? 0x71000000 : 0x64E6E6E6); drawEffect = false; centerImage.setColorFilter(null); if (contentType == CONTENT_TYPE_STICKER || contentType == CONTENT_TYPE_EMOJI) { @@ -1176,7 +1183,7 @@ public void open(TLRPC.Document document, SendMessagesHelper.ImportingSticker st } if (contentType == CONTENT_TYPE_EMOJI && emojiPath != null) { CharSequence emoji = Emoji.replaceEmoji(emojiPath, textPaint.getFontMetricsInt(), AndroidUtilities.dp(24), false); - stickerEmojiLayout = new StaticLayout(emoji, textPaint, AndroidUtilities.dp(100), Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false); + stickerEmojiLayout = new StaticLayout(emoji, textPaint, AndroidUtilities.dp(500), Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false); } if ((newSet != null || contentType == CONTENT_TYPE_EMOJI) && (delegate == null || delegate.needMenu())) { AndroidUtilities.cancelRunOnUIThread(showSheetRunnable); @@ -1194,15 +1201,17 @@ public void open(TLRPC.Document document, SendMessagesHelper.ImportingSticker st } } if (MessageObject.isTextColorEmoji(document)) { - centerImage.setColorFilter(Theme.chat_animatedEmojiTextColorFilter); + centerImage.setColorFilter(Theme.getAnimatedEmojiColorFilter(resourcesProvider)); } - for (int a = 0; a < document.attributes.size(); a++) { - TLRPC.DocumentAttribute attribute = document.attributes.get(a); - if (attribute instanceof TLRPC.TL_documentAttributeSticker) { - if (!TextUtils.isEmpty(attribute.alt)) { - CharSequence emoji = Emoji.replaceEmoji(attribute.alt, textPaint.getFontMetricsInt(), AndroidUtilities.dp(24), false); - stickerEmojiLayout = new StaticLayout(emoji, textPaint, AndroidUtilities.dp(100), Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false); - break; + if (stickerEmojiLayout == null) { + for (int a = 0; a < document.attributes.size(); a++) { + TLRPC.DocumentAttribute attribute = document.attributes.get(a); + if (attribute instanceof TLRPC.TL_documentAttributeSticker) { + if (!TextUtils.isEmpty(attribute.alt)) { + CharSequence emoji = Emoji.replaceEmoji(attribute.alt, textPaint.getFontMetricsInt(), AndroidUtilities.dp(24), false); + stickerEmojiLayout = new StaticLayout(emoji, textPaint, AndroidUtilities.dp(500), Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false); + break; + } } } } @@ -1210,7 +1219,7 @@ public void open(TLRPC.Document document, SendMessagesHelper.ImportingSticker st centerImage.setImage(sticker.path, null, null, sticker.animated ? "tgs" : null, 0); if (emojiPath != null) { CharSequence emoji = Emoji.replaceEmoji(emojiPath, textPaint.getFontMetricsInt(), AndroidUtilities.dp(24), false); - stickerEmojiLayout = new StaticLayout(emoji, textPaint, AndroidUtilities.dp(100), Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false); + stickerEmojiLayout = new StaticLayout(emoji, textPaint, AndroidUtilities.dp(500), Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false); } if (delegate.needMenu()) { AndroidUtilities.cancelRunOnUIThread(showSheetRunnable); @@ -1310,12 +1319,24 @@ public void close() { currentQuery = null; delegate = null; isVisible = false; + resourcesProvider = null; if (unlockPremiumView != null) { unlockPremiumView.animate().alpha(0).translationY(AndroidUtilities.dp(56)).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start(); } NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.startAllHeavyOperations, 8); } + public void clearDelegate(ContentPreviewViewerDelegate contentPreviewViewerDelegate) { + if (delegate == contentPreviewViewerDelegate) { + currentDocument = null; + currentStickerSet = null; + currentQuery = null; + delegate = null; + resourcesProvider = null; + reset(); + } + } + public void destroy() { isVisible = false; delegate = null; @@ -1378,6 +1399,7 @@ private void onDraw(Canvas canvas) { paint.setAlpha((int) (blurProgress * 255)); canvas.save(); canvas.scale(12f, 12f); + canvas.drawColor(Theme.multAlpha(Theme.getColor(Theme.key_windowBackgroundGray, resourcesProvider), blurProgress)); canvas.drawBitmap(blurrBitmap, 0, 0, paint); canvas.restore(); } @@ -1441,10 +1463,11 @@ private void onDraw(Canvas canvas) { } if (stickerEmojiLayout != null) { if (drawEffect) { - canvas.translate(-AndroidUtilities.dp(50), -effectImage.getImageHeight() / 2 - AndroidUtilities.dp(30)); + canvas.translate(-AndroidUtilities.dp(250), -effectImage.getImageHeight() / 2 - AndroidUtilities.dp(30)); } else { - canvas.translate(-AndroidUtilities.dp(50), -centerImage.getImageHeight() / 2 - AndroidUtilities.dp(30)); + canvas.translate(-AndroidUtilities.dp(250), -centerImage.getImageHeight() / 2 - AndroidUtilities.dp(30)); } + textPaint.setAlpha((int) (0xFF * showProgress)); stickerEmojiLayout.draw(canvas); } canvas.restore(); @@ -1490,28 +1513,23 @@ private void onDraw(Canvas canvas) { } } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } + private boolean preparingBitmap; private void prepareBlurBitmap() { - if (parentActivity == null) { + if (parentActivity == null || preparingBitmap) { return; } - View parentView = parentActivity.getWindow().getDecorView(); - int w = (int) (parentView.getMeasuredWidth() / 12.0f); - int h = (int) (parentView.getMeasuredHeight() / 12.0f); - Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - canvas.scale(1.0f / 12.0f, 1.0f / 12.0f); - canvas.drawColor(Theme.getColor(Theme.key_windowBackgroundWhite)); - parentView.draw(canvas); - if (parentActivity instanceof LaunchActivity && ((LaunchActivity) parentActivity).getActionBarLayout().getLastFragment().getVisibleDialog() != null) { - ((LaunchActivity) parentActivity).getActionBarLayout().getLastFragment().getVisibleDialog().getWindow().getDecorView().draw(canvas); - } - Utilities.stackBlurBitmap(bitmap, Math.max(10, Math.max(w, h) / 180)); - blurrBitmap = bitmap; + preparingBitmap = true; + AndroidUtilities.makeGlobalBlurBitmap(bitmap -> { + blurrBitmap = bitmap; + preparingBitmap = false; + if (containerView != null) { + containerView.invalidate(); + } + }, 12); } public boolean showMenuFor(View view) { @@ -1522,7 +1540,7 @@ public boolean showMenuFor(View view) { } setParentActivity(activity); StickerEmojiCell stickerEmojiCell = (StickerEmojiCell) view; - open(stickerEmojiCell.getSticker(), stickerEmojiCell.getStickerPath(), stickerEmojiCell.getEmoji(), delegate != null ? delegate.getQuery(false) : null, null, CONTENT_TYPE_STICKER, stickerEmojiCell.isRecent(), stickerEmojiCell.getParentObject(), resourcesProvider); + open(stickerEmojiCell.getSticker(), stickerEmojiCell.getStickerPath(), MessageObject.findAnimatedEmojiEmoticon(stickerEmojiCell.getSticker(), null, currentAccount), delegate != null ? delegate.getQuery(false) : null, null, CONTENT_TYPE_STICKER, stickerEmojiCell.isRecent(), stickerEmojiCell.getParentObject(), resourcesProvider); AndroidUtilities.cancelRunOnUIThread(showSheetRunnable); AndroidUtilities.runOnUIThread(showSheetRunnable, 16); stickerEmojiCell.setScaled(true); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/CreateTopicEmptyView.java b/TMessagesProj/src/main/java/org/telegram/ui/CreateTopicEmptyView.java index 4a32551694..5100ead168 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/CreateTopicEmptyView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/CreateTopicEmptyView.java @@ -64,9 +64,8 @@ public CreateTopicEmptyView(Context context, FrameLayout parent, Theme.Resources } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } private Paint getThemedPaint(String paintKey) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/DataAutoDownloadActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/DataAutoDownloadActivity.java index 4a6160713c..5ac89edeef 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/DataAutoDownloadActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/DataAutoDownloadActivity.java @@ -77,6 +77,7 @@ public class DataAutoDownloadActivity extends BaseFragment { private int photosRow; private int videosRow; private int filesRow; + private int storiesRow; private int typeSectionRow; private int rowCount; @@ -188,9 +189,9 @@ public void onItemClick(int id) { cell.setBackgroundColorAnimated(!checked, Theme.getColor(typePreset.enabled ? Theme.key_windowBackgroundChecked : Theme.key_windowBackgroundUnchecked)); updateRows(); if (typePreset.enabled) { - listAdapter.notifyItemRangeInserted(autoDownloadSectionRow + 1, 8); + listAdapter.notifyItemRangeInserted(autoDownloadSectionRow + 1, 9); } else { - listAdapter.notifyItemRangeRemoved(autoDownloadSectionRow + 1, 8); + listAdapter.notifyItemRangeRemoved(autoDownloadSectionRow + 1, 9); } listAdapter.notifyItemChanged(autoDownloadSectionRow); SharedPreferences.Editor editor = MessagesController.getMainSettings(currentAccount).edit(); @@ -208,7 +209,7 @@ public void onItemClick(int id) { cell.setChecked(!checked); DownloadController.getInstance(currentAccount).checkAutodownloadSettings(); wereAnyChanges = true; - } else if (position == photosRow || position == videosRow || position == filesRow) { + } else if (position == photosRow || position == videosRow || position == filesRow || position == storiesRow) { if (!view.isEnabled()) { return; } @@ -217,6 +218,8 @@ public void onItemClick(int id) { type = DownloadController.AUTODOWNLOAD_TYPE_PHOTO; } else if (position == videosRow) { type = DownloadController.AUTODOWNLOAD_TYPE_VIDEO; + } else if (position == storiesRow) { + type = -1; } else { type = DownloadController.AUTODOWNLOAD_TYPE_DOCUMENT; } @@ -242,8 +245,7 @@ public void onItemClick(int id) { NotificationsCheckCell cell = (NotificationsCheckCell) view; boolean checked = cell.isChecked(); - if (LocaleController.isRTL && x <= AndroidUtilities.dp(76) || !LocaleController.isRTL && x >= view.getMeasuredWidth() - AndroidUtilities.dp(76)) { - + if (position == storiesRow || (LocaleController.isRTL && x <= AndroidUtilities.dp(76) || !LocaleController.isRTL && x >= view.getMeasuredWidth() - AndroidUtilities.dp(76))) { if (currentPresetNum != 3) { if (currentPresetNum == 0) { typePreset.set(lowPreset); @@ -254,18 +256,22 @@ public void onItemClick(int id) { } } - boolean hasAny = false; - for (int a = 0; a < typePreset.mask.length; a++) { - if ((currentPreset.mask[a] & type) != 0) { - hasAny = true; - break; + if (position == storiesRow) { + typePreset.preloadStories = !checked; + } else { + boolean hasAny = false; + for (int a = 0; a < typePreset.mask.length; a++) { + if ((currentPreset.mask[a] & type) != 0) { + hasAny = true; + break; + } } - } - for (int a = 0; a < typePreset.mask.length; a++) { - if (checked) { - typePreset.mask[a] &=~ type; - } else if (!hasAny) { - typePreset.mask[a] |= type; + for (int a = 0; a < typePreset.mask.length; a++) { + if (checked) { + typePreset.mask[a] &= ~type; + } else if (!hasAny) { + typePreset.mask[a] |= type; + } } } @@ -407,7 +413,7 @@ public void onAnimationEnd(Animator animator) { linearLayout.addView(checkCell[0], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48)); checkCell[0].setOnClickListener(v -> checkCell[0].setChecked(!checkCell[0].isChecked())); - Drawable drawable = Theme.getThemedDrawable(getParentActivity(), R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow); + Drawable drawable = Theme.getThemedDrawableByKey(getParentActivity(), R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow); CombinedDrawable combinedDrawable = new CombinedDrawable(new ColorDrawable(Theme.getColor(Theme.key_windowBackgroundGray)), drawable); combinedDrawable.setFullsize(true); infoCell.setBackgroundDrawable(combinedDrawable); @@ -582,8 +588,9 @@ private void fillPresets() { break; } } - long size1 = (video1 ? o1.sizes[index1] : 0) + (doc1 ? o1.sizes[index2] : 0); - long size2 = (video2 ? o2.sizes[index1] : 0) + (doc2 ? o2.sizes[index2] : 0); + long size1 = (video1 ? o1.sizes[index1] : 0) + (doc1 ? o1.sizes[index2] : 0) + (o1.preloadStories ? 1 : 0); + long size2 = (video2 ? o2.sizes[index1] : 0) + (doc2 ? o2.sizes[index2] : 0) + (o2.preloadStories ? 1 : 0); + if (size1 > size2) { return 1; } else if (size1 < size2) { @@ -622,6 +629,7 @@ private void updateRows() { photosRow = rowCount++; videosRow = rowCount++; filesRow = rowCount++; + storiesRow = rowCount++; typeSectionRow = rowCount++; } else { usageHeaderRow = -1; @@ -631,6 +639,7 @@ private void updateRows() { photosRow = -1; videosRow = -1; filesRow = -1; + storiesRow = -1; typeSectionRow = -1; } } @@ -680,12 +689,17 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { DownloadController.Preset preset; String text; int type; + view.setDrawLine(true); if (position == photosRow) { text = LocaleController.getString("AutoDownloadPhotos", R.string.AutoDownloadPhotos); type = DownloadController.AUTODOWNLOAD_TYPE_PHOTO; } else if (position == videosRow) { text = LocaleController.getString("AutoDownloadVideos", R.string.AutoDownloadVideos); type = DownloadController.AUTODOWNLOAD_TYPE_VIDEO; + } else if (position == storiesRow) { + text = LocaleController.getString("AutoDownloadStories", R.string.AutoDownloadStories); + type = -1; + view.setDrawLine(false); } else { text = LocaleController.getString("AutoDownloadFiles", R.string.AutoDownloadFiles); type = DownloadController.AUTODOWNLOAD_TYPE_DOCUMENT; @@ -701,60 +715,70 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { int count = 0; StringBuilder builder = new StringBuilder(); - for (int a = 0; a < preset.mask.length; a++) { - if ((preset.mask[a] & type) != 0) { - if (builder.length() != 0) { - builder.append(", "); - } - switch (a) { - case 0: - builder.append(LocaleController.getString("AutoDownloadContacts", R.string.AutoDownloadContacts)); - break; - case 1: - builder.append(LocaleController.getString("AutoDownloadPm", R.string.AutoDownloadPm)); - break; - case 2: - builder.append(LocaleController.getString("AutoDownloadGroups", R.string.AutoDownloadGroups)); - break; - case 3: - builder.append(LocaleController.getString("AutoDownloadChannels", R.string.AutoDownloadChannels)); - break; - } - count++; - } - } - if (count == 4) { - builder.setLength(0); - if (position == photosRow) { - builder.append(LocaleController.getString("AutoDownloadOnAllChats", R.string.AutoDownloadOnAllChats)); + if (position == storiesRow) { + if (preset.preloadStories) { + builder = new StringBuilder(LocaleController.formatString("AutoDownloadOn", R.string.AutoDownloadOn, builder.toString())); + count = 1; } else { - builder.append(LocaleController.formatString("AutoDownloadUpToOnAllChats", R.string.AutoDownloadUpToOnAllChats, AndroidUtilities.formatFileSize(maxSize))); + builder = new StringBuilder(LocaleController.formatString("AutoDownloadOff", R.string.AutoDownloadOff, builder.toString())); + count = 0; } - } else if (count == 0) { - builder.append(LocaleController.getString("AutoDownloadOff", R.string.AutoDownloadOff)); } else { - if (position == photosRow) { - builder = new StringBuilder(LocaleController.formatString("AutoDownloadOnFor", R.string.AutoDownloadOnFor, builder.toString())); + for (int a = 0; a < preset.mask.length; a++) { + if ((preset.mask[a] & type) != 0) { + if (builder.length() != 0) { + builder.append(", "); + } + switch (a) { + case 0: + builder.append(LocaleController.getString("AutoDownloadContacts", R.string.AutoDownloadContacts)); + break; + case 1: + builder.append(LocaleController.getString("AutoDownloadPm", R.string.AutoDownloadPm)); + break; + case 2: + builder.append(LocaleController.getString("AutoDownloadGroups", R.string.AutoDownloadGroups)); + break; + case 3: + builder.append(LocaleController.getString("AutoDownloadChannels", R.string.AutoDownloadChannels)); + break; + } + count++; + } + } + if (count == 4) { + builder.setLength(0); + if (position == photosRow) { + builder.append(LocaleController.getString("AutoDownloadOnAllChats", R.string.AutoDownloadOnAllChats)); + } else { + builder.append(LocaleController.formatString("AutoDownloadUpToOnAllChats", R.string.AutoDownloadUpToOnAllChats, AndroidUtilities.formatFileSize(maxSize))); + } + } else if (count == 0) { + builder.append(LocaleController.getString("AutoDownloadOff", R.string.AutoDownloadOff)); } else { - builder = new StringBuilder(LocaleController.formatString("AutoDownloadOnUpToFor", R.string.AutoDownloadOnUpToFor, AndroidUtilities.formatFileSize(maxSize), builder.toString())); + if (position == photosRow) { + builder = new StringBuilder(LocaleController.formatString("AutoDownloadOnFor", R.string.AutoDownloadOnFor, builder.toString())); + } else { + builder = new StringBuilder(LocaleController.formatString("AutoDownloadOnUpToFor", R.string.AutoDownloadOnUpToFor, AndroidUtilities.formatFileSize(maxSize), builder.toString())); + } } } if (animateChecked) { view.setChecked(count != 0); } - view.setTextAndValueAndCheck(text, builder, count != 0, 0, true, position != filesRow); + view.setTextAndValueAndCheck(text, builder, count != 0, 0, true, position != storiesRow); break; } case 5: { TextInfoPrivacyCell view = (TextInfoPrivacyCell) holder.itemView; if (position == typeSectionRow) { view.setText(LocaleController.getString("AutoDownloadAudioInfo", R.string.AutoDownloadAudioInfo)); - view.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + view.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); view.setFixedSize(0); view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); } else if (position == autoDownloadSectionRow) { if (usageHeaderRow == -1) { - view.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + view.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); if (currentType == 0) { view.setText(LocaleController.getString("AutoDownloadOnMobileDataInfo", R.string.AutoDownloadOnMobileDataInfo)); } else if (currentType == 1) { @@ -764,7 +788,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); } else { - view.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + view.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); view.setText(null); view.setFixedSize(12); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { @@ -782,7 +806,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { @Override public boolean isEnabled(RecyclerView.ViewHolder holder) { int position = holder.getAdapterPosition(); - return position == photosRow || position == videosRow || position == filesRow; + return position == photosRow || position == videosRow || position == filesRow || position == storiesRow; } @Override @@ -831,7 +855,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType editor.putInt(key2, currentPresetNum); editor.commit(); DownloadController.getInstance(currentAccount).checkAutodownloadSettings(); - for (int a = 0; a < 3; a++) { + for (int a = 0; a < 4; a++) { RecyclerView.ViewHolder holder = listView.findViewHolderForAdapterPosition(photosRow + a); if (holder != null) { listAdapter.onBindViewHolder(holder, photosRow + a); @@ -850,7 +874,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType case 5: default: { view = new TextInfoPrivacyCell(mContext); - view.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + view.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } } view.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT)); @@ -867,7 +891,7 @@ public int getItemViewType(int position) { return 2; } else if (position == usageProgressRow) { return 3; - } else if (position == photosRow || position == videosRow || position == filesRow) { + } else if (position == photosRow || position == videosRow || position == filesRow || position == storiesRow) { return 4; } else { return 5; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/DataSettingsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/DataSettingsActivity.java index 64cf8120d2..3b4ffb3dd3 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/DataSettingsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/DataSettingsActivity.java @@ -97,7 +97,7 @@ public class DataSettingsActivity extends BaseFragment { private int autoplaySectionRow = -1; private int callsSectionRow; private int useLessDataForCallsRow; - private int quickRepliesRow; + private int quickRepliesRow = -1; private int callsSection2Row; private int proxySectionRow; private int proxyRow; @@ -185,7 +185,7 @@ private void updateRows(boolean fullNotify) { enableCacheStreamRow = -1;//rowCount++; callsSectionRow = rowCount++; useLessDataForCallsRow = rowCount++; - quickRepliesRow = rowCount++; +// quickRepliesRow = rowCount++; callsSection2Row = rowCount++; proxySectionRow = rowCount++; proxyRow = rowCount++; @@ -280,7 +280,7 @@ public void onItemClick(int id) { @Override public Integer getSelectorColor(int position) { if (position == resetDownloadRow) { - return Theme.multAlpha(getThemedColor(Theme.key_windowBackgroundWhiteRedText2), .1f); + return Theme.multAlpha(getThemedColor(Theme.key_text_RedRegular), .1f); } return getThemedColor(Theme.key_listSelector); } @@ -415,7 +415,7 @@ public Integer getSelectorColor(int position) { showDialog(dialog); TextView button = (TextView) dialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } else if (position == storageUsageRow) { presentFragment(new CacheControlActivity()); @@ -545,7 +545,7 @@ public Integer getSelectorColor(int position) { showDialog(alertDialog); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } }); @@ -603,9 +603,9 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { switch (holder.getItemViewType()) { case 0: { if (position == clearDraftsSectionRow) { - holder.itemView.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + holder.itemView.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } else { - holder.itemView.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + holder.itemView.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); } break; } @@ -667,7 +667,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } else if (position == resetDownloadRow) { textCell.setIcon(0); textCell.setCanDisable(true); - textCell.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText)); + textCell.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); textCell.setText(LocaleController.getString("ResetAutomaticMediaDownload", R.string.ResetAutomaticMediaDownload), false); } else if (position == quickRepliesRow) { textCell.setIcon(0); @@ -785,14 +785,14 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { builder.append(", "); } builder.append(LocaleController.getString("AutoDownloadVideosOn", R.string.AutoDownloadVideosOn)); - builder.append(String.format(" (%1$s)", AndroidUtilities.formatFileSize(preset.sizes[DownloadController.typeToIndex(DownloadController.AUTODOWNLOAD_TYPE_VIDEO)], true))); + builder.append(String.format(" (%1$s)", AndroidUtilities.formatFileSize(preset.sizes[DownloadController.typeToIndex(DownloadController.AUTODOWNLOAD_TYPE_VIDEO)], true, false))); } if (files) { if (builder.length() > 0) { builder.append(", "); } builder.append(LocaleController.getString("AutoDownloadFilesOn", R.string.AutoDownloadFilesOn)); - builder.append(String.format(" (%1$s)", AndroidUtilities.formatFileSize(preset.sizes[DownloadController.typeToIndex(DownloadController.AUTODOWNLOAD_TYPE_DOCUMENT)], true))); + builder.append(String.format(" (%1$s)", AndroidUtilities.formatFileSize(preset.sizes[DownloadController.typeToIndex(DownloadController.AUTODOWNLOAD_TYPE_DOCUMENT)], true, false))); } } else { builder.append(LocaleController.getString("NoMediaAutoDownload", R.string.NoMediaAutoDownload)); @@ -863,7 +863,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType break; case 4: view = new TextInfoPrivacyCell(mContext); - view.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + view.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); break; case 5: view = new NotificationsCheckCell(mContext); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/DataUsage2Activity.java b/TMessagesProj/src/main/java/org/telegram/ui/DataUsage2Activity.java index 6cd8c82434..9ded923ceb 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/DataUsage2Activity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/DataUsage2Activity.java @@ -156,7 +156,7 @@ public String getItemTitle(int position) { } } - private static String[] colors = { + private static int[] colors = { Theme.key_statisticChartLine_blue, Theme.key_statisticChartLine_green, Theme.key_statisticChartLine_lightblue, @@ -252,7 +252,7 @@ public ListView(Context context) { showDialog(dialog); TextView button = (TextView) dialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } }); @@ -584,7 +584,7 @@ protected void onSectionDown(int index, boolean down) { break; case VIEW_TYPE_RESET_BUTTON: TextCell textCell = new TextCell(getContext()); - textCell.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteRedText5)); + textCell.setTextColor(getThemedColor(Theme.key_text_RedRegular)); textCell.setBackgroundColor(getThemedColor(Theme.key_windowBackgroundWhite)); view = textCell; break; @@ -624,7 +624,7 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi int bottomViewType; boolean bottom = position + 1 < itemInners.size() && (bottomViewType = itemInners.get(position + 1).viewType) != item.viewType && bottomViewType != VIEW_TYPE_SEPARATOR && bottomViewType != VIEW_TYPE_ROUNDING; if (bottom) { - subtitleCell.setBackground(Theme.getThemedDrawable(getContext(), R.drawable.greydivider_top, Theme.key_windowBackgroundGrayShadow)); + subtitleCell.setBackground(Theme.getThemedDrawableByKey(getContext(), R.drawable.greydivider_top, Theme.key_windowBackgroundGrayShadow)); } else { subtitleCell.setBackground(null); } @@ -637,11 +637,11 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi boolean top = position > 0 && item.viewType != itemInners.get(position - 1).viewType; boolean bottom = position + 1 < itemInners.size() && itemInners.get(position + 1).viewType != item.viewType; if (top && bottom) { - view.setBackground(Theme.getThemedDrawable(getContext(), R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + view.setBackground(Theme.getThemedDrawableByKey(getContext(), R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); } else if (top) { - view.setBackground(Theme.getThemedDrawable(getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + view.setBackground(Theme.getThemedDrawableByKey(getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } else if (bottom) { - view.setBackground(Theme.getThemedDrawable(getContext(), R.drawable.greydivider_top, Theme.key_windowBackgroundGrayShadow)); + view.setBackground(Theme.getThemedDrawableByKey(getContext(), R.drawable.greydivider_top, Theme.key_windowBackgroundGrayShadow)); } else { view.setBackground(null); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/DataUsageActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/DataUsageActivity.java index 3f078d9de9..a9c2c9d536 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/DataUsageActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/DataUsageActivity.java @@ -483,7 +483,7 @@ public boolean supportsPredictiveItemAnimations() { showDialog(dialog); TextView button = (TextView) dialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } }); @@ -735,18 +735,18 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { switch (holder.getItemViewType()) { case 0: { if (position == resetSection2Row) { - holder.itemView.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + holder.itemView.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } else { - holder.itemView.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + holder.itemView.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); } break; } case 1: { TextSettingsCell textCell = (TextSettingsCell) holder.itemView; if (position == resetRow) { - textCell.setTag(Theme.key_windowBackgroundWhiteRedText2); + textCell.setTag(Theme.key_text_RedRegular); textCell.setText(LocaleController.getString("ResetStatistics", R.string.ResetStatistics), false); - textCell.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText2)); + textCell.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); } else { int type; textCell.setTag(Theme.key_windowBackgroundWhiteBlackText); @@ -806,7 +806,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } case 3: { TextInfoPrivacyCell cell = (TextInfoPrivacyCell) holder.itemView; - cell.setBackground(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + cell.setBackground(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); cell.setText(LocaleController.formatString("NetworkUsageSince", R.string.NetworkUsageSince, LocaleController.getInstance().formatterStats.format(StatsController.getInstance(currentAccount).getResetStatsDate(currentType)))); break; } @@ -889,7 +889,7 @@ public ArrayList getThemeDescriptions() { arrayList.add(new ThemeDescription(viewPages[a].listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); arrayList.add(new ThemeDescription(viewPages[a].listView, 0, new Class[]{TextSettingsCell.class}, new String[]{"valueTextView"}, null, null, null, Theme.key_windowBackgroundWhiteValueText)); - arrayList.add(new ThemeDescription(viewPages[a].listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteRedText2)); + arrayList.add(new ThemeDescription(viewPages[a].listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_text_RedRegular)); } return arrayList; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/DefaultThemesPreviewCell.java b/TMessagesProj/src/main/java/org/telegram/ui/DefaultThemesPreviewCell.java index 7793fed943..e8bd1c9468 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/DefaultThemesPreviewCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/DefaultThemesPreviewCell.java @@ -445,7 +445,7 @@ public void updateColors() { Theme.setSelectorDrawableColor(dayNightCell.getBackground(), Theme.getColor(Theme.key_listSelector), true); browseThemesCell.setBackground(Theme.createSelectorWithBackgroundDrawable(Theme.getColor(Theme.key_windowBackgroundWhite), Theme.getColor(Theme.key_listSelector))); - dayNightCell.setColors(null, Theme.key_windowBackgroundWhiteBlueText4); + dayNightCell.setColors(-1, Theme.key_windowBackgroundWhiteBlueText4); browseThemesCell.setColors(Theme.key_windowBackgroundWhiteBlueText4, Theme.key_windowBackgroundWhiteBlueText4); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Delegates/MemberRequestsDelegate.java b/TMessagesProj/src/main/java/org/telegram/ui/Delegates/MemberRequestsDelegate.java index 68e338b6b2..7fc7dbdfbb 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Delegates/MemberRequestsDelegate.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Delegates/MemberRequestsDelegate.java @@ -166,7 +166,7 @@ public FlickerLoadingView getLoadingView() { if (isShowLastItemDivider) { loadingView.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite, fragment.getResourceProvider())); } - loadingView.setColors(Theme.key_windowBackgroundWhite, Theme.key_windowBackgroundGray, null); + loadingView.setColors(Theme.key_windowBackgroundWhite, Theme.key_windowBackgroundGray, -1); loadingView.setViewType(FlickerLoadingView.MEMBER_REQUESTS_TYPE); } return loadingView; @@ -568,7 +568,7 @@ public RecyclerListView.Holder onCreateViewHolder(@NonNull ViewGroup parent, int break; case 1: view = new View(parent.getContext()); - view.setBackground(Theme.getThemedDrawable(parent.getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + view.setBackground(Theme.getThemedDrawableByKey(parent.getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); break; case 2: view = new View(parent.getContext()) { @@ -776,8 +776,7 @@ protected void onDraw(Canvas canvas) { popupLayout.addView(sendMsgCell); ActionBarMenuSubItem dismissCell = new ActionBarMenuSubItem(context, false, true); - dismissCell.setColors(Theme.getColor(Theme.key_dialogTextRed, resourcesProvider), Theme.getColor(Theme.key_dialogRedIcon, resourcesProvider)); - + dismissCell.setColors(Theme.getColor(Theme.key_text_RedBold, resourcesProvider), Theme.getColor(Theme.key_text_RedRegular, resourcesProvider)); dismissCell.setSelectorColor(Theme.getColor(Theme.key_dialogButtonSelector, resourcesProvider)); dismissCell.setTextAndIcon(LocaleController.getString("DismissRequest", R.string.DismissRequest), R.drawable.msg_remove); dismissCell.setOnClickListener((v) -> { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/DialogOrContactPickerActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/DialogOrContactPickerActivity.java index afed1c94b4..b0b573d60b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/DialogOrContactPickerActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/DialogOrContactPickerActivity.java @@ -506,6 +506,7 @@ public void setTranslationX(float translationX) { viewPages[a].fragmentView = (FrameLayout) viewPages[a].parentFragment.getFragmentView(); viewPages[a].actionBar = viewPages[a].parentFragment.getActionBar(); viewPages[a].addView(viewPages[a].fragmentView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + AndroidUtilities.removeFromParent(viewPages[a].actionBar); viewPages[a].addView(viewPages[a].actionBar, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); viewPages[a].actionBar.setVisibility(View.GONE); @@ -638,7 +639,7 @@ private void showBlockAlert(TLRPC.User user) { showDialog(dialog); TextView button = (TextView) dialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java index 3c2d11850e..603ec1255a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java @@ -34,7 +34,6 @@ import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; -import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Shader; import android.graphics.drawable.AnimatedVectorDrawable; @@ -46,6 +45,7 @@ import android.os.Bundle; import android.text.TextPaint; import android.text.TextUtils; +import android.util.Log; import android.util.LongSparseArray; import android.util.Property; import android.util.SparseArray; @@ -72,11 +72,11 @@ import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; -import android.widget.ScrollView; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import androidx.core.graphics.ColorUtils; import androidx.recyclerview.widget.ItemTouchHelper; @@ -87,7 +87,9 @@ import org.telegram.messenger.AccountInstance; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.BotWebViewVibrationEffect; import org.telegram.messenger.BuildVars; import org.telegram.messenger.ChatObject; import org.telegram.messenger.ContactsController; @@ -158,6 +160,7 @@ import org.telegram.ui.Components.AlertsCreator; import org.telegram.ui.Components.AnimatedEmojiDrawable; import org.telegram.ui.Components.AnimationProperties; +import org.telegram.ui.Components.ArchiveHelp; import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.BackButtonMenu; import org.telegram.ui.Components.BackupImageView; @@ -175,11 +178,14 @@ import org.telegram.ui.Components.FlickerLoadingView; import org.telegram.ui.Components.FloatingDebug.FloatingDebugController; import org.telegram.ui.Components.FloatingDebug.FloatingDebugProvider; +import org.telegram.ui.Components.FolderBottomSheet; import org.telegram.ui.Components.Forum.ForumUtilities; import org.telegram.ui.Components.FragmentContextView; +import org.telegram.ui.Components.ItemOptions; import org.telegram.ui.Components.JoinGroupAlert; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.MediaActionDrawable; +import org.telegram.ui.Components.MediaActivity; import org.telegram.ui.Components.NumberTextView; import org.telegram.ui.Components.PacmanAnimation; import org.telegram.ui.Components.Premium.LimitReachedBottomSheet; @@ -195,12 +201,19 @@ import org.telegram.ui.Components.RecyclerItemsEnterAnimator; import org.telegram.ui.Components.RecyclerListView; import org.telegram.ui.Components.SearchViewPager; +import org.telegram.ui.Components.SharedMediaLayout; import org.telegram.ui.Components.SimpleThemeDescription; import org.telegram.ui.Components.SizeNotifierFrameLayout; import org.telegram.ui.Components.StickersAlert; import org.telegram.ui.Components.SwipeGestureSettingsView; import org.telegram.ui.Components.UndoView; import org.telegram.ui.Components.ViewPagerFixed; +import org.telegram.ui.Stories.DialogStoriesCell; +import org.telegram.ui.Stories.StoriesController; +import org.telegram.ui.Stories.StoriesListPlaceProvider; +import org.telegram.ui.Stories.UserListPoller; +import org.telegram.ui.Stories.recorder.HintView2; +import org.telegram.ui.Stories.recorder.StoryRecorder; import java.io.File; import java.util.ArrayList; @@ -210,6 +223,7 @@ import java.util.Random; import kotlin.Unit; +import tw.nekomimi.nekogram.BackButtonMenuRecent; import tw.nekomimi.nekogram.helpers.PasscodeHelper; import tw.nekomimi.nekogram.ui.BottomBuilder; import tw.nekomimi.nekogram.InternalUpdater; @@ -233,17 +247,34 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. private final String ACTION_MODE_SEARCH_DIALOGS_TAG = "search_dialogs_action_mode"; private boolean isFirstTab = true; private boolean rightFragmentTransitionInProgress; + private boolean rightFragmentTransitionIsOpen; private boolean allowGlobalSearch = true; private TLRPC.RequestPeerType requestPeerType; private long requestPeerBotId; + ValueAnimator storiesVisibilityAnimator; + ValueAnimator storiesVisibilityAnimator2; + public float progressToShowStories; + public boolean hasStories = false; + public boolean hasOnlySlefStories = false; + private boolean animateToHasStories = false; + private float scrollYOffset; + private boolean actionModeFullyShowed; + private int actionModeAdditionalHeight; + private boolean invalidateScrollY = true; + private boolean fixScrollYAfterArchiveOpened; + private Bulletin storiesBulletin; + private float storiesOverscroll; + private boolean wasDrawn; public MessagesStorage.TopicKey getOpenedDialogId() { return openedDialogId; } public class ViewPage extends FrameLayout { - private DialogsRecyclerView listView; + public int pageAdditionalOffset; + public DialogsRecyclerView listView; + public RecyclerListViewScroller scroller; private LinearLayoutManager layoutManager; private DialogsAdapter dialogsAdapter; private ItemTouchHelper itemTouchhelper; @@ -257,7 +288,9 @@ public class ViewPage extends FrameLayout { private int lastItemsCount; private DialogsItemAnimator dialogsItemAnimator; private RecyclerItemsEnterAnimator recyclerItemsEnterAnimator; + private boolean isLocked; + public boolean animateStoriesView; private RecyclerListView animationSupportListView; private DialogsAdapter animationSupportDialogsAdapter; @@ -272,11 +305,29 @@ public boolean isDefaultDialogType() { boolean updating; Runnable updateListRunnable = () -> { - dialogsAdapter.updateList(listView, dialogsType == DIALOGS_TYPE_DEFAULT && hasHiddenArchive() && archivePullViewState == ARCHIVE_ITEM_STATE_HIDDEN, actionBar.getTranslationY()); + float scrollYOffset = DialogsActivity.this.scrollYOffset; + boolean hasStories = DialogsActivity.this.hasStories; +// if (hasStories && rightSlidingDialogContainer.hasFragment()) { +// hasStories = false; +// //scrollYOffset = 0; +// } + dialogsAdapter.updateList(listView, dialogsType == DIALOGS_TYPE_DEFAULT && hasHiddenArchive() && archivePullViewState == ARCHIVE_ITEM_STATE_HIDDEN, scrollYOffset, hasStories); + invalidateScrollY = true; listView.updateDialogsOnNextDraw = true; updating = false; }; + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + FrameLayout.LayoutParams lp = (LayoutParams) listView.getLayoutParams(); + if (animateStoriesView) { + lp.bottomMargin = -AndroidUtilities.dp(85); + } else { + lp.bottomMargin = 0; + } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + public void updateList(boolean animated) { if (isPaused) { return; @@ -295,7 +346,6 @@ public void updateList(boolean animated) { listView.setItemAnimator(null); } AndroidUtilities.runOnUIThread(updateListRunnable, 36); - } } @@ -312,31 +362,34 @@ public void updateList(boolean animated) { private boolean proxyItemVisible; private ActionBarMenuItem scanItem; private ActionBarMenuItem searchItem; + private ActionBarMenuItem optionsItem; private ActionBarMenuItem speedItem; private AnimatorSet speedAnimator; private ActionBarMenuItem doneItem; private ProxyDrawable proxyDrawable; + private HintView2 storyHint; + private HintView2 manyStoriesHint; private RLottieImageView floatingButton; - private RadialProgressView floatingProgressView; private FrameLayout floatingButtonContainer; + private RLottieImageView floatingButton2; + private RadialProgressView floating2ProgressView; + private FrameLayout floatingButton2Container; private ChatAvatarContainer avatarContainer; + private int undoViewIndex; private UndoView[] undoView = new UndoView[2]; private FilterTabsView filterTabsView; private boolean askingForPermissions; private RLottieDrawable passcodeDrawable; private SearchViewPager searchViewPager; + public DialogStoriesCell dialogStoriesCell; + public boolean dialogStoriesCellVisible; + public float progressToDialogStoriesCell; + float searchViewPagerTranslationY; + float panTranslationY; private View blurredView; - private Paint scrimPaint; - private Drawable scrimViewBackground; - private View scrimView; - private boolean scrimViewSelected; - private int[] scrimViewLocation = new int[2]; - private boolean scrimViewAppearing; - private AnimatorSet scrimAnimatorSet; - private ActionBarPopupWindow scrimPopupWindow; - private ActionBarMenuSubItem[] scrimPopupWindowItems; + private ItemOptions filterOptions; private SelectAnimatedEmojiDialog.SelectAnimatedEmojiDialogWindow selectAnimatedEmojiDialog; @@ -451,7 +504,7 @@ public void updateList(boolean animated) { private boolean afterSignup; private boolean showSetPasswordConfirm; private int otherwiseReloginDays; - private boolean allowGroups; + private boolean allowGroups, allowMegagroups, allowLegacyGroups; private boolean allowChannels; private boolean allowUsers; private boolean allowBots; @@ -520,7 +573,7 @@ public void updateList(boolean animated) { private Bulletin topBulletin; - private int animationIndex = -1; + private AnimationNotificationsLocker notificationsLocker = new AnimationNotificationsLocker(); private boolean searchIsShowed; private boolean searchWasFullyShowed; public boolean whiteActionBar; @@ -531,6 +584,7 @@ public void updateList(boolean animated) { private ValueAnimator filtersTabAnimator; private float filterTabsProgress; private float filterTabsMoveFrom; + private float storiesYOffset; private float tabsYOffset; private float scrollAdditionalOffset; @@ -551,7 +605,20 @@ public void setValue(DialogsActivity object, float value) { @Override public Float get(DialogsActivity object) { - return actionBar.getTranslationY(); + return scrollYOffset; + } + }; + + public final Property SEARCH_TRANSLATION_Y = new AnimationProperties.FloatProperty("viewPagerTranslation") { + @Override + public void setValue(View object, float value) { + searchViewPagerTranslationY = value; + object.setTranslationY(panTranslationY + searchViewPagerTranslationY); + } + + @Override + public Float get(View object) { + return searchViewPagerTranslationY; } }; @@ -606,7 +673,7 @@ public void setPadding(int left, int top, int right, int bottom) { rightSlidingDialogContainer.setFragmentViewPadding(topPadding); } if (whiteActionBar && searchViewPager != null) { - searchViewPager.setTranslationY(topPadding - lastMeasuredTopPadding); + searchViewPager.setTranslationY(topPadding - lastMeasuredTopPadding + searchViewPagerTranslationY); } else { requestLayout(); } @@ -650,9 +717,31 @@ public int getActionBarFullHeight() { searchTabsHeight = searchTabsView.getMeasuredHeight(); } h += filtersTabsHeight * (1f - searchAnimationProgress) + searchTabsHeight * searchAnimationProgress; + float rightSlidingProgress = 0f; + if (rightSlidingDialogContainer != null && rightSlidingDialogContainer.hasFragment()) { + rightSlidingProgress = rightSlidingDialogContainer.openedProgress; + } + if (hasStories) { + int storiesHeight = AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP); + h += storiesHeight * (1f - searchAnimationProgress) * (1f - rightSlidingProgress) * (1f - progressToActionMode); + } + h += storiesOverscroll; + return (int) h; } + public int getActionBarTop() { + float scrollY = scrollYOffset; + if (hasStories) { + float rightSlidingProgress = 0f; + if (rightSlidingDialogContainer != null && rightSlidingDialogContainer.hasFragment()) { + rightSlidingProgress = rightSlidingDialogContainer.openedProgress; + } + scrollY *= (1f - progressToActionMode) * (1f - rightSlidingProgress); + } + return (int) (-getY() + scrollY * (1f - searchAnimationProgress)); + } + @Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { if (child == fragmentContextView && fragmentContextView.isCallStyle()) { @@ -664,14 +753,15 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { boolean result; if (child == viewPages[0] || (viewPages.length > 1 && child == viewPages[1]) || child == fragmentContextView || child == fragmentLocationContextView || child == dialogsHintCell) { canvas.save(); - canvas.clipRect(0, -getY() + actionBar.getY() + getActionBarFullHeight(), getMeasuredWidth(), getMeasuredHeight()); + + canvas.clipRect(0, -getY() + getActionBarTop() + getActionBarFullHeight(), getMeasuredWidth(), getMeasuredHeight()); if (slideFragmentProgress != 1f) { if (slideFragmentLite) { canvas.translate((isDrawerTransition ? 1 : -1) * AndroidUtilities.dp(slideAmplitudeDp) * (1f - slideFragmentProgress), 0); } else { final float s = 1f - 0.05f * (1f - slideFragmentProgress); canvas.translate((isDrawerTransition ? AndroidUtilities.dp(4) : -AndroidUtilities.dp(4)) * (1f - slideFragmentProgress), 0); - canvas.scale(s, s, isDrawerTransition ? getMeasuredWidth() : 0, -getY() + actionBar.getY() + getActionBarFullHeight()); + canvas.scale(s, s, isDrawerTransition ? getMeasuredWidth() : 0, -getY() + scrollYOffset + getActionBarFullHeight()); } } result = super.drawChild(canvas, child, drawingTime); @@ -695,14 +785,61 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { @Override protected void dispatchDraw(Canvas canvas) { + if (invalidateScrollY && !rightSlidingDialogContainer.hasFragment() && hasStories && progressToActionMode == 0) { + invalidateScrollY = false; + int firstItemPosition = hasHiddenArchive() && viewPages[0].dialogsType == DIALOGS_TYPE_DEFAULT ? 1 : 0; + DialogsRecyclerView recyclerView = viewPages[0].listView; + if (fixScrollYAfterArchiveOpened) { + if (waitingForScrollFinished) { + firstItemPosition = 0; + } else { + if (firstItemPosition == 0) { + fixScrollYAfterArchiveOpened = false; + } + if (fixScrollYAfterArchiveOpened) { + RecyclerView.ViewHolder archiveHolder = recyclerView.findViewHolderForLayoutPosition(0); + if (archiveHolder == null) { + fixScrollYAfterArchiveOpened = false; + } else if (archiveHolder.itemView.getBottom() <= recyclerView.getPaddingTop() - AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP)) { + fixScrollYAfterArchiveOpened = false; + } else if (archiveHolder.itemView.getTop() >= recyclerView.getPaddingTop()) { + fixScrollYAfterArchiveOpened = false; + } + if (fixScrollYAfterArchiveOpened && firstItemPosition == 1) { + firstItemPosition = 0; + } + } + } + } + + RecyclerView.ViewHolder holder = recyclerView.findViewHolderForLayoutPosition(firstItemPosition); + if (holder != null) { + float visiblePartAfterScroll = recyclerView.getPaddingTop() - holder.itemView.getY(); + if (visiblePartAfterScroll >= 0) { + int maxScrollYOffset = getMaxScrollYOffset(); + float newTranslation = -visiblePartAfterScroll; + if (newTranslation < -maxScrollYOffset) { + newTranslation = -maxScrollYOffset; + } else if (newTranslation > 0) { + newTranslation = 0; + } + DialogsActivity.this.setScrollY(newTranslation); + } else { + DialogsActivity.this.setScrollY(0); + } + } else { + DialogsActivity.this.setScrollY(-getMaxScrollYOffset()); + } + } int actionBarHeight = getActionBarFullHeight(); int top; if (inPreviewMode) { top = AndroidUtilities.statusBarHeight; } else { - top = (int) (-getY() + actionBar.getY()); + top = getActionBarTop(); } rightSlidingDialogContainer.setCurrentTop(top + actionBarHeight); + float storiesAlpha = 1f; if (whiteActionBar) { if (searchAnimationProgress == 1f) { actionBarSearchPaint.setColor(Theme.getColor(Theme.key_windowBackgroundWhite)); @@ -716,7 +853,7 @@ protected void dispatchDraw(Canvas canvas) { } } else if (searchAnimationProgress == 0) { if (filterTabsView != null && filterTabsView.getVisibility() == View.VISIBLE) { - filterTabsView.setTranslationY(actionBar.getTranslationY()); + filterTabsView.setTranslationY(scrollYOffset); } } AndroidUtilities.rectTmp2.set(0, top, getMeasuredWidth(), top + actionBarHeight); @@ -739,7 +876,7 @@ protected void dispatchDraw(Canvas canvas) { filterTabsView.setTranslationY(actionBarHeight - (actionBar.getHeight() + filterTabsView.getMeasuredHeight())); } if (searchTabsView != null) { - float y = actionBarHeight - (actionBar.getHeight() + searchTabsView.getMeasuredHeight()); + float y = top + actionBarHeight - searchTabsView.getMeasuredHeight() - searchTabsView.getTop(); float alpha; if (searchAnimationTabsDelayedCrossfade) { alpha = searchAnimationProgress < 0.5f ? 0 : (searchAnimationProgress - 0.5f) / 0.5f; @@ -766,25 +903,57 @@ protected void dispatchDraw(Canvas canvas) { } } tabsYOffset = 0; - if ((filtersTabAnimator != null || rightSlidingDialogContainer.hasFragment()) && filterTabsView != null && filterTabsView.getVisibility() == View.VISIBLE) { - tabsYOffset = -(1f - filterTabsProgress) * filterTabsView.getMeasuredHeight(); - filterTabsView.setTranslationY(actionBar.getTranslationY() + tabsYOffset); - filterTabsView.setAlpha(filterTabsProgress); - viewPages[0].setTranslationY(-(1f - filterTabsProgress) * filterTabsMoveFrom); + storiesYOffset = 0; + if (hasStories) { + tabsYOffset -= Math.min(AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP) + scrollYOffset, progressToActionMode * AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP)); + storiesYOffset = tabsYOffset; + } + if ((filtersTabAnimator != null || rightSlidingDialogContainer.hasFragment())) { + float rightSlidingProgress = 0f; + if (rightSlidingDialogContainer != null && rightSlidingDialogContainer.hasFragment()) { + rightSlidingProgress = rightSlidingDialogContainer.openedProgress; + } + if (hasStories) { + tabsYOffset -= rightSlidingProgress * (getMaxScrollYOffset() + scrollYOffset); + storiesYOffset = tabsYOffset; + } + if (dialogStoriesCellVisible) { + storiesAlpha = 1f - Utilities.clamp(rightSlidingProgress / 0.5f, 1f, 0f); + } + if (filterTabsView != null && filterTabsView.getVisibility() == View.VISIBLE) { + tabsYOffset -= (1f - filterTabsProgress) * filterTabsView.getMeasuredHeight(); + filterTabsView.setTranslationY(scrollYOffset + tabsYOffset); + filterTabsView.setAlpha(filterTabsProgress); + } + if (rightSlidingDialogContainer.hasFragment()) { + float rightFragmentOffset = 0; + if (rightFragmentTransitionInProgress) { + float scrollOffset = rightFragmentTransitionIsOpen ? 0 : scrollYOffset; + rightFragmentOffset = -AndroidUtilities.lerp(-scrollOffset, scrollOffset, rightSlidingDialogContainer.openedProgress); + } + viewPages[0].setTranslationY(-(1f - filterTabsProgress) * filterTabsMoveFrom + rightFragmentOffset); + } else { + viewPages[0].setTranslationY(getActionBarMoveFrom(false) - AndroidUtilities.lerp(getActionBarMoveFrom(false), filterTabsMoveFrom, (1f - filterTabsProgress))); + } } else if (filterTabsView != null && filterTabsView.getVisibility() == View.VISIBLE) { - filterTabsView.setTranslationY(actionBar.getTranslationY()); + filterTabsView.setTranslationY(scrollYOffset + tabsYOffset + storiesOverscroll); filterTabsView.setAlpha(1f); } updateContextViewPosition(); + updateStoriesViewAlpha(storiesAlpha); super.dispatchDraw(canvas); if (whiteActionBar && searchAnimationProgress > 0 && searchAnimationProgress < 1f && searchTabsView != null) { windowBackgroundPaint.setColor(Theme.getColor(Theme.key_windowBackgroundWhite)); windowBackgroundPaint.setAlpha((int) (windowBackgroundPaint.getAlpha() * searchAnimationProgress)); - canvas.drawRect(0, top + actionBarHeight, getMeasuredWidth(), top + actionBar.getMeasuredHeight() + searchTabsView.getMeasuredHeight(), windowBackgroundPaint); + float b = top + actionBar.getMeasuredHeight() + searchTabsView.getMeasuredHeight(); + float t = top + actionBarHeight; + if (b > t) { + canvas.drawRect(0, top + actionBarHeight, getMeasuredWidth(), b, windowBackgroundPaint); + } } if (parentLayout != null && actionBar != null && !actionBar.getCastShadows()) { - int y = (int) (actionBar.getY() + getActionBarFullHeight()); + int y = top + actionBarHeight; parentLayout.drawHeaderShadow(canvas, (int) (255 * (1f - searchAnimationProgress)), y); if (searchAnimationProgress > 0) { if (searchAnimationProgress < 1) { @@ -827,32 +996,7 @@ protected void dispatchDraw(Canvas canvas) { blurredView.draw(canvas); } } - if (scrimView != null) { - canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), scrimPaint); - canvas.save(); - getLocationInWindow(pos); - canvas.translate(scrimViewLocation[0] - pos[0], scrimViewLocation[1] - (Build.VERSION.SDK_INT < 21 ? AndroidUtilities.statusBarHeight : 0)); - if (scrimViewBackground != null) { - scrimViewBackground.setAlpha(scrimViewAppearing ? 255 : (int) (scrimPaint.getAlpha() / 50f * 255f)); - scrimViewBackground.setBounds(0, 0, scrimView.getWidth(), scrimView.getHeight()); - scrimViewBackground.draw(canvas); - } - Drawable selectorDrawable = filterTabsView.getListView().getSelectorDrawable(); - if (scrimViewAppearing && selectorDrawable != null) { - canvas.save(); - Rect selectorBounds = selectorDrawable.getBounds(); - canvas.translate(-selectorBounds.left, -selectorBounds.top); - selectorDrawable.draw(canvas); - canvas.restore(); - } - scrimView.draw(canvas); - if (scrimViewSelected) { - Drawable drawable = filterTabsView.getSelectorDrawable(); - canvas.translate(-scrimViewLocation[0], -drawable.getIntrinsicHeight() - 1); - drawable.draw(canvas); - } - canvas.restore(); - } + wasDrawn = true; } private boolean wasPortrait; @@ -902,7 +1046,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { topBulletin.updatePosition(); } } - searchViewPager.setTranslationY(0); + searchViewPager.setTranslationY(searchViewPagerTranslationY); } } @@ -918,16 +1062,30 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { child.measure(contentWidthSpec, contentHeightSpec); } else if (child instanceof ViewPage) { int contentWidthSpec = View.MeasureSpec.makeMeasureSpec(widthSize, View.MeasureSpec.EXACTLY); - int h; - if (filterTabsView != null && filterTabsView.getVisibility() == VISIBLE) { - h = heightSize - inputFieldHeight + AndroidUtilities.dp(2) - AndroidUtilities.dp(44) - topPadding - (dialogsHintCell != null ? dialogsHintCell.height() : 0); + int h = heightSize - inputFieldHeight + AndroidUtilities.dp(2) - topPadding; + if (hasStories || (filterTabsView != null && filterTabsView.getVisibility() == VISIBLE)) { + if (filterTabsView != null && filterTabsView.getVisibility() == VISIBLE) { + h -= AndroidUtilities.dp(44); + } if (rightSlidingDialogContainer.hasFragment()) { - h += AndroidUtilities.dp(44); + if (filterTabsView != null && filterTabsView.getVisibility() == VISIBLE) { + h += AndroidUtilities.dp(44); + } + if (hasStories) { + h += AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP); + } + if (dialogsHintCell != null && dialogsHintCell.getVisibility() == View.VISIBLE) { + h += dialogsHintCell.getMeasuredHeight(); + } } - } else { - h = heightSize - inputFieldHeight + AndroidUtilities.dp(2) - ((onlySelect && !(initialDialogsType == 3 && NekoConfig.showTabsOnForward.Bool())) ? 0 : actionBar.getMeasuredHeight()) - topPadding - (dialogsHintCell != null ? dialogsHintCell.height() : 0); + } else if (!onlySelect || initialDialogsType == DIALOGS_TYPE_FORWARD) { + h -= actionBar.getMeasuredHeight(); } - if (filtersTabAnimator != null && filterTabsView != null && filterTabsView.getVisibility() == VISIBLE) { + if (dialogsHintCell != null) { + h -= dialogsHintCell.height(); + } + h += actionModeAdditionalHeight; + if (filtersTabAnimator != null && (hasStories || (filterTabsView != null && filterTabsView.getVisibility() == VISIBLE))) { h += filterTabsMoveFrom; } else { child.setTranslationY(0); @@ -938,10 +1096,10 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { child.measure(contentWidthSpec, View.MeasureSpec.makeMeasureSpec(Math.max(AndroidUtilities.dp(10), h), View.MeasureSpec.EXACTLY)); child.setPivotX(child.getMeasuredWidth() / 2); } else if (child == searchViewPager) { - searchViewPager.setTranslationY(0); + searchViewPager.setTranslationY(searchViewPagerTranslationY); int contentWidthSpec = View.MeasureSpec.makeMeasureSpec(widthSize, View.MeasureSpec.EXACTLY); int h = View.MeasureSpec.getSize(heightMeasureSpec) + keyboardSize; - int contentHeightSpec = View.MeasureSpec.makeMeasureSpec(Math.max(AndroidUtilities.dp(10), h - inputFieldHeight + AndroidUtilities.dp(2) - (onlySelect && !(initialDialogsType == 3 && NekoConfig.showTabsOnForward.Bool()) ? 0 : actionBar.getMeasuredHeight()) - topPadding) - (searchTabsView == null ? 0 : AndroidUtilities.dp(44)), View.MeasureSpec.EXACTLY); + int contentHeightSpec = View.MeasureSpec.makeMeasureSpec(Math.max(AndroidUtilities.dp(10), h - inputFieldHeight + AndroidUtilities.dp(2) - (onlySelect && !(initialDialogsType == DIALOGS_TYPE_FORWARD && NekoConfig.showTabsOnForward.Bool()) ? 0 : actionBar.getMeasuredHeight()) - topPadding) - (searchTabsView == null ? 0 : AndroidUtilities.dp(44)), View.MeasureSpec.EXACTLY); child.measure(contentWidthSpec, contentHeightSpec); child.setPivotX(child.getMeasuredWidth() / 2); } else if (commentView != null && commentView.isPopupView(child)) { @@ -954,7 +1112,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } else { child.measure(View.MeasureSpec.makeMeasureSpec(widthSize, View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(child.getLayoutParams().height, View.MeasureSpec.EXACTLY)); } - } else if (child == rightSlidingDialogContainer){ + } else if (child == rightSlidingDialogContainer) { int h = View.MeasureSpec.getSize(heightMeasureSpec); int transitionPadding = ((isSlideBackTransition || isDrawerTransition) ? (int) (h * 0.05f) : 0); h += transitionPadding; @@ -1046,16 +1204,25 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) { } else { childTop = commentView.getBottom(); } - } else if (child == filterTabsView || child == searchTabsView || child == filtersView) { + } else if (child == filterTabsView || child == searchTabsView || child == filtersView || child == dialogStoriesCell) { childTop = actionBar.getMeasuredHeight(); + if (hasStories && child == filterTabsView) { + childTop += AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP); + } + if (child == dialogStoriesCell && dialogStoriesCell.getPremiumHint() != null) { + dialogStoriesCell.getPremiumHint().layout(childLeft, childTop - AndroidUtilities.dp(24 + 8 + 22) + height, childLeft + width, childTop - AndroidUtilities.dp(24 + 8 + 22) + height + dialogStoriesCell.getPremiumHint().getMeasuredHeight()); + } } else if (child == searchViewPager) { - childTop = (onlySelect && !(initialDialogsType == 3 && NekoConfig.showTabsOnForward.Bool()) ? 0 : actionBar.getMeasuredHeight()) + topPadding + (searchTabsView == null ? 0 : AndroidUtilities.dp(44)); + childTop = (onlySelect && !(initialDialogsType == DIALOGS_TYPE_FORWARD && NekoConfig.showTabsOnForward.Bool()) ? 0 : actionBar.getMeasuredHeight()) + topPadding + (searchTabsView == null ? 0 : AndroidUtilities.dp(44)); } else if (child instanceof DatabaseMigrationHint) { childTop = actionBar.getMeasuredHeight(); } else if (child instanceof ViewPage) { - if ((initialDialogsType == 3 && NekoConfig.showTabsOnForward.Bool()) || !onlySelect) { - if (filterTabsView != null && filterTabsView.getVisibility() == VISIBLE) { - childTop = AndroidUtilities.dp(44); + if (!onlySelect || initialDialogsType == DIALOGS_TYPE_FORWARD) { + if (hasStories || (filterTabsView != null && filterTabsView.getVisibility() == VISIBLE)) { + childTop = 0; + if (filterTabsView != null && filterTabsView.getVisibility() == VISIBLE) { + childTop += AndroidUtilities.dp(44); + } } else { childTop = actionBar.getMeasuredHeight(); } @@ -1064,10 +1231,10 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) { if (dialogsHintCell != null) { childTop += dialogsHintCell.height(); } - } else if (child instanceof DialogsHintCell) { - childTop += actionBar.getMeasuredHeight() + (filterTabsView != null && filterTabsView.getVisibility() == View.VISIBLE ? filterTabsView.getMeasuredHeight() : 0); - } else if (child instanceof FragmentContextView) { + } else if (child instanceof DialogsHintCell || child instanceof FragmentContextView) { childTop += actionBar.getMeasuredHeight(); + } else if (dialogStoriesCell != null && dialogStoriesCell.getPremiumHint() == child) { + continue; } else if (child == floatingButtonContainer && selectAnimatedEmojiDialog != null) { childTop += keyboardSize; } @@ -1101,9 +1268,22 @@ public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { @Override public boolean onTouchEvent(MotionEvent ev) { - if (parentLayout != null && filterTabsView != null && !filterTabsView.isEditing() && !searching && !rightSlidingDialogContainer.hasFragment() && - !parentLayout.checkTransitionAnimation() && !parentLayout.isInPreviewMode() && !parentLayout.isPreviewOpenAnimationInProgress() && !parentLayout.getDrawerLayoutContainer().isDrawerOpened() && - (ev == null || startedTracking || ev.getY() > actionBar.getMeasuredHeight() + actionBar.getTranslationY()) && (SharedConfig.getChatSwipeAction(currentAccount) == SwipeGestureSettingsView.SWIPE_GESTURE_FOLDERS || SharedConfig.getChatSwipeAction(currentAccount) == SwipeGestureSettingsView.SWIPE_GESTURE_ARCHIVE && viewPages[0] != null && (viewPages[0].dialogsAdapter.getDialogsType() == 7 || viewPages[0].dialogsAdapter.getDialogsType() == 8))) { + if ( + parentLayout != null && + filterTabsView != null && !filterTabsView.isEditing() && + !searching && + !rightSlidingDialogContainer.hasFragment() && + !parentLayout.checkTransitionAnimation() && !parentLayout.isInPreviewMode() && !parentLayout.isPreviewOpenAnimationInProgress() && !parentLayout.getDrawerLayoutContainer().isDrawerOpened() && + ( + ev == null || + startedTracking || + ev.getY() > getActionBarTop() + getActionBarFullHeight() + ) && ( + initialDialogsType == DIALOGS_TYPE_FORWARD || + SharedConfig.getChatSwipeAction(currentAccount) == SwipeGestureSettingsView.SWIPE_GESTURE_FOLDERS || + SharedConfig.getChatSwipeAction(currentAccount) == SwipeGestureSettingsView.SWIPE_GESTURE_ARCHIVE && + viewPages[0] != null && (viewPages[0].dialogsAdapter.getDialogsType() == 7 || viewPages[0].dialogsAdapter.getDialogsType() == 8)) + ) { if (ev != null) { if (velocityTracker == null) { velocityTracker = VelocityTracker.obtain(); @@ -1374,9 +1554,55 @@ protected void onDetachedFromWindow() { } } + private void updateStoriesViewAlpha(float alpha) { + dialogStoriesCell.setAlpha((1f - progressToActionMode) * alpha * progressToDialogStoriesCell * (1f - Utilities.clamp(searchAnimationProgress / 0.5f, 1f, 0f))); + float containersAlpha; + + if (hasStories || animateToHasStories) { + float p = Utilities.clamp(-scrollYOffset / AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP), 1f, 0f); + float pHalf = Utilities.clamp(p / 0.5f, 1f, 0f); + dialogStoriesCell.setClipTop(0); + if (!hasStories && animateToHasStories) { + dialogStoriesCell.setTranslationY(-AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP)); + dialogStoriesCell.setProgressToCollapse(1f); + containersAlpha = 1f - progressToDialogStoriesCell; + } else { + dialogStoriesCell.setTranslationY(scrollYOffset + storiesYOffset + storiesOverscroll / 2f); + dialogStoriesCell.setProgressToCollapse(p, !rightSlidingDialogContainer.hasFragment()); + if (!animateToHasStories) { + containersAlpha = 1f - progressToDialogStoriesCell; + } else { + containersAlpha = (1f - pHalf); + } + } + actionBar.setTranslationY(0); + } else { + if (hasOnlySlefStories) { + dialogStoriesCell.setTranslationY(-AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP) + scrollYOffset); + dialogStoriesCell.setProgressToCollapse(1f); + dialogStoriesCell.setClipTop((int) (AndroidUtilities.statusBarHeight - dialogStoriesCell.getY())); + } + containersAlpha = 1f - progressToDialogStoriesCell; + actionBar.setTranslationY(scrollYOffset); + } + if (containersAlpha != 1f) { + actionBar.getTitlesContainer().setPivotY(AndroidUtilities.statusBarHeight + ActionBar.getCurrentActionBarHeight() / 2f); + actionBar.getTitlesContainer().setPivotX(AndroidUtilities.dp(72)); + float s = 0.8f + 0.2f * containersAlpha; + actionBar.getTitlesContainer().setScaleY(s); + actionBar.getTitlesContainer().setScaleX(s); + actionBar.getTitlesContainer().setAlpha(containersAlpha); + } else { + actionBar.getTitlesContainer().setScaleY(1f); + actionBar.getTitlesContainer().setScaleY(1f); + actionBar.getTitlesContainer().setScaleX(1f); + actionBar.getTitlesContainer().setAlpha(1f); + } + } + public static float viewOffset = 0.0f; - public class DialogsRecyclerView extends BlurredRecyclerView { + public class DialogsRecyclerView extends BlurredRecyclerView implements StoriesListPlaceProvider.ClippedView { public boolean updateDialogsOnNextDraw; private boolean firstLayout = true; @@ -1396,6 +1622,7 @@ public class DialogsRecyclerView extends BlurredRecyclerView { float selectorPositionProgress = 1f; float animateFromSelectorPosition; boolean animateSwitchingSelector; + UserListPoller poller; public DialogsRecyclerView(Context context, ViewPage page) { super(context); @@ -1503,6 +1730,9 @@ protected void dispatchDraw(Canvas canvas) { float maxTop = Integer.MAX_VALUE; float maxBottom = Integer.MIN_VALUE; DialogCell selectedCell = null; + + float scrollOffset = rightFragmentTransitionIsOpen ? 0 : scrollYOffset; + for (int i = 0; i < getChildCount(); i++) { View view = getChildAt(i); DialogCell dialogCell = null; @@ -1527,11 +1757,11 @@ protected void dispatchDraw(Canvas canvas) { dialogCell.collapseOffset = (animateToView.getTop() - dialogCell.getTop()) * rightFragmentOpenedProgress; if (dialogCell.getTop() + dialogCell.collapseOffset < maxTop) { - maxTop = dialogCell.getTop() + dialogCell.collapseOffset; + maxTop = dialogCell.getTop() + dialogCell.collapseOffset - scrollOffset; } float bottom = dialogCell.getTop() + AndroidUtilities.lerp(dialogCell.getMeasuredHeight(), animateToView.getMeasuredHeight(), rightFragmentOpenedProgress); if (bottom + dialogCell.collapseOffset > maxBottom) { - maxBottom = bottom + dialogCell.collapseOffset; + maxBottom = bottom + dialogCell.collapseOffset - scrollOffset; } } } @@ -1549,14 +1779,18 @@ protected void dispatchDraw(Canvas canvas) { } if (animationSupportListView != null) { int restoreCount = canvas.save(); + canvas.translate(view.getX(), view.getY()); - if (dialogCell == null) { + if (dialogCell != null) { + dialogCell.rightFragmentOffset = -scrollOffset; + } else { canvas.saveLayerAlpha(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight(), (int) (255 * (1f - rightFragmentOpenedProgress)), Canvas.ALL_SAVE_FLAG); } view.draw(canvas); if (dialogCell != null && dialogCell != selectedCell) { dialogCell.collapseOffset = 0; + dialogCell.rightFragmentOffset = 0; } canvas.restoreToCount(restoreCount); } @@ -1567,6 +1801,7 @@ protected void dispatchDraw(Canvas canvas) { canvas.save(); lastDrawSelectorY = selectedCell.getY() + selectedCell.collapseOffset + selectedCell.avatarImage.getImageY(); selectedCell.collapseOffset = 0; + selectedCell.rightFragmentOffset = 0; if (selectorPositionProgress != 1f) { selectorPositionProgress += 16 / 200f; selectorPositionProgress = Utilities.clamp(selectorPositionProgress, 1f, 0f); @@ -1605,7 +1840,7 @@ protected void dispatchDraw(Canvas canvas) { for (int i = 0; i < animationSupportViewsByDialogId.size(); i++) { View view = animationSupportViewsByDialogId.valueAt(i); int position = animationSupportListView.getChildLayoutPosition(view); - if ( position < minSupportedViewsPosition && view.getTop() > maxUndrawTop) { + if (position < minSupportedViewsPosition && view.getTop() > maxUndrawTop) { maxUndrawTop = view.getTop(); } if (position > maxSupportedViewsPosition && view.getBottom() < maxUndrawBottom) { @@ -1668,6 +1903,10 @@ protected void dispatchDraw(Canvas canvas) { if (slidingView != null && pacmanAnimation != null) { pacmanAnimation.draw(canvas, slidingView.getTop() + slidingView.getMeasuredHeight() / 2); } + if (poller == null) { + poller = UserListPoller.getInstance(currentAccount); + } + poller.checkList( this); } private boolean drawMovingViewsOverlayed() { @@ -1696,7 +1935,7 @@ public void setAdapter(RecyclerView.Adapter adapter) { @Override protected void onMeasure(int widthSpec, int heightSpec) { int t = 0; - if ((initialDialogsType == 3 && NekoConfig.showTabsOnForward.Bool()) || !onlySelect) { + if ((initialDialogsType == DIALOGS_TYPE_FORWARD && NekoConfig.showTabsOnForward.Bool()) || !onlySelect) { if (filterTabsView != null && filterTabsView.getVisibility() == VISIBLE) { t = AndroidUtilities.dp(44); } else { @@ -1705,7 +1944,7 @@ protected void onMeasure(int widthSpec, int heightSpec) { } int pos = parentPage.layoutManager.findFirstVisibleItemPosition(); - if (pos != RecyclerView.NO_POSITION && parentPage.itemTouchhelper.isIdle()) { + if (pos != RecyclerView.NO_POSITION && parentPage.itemTouchhelper.isIdle() && !parentPage.layoutManager.hasPendingScrollPosition()) { RecyclerView.ViewHolder holder = parentPage.listView.findViewHolderForAdapterPosition(pos); if (holder != null) { int top = holder.itemView.getTop(); @@ -1713,20 +1952,29 @@ protected void onMeasure(int widthSpec, int heightSpec) { pos = Math.max(1, pos); } ignoreLayout = true; - parentPage.layoutManager.scrollToPositionWithOffset(pos, (int) (top - lastListPadding + scrollAdditionalOffset)); + parentPage.layoutManager.scrollToPositionWithOffset(pos, (int) (top - lastListPadding + scrollAdditionalOffset + parentPage.pageAdditionalOffset)); ignoreLayout = false; } + } else if (pos == RecyclerView.NO_POSITION && firstLayout) { + parentPage.layoutManager.scrollToPositionWithOffset(parentPage.dialogsType == DIALOGS_TYPE_DEFAULT && hasHiddenArchive() ? 1 : 0, (int) scrollYOffset); } - if ((initialDialogsType == 3 && NekoConfig.showTabsOnForward.Bool()) || !onlySelect) { + if (!onlySelect || (initialDialogsType == DIALOGS_TYPE_FORWARD && NekoConfig.showTabsOnForward.Bool())) { ignoreLayout = true; - if (filterTabsView != null && filterTabsView.getVisibility() == VISIBLE) { + if (hasStories || (filterTabsView != null && filterTabsView.getVisibility() == VISIBLE)) { t = ActionBar.getCurrentActionBarHeight() + (actionBar.getOccupyStatusBar() ? AndroidUtilities.statusBarHeight : 0); } else { t = inPreviewMode && Build.VERSION.SDK_INT >= 21 ? AndroidUtilities.statusBarHeight : 0; } + if (hasStories && !actionModeFullyShowed) { + t += AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP); + } setTopGlowOffset(t); setPadding(0, t, 0, 0); - parentPage.progressView.setPaddingTop(t); + if (hasStories) { + parentPage.progressView.setPaddingTop(t - AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP)); + } else { + parentPage.progressView.setPaddingTop(t); + } ignoreLayout = false; } @@ -1734,7 +1982,7 @@ protected void onMeasure(int widthSpec, int heightSpec) { if (parentPage.dialogsType == DIALOGS_TYPE_DEFAULT && hasHiddenArchive()) { ignoreLayout = true; LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager(); - layoutManager.scrollToPositionWithOffset(1, 0); + layoutManager.scrollToPositionWithOffset(1, (int) scrollYOffset); ignoreLayout = false; } firstLayout = false; @@ -1742,7 +1990,7 @@ protected void onMeasure(int widthSpec, int heightSpec) { super.onMeasure(widthSpec, heightSpec); if ((initialDialogsType == 3 && NekoConfig.showTabsOnForward.Bool()) || !onlySelect) { if (appliedPaddingTop != t && viewPages != null && viewPages.length > 1 && !startedTracking && (tabsAnimation == null || !tabsAnimation.isRunning()) && !tabsAnimationInProgress && (filterTabsView == null || !filterTabsView.isAnimatingIndicator())) { - viewPages[1].setTranslationX(viewPages[0].getMeasuredWidth()); +// viewPages[1].setTranslationX(viewPages[0].getMeasuredWidth()); } } } @@ -1753,6 +2001,7 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) { lastListPadding = getPaddingTop(); lastTop = t; scrollAdditionalOffset = 0; + parentPage.pageAdditionalOffset = 0; } @Override @@ -1765,20 +2014,26 @@ public void requestLayout() { private void toggleArchiveHidden(boolean action, DialogCell dialogCell) { SharedConfig.toggleArchiveHidden(); + final UndoView undoView = getUndoView(); if (SharedConfig.archiveHidden) { if (dialogCell != null) { disableActionBarScrolling = true; waitingForScrollFinished = true; - smoothScrollBy(0, (dialogCell.getMeasuredHeight() + (dialogCell.getTop() - getPaddingTop())), CubicBezierInterpolator.EASE_OUT); + int offset = (dialogCell.getMeasuredHeight() + (dialogCell.getTop() - getPaddingTop())); + if (hasStories && !dialogStoriesCell.isExpanded()) { + fixScrollYAfterArchiveOpened = true; + offset += AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP); + } + smoothScrollBy(0, offset, CubicBezierInterpolator.EASE_OUT); if (action) { updatePullAfterScroll = true; } else { updatePullState(); } } - getUndoView().showWithAction(0, UndoView.ACTION_ARCHIVE_HIDDEN, null, null); + undoView.showWithAction(0, UndoView.ACTION_ARCHIVE_HIDDEN, null, null); } else { - getUndoView().showWithAction(0, UndoView.ACTION_ARCHIVE_PINNED, null, null); + undoView.showWithAction(0, UndoView.ACTION_ARCHIVE_PINNED, null, null); updatePullState(); if (action && dialogCell != null) { dialogCell.resetPinnedArchiveState(); @@ -1860,10 +2115,11 @@ public boolean onTouchEvent(MotionEvent e) { int currentPosition = layoutManager.findFirstVisibleItemPosition(); if (currentPosition == 0) { int pTop = getPaddingTop(); - View view = layoutManager.findViewByPosition(currentPosition); - int height = (int) (AndroidUtilities.dp(SharedConfig.useThreeLinesLayout ? 78 : 72) * PullForegroundDrawable.SNAP_HEIGHT); - int diff = (view.getTop() - pTop) + view.getMeasuredHeight(); - if (view instanceof DialogCell) { + DialogCell view = findArchiveDialogCell(parentPage); + if (view != null) { + int height = (int) (AndroidUtilities.dp(SharedConfig.useThreeLinesLayout ? 78 : 72) * PullForegroundDrawable.SNAP_HEIGHT); + int diff = (view.getTop() - pTop) + view.getMeasuredHeight(); + long pullingTime = System.currentTimeMillis() - startArchivePullingTime; if (diff < height || pullingTime < PullForegroundDrawable.minPullingTime) { disableActionBarScrolling = true; @@ -1951,7 +2207,7 @@ public void setOpenRightFragmentProgress(float progress) { invalidate(); } - public void setAnimationSupportView(RecyclerListView animationSupportListView, float scrollOffset, boolean opened) { + public void setAnimationSupportView(RecyclerListView animationSupportListView, float scrollOffset, boolean opened, boolean backward) { RecyclerListView anchorListView = animationSupportListView == null ? this.animationSupportListView : this; DialogCell anchorView = null; DialogCell selectedDialogView = null; @@ -1960,6 +2216,10 @@ public void setAnimationSupportView(RecyclerListView animationSupportListView, f return; } int maxTop = Integer.MAX_VALUE; + int padding = 0;//getPaddingTop(); +// if (hasStories) { +// padding -= AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP); +// } for (int i = 0; i < anchorListView.getChildCount(); i++) { View child = anchorListView.getChildAt(i); if (child instanceof DialogCell) { @@ -1967,7 +2227,7 @@ public void setAnimationSupportView(RecyclerListView animationSupportListView, f if (dialogCell.getDialogId() == rightSlidingDialogContainer.getCurrentFragmetDialogId()) { selectedDialogView = dialogCell; } - if (child.getTop() >= getPaddingTop() && dialogCell.getDialogId() != 0 && child.getTop() < maxTop){ + if (child.getTop() >= padding && dialogCell.getDialogId() != 0 && child.getTop() < maxTop) { anchorView = (DialogCell) child; maxTop = anchorView.getTop(); } @@ -1980,24 +2240,49 @@ public void setAnimationSupportView(RecyclerListView animationSupportListView, f if (anchorView != null) { if (animationSupportListView != null) { + int topPadding = this.topPadding; +// if (hasStories) { +// topPadding -= AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP); +// } animationSupportListView.setPadding(getPaddingLeft(), topPadding, getPaddingLeft(), getPaddingBottom()); if (anchorView != null) { DialogsAdapter adapter = (DialogsAdapter) animationSupportListView.getAdapter(); int p = adapter.findDialogPosition(anchorView.getDialogId()); int offset = (int) (anchorView.getTop() - anchorListView.getPaddingTop() + scrollOffset); +// if (hasStories) { +// offset += AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP); +// } if (p >= 0) { - ((LinearLayoutManager) animationSupportListView.getLayoutManager()).scrollToPositionWithOffset(p, adapter.fixScrollGap(this, p, offset, parentPage.dialogsType == DIALOGS_TYPE_DEFAULT && parentPage.archivePullViewState == ARCHIVE_ITEM_STATE_HIDDEN && hasHiddenArchive(), opened)); + boolean hasArchive = parentPage.dialogsType == DIALOGS_TYPE_DEFAULT && parentPage.archivePullViewState == ARCHIVE_ITEM_STATE_HIDDEN && hasHiddenArchive(); + int fixedOffset = adapter.fixScrollGap(this, p, offset, hasArchive, hasStories, canShowFilterTabsView, opened); + ((LinearLayoutManager) animationSupportListView.getLayoutManager()).scrollToPositionWithOffset(p, fixedOffset); } } } - DialogsAdapter adapter = (DialogsAdapter) getAdapter(); - int p = adapter.findDialogPosition(anchorView.getDialogId()); - int offset = (int) (anchorView.getTop() - getPaddingTop()); - if (p >= 0) { - ((LinearLayoutManager) getLayoutManager()).scrollToPositionWithOffset(p, offset); - } + // if (!backward) { + DialogsAdapter adapter = (DialogsAdapter) getAdapter(); + int p = adapter.findDialogPosition(anchorView.getDialogId()); + int offset = (int) (anchorView.getTop() - getPaddingTop()); + if (backward && hasStories) { + offset += AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP); + } + if (p >= 0) { + ((LinearLayoutManager) getLayoutManager()).scrollToPositionWithOffset(p, offset); + } + // } } } + + @Override + public void updateClip(int[] clip) { + int y = (int) (getPaddingTop() + scrollYOffset); + clip[0] = y; + clip[1] = y + getMeasuredHeight(); + } + } + + private StoriesController getStoriesController() { + return getMessagesController().getStoriesController(); } private class SwipeController extends ItemTouchHelper.Callback { @@ -2040,7 +2325,8 @@ public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder v int currentDialogsType = initialDialogsType; try { currentDialogsType = parentPage.dialogsAdapter.getDialogsType(); - } catch (Exception ignore) {} + } catch (Exception ignore) { + } if ((filterTabsView != null && filterTabsView.getVisibility() == View.VISIBLE && SharedConfig.getChatSwipeAction(currentAccount) == SwipeGestureSettingsView.SWIPE_GESTURE_FOLDERS) || !allowSwipeDuringCurrentTouch || ((dialogId == getUserConfig().clientUserId || dialogId == 777000 || currentDialogsType == 7 || currentDialogsType == 8) && SharedConfig.getChatSwipeAction(currentAccount) == SwipeGestureSettingsView.SWIPE_GESTURE_ARCHIVE) || getMessagesController().isPromoDialog(dialogId, false) && getMessagesController().promoDialogType != MessagesController.PROMO_TYPE_PSA) { return 0; } @@ -2180,36 +2466,39 @@ public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { if (!hintShowed) { preferences.edit().putBoolean("archivehint_l", true).apply(); } - getUndoView().showWithAction(dialog.id, hintShowed ? UndoView.ACTION_ARCHIVE : UndoView.ACTION_ARCHIVE_HINT, null, () -> { - dialogsListFrozen = true; - getMessagesController().addDialogToFolder(dialog.id, 0, pinnedNum, 0); - dialogsListFrozen = false; - ArrayList dialogs = getMessagesController().getDialogs(0); - int index = dialogs.indexOf(dialog); - if (index >= 0) { - ArrayList archivedDialogs = getMessagesController().getDialogs(1); - if (!archivedDialogs.isEmpty() || index != 1) { - setDialogsListFrozen(true); - parentPage.dialogsItemAnimator.prepareForRemove(); - parentPage.updateList(true); - checkAnimationFinished(); - } - if (archivedDialogs.isEmpty()) { - dialogs.remove(0); - if (index == 1) { + final UndoView undoView = getUndoView(); + if (undoView != null) { + undoView.showWithAction(dialog.id, hintShowed ? UndoView.ACTION_ARCHIVE : UndoView.ACTION_ARCHIVE_HINT, null, () -> { + dialogsListFrozen = true; + getMessagesController().addDialogToFolder(dialog.id, 0, pinnedNum, 0); + dialogsListFrozen = false; + ArrayList dialogs = getMessagesController().getDialogs(0); + int index = dialogs.indexOf(dialog); + if (index >= 0) { + ArrayList archivedDialogs = getMessagesController().getDialogs(1); + if (!archivedDialogs.isEmpty() || index != 1) { setDialogsListFrozen(true); - parentPage.updateList(true); - checkAnimationFinished(); - } else { - frozenDialogsList.remove(0); parentPage.dialogsItemAnimator.prepareForRemove(); parentPage.updateList(true); + checkAnimationFinished(); + } + if (archivedDialogs.isEmpty()) { + dialogs.remove(0); + if (index == 1) { + setDialogsListFrozen(true); + parentPage.updateList(true); + checkAnimationFinished(); + } else { + frozenDialogsList.remove(0); + parentPage.dialogsItemAnimator.prepareForRemove(); + parentPage.updateList(true); + } } + } else { + parentPage.updateList(false); } - } else { - parentPage.updateList(false); - } - }); + }); + } } if (folderId != 0 && frozenDialogsList.isEmpty()) { parentPage.listView.setEmptyView(null); @@ -2314,6 +2603,8 @@ public boolean onFragmentCreate() { showSetPasswordConfirm = arguments.getBoolean("showSetPasswordConfirm", showSetPasswordConfirm); otherwiseReloginDays = arguments.getInt("otherwiseRelogin"); allowGroups = arguments.getBoolean("allowGroups", true); + allowMegagroups = arguments.getBoolean("allowMegagroups", true); + allowLegacyGroups = arguments.getBoolean("allowLegacyGroups", true); allowChannels = arguments.getBoolean("allowChannels", true); allowUsers = arguments.getBoolean("allowUsers", true); allowBots = arguments.getBoolean("allowBots", true); @@ -2326,7 +2617,8 @@ public boolean onFragmentCreate() { SerializedData buffer = new SerializedData(requestPeerTypeBytes); requestPeerType = TLRPC.RequestPeerType.TLdeserialize(buffer, buffer.readInt32(true), true); buffer.cleanup(); - } catch (Exception e) {} + } catch (Exception e) { + } } requestPeerBotId = arguments.getLong("requestPeerBotId", 0); } @@ -2381,8 +2673,16 @@ public boolean onFragmentCreate() { getNotificationCenter().addObserver(this, NotificationCenter.onDatabaseOpened); getNotificationCenter().addObserver(this, NotificationCenter.didClearDatabase); getNotificationCenter().addObserver(this, NotificationCenter.onDatabaseReset); + getNotificationCenter().addObserver(this, NotificationCenter.storiesUpdated); + getNotificationCenter().addObserver(this, NotificationCenter.storiesEnabledUpdate); + + if (initialDialogsType == DIALOGS_TYPE_DEFAULT) { + getNotificationCenter().addObserver(this, NotificationCenter.chatlistFolderUpdate); + getNotificationCenter().addObserver(this, NotificationCenter.dialogTranslate); + } loadDialogs(getAccountInstance()); + getMessagesController().getStoriesController().loadAllStories(); getMessagesController().loadPinnedDialogs(folderId, 0, null); if (databaseMigrationHint != null && !getMessagesStorage().isDatabaseMigrationInProgress()) { View localView = databaseMigrationHint; @@ -2391,6 +2691,13 @@ public boolean onFragmentCreate() { } databaseMigrationHint = null; } + if (isArchive()) { + getMessagesController().getStoriesController().loadHiddenStories(); + } else { + getMessagesController().getStoriesController().loadStories(); + } + + getContactsController().loadGlobalPrivacySetting(); return true; } @@ -2415,6 +2722,7 @@ public static void loadDialogs(AccountInstance accountInstance) { private Drawable premiumStar; + public void updateStatus(TLRPC.User user, boolean animated) { if (statusDrawable == null || actionBar == null) { return; @@ -2502,13 +2810,21 @@ public void onFragmentDestroy() { getNotificationCenter().removeObserver(this, NotificationCenter.onDatabaseOpened); getNotificationCenter().removeObserver(this, NotificationCenter.didClearDatabase); getNotificationCenter().removeObserver(this, NotificationCenter.onDatabaseReset); + getNotificationCenter().removeObserver(this, NotificationCenter.storiesUpdated); + getNotificationCenter().removeObserver(this, NotificationCenter.storiesEnabledUpdate); + + if (initialDialogsType == DIALOGS_TYPE_DEFAULT) { + getNotificationCenter().removeObserver(this, NotificationCenter.chatlistFolderUpdate); + getNotificationCenter().removeObserver(this, NotificationCenter.dialogTranslate); + } + if (commentView != null) { commentView.onDestroy(); } if (undoView[0] != null) { undoView[0].hide(true, 0); } - getNotificationCenter().onAnimationFinish(animationIndex); + notificationsLocker.unlock(); delegate = null; SuggestClearDatabaseBottomSheet.dismissDialog(); } @@ -2545,6 +2861,9 @@ public void setTitleOverlayText(String title, int titleId, Runnable action) { SimpleTextView textView = getTitleTextView(); ((SelectAnimatedEmojiDialog) selectAnimatedEmojiDialog.getContentView()).setScrimDrawable(textView != null && textView.getRightDrawable() == statusDrawable ? statusDrawable : null, textView); } + if (dialogStoriesCell != null) { + dialogStoriesCell.setTitleOverlayText(title, titleId); + } } @Override @@ -2560,6 +2879,7 @@ public void onSearchFieldVisibilityChanged(boolean visible) { super.onSearchFieldVisibilityChanged(visible); } }; + actionBar.setUseContainerForTitles(); actionBar.setItemsBackgroundColor(Theme.getColor(Theme.key_actionBarDefaultSelector), false); actionBar.setItemsBackgroundColor(Theme.getColor(Theme.key_actionBarActionModeDefaultSelector), true); actionBar.setItemsColor(Theme.getColor(Theme.key_actionBarDefaultIcon), false); @@ -2576,6 +2896,7 @@ public void onSearchFieldVisibilityChanged(boolean visible) { public View createView(final Context context) { searching = false; searchWas = false; + wasDrawn = false; pacmanAnimation = null; selectedDialogs.clear(); @@ -2665,14 +2986,28 @@ public void onSearchExpand() { } if (!onlySelect) { floatingButtonContainer.setVisibility(View.GONE); + if (floatingButton2Container != null) { + floatingButton2Container.setVisibility(View.GONE); + } + if (storyHint != null) { + storyHint.hide(); + } } } - setScrollY(0); + if (dialogStoriesCell != null && dialogStoriesCell.getPremiumHint() != null) { + dialogStoriesCell.getPremiumHint().hide(); + } + if (!hasStories) { + setScrollY(0); + } updatePasscodeButton(); updateProxyButton(false, false); actionBar.setBackButtonContentDescription(LocaleController.getString("AccDescrGoBack", R.string.AccDescrGoBack)); NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.needCheckSystemBarColors); ((SizeNotifierFrameLayout) fragmentView).invalidateBlur(); + if (optionsItem != null) { + optionsItem.setVisibility(View.GONE); + } } @Override @@ -2704,6 +3039,9 @@ public void onSearchCollapse() { viewPages[0].listView.setEmptyView(folderId == 0 ? viewPages[0].progressView : null); if (!onlySelect) { floatingButtonContainer.setVisibility(View.VISIBLE); + if (floatingButton2Container != null) { + floatingButton2Container.setVisibility(storiesEnabled ? View.VISIBLE : View.GONE); + } floatingHidden = true; floatingButtonTranslation = AndroidUtilities.dp(100); floatingButtonHideProgress = 1f; @@ -2722,12 +3060,15 @@ public void onSearchCollapse() { } NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.needCheckSystemBarColors, true); ((SizeNotifierFrameLayout) fragmentView).invalidateBlur(); + if (optionsItem != null) { + optionsItem.setVisibility(View.VISIBLE); + } } @Override public void onTextChanged(EditText editText) { String text = editText.getText().toString(); - if (text.length() != 0 || (searchViewPager.dialogsSearchAdapter != null && searchViewPager.dialogsSearchAdapter.hasRecentSearch()) || searchFiltersWasShowed) { + if (text.length() != 0 || (searchViewPager.dialogsSearchAdapter != null && searchViewPager.dialogsSearchAdapter.hasRecentSearch()) || searchFiltersWasShowed || hasStories) { searchWas = true; if (!searchIsShowed) { showSearch(true, false, true); @@ -2752,10 +3093,18 @@ public boolean canToggleSearch() { return !actionBar.isActionModeShowed() && databaseMigrationHint == null;// && !rightSlidingDialogContainer.hasFragment(); } }); - - if (initialDialogsType == DIALOGS_TYPE_ADD_USERS_TO || initialDialogsType == DIALOGS_TYPE_START_ATTACH_BOT) { + if (initialDialogsType == DIALOGS_TYPE_ADD_USERS_TO || initialDialogsType == DIALOGS_TYPE_START_ATTACH_BOT || isArchive() && getDialogsArray(currentAccount, initialDialogsType, folderId, false).isEmpty()) { searchItem.setVisibility(View.GONE); } + if (isArchive()) { + optionsItem = menu.addItem(4, R.drawable.ic_ab_other); + optionsItem.addSubItem(5, R.drawable.msg_customize, LocaleController.getString("ArchiveSettings")).setColors(getThemedColor(Theme.key_actionBarDefaultSubmenuItem), getThemedColor(Theme.key_actionBarDefaultSubmenuItem)); + optionsItem.addSubItem(6, R.drawable.msg_help, LocaleController.getString("HowDoesItWork")).setColors(getThemedColor(Theme.key_actionBarDefaultSubmenuItem), getThemedColor(Theme.key_actionBarDefaultSubmenuItem)); + optionsItem.setOnClickListener(v -> { + getContactsController().loadGlobalPrivacySetting(); + optionsItem.toggleSubMenu(); + }); + } searchItem.setSearchFieldHint(LocaleController.getString("Search", R.string.Search)); searchItem.setContentDescription(LocaleController.getString("Search", R.string.Search)); if (onlySelect) { @@ -2764,6 +3113,18 @@ public boolean canToggleSearch() { actionBar.setTitle(LocaleController.getString("ForwardTo", R.string.ForwardTo)); } else if (initialDialogsType == DIALOGS_TYPE_WIDGET) { actionBar.setTitle(LocaleController.getString("SelectChats", R.string.SelectChats)); + } else if (initialDialogsType == DIALOGS_TYPE_START_ATTACH_BOT) { + if (allowBots && !allowUsers && !allowGroups && !allowChannels) { + actionBar.setTitle(LocaleController.getString("ChooseBot", R.string.ChooseBot)); + } else if (allowUsers && !allowBots && !allowGroups && !allowChannels) { + actionBar.setTitle(LocaleController.getString("ChooseUser", R.string.ChooseUser)); + } else if (allowGroups && !allowUsers && !allowBots && !allowChannels) { + actionBar.setTitle(LocaleController.getString("ChooseGroup", R.string.ChooseGroup)); + } else if (allowChannels && !allowUsers && !allowBots && !allowGroups) { + actionBar.setTitle(LocaleController.getString("ChooseChannel", R.string.ChooseChannel)); + } else { + actionBar.setTitle(LocaleController.getString("SelectChat", R.string.SelectChat)); + } } else if (requestPeerType instanceof TLRPC.TL_requestPeerTypeUser) { if (((TLRPC.TL_requestPeerTypeUser) requestPeerType).bot != null) { if (((TLRPC.TL_requestPeerTypeUser) requestPeerType).bot) { @@ -2816,29 +3177,27 @@ public boolean canToggleSearch() { actionBar.setSupportsHolidayImage(true); } } - if ((initialDialogsType == 3 && NekoConfig.showTabsOnForward.Bool()) || !onlySelect) { + + if (!onlySelect || (initialDialogsType == DIALOGS_TYPE_FORWARD && NekoConfig.showTabsOnForward.Bool())) { actionBar.setAddToContainer(false); actionBar.setCastShadows(false); actionBar.setClipContent(true); } actionBar.setTitleActionRunnable(() -> { - if (initialDialogsType != 10) { + if (initialDialogsType != DIALOGS_TYPE_WIDGET) { hideFloatingButton(false); } - scrollToTop(); + if (hasOnlySlefStories && getStoriesController().hasOnlySelfStories()) { + dialogStoriesCell.openSelfStories(); + } else { + scrollToTop(true, true); + } }); - if ((initialDialogsType == DIALOGS_TYPE_FORWARD && NekoConfig.showTabsOnForward.Bool()) || initialDialogsType == DIALOGS_TYPE_DEFAULT && folderId == 0 && !onlySelect && TextUtils.isEmpty(searchString)) { - scrimPaint = new Paint() { - @Override - public void setAlpha(int a) { - super.setAlpha(a); - if (fragmentView != null) { - fragmentView.invalidate(); - } - } - }; - + if ( + (initialDialogsType == DIALOGS_TYPE_DEFAULT && !onlySelect || initialDialogsType == DIALOGS_TYPE_FORWARD) && + folderId == 0 && TextUtils.isEmpty(searchString) + ) { filterTabsView = new FilterTabsView(context) { @Override public boolean onInterceptTouchEvent(MotionEvent ev) { @@ -2858,22 +3217,14 @@ public void setTranslationY(float translationY) { } } - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - super.onLayout(changed, l, t, r, b); - if (scrimView != null) { - scrimView.getLocationInWindow(scrimViewLocation); - fragmentView.invalidate(); - } - } - @Override protected void onDefaultTabMoved() { if (!getMessagesController().premiumLocked) { try { if (!NekoConfig.disableVibration.Bool()) performHapticFeedback(HapticFeedbackConstants.KEYBOARD_PRESS, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); - } catch (Exception ignore) {} + } catch (Exception ignore) { + } topBulletin = BulletinFactory.of(DialogsActivity.this).createSimpleBulletin(R.raw.filter_reorder, AndroidUtilities.replaceTags(LocaleController.formatString("LimitReachedReorderFolder", R.string.LimitReachedReorderFolder, LocaleController.getString(R.string.FilterAllChats))), LocaleController.getString("PremiumMore", R.string.PremiumMore), Bulletin.DURATION_PROLONG, () -> { showDialog(new PremiumFeatureBottomSheet(DialogsActivity.this, PremiumPreviewFragment.PREMIUM_FEATURE_ADVANCED_CHAT_MANAGEMENT, true)); filterTabsView.setIsEditing(false); @@ -2888,30 +3239,32 @@ protected void onDefaultTabMoved() { filterTabsView.setDelegate(new FilterTabsView.FilterTabsViewDelegate() { private void showDeleteAlert(MessagesController.DialogFilter dialogFilter) { - AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); - builder.setTitle(LocaleController.getString("FilterDelete", R.string.FilterDelete)); - builder.setMessage(LocaleController.getString("FilterDeleteAlert", R.string.FilterDeleteAlert)); - builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - builder.setPositiveButton(LocaleController.getString("Delete", R.string.Delete), (dialog2, which2) -> { - TLRPC.TL_messages_updateDialogFilter req = new TLRPC.TL_messages_updateDialogFilter(); - req.id = dialogFilter.id; - getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { - - })); - getMessagesController().removeFilter(dialogFilter); - getMessagesStorage().deleteDialogFilter(dialogFilter); - }); - AlertDialog alertDialog = builder.create(); - showDialog(alertDialog); - TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); - if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + if (dialogFilter.isChatlist()) { + FolderBottomSheet.showForDeletion(DialogsActivity.this, dialogFilter.id, null); + } else { + AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); + builder.setTitle(LocaleController.getString("FilterDelete", R.string.FilterDelete)); + builder.setMessage(LocaleController.getString("FilterDeleteAlert", R.string.FilterDeleteAlert)); + builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + builder.setPositiveButton(LocaleController.getString("Delete", R.string.Delete), (dialog2, which2) -> { + TLRPC.TL_messages_updateDialogFilter req = new TLRPC.TL_messages_updateDialogFilter(); + req.id = dialogFilter.id; + getConnectionsManager().sendRequest(req, null); + getMessagesController().removeFilter(dialogFilter); + getMessagesStorage().deleteDialogFilter(dialogFilter); + }); + AlertDialog alertDialog = builder.create(); + showDialog(alertDialog); + TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); + if (button != null) { + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); + } } } @Override public void onSamePageSelected() { - scrollToTop(); + scrollToTop(true, false); } @Override @@ -2936,7 +3289,7 @@ public void onPageSelected(FilterTabsView.Tab tab, boolean forward) { return; } - ArrayList dialogFilters = getMessagesController().dialogFilters; + ArrayList dialogFilters = getMessagesController().getDialogFilters(); if (!tab.isDefault && (tab.id < 0 || tab.id >= dialogFilters.size())) { return; } @@ -2975,6 +3328,7 @@ public void onPageScrolled(float progress) { viewPages[1].setVisibility(View.GONE); showScrollbars(true); updateCounters(false); + filterTabsView.stopAnimatingIndicator(); checkListLoad(viewPages[0]); viewPages[0].dialogsAdapter.resume(); viewPages[1].dialogsAdapter.pause(); @@ -2986,238 +3340,143 @@ public int getTabCounter(int tabId) { if (NaConfig.INSTANCE.getIgnoreFolderCount().Bool()) { return 0; } + + if (initialDialogsType == DIALOGS_TYPE_FORWARD) { + return 0; + } if (tabId == filterTabsView.getDefaultTabId()) { return getMessagesStorage().getMainUnreadCount(); } - ArrayList dialogFilters = getMessagesController().dialogFilters; + ArrayList dialogFilters = getMessagesController().getDialogFilters(); if (tabId < 0 || tabId >= dialogFilters.size()) { return 0; } - return dialogFilters.get(tabId).unreadCount; + return getMessagesController().getDialogFilters().get(tabId).unreadCount; } @Override public boolean didSelectTab(FilterTabsView.TabView tabView, boolean selected) { - if (actionBar.isActionModeShowed()) { + if (initialDialogsType != DIALOGS_TYPE_DEFAULT) { return false; } - if (scrimPopupWindow != null) { - scrimPopupWindow.dismiss(); - scrimPopupWindow = null; - scrimPopupWindowItems = null; + if (actionBar.isActionModeShowed() || storiesOverscroll != 0) { + return false; + } + if (filterOptions != null && filterOptions.isShown()) { + filterOptions.dismiss(); + filterOptions = null; return false; } - Rect rect = new Rect(); - MessagesController.DialogFilter dialogFilter; + final MessagesController.DialogFilter dialogFilter; if (tabView.getId() == filterTabsView.getDefaultTabId()) { dialogFilter = null; } else { - dialogFilter = getMessagesController().dialogFilters.get(tabView.getId()); + dialogFilter = getMessagesController().getDialogFilters().get(tabView.getId()); } - ActionBarPopupWindow.ActionBarPopupWindowLayout popupLayout = new ActionBarPopupWindow.ActionBarPopupWindowLayout(getParentActivity()); - popupLayout.setOnTouchListener(new View.OnTouchListener() { + boolean defaultTab = dialogFilter == null; + boolean hasUnread = false, hasShare = false; + boolean muteAll = false; + boolean[] shareEmpty = new boolean[1]; + shareEmpty[0] = true; - private int[] pos = new int[2]; - - @Override - public boolean onTouch(View v, MotionEvent event) { - if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { - if (scrimPopupWindow != null && scrimPopupWindow.isShowing()) { - View contentView = scrimPopupWindow.getContentView(); - contentView.getLocationInWindow(pos); - rect.set(pos[0], pos[1], pos[0] + contentView.getMeasuredWidth(), pos[1] + contentView.getMeasuredHeight()); - if (!rect.contains((int) event.getX(), (int) event.getY())) { - scrimPopupWindow.dismiss(); - } + ArrayList dialogs = new ArrayList<>(defaultTab ? getMessagesController().getDialogs(folderId) : getMessagesController().getAllDialogs()); + MessagesController.DialogFilter filter = null; + if (dialogFilter != null) { + filter = getMessagesController().getDialogFilters().get(tabView.getId()); + if (filter != null) { + for (int i = 0; i < dialogs.size(); i++) { + if (!filter.includesDialog(getAccountInstance(), dialogs.get(i).id)) { + dialogs.remove(i); + i--; } - } else if (event.getActionMasked() == MotionEvent.ACTION_OUTSIDE) { - if (scrimPopupWindow != null && scrimPopupWindow.isShowing()) { - scrimPopupWindow.dismiss(); + } + hasShare = filter.isChatlist() || filter.neverShow.isEmpty() && (filter.flags & ~(MessagesController.DIALOG_FILTER_FLAG_CHATLIST | MessagesController.DIALOG_FILTER_FLAG_CHATLIST_ADMIN)) == 0; + if (hasShare) { + for (int i = 0; i < filter.alwaysShow.size(); ++i) { + long did = filter.alwaysShow.get(i); + if (did < 0) { + TLRPC.Chat chat = getMessagesController().getChat(-did); + if (chat != null && FilterCreateActivity.canAddToFolder(chat)) { + shareEmpty[0] = false; + break; + } + } } } - return false; } - }); - popupLayout.setDispatchKeyEventListener(keyEvent -> { - if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_BACK && keyEvent.getRepeatCount() == 0 && scrimPopupWindow != null && scrimPopupWindow.isShowing()) { - scrimPopupWindow.dismiss(); - } - }); - Rect backgroundPaddings = new Rect(); - Drawable shadowDrawable = getParentActivity().getResources().getDrawable(R.drawable.popup_fixed_alert).mutate(); - shadowDrawable.getPadding(backgroundPaddings); - popupLayout.setBackgroundDrawable(shadowDrawable); - popupLayout.setBackgroundColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuBackground)); - - LinearLayout linearLayout = new LinearLayout(getParentActivity()); - ScrollView scrollView; - if (Build.VERSION.SDK_INT >= 21) { - scrollView = new ScrollView(getParentActivity(), null, 0, R.style.scrollbarShapeStyle) { - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - setMeasuredDimension(linearLayout.getMeasuredWidth(), getMeasuredHeight()); - } - }; - } else { - scrollView = new ScrollView(getParentActivity()); - } - scrollView.setClipToPadding(false); - popupLayout.addView(scrollView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT)); - - linearLayout.setMinimumWidth(AndroidUtilities.dp(200)); - linearLayout.setOrientation(LinearLayout.VERTICAL); - scrimPopupWindowItems = new ActionBarMenuSubItem[4]; - - boolean defaultTab = tabView.getId() == filterTabsView.getDefaultTabId(); - boolean hasUnread = false; - - - ArrayList dialogs = new ArrayList<>(defaultTab ? getMessagesController().getDialogs(folderId) : getMessagesController().getAllDialogs()); - if (!defaultTab) { - MessagesController.DialogFilter filter = getMessagesController().dialogFilters.get(tabView.getId()); - for (int i = 0; i < dialogs.size(); i++) { - if (!filter.includesDialog(getAccountInstance(), dialogs.get(i).id)) { - dialogs.remove(i); - i--; + if (!dialogs.isEmpty()) { + boolean allAreMuted = true; + for (int i = 0; i < dialogs.size(); ++i) { + TLRPC.Dialog dialog = dialogs.get(i); + if (!getMessagesController().isDialogMuted(dialog.id, 0)) { + allAreMuted = false; + break; + } } + muteAll = !allAreMuted; } } + final boolean finalMuteAll = muteAll; + + final MessagesController.DialogFilter finalFilter = filter; for (int i = 0; i < dialogs.size(); i++) { if (dialogs.get(i).unread_mark || dialogs.get(i).unread_count > 0) { hasUnread = true; } } - for (int a = 0, N = 4; a < N; a++) { - ActionBarMenuSubItem cell = new ActionBarMenuSubItem(getParentActivity(), a == 0, a == N - 1); - if (a == 0) { - if (getMessagesController().dialogFilters.size() <= 1) continue; - cell.setTextAndIcon(LocaleController.getString("FilterReorder", R.string.FilterReorder), R.drawable.tabs_reorder); - } else if (a == 1) { - if (defaultTab) { - cell.setTextAndIcon(LocaleController.getString("FilterEditAll", R.string.FilterEditAll), R.drawable.msg_edit); - } else { - cell.setTextAndIcon(LocaleController.getString("FilterEdit", R.string.FilterEdit), R.drawable.msg_edit); - } - } else if (a == 2) { - if (!hasUnread) continue; - cell.setTextAndIcon(LocaleController.getString("MarkAllAsRead", R.string.MarkAllAsRead), R.drawable.msg_markread); - } else { - if (defaultTab) continue; - cell.setTextAndIcon(LocaleController.getString("FilterDeleteItem", R.string.FilterDeleteItem), R.drawable.msg_delete); - } - scrimPopupWindowItems[a] = cell; - linearLayout.addView(cell); - final int i = a; - boolean finalHasUnread = hasUnread; - cell.setOnClickListener(v1 -> { - if (i == 0) { + + filterOptions = ItemOptions.makeOptions(DialogsActivity.this, tabView) + .setScrimViewBackground(Theme.createRoundRectDrawable(AndroidUtilities.dp(6), 0, Theme.getColor(Theme.key_actionBarDefault))) + .addIf(getMessagesController().getDialogFilters().size() > 1, R.drawable.tabs_reorder, LocaleController.getString("FilterReorder", R.string.FilterReorder), () -> { resetScroll(); filterTabsView.setIsEditing(true); showDoneItem(true); - } else if (i == 1) { - if (defaultTab) { - presentFragment(new FiltersSetupActivity()); + }) + .add(R.drawable.msg_edit, defaultTab ? LocaleController.getString("FilterEditAll", R.string.FilterEditAll) : LocaleController.getString("FilterEdit", R.string.FilterEdit), () -> { + presentFragment(defaultTab ? new FiltersSetupActivity() : new FilterCreateActivity(dialogFilter)); + }) + .addIf(dialogFilter != null && !dialogs.isEmpty(), muteAll ? R.drawable.msg_mute : R.drawable.msg_unmute, muteAll ? LocaleController.getString("FilterMuteAll", R.string.FilterMuteAll) : LocaleController.getString("FilterUnmuteAll", R.string.FilterUnmuteAll), () -> { + int count = 0; + for (int i = 0; i < dialogs.size(); ++i) { + TLRPC.Dialog dialog = dialogs.get(i); + if (dialog != null) { + getNotificationsController().setDialogNotificationsSettings(dialog.id, 0, finalMuteAll ? NotificationsController.SETTING_MUTE_FOREVER : NotificationsController.SETTING_MUTE_UNMUTE); + count++; + } + } + BulletinFactory.createMuteBulletin(DialogsActivity.this, finalMuteAll, count, null).show(); + }) + .addIf(hasUnread, R.drawable.msg_markread, LocaleController.getString("MarkAllAsRead", R.string.MarkAllAsRead), () -> { + markDialogsAsRead(dialogs); + }) + .addIf(hasShare, R.drawable.msg_share, FilterCreateActivity.withNew(filter != null && filter.isMyChatlist() ? -1 : 0, LocaleController.getString("LinkActionShare", R.string.LinkActionShare), true), () -> { + if (shareEmpty[0]) { + presentFragment(new FilterChatlistActivity(finalFilter, null)); } else { - presentFragment(new FilterCreateActivity(dialogFilter)); + FilterCreateActivity.FilterInvitesBottomSheet.show(DialogsActivity.this, finalFilter, null); } - } else if (i == 2) { - if (finalHasUnread) - markDialogsAsRead(dialogs); - } else { + }) + .addIf(!defaultTab, R.drawable.msg_delete, LocaleController.getString("FilterDeleteItem", R.string.FilterDeleteItem), true, () -> { showDeleteAlert(dialogFilter); - } - if (scrimPopupWindow != null) { - scrimPopupWindow.dismiss(); - } - }); - } - scrollView.addView(linearLayout, LayoutHelper.createScroll(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP)); - scrimPopupWindow = new ActionBarPopupWindow(popupLayout, LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT) { - @Override - public void dismiss() { - super.dismiss(); - if (scrimPopupWindow != this) { - return; - } - scrimPopupWindow = null; - scrimPopupWindowItems = null; - if (scrimAnimatorSet != null) { - scrimAnimatorSet.cancel(); - scrimAnimatorSet = null; - } - scrimAnimatorSet = new AnimatorSet(); - scrimViewAppearing = false; - ArrayList animators = new ArrayList<>(); - animators.add(ObjectAnimator.ofInt(scrimPaint, AnimationProperties.PAINT_ALPHA, 0)); - scrimAnimatorSet.playTogether(animators); - scrimAnimatorSet.setDuration(220); - scrimAnimatorSet.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (scrimView != null) { - scrimView.setBackground(null); - scrimView = null; - } - if (fragmentView != null) { - fragmentView.invalidate(); - } - } - }); - scrimAnimatorSet.start(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - getParentActivity().getWindow().getDecorView().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO); - } - } - }; - scrimViewBackground = Theme.createRoundRectDrawable(AndroidUtilities.dp(6), 0, Theme.getColor(Theme.key_actionBarDefault)); - scrimPopupWindow.setDismissAnimationDuration(220); - scrimPopupWindow.setOutsideTouchable(true); - scrimPopupWindow.setClippingEnabled(true); - scrimPopupWindow.setAnimationStyle(R.style.PopupContextAnimation); - scrimPopupWindow.setFocusable(true); - popupLayout.measure(View.MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(1000), View.MeasureSpec.AT_MOST), View.MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(1000), View.MeasureSpec.AT_MOST)); - scrimPopupWindow.setInputMethodMode(ActionBarPopupWindow.INPUT_METHOD_NOT_NEEDED); - scrimPopupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED); - scrimPopupWindow.getContentView().setFocusableInTouchMode(true); - tabView.getLocationInWindow(scrimViewLocation); - int popupX = scrimViewLocation[0] + backgroundPaddings.left - AndroidUtilities.dp(16); - if (popupX < AndroidUtilities.dp(6)) { - popupX = AndroidUtilities.dp(6); - } else if (popupX > fragmentView.getMeasuredWidth() - AndroidUtilities.dp(6) - popupLayout.getMeasuredWidth()) { - popupX = fragmentView.getMeasuredWidth() - AndroidUtilities.dp(6) - popupLayout.getMeasuredWidth(); - } - int popupY = scrimViewLocation[1] + tabView.getMeasuredHeight() - AndroidUtilities.dp(12); - - scrimPopupWindow.showAtLocation(fragmentView, Gravity.LEFT | Gravity.TOP, popupX, popupY); - scrimView = tabView; - scrimViewSelected = selected; - fragmentView.invalidate(); - if (scrimAnimatorSet != null) { - scrimAnimatorSet.cancel(); - } - scrimAnimatorSet = new AnimatorSet(); - scrimViewAppearing = true; - ArrayList animators = new ArrayList<>(); - animators.add(ObjectAnimator.ofInt(scrimPaint, AnimationProperties.PAINT_ALPHA, 0, 50)); - scrimAnimatorSet.playTogether(animators); - scrimAnimatorSet.setDuration(150); - scrimAnimatorSet.start(); + }) + .setGravity(Gravity.LEFT) + .translate(AndroidUtilities.dp(-8), AndroidUtilities.dp(-10)) + .show(); return true; } @Override public boolean isTabMenuVisible() { - return scrimPopupWindow != null && scrimPopupWindow.isShowing(); + return filterOptions != null && filterOptions.isShown(); } @Override public void onDeletePressed(int id) { - showDeleteAlert(getMessagesController().dialogFilters.get(id)); + showDeleteAlert(getMessagesController().getDialogFilters().get(id)); } }); } @@ -3263,7 +3522,7 @@ public void onDeletePressed(int id) { ContentView contentView = new ContentView(context); fragmentView = contentView; - int pagesCount = (initialDialogsType == 3 && NekoConfig.showTabsOnForward.Bool()) || (folderId == 0 && initialDialogsType == DIALOGS_TYPE_DEFAULT && !onlySelect) ? 2 : 1; + int pagesCount = folderId == 0 && (initialDialogsType == DIALOGS_TYPE_DEFAULT && !onlySelect || (initialDialogsType == DIALOGS_TYPE_FORWARD && NekoConfig.showTabsOnForward.Bool())) ? 2 : 1; viewPages = new ViewPage[pagesCount]; for (int a = 0; a < pagesCount; a++) { final ViewPage viewPage = new ViewPage(context) { @@ -3291,6 +3550,7 @@ public void setTranslationX(float translationX) { viewPage.addView(viewPage.progressView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); viewPage.listView = new DialogsRecyclerView(context, viewPage); + viewPage.scroller = new RecyclerListViewScroller(viewPage.listView); viewPage.listView.setAllowStopHeaveOperations(true); viewPage.listView.setAccessibilityEnabled(false); viewPage.listView.setAnimateEmptyView(true, RecyclerListView.EMPTY_VIEW_ANIMATION_TYPE_ALPHA); @@ -3357,16 +3617,66 @@ public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State } } + boolean lastDragging; + + ValueAnimator storiesOverscrollAnimator; + @Override + public void onScrollStateChanged(int state) { + super.onScrollStateChanged(state); + if (storiesOverscrollAnimator != null) { + storiesOverscrollAnimator.removeAllListeners(); + storiesOverscrollAnimator.cancel(); + } + if (viewPage.listView.getScrollState() != RecyclerView.SCROLL_STATE_DRAGGING) { + storiesOverscrollAnimator = ValueAnimator.ofFloat(storiesOverscroll, 0); + storiesOverscrollAnimator.addUpdateListener(animation -> { + setStoriesOvercroll(viewPage, (float) animation.getAnimatedValue()); + }); + storiesOverscrollAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + setStoriesOvercroll(viewPage, 0); + } + }); + storiesOverscrollAnimator.setDuration(200); + storiesOverscrollAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); + storiesOverscrollAnimator.start(); + } + } + @Override public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) { if (viewPage.listView.fastScrollAnimationRunning) { return 0; } boolean isDragging = viewPage.listView.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING; - + if (isDragging != lastDragging) { + lastDragging = isDragging; + if (!isDragging) { + if (checkAutoscrollToStories(viewPage)) { + return 0; + } + } + } int measuredDy = dy; + if (dy > 0 && storiesOverscroll != 0) { + float newOverscroll = storiesOverscroll - dy; + if (newOverscroll < 0) { + measuredDy = (int) -newOverscroll; + newOverscroll = 0; + } else { + measuredDy = 0; + } + setStoriesOvercroll(viewPage, newOverscroll); + return super.scrollVerticallyBy(measuredDy, recycler, state); + } int pTop = viewPage.listView.getPaddingTop(); - if (viewPage.dialogsType == DIALOGS_TYPE_DEFAULT && !onlySelect && folderId == 0 && dy < 0 && getMessagesController().hasHiddenArchive() && viewPage.archivePullViewState == ARCHIVE_ITEM_STATE_HIDDEN) { + int realTopPadding = pTop; + if (hasStories && !rightSlidingDialogContainer.hasFragment() && !fixScrollYAfterArchiveOpened) { + pTop -= AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP); + } + boolean hasHidenArchive = !fixScrollYAfterArchiveOpened && viewPage.dialogsType == DIALOGS_TYPE_DEFAULT && !onlySelect && folderId == 0 && getMessagesController().hasHiddenArchive() && viewPage.archivePullViewState == ARCHIVE_ITEM_STATE_HIDDEN; + if ((hasHidenArchive || (hasStories && !rightSlidingDialogContainer.hasFragment())) && dy < 0) { viewPage.listView.setOverScrollMode(View.OVER_SCROLL_ALWAYS); int currentPosition = viewPage.layoutManager.findFirstVisibleItemPosition(); if (currentPosition == 0) { @@ -3377,17 +3687,24 @@ public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerVi } if (!isDragging) { View view = viewPage.layoutManager.findViewByPosition(currentPosition); - if (view != null) { + if (view != null && currentPosition < 10) { int dialogHeight = AndroidUtilities.dp(SharedConfig.useThreeLinesLayout ? 78 : 72) + 1; - int canScrollDy = -(view.getTop() - pTop) + (currentPosition - 1) * dialogHeight; + int viewsH = 0; + for (int i = hasHidenArchive ? 1 : 0; i < currentPosition; i++) { + viewsH += viewPage.dialogsAdapter.getItemHeight(i); + } + int canScrollDy = -(view.getTop() - pTop) + viewsH; + if (hasStories && (viewPage.scroller.isRunning() || dialogStoriesCell.isExpanded()) && !rightSlidingDialogContainer.hasFragment() && !fixScrollYAfterArchiveOpened) { + canScrollDy += AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP); + } int positiveDy = Math.abs(dy); if (canScrollDy < positiveDy) { measuredDy = -canScrollDy; } } - } else if (currentPosition == 0) { + } else if (currentPosition == 0 && hasHidenArchive) { View v = viewPage.layoutManager.findViewByPosition(currentPosition); - float k = 1f + ((v.getTop() - pTop) / (float) v.getMeasuredHeight()); + float k = 1f + ((v.getTop() - realTopPadding) / (float) v.getMeasuredHeight()); if (k > 1f) { k = 1f; } @@ -3396,10 +3713,20 @@ public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerVi if (measuredDy > -1) { measuredDy = -1; } - if (undoView[0].getVisibility() == View.VISIBLE) { + if (undoView[0] != null && undoView[0].getVisibility() == View.VISIBLE) { undoView[0].hide(true, 1); } - } + } else if (((currentPosition == 1 && hasHidenArchive) || currentPosition == 0) && hasStories && isDragging && !rightSlidingDialogContainer.hasFragment()) { + if (scrollYOffset == 0) { + viewPage.listView.setOverScrollMode(View.OVER_SCROLL_ALWAYS); + } else { + viewPage.listView.setOverScrollMode(View.OVER_SCROLL_NEVER); + } + measuredDy *= 0.3f; + if (measuredDy > -1) { + measuredDy = -1; + } + } } if (viewPage.dialogsType == 0 && viewPage.listView.getViewOffset() != 0 && dy > 0 && isDragging) { @@ -3414,7 +3741,7 @@ public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerVi viewPage.listView.setViewsOffset(ty); } - if (viewPage.dialogsType == DIALOGS_TYPE_DEFAULT && viewPage.archivePullViewState != ARCHIVE_ITEM_STATE_PINNED && hasHiddenArchive()) { + if (viewPage.dialogsType == DIALOGS_TYPE_DEFAULT && viewPage.archivePullViewState != ARCHIVE_ITEM_STATE_PINNED && hasHiddenArchive() && !fixScrollYAfterArchiveOpened) { int usedDy = super.scrollVerticallyBy(measuredDy, recycler, state); if (viewPage.pullForegroundDrawable != null) { viewPage.pullForegroundDrawable.scrollDy = usedDy; @@ -3433,6 +3760,9 @@ public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerVi viewPage.pullForegroundDrawable.showHidden(); } } + if (hasStories && !rightSlidingDialogContainer.hasFragment() && !fixScrollYAfterArchiveOpened) { + pTop += AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP); + } float k = 1f + ((firstView.getTop() - pTop) / (float) firstView.getMeasuredHeight()); if (k > 1f) { k = 1f; @@ -3478,9 +3808,19 @@ public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerVi if (firstView != null) { firstView.invalidate(); } + if (viewPage.archivePullViewState == ARCHIVE_ITEM_STATE_SHOWED && usedDy == 0 && dy < 0 && isDragging && !rightSlidingDialogContainer.hasFragment() && hasStories && progressToActionMode == 0) { + float newOverScroll = storiesOverscroll - dy * AndroidUtilities.lerp( 0.2f, 0.5f, dialogStoriesCell.overscrollProgress()); + setStoriesOvercroll(viewPage, newOverScroll); + } return usedDy; } - return super.scrollVerticallyBy(measuredDy, recycler, state); + + int scrolled = super.scrollVerticallyBy(measuredDy, recycler, state); + if (scrolled == 0 && dy < 0 && isDragging && !rightSlidingDialogContainer.hasFragment() && hasStories && progressToActionMode == 0) { + float newOverScroll = storiesOverscroll - dy * AndroidUtilities.lerp(0.2f, 0.5f, dialogStoriesCell.overscrollProgress()); + setStoriesOvercroll(viewPage, newOverScroll); + } + return scrolled; } @Override @@ -3505,7 +3845,7 @@ public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State viewPage.listView.setLayoutManager(viewPage.layoutManager); viewPage.listView.setVerticalScrollbarPosition(LocaleController.isRTL ? RecyclerListView.SCROLLBAR_POSITION_LEFT : RecyclerListView.SCROLLBAR_POSITION_RIGHT); viewPage.addView(viewPage.listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); - viewPage.listView.setOnItemClickListener((view, position) -> { + viewPage.listView.setOnItemClickListener((view, position, x, y) -> { if (initialDialogsType == DIALOGS_TYPE_BOT_REQUEST_PEER && view instanceof TextCell) { viewPage.dialogsAdapter.onCreateGroupForThisClick(); return; @@ -3547,6 +3887,21 @@ public void didFailChatCreation() { }); presentFragment(activity); return; + } else if (view instanceof DialogsHintCell && (viewPage.dialogsType == 7 || viewPage.dialogsType == 8)) { + TLRPC.TL_chatlists_chatlistUpdates updates = viewPage.dialogsAdapter.getChatlistUpdate(); + if (updates != null) { + MessagesController.DialogFilter filter = getMessagesController().selectedDialogFilter[viewPage.dialogsType - 7]; + if (filter != null) { + showDialog(new FolderBottomSheet(DialogsActivity.this, filter.id, updates)); + } + return; + } + } else if (view instanceof DialogCell && !actionBar.isActionModeShowed() && !rightSlidingDialogContainer.hasFragment()) { + DialogCell dialogCell = (DialogCell) view; + AndroidUtilities.rectTmp.set( + dialogCell.avatarImage.getImageX(), dialogCell.avatarImage.getImageY(), + dialogCell.avatarImage.getImageX2(), dialogCell.avatarImage.getImageY2() + ); } onItemClick(view, position, viewPage.dialogsAdapter); }); @@ -3584,16 +3939,17 @@ public void onLongClickRelease() { private boolean wasManualScroll; private boolean stoppedAllHeavyOperations; + @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { if (newState == RecyclerView.SCROLL_STATE_DRAGGING) { wasManualScroll = true; scrollingManually = true; + viewPages[0].scroller.cancel(); } else { scrollingManually = false; } if (newState == RecyclerView.SCROLL_STATE_IDLE) { - wasManualScroll = false; disableActionBarScrolling = false; if (waitingForScrollFinished) { @@ -3604,55 +3960,60 @@ public void onScrollStateChanged(RecyclerView recyclerView, int newState) { } viewPage.dialogsAdapter.notifyDataSetChanged(); } - - if (filterTabsView != null && filterTabsView.getVisibility() == View.VISIBLE && viewPages[0].listView == recyclerView) { - int scrollY = (int) -actionBar.getTranslationY(); - int actionBarHeight = ActionBar.getCurrentActionBarHeight(); - if (scrollY != 0 && scrollY != actionBarHeight) { - if (scrollY < actionBarHeight / 2) { - if (viewPages[0].listView.canScrollVertically(-1)) { - recyclerView.smoothScrollBy(0, -scrollY); - } - } else if (viewPages[0].listView.canScrollVertically(1)) { - recyclerView.smoothScrollBy(0, actionBarHeight - scrollY); - } - } - } + checkAutoscrollToStories(viewPage); } } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { viewPage.dialogsItemAnimator.onListScroll(-dy); + int firstVisiblePosition = -1; + int lastVisiblePosition = -1; + for (int i = 0; i < recyclerView.getChildCount(); i++) { + int position = recyclerView.getChildAdapterPosition(recyclerView.getChildAt(i)); + if (position >= 0) { + if (lastVisiblePosition == -1 || position > lastVisiblePosition) { + lastVisiblePosition = position; + } + if (firstVisiblePosition == -1 || position < firstVisiblePosition) { + firstVisiblePosition = position; + } + } + } checkListLoad(viewPage); - if (initialDialogsType != 10 && wasManualScroll && floatingButtonContainer.getVisibility() != View.GONE && recyclerView.getChildCount() > 0) { - int firstVisibleItem = viewPage.layoutManager.findFirstVisibleItemPosition(); - if (firstVisibleItem != RecyclerView.NO_POSITION) { - RecyclerView.ViewHolder holder = recyclerView.findViewHolderForAdapterPosition(firstVisibleItem); - if (!hasHiddenArchive() || holder != null && holder.getAdapterPosition() != 0) { + if (hasStories) { + invalidateScrollY = true; + if (fragmentView != null) { + fragmentView.invalidate(); + } + } + if (initialDialogsType != DIALOGS_TYPE_WIDGET && wasManualScroll && (floatingButtonContainer.getVisibility() != View.GONE || !storiesEnabled) && recyclerView.getChildCount() > 0) { + if (firstVisiblePosition != RecyclerView.NO_POSITION) { + RecyclerView.ViewHolder holder = recyclerView.findViewHolderForAdapterPosition(firstVisiblePosition); + if (!hasHiddenArchive() || holder != null && holder.getAdapterPosition() >= 0) { int firstViewTop = 0; if (holder != null) { firstViewTop = holder.itemView.getTop(); } boolean goingDown; boolean changed = true; - if (prevPosition == firstVisibleItem) { + if (prevPosition == firstVisiblePosition) { final int topDelta = prevTop - firstViewTop; goingDown = firstViewTop < prevTop; changed = Math.abs(topDelta) > 1; } else { - goingDown = firstVisibleItem > prevPosition; + goingDown = firstVisiblePosition > prevPosition; } if (changed && scrollUpdated && (goingDown || scrollingManually)) { hideFloatingButton(goingDown); } - prevPosition = firstVisibleItem; + prevPosition = firstVisiblePosition; prevTop = firstViewTop; scrollUpdated = true; } } } - if (filterTabsView != null && filterTabsView.getVisibility() == View.VISIBLE && recyclerView == viewPages[0].listView && !searching && !actionBar.isActionModeShowed() && !disableActionBarScrolling && filterTabsViewIsVisible && !rightSlidingDialogContainer.hasFragment()) { + if (!hasStories && (filterTabsView != null && filterTabsView.getVisibility() == View.VISIBLE && filterTabsViewIsVisible) && recyclerView == viewPages[0].listView && !searching && !actionBar.isActionModeShowed() && !disableActionBarScrolling && !rightSlidingDialogContainer.hasFragment()) { if (dy > 0 && hasHiddenArchive() && viewPages[0].dialogsType == DIALOGS_TYPE_DEFAULT) { View child = recyclerView.getChildAt(0); if (child != null) { @@ -3669,24 +4030,38 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { } } } - - float currentTranslation = actionBar.getTranslationY(); + float currentTranslation = scrollYOffset; float newTranslation = currentTranslation - dy; - if (newTranslation < -ActionBar.getCurrentActionBarHeight()) { - newTranslation = -ActionBar.getCurrentActionBarHeight(); - } else if (newTranslation > 0) { - newTranslation = 0; + boolean applyScrollY = true; + if (hasStories) { + applyScrollY = false; + invalidateScrollY = true; + if (fragmentView != null) { + fragmentView.invalidate(); + } } - if (newTranslation != currentTranslation) { - setScrollY(newTranslation); + + if (applyScrollY) { + int maxScrollYOffset = getMaxScrollYOffset(); + if (newTranslation < -maxScrollYOffset) { + newTranslation = -maxScrollYOffset; + } else if (newTranslation > 0) { + newTranslation = 0; + } + if (newTranslation != currentTranslation) { + setScrollY(newTranslation); + } } } if (fragmentView != null) { ((SizeNotifierFrameLayout) fragmentView).invalidateBlur(); } - if (rightSlidingDialogContainer != null && rightSlidingDialogContainer.hasFragment()) { + if (rightSlidingDialogContainer != null && rightSlidingDialogContainer.hasFragment() && viewPage.listView != null) { viewPage.listView.invalidate(); } + if (dialogStoriesCell != null && dialogStoriesCell.getPremiumHint() != null && dialogStoriesCell.getPremiumHint().shown()) { + dialogStoriesCell.getPremiumHint().hide(); + } } }); @@ -3742,6 +4117,11 @@ public void onButtonLongPress(DialogCell dialogCell) { public void onCreateGroupForThisClick() { createGroupForThis(); } + + @Override + protected void onArchiveSettingsClick() { + presentFragment(new ArchiveSettingsActivity()); + } }; viewPage.dialogsAdapter.setRecyclerListView(viewPage.listView); viewPage.dialogsAdapter.setForceShowEmptyCell(afterSignup); @@ -3753,6 +4133,17 @@ public void onCreateGroupForThisClick() { viewPage.listView.setEmptyView(folderId == 0 ? viewPage.progressView : null); viewPage.scrollHelper = new RecyclerAnimationScrollHelper(viewPage.listView, viewPage.layoutManager); + viewPage.scrollHelper.forceUseStableId = true; + viewPage.scrollHelper.isDialogs = true; + viewPage.scrollHelper.setScrollListener(new RecyclerAnimationScrollHelper.ScrollListener() { + @Override + public void onScroll() { + if (hasStories) { + invalidateScrollY = true; + fragmentView.invalidate(); + } + } + }); if (a != 0) { viewPages[a].setVisibility(View.GONE); @@ -3850,7 +4241,7 @@ public void didPressedOnSubDialog(long did) { updateSelectedCount(); actionBar.closeSearchField(); } else { - didSelectResult(did, 0,true, false); + didSelectResult(did, 0, true, false); } } else { Bundle args = new Bundle(); @@ -3897,7 +4288,7 @@ public void needRemoveHint(long did) { showDialog(dialog); TextView button = (TextView) dialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } @@ -3926,7 +4317,7 @@ public void needClearList() { showDialog(dialog); TextView button = (TextView) dialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } @@ -3990,6 +4381,50 @@ public void onLongClickRelease() { contentView.addView(filtersView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP)); filtersView.setVisibility(View.GONE); + if (initialDialogsType != DIALOGS_TYPE_WIDGET) { + floatingButton2Container = new FrameLayout(context); + floatingButton2Container.setVisibility(onlySelect && initialDialogsType != 10 || folderId != 0 || !storiesEnabled ? View.GONE : View.VISIBLE); + contentView.addView(floatingButton2Container, LayoutHelper.createFrame((Build.VERSION.SDK_INT >= 21 ? 36 : 40), (Build.VERSION.SDK_INT >= 21 ? 36 : 40), (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.BOTTOM, LocaleController.isRTL ? 24 : 0, 0, LocaleController.isRTL ? 0 : 24, 14 + 60 + 8)); + floatingButton2Container.setOnClickListener(v -> { + if (parentLayout != null && parentLayout.isInPreviewMode()) { + finishPreviewFragment(); + return; + } + Bundle args = new Bundle(); + args.putBoolean("destroyAfterSelect", true); + presentFragment(new ContactsActivity(args)); + }); + if (Build.VERSION.SDK_INT >= 21) { + StateListAnimator animator = new StateListAnimator(); + animator.addState(new int[]{android.R.attr.state_pressed}, ObjectAnimator.ofFloat(floatingButton2Container, View.TRANSLATION_Z, AndroidUtilities.dp(2), AndroidUtilities.dp(4)).setDuration(200)); + animator.addState(new int[]{}, ObjectAnimator.ofFloat(floatingButton2Container, View.TRANSLATION_Z, AndroidUtilities.dp(4), AndroidUtilities.dp(2)).setDuration(200)); + floatingButton2Container.setStateListAnimator(animator); + floatingButton2Container.setOutlineProvider(new ViewOutlineProvider() { + @SuppressLint("NewApi") + @Override + public void getOutline(View view, Outline outline) { + outline.setOval(0, 0, AndroidUtilities.dp(36), AndroidUtilities.dp(36)); + } + }); + } + + floatingButton2 = new RLottieImageView(context); + floatingButton2.setScaleType(ImageView.ScaleType.CENTER); + floatingButton2.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_windowBackgroundWhiteGrayIcon), PorterDuff.Mode.MULTIPLY)); + floatingButton2.setImageResource(R.drawable.fab_compose_small); + floatingButton2Container.setContentDescription(LocaleController.getString("NewMessageTitle", R.string.NewMessageTitle)); + floatingButton2Container.addView(floatingButton2, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + + floating2ProgressView = new RadialProgressView(context); + floating2ProgressView.setProgressColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayIcon)); + floating2ProgressView.setScaleX(0.1f); + floating2ProgressView.setScaleY(0.1f); + floating2ProgressView.setAlpha(0f); + floating2ProgressView.setVisibility(View.GONE); + floating2ProgressView.setSize(AndroidUtilities.dp(22)); + floatingButton2Container.addView(floating2ProgressView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + } + floatingButtonContainer = new FrameLayout(context); floatingButtonContainer.setVisibility(onlySelect && initialDialogsType != 10 || folderId != 0 ? View.GONE : View.VISIBLE); contentView.addView(floatingButtonContainer, LayoutHelper.createFrame((Build.VERSION.SDK_INT >= 21 ? 56 : 60), (Build.VERSION.SDK_INT >= 21 ? 56 : 60), (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.BOTTOM, LocaleController.isRTL ? 14 : 0, 0, LocaleController.isRTL ? 0 : 14, 14)); @@ -4012,22 +4447,94 @@ public void onLongClickRelease() { return; } - Bundle args = new Bundle(); - args.putBoolean("destroyAfterSelect", true); - presentFragment(new ContactsActivity(args)); + if (!storiesEnabled) { + Bundle args = new Bundle(); + args.putBoolean("destroyAfterSelect", true); + presentFragment(new ContactsActivity(args)); + return; + } + + if (storyHint != null) { + storyHint.hide(); + } + + final int limit = getUserConfig().isPremium() ? + getMessagesController().storyExpiringLimitPremium : + getMessagesController().storyExpiringLimitDefault; + if (getMessagesController().getStoriesController().getMyStoriesCount() >= limit) { + CharSequence text = AndroidUtilities.replaceTags(LocaleController.formatPluralString("StoriesTooMuch", limit)); + manyStoriesHint.setMaxWidthPx(HintView2.cutInFancyHalf(text, manyStoriesHint.getTextPaint())); + manyStoriesHint.setText(text); + if (manyStoriesHint.shown()) { + BotWebViewVibrationEffect.APP_ERROR.vibrate(); + } + manyStoriesHint.show(); + return; + } else if (manyStoriesHint != null) { + manyStoriesHint.show(); + } + + StoryRecorder.getInstance(getParentActivity(), currentAccount) + .closeToWhenSent(new StoryRecorder.ClosingViewProvider() { + @Override + public void preLayout(Runnable runnable) { + if (dialogStoriesCell != null) { + scrollToTop(false, true); + invalidateScrollY = true; + fragmentView.invalidate(); + dialogStoriesCell.scrollToFirstCell(); + viewPages[0].listView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + viewPages[0].listView.getViewTreeObserver().removeOnPreDrawListener(this); + AndroidUtilities.runOnUIThread(runnable, 100); + return false; + } + }); + } else { + runnable.run(); + } + } + + @Override + public StoryRecorder.SourceView getView() { + return StoryRecorder.SourceView.fromStoryCell(dialogStoriesCell != null ? dialogStoriesCell.findSelfStoryCell() : null); + } + }) + .open(StoryRecorder.SourceView.fromFloatingButton(floatingButtonContainer), true); } }); + boolean showStoryHint = false; + if (!isArchive() && initialDialogsType == DIALOGS_TYPE_DEFAULT) { + if (MessagesController.getInstance(currentAccount).getMainSettings().getBoolean("storyhint", true)) { + storyHint = new HintView2(context, HintView2.DIRECTION_RIGHT) + .setRounding(8) + .setDuration(-1) + .setCloseButton(true) + .setMaxWidth(165) + .setMultilineText(true) + .setText(AndroidUtilities.replaceCharSequence("%s", LocaleController.getString(R.string.StoryCameraHint), StoryRecorder.cameraBtnSpan(context))) + .setJoint(1, -40) + .setBgColor(getThemedColor(Theme.key_undo_background)) + .setOnHiddenListener(() -> MessagesController.getInstance(currentAccount).getMainSettings().edit().putBoolean("storyhint", false).commit()); + contentView.addView(storyHint, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 160, Gravity.BOTTOM | Gravity.FILL_HORIZONTAL, 0, 0, 80, 0)); + showStoryHint = true; + } + manyStoriesHint = new HintView2(context, HintView2.DIRECTION_RIGHT) + .setRounding(8) + .setDuration(5000) + .setCloseButton(false) + .setMultilineText(true) + .setJoint(1, -40) + .setBgColor(getThemedColor(Theme.key_undo_background)); + manyStoriesHint.setPadding(0, 0, AndroidUtilities.dp(24), 0); + contentView.addView(manyStoriesHint, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 200, Gravity.BOTTOM | Gravity.FILL_HORIZONTAL, 0, 0, 56, 0)); + } + floatingButton = new RLottieImageView(context); floatingButton.setScaleType(ImageView.ScaleType.CENTER); - floatingButton.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_chats_actionIcon), PorterDuff.Mode.SRC_IN)); - if (initialDialogsType == DIALOGS_TYPE_WIDGET) { - floatingButton.setImageResource(R.drawable.floating_check); - floatingButtonContainer.setContentDescription(LocaleController.getString("Done", R.string.Done)); - } else { - floatingButton.setAnimation(R.raw.write_contacts_fab_icon, 52, 52); - floatingButtonContainer.setContentDescription(LocaleController.getString("NewMessageTitle", R.string.NewMessageTitle)); - } + floatingButton.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_chats_actionIcon), PorterDuff.Mode.MULTIPLY)); if (Build.VERSION.SDK_INT >= 21) { StateListAnimator animator = new StateListAnimator(); animator.addState(new int[]{android.R.attr.state_pressed}, ObjectAnimator.ofFloat(floatingButtonContainer, View.TRANSLATION_Z, AndroidUtilities.dp(2), AndroidUtilities.dp(4)).setDuration(200)); @@ -4049,17 +4556,12 @@ public void getOutline(View view, Outline outline) { combinedDrawable.setIconSize(AndroidUtilities.dp(56), AndroidUtilities.dp(56)); drawable = combinedDrawable; } - updateFloatingButtonColor(); floatingButtonContainer.addView(floatingButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); - - floatingProgressView = new RadialProgressView(context); - floatingProgressView.setProgressColor(Theme.getColor(Theme.key_chats_actionIcon)); - floatingProgressView.setScaleX(0.1f); - floatingProgressView.setScaleY(0.1f); - floatingProgressView.setAlpha(0f); - floatingProgressView.setVisibility(View.GONE); - floatingProgressView.setSize(AndroidUtilities.dp(22)); - floatingButtonContainer.addView(floatingProgressView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + updateFloatingButtonColor(); + updateStoriesPosting(); + if (showStoryHint && storyHint != null && storiesEnabled) { + storyHint.show(); + } searchTabsView = null; @@ -4098,6 +4600,7 @@ public boolean dispatchTouchEvent(MotionEvent ev) { } return super.dispatchTouchEvent(ev); } + @Override public void setTranslationY(float translationY) { super.setTranslationY(translationY); @@ -4354,16 +4857,118 @@ public void getOutline(View view, Outline outline) { writeButtonContainer.addView(writeButtonBackground, LayoutHelper.createFrame(Build.VERSION.SDK_INT >= 21 ? 56 : 60, Build.VERSION.SDK_INT >= 21 ? 56 : 60, Gravity.LEFT | Gravity.TOP, Build.VERSION.SDK_INT >= 21 ? 2 : 0, 0, 0, 0)); } + if (filterTabsView != null) { contentView.addView(filterTabsView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 44)); } - if ((initialDialogsType == 3 && NekoConfig.showTabsOnForward.Bool()) || !onlySelect) { + + dialogStoriesCell = new DialogStoriesCell(context, this, currentAccount, isArchive() ? DialogStoriesCell.TYPE_ARCHIVE : DialogStoriesCell.TYPE_DIALOGS) { + @Override + public void onUserLongPressed(View view, long dialogId) { + filterOptions = ItemOptions.makeOptions(DialogsActivity.this, view) + .setViewAdditionalOffsets(0, AndroidUtilities.dp(8), 0, 0) + .setScrimViewBackground(Theme.createRoundRectDrawable( + AndroidUtilities.dp(6), + canShowFilterTabsView ? AndroidUtilities.dp(6) : 0, + Theme.getColor(isArchive() ? Theme.key_actionBarDefaultArchived : Theme.key_actionBarDefault) + )); + if (UserObject.isService(dialogId)) { + BotWebViewVibrationEffect.APP_ERROR.vibrate(); + return; + } + if (!NekoConfig.disableVibration.Bool()) + view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + if (dialogId == UserConfig.getInstance(currentAccount).getClientUserId()) { + if (!storiesEnabled) { + if (dialogStoriesCell != null) { + dialogStoriesCell.showPremiumHint(); + } + return; + } + filterOptions.add(R.drawable.msg_stories_add, LocaleController.getString("AddStory", R.string.AddStory), Theme.key_actionBarDefaultSubmenuItemIcon, Theme.key_actionBarDefaultSubmenuItem, () -> { + dialogStoriesCell.openStoryRecorder(); + }, null); + filterOptions.add(R.drawable.msg_stories_archive, LocaleController.getString("ArchivedStories", R.string.ArchivedStories), Theme.key_actionBarDefaultSubmenuItemIcon, Theme.key_actionBarDefaultSubmenuItem, () -> { + Bundle args = new Bundle(); + args.putLong("dialog_id", UserConfig.getInstance(currentAccount).getClientUserId()); + args.putInt("type", MediaActivity.TYPE_STORIES); + args.putInt("start_from", SharedMediaLayout.TAB_ARCHIVED_STORIES); + MediaActivity mediaActivity = new MediaActivity(args, null); + presentFragment(mediaActivity); + }, null); + filterOptions.add(R.drawable.msg_stories_saved, LocaleController.getString("SavedStories", R.string.SavedStories), Theme.key_actionBarDefaultSubmenuItemIcon, Theme.key_actionBarDefaultSubmenuItem, () -> { + Bundle args = new Bundle(); + args.putLong("dialog_id", UserConfig.getInstance(currentAccount).getClientUserId()); + args.putInt("type", MediaActivity.TYPE_STORIES); + presentFragment(new MediaActivity(args, null)); + }, null); + } else { + final String key = NotificationsController.getSharedPrefKey(dialogId, 0); + boolean muted = !NotificationsCustomSettingsActivity.areStoriesNotMuted(currentAccount, dialogId); + filterOptions + .add(R.drawable.msg_discussion, LocaleController.getString("SendMessage", R.string.SendMessage), () -> { + presentFragment(ChatActivity.of(dialogId)); + }) + .add(R.drawable.msg_openprofile, LocaleController.getString("OpenProfile", R.string.OpenProfile), Theme.key_actionBarDefaultSubmenuItemIcon, Theme.key_actionBarDefaultSubmenuItem, () -> { + presentFragment(ProfileActivity.of(dialogId)); + }, null) + .addIf(!muted, R.drawable.msg_mute, LocaleController.getString("NotificationsStoryMute2", R.string.NotificationsStoryMute2), () -> { + MessagesController.getNotificationsSettings(currentAccount).edit().putBoolean("stories_" + key, false).apply(); + getNotificationsController().updateServerNotificationsSettings(dialogId, 0); + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); + String name = user == null ? "" : user.first_name.trim(); + int index = name.indexOf(" "); + if (index > 0) { + name = name.substring(0, index); + } + BulletinFactory.of(DialogsActivity.this).createUsersBulletin(Arrays.asList(user), AndroidUtilities.replaceTags(LocaleController.formatString("NotificationsStoryMutedHint", R.string.NotificationsStoryMutedHint, name))).show(); + }, subItem -> { + subItem.setMultiline(false); + }) + .addIf(muted, R.drawable.msg_unmute, LocaleController.getString("NotificationsStoryUnmute2", R.string.NotificationsStoryUnmute2), () -> { + MessagesController.getNotificationsSettings(currentAccount).edit().putBoolean("stories_" + key, true).apply(); + getNotificationsController().updateServerNotificationsSettings(dialogId, 0); + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); + String name = user == null ? "" : user.first_name.trim(); + int index = name.indexOf(" "); + if (index > 0) { + name = name.substring(0, index); + } + BulletinFactory.of(DialogsActivity.this).createUsersBulletin(Arrays.asList(user), AndroidUtilities.replaceTags(LocaleController.formatString("NotificationsStoryUnmutedHint", R.string.NotificationsStoryUnmutedHint, name))).show(); + }, subItem -> { + subItem.setMultiline(false); + }) + .addIf(!isArchive(), R.drawable.msg_archive, LocaleController.getString("ArchivePeerStories", R.string.ArchivePeerStories), () -> { + toggleArciveForStory(dialogId); + }, subItem -> { + subItem.setMultiline(false); + }) + .addIf(isArchive(), R.drawable.msg_unarchive, LocaleController.getString("UnarchiveStories", R.string.UnarchiveStories), () -> { + toggleArciveForStory(dialogId); + }, subItem -> { + subItem.setMultiline(false); + }); + } + filterOptions.setGravity(Gravity.LEFT) + .translate(AndroidUtilities.dp(-8), AndroidUtilities.dp(-10)) + .show(); + } + }; + dialogStoriesCell.setActionBar(actionBar); + dialogStoriesCell.allowGlobalUpdates = false; + dialogStoriesCell.setVisibility(View.GONE); + animateToHasStories = false; + hasOnlySlefStories = false; + hasStories = false; + contentView.addView(dialogStoriesCell, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, DialogStoriesCell.HEIGHT_IN_DP)); + if (!onlySelect || (initialDialogsType == DIALOGS_TYPE_FORWARD && NekoConfig.showTabsOnForward.Bool())) { final FrameLayout.LayoutParams layoutParams = LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT); if (inPreviewMode && Build.VERSION.SDK_INT >= 21) { layoutParams.topMargin = AndroidUtilities.statusBarHeight; } contentView.addView(actionBar, layoutParams); - + } + if (!onlySelect) { animatedStatusView = new DrawerProfileCell.AnimatedStatusView(context, 20, 60); contentView.addView(animatedStatusView, LayoutHelper.createFrame(20, 20, Gravity.LEFT | Gravity.TOP)); } @@ -4372,69 +4977,9 @@ public void getOutline(View view, Outline outline) { // NekoX: Remove UPDATE NOW Bottom View in DialogsActivity } - for (int a = 0; a < 2; a++) { - undoView[a] = new UndoView(context) { - @Override - public void setTranslationY(float translationY) { - super.setTranslationY(translationY); - if (this == undoView[0] && undoView[1].getVisibility() != VISIBLE) { - additionalFloatingTranslation = getMeasuredHeight() + AndroidUtilities.dp(8) - translationY; - if (additionalFloatingTranslation < 0) { - additionalFloatingTranslation = 0; - } - if (!floatingHidden) { - updateFloatingButtonOffset(); - } - } - } - - @Override - protected boolean canUndo() { - for (int a = 0; a < viewPages.length; a++) { - if (viewPages[a].dialogsItemAnimator.isRunning()) { - return false; - } - } - return true; - } - - @Override - protected void onRemoveDialogAction(long currentDialogId, int action) { - if (action == UndoView.ACTION_DELETE || action == UndoView.ACTION_DELETE_FEW) { - debugLastUpdateAction = 1; - setDialogsListFrozen(true); - if (frozenDialogsList != null) { - int selectedIndex = -1; - for (int i = 0; i < frozenDialogsList.size(); i++) { - if (frozenDialogsList.get(i).id == currentDialogId) { - selectedIndex = i; - break; - } - } - - if (selectedIndex >= 0) { - TLRPC.Dialog dialog = frozenDialogsList.remove(selectedIndex); - viewPages[0].dialogsAdapter.notifyDataSetChanged(); - int finalSelectedIndex = selectedIndex; - AndroidUtilities.runOnUIThread(() -> { - if (frozenDialogsList != null) { - if (finalSelectedIndex < 0 || finalSelectedIndex >= frozenDialogsList.size()) { - return; - } - frozenDialogsList.add(finalSelectedIndex, dialog); - viewPages[0].updateList(true); - } - }); - } else { - setDialogsListFrozen(false); - } - } - checkAnimationFinished(); - } - } - }; - contentView.addView(undoView[a], LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM | Gravity.LEFT, 8, 0, 8, 8)); - } + undoViewIndex = contentView.getChildCount(); + undoView[0] = null; + undoView[1] = null; if (folderId != 0) { viewPages[0].listView.setGlowColor(Theme.getColor(Theme.key_actionBarDefaultArchived)); @@ -4507,55 +5052,9 @@ public void setAlpha(float alpha) { } View backButton = actionBar.getBackButton(); backButton.setOnLongClickListener(e -> { - scrimPopupWindow = BackButtonMenu.showHistory(this, backButton); - final ActionBarPopupWindow scrimPopupWindowBack = scrimPopupWindow; - if (scrimPopupWindow != null) { - scrimPopupWindow.setOnDismissListener(() -> { - if (scrimPopupWindow != scrimPopupWindowBack) { - return; - } - if (scrimAnimatorSet != null) { - scrimAnimatorSet.cancel(); - scrimAnimatorSet = null; - } - scrimAnimatorSet = new AnimatorSet(); - scrimViewAppearing = false; - ArrayList animators = new ArrayList<>(); - animators.add(ObjectAnimator.ofInt(scrimPaint, AnimationProperties.PAINT_ALPHA, 0)); - scrimAnimatorSet.playTogether(animators); - scrimAnimatorSet.setDuration(220); - scrimAnimatorSet.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (scrimView != null) { - scrimView.setBackground(null); - scrimView = null; - } - if (fragmentView != null) { - fragmentView.invalidate(); - } - } - }); - scrimAnimatorSet.start(); - scrimPopupWindow = null; - scrimPopupWindowItems = null; - }); - if (scrimAnimatorSet != null) { - scrimAnimatorSet.cancel(); - } else { - scrimAnimatorSet = new AnimatorSet(); - } - fragmentView.invalidate(); - scrimViewAppearing = true; - ArrayList animators = new ArrayList<>(); - animators.add(ObjectAnimator.ofInt(scrimPaint, AnimationProperties.PAINT_ALPHA, 0, 50)); - scrimAnimatorSet.playTogether(animators); - scrimAnimatorSet.setDuration(150); - scrimAnimatorSet.start(); - return true; - } else { - return false; - } + if (searching || filterTabsView != null && filterTabsView.isEditing() || actionBar.isActionModeShowed()) return false; + BackButtonMenuRecent.show(currentAccount, this, backButton); + return true; }); updateMenuButton(false); @@ -4581,8 +5080,9 @@ public void openAnimationStarted(boolean open) { } rightFragmentTransitionInProgress = true; + rightFragmentTransitionIsOpen = open; contentView.requestLayout(); - fromScrollYProperty = actionBar.getTranslationY(); + fromScrollYProperty = scrollYOffset; if (canShowFilterTabsView && filterTabsView != null) { filterTabsView.setVisibility(View.VISIBLE); @@ -4611,15 +5111,28 @@ public boolean onTouchEvent(MotionEvent e) { return false; } }; - LinearLayoutManager layoutManager = new LinearLayoutManager(context); - layoutManager.setNeedFixEndGap(false); + ViewPage page = transitionPage; + LinearLayoutManager layoutManager = new LinearLayoutManager(context) { + @Override + protected int firstPosition() { + if (page.dialogsType == DIALOGS_TYPE_DEFAULT && hasHiddenArchive() && page.archivePullViewState == ARCHIVE_ITEM_STATE_HIDDEN) { + return 1; + } + return 0; + } + }; transitionPage.animationSupportListView.setLayoutManager(layoutManager); transitionPage.animationSupportDialogsAdapter = new DialogsAdapter(DialogsActivity.this, context, transitionPage.dialogsType, folderId, onlySelect, selectedDialogs, currentAccount, requestPeerType); transitionPage.animationSupportDialogsAdapter.setIsTransitionSupport(); transitionPage.animationSupportListView.setAdapter(transitionPage.animationSupportDialogsAdapter); transitionPage.addView(transitionPage.animationSupportListView); } + if (!open && hasStories) { + invalidateScrollY = false; + DialogsActivity.this.setScrollY(-getMaxScrollYOffset()); + } + transitionPage.listView.stopScroll(); transitionPage.animationSupportDialogsAdapter.setDialogsType(transitionPage.dialogsType); transitionPage.dialogsAdapter.setCollapsedView(false, transitionPage.listView); transitionPage.dialogsAdapter.setDialogsListFrozen(true); @@ -4629,7 +5142,8 @@ public boolean onTouchEvent(MotionEvent e) { hideFloatingButton(anotherFragmentOpened); transitionPage.dialogsAdapter.notifyDataSetChanged(); transitionPage.animationSupportDialogsAdapter.notifyDataSetChanged(); - transitionPage.listView.setAnimationSupportView(transitionPage.animationSupportListView, -actionBar.getTranslationY(), open); + float scrollOffset = hasStories && !open ? scrollYOffset : -scrollYOffset; + transitionPage.listView.setAnimationSupportView(transitionPage.animationSupportListView, scrollOffset, open, false); transitionPage.listView.setClipChildren(false); actionBar.setAllowOverlayTitle(false); transitionPage.listView.stopScroll(); @@ -4637,12 +5151,12 @@ public boolean onTouchEvent(MotionEvent e) { } @Override - public void openAnimationFinished() { + public void openAnimationFinished(boolean backward) { if (!canShowFilterTabsView && filterTabsView != null) { filterTabsView.setVisibility(View.GONE); } transitionPage.layoutManager.setNeedFixGap(true); - transitionPage.dialogsAdapter.setCollapsedView(hasFragment(), transitionPage.listView); + transitionPage.dialogsAdapter.setCollapsedView(hasFragment(), transitionPage.listView); transitionPage.dialogsAdapter.setDialogsListFrozen(false); transitionPage.animationSupportDialogsAdapter.setDialogsListFrozen(false); setDialogsListFrozen(false); @@ -4650,14 +5164,22 @@ public void openAnimationFinished() { transitionPage.listView.invalidate(); transitionPage.dialogsAdapter.notifyDataSetChanged(); transitionPage.animationSupportDialogsAdapter.notifyDataSetChanged(); - transitionPage.listView.setAnimationSupportView(null, 0, hasFragment()); + transitionPage.listView.setAnimationSupportView(null, 0, hasFragment(), backward); rightFragmentTransitionInProgress = false; actionBar.setAllowOverlayTitle(!hasFragment()); contentView.requestLayout(); - // transitionPage.layoutManager.setNeedFixEndGap(!hasFragment()); - DialogsActivity.this.setScrollY(0); + // transitionPage.layoutManager.setNeedFixGap(!hasFragment()); + if (!hasStories) { + DialogsActivity.this.setScrollY(0); + } + if (!hasFragment()) { + invalidateScrollY = true; + fixScrollYAfterArchiveOpened = true; + fragmentView.invalidate(); + } searchViewPager.updateTabs(); updateDrawerSwipeEnabled(); + updateFilterTabs(false, true); } @Override @@ -4666,13 +5188,15 @@ void setOpenProgress(float progress) { if (anotherFragmentOpened != opened) { anotherFragmentOpened = opened; } - filterTabsMoveFrom = AndroidUtilities.dp(44); - filterTabsProgress = canShowFilterTabsView ? 1f - progress : 0; + filterTabsMoveFrom = getActionBarMoveFrom(canShowFilterTabsView); + filterTabsProgress = canShowFilterTabsView || hasStories ? 1f - progress : 0; if (fragmentView != null) { fragmentView.invalidate(); } - DialogsActivity.this.setScrollY(AndroidUtilities.lerp(fromScrollYProperty, 0, progress)); + if (!hasStories) { + DialogsActivity.this.setScrollY(AndroidUtilities.lerp(fromScrollYProperty, 0, progress)); + } updateDrawerSwipeEnabled(); if (menuDrawable != null && hasFragment()) { menuDrawable.setRotation(progress, false); @@ -4701,11 +5225,11 @@ void setOpenProgress(float progress) { if (folderId != 0) { actionBarDefaultPaint.setColor( - ColorUtils.blendARGB( - Theme.getColor(Theme.key_actionBarDefaultArchived), - Theme.getColor(Theme.key_actionBarDefault), - progress - ) + ColorUtils.blendARGB( + Theme.getColor(Theme.key_actionBarDefaultArchived), + Theme.getColor(Theme.key_actionBarDefault), + progress + ) ); } @@ -4722,9 +5246,102 @@ void setOpenProgress(float progress) { else if (new Random().nextInt(100) < 20) UpdateUtil.postCheckFollowChannel(getParentActivity(), currentAccount); + updateStoriesVisibility(false); return fragmentView; } + private void setStoriesOvercroll(ViewPage viewPage, float storiesOverscroll) { + if (this.storiesOverscroll == storiesOverscroll) { + return; + } + this.storiesOverscroll = storiesOverscroll; + dialogStoriesCell.setOverscoll(storiesOverscroll); + viewPage.listView.setViewsOffset(storiesOverscroll); + viewPage.listView.setOverScrollMode(storiesOverscroll != 0 ? RecyclerView.OVER_SCROLL_NEVER : RecyclerView.OVER_SCROLL_ALWAYS); + fragmentView.invalidate(); + if (storiesOverscroll > AndroidUtilities.dp(90)) { + getOrCreateStoryViewer().doOnAnimationReady(() -> { + fragmentView.dispatchTouchEvent(AndroidUtilities.emptyMotionEvent()); + }); + dialogStoriesCell.openOverscrollSelectedStory(); + dialogStoriesCell.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + } + } + + private void toggleArciveForStory(long dialogId) { + boolean hide = !isArchive(); + AndroidUtilities.runOnUIThread(() -> { + getMessagesController().getStoriesController().toggleHidden(dialogId, hide, false, true); + TLRPC.User user = getMessagesController().getUser(dialogId); + BulletinFactory.UndoObject undoObject = new BulletinFactory.UndoObject(); + undoObject.onUndo = () -> { + getMessagesController().getStoriesController().toggleHidden(dialogId, !hide, false, true); + }; + undoObject.onAction = () -> { + getMessagesController().getStoriesController().toggleHidden(dialogId, hide, true, true); + }; + CharSequence str; + if (isArchive()) { + str = AndroidUtilities.replaceTags(LocaleController.formatString("StoriesMovedToDialogs", R.string.StoriesMovedToDialogs, ContactsController.formatName(user.first_name, null, 10))); + } else { + str = AndroidUtilities.replaceTags(LocaleController.formatString("StoriesMovedToContacts", R.string.StoriesMovedToContacts, ContactsController.formatName(user.first_name, null, 10))); + } + storiesBulletin = BulletinFactory.global().createUsersBulletin( + Arrays.asList(user), + str, + null, + undoObject).show(); + }, 200); + } + + private boolean checkAutoscrollToStories(ViewPage viewPage) { + if ((hasStories || (filterTabsView != null && filterTabsView.getVisibility() == View.VISIBLE)) && !rightSlidingDialogContainer.hasFragment()) { + int scrollY = (int) -scrollYOffset; + int actionBarHeight = getMaxScrollYOffset(); + if (scrollY != 0 && scrollY != actionBarHeight) { + if (scrollY < actionBarHeight / 2) { + if (viewPage.listView.canScrollVertically(-1)) { + viewPage.scroller.smoothScrollBy(-scrollY); + return true; + } + } else if (viewPage.listView.canScrollVertically(1)) { + viewPage.scroller.smoothScrollBy(actionBarHeight - scrollY); + return true; + } + } + } + return false; + } + + private float getActionBarMoveFrom(boolean showFilterTabs) { + float h = 0; + if (hasStories) { + h += AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP); + } + if (showFilterTabs) { + h += AndroidUtilities.dp(44); + } + if (dialogsHintCell != null && dialogsHintCell.getVisibility() == View.VISIBLE) { + h += dialogsHintCell.getMeasuredHeight(); + } + return h; + } + + private int getMaxScrollYOffset() { + if (hasStories) { + return AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP); + } else { + return ActionBar.getCurrentActionBarHeight(); + } + } + + public boolean isPremiumRestoreHintVisible() { + if (!MessagesController.getInstance(currentAccount).premiumLocked && folderId == 0) { + return MessagesController.getInstance(currentAccount).pendingSuggestions.contains("PREMIUM_RESTORE") && !getUserConfig().isPremium(); + } + return false; + } + public boolean isPremiumHintVisible() { if (!MessagesController.getInstance(currentAccount).premiumLocked && folderId == 0) { if (MessagesController.getInstance(currentAccount).pendingSuggestions.contains("PREMIUM_UPGRADE") && getUserConfig().isPremium() || MessagesController.getInstance(currentAccount).pendingSuggestions.contains("PREMIUM_ANNUAL") && !getUserConfig().isPremium()) { @@ -4751,7 +5368,7 @@ private boolean isCacheHintVisible() { private void resetCacheHintVisible() { SharedPreferences prefs = MessagesController.getGlobalMainSettings(); - final long week = 1000L * 60L * 60L * 24L * 7L; + final long week = 1000L * 60L * 60L * 24L * 7L; final long month = 1000L * 60L * 60L * 24L * 30L; long period = prefs.getLong("cache_hint_period", week); if (period <= week) { @@ -4771,7 +5388,7 @@ private void clearCacheHintVisible() { // } public void showSelectStatusDialog() { - if (selectAnimatedEmojiDialog != null || SharedConfig.appLocked) { + if (selectAnimatedEmojiDialog != null || SharedConfig.appLocked || (hasStories && !dialogStoriesCell.isExpanded())) { return; } final SelectAnimatedEmojiDialog.SelectAnimatedEmojiDialogWindow[] popup = new SelectAnimatedEmojiDialog.SelectAnimatedEmojiDialogWindow[1]; @@ -4864,7 +5481,25 @@ private void updateDialogsHint() { if (dialogsHintCell == null) { return; } - if (isPremiumHintVisible()) { + if (isPremiumRestoreHintVisible()) { + dialogsHintCell.setVisibility(View.VISIBLE); + dialogsHintCell.setOnClickListener(v -> { + presentFragment(new PremiumPreviewFragment("dialogs_hint").setSelectAnnualByDefault()); + AndroidUtilities.runOnUIThread(() -> { + MessagesController.getInstance(currentAccount).removeSuggestion(0, "PREMIUM_RESTORE"); + updateDialogsHint(); + }, 250); + }); + dialogsHintCell.setText( + AndroidUtilities.replaceSingleTag( + LocaleController.formatString(R.string.RestorePremiumHintTitle, MediaDataController.getInstance(currentAccount).getPremiumHintAnnualDiscount(false)), + Theme.key_windowBackgroundWhiteValueText, + 0, + null + ), + LocaleController.getString(R.string.RestorePremiumHintMessage) + ); + } else if (isPremiumHintVisible()) { dialogsHintCell.setVisibility(View.VISIBLE); dialogsHintCell.setOnClickListener(v -> { presentFragment(new PremiumPreviewFragment("dialogs_hint").setSelectAnnualByDefault()); @@ -4874,13 +5509,13 @@ private void updateDialogsHint() { }, 250); }); dialogsHintCell.setText( - AndroidUtilities.replaceSingleTag( - LocaleController.formatString(isPremiumHintUpgrade ? R.string.SaveOnAnnualPremiumTitle : R.string.UpgradePremiumTitle, MediaDataController.getInstance(currentAccount).getPremiumHintAnnualDiscount(false)), - Theme.key_windowBackgroundWhiteValueText, - 0, - null - ), - LocaleController.getString(isPremiumHintUpgrade ? R.string.UpgradePremiumMessage : R.string.SaveOnAnnualPremiumMessage) + AndroidUtilities.replaceSingleTag( + LocaleController.formatString(isPremiumHintUpgrade ? R.string.SaveOnAnnualPremiumTitle : R.string.UpgradePremiumTitle, MediaDataController.getInstance(currentAccount).getPremiumHintAnnualDiscount(false)), + Theme.key_windowBackgroundWhiteValueText, + 0, + null + ), + LocaleController.getString(isPremiumHintUpgrade ? R.string.UpgradePremiumMessage : R.string.SaveOnAnnualPremiumMessage) ); } else if (isCacheHintVisible()) { dialogsHintCell.setVisibility(View.VISIBLE); @@ -4892,13 +5527,13 @@ private void updateDialogsHint() { }, 250); }); dialogsHintCell.setText( - AndroidUtilities.replaceSingleTag( - LocaleController.formatString(R.string.ClearStorageHintTitle, AndroidUtilities.formatFileSize(cacheSize)), - Theme.key_windowBackgroundWhiteValueText, - 0, - null - ), - LocaleController.getString(R.string.ClearStorageHintMessage) + AndroidUtilities.replaceSingleTag( + LocaleController.formatString(R.string.ClearStorageHintTitle, AndroidUtilities.formatFileSize(cacheSize)), + Theme.key_windowBackgroundWhiteValueText, + 0, + null + ), + LocaleController.getString(R.string.ClearStorageHintMessage) ); } else { dialogsHintCell.setVisibility(View.GONE); @@ -4916,109 +5551,12 @@ private void createGroupForThis() { ChannelCreateActivity fragment = new ChannelCreateActivity(args); fragment.setOnFinishListener((fragment2, chatId) -> { Utilities.doCallbacks( - next -> { - TLRPC.Chat chat = getMessagesController().getChat(chatId); - showSendToBotAlert(chat, next, () -> { - DialogsActivity.this.removeSelfFromStack(); - fragment.removeSelfFromStack(); - fragment2.finishFragment(); - }); - }, - next -> { - progress.showDelayed(150); - if (requestPeerType.bot_participant != null && requestPeerType.bot_participant) { - TLRPC.User bot = getMessagesController().getUser(requestPeerBotId); - getMessagesController().addUserToChat(chatId, bot, 0, null, DialogsActivity.this, false, next, err -> { - next.run(); - return true; - }); - } else { - next.run(); - } - }, - next -> { - if (requestPeerType.bot_admin_rights != null) { - TLRPC.User bot = getMessagesController().getUser(requestPeerBotId); - getMessagesController().setUserAdminRole(chatId, bot, requestPeerType.bot_admin_rights, null, false, DialogsActivity.this, !(requestPeerType.bot_participant != null && requestPeerType.bot_participant), true, null, next, err -> { - next.run(); - return true; - }); - } else { - next.run(); - } - }, - next -> { - if (requestPeerType.user_admin_rights != null) { - TLRPC.Chat chat = getMessagesController().getChat(chatId); - getMessagesController().setUserAdminRole(chatId, getAccountInstance().getUserConfig().getCurrentUser(), ChatRightsEditActivity.rightsOR(chat.admin_rights, requestPeerType.user_admin_rights), null, true, DialogsActivity.this, false, true, null, next, err -> { - next.run(); - return true; - }); - } else { - next.run(); - } - }, - next -> { - progress.dismiss(); - getMessagesController().loadChannelParticipants(chatId); - DialogsActivityDelegate delegate = DialogsActivity.this.delegate; - DialogsActivity.this.removeSelfFromStack(); - fragment.removeSelfFromStack(); - fragment2.finishFragment(); - if (delegate != null) { - ArrayList keys = new ArrayList<>(); - keys.add(MessagesStorage.TopicKey.of(-chatId, 0)); - delegate.didSelectDialogs(DialogsActivity.this, keys, null, false, null); - } - } - ); - }); - presentFragment(fragment); - } else if (requestPeerType instanceof TLRPC.TL_requestPeerTypeChat) { - Bundle args = new Bundle(); - long[] array; - if (requestPeerType.bot_participant != null && requestPeerType.bot_participant) { - array = new long[]{ getUserConfig().getClientUserId(), requestPeerBotId }; - } else { - array = new long[]{ getUserConfig().getClientUserId() }; - } - args.putLongArray("result", array); - args.putInt("chatType", requestPeerType.forum != null && requestPeerType.forum ? ChatObject.CHAT_TYPE_FORUM : ChatObject.CHAT_TYPE_MEGAGROUP); - args.putBoolean("canToggleTopics", false); - GroupCreateFinalActivity activity = new GroupCreateFinalActivity(args); - activity.setDelegate(new GroupCreateFinalActivity.GroupCreateFinalActivityDelegate() { - @Override - public void didStartChatCreation() {} - @Override - public void didFailChatCreation() {} - @Override - public void didFinishChatCreation(GroupCreateFinalActivity fragment, long chatId) { - BaseFragment[] lastFragments = new BaseFragment[] { fragment, null }; - Utilities.doCallbacks( - next -> { - if (requestPeerType.has_username != null && requestPeerType.has_username) { - Bundle args = new Bundle(); - args.putInt("step", 1); - args.putLong("chat_id", chatId); - args.putBoolean("forcePublic", requestPeerType.has_username); - ChannelCreateActivity fragment2 = new ChannelCreateActivity(args); - fragment2.setOnFinishListener((_fragment, _chatId) -> next.run()); - presentFragment(fragment2); - lastFragments[1] = fragment2; - } else { - next.run(); - } - }, next -> { TLRPC.Chat chat = getMessagesController().getChat(chatId); showSendToBotAlert(chat, next, () -> { DialogsActivity.this.removeSelfFromStack(); - if (lastFragments[1] != null) { - lastFragments[0].removeSelfFromStack(); - lastFragments[1].finishFragment(); - } else { - lastFragments[0].finishFragment(); - } + fragment.removeSelfFromStack(); + fragment2.finishFragment(); }); }, next -> { @@ -5047,7 +5585,7 @@ public void didFinishChatCreation(GroupCreateFinalActivity fragment, long chatId next -> { if (requestPeerType.user_admin_rights != null) { TLRPC.Chat chat = getMessagesController().getChat(chatId); - getMessagesController().setUserAdminRole(chatId, getAccountInstance().getUserConfig().getCurrentUser(), ChatRightsEditActivity.rightsOR(chat.admin_rights, requestPeerType.user_admin_rights), null, false, DialogsActivity.this, false, true, null, next, err -> { + getMessagesController().setUserAdminRole(chatId, getAccountInstance().getUserConfig().getCurrentUser(), ChatRightsEditActivity.rightsOR(chat.admin_rights, requestPeerType.user_admin_rights), null, true, DialogsActivity.this, false, true, null, next, err -> { next.run(); return true; }); @@ -5060,18 +5598,119 @@ public void didFinishChatCreation(GroupCreateFinalActivity fragment, long chatId getMessagesController().loadChannelParticipants(chatId); DialogsActivityDelegate delegate = DialogsActivity.this.delegate; DialogsActivity.this.removeSelfFromStack(); - if (lastFragments[1] != null) { - lastFragments[0].removeSelfFromStack(); - lastFragments[1].finishFragment(); - } else { - lastFragments[0].finishFragment(); - } + fragment.removeSelfFromStack(); + fragment2.finishFragment(); if (delegate != null) { ArrayList keys = new ArrayList<>(); keys.add(MessagesStorage.TopicKey.of(-chatId, 0)); delegate.didSelectDialogs(DialogsActivity.this, keys, null, false, null); } } + ); + }); + presentFragment(fragment); + } else if (requestPeerType instanceof TLRPC.TL_requestPeerTypeChat) { + Bundle args = new Bundle(); + long[] array; + if (requestPeerType.bot_participant != null && requestPeerType.bot_participant) { + array = new long[]{getUserConfig().getClientUserId(), requestPeerBotId}; + } else { + array = new long[]{getUserConfig().getClientUserId()}; + } + args.putLongArray("result", array); + args.putInt("chatType", requestPeerType.forum != null && requestPeerType.forum ? ChatObject.CHAT_TYPE_FORUM : ChatObject.CHAT_TYPE_MEGAGROUP); + args.putBoolean("canToggleTopics", false); + GroupCreateFinalActivity activity = new GroupCreateFinalActivity(args); + activity.setDelegate(new GroupCreateFinalActivity.GroupCreateFinalActivityDelegate() { + @Override + public void didStartChatCreation() { + } + + @Override + public void didFailChatCreation() { + } + + @Override + public void didFinishChatCreation(GroupCreateFinalActivity fragment, long chatId) { + BaseFragment[] lastFragments = new BaseFragment[]{fragment, null}; + Utilities.doCallbacks( + next -> { + if (requestPeerType.has_username != null && requestPeerType.has_username) { + Bundle args = new Bundle(); + args.putInt("step", 1); + args.putLong("chat_id", chatId); + args.putBoolean("forcePublic", requestPeerType.has_username); + ChannelCreateActivity fragment2 = new ChannelCreateActivity(args); + fragment2.setOnFinishListener((_fragment, _chatId) -> next.run()); + presentFragment(fragment2); + lastFragments[1] = fragment2; + } else { + next.run(); + } + }, + next -> { + TLRPC.Chat chat = getMessagesController().getChat(chatId); + showSendToBotAlert(chat, next, () -> { + DialogsActivity.this.removeSelfFromStack(); + if (lastFragments[1] != null) { + lastFragments[0].removeSelfFromStack(); + lastFragments[1].finishFragment(); + } else { + lastFragments[0].finishFragment(); + } + }); + }, + next -> { + progress.showDelayed(150); + if (requestPeerType.bot_participant != null && requestPeerType.bot_participant) { + TLRPC.User bot = getMessagesController().getUser(requestPeerBotId); + getMessagesController().addUserToChat(chatId, bot, 0, null, DialogsActivity.this, false, next, err -> { + next.run(); + return true; + }); + } else { + next.run(); + } + }, + next -> { + if (requestPeerType.bot_admin_rights != null) { + TLRPC.User bot = getMessagesController().getUser(requestPeerBotId); + getMessagesController().setUserAdminRole(chatId, bot, requestPeerType.bot_admin_rights, null, false, DialogsActivity.this, !(requestPeerType.bot_participant != null && requestPeerType.bot_participant), true, null, next, err -> { + next.run(); + return true; + }); + } else { + next.run(); + } + }, + next -> { + if (requestPeerType.user_admin_rights != null) { + TLRPC.Chat chat = getMessagesController().getChat(chatId); + getMessagesController().setUserAdminRole(chatId, getAccountInstance().getUserConfig().getCurrentUser(), ChatRightsEditActivity.rightsOR(chat.admin_rights, requestPeerType.user_admin_rights), null, false, DialogsActivity.this, false, true, null, next, err -> { + next.run(); + return true; + }); + } else { + next.run(); + } + }, + next -> { + progress.dismiss(); + getMessagesController().loadChannelParticipants(chatId); + DialogsActivityDelegate delegate = DialogsActivity.this.delegate; + DialogsActivity.this.removeSelfFromStack(); + if (lastFragments[1] != null) { + lastFragments[0].removeSelfFromStack(); + lastFragments[1].finishFragment(); + } else { + lastFragments[0].finishFragment(); + } + if (delegate != null) { + ArrayList keys = new ArrayList<>(); + keys.add(MessagesStorage.TopicKey.of(-chatId, 0)); + delegate.didSelectDialogs(DialogsActivity.this, keys, null, false, null); + } + } ); } }); @@ -5092,19 +5731,43 @@ private void updateContextViewPosition() { if (searchTabsView != null && searchTabsView.getVisibility() != View.GONE) { searchTabsHeight = searchTabsView.getMeasuredHeight(); } + float storiesHeight = 0; + if (hasStories) { + storiesHeight = AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP); + } + float totalOffset; + if (hasStories) { + totalOffset = scrollYOffset * (1f - searchAnimationProgress) + + storiesHeight * (1f - searchAnimationProgress) + + filtersTabsHeight * (1f - searchAnimationProgress) + + searchTabsHeight * searchAnimationProgress + tabsYOffset; + } else { + totalOffset = scrollYOffset + + filtersTabsHeight * (1f - searchAnimationProgress) + + searchTabsHeight * searchAnimationProgress + tabsYOffset; + } + totalOffset += storiesOverscroll; + + if (dialogsHintCell != null) { + if (rightSlidingDialogContainer != null && rightSlidingDialogContainer.hasFragment()) { + totalOffset -= dialogsHintCell.getMeasuredHeight() * rightSlidingDialogContainer.openedProgress; + } + dialogsHintCell.setTranslationY(totalOffset); + totalOffset += dialogsHintCell.getMeasuredHeight(); + } if (fragmentContextView != null) { float from = 0; if (fragmentLocationContextView != null && fragmentLocationContextView.getVisibility() == View.VISIBLE) { from += AndroidUtilities.dp(36); } - fragmentContextView.setTranslationY(from + fragmentContextView.getTopPadding() + actionBar.getTranslationY() + filtersTabsHeight * (1f - searchAnimationProgress) + searchTabsHeight * searchAnimationProgress + tabsYOffset); + fragmentContextView.setTranslationY(from + fragmentContextView.getTopPadding() + totalOffset); } if (fragmentLocationContextView != null) { float from = 0; if (fragmentContextView != null && fragmentContextView.getVisibility() == View.VISIBLE) { from += AndroidUtilities.dp(fragmentContextView.getStyleHeight()) + fragmentContextView.getTopPadding(); } - fragmentLocationContextView.setTranslationY(from + fragmentLocationContextView.getTopPadding() + actionBar.getTranslationY() + filtersTabsHeight * (1f - searchAnimationProgress) + searchTabsHeight * searchAnimationProgress + tabsYOffset); + fragmentLocationContextView.setTranslationY(from + fragmentLocationContextView.getTopPadding() + totalOffset); } } @@ -5344,6 +6007,10 @@ public void onItemClick(int id) { } else if (id == 3) { showSearch(true, true, true); actionBar.openSearchField(true); + } else if (id == 5) { + presentFragment(new ArchiveSettingsActivity()); + } else if (id == 6) { + showArchiveHelp(); } else if (id == nekox_scanqr) { if (Build.VERSION.SDK_INT >= 23) { if (getParentActivity().checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { @@ -5364,7 +6031,7 @@ public boolean processQr(String text, Runnable onLoadEnd) { return false; } }); - } else if (id >= 10 && id < 10 + accounts) { + } else if (id >= 10 && id < 10 + accounts) { if (getParentActivity() == null) { return; } @@ -5404,7 +6071,10 @@ public boolean processQr(String text, Runnable onLoadEnd) { } else { did = 0; } - getUndoView().showWithAction(did, UndoView.ACTION_ADDED_TO_FOLDER, alwaysShow.size(), filter, null, null); + final UndoView undoView = getUndoView(); + if (undoView != null) { + undoView.showWithAction(did, UndoView.ACTION_ADDED_TO_FOLDER, alwaysShow.size(), filter, null, null); + } } else { presentFragment(new FilterCreateActivity(null, alwaysShow)); } @@ -5412,7 +6082,7 @@ public boolean processQr(String text, Runnable onLoadEnd) { }); showDialog(sheet); } else if (id == remove_from_folder) { - MessagesController.DialogFilter filter = getMessagesController().dialogFilters.get(viewPages[0].selectedType); + MessagesController.DialogFilter filter = getMessagesController().getDialogFilters().get(viewPages[0].selectedType); ArrayList neverShow = FiltersListBottomSheet.getDialogsCount(DialogsActivity.this, filter, selectedDialogs, false, false); int currentCount; @@ -5432,6 +6102,9 @@ public boolean processQr(String text, Runnable onLoadEnd) { filter.alwaysShow.remove(did); filter.pinnedDialogs.delete(did); } + if (filter.isChatlist()) { + filter.neverShow.clear(); + } FilterCreateActivity.saveFilterToServer(filter, filter.flags, filter.emoticon, filter.name, filter.alwaysShow, filter.neverShow, filter.pinnedDialogs, false, false, true, false, false, DialogsActivity.this, null); } long did; @@ -5440,7 +6113,10 @@ public boolean processQr(String text, Runnable onLoadEnd) { } else { did = 0; } - getUndoView().showWithAction(did, UndoView.ACTION_REMOVED_FROM_FOLDER, neverShow.size(), filter, null, null); + final UndoView undoView = getUndoView(); + if (undoView != null) { + undoView.showWithAction(did, UndoView.ACTION_REMOVED_FROM_FOLDER, neverShow.size(), filter, null, null); + } hideActionMode(false); } else if (id == pin || id == read || id == delete || id == clear || id == mute || id == archive || id == block || id == archive2 || id == pin2) { performSelectedDialogsAction(selectedDialogs, id, true, false); @@ -5450,16 +6126,47 @@ public boolean processQr(String text, Runnable onLoadEnd) { } } - private void switchToCurrentSelectedMode(boolean animated) { + public void closeSearching() { + if (actionBar != null && actionBar.isSearchFieldVisible()) { + actionBar.closeSearchField(); + searchIsShowed = false; + updateFilterTabs(true, true); + } + } + + public void scrollToFolder(int fid) { + if (filterTabsView == null) { + updateFilterTabs(true, true); + if (filterTabsView == null) { + return; + } + } + int index = filterTabsView.getTabsCount() - 1; + ArrayList filters = getMessagesController().getDialogFilters(); + for (int i = 0; i < filters.size(); ++i) { + if (filters.get(i).id == fid) { + index = i; + break; + } + } + + FilterTabsView.Tab tab = filterTabsView.getTab(index); + if (tab != null) { + filterTabsView.scrollToTab(tab, index); + } else { + filterTabsView.selectLastTab(); + } + } + + public void switchToCurrentSelectedMode(boolean animated) { for (int a = 0; a < viewPages.length; a++) { viewPages[a].listView.stopScroll(); } int a = animated ? 1 : 0; - RecyclerView.Adapter currentAdapter = viewPages[a].listView.getAdapter(); - if (viewPages[a].selectedType < 0 || viewPages[a].selectedType >= getMessagesController().dialogFilters.size()) { + if (viewPages[a].selectedType < 0 || viewPages[a].selectedType >= getMessagesController().getDialogFilters().size()) { return; } - MessagesController.DialogFilter filter = getMessagesController().dialogFilters.get(viewPages[a].selectedType); + MessagesController.DialogFilter filter = getMessagesController().getDialogFilters().get(viewPages[a].selectedType); if (filter.isDefault()) { viewPages[a].dialogsType = initialDialogsType; viewPages[a].listView.updatePullState(); @@ -5475,7 +6182,7 @@ private void switchToCurrentSelectedMode(boolean animated) { viewPages[1].isLocked = filter.locked; viewPages[a].dialogsAdapter.setDialogsType(viewPages[a].dialogsType); - viewPages[a].layoutManager.scrollToPositionWithOffset(viewPages[a].dialogsType == DIALOGS_TYPE_DEFAULT && hasHiddenArchive() && viewPages[a].archivePullViewState == ARCHIVE_ITEM_STATE_HIDDEN ? 1 : 0, (int) actionBar.getTranslationY()); + viewPages[a].layoutManager.scrollToPositionWithOffset(viewPages[a].dialogsType == DIALOGS_TYPE_DEFAULT && hasHiddenArchive() && viewPages[a].archivePullViewState == ARCHIVE_ITEM_STATE_HIDDEN ? 1 : 0, (int) scrollYOffset); checkListLoad(viewPages[a]); } @@ -5498,15 +6205,14 @@ private void showScrollbars(boolean show) { } private void updateFilterTabs(boolean force, boolean animated) { - if (filterTabsView == null || inPreviewMode || searchIsShowed) { + if (filterTabsView == null || inPreviewMode || searchIsShowed || (rightSlidingDialogContainer != null && rightSlidingDialogContainer.hasFragment())) { return; } - if (scrimPopupWindow != null) { - scrimPopupWindow.dismiss(); - scrimPopupWindow = null; + if (filterOptions != null) { + filterOptions.dismiss(); + filterOptions = null; } - ArrayList filters = getMessagesController().dialogFilters; - SharedPreferences preferences = MessagesController.getMainSettings(currentAccount); + ArrayList filters = getMessagesController().getDialogFilters(); if (filters.size() > 1) { if (force || filterTabsView.getVisibility() != View.VISIBLE) { boolean animatedUpdateItems = animated; @@ -5527,9 +6233,9 @@ private void updateFilterTabs(boolean force, boolean animated) { MessagesController.DialogFilter dialogFilter = filters.get(a); if (filters.get(a).isDefault()) { if (filterTabsView.showAllChatsTab) - filterTabsView.addTab(a, 0, LocaleController.getString("FilterAllChats", R.string.FilterAllChats), "\uD83D\uDCAC", true, filters.get(a).locked); + filterTabsView.addTab(a, 0, LocaleController.getString("FilterAllChats", R.string.FilterAllChats), filters.get(a).emoticon, true, filters.get(a).locked); } else { - filterTabsView.addTab(a, filters.get(a).localId, filters.get(a).name, filters.get(a).emoticon == null ? "\uD83D\uDCC1" : filters.get(a).emoticon, false, filters.get(a).locked); + filterTabsView.addTab(a, filters.get(a).localId, filters.get(a).name, filters.get(a).emoticon, false, filters.get(a).locked); } } boolean updateCurrentTab = NekoConfig.hideAllTab.Bool(); @@ -5539,13 +6245,20 @@ private void updateFilterTabs(boolean force, boolean animated) { viewPages[0].selectedType = id; filterTabsView.selectTabWithStableId(filterTabsView.getStableId(0)); } else if (stableId >= 0) { + if (selectWithStableId) { + if (!filterTabsView.selectTabWithStableId(stableId)) { + while (id >= 0 && !filterTabsView.selectTabWithStableId(filterTabsView.getStableId(id))) { + id--; + } + if (id < 0) { + id = 0; + } + } + } if (filterTabsView.getStableId(viewPages[0].selectedType) != stableId) { updateCurrentTab = true; viewPages[0].selectedType = id; } - if (selectWithStableId) { - filterTabsView.selectTabWithStableId(stableId); - } } for (ViewPage viewPage : viewPages) { if (viewPage.selectedType >= filters.size()) { @@ -5576,13 +6289,13 @@ private void updateFilterTabs(boolean force, boolean animated) { } if (viewPages[0].selectedType != filterTabsView.getDefaultTabId()) { viewPages[0].selectedType = filterTabsView.getDefaultTabId(); - viewPages[0].dialogsAdapter.setDialogsType(initialDialogsType); + viewPages[0].dialogsAdapter.setDialogsType(0); viewPages[0].dialogsType = initialDialogsType; viewPages[0].dialogsAdapter.notifyDataSetChanged(); } viewPages[1].setVisibility(View.GONE); viewPages[1].selectedType = 0; - viewPages[1].dialogsAdapter.setDialogsType(initialDialogsType); + viewPages[1].dialogsAdapter.setDialogsType(0); viewPages[1].dialogsType = initialDialogsType; viewPages[1].dialogsAdapter.notifyDataSetChanged(); canShowFilterTabsView = false; @@ -5591,17 +6304,37 @@ private void updateFilterTabs(boolean force, boolean animated) { if (viewPages[a].dialogsType == DIALOGS_TYPE_DEFAULT && viewPages[a].archivePullViewState == ARCHIVE_ITEM_STATE_HIDDEN && hasHiddenArchive()) { int p = viewPages[a].layoutManager.findFirstVisibleItemPosition(); if (p == 0 || p == 1) { - viewPages[a].layoutManager.scrollToPositionWithOffset(1, (int) actionBar.getTranslationY()); + viewPages[a].layoutManager.scrollToPositionWithOffset(1, (int) scrollYOffset); } } viewPages[a].listView.setScrollingTouchSlop(RecyclerView.TOUCH_SLOP_DEFAULT); viewPages[a].listView.requestLayout(); viewPages[a].requestLayout(); } + + filterTabsView.resetTabId(); } updateDrawerSwipeEnabled(); } updateCounters(false); + + final int currentDialogsType = viewPages[0].dialogsType; + if (currentDialogsType == 7 || currentDialogsType == 8) { + MessagesController.DialogFilter currentFilter = getMessagesController().selectedDialogFilter[currentDialogsType - 7]; + if (currentFilter != null) { + boolean found = false; + for (int i = 0; i < filters.size(); ++i) { + MessagesController.DialogFilter f = filters.get(i); + if (f != null && f.id == currentFilter.id) { + found = true; + break; + } + } + if (!found) { + switchToCurrentSelectedMode(false); + } + } + } } private void updateDrawerSwipeEnabled() { @@ -5615,6 +6348,7 @@ protected void onPanTranslationUpdate(float y) { if (viewPages == null) { return; } + panTranslationY = y; if (commentView != null && commentView.isPopupShowing()) { fragmentView.setTranslationY(y); for (int a = 0; a < viewPages.length; a++) { @@ -5626,7 +6360,7 @@ protected void onPanTranslationUpdate(float y) { topBulletin.updatePosition(); } } - searchViewPager.setTranslationY(0); + searchViewPager.setTranslationY(searchViewPagerTranslationY); } else { for (int a = 0; a < viewPages.length; a++) { viewPages[a].setTranslationY(y); @@ -5637,21 +6371,24 @@ protected void onPanTranslationUpdate(float y) { topBulletin.updatePosition(); } } - searchViewPager.setTranslationY(y); + searchViewPager.setTranslationY(panTranslationY + searchViewPagerTranslationY); } } @Override public void finishFragment() { super.finishFragment(); - if (scrimPopupWindow != null) { - scrimPopupWindow.dismiss(); + if (filterOptions != null) { + filterOptions.dismiss(); } } @Override public void onResume() { super.onResume(); + if (dialogStoriesCell != null) { + dialogStoriesCell.onResume(); + } if (rightSlidingDialogContainer != null) { rightSlidingDialogContainer.onResume(); } @@ -5741,7 +6478,7 @@ public void onResume() { if (viewPages != null) { for (int a = 0; a < viewPages.length; a++) { if (viewPages[a].dialogsType == 0 && viewPages[a].archivePullViewState == ARCHIVE_ITEM_STATE_HIDDEN && viewPages[a].layoutManager.findFirstVisibleItemPosition() == 0 && hasHiddenArchive()) { - viewPages[a].layoutManager.scrollToPositionWithOffset(1, (int) actionBar.getTranslationY()); + viewPages[a].layoutManager.scrollToPositionWithOffset(1, (int) scrollYOffset); } if (a == 0) { viewPages[a].dialogsAdapter.resume(); @@ -5775,7 +6512,13 @@ public void onShow(Bulletin bulletin) { @Override public int getTopOffset(int tag) { - return (actionBar != null ? actionBar.getMeasuredHeight() + (int) actionBar.getTranslationY() : 0) + (filterTabsView != null && filterTabsView.getVisibility() == View.VISIBLE ? filterTabsView.getMeasuredHeight() : 0) + (fragmentContextView != null && fragmentContextView.isCallTypeVisible() ? AndroidUtilities.dp(fragmentContextView.getStyleHeight()) : 0) + (dialogsHintCell != null && dialogsHintCell.getVisibility() == View.VISIBLE ? dialogsHintCell.getHeight() : 0); + return ( + (actionBar != null ? actionBar.getMeasuredHeight() : 0) + + (filterTabsView != null && filterTabsView.getVisibility() == View.VISIBLE ? filterTabsView.getMeasuredHeight() : 0) + + (fragmentContextView != null && fragmentContextView.isCallTypeVisible() ? AndroidUtilities.dp(fragmentContextView.getStyleHeight()) : 0) + + (dialogsHintCell != null && dialogsHintCell.getVisibility() == View.VISIBLE ? dialogsHintCell.getHeight() : 0) + + (dialogStoriesCell != null && dialogStoriesCellVisible ? (int) ((1f - dialogStoriesCell.getCollapsedProgress()) * AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP)) : 0) + ); } }); if (searchIsShowed) { @@ -5783,6 +6526,7 @@ public int getTopOffset(int tag) { } updateVisibleRows(0, false); updateProxyButton(false, true); + updateStoriesVisibility(false); checkSuggestClearDatabase(); } @@ -5802,11 +6546,15 @@ public boolean presentFragment(BaseFragment fragment) { @Override public void onPause() { super.onPause(); + if (storiesBulletin != null) { + storiesBulletin.hide(); + storiesBulletin = null; + } if (rightSlidingDialogContainer != null) { rightSlidingDialogContainer.onPause(); } - if (scrimPopupWindow != null) { - scrimPopupWindow.dismiss(); + if (filterOptions != null) { + filterOptions.dismiss(); } if (commentView != null) { commentView.onPause(); @@ -5825,14 +6573,17 @@ public void onPause() { @Override public boolean onBackPressed() { - if (rightSlidingDialogContainer.hasFragment()) { + if (closeStoryViewer()) { + return false; + } else if (rightSlidingDialogContainer.hasFragment()) { if (rightSlidingDialogContainer.getFragment().onBackPressed()) { rightSlidingDialogContainer.finishPreview(); searchViewPager.updateTabs(); } return false; - } else if (scrimPopupWindow != null) { - scrimPopupWindow.dismiss(); + } else if (filterOptions != null) { + filterOptions.dismiss(); + filterOptions = null; return false; } else if (filterTabsView != null && filterTabsView.isEditing()) { filterTabsView.setIsEditing(false); @@ -5855,6 +6606,8 @@ public boolean onBackPressed() { } else if (commentView != null && commentView.isPopupShowing()) { commentView.hidePopup(true); return false; + } else if (dialogStoriesCell.isFullExpanded() && dialogStoriesCell.scrollToFirst()) { + return false; } return super.onBackPressed(); } @@ -5871,8 +6624,8 @@ public void onBecomeFullyHidden() { } closeSearchFieldOnHide = false; } - if (filterTabsView != null && filterTabsView.getVisibility() == View.VISIBLE && filterTabsViewIsVisible) { - int scrollY = (int) -actionBar.getTranslationY(); + if (!hasStories && filterTabsView != null && filterTabsView.getVisibility() == View.VISIBLE && filterTabsViewIsVisible) { + int scrollY = (int) -scrollYOffset; int actionBarHeight = ActionBar.getCurrentActionBarHeight(); if (scrollY != 0 && scrollY != actionBarHeight) { if (scrollY < actionBarHeight / 2) { @@ -5887,6 +6640,52 @@ public void onBecomeFullyHidden() { } } + @Override + public void onBecomeFullyVisible() { + super.onBecomeFullyVisible(); + if (isArchive()) { + SharedPreferences preferences = MessagesController.getGlobalMainSettings(); + boolean showArchiveHint = preferences.getBoolean("archivehint", true); + final boolean isEmpty = getDialogsArray(currentAccount, initialDialogsType, folderId, false).isEmpty(); + if (showArchiveHint && isEmpty) { + showArchiveHint = false; + MessagesController.getGlobalMainSettings().edit().putBoolean("archivehint", false).commit(); + } + if (showArchiveHint) { + preferences.edit().putBoolean("archivehint", false).commit(); + showArchiveHelp(); + } + if (optionsItem != null) { + if (isEmpty) { + optionsItem.hideSubItem(6); + } else { + optionsItem.showSubItem(6); + } + } + } + } + + private void showArchiveHelp() { + getContactsController().loadGlobalPrivacySetting(); + BottomSheet[] bottomSheet = new BottomSheet[1]; + ArchiveHelp archiveHelp = new ArchiveHelp(getContext(), currentAccount, getResourceProvider(), () -> { + if (bottomSheet[0] != null) { + bottomSheet[0].dismiss(); + bottomSheet[0] = null; + } + AndroidUtilities.runOnUIThread(() -> presentFragment(new ArchiveSettingsActivity()), 300); + }, () -> { + if (bottomSheet[0] != null) { + bottomSheet[0].dismiss(); + bottomSheet[0] = null; + } + }); + bottomSheet[0] = new BottomSheet.Builder(getContext(), false, getResourceProvider()) + .setCustomView(archiveHelp, Gravity.TOP | Gravity.CENTER_HORIZONTAL) + .show(); + bottomSheet[0].fixNavigationBar(Theme.getColor(Theme.key_dialogBackground)); + } + @Override public void setInPreviewMode(boolean value) { super.setInPreviewMode(value); @@ -5960,7 +6759,7 @@ private void showSearch(boolean show, boolean startFromDownloads, boolean animat onlyDialogsAdapter = onlyDialogsAdapter(); } searchViewPager.showOnlyDialogsAdapter(onlyDialogsAdapter); - whiteActionBar = !onlyDialogsAdapter; + whiteActionBar = !onlyDialogsAdapter || hasStories; if (whiteActionBar) { searchFiltersWasShowed = true; } @@ -6004,7 +6803,7 @@ private void showSearch(boolean show, boolean startFromDownloads, boolean animat searchViewPager.clear(); if (folderId != 0 && (rightSlidingDialogContainer == null || !rightSlidingDialogContainer.hasFragment())) { - FiltersView.MediaFilterData filterData = new FiltersView.MediaFilterData(R.drawable.chats_archive, LocaleController.getString("ArchiveSearchFilter", R.string.ArchiveSearchFilter), null, FiltersView.FILTER_TYPE_ARCHIVE); + FiltersView.MediaFilterData filterData = new FiltersView.MediaFilterData(R.drawable.chats_archive, R.string.ArchiveSearchFilter, null, FiltersView.FILTER_TYPE_ARCHIVE); addSearchFilter(filterData); } } else { @@ -6019,6 +6818,9 @@ private void showSearch(boolean show, boolean startFromDownloads, boolean animat if (!show && filterTabsView != null && canShowFilterTabsView) { filterTabsView.setVisibility(View.VISIBLE); } + if (!show && dialogStoriesCell != null && dialogStoriesCellVisible) { + dialogStoriesCell.setVisibility(View.VISIBLE); + } final boolean budget = SharedConfig.getDevicePerformanceClass() == SharedConfig.PERFORMANCE_CLASS_LOW || !LiteMode.isEnabled(LiteMode.FLAG_CHAT_SCALE); if (animated) { if (show) { @@ -6052,6 +6854,10 @@ private void showSearch(boolean show, boolean startFromDownloads, boolean animat animators.add(ObjectAnimator.ofFloat(rightSlidingDialogContainer, View.ALPHA, show ? 0.0f : 1.0f)); } animators.add(ObjectAnimator.ofFloat(searchViewPager, View.ALPHA, show ? 1.0f : 0.0f)); + if (hasStories) { + float translationY = AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP) + scrollYOffset; + animators.add(ObjectAnimator.ofFloat(searchViewPager, SEARCH_TRANSLATION_Y, show ? 0 : translationY)); + } if (!budget) { animators.add(ObjectAnimator.ofFloat(searchViewPager, View.SCALE_X, show ? 1.0f : 1.05f)); animators.add(ObjectAnimator.ofFloat(searchViewPager, View.SCALE_Y, show ? 1.0f : 1.05f)); @@ -6112,7 +6918,7 @@ public void onAnimationEnd(Animator animation) { searchAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - getNotificationCenter().onAnimationFinish(animationIndex); + notificationsLocker.unlock(); if (searchAnimator != animation) { return; } @@ -6122,6 +6928,9 @@ public void onAnimationEnd(Animator animation) { if (filterTabsView != null) { filterTabsView.setVisibility(View.GONE); } + if (dialogStoriesCell != null) { + dialogStoriesCell.setVisibility(View.GONE); + } searchWasFullyShowed = true; AndroidUtilities.requestAdjustResize(getParentActivity(), classGuid); searchItem.setVisibility(View.GONE); @@ -6165,7 +6974,7 @@ public void onAnimationEnd(Animator animation) { @Override public void onAnimationCancel(Animator animation) { - getNotificationCenter().onAnimationFinish(animationIndex); + notificationsLocker.unlock(); if (searchAnimator == animation) { if (show) { viewPages[0].listView.hide(); @@ -6176,7 +6985,7 @@ public void onAnimationCancel(Animator animation) { } } }); - animationIndex = getNotificationCenter().setAnimationInProgress(animationIndex, null); + notificationsLocker.lock(); searchAnimator.start(); if (tabsAlphaAnimator != null) { tabsAlphaAnimator.start(); @@ -6216,6 +7025,13 @@ public void onAnimationCancel(Animator animation) { filterTabsView.setVisibility(View.GONE); } } + if (dialogStoriesCell != null) { + if (dialogStoriesCellVisible && !show) { + dialogStoriesCell.setVisibility(View.VISIBLE); + } else { + dialogStoriesCell.setVisibility(View.GONE); + } + } searchViewPager.setVisibility(show ? View.VISIBLE : View.GONE); setSearchAnimationProgress(show ? 1f : 0, false); fragmentView.invalidate(); @@ -6238,7 +7054,7 @@ public void onAnimationCancel(Animator animation) { public boolean onlyDialogsAdapter() { int dialogsCount = getMessagesController().getTotalDialogsCount(); - return onlySelect || !searchViewPager.dialogsSearchAdapter.hasRecentSearch() || dialogsCount <= 10; + return onlySelect || !searchViewPager.dialogsSearchAdapter.hasRecentSearch() || dialogsCount <= 10 && !hasStories; } private void updateFilterTabsVisibility(boolean animated) { @@ -6268,29 +7084,28 @@ private void updateFilterTabsVisibility(boolean animated) { filterTabsView.setVisibility(View.VISIBLE); } filtersTabAnimator = ValueAnimator.ofFloat(0, 1f); - filterTabsMoveFrom = AndroidUtilities.dp(44); } else { filtersTabAnimator = ValueAnimator.ofFloat(1f, 0f); - filterTabsMoveFrom = Math.max(0, AndroidUtilities.dp(44) + actionBar.getTranslationY()); } - float animateFromScrollY = actionBar.getTranslationY(); + filterTabsMoveFrom = getActionBarMoveFrom(true); + float animateFromScrollY = scrollYOffset; filtersTabAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { filtersTabAnimator = null; - scrollAdditionalOffset = AndroidUtilities.dp(44) - filterTabsMoveFrom; + scrollAdditionalOffset = 0;//getActionBarMoveFrom(false) - filterTabsMoveFrom; if (!visible) { filterTabsView.setVisibility(View.GONE); } if (fragmentView != null) { fragmentView.requestLayout(); } - getNotificationCenter().onAnimationFinish(animationIndex); + notificationsLocker.unlock(); } }); filtersTabAnimator.addUpdateListener(valueAnimator -> { filterTabsProgress = (float) valueAnimator.getAnimatedValue(); - if (!visible) { + if (!visible && !hasStories) { setScrollY(animateFromScrollY * filterTabsProgress); } if (fragmentView != null) { @@ -6299,7 +7114,7 @@ public void onAnimationEnd(Animator animation) { }); filtersTabAnimator.setDuration(220); filtersTabAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); - animationIndex = getNotificationCenter().setAnimationInProgress(animationIndex, null); + notificationsLocker.lock(); filtersTabAnimator.start(); fragmentView.requestLayout(); } else { @@ -6392,12 +7207,14 @@ private void findAndUpdateCheckBox(long dialogId, boolean checked) { } private void checkListLoad(ViewPage viewPage) { + checkListLoad(viewPage, viewPage.layoutManager.findFirstVisibleItemPosition(), viewPage.layoutManager.findLastVisibleItemPosition()); + } + + private void checkListLoad(ViewPage viewPage, int firstVisibleItem, int lastVisibleItem) { if (tabsAnimationInProgress || startedTracking || filterTabsView != null && filterTabsView.getVisibility() == View.VISIBLE && filterTabsView.isAnimatingIndicator()) { return; } - int firstVisibleItem = viewPage.layoutManager.findFirstVisibleItemPosition(); - int lastVisibleItem = viewPage.layoutManager.findLastVisibleItemPosition(); - int visibleItemCount = Math.abs(viewPage.layoutManager.findLastVisibleItemPosition() - firstVisibleItem) + 1; + int visibleItemCount = Math.abs(lastVisibleItem - firstVisibleItem) + 1; if (lastVisibleItem != RecyclerView.NO_POSITION) { RecyclerView.ViewHolder holder = viewPage.listView.findViewHolderForAdapterPosition(lastVisibleItem); if (floatingForceVisible = holder != null && holder.getItemViewType() == 11) { @@ -6411,9 +7228,9 @@ private void checkListLoad(ViewPage viewPage) { boolean load = false; boolean loadFromCache = false; if (viewPage.dialogsType == 7 || viewPage.dialogsType == 8) { - ArrayList dialogFilters = getMessagesController().dialogFilters; + ArrayList dialogFilters = getMessagesController().getDialogFilters(); if (viewPage.selectedType >= 0 && viewPage.selectedType < dialogFilters.size()) { - MessagesController.DialogFilter filter = getMessagesController().dialogFilters.get(viewPage.selectedType); + MessagesController.DialogFilter filter = dialogFilters.get(viewPage.selectedType); if ((filter.flags & MessagesController.DIALOG_FILTER_FLAG_EXCLUDE_ARCHIVED) == 0) { if (visibleItemCount > 0 && lastVisibleItem >= getDialogsArray(currentAccount, viewPage.dialogsType, 1, dialogsListFrozen).size() - 10 || visibleItemCount == 0 && !getMessagesController().isDialogsEndReached(1)) { @@ -6607,7 +7424,7 @@ private void onItemClick(View view, int position, RecyclerListView.Adapter adapt topicsFragment.setForwardFromDialogFragment(DialogsActivity.this); presentFragment(topicsFragment); } else { - didSelectResult(dialogId, 0,true, false); + didSelectResult(dialogId, 0, true, false); } } } else { @@ -6730,10 +7547,10 @@ public void setOpenedDialogId(long dialogId, int topicId) { } private boolean onItemLongClick(RecyclerListView listView, View view, int position, float x, float y, int dialogsType, RecyclerListView.Adapter adapter) { - if (getParentActivity() == null) { + if (getParentActivity() == null || view instanceof DialogsHintCell) { return false; } - if (!actionBar.isActionModeShowed() && !AndroidUtilities.isTablet() && !onlySelect && view instanceof DialogCell && !getMessagesController().isForum(((DialogCell)view).getDialogId())) { + if (!actionBar.isActionModeShowed() && !AndroidUtilities.isTablet() && !onlySelect && view instanceof DialogCell && !getMessagesController().isForum(((DialogCell) view).getDialogId()) && !rightSlidingDialogContainer.hasFragment()) { DialogCell cell = (DialogCell) view; if (cell.isPointInsideAvatar(x, y)) { return showChatPreview(cell); @@ -6774,7 +7591,7 @@ private boolean onItemLongClick(RecyclerListView listView, View view, int positi showDialog(alertDialog); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } return true; } @@ -6809,7 +7626,7 @@ private boolean onItemLongClick(RecyclerListView listView, View view, int positi } if (onlySelect) { - if (initialDialogsType != 3 && initialDialogsType != 10) { + if (initialDialogsType != DIALOGS_TYPE_FORWARD && initialDialogsType != DIALOGS_TYPE_WIDGET) { return false; } if (!validateSlowModeDialog(dialog.id)) { @@ -6854,11 +7671,7 @@ private void onArchiveLongPress(View view) { if (viewPages[a].dialogsType != 0 || viewPages[a].getVisibility() != View.VISIBLE) { continue; } - View child = viewPages[a].listView.getChildAt(0); - DialogCell dialogCell = null; - if (child instanceof DialogCell && ((DialogCell) child).isFolderCell()) { - dialogCell = (DialogCell) child; - } + DialogCell dialogCell = findArchiveDialogCell(viewPages[a]); viewPages[a].listView.toggleArchiveHidden(true, dialogCell); } } @@ -6866,6 +7679,16 @@ private void onArchiveLongPress(View view) { showDialog(builder.create()); } + private DialogCell findArchiveDialogCell(ViewPage page) { + RecyclerListView listView = page.listView; + for (int i = 0; i < listView.getChildCount(); i++) { + View child = listView.getChildAt(i); + if (child instanceof DialogCell && ((DialogCell) child).isFolderCell()) { + return (DialogCell) child; + } + } + return null; + } public boolean showChatPreview(DialogCell cell) { long dialogId = cell.getDialogId(); @@ -7035,8 +7858,8 @@ public boolean showChatPreview(DialogCell cell) { TLRPC.Dialog dialog = getMessagesController().dialogs_dict.get(dialogId); boolean containsFilter; final MessagesController.DialogFilter filter = ( - (containsFilter = (viewPages[0].dialogsType == 7 || viewPages[0].dialogsType == 8) && (!actionBar.isActionModeShowed() || actionBar.isActionModeShowed(null))) ? - getMessagesController().selectedDialogFilter[viewPages[0].dialogsType == 8 ? 1 : 0] : null + (containsFilter = (viewPages[0].dialogsType == 7 || viewPages[0].dialogsType == 8) && (!actionBar.isActionModeShowed() || actionBar.isActionModeShowed(null))) ? + getMessagesController().selectedDialogFilter[viewPages[0].dialogsType == 8 ? 1 : 0] : null ); if (!isDialogPinned(dialog)) { int pinnedCount = 0; @@ -7111,9 +7934,13 @@ public boolean showChatPreview(DialogCell cell) { if (DialogObject.isEncryptedDialog(dialogId)) { encryptedChat = getMessagesController().getEncryptedChat(DialogObject.getEncryptedChatId(dialogId)); } + UndoView undoView = getUndoView(); + if (undoView == null) { + return; + } if (!isDialogPinned(dialog)) { pinDialog(dialogId, true, filter, minPinnedNum, true); - getUndoView().showWithAction(0, UndoView.ACTION_PIN_DIALOGS, 1, 1600, null, null); + undoView.showWithAction(0, UndoView.ACTION_PIN_DIALOGS, 1, 1600, null, null); if (filter != null) { if (encryptedChat != null) { if (!filter.alwaysShow.contains(encryptedChat.user_id)) { @@ -7127,7 +7954,7 @@ public boolean showChatPreview(DialogCell cell) { } } else { pinDialog(dialogId, false, filter, minPinnedNum, true); - getUndoView().showWithAction(0, UndoView.ACTION_UNPIN_DIALOGS, 1, 1600, null, null); + undoView.showWithAction(0, UndoView.ACTION_UNPIN_DIALOGS, 1, 1600, null, null); } if (filter != null) { FilterCreateActivity.saveFilterToServer(filter, filter.flags, filter.emoticon, filter.name, filter.alwaysShow, filter.neverShow, filter.pinnedDialogs, false, false, true, true, false, DialogsActivity.this, null); @@ -7168,9 +7995,9 @@ public boolean showChatPreview(DialogCell cell) { } ActionBarMenuSubItem deleteItem = new ActionBarMenuSubItem(getParentActivity(), false, true); - deleteItem.setIconColor(getThemedColor(Theme.key_dialogRedIcon)); - deleteItem.setTextColor(getThemedColor(Theme.key_dialogTextRed)); - deleteItem.setSelectorColor(Theme.multAlpha(getThemedColor(Theme.key_dialogTextRed), .12f)); + deleteItem.setIconColor(getThemedColor(Theme.key_text_RedRegular)); + deleteItem.setTextColor(getThemedColor(Theme.key_text_RedBold)); + deleteItem.setSelectorColor(Theme.multAlpha(getThemedColor(Theme.key_text_RedBold), .12f)); deleteItem.setTextAndIcon(LocaleController.getString("Delete", R.string.Delete), R.drawable.msg_delete); deleteItem.setMinimumWidth(160); deleteItem.setOnClickListener(e -> { @@ -7193,7 +8020,8 @@ public boolean showChatPreview(DialogCell cell) { chatActivity[0].allowExpandPreviewByClick = true; try { chatActivity[0].getAvatarContainer().getAvatarImageView().performAccessibilityAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); - } catch (Exception ignore) {} + } catch (Exception ignore) { + } } } return true; @@ -7202,11 +8030,52 @@ public boolean showChatPreview(DialogCell cell) { } private void updateFloatingButtonOffset() { - floatingButtonContainer.setTranslationY(floatingButtonTranslation - floatingButtonPanOffset - Math.max(additionalFloatingTranslation, additionalFloatingTranslation2) * (1f - floatingButtonHideProgress)); + if (floatingButtonContainer != null) { + floatingButtonContainer.setTranslationY(floatingButtonTranslation - floatingButtonPanOffset - Math.max(additionalFloatingTranslation, additionalFloatingTranslation2) * (1f - floatingButtonHideProgress)); + if (storyHint != null) { + storyHint.setTranslationY(floatingButtonContainer.getTranslationY()); + } + if (manyStoriesHint != null) { + manyStoriesHint.setTranslationY(floatingButtonContainer.getTranslationY()); + } + } + if (floatingButton2Container != null) { + floatingButton2Container.setTranslationY(floatingButtonTranslation - floatingButtonPanOffset - Math.max(additionalFloatingTranslation, additionalFloatingTranslation2) * (1f - floatingButtonHideProgress) + AndroidUtilities.dp(44) * floatingButtonHideProgress); + } + } + + public boolean storiesEnabled = true; + private void updateStoriesPosting() { + final boolean storiesEnabled = getMessagesController().storiesEnabled(); + if (this.storiesEnabled != storiesEnabled) { + if (floatingButton2Container != null) { + floatingButton2Container.setVisibility(onlySelect && initialDialogsType != 10 || folderId != 0 || !storiesEnabled || (searchItem != null && searchItem.isSearchFieldVisible()) ? View.GONE : View.VISIBLE); + } + updateFloatingButtonOffset(); + if (!this.storiesEnabled && storiesEnabled && storyHint != null) { + storyHint.show(); + } + this.storiesEnabled = storiesEnabled; + } + + if (floatingButton == null || floatingButtonContainer == null) { + return; + } + + if (initialDialogsType == DIALOGS_TYPE_WIDGET) { + floatingButton.setImageResource(R.drawable.floating_check); + floatingButtonContainer.setContentDescription(LocaleController.getString("Done", R.string.Done)); + } else if (storiesEnabled) { + floatingButton.setAnimation(R.raw.write_contacts_fab_icon_camera, 56, 56); + floatingButtonContainer.setContentDescription(LocaleController.getString("AccDescrCaptureStory", R.string.AccDescrCaptureStory)); + } else { + floatingButton.setAnimation(R.raw.write_contacts_fab_icon, 52, 52); + floatingButtonContainer.setContentDescription(LocaleController.getString("NewMessageTitle", R.string.NewMessageTitle)); + } } private boolean hasHiddenArchive() { - return !onlySelect && initialDialogsType == 0 && folderId == 0 && getMessagesController().hasHiddenArchive(); + return !onlySelect && initialDialogsType == DIALOGS_TYPE_DEFAULT && folderId == 0 && getMessagesController().hasHiddenArchive(); } private boolean waitingForDialogsAnimationEnd(ViewPage viewPage) { @@ -7228,32 +8097,23 @@ private void checkAnimationFinished() { } private void setScrollY(float value) { - if (fragmentView == null) { - return; + if (viewPages != null) { + int glowOffset = viewPages[0].listView.getPaddingTop() + (int) value; + for (int a = 0; a < viewPages.length; a++) { + viewPages[a].listView.setTopGlowOffset(glowOffset); + } } - if (scrimView != null) { - scrimView.getLocationInWindow(scrimViewLocation); + if (fragmentView == null || value == scrollYOffset) { + return; } - actionBar.setTranslationY(value); + scrollYOffset = value; if (topBulletin != null) { topBulletin.updatePosition(); } - if (filterTabsView != null) { - filterTabsView.setTranslationY(value); - } - if (dialogsHintCell != null) { - dialogsHintCell.setTranslationY(value); - } if (animatedStatusView != null) { animatedStatusView.translateY2((int) value); animatedStatusView.setAlpha(1f - -value / ActionBar.getCurrentActionBarHeight()); } - updateContextViewPosition(); - if (viewPages != null) { - for (int a = 0; a < viewPages.length; a++) { - viewPages[a].listView.setTopGlowOffset(viewPages[a].listView.getPaddingTop() + (int) value); - } - } fragmentView.invalidate(); } @@ -7309,7 +8169,7 @@ public void onTransitionAnimationEnd(boolean isOpen, boolean backward) { } private void resetScroll() { - if (actionBar.getTranslationY() == 0) { + if (scrollYOffset == 0 || hasStories) { return; } AnimatorSet animatorSet = new AnimatorSet(); @@ -7336,8 +8196,22 @@ private void hideActionMode(boolean animateCheck) { if (actionBarColorAnimator != null) { actionBarColorAnimator.cancel(); } + float translateListHeight = 0; + if (hasStories) { + setScrollY(-getMaxScrollYOffset()); + for (int i = 0; i < viewPages.length; i++) { + if (viewPages[i] != null) { + viewPages[i].listView.cancelClickRunnables(true); + } + } + translateListHeight = Math.max(0, AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP) + scrollYOffset); + } + float finalTranslateListHeight = translateListHeight; actionBarColorAnimator = ValueAnimator.ofFloat(progressToActionMode, 0); actionBarColorAnimator.addUpdateListener(valueAnimator -> { + if (hasStories) { + viewPages[0].setTranslationY(finalTranslateListHeight * (1f - progressToActionMode)); + } progressToActionMode = (float) valueAnimator.getAnimatedValue(); for (int i = 0; i < actionBar.getChildCount(); i++) { if (actionBar.getChildAt(i).getVisibility() == View.VISIBLE && actionBar.getChildAt(i) != actionBar.getActionMode() && actionBar.getChildAt(i) != actionBar.getBackButton()) { @@ -7348,6 +8222,25 @@ private void hideActionMode(boolean animateCheck) { fragmentView.invalidate(); } }); + actionBarColorAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + actionBarColorAnimator = null; + actionModeFullyShowed = false; + if (hasStories) { + scrollAdditionalOffset = -(AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP) - finalTranslateListHeight); + + viewPages[0].setTranslationY(0); + for (int i = 0; i < viewPages.length; i++) { + if (viewPages[i] != null) { + viewPages[i].listView.requestLayout(); + } + } + fragmentView.requestLayout(); + } + } + }); actionBarColorAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); actionBarColorAnimator.setDuration(200); actionBarColorAnimator.start(); @@ -7440,10 +8333,13 @@ private void performSelectedDialogsAction(ArrayList selectedDialogs, int a } else { undoAction = copy.size() > 1 ? UndoView.ACTION_ARCHIVE_FEW_HINT : UndoView.ACTION_ARCHIVE_HINT; } - getUndoView().showWithAction(0, undoAction, null, () -> getMessagesController().addDialogToFolder(copy, folderId == 0 ? 0 : 1, -1, null, 0)); + final UndoView undoView = getUndoView(); + if (undoView != null) { + undoView.showWithAction(0, undoAction, null, () -> getMessagesController().addDialogToFolder(copy, folderId == 0 ? 0 : 1, -1, null, 0)); + } } else { ArrayList dialogs = getMessagesController().getDialogs(folderId); - if (viewPages != null && dialogs.isEmpty()) { + if (viewPages != null && dialogs.isEmpty() && !hasStories) { viewPages[0].listView.setEmptyView(null); viewPages[0].progressView.setVisibility(View.INVISIBLE); finishFragment(); @@ -7530,21 +8426,24 @@ private void performSelectedDialogsAction(ArrayList selectedDialogs, int a return; } ArrayList didsCopy = new ArrayList<>(selectedDialogs); - getUndoView().showWithAction(didsCopy, action == delete ? UndoView.ACTION_DELETE_FEW : UndoView.ACTION_CLEAR_FEW, null, null, () -> { - if (action == delete) { - getMessagesController().setDialogsInTransaction(true); - performSelectedDialogsAction(didsCopy, action, false, false); - getMessagesController().setDialogsInTransaction(false); - getMessagesController().checkIfFolderEmpty(folderId); - if (folderId != 0 && getDialogsArray(currentAccount, viewPages[0].dialogsType, folderId, false).size() == 0) { - viewPages[0].listView.setEmptyView(null); - viewPages[0].progressView.setVisibility(View.INVISIBLE); - finishFragment(); + final UndoView undoView = getUndoView(); + if (undoView != null) { + undoView.showWithAction(didsCopy, action == delete ? UndoView.ACTION_DELETE_FEW : UndoView.ACTION_CLEAR_FEW, null, null, () -> { + if (action == delete) { + getMessagesController().setDialogsInTransaction(true); + performSelectedDialogsAction(didsCopy, action, false, false); + getMessagesController().setDialogsInTransaction(false); + getMessagesController().checkIfFolderEmpty(folderId); + if (folderId != 0 && getDialogsArray(currentAccount, viewPages[0].dialogsType, folderId, false).size() == 0) { + viewPages[0].listView.setEmptyView(null); + viewPages[0].progressView.setVisibility(View.INVISIBLE); + finishFragment(); + } + } else { + performSelectedDialogsAction(didsCopy, action, false, false); } - } else { - performSelectedDialogsAction(didsCopy, action, false, false); - } - }, null); + }, null); + } hideActionMode(action == clear); }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); @@ -7552,7 +8451,7 @@ private void performSelectedDialogsAction(ArrayList selectedDialogs, int a showDialog(alertDialog); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } return; } else if (action == block && alert) { @@ -7684,7 +8583,10 @@ private void performSelectedDialogsAction(ArrayList selectedDialogs, int a checkAnimationFinished(); } - getUndoView().showWithAction(selectedDialog, action == clear ? UndoView.ACTION_CLEAR : UndoView.ACTION_DELETE, () -> performDeleteOrClearDialogAction(action, selectedDialog, chat, isBot, param)); + final UndoView undoView = getUndoView(); + if (undoView != null) { + undoView.showWithAction(selectedDialog, action == clear ? UndoView.ACTION_CLEAR : UndoView.ACTION_DELETE, () -> performDeleteOrClearDialogAction(action, selectedDialog, chat, isBot, param)); + } ArrayList currentDialogs = new ArrayList<>(getDialogsArray(currentAccount, viewPages[0].dialogsType, folderId, false)); int currentDialogIndex = -1; @@ -7721,7 +8623,7 @@ private void performSelectedDialogsAction(ArrayList selectedDialogs, int a } } else if (action == mute) { if (count == 1 && canMuteCount == 1) { - showDialog(AlertsCreator.createMuteAlert(this, selectedDialog, 0,null), dialog12 -> hideActionMode(true)); + showDialog(AlertsCreator.createMuteAlert(this, selectedDialog, 0, null), dialog12 -> hideActionMode(true)); return; } else { if (canUnmuteCount != 0) { @@ -7730,7 +8632,7 @@ private void performSelectedDialogsAction(ArrayList selectedDialogs, int a } getNotificationsController().setDialogNotificationsSettings(selectedDialog, 0, NotificationsController.SETTING_MUTE_UNMUTE); } else if (longPress) { - showDialog(AlertsCreator.createMuteAlert(this, selectedDialogs, 0,null), dialog12 -> hideActionMode(true)); + showDialog(AlertsCreator.createMuteAlert(this, selectedDialogs, 0, null), dialog12 -> hideActionMode(true)); return; } else { if (getMessagesController().isDialogMuted(selectedDialog, 0)) { @@ -7750,15 +8652,16 @@ private void performSelectedDialogsAction(ArrayList selectedDialogs, int a } else { getMessagesController().reorderPinnedDialogs(folderId, null, 0); } - if (searchIsShowed) { - getUndoView().showWithAction(0, canPinCount != 0 ? UndoView.ACTION_PIN_DIALOGS : UndoView.ACTION_UNPIN_DIALOGS, pinnedActionCount); + UndoView undoView = getUndoView(); + if (searchIsShowed && undoView != null) { + undoView.showWithAction(0, canPinCount != 0 ? UndoView.ACTION_PIN_DIALOGS : UndoView.ACTION_UNPIN_DIALOGS, pinnedActionCount); } } if (scrollToTop) { if (initialDialogsType != 10) { hideFloatingButton(false); } - scrollToTop(); + scrollToTop(true, false); } hideActionMode(action != pin2 && action != pin && action != delete); } @@ -7838,12 +8741,12 @@ private void performDeleteOrClearDialogAction(int action, long selectedDialog, T getMessagesController().deleteDialog(selectedDialog, 0, revoke); } else { TLRPC.User currentUser = getMessagesController().getUser(getUserConfig().getClientUserId()); - getMessagesController().deleteParticipantFromChat((int) -selectedDialog, currentUser, null, revoke, false); + getMessagesController().deleteParticipantFromChat(-selectedDialog, currentUser, null, revoke, false); } } else { getMessagesController().deleteDialog(selectedDialog, 0, revoke); - if (isBot) { - getMessagesController().blockPeer((int) selectedDialog); + if (isBot && revoke) { + getMessagesController().blockPeer(selectedDialog); } } if (AndroidUtilities.isTablet()) { @@ -7908,7 +8811,7 @@ private void pinDialog(long selectedDialog, boolean pin, MessagesController.Dial if (initialDialogsType != 10) { hideFloatingButton(false); } - scrollToTop(); + scrollToTop(true, false); } else { ArrayList currentDialogs = getDialogsArray(currentAccount, viewPages[0].dialogsType, folderId, false); for (int i = 0; i < currentDialogs.size(); i++) { @@ -7928,7 +8831,7 @@ private void pinDialog(long selectedDialog, boolean pin, MessagesController.Dial viewPages[0].dialogsItemAnimator.prepareForRemove(); viewPages[0].updateList(true); - viewPages[0].layoutManager.scrollToPositionWithOffset(viewPages[0].dialogsType == 0 && hasHiddenArchive() && viewPages[0].archivePullViewState == ARCHIVE_ITEM_STATE_HIDDEN ? 1 : 0, (int) actionBar.getTranslationY()); + viewPages[0].layoutManager.scrollToPositionWithOffset(viewPages[0].dialogsType == 0 && hasHiddenArchive() && viewPages[0].archivePullViewState == ARCHIVE_ITEM_STATE_HIDDEN ? 1 : 0, (int) scrollYOffset); animate = true; } else if (currentDialogIndex >= 0 && selectedDialogIndex == currentDialogIndex) { @@ -7942,19 +8845,19 @@ private void pinDialog(long selectedDialog, boolean pin, MessagesController.Dial } } - private void scrollToTop() { - int scrollDistance = viewPages[0].layoutManager.findFirstVisibleItemPosition() * AndroidUtilities.dp(SharedConfig.useThreeLinesLayout ? 78 : 72); + public void scrollToTop(boolean animated, boolean expandStories) { int position = viewPages[0].dialogsType == 0 && hasHiddenArchive() && viewPages[0].archivePullViewState == ARCHIVE_ITEM_STATE_HIDDEN ? 1 : 0; - RecyclerView.ItemAnimator animator = viewPages[0].listView.getItemAnimator(); -// if (animator != null) { -// animator.endAnimations(); -// } - if (scrollDistance >= viewPages[0].listView.getMeasuredHeight() * 1.2f) { + int offset = 0; + if (hasStories && !expandStories && !dialogStoriesCell.isExpanded()) { + offset = -AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP); + } + if (animated) { viewPages[0].scrollHelper.setScrollDirection(RecyclerAnimationScrollHelper.SCROLL_DIRECTION_UP); - viewPages[0].scrollHelper.scrollToPosition(position, 0, false, true); + viewPages[0].scrollHelper.scrollToPosition(position, offset, false, true); resetScroll(); } else { - viewPages[0].listView.smoothScrollToPosition(position); + viewPages[0].layoutManager.scrollToPositionWithOffset(position, offset); + resetScroll(); } } @@ -8169,7 +9072,8 @@ private void updateCounters(boolean hide) { try { final int dialogsCount = getDialogsArray(currentAccount, viewPages[0].dialogsAdapter.getDialogsType(), folderId, dialogsListFrozen).size(); cantRemoveFromFolder = count >= dialogsCount; - } catch (Exception ignore) {} + } catch (Exception ignore) { + } } if (cantRemoveFromFolder) { removeFromFolderItem.setVisibility(View.GONE); @@ -8246,7 +9150,9 @@ private void showOrUpdateActionMode(long dialogId, View cell) { AndroidUtilities.hideKeyboard(fragmentView.findFocus()); actionBar.setActionModeOverrideColor(Theme.getColor(Theme.key_windowBackgroundWhite)); actionBar.showActionMode(); - resetScroll(); + if (!hasStories) { + resetScroll(); + } if (menuDrawable != null) { actionBar.setBackButtonContentDescription(LocaleController.getString("AccDescrGoBack", R.string.AccDescrGoBack)); } @@ -8277,8 +9183,25 @@ private void showOrUpdateActionMode(long dialogId, View cell) { actionBarColorAnimator.cancel(); } actionBarColorAnimator = ValueAnimator.ofFloat(progressToActionMode, 1f); + float translateListHeight = 0; + if (hasStories) { + for (int i = 0; i < viewPages.length; i++) { + if (viewPages[i] != null) { + viewPages[i].listView.cancelClickRunnables(true); + } + } + translateListHeight = Math.max(0, AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP) + scrollYOffset); + if (translateListHeight != 0) { + actionModeAdditionalHeight = (int) translateListHeight; + fragmentView.requestLayout(); + } + } + float finalTranslateListHeight = translateListHeight; actionBarColorAnimator.addUpdateListener(valueAnimator -> { progressToActionMode = (float) valueAnimator.getAnimatedValue(); + if (hasStories) { + viewPages[0].setTranslationY(-finalTranslateListHeight * progressToActionMode); + } for (int i = 0; i < actionBar.getChildCount(); i++) { if (actionBar.getChildAt(i).getVisibility() == View.VISIBLE && actionBar.getChildAt(i) != actionBar.getActionMode() && actionBar.getChildAt(i) != actionBar.getBackButton()) { actionBar.getChildAt(i).setAlpha(1f - progressToActionMode); @@ -8288,6 +9211,25 @@ private void showOrUpdateActionMode(long dialogId, View cell) { fragmentView.invalidate(); } }); + actionBarColorAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + actionBarColorAnimator = null; + actionModeAdditionalHeight = 0; + actionModeFullyShowed = true; + if (hasStories) { + scrollAdditionalOffset = AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP) - finalTranslateListHeight; + viewPages[0].setTranslationY(0); + for (int i = 0; i < viewPages.length; i++) { + if (viewPages[i] != null) { + viewPages[i].listView.requestLayout(); + } + } + fragmentView.requestLayout(); + } + } + }); actionBarColorAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); actionBarColorAnimator.setDuration(200); actionBarColorAnimator.start(); @@ -8328,8 +9270,85 @@ protected RecyclerListView getSearchListView() { return searchViewPager.searchListView; } + public void createUndoView() { + if (undoView[0] != null) { + return; + } + + Context context = getContext(); + if (context == null) { + return; + } + + for (int a = 0; a < 2; a++) { + undoView[a] = new UndoView(context) { + @Override + public void setTranslationY(float translationY) { + super.setTranslationY(translationY); + if (this == undoView[0] && (undoView[1] == null || undoView[1].getVisibility() != VISIBLE)) { + additionalFloatingTranslation = getMeasuredHeight() + AndroidUtilities.dp(8) - translationY; + if (additionalFloatingTranslation < 0) { + additionalFloatingTranslation = 0; + } + if (!floatingHidden) { + updateFloatingButtonOffset(); + } + } + } + + @Override + protected boolean canUndo() { + for (int a = 0; a < viewPages.length; a++) { + if (viewPages[a].dialogsItemAnimator.isRunning()) { + return false; + } + } + return true; + } + + @Override + protected void onRemoveDialogAction(long currentDialogId, int action) { + if (action == UndoView.ACTION_DELETE || action == UndoView.ACTION_DELETE_FEW) { + debugLastUpdateAction = 1; + setDialogsListFrozen(true); + if (frozenDialogsList != null) { + int selectedIndex = -1; + for (int i = 0; i < frozenDialogsList.size(); i++) { + if (frozenDialogsList.get(i).id == currentDialogId) { + selectedIndex = i; + break; + } + } + + if (selectedIndex >= 0) { + TLRPC.Dialog dialog = frozenDialogsList.remove(selectedIndex); + viewPages[0].dialogsAdapter.notifyDataSetChanged(); + int finalSelectedIndex = selectedIndex; + AndroidUtilities.runOnUIThread(() -> { + if (frozenDialogsList != null) { + if (finalSelectedIndex < 0 || finalSelectedIndex >= frozenDialogsList.size()) { + return; + } + frozenDialogsList.add(finalSelectedIndex, dialog); + viewPages[0].updateList(true); + } + }); + } else { + setDialogsListFrozen(false); + } + } + checkAnimationFinished(); + } + } + }; + ((ContentView) fragmentView).addView(undoView[a], ++undoViewIndex, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM | Gravity.LEFT, 8, 0, 8, 8)); + } + } + + @Nullable public UndoView getUndoView() { - if (undoView[0].getVisibility() == View.VISIBLE) { + createUndoView(); + if (undoView[0] != null && undoView[0].getVisibility() == View.VISIBLE) { UndoView old = undoView[0]; undoView[0] = undoView[1]; undoView[1] = old; @@ -8346,15 +9365,14 @@ private void updateProxyButton(boolean animated, boolean force) { return; } boolean showDownloads = false; + for (int i = 0; i < getDownloadController().downloadingFiles.size(); i++) { + if (getFileLoader().isLoadingFile(getDownloadController().downloadingFiles.get(i).getFileName())) { + showDownloads = true; + break; + } + } if (NaConfig.INSTANCE.getAlwaysShowDownloadIcon().Bool()) { showDownloads = true; - } else { - for (int i = 0; i < getDownloadController().downloadingFiles.size(); i++){ - if (getFileLoader().isLoadingFile(getDownloadController().downloadingFiles.get(i).getFileName())) { - showDownloads = true; - break; - } - } } if (!searching && (getDownloadController().hasUnviewedDownloads() || showDownloads || (downloadsItem.getVisibility() == View.VISIBLE && downloadsItem.getAlpha() == 1 && !force))) { downloadsItemVisible = true; @@ -8502,7 +9520,7 @@ public void onAnimationEnd(Animator animation) { writeButtonContainer.setVisibility(View.VISIBLE); commentViewAnimator = new AnimatorSet(); commentViewAnimator.playTogether( - ObjectAnimator.ofFloat(commentView, View.TRANSLATION_Y, commentView.getMeasuredHeight(),0), + ObjectAnimator.ofFloat(commentView, View.TRANSLATION_Y, commentView.getMeasuredHeight(), 0), ObjectAnimator.ofFloat(writeButtonContainer, View.SCALE_X, 1f), ObjectAnimator.ofFloat(writeButtonContainer, View.SCALE_Y, 1f), ObjectAnimator.ofFloat(writeButtonContainer, View.ALPHA, 1f), @@ -8583,8 +9601,8 @@ protected void onDialogDismiss(Dialog dialog) { @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - if (scrimPopupWindow != null) { - scrimPopupWindow.dismiss(); + if (filterOptions != null) { + filterOptions.dismiss(); } if (!onlySelect && floatingButtonContainer != null) { floatingButtonContainer.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @@ -8651,7 +9669,7 @@ private void reloadViewPageDialogs(ViewPage viewPage, boolean newMessage) { if (viewPage.dialogsType == 0 && hasHiddenArchive() && viewPage.listView.getChildCount() == 0 && viewPage.archivePullViewState == ARCHIVE_ITEM_STATE_HIDDEN) { LinearLayoutManager layoutManager = (LinearLayoutManager) viewPage.listView.getLayoutManager(); - layoutManager.scrollToPositionWithOffset(1, (int) actionBar.getTranslationY()); + layoutManager.scrollToPositionWithOffset(1, (int) scrollYOffset); } // NekoX TODO: Remove satouriko's fix wrong tab dialogs after forward @@ -8869,9 +9887,13 @@ public void didReceivedNotification(int id, int account, Object... args) { } getMessagesController().checkIfFolderEmpty(folderId); }; + createUndoView(); if (undoView[0] != null) { if (!ChatObject.isForum(chat)) { - getUndoView().showWithAction(dialogId, UndoView.ACTION_DELETE, deleteRunnable); + UndoView undoView = getUndoView(); + if (undoView != null) { + undoView.showWithAction(dialogId, UndoView.ACTION_DELETE, deleteRunnable); + } } else { deleteRunnable.run(); } @@ -8956,10 +9978,44 @@ public void onAnimationEnd(Animator animation) { updateStatus((TLRPC.User) args[0], true); } else if (id == NotificationCenter.currentUserPremiumStatusChanged) { updateStatus(UserConfig.getInstance(account).getCurrentUser(), true); + updateStoriesPosting(); } else if (id == NotificationCenter.onDatabaseReset) { dialogsLoaded.set(currentAccount, false); loadDialogs(getAccountInstance()); getMessagesController().loadPinnedDialogs(folderId, 0, null); + } else if (id == NotificationCenter.chatlistFolderUpdate) { + int filterId = (int) args[0]; + for (int i = 0; i < viewPages.length; ++i) { + ViewPage viewPage = viewPages[i]; + if (viewPage != null && (viewPage.dialogsType == 7 || viewPage.dialogsType == 8)) { + MessagesController.DialogFilter filter = getMessagesController().selectedDialogFilter[viewPage.dialogsType - 7]; + if (filter != null && filterId == filter.id) { + viewPage.updateList(true); + break; + } + } + } + } else if (id == NotificationCenter.dialogTranslate) { + long dialogId = (long) args[0]; + for (int i = 0; i < viewPages.length; ++i) { + ViewPage viewPage = viewPages[i]; + if (viewPage.listView != null) { + for (int j = 0; j < viewPage.listView.getChildCount(); ++j) { + View child = viewPage.listView.getChildAt(j); + if (child instanceof DialogCell) { + DialogCell dialogCell = (DialogCell) child; + if (dialogId == dialogCell.getDialogId()) { + dialogCell.buildLayout(); + break; + } + } + } + } + } + } else if (id == NotificationCenter.storiesUpdated) { + updateStoriesVisibility(wasDrawn); + } else if (id == NotificationCenter.storiesEnabledUpdate) { + updateStoriesPosting(); } } @@ -9037,7 +10093,7 @@ private boolean showSuggestion(String suggestion) { } private void showFiltersHint() { - if (askingForPermissions || !getMessagesController().dialogFiltersLoaded || !getMessagesController().showFiltersTooltip || filterTabsView == null || !getMessagesController().dialogFilters.isEmpty() || isPaused || !getUserConfig().filtersLoaded || inPreviewMode) { + if (askingForPermissions || !getMessagesController().dialogFiltersLoaded || !getMessagesController().showFiltersTooltip || filterTabsView == null || !getMessagesController().getDialogFilters().isEmpty() || isPaused || !getUserConfig().filtersLoaded || inPreviewMode) { return; } SharedPreferences preferences = MessagesController.getGlobalMainSettings(); @@ -9045,7 +10101,12 @@ private void showFiltersHint() { return; } preferences.edit().putBoolean("filterhint", true).apply(); - AndroidUtilities.runOnUIThread(() -> getUndoView().showWithAction(0, UndoView.ACTION_FILTERS_AVAILABLE, null, () -> presentFragment(new FiltersSetupActivity())), 1000); + AndroidUtilities.runOnUIThread(() -> { + UndoView undoView = getUndoView(); + if (undoView != null) { + undoView.showWithAction(0, UndoView.ACTION_FILTERS_AVAILABLE, null, () -> presentFragment(new FiltersSetupActivity())); + } + }, 1000); } private void setDialogsListFrozen(boolean frozen, boolean notify) { @@ -9060,7 +10121,11 @@ private void setDialogsListFrozen(boolean frozen, boolean notify) { dialogsListFrozen = frozen; viewPages[0].dialogsAdapter.setDialogsListFrozen(frozen); if (!frozen && notify) { - viewPages[0].dialogsAdapter.notifyDataSetChanged(); + if (viewPages[0].listView.isComputingLayout()) { + viewPages[0].listView.post(() -> viewPages[0].dialogsAdapter.notifyDataSetChanged()); + } else { + viewPages[0].dialogsAdapter.notifyDataSetChanged(); + } } } @@ -9073,6 +10138,7 @@ public class DialogsHeader extends TLRPC.Dialog { public static final int HEADER_TYPE_MY_GROUPS = 1; public static final int HEADER_TYPE_GROUPS = 2; public int headerType; + public DialogsHeader(int type) { this.headerType = type; } @@ -9095,6 +10161,8 @@ public DialogsHeader(int type) { public static final int DIALOGS_TYPE_START_ATTACH_BOT = 14; public static final int DIALOGS_TYPE_BOT_REQUEST_PEER = 15; + private ArrayList botShareDialogs; + @NonNull public ArrayList getDialogsArray(int currentAccount, int dialogsType, int folderId, boolean frozen) { if (frozen && frozenDialogsList != null) { @@ -9103,7 +10171,7 @@ public ArrayList getDialogsArray(int currentAccount, int dialogsTy MessagesController messagesController = AccountInstance.getInstance(currentAccount).getMessagesController(); if (dialogsType == DIALOGS_TYPE_DEFAULT) { return messagesController.getDialogs(folderId); - } else if (dialogsType == DIALOGS_TYPE_BOT_SHARE || dialogsType == DIALOGS_TYPE_WIDGET || dialogsType == DIALOGS_TYPE_IMPORT_HISTORY) { + } else if (dialogsType == DIALOGS_TYPE_WIDGET || dialogsType == DIALOGS_TYPE_IMPORT_HISTORY) { return messagesController.dialogsServerOnly; } else if (dialogsType == DIALOGS_TYPE_ADD_USERS_TO) { ArrayList dialogs = new ArrayList<>(messagesController.dialogsCanAddUsers.size() + messagesController.dialogsMyChannels.size() + messagesController.dialogsMyGroups.size() + 2); @@ -9121,7 +10189,7 @@ public ArrayList getDialogsArray(int currentAccount, int dialogsTy for (int i = 0; i < count; ++i) { TLRPC.Dialog dialog = messagesController.dialogsCanAddUsers.get(i); if (allowChannels && ChatObject.isChannelAndNotMegaGroup(-dialog.id, currentAccount) || - allowGroups && (ChatObject.isMegagroup(currentAccount, -dialog.id) || !ChatObject.isChannel(-dialog.id, currentAccount))) { + allowGroups && (ChatObject.isMegagroup(currentAccount, -dialog.id) || !ChatObject.isChannel(-dialog.id, currentAccount))) { if (first) { dialogs.add(new DialogsHeader(DialogsHeader.HEADER_TYPE_GROUPS)); first = false; @@ -9144,27 +10212,50 @@ public ArrayList getDialogsArray(int currentAccount, int dialogsTy if (dialogFilter == null) { return messagesController.getDialogs(folderId); } else { + if (initialDialogsType == DIALOGS_TYPE_FORWARD) { + return dialogFilter.dialogsForward; + } return dialogFilter.dialogs; } } else if (dialogsType == DIALOGS_TYPE_BLOCK) { return messagesController.dialogsForBlock; - } else if (dialogsType == DIALOGS_TYPE_START_ATTACH_BOT) { - ArrayList dialogs = new ArrayList<>(); + } else if (dialogsType == DIALOGS_TYPE_BOT_SHARE || dialogsType == DIALOGS_TYPE_START_ATTACH_BOT) { + if (botShareDialogs != null) { + return botShareDialogs; + } + botShareDialogs = new ArrayList<>(); if (allowUsers || allowBots) { for (TLRPC.Dialog d : messagesController.dialogsUsersOnly) { TLRPC.User user = messagesController.getUser(d.id); if (user != null && !UserObject.isUserSelf(user) && (user.bot ? allowBots : allowUsers)) { - dialogs.add(d); + botShareDialogs.add(d); } } } - if (allowGroups) { - dialogs.addAll(messagesController.dialogsGroupsOnly); + if (allowGroups || (allowLegacyGroups && allowMegagroups)) { + for (TLRPC.Dialog d : messagesController.dialogsGroupsOnly) { + TLRPC.Chat chat = messagesController.getChat(-d.id); + if (chat != null && !ChatObject.isChannelAndNotMegaGroup(chat) && messagesController.canAddToForward(d)) { + botShareDialogs.add(d); + } + } + } else if (allowLegacyGroups || allowMegagroups) { + for (TLRPC.Dialog d : messagesController.dialogsGroupsOnly) { + TLRPC.Chat chat = messagesController.getChat(-d.id); + if (chat != null && !ChatObject.isChannelAndNotMegaGroup(chat) && messagesController.canAddToForward(d) && (allowLegacyGroups && !ChatObject.isMegagroup(chat) || allowMegagroups && ChatObject.isMegagroup(chat))) { + botShareDialogs.add(d); + } + } } if (allowChannels) { - dialogs.addAll(messagesController.dialogsChannelsOnly); + for (TLRPC.Dialog d : messagesController.dialogsChannelsOnly) { + if (messagesController.canAddToForward(d)) { + botShareDialogs.add(d); + } + } } - return dialogs; + getMessagesController().sortDialogsList(botShareDialogs); + return botShareDialogs; } else if (dialogsType == DIALOGS_TYPE_BOT_REQUEST_PEER) { ArrayList dialogs = new ArrayList<>(); TLRPC.User bot = messagesController.getUser(requestPeerBotId); @@ -9217,24 +10308,24 @@ public ArrayList getDialogsArray(int currentAccount, int dialogsTy private boolean meetRequestPeerRequirements(TLRPC.User user) { TLRPC.TL_requestPeerTypeUser type = (TLRPC.TL_requestPeerTypeUser) requestPeerType; return ( - user != null && - !UserObject.isReplyUser(user) && - !UserObject.isDeleted(user) && - (type.bot == null || type.bot == user.bot) && - (type.premium == null || type.premium == user.premium) + user != null && + !UserObject.isReplyUser(user) && + !UserObject.isDeleted(user) && + (type.bot == null || type.bot == user.bot) && + (type.premium == null || type.premium == user.premium) ); } private boolean meetRequestPeerRequirements(TLRPC.User bot, TLRPC.Chat chat) { return ( - chat != null && - ChatObject.isChannelAndNotMegaGroup(chat) == requestPeerType instanceof TLRPC.TL_requestPeerTypeBroadcast && - (requestPeerType.creator == null || !requestPeerType.creator || chat.creator) && - (requestPeerType.bot_participant == null || !requestPeerType.bot_participant || getMessagesController().isInChatCached(chat, bot) || ChatObject.canAddBotsToChat(chat)) && - (requestPeerType.has_username == null || requestPeerType.has_username == (ChatObject.getPublicUsername(chat) != null)) && - (requestPeerType.forum == null || requestPeerType.forum == ChatObject.isForum(chat)) && - (requestPeerType.user_admin_rights == null || getMessagesController().matchesAdminRights(chat, getUserConfig().getCurrentUser(), requestPeerType.user_admin_rights)) && - (requestPeerType.bot_admin_rights == null || getMessagesController().matchesAdminRights(chat, bot, requestPeerType.bot_admin_rights) || ChatObject.canAddAdmins(chat)) + chat != null && + ChatObject.isChannelAndNotMegaGroup(chat) == requestPeerType instanceof TLRPC.TL_requestPeerTypeBroadcast && + (requestPeerType.creator == null || !requestPeerType.creator || chat.creator) && + (requestPeerType.bot_participant == null || !requestPeerType.bot_participant || getMessagesController().isInChatCached(chat, bot) || ChatObject.canAddBotsToChat(chat)) && + (requestPeerType.has_username == null || requestPeerType.has_username == (ChatObject.getPublicUsername(chat) != null)) && + (requestPeerType.forum == null || requestPeerType.forum == ChatObject.isForum(chat)) && + (requestPeerType.user_admin_rights == null || getMessagesController().matchesAdminRights(chat, getUserConfig().getCurrentUser(), requestPeerType.user_admin_rights)) && + (requestPeerType.bot_admin_rights == null || getMessagesController().matchesAdminRights(chat, bot, requestPeerType.bot_admin_rights) || ChatObject.canAddAdmins(chat)) ); } @@ -9261,7 +10352,7 @@ private void updatePasscodeButton() { } private void setFloatingProgressVisible(boolean visible, boolean animate) { - if (floatingButton == null || floatingProgressView == null) { + if (floatingButton2 == null || floating2ProgressView == null) { return; } if (animate) { @@ -9276,30 +10367,30 @@ private void setFloatingProgressVisible(boolean visible, boolean animate) { floatingProgressAnimator = new AnimatorSet(); floatingProgressAnimator.playTogether( - ObjectAnimator.ofFloat(floatingButton, View.ALPHA, visible ? 0f : 1f), - ObjectAnimator.ofFloat(floatingButton, View.SCALE_X, visible ? 0.1f : 1f), - ObjectAnimator.ofFloat(floatingButton, View.SCALE_Y, visible ? 0.1f : 1f), - ObjectAnimator.ofFloat(floatingProgressView, View.ALPHA, visible ? 1f : 0f), - ObjectAnimator.ofFloat(floatingProgressView, View.SCALE_X, visible ? 1f : 0.1f), - ObjectAnimator.ofFloat(floatingProgressView, View.SCALE_Y, visible ? 1f : 0.1f) + ObjectAnimator.ofFloat(floatingButton2, View.ALPHA, visible ? 0f : 1f), + ObjectAnimator.ofFloat(floatingButton2, View.SCALE_X, visible ? 0.1f : 1f), + ObjectAnimator.ofFloat(floatingButton2, View.SCALE_Y, visible ? 0.1f : 1f), + ObjectAnimator.ofFloat(floating2ProgressView, View.ALPHA, visible ? 1f : 0f), + ObjectAnimator.ofFloat(floating2ProgressView, View.SCALE_X, visible ? 1f : 0.1f), + ObjectAnimator.ofFloat(floating2ProgressView, View.SCALE_Y, visible ? 1f : 0.1f) ); floatingProgressAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { - floatingProgressView.setVisibility(View.VISIBLE); - floatingButton.setVisibility(View.VISIBLE); + floating2ProgressView.setVisibility(View.VISIBLE); + floatingButton2.setVisibility(View.VISIBLE); } @Override public void onAnimationEnd(Animator animation) { if (animation == floatingProgressAnimator) { if (visible) { - if (floatingButton != null) { - floatingButton.setVisibility(View.GONE); + if (floatingButton2 != null) { + floatingButton2.setVisibility(View.GONE); } } else { - if (floatingButton != null) { - floatingProgressView.setVisibility(View.GONE); + if (floatingButton2 != null) { + floating2ProgressView.setVisibility(View.GONE); } } floatingProgressAnimator = null; @@ -9316,25 +10407,25 @@ public void onAnimationEnd(Animator animation) { floatingProgressVisible = visible; if (visible) { - floatingButton.setAlpha(0f); - floatingButton.setScaleX(0.1f); - floatingButton.setScaleY(0.1f); - floatingButton.setVisibility(View.GONE); - - floatingProgressView.setAlpha(1f); - floatingProgressView.setScaleX(1f); - floatingProgressView.setScaleY(1f); - floatingProgressView.setVisibility(View.VISIBLE); + floatingButton2.setAlpha(0f); + floatingButton2.setScaleX(0.1f); + floatingButton2.setScaleY(0.1f); + floatingButton2.setVisibility(View.GONE); + + floating2ProgressView.setAlpha(1f); + floating2ProgressView.setScaleX(1f); + floating2ProgressView.setScaleY(1f); + floating2ProgressView.setVisibility(View.VISIBLE); } else { - floatingButton.setAlpha(1f); - floatingButton.setScaleX(1f); - floatingButton.setScaleY(1f); - floatingButton.setVisibility(View.VISIBLE); + floatingButton2.setAlpha(1f); + floatingButton2.setScaleX(1f); + floatingButton2.setScaleY(1f); + floatingButton2.setVisibility(View.VISIBLE); - floatingProgressView.setAlpha(0f); - floatingProgressView.setScaleX(0.1f); - floatingProgressView.setScaleY(0.1f); - floatingProgressView.setVisibility(View.GONE); + floating2ProgressView.setAlpha(0f); + floating2ProgressView.setScaleX(0.1f); + floating2ProgressView.setScaleY(0.1f); + floating2ProgressView.setVisibility(View.GONE); } } } @@ -9359,6 +10450,15 @@ private void hideFloatingButton(boolean hide) { animatorSet.setInterpolator(floatingInterpolator); floatingButtonContainer.setClickable(!hide); animatorSet.start(); + + if (hide) { + if (storyHint != null) { + storyHint.hide(); + } + if (manyStoriesHint != null) { + manyStoriesHint.hide(); + } + } } public float getContactsAlpha() { @@ -9473,15 +10573,7 @@ private void updateVisibleRows(int mask, boolean animated) { if ((mask & MessagesController.UPDATE_MASK_CHECK) != 0) { cell.setChecked(false, (mask & MessagesController.UPDATE_MASK_CHAT) != 0); } else { - if ((mask & MessagesController.UPDATE_MASK_NEW_MESSAGE) != 0) { - if (list.getAdapter() != null) { - viewPage.updateList(false); - break; - } - if (viewPages[c].isDefaultDialogType() && AndroidUtilities.isTablet()) { - cell.setDialogSelected(cell.getDialogId() == openedDialogId.dialogId); - } - } else if ((mask & MessagesController.UPDATE_MASK_SELECT_DIALOG) != 0) { + if ((mask & MessagesController.UPDATE_MASK_SELECT_DIALOG) != 0) { if (viewPages[c].isDefaultDialogType() && AndroidUtilities.isTablet()) { cell.setDialogSelected(cell.getDialogId() == openedDialogId.dialogId); } @@ -9549,6 +10641,10 @@ public boolean isArchive() { return folderId == 1; } + public int getType() { + return initialDialogsType; + } + public void setInitialSearchType(int type) { this.initialSearchType = type; } @@ -9603,14 +10699,20 @@ public void didSelectResult(final long dialogId, int topicId, boolean useAlert, user = getMessagesController().getUser(dialogId); chat = null; if (!user.mutual_contact) { - getUndoView().showWithAction(dialogId, UndoView.ACTION_IMPORT_NOT_MUTUAL, null); + final UndoView undoView = getUndoView(); + if (undoView != null) { + undoView.showWithAction(dialogId, UndoView.ACTION_IMPORT_NOT_MUTUAL, null); + } return; } } else { user = null; chat = getMessagesController().getChat(-dialogId); if (!ChatObject.hasAdminRights(chat) || !ChatObject.canChangeChatInfo(chat)) { - getUndoView().showWithAction(dialogId, UndoView.ACTION_IMPORT_GROUP_NOT_ADMIN, null); + final UndoView undoView = getUndoView(); + if (undoView != null) { + undoView.showWithAction(dialogId, UndoView.ACTION_IMPORT_GROUP_NOT_ADMIN, null); + } return; } } @@ -9698,7 +10800,7 @@ public void didSelectResult(final long dialogId, int topicId, boolean useAlert, } builder.setTitle(title); builder.setMessage(AndroidUtilities.replaceTags(message)); - builder.setPositiveButton(buttonText, (dialogInterface, i) -> didSelectResult(dialogId, topicId,false, false, topicsFragment)); + builder.setPositiveButton(buttonText, (dialogInterface, i) -> didSelectResult(dialogId, topicId, false, false, topicsFragment)); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); Dialog dialog = builder.create(); if (showDialog(dialog) == null) { @@ -9751,17 +10853,17 @@ private void showSendToBotAlert(TLRPC.User user, Runnable ok, Runnable cancel) { TLRPC.User bot = getMessagesController().getUser(requestPeerBotId); showDialog( - new AlertDialog.Builder(getContext()) - .setTitle(LocaleController.formatString(R.string.AreYouSureSendChatToBotTitle, UserObject.getFirstName(user), UserObject.getFirstName(bot))) - .setMessage(TextUtils.concat( - AndroidUtilities.replaceTags(LocaleController.formatString(R.string.AreYouSureSendChatToBotMessage, UserObject.getFirstName(user), UserObject.getFirstName(bot))) - )) - .setPositiveButton(LocaleController.formatString("Send", R.string.Send), (di, p) -> ok.run()) - .setNegativeButton(LocaleController.formatString("Cancel", R.string.Cancel), (di, p) -> { - if (cancel != null) { - cancel.run(); - } - }).create() + new AlertDialog.Builder(getContext()) + .setTitle(LocaleController.formatString(R.string.AreYouSureSendChatToBotTitle, UserObject.getFirstName(user), UserObject.getFirstName(bot))) + .setMessage(TextUtils.concat( + AndroidUtilities.replaceTags(LocaleController.formatString(R.string.AreYouSureSendChatToBotMessage, UserObject.getFirstName(user), UserObject.getFirstName(bot))) + )) + .setPositiveButton(LocaleController.formatString("Send", R.string.Send), (di, p) -> ok.run()) + .setNegativeButton(LocaleController.formatString("Cancel", R.string.Cancel), (di, p) -> { + if (cancel != null) { + cancel.run(); + } + }).create() ); } @@ -9770,26 +10872,26 @@ private void showSendToBotAlert(TLRPC.Chat chat, Runnable ok, Runnable cancel) { final boolean isChannel = ChatObject.isChannelAndNotMegaGroup(chat); showDialog( - new AlertDialog.Builder(getContext()) - .setTitle(LocaleController.formatString(R.string.AreYouSureSendChatToBotTitle, chat.title, UserObject.getFirstName(bot))) - .setMessage(TextUtils.concat( - AndroidUtilities.replaceTags(LocaleController.formatString(R.string.AreYouSureSendChatToBotMessage, chat.title, UserObject.getFirstName(bot))), - ( - requestPeerType.bot_participant != null && requestPeerType.bot_participant && !getMessagesController().isInChatCached(chat, bot) || - requestPeerType.bot_admin_rights != null - ) ? - TextUtils.concat("\n\n", AndroidUtilities.replaceTags( - (requestPeerType.bot_admin_rights == null) ? - LocaleController.formatString(R.string.AreYouSureSendChatToBotAdd, UserObject.getFirstName(bot), chat.title) : - LocaleController.formatString(R.string.AreYouSureSendChatToBotAddRights, UserObject.getFirstName(bot), chat.title, RequestPeerRequirementsCell.rightsToString(requestPeerType.bot_admin_rights, isChannel)) - )) : "" - )) - .setPositiveButton(LocaleController.formatString("Send", R.string.Send), (di, p) -> ok.run()) - .setNegativeButton(LocaleController.formatString("Cancel", R.string.Cancel), (di, p) -> { - if (cancel != null) { - cancel.run(); - } - }).create() + new AlertDialog.Builder(getContext()) + .setTitle(LocaleController.formatString(R.string.AreYouSureSendChatToBotTitle, chat.title, UserObject.getFirstName(bot))) + .setMessage(TextUtils.concat( + AndroidUtilities.replaceTags(LocaleController.formatString(R.string.AreYouSureSendChatToBotMessage, chat.title, UserObject.getFirstName(bot))), + ( + requestPeerType.bot_participant != null && requestPeerType.bot_participant && !getMessagesController().isInChatCached(chat, bot) || + requestPeerType.bot_admin_rights != null + ) ? + TextUtils.concat("\n\n", AndroidUtilities.replaceTags( + (requestPeerType.bot_admin_rights == null) ? + LocaleController.formatString(R.string.AreYouSureSendChatToBotAdd, UserObject.getFirstName(bot), chat.title) : + LocaleController.formatString(R.string.AreYouSureSendChatToBotAddRights, UserObject.getFirstName(bot), chat.title, RequestPeerRequirementsCell.rightsToString(requestPeerType.bot_admin_rights, isChannel)) + )) : "" + )) + .setPositiveButton(LocaleController.formatString("Send", R.string.Send), (di, p) -> ok.run()) + .setNegativeButton(LocaleController.formatString("Cancel", R.string.Cancel), (di, p) -> { + if (cancel != null) { + cancel.run(); + } + }).create() ); } @@ -9956,10 +11058,9 @@ public ArrayList getThemeDescriptions() { } if (viewPages != null) { for (int a = 0; a < viewPages.length; a++) { - if (viewPages[a].pullForegroundDrawable == null) { - continue; + if (viewPages[a].pullForegroundDrawable != null) { + viewPages[a].pullForegroundDrawable.updateColors(); } - viewPages[a].pullForegroundDrawable.updateColors(); } } if (actionBar != null) { @@ -9972,18 +11073,16 @@ public ArrayList getThemeDescriptions() { updateStatus(UserConfig.getInstance(currentAccount).getCurrentUser(), false); } - if (scrimPopupWindowItems != null) { - for (int a = 0; a < scrimPopupWindowItems.length; a++) { - if (scrimPopupWindowItems[a] != null) { - scrimPopupWindowItems[a].setColors(Theme.getColor(Theme.key_actionBarDefaultSubmenuItem), Theme.getColor(Theme.key_actionBarDefaultSubmenuItemIcon)); - scrimPopupWindowItems[a].setSelectorColor(Theme.getColor(Theme.key_dialogButtonSelector)); - } - } - } - if (scrimPopupWindow != null) { - final View contentView = scrimPopupWindow.getContentView(); - contentView.setBackgroundColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuBackground)); - contentView.invalidate(); +// if (scrimPopupWindowItems != null) { +// for (int a = 0; a < scrimPopupWindowItems.length; a++) { +// if (scrimPopupWindowItems[a] != null) { +// scrimPopupWindowItems[a].setColors(Theme.getColor(Theme.key_actionBarDefaultSubmenuItem), Theme.getColor(Theme.key_actionBarDefaultSubmenuItemIcon)); +// scrimPopupWindowItems[a].setSelectorColor(Theme.getColor(Theme.key_dialogButtonSelector)); +// } +// } +// } + if (filterOptions != null) { + filterOptions.updateColors(); } if (doneItem != null) { doneItem.setIconColor(Theme.getColor(Theme.key_actionBarDefaultIcon)); @@ -10021,6 +11120,9 @@ public ArrayList getThemeDescriptions() { } updateFloatingButtonColor(); setSearchAnimationProgress(searchAnimationProgress, false); + if (dialogStoriesCell != null) { + dialogStoriesCell.updateColors(); + } }; ArrayList arrayList = new ArrayList<>(); @@ -10089,7 +11191,7 @@ public ArrayList getThemeDescriptions() { arrayList.add(new ThemeDescription(floatingButton, ThemeDescription.FLAG_BACKGROUNDFILTER, null, null, null, null, Theme.key_chats_actionBackground)); arrayList.add(new ThemeDescription(floatingButton, ThemeDescription.FLAG_BACKGROUNDFILTER | ThemeDescription.FLAG_DRAWABLESELECTEDSTATE, null, null, null, null, Theme.key_chats_actionPressedBackground)); - arrayList.addAll(SimpleThemeDescription.createThemeDescriptions(()->{ + arrayList.addAll(SimpleThemeDescription.createThemeDescriptions(() -> { if (searchViewPager != null) { ActionBarMenu actionMode = searchViewPager.getActionMode(); if (actionMode != null) { @@ -10343,13 +11445,13 @@ public ArrayList getThemeDescriptions() { arrayList.add(new ThemeDescription(null, 0, null, null, null, null, Theme.key_dialogTextBlue)); arrayList.add(new ThemeDescription(null, 0, null, null, null, null, Theme.key_dialogTextBlue2)); arrayList.add(new ThemeDescription(null, 0, null, null, null, null, Theme.key_dialogTextBlue4)); - arrayList.add(new ThemeDescription(null, 0, null, null, null, null, Theme.key_dialogTextRed)); + arrayList.add(new ThemeDescription(null, 0, null, null, null, null, Theme.key_text_RedBold)); arrayList.add(new ThemeDescription(null, 0, null, null, null, null, Theme.key_dialogTextGray)); arrayList.add(new ThemeDescription(null, 0, null, null, null, null, Theme.key_dialogTextGray2)); arrayList.add(new ThemeDescription(null, 0, null, null, null, null, Theme.key_dialogTextGray3)); arrayList.add(new ThemeDescription(null, 0, null, null, null, null, Theme.key_dialogTextGray4)); arrayList.add(new ThemeDescription(null, 0, null, null, null, null, Theme.key_dialogIcon)); - arrayList.add(new ThemeDescription(null, 0, null, null, null, null, Theme.key_dialogRedIcon)); + arrayList.add(new ThemeDescription(null, 0, null, null, null, null, Theme.key_text_RedRegular)); arrayList.add(new ThemeDescription(null, 0, null, null, null, null, Theme.key_dialogTextHint)); arrayList.add(new ThemeDescription(null, 0, null, null, null, null, Theme.key_dialogInputField)); arrayList.add(new ThemeDescription(null, 0, null, null, null, null, Theme.key_dialogInputFieldActivated)); @@ -10450,18 +11552,37 @@ public ArrayList getThemeDescriptions() { } private void updateFloatingButtonColor() { - if (getParentActivity() == null || floatingButtonContainer == null) { + if (getParentActivity() == null) { return; } - Drawable drawable = Theme.createSimpleSelectorCircleDrawable(AndroidUtilities.dp(56), Theme.getColor(Theme.key_chats_actionBackground), Theme.getColor(Theme.key_chats_actionPressedBackground)); - if (Build.VERSION.SDK_INT < 21) { - Drawable shadowDrawable = ContextCompat.getDrawable(getParentActivity(), R.drawable.floating_shadow).mutate(); - shadowDrawable.setColorFilter(new PorterDuffColorFilter(0xff000000, PorterDuff.Mode.MULTIPLY)); - CombinedDrawable combinedDrawable = new CombinedDrawable(shadowDrawable, drawable, 0, 0); - combinedDrawable.setIconSize(AndroidUtilities.dp(56), AndroidUtilities.dp(56)); - drawable = combinedDrawable; + Drawable drawable; + if (floatingButtonContainer != null) { + drawable = Theme.createSimpleSelectorCircleDrawable(AndroidUtilities.dp(56), Theme.getColor(Theme.key_chats_actionBackground), Theme.getColor(Theme.key_chats_actionPressedBackground)); + if (Build.VERSION.SDK_INT < 21) { + Drawable shadowDrawable = ContextCompat.getDrawable(getParentActivity(), R.drawable.floating_shadow).mutate(); + shadowDrawable.setColorFilter(new PorterDuffColorFilter(0xff000000, PorterDuff.Mode.MULTIPLY)); + CombinedDrawable combinedDrawable = new CombinedDrawable(shadowDrawable, drawable, 0, 0); + combinedDrawable.setIconSize(AndroidUtilities.dp(56), AndroidUtilities.dp(56)); + drawable = combinedDrawable; + } + floatingButtonContainer.setBackground(drawable); + } + + if (floatingButton2Container != null) { + drawable = Theme.createSimpleSelectorCircleDrawable( + AndroidUtilities.dp(36), + ColorUtils.blendARGB(Theme.getColor(Theme.key_windowBackgroundWhite), Color.WHITE, 0.1f), + Theme.blendOver(Theme.getColor(Theme.key_windowBackgroundWhite), Theme.getColor(Theme.key_listSelector)) + ); + if (Build.VERSION.SDK_INT < 21) { + Drawable shadowDrawable = ContextCompat.getDrawable(getParentActivity(), R.drawable.floating_shadow).mutate(); + shadowDrawable.setColorFilter(new PorterDuffColorFilter(0xff000000, PorterDuff.Mode.MULTIPLY)); + CombinedDrawable combinedDrawable = new CombinedDrawable(shadowDrawable, drawable, 0, 0); + combinedDrawable.setIconSize(AndroidUtilities.dp(36), AndroidUtilities.dp(36)); + drawable = combinedDrawable; + } + floatingButton2Container.setBackground(drawable); } - floatingButtonContainer.setBackground(drawable); } float slideFragmentProgress = 1f; @@ -10520,12 +11641,14 @@ private void setFragmentIsSliding(boolean sliding) { if (filterTabsView != null) { filterTabsView.getListView().setLayerType(View.LAYER_TYPE_HARDWARE, null); } +// if (dialogStoriesCell != null) { +// dialogStoriesCell.setLayerType(View.LAYER_TYPE_HARDWARE, null); +// } if (fragmentView != null) { ((ViewGroup) fragmentView).setClipChildren(false); fragmentView.requestLayout(); } } else { - for (int i = 0; i < viewPages.length; i++) { ViewPage page = viewPages[i]; if (page != null) { @@ -10535,14 +11658,15 @@ private void setFragmentIsSliding(boolean sliding) { page.listView.setClipChildren(true); } } - if (actionBar != null) { actionBar.setLayerType(View.LAYER_TYPE_NONE, null); } - if (filterTabsView != null) { filterTabsView.getListView().setLayerType(View.LAYER_TYPE_NONE, null); } + if (dialogStoriesCell != null) { + dialogStoriesCell.setLayerType(View.LAYER_TYPE_NONE, null); + } if (fragmentView != null) { ((ViewGroup) fragmentView).setClipChildren(true); fragmentView.requestLayout(); @@ -10561,7 +11685,7 @@ public void onSlideProgress(boolean isOpen, float progress) { } private void setSlideTransitionProgress(float progress) { - if (SharedConfig.getDevicePerformanceClass() <= SharedConfig.PERFORMANCE_CLASS_LOW) { + if (SharedConfig.getDevicePerformanceClass() <= SharedConfig.PERFORMANCE_CLASS_LOW || slideFragmentProgress == progress) { return; } @@ -10576,6 +11700,9 @@ private void setSlideTransitionProgress(float progress) { filterTabsView.getListView().setTranslationX((isDrawerTransition ? 1 : -1) * AndroidUtilities.dp(slideAmplitudeDp) * (1f - slideFragmentProgress)); filterTabsView.invalidate(); } + if (dialogStoriesCell != null) { + dialogStoriesCell.setTranslationX((isDrawerTransition ? 1 : -1) * AndroidUtilities.dp(slideAmplitudeDp) * (1f - slideFragmentProgress)); + } if (rightSlidingDialogContainer != null && rightSlidingDialogContainer.getFragmentView() != null) { if (!rightFragmentTransitionInProgress) { rightSlidingDialogContainer.getFragmentView().setTranslationX((isDrawerTransition ? 1 : -1) * AndroidUtilities.dp(slideAmplitudeDp) * (1f - slideFragmentProgress)); @@ -10591,6 +11718,13 @@ private void setSlideTransitionProgress(float progress) { filterTabsView.getListView().setPivotY(0); filterTabsView.invalidate(); } + if (dialogStoriesCell != null) { + dialogStoriesCell.setScaleX(s); + dialogStoriesCell.setScaleY(s); + dialogStoriesCell.setTranslationX((isDrawerTransition ? AndroidUtilities.dp(4) : -AndroidUtilities.dp(4)) * (1f - slideFragmentProgress)); + dialogStoriesCell.setPivotX(isDrawerTransition ? dialogStoriesCell.getMeasuredWidth() : 0); + dialogStoriesCell.setPivotY(0); + } if (rightSlidingDialogContainer != null && rightSlidingDialogContainer.getFragmentView() != null) { if (!rightFragmentTransitionInProgress) { rightSlidingDialogContainer.getFragmentView().setScaleX(s); @@ -10698,6 +11832,167 @@ public boolean getAllowGlobalSearch() { @Override public boolean canBeginSlide() { - return !rightSlidingDialogContainer.hasFragment(); + if (rightSlidingDialogContainer.hasFragment()) { + return false; + } + if (initialDialogsType == DIALOGS_TYPE_FORWARD && filterTabsView != null && filterTabsView.getVisibility() == View.VISIBLE) { + return filterTabsView.isFirstTab(); + } + return true; + } + + public void updateStoriesVisibility(boolean animated) { + if (dialogStoriesCell == null || storiesVisibilityAnimator != null || rightSlidingDialogContainer != null && rightSlidingDialogContainer.hasFragment() || searchIsShowed || actionBar.isActionModeShowed() || onlySelect) { + return; + } + if (StoryRecorder.isVisible() || (storyViewer != null && storyViewer.isFullyVisible())) { + animated = false; + } + boolean onlySelfStories = !isArchive() && getStoriesController().hasOnlySelfStories(); + boolean newVisibility; + if (isArchive()) { + newVisibility = !getStoriesController().getHiddenList().isEmpty(); + } else { + newVisibility = !onlySelfStories && getStoriesController().hasStories(); + onlySelfStories = getStoriesController().hasOnlySelfStories(); + } + + hasOnlySlefStories = onlySelfStories; + + boolean oldStoriesCellVisibility = dialogStoriesCellVisible; + dialogStoriesCellVisible = onlySelfStories || newVisibility; + + if (newVisibility || dialogStoriesCellVisible) { + dialogStoriesCell.updateItems(animated, dialogStoriesCellVisible != oldStoriesCellVisibility); + } + if (dialogStoriesCellVisible != oldStoriesCellVisibility) { + if (animated) { + if (storiesVisibilityAnimator2 != null) { + storiesVisibilityAnimator2.cancel(); + } + if (dialogStoriesCellVisible) { + dialogStoriesCell.setVisibility(View.VISIBLE); + } + storiesVisibilityAnimator2 = ValueAnimator.ofFloat(progressToDialogStoriesCell, dialogStoriesCellVisible ? 1f : 0); + storiesVisibilityAnimator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(@NonNull ValueAnimator animation) { + progressToDialogStoriesCell = (float) animation.getAnimatedValue(); + if (fragmentView != null) { + fragmentView.invalidate(); + } + } + }); + storiesVisibilityAnimator2.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + progressToDialogStoriesCell = dialogStoriesCellVisible ? 1f : 0; + if (!dialogStoriesCellVisible) { + dialogStoriesCell.setVisibility(View.GONE); + } + if (fragmentView != null) { + fragmentView.invalidate(); + } + } + }); + storiesVisibilityAnimator2.setDuration(200); + storiesVisibilityAnimator2.setInterpolator(CubicBezierInterpolator.DEFAULT); + storiesVisibilityAnimator2.start(); + } else { + dialogStoriesCell.setVisibility(dialogStoriesCellVisible ? View.VISIBLE : View.GONE); + progressToDialogStoriesCell = dialogStoriesCellVisible ? 1f : 0; + if (fragmentView != null) { + fragmentView.invalidate(); + } + } + } + if (newVisibility == animateToHasStories) { + return; + } + animateToHasStories = newVisibility; + + if (newVisibility) { + dialogStoriesCell.setProgressToCollapse(1f, false); + } + if (animated) { + dialogStoriesCell.setVisibility(View.VISIBLE); + float fromScrollY = -scrollYOffset; + float toScrollY; + if (!newVisibility) { + toScrollY = getMaxScrollYOffset(); + } else { + toScrollY = 0; + } + storiesVisibilityAnimator = ValueAnimator.ofFloat(0f, 1f); + storiesVisibilityAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + + int currentValue = (int) fromScrollY; + + @Override + public void onAnimationUpdate(@NonNull ValueAnimator animation) { + progressToShowStories = (float) animation.getAnimatedValue(); + if (!newVisibility) { + progressToShowStories = 1f - progressToShowStories; + } + int newValue = (int) AndroidUtilities.lerp(fromScrollY, toScrollY, (float) animation.getAnimatedValue()); + int dy = newValue - currentValue; + currentValue = newValue; + viewPages[0].listView.scrollBy(0, dy); + if (fragmentView != null) { + fragmentView.invalidate(); + } + } + }); + storiesVisibilityAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + storiesVisibilityAnimator = null; + hasStories = newVisibility; + if (!hasStories && !hasOnlySlefStories) { + dialogStoriesCell.setVisibility(View.GONE); + } + if (!newVisibility) { + setScrollY(0); + scrollAdditionalOffset = AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP); + } else { + scrollAdditionalOffset = -AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP); + setScrollY(-getMaxScrollYOffset()); + } + for (int i = 0; i < viewPages.length; i++) { + if (viewPages[i] != null) { + viewPages[i].listView.requestLayout(); + } + } + if (fragmentView != null) { + fragmentView.requestLayout(); + } + } + }); + storiesVisibilityAnimator.setDuration(200); + storiesVisibilityAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); + storiesVisibilityAnimator.start(); + } else { + progressToShowStories = newVisibility ? 1f : 0; + hasStories = newVisibility; + dialogStoriesCell.setVisibility(hasStories || hasOnlySlefStories ? View.VISIBLE : View.GONE); + if (!newVisibility) { + setScrollY(0); + } else { + scrollAdditionalOffset = -AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP); + setScrollY(-getMaxScrollYOffset()); + } + for (int i = 0; i < viewPages.length; i++) { + if (viewPages[i] != null) { + viewPages[i].listView.requestLayout(); + } + } +// if (viewPages[0] != null) { +// viewPages[0].layoutManager.scrollToPositionWithOffset(hasHiddenArchive() ? 1 : 0, 0/*(int) -scrollYOffset*/); +// } + if (fragmentView != null) { + fragmentView.requestLayout(); + fragmentView.invalidate(); + } + } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/DilogCacheBottomSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/DilogCacheBottomSheet.java index de37c7018b..5160481779 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/DilogCacheBottomSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/DilogCacheBottomSheet.java @@ -72,7 +72,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int textInfoPrivacyCell.setFixedSize(12); CombinedDrawable combinedDrawable = new CombinedDrawable( new ColorDrawable(Theme.getColor(Theme.key_windowBackgroundGray)), - Theme.getThemedDrawable(parent.getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow) + Theme.getThemedDrawableByKey(parent.getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow) ); combinedDrawable.setFullsize(true); textInfoPrivacyCell.setBackgroundDrawable(combinedDrawable); @@ -131,10 +131,10 @@ protected void onAvatarClick() { } linearLayout.addView(circleDiagramView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 0, 16, 0, 16)); CheckBoxCell lastCreatedCheckbox = null; - for (int a = 0; a < 7; a++) { + for (int a = 0; a < 8; a++) { long size = 0; String name; - String color; + int color; if (a == CacheControlActivity.TYPE_PHOTOS) { name = LocaleController.getString("LocalPhotoCache", R.string.LocalPhotoCache); @@ -154,6 +154,9 @@ protected void onAvatarClick() { } else if (a == CacheControlActivity.TYPE_ANIMATED_STICKERS_CACHE) { name = LocaleController.getString("LocalStickersCache", R.string.LocalStickersCache); color = Theme.key_statisticChartLine_orange; + } else if (a == CacheControlActivity.TYPE_STORIES) { + name = LocaleController.getString("LocalStoriesCache", R.string.LocalStoriesCache); + color = Theme.key_statisticChartLine_indigo; } else { name = LocaleController.getString("LocalMiscellaneousCache", R.string.LocalMiscellaneousCache); color = Theme.key_statisticChartLine_purple; @@ -169,7 +172,7 @@ protected void onAvatarClick() { if (size > 0) { clearViewData[a] = new StorageDiagramView.ClearViewData(circleDiagramView); clearViewData[a].size = size; - clearViewData[a].color = color; + clearViewData[a].colorKey = color; CheckBoxCell checkBoxCell = new CheckBoxCell(context, 4, 21, null); lastCreatedCheckbox = checkBoxCell; checkBoxCell.setTag(a); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/EditWidgetActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/EditWidgetActivity.java index a918eae0ec..43d501751c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/EditWidgetActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/EditWidgetActivity.java @@ -230,7 +230,7 @@ public WidgetPreviewCell(Context context) { } updateDialogs(); - shadowDrawable = Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow); + shadowDrawable = Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow); } public void updateDialogs() { @@ -935,7 +935,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType switch (viewType) { case 0: view = new TextInfoPrivacyCell(mContext); - view.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + view.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); break; case 1: view = new TextCell(mContext); @@ -986,7 +986,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } case 1: { TextCell cell = (TextCell) holder.itemView; - cell.setColors(null, Theme.key_windowBackgroundWhiteBlueText4); + cell.setColors(-1, Theme.key_windowBackgroundWhiteBlueText4); Drawable drawable1 = mContext.getResources().getDrawable(R.drawable.poll_add_circle); Drawable drawable2 = mContext.getResources().getDrawable(R.drawable.poll_add_plus); drawable1.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_switchTrackChecked), PorterDuff.Mode.MULTIPLY)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/FeaturedStickersActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/FeaturedStickersActivity.java index 81a3febf30..4a4bd938a9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/FeaturedStickersActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/FeaturedStickersActivity.java @@ -247,7 +247,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType case 1: default: view = new TextInfoPrivacyCell(mContext); - view.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + view.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); break; } view.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/FilterChatlistActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/FilterChatlistActivity.java new file mode 100644 index 0000000000..5c9d2f7239 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/FilterChatlistActivity.java @@ -0,0 +1,1350 @@ +package org.telegram.ui; + +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.AndroidUtilities.lerp; +import static org.telegram.ui.Components.TextStyleSpan.FLAG_STYLE_SPOILER; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.Intent; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.Rect; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.RippleDrawable; +import android.os.Build; +import android.text.Editable; +import android.text.InputType; +import android.text.SpannableString; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.HapticFeedbackConstants; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.view.WindowManager; +import android.view.inputmethod.EditorInfo; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.PopupWindow; +import android.widget.ScrollView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.BotWebViewVibrationEffect; +import org.telegram.messenger.ChatObject; +import org.telegram.messenger.DialogObject; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.R; +import org.telegram.messenger.UserObject; +import org.telegram.messenger.Utilities; +import org.telegram.tgnet.TLObject; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.ActionBar; +import org.telegram.ui.ActionBar.ActionBarMenu; +import org.telegram.ui.ActionBar.ActionBarMenuItem; +import org.telegram.ui.ActionBar.ActionBarMenuSubItem; +import org.telegram.ui.ActionBar.ActionBarPopupWindow; +import org.telegram.ui.ActionBar.AlertDialog; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.SimpleTextView; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Cells.DialogCell; +import org.telegram.ui.Cells.GroupCreateUserCell; +import org.telegram.ui.Cells.TextInfoPrivacyCell; +import org.telegram.ui.Components.Bulletin; +import org.telegram.ui.Components.BulletinFactory; +import org.telegram.ui.Components.CircularProgressDrawable; +import org.telegram.ui.Components.ColoredImageSpan; +import org.telegram.ui.Components.CrossfadeDrawable; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.EditTextBoldCursor; +import org.telegram.ui.Components.FolderBottomSheet; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.Premium.LimitReachedBottomSheet; +import org.telegram.ui.Components.QRCodeBottomSheet; +import org.telegram.ui.Components.RLottieImageView; +import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.Components.TextStyleSpan; + +import java.util.ArrayList; + +public class FilterChatlistActivity extends BaseFragment { + + private RecyclerListView listView; + private ListAdapter adapter; + + MessagesController.DialogFilter filter; + TLRPC.TL_exportedChatlistInvite invite; + + private static final int MAX_NAME_LENGTH = 32; + + private ArrayList selectedPeers = new ArrayList<>(); + private ArrayList allowedPeers = new ArrayList<>(); + private ArrayList peers = new ArrayList<>(); + + private CrossfadeDrawable doneButtonDrawable; + private ActionBarMenuItem doneButton; + + private int shiftDp = -5; + private long lastClickedDialogId, lastClicked; + + public FilterChatlistActivity(MessagesController.DialogFilter filter, TLRPC.TL_exportedChatlistInvite invite) { + super(); + + this.filter = filter; + this.invite = invite; + } + + private Utilities.Callback onDelete; + private Utilities.Callback onEdit; + + public void setOnDelete(Utilities.Callback onDelete) { + this.onDelete = onDelete; + } + + public void setOnEdit(Utilities.Callback onEdit) { + this.onEdit = onEdit; + } + + private void updateActionBarTitle(boolean animated) { + String title = TextUtils.isEmpty(invite == null ? null : invite.title) ? LocaleController.getString("FilterShare", R.string.FilterShare) : invite.title; + if (animated) { + actionBar.setTitleAnimated(title, false, 220); + } else { + actionBar.setTitle(title); + } + } + + @Override + public View createView(Context context) { + actionBar.setBackButtonImage(R.drawable.ic_ab_back); + actionBar.setAllowOverlayTitle(true); + updateActionBarTitle(false); + actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { + @Override + public void onItemClick(int id) { + if (id == -1) { + if (checkDiscard()) { + finishFragment(); + } + } else if (id == 1) { + if (Math.abs(doneButtonAlpha - 1) < 0.1f) { + save(); + } else if (Math.abs(doneButtonAlpha - 0.5f) < 0.1f) { + shakeHeader(); + } + } + } + }); + + ActionBarMenu menu = actionBar.createMenu(); + Drawable checkmark = context.getResources().getDrawable(R.drawable.ic_ab_done).mutate(); + checkmark.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_actionBarDefaultIcon), PorterDuff.Mode.MULTIPLY)); + doneButtonDrawable = new CrossfadeDrawable(checkmark, new CircularProgressDrawable(Theme.getColor(Theme.key_actionBarDefaultIcon))); + doneButton = menu.addItemWithWidth(1, doneButtonDrawable, AndroidUtilities.dp(56), LocaleController.getString("Done", R.string.Done)); + checkDoneButton(); + + fragmentView = new FrameLayout(context); + FrameLayout frameLayout = (FrameLayout) fragmentView; + frameLayout.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundGray)); + + listView = new RecyclerListView(context) { + @Override + public boolean requestFocus(int direction, Rect previouslyFocusedRect) { + return false; + } + }; + listView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)); + listView.setVerticalScrollBarEnabled(false); + frameLayout.addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + listView.setAdapter(adapter = new ListAdapter()); + listView.setOnItemClickListener((view, position) -> { + if (getParentActivity() == null) { + return; + } + + if (view instanceof GroupCreateUserCell) { + long did = peers.get(position - chatsStartRow); + if (selectedPeers.contains(did)) { + selectedPeers.remove(did); + peersChanged = true; + checkDoneButton(); + ((GroupCreateUserCell) view).setChecked(false, true); + } else if (allowedPeers.contains(did)) { + if (selectedPeers.size() + 1 > getMaxChats()) { + showDialog(new LimitReachedBottomSheet(this, getContext(), LimitReachedBottomSheet.TYPE_CHATS_IN_FOLDER, currentAccount)); + return; + } + selectedPeers.add(did); + peersChanged = true; + checkDoneButton(); + ((GroupCreateUserCell) view).setChecked(true, true); + } else { + AndroidUtilities.shakeViewSpring(view, shiftDp = -shiftDp); + BotWebViewVibrationEffect.APP_ERROR.vibrate(); + ArrayList array = new ArrayList<>(); + String text; + if (did >= 0) { + array.add(getMessagesController().getUser(did)); + TLRPC.User user = getMessagesController().getUser(did); + if (user != null && user.bot) { + text = LocaleController.getString("FilterInviteBotToast", R.string.FilterInviteBotToast); + } else { + text = LocaleController.getString("FilterInviteUserToast", R.string.FilterInviteUserToast); + } + } else { + TLRPC.Chat chat = getMessagesController().getChat(-did); + if (ChatObject.isChannelAndNotMegaGroup(chat)) { + if (ChatObject.isPublic(chat)) { + text = LocaleController.getString("FilterInviteChannelToast", R.string.FilterInviteChannelToast); + } else { + text = LocaleController.getString("FilterInvitePrivateChannelToast", R.string.FilterInvitePrivateChannelToast); + } + } else { + if (ChatObject.isPublic(chat)) { + text = LocaleController.getString("FilterInviteGroupToast", R.string.FilterInviteGroupToast); + } else { + text = LocaleController.getString("FilterInvitePrivateGroupToast", R.string.FilterInvitePrivateGroupToast); + } + } + array.add(chat); + } + if (lastClickedDialogId != did || System.currentTimeMillis() - lastClicked > Bulletin.DURATION_SHORT) { + lastClickedDialogId = did; + lastClicked = System.currentTimeMillis(); + BulletinFactory.of(this).createChatsBulletin(array, text, null).show(); + } + return; + } + checkPeersChanged(); + + updateHeaderCell(true); + updateHintCell(true); + } + }); + + getMessagesController().updateFilterDialogs(filter); + peers.clear(); + if (invite != null) { + for (int i = 0; i < invite.peers.size(); ++i) { + TLRPC.Peer peer = invite.peers.get(i); + long did = DialogObject.getPeerDialogId(peer); + peers.add(did); + selectedPeers.add(did); + allowedPeers.add(did); + } + } + for (int i = 0; i < filter.dialogs.size(); ++i) { + TLRPC.Dialog dialog = filter.dialogs.get(i); + if (dialog != null && !DialogObject.isEncryptedDialog(dialog.id) && !peers.contains(dialog.id)) { + boolean canInvite = dialog.id < 0; + if (dialog.id < 0) { + TLRPC.Chat chat = getMessagesController().getChat(-dialog.id); + canInvite = FilterCreateActivity.canAddToFolder(chat); + } + if (canInvite) { + peers.add(dialog.id); + allowedPeers.add(dialog.id); + } + } + } + for (int i = 0; i < filter.dialogs.size(); ++i) { + TLRPC.Dialog dialog = filter.dialogs.get(i); + if (dialog != null && !DialogObject.isEncryptedDialog(dialog.id) && !peers.contains(dialog.id) && !allowedPeers.contains(dialog.id)) { + peers.add(dialog.id); + } + } + + updateRows(); + + return fragmentView; + } + + private void checkPeersChanged() { + if (invite != null && invite.url != null && peersChanged) { + boolean changed = selectedPeers.size() != invite.peers.size(); + if (!changed) { + for (int i = 0; i < invite.peers.size(); ++i) { + TLRPC.Peer peer = invite.peers.get(i); + if (!selectedPeers.contains(DialogObject.getPeerDialogId(peer))) { + changed = true; + break; + } + } + } + if (!changed) { + peersChanged = false; + checkDoneButton(); + } + } + } + + @Override + public boolean onBackPressed() { + return checkDiscard(); + } + + @Override + public boolean canBeginSlide() { + return checkDiscard(); + } + + private boolean saving = false; + private void save() { + if (invite == null || saving || !peersChanged) { + return; + } + + updateDoneProgress(true); + saving = true; + + invite.peers.clear(); + for (int i = 0; i < selectedPeers.size(); ++i) { + invite.peers.add(getMessagesController().getPeer(selectedPeers.get(i))); + } + + TLRPC.TL_chatlists_editExportedInvite req = new TLRPC.TL_chatlists_editExportedInvite(); + req.chatlist = new TLRPC.TL_inputChatlistDialogFilter(); + req.chatlist.filter_id = filter.id; + req.slug = getSlug(); + req.revoked = invite.revoked; + req.flags |= 4; + for (int i = 0; i < selectedPeers.size(); ++i) { + req.peers.add(getMessagesController().getInputPeer(selectedPeers.get(i))); + } + getConnectionsManager().sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + updateDoneProgress(false); + saving = false; + if (err != null && "INVITES_TOO_MUCH".equals(err.text)) { + showDialog(new LimitReachedBottomSheet(this, getContext(), LimitReachedBottomSheet.TYPE_FOLDER_INVITES, currentAccount)); + } else if (err != null && "INVITE_PEERS_TOO_MUCH".equals(err.text)) { + showDialog(new LimitReachedBottomSheet(this, getContext(), LimitReachedBottomSheet.TYPE_CHATS_IN_FOLDER, currentAccount)); + } else if (err != null && "CHATLISTS_TOO_MUCH".equals(err.text)) { + showDialog(new LimitReachedBottomSheet(this, getContext(), LimitReachedBottomSheet.TYPE_SHARED_FOLDERS, currentAccount)); + } else { + finishFragment(); + } + })); + + if (onEdit != null) { + onEdit.run(invite); + } + } + + private int savingTitleReqId; + private void saveTitle() { + if (savingTitleReqId != 0) { + getConnectionsManager().cancelRequest(savingTitleReqId, true); + savingTitleReqId = 0; + } + TLRPC.TL_chatlists_editExportedInvite req = new TLRPC.TL_chatlists_editExportedInvite(); + req.chatlist = new TLRPC.TL_inputChatlistDialogFilter(); + req.chatlist.filter_id = filter.id; + req.slug = getSlug(); + req.revoked = invite.revoked; + req.flags |= 2; + req.title = invite.title; + savingTitleReqId = getConnectionsManager().sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + savingTitleReqId = 0; + if (err == null) { + BulletinFactory.of(this).createSimpleBulletin(R.raw.contact_check, LocaleController.getString("FilterInviteNameEdited", R.string.FilterInviteNameEdited)).show(); + } + })); + + if (onEdit != null) { + onEdit.run(invite); + } + } + + @Override + public void onFragmentDestroy() { + super.onFragmentDestroy(); + + if (savingTitleReqId != 0) { + getConnectionsManager().cancelRequest(savingTitleReqId, true); + savingTitleReqId = 0; + } + } + + private boolean titleChanged; + private boolean peersChanged; + private int rowsCount = 0; + + private int hintRow = -1; + private int linkRow = -1; + private int linkHeaderRow = -1; + private int linkSectionRow = -1; + private int chatsHeaderRow = -1; + private int chatsStartRow = -1; + private int chatsEndRow = -1; + private int chatsSectionRow = -1; + + private FolderBottomSheet.HeaderCell headerCountCell; + private HintInnerCell hintCountCell; + + private void updateHintCell(boolean animated) { + if (hintCountCell == null) { + return; + } + + if (invite == null) { + hintCountCell.setText(LocaleController.getString("FilterInviteHeaderNo", R.string.FilterInviteHeaderNo), animated); + } else { + hintCountCell.setText(AndroidUtilities.replaceTags(LocaleController.formatPluralString("FilterInviteHeader", selectedPeers.size(), filter.name)), animated); + } + } + + private void updateHeaderCell(boolean animated) { + if (headerCountCell == null) { + return; + } + + headerCountCell.setText(selectedPeers.size() <= 0 ? LocaleController.getString("FilterInviteHeaderChatsEmpty") : LocaleController.formatPluralString("FilterInviteHeaderChats", selectedPeers.size()), animated); + if (allowedPeers.size() > 1) { + final boolean deselect = selectedPeers.size() >= Math.min(getMaxChats(), allowedPeers.size()); + headerCountCell.setAction(!deselect ? LocaleController.getString(R.string.SelectAll) : LocaleController.getString(R.string.DeselectAll), () -> deselectAll(headerCountCell, deselect)); + } else { + headerCountCell.setAction("", null); + } + + if (animated) { + AndroidUtilities.makeAccessibilityAnnouncement( + headerCountCell.textView.getText() + ", " + headerCountCell.actionTextView.getText() + ); + } + } + + public void updateRows() { + rowsCount = 0; + hintRow = rowsCount++; + if (invite != null) { + linkHeaderRow = rowsCount++; + linkRow = rowsCount++; + linkSectionRow = rowsCount++; + } else { + linkHeaderRow = -1; + linkRow = -1; + linkSectionRow = -1; + } + if (invite == null && peers.isEmpty()) { + chatsHeaderRow = -1; + chatsStartRow = -1; + chatsEndRow = -1; + chatsSectionRow = -1; + } else { + chatsHeaderRow = rowsCount++; + chatsStartRow = rowsCount++; + chatsEndRow = (rowsCount += (peers.size() - 1)); + chatsSectionRow = rowsCount++; + } + + if (adapter != null) { + adapter.notifyDataSetChanged(); + } + } + + private String getSlug() { + if (invite == null || invite.url == null) { + return null; + } + return invite.url.substring(invite.url.lastIndexOf('/') + 1); + } + + class ListAdapter extends RecyclerListView.SelectionAdapter { + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = null; + if (viewType == 0) { + view = new HintInnerCell(getContext(), R.raw.folder_share); + } else if (viewType == 2) { + TextInfoPrivacyCell cell = new TextInfoPrivacyCell(getContext()); + view = cell; + } else if (viewType == 3) { + InviteLinkCell actionView = new InviteLinkCell(getContext(), FilterChatlistActivity.this) { + @Override + protected void revoke(boolean revoke) { + if (invite == null || invite.url == null) { + return; + } + + TLRPC.TL_chatlists_editExportedInvite req = new TLRPC.TL_chatlists_editExportedInvite(); + req.chatlist = new TLRPC.TL_inputChatlistDialogFilter(); + req.chatlist.filter_id = filter.id; + req.revoked = revoke; + req.slug = getSlug(); + final AlertDialog progressDialog = new AlertDialog(getContext(), AlertDialog.ALERT_TYPE_SPINNER); + progressDialog.showDelayed(180); + getConnectionsManager().sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + invite.revoked = revoke; + progressDialog.dismiss(); + if (revoke) { + finishFragment(); + } + })); + } + + @Override + protected void deleteLink() { + TLRPC.TL_chatlists_deleteExportedInvite req = new TLRPC.TL_chatlists_deleteExportedInvite(); + req.chatlist = new TLRPC.TL_inputChatlistDialogFilter(); + req.chatlist.filter_id = filter.id; + req.slug = getSlug(); + final AlertDialog progressDialog = new AlertDialog(getContext(), AlertDialog.ALERT_TYPE_SPINNER); + progressDialog.showDelayed(180); + getConnectionsManager().sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + progressDialog.dismiss(); + if (onDelete != null) { + onDelete.run(invite); + } + finishFragment(); + })); + } + + @Override + public void editname() { + if (invite == null || invite.url == null) { + return; + } + + final EditTextBoldCursor editText = new EditTextBoldCursor(getContext()); + editText.setBackgroundDrawable(Theme.createEditTextDrawable(getContext(), true)); + + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder.setDialogButtonColorKey(Theme.key_dialogButton); + builder.setTitle(LocaleController.getString("FilterInviteEditName", R.string.FilterInviteEditName)); +// builder.setCheckFocusable(false); + builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), (dialog, which) -> AndroidUtilities.hideKeyboard(editText)); + + LinearLayout linearLayout = new LinearLayout(getContext()); + linearLayout.setOrientation(LinearLayout.VERTICAL); + builder.setView(linearLayout); + + editText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + editText.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); + editText.setMaxLines(1); + editText.setLines(1); + editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES); + editText.setGravity(Gravity.LEFT | Gravity.TOP); + editText.setSingleLine(true); + editText.setImeOptions(EditorInfo.IME_ACTION_DONE); + editText.setHint(filter.name); + editText.setHintTextColor(Theme.getColor(Theme.key_dialogTextHint)); + editText.setCursorColor(Theme.getColor(Theme.key_dialogTextBlack)); + editText.setCursorSize(AndroidUtilities.dp(20)); + editText.setCursorWidth(1.5f); + editText.setPadding(0, AndroidUtilities.dp(4), 0, 0); + linearLayout.addView(editText, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 36, Gravity.TOP | Gravity.LEFT, 24, 6, 24, 0)); + editText.setOnEditorActionListener((textView, i, keyEvent) -> { + AndroidUtilities.hideKeyboard(textView); + builder.create().getButton(AlertDialog.BUTTON_POSITIVE).callOnClick(); + return false; + }); + editText.addTextChangedListener(new TextWatcher() { + + boolean ignoreTextChange; + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + @Override + public void afterTextChanged(Editable s) { + if (ignoreTextChange) { + return; + } + if (s.length() > MAX_NAME_LENGTH) { + ignoreTextChange = true; + s.delete(MAX_NAME_LENGTH, s.length()); + AndroidUtilities.shakeView(editText); + editText.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); + ignoreTextChange = false; + } + } + }); + if (!TextUtils.isEmpty(invite.title)) { + editText.setText(invite.title); + editText.setSelection(editText.length()); + } + builder.setPositiveButton(LocaleController.getString("Save", R.string.Save), (dialog, which) -> { + AndroidUtilities.hideKeyboard(editText); +// call.setTitle(editText.getText().toString()); + builder.getDismissRunnable().run(); + + invite.title = editText.getText().toString(); + titleChanged = true; + updateActionBarTitle(true); + saveTitle(); + }); + + final AlertDialog alertDialog = builder.create(); + alertDialog.setOnShowListener(dialog -> AndroidUtilities.runOnUIThread(() -> { + editText.requestFocus(); + AndroidUtilities.showKeyboard(editText); + })); + alertDialog.setOnDismissListener(dialog -> AndroidUtilities.hideKeyboard(editText)); + alertDialog.show(); + alertDialog.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); + editText.requestFocus(); + } + + @Override + protected boolean isRevoked() { + return invite != null && invite.revoked; + } + }; + actionView.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + actionView.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + view = actionView; + } else if (viewType == 4) { + GroupCreateUserCell userCell = new GroupCreateUserCell(getContext(), 1, 0, false); + userCell.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + view = userCell; + } else if (viewType == 5) { + FolderBottomSheet.HeaderCell headerCell = new FolderBottomSheet.HeaderCell(getContext()); + headerCell.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + view = headerCell; + } + return new RecyclerListView.Holder(view); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + int viewType = holder.getItemViewType(); + if (viewType == 0) { + hintCountCell = (HintInnerCell) holder.itemView; + updateHintCell(false); + } else if (viewType == 2) { + TextInfoPrivacyCell cell = (TextInfoPrivacyCell) holder.itemView; + cell.setBackground(Theme.getThemedDrawableByKey(getContext(), position == chatsSectionRow ? R.drawable.greydivider_bottom : R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + if (position == chatsSectionRow) { + cell.setFixedSize(0); + if (invite == null || allowedPeers.isEmpty()) { + cell.setText(LocaleController.getString("FilterInviteHintNo", R.string.FilterInviteHintNo)); + } else { + cell.setText(LocaleController.getString("FilterInviteHint", R.string.FilterInviteHint)); + } + } else { + cell.setFixedSize(12); + } + } else if (viewType == 3) { + InviteLinkCell actionView = (InviteLinkCell) holder.itemView; + actionView.setLink(invite == null ? null : invite.url, false); + } else if (viewType == 4) { + GroupCreateUserCell userCell = (GroupCreateUserCell) holder.itemView; + long did = peers.get(position - chatsStartRow); + TLObject object; + CharSequence name = null, status = null; + if (did >= 0) { + TLRPC.User user = getMessagesController().getUser(did); + if (user != null) { + name = UserObject.getUserName(user); + } + object = user; + } else { + TLRPC.Chat chat = getMessagesController().getChat(-did); + if (chat != null) { + name = chat.title; + if (chat.participants_count != 0) { + if (ChatObject.isChannelAndNotMegaGroup(chat)) { + status = LocaleController.formatPluralStringComma("Subscribers", chat.participants_count); + } else { + status = LocaleController.formatPluralStringComma("Members", chat.participants_count); + } + } else { + if (ChatObject.isChannelAndNotMegaGroup(chat)) { + status = LocaleController.getString("ChannelPublic"); + } else { + status = LocaleController.getString("MegaPublic"); + } + } + } + object = chat; + } + if (allowedPeers.contains(did)) { + userCell.setForbiddenCheck(false); + userCell.setChecked(selectedPeers.contains(did), false); + } else { + userCell.setForbiddenCheck(true); + userCell.setChecked(false, false); + if (object instanceof TLRPC.User) { + if (((TLRPC.User) object).bot) { + status = LocaleController.getString("FilterInviteBot", R.string.FilterInviteBot); + } else { + status = LocaleController.getString("FilterInviteUser", R.string.FilterInviteUser); + } + } else if (object instanceof TLRPC.Chat) { + if (ChatObject.isChannelAndNotMegaGroup((TLRPC.Chat) object)) { + status = LocaleController.getString("FilterInviteChannel", R.string.FilterInviteChannel); + } else { + status = LocaleController.getString("FilterInviteGroup", R.string.FilterInviteGroup); + } + } + } + userCell.setTag(did); + userCell.setObject(object, name, status); + } else if (viewType == 5) { + FolderBottomSheet.HeaderCell headerCell = (FolderBottomSheet.HeaderCell) holder.itemView; + if (headerCell == headerCountCell) { + headerCountCell = null; + } + if (position == linkHeaderRow) { + headerCell.setText(LocaleController.getString("InviteLink", R.string.InviteLink), false); + headerCell.setAction("", null); + } else { + headerCountCell = headerCell; + if (invite == null || allowedPeers.isEmpty()) { + headerCell.setText(LocaleController.getString("FilterInviteHeaderChatsNo", R.string.FilterInviteHeaderChatsNo), false); + headerCell.setAction("", null); + } else { + updateHeaderCell(false); + } + } + } + } + + @Override + public int getItemCount() { + return rowsCount; + } + + @Override + public int getItemViewType(int position) { + if (position == 0) { + return 0; + } else if (position == chatsSectionRow || position == linkSectionRow) { + return 2; + } else if (position == linkRow) { + return 3; + } else if (position >= chatsStartRow && position < chatsEndRow) { + return 4; + } else if (position == chatsHeaderRow || position == linkHeaderRow) { + return 5; + } + return 0; + } + + @Override + public boolean isEnabled(RecyclerView.ViewHolder holder) { + return holder.getItemViewType() == 4; + } + } + + private int getMaxChats() { + return getUserConfig().isPremium() ? getMessagesController().dialogFiltersChatsLimitPremium : getMessagesController().dialogFiltersChatsLimitDefault; + } + + private void deselectAll(FolderBottomSheet.HeaderCell headerCell, boolean deselect) { + selectedPeers.clear(); + if (!deselect) { + selectedPeers.addAll(allowedPeers.subList(0, Math.min(getMaxChats(), allowedPeers.size()))); + } + final boolean newDeselect = selectedPeers.size() >= Math.min(getMaxChats(), allowedPeers.size()); + headerCell.setAction(!newDeselect ? LocaleController.getString(R.string.SelectAll) : LocaleController.getString(R.string.DeselectAll), () -> deselectAll(headerCell, !deselect)); + peersChanged = true; + checkPeersChanged(); + checkDoneButton(); + updateHeaderCell(true); + updateHintCell(true); + for (int i = 0; i < listView.getChildCount(); ++i) { + View child = listView.getChildAt(i); + if (child instanceof GroupCreateUserCell) { + Object tag = child.getTag(); + if (tag instanceof Long) { + ((GroupCreateUserCell) child).setChecked(selectedPeers.contains((long) tag),true); + } + } + } + } + + @Override + public void onPause() { + super.onPause(); + } + + @SuppressWarnings("FieldCanBeLocal") + public static class HintInnerCell extends FrameLayout { + + private RLottieImageView imageView; + private TextView subtitleTextView; + + public HintInnerCell(Context context, int resId) { + super(context); + + imageView = new RLottieImageView(context); + imageView.setAnimation(resId, 90, 90); + imageView.setScaleType(ImageView.ScaleType.CENTER); + imageView.playAnimation(); + imageView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); + addView(imageView, LayoutHelper.createFrame(90, 90, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, 14, 0, 0)); + + subtitleTextView = new TextView(context); + subtitleTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText4)); + subtitleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + subtitleTextView.setGravity(Gravity.CENTER); + subtitleTextView.setLines(2); + addView(subtitleTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 40, 121, 40, 24)); + } + + public void setText(CharSequence text, boolean animated) { + subtitleTextView.setText(text); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), heightMeasureSpec); + } + } + + private Runnable enableDoneLoading = () -> updateDoneProgress(true); + private ValueAnimator doneButtonDrawableAnimator; + private void updateDoneProgress(boolean loading) { + if (!loading) { + AndroidUtilities.cancelRunOnUIThread(enableDoneLoading); + } + if (doneButtonDrawable != null) { + if (doneButtonDrawableAnimator != null) { + doneButtonDrawableAnimator.cancel(); + } + doneButtonDrawableAnimator = ValueAnimator.ofFloat(doneButtonDrawable.getProgress(), loading ? 1f : 0); + doneButtonDrawableAnimator.addUpdateListener(a -> { + doneButtonDrawable.setProgress((float) a.getAnimatedValue()); + doneButtonDrawable.invalidateSelf(); + }); + doneButtonDrawableAnimator.setDuration((long) (200 * Math.abs(doneButtonDrawable.getProgress() - (loading ? 1f : 0)))); + doneButtonDrawableAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); + doneButtonDrawableAnimator.start(); + } + } + + private float doneButtonAlpha = 1; + private void checkDoneButton() { + boolean shown = peersChanged; + boolean enabled = !selectedPeers.isEmpty(); + float alpha = shown ? (enabled ? 1 : .5f) : 0; + + if (Math.abs(doneButtonAlpha - alpha) > .1f) { + doneButton.clearAnimation(); + doneButton.animate().alpha(doneButtonAlpha = alpha).setDuration(320).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).start(); + } + } + + private void shakeHeader() { + for (int i = 0; i < listView.getChildCount(); ++i) { + View child = listView.getChildAt(i); + int position = listView.getChildAdapterPosition(child); + if (position == chatsHeaderRow && child instanceof FolderBottomSheet.HeaderCell) { + AndroidUtilities.shakeViewSpring(child, shiftDp = -shiftDp); + break; + } + } + } + + private boolean checkDiscard() { + if (selectedPeers.isEmpty()) { + return true; + } + if (peersChanged) { + AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); + builder.setTitle(LocaleController.getString("UnsavedChanges", R.string.UnsavedChanges)); + builder.setMessage(LocaleController.getString("UnsavedChangesMessage", R.string.UnsavedChangesMessage)); + builder.setPositiveButton(LocaleController.getString("ApplyTheme", R.string.ApplyTheme), (dialogInterface, i) -> save()); + builder.setNegativeButton(LocaleController.getString("PassportDiscard", R.string.PassportDiscard), (dialog, which) -> finishFragment()); + showDialog(builder.create()); + return false; + } + return true; + } + + public static class InviteLinkCell extends FrameLayout { + + FrameLayout linkBox; + SimpleTextView spoilerTextView; + SimpleTextView textView; + ImageView optionsIcon; + + ButtonsBox buttonsBox; + TextView copyButton, shareButton, generateButton; + + BaseFragment parentFragment; + + class ButtonsBox extends FrameLayout { + + private Paint paint = new Paint(); + private float[] radii = new float[8]; + private Path path = new Path(); + + public ButtonsBox(Context context) { + super(context); + setWillNotDraw(false); + paint.setColor(Theme.getColor(Theme.key_featuredStickers_addButton)); + } + + private float t; + public void setT(float t) { + this.t = t; + invalidate(); + } + + private void setRadii(float left, float right) { + radii[0] = radii[1] = radii[6] = radii[7] = left; + radii[2] = radii[3] = radii[4] = radii[5] = right; + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + final float cx = getMeasuredWidth() / 2f; + + path.rewind(); + AndroidUtilities.rectTmp.set(0, 0, cx - lerp(0, dp(4), t), getMeasuredHeight()); + setRadii(dp(8), lerp(0, dp(8), t)); + path.addRoundRect(AndroidUtilities.rectTmp, radii, Path.Direction.CW); + canvas.drawPath(path, paint); + + path.rewind(); + AndroidUtilities.rectTmp.set(cx + lerp(0, dp(4), t), 0, getMeasuredWidth(), getMeasuredHeight()); + setRadii(lerp(0, dp(8), t), dp(8)); + path.addRoundRect(AndroidUtilities.rectTmp, radii, Path.Direction.CW); + canvas.drawPath(path, paint); + } + } + + public InviteLinkCell(Context context, BaseFragment fragment) { + super(context); + + parentFragment = fragment; + + linkBox = new FrameLayout(context); + linkBox.setBackground(Theme.createSimpleSelectorRoundRectDrawable(dp(8), Theme.getColor(Theme.key_graySection), Theme.blendOver(Theme.getColor(Theme.key_graySection), Theme.getColor(Theme.key_listSelector)))); + linkBox.setOnClickListener(e -> copy()); + addView(linkBox, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.TOP | Gravity.FILL_HORIZONTAL, 22, 9, 22, 0)); + + spoilerTextView = new SimpleTextView(context); + spoilerTextView.setTextSize(16); + spoilerTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + SpannableString spoileredText = new SpannableString("t.me/folder/N3k/dImA/bIo"); + TextStyleSpan.TextStyleRun run = new TextStyleSpan.TextStyleRun(); + run.flags |= FLAG_STYLE_SPOILER; + spoileredText.setSpan(new TextStyleSpan(run), 0, spoileredText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + spoilerTextView.setText(spoileredText); + spoilerTextView.setAlpha(1); + linkBox.addView(spoilerTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL | Gravity.FILL_HORIZONTAL, 20, 0, 40, 0)); + + textView = new SimpleTextView(context); + textView.setTextSize(16); + textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + textView.setText(spoileredText); + textView.setAlpha(0); + linkBox.addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL | Gravity.FILL_HORIZONTAL, 20, 0, 40, 0)); + + optionsIcon = new ImageView(context); + optionsIcon.setImageDrawable(getContext().getResources().getDrawable(R.drawable.ic_ab_other)); + optionsIcon.setScaleType(ImageView.ScaleType.CENTER); + optionsIcon.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_dialogTextGray3), PorterDuff.Mode.SRC_IN)); + optionsIcon.setAlpha(0f); + optionsIcon.setVisibility(GONE); + optionsIcon.setContentDescription(LocaleController.getString("AccDescrMoreOptions", R.string.AccDescrMoreOptions)); + optionsIcon.setOnClickListener(e -> { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && linkBox.getBackground() instanceof RippleDrawable) { + linkBox.getBackground().setState(new int[] { android.R.attr.state_pressed, android.R.attr.state_enabled }); + postDelayed(() -> linkBox.getBackground().setState(new int[] {}), 180); + } + options(); + }); + linkBox.addView(optionsIcon, LayoutHelper.createFrame(40, 40, Gravity.CENTER_VERTICAL | Gravity.RIGHT, 4, 4, 4, 4)); + + buttonsBox = new ButtonsBox(context); + addView(buttonsBox, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 42, Gravity.FILL_HORIZONTAL | Gravity.TOP, 22, 69, 22, 0)); + + copyButton = new TextView(context) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure( + MeasureSpec.makeMeasureSpec((MeasureSpec.getSize(widthMeasureSpec) - dp(8)) / 2, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(dp(42), MeasureSpec.EXACTLY) + ); + } + }; + copyButton.setGravity(Gravity.CENTER); + copyButton.setTextColor(Theme.getColor(Theme.key_featuredStickers_buttonText)); + copyButton.setBackground(Theme.createRadSelectorDrawable(0x30ffffff, 8, 8)); + copyButton.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + copyButton.setTextSize(14); + SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(); + spannableStringBuilder.append("..").setSpan(new ColoredImageSpan(ContextCompat.getDrawable(context, R.drawable.msg_copy_filled)), 0, 1, 0); + spannableStringBuilder.setSpan(new DialogCell.FixedWidthSpan(AndroidUtilities.dp(8)), 1, 2, 0); + spannableStringBuilder.append(LocaleController.getString("LinkActionCopy", R.string.LinkActionCopy)); + spannableStringBuilder.append(".").setSpan(new DialogCell.FixedWidthSpan(AndroidUtilities.dp(5)), spannableStringBuilder.length() - 1, spannableStringBuilder.length(), 0); + copyButton.setText(spannableStringBuilder); + copyButton.setOnClickListener(e -> copy()); + copyButton.setAlpha(0); + copyButton.setVisibility(GONE); + buttonsBox.addView(copyButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT)); + + shareButton = new TextView(context) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure( + MeasureSpec.makeMeasureSpec((MeasureSpec.getSize(widthMeasureSpec) - dp(8)) / 2, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(dp(42), MeasureSpec.EXACTLY) + ); + } + }; + shareButton.setGravity(Gravity.CENTER); + shareButton.setTextColor(Theme.getColor(Theme.key_featuredStickers_buttonText)); + shareButton.setBackground(Theme.createRadSelectorDrawable(0x30ffffff, 8, 8)); + shareButton.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + shareButton.setTextSize(14); + spannableStringBuilder = new SpannableStringBuilder(); + spannableStringBuilder.append("..").setSpan(new ColoredImageSpan(ContextCompat.getDrawable(context, R.drawable.msg_share_filled)), 0, 1, 0); + spannableStringBuilder.setSpan(new DialogCell.FixedWidthSpan(AndroidUtilities.dp(8)), 1, 2, 0); + spannableStringBuilder.append(LocaleController.getString("LinkActionShare", R.string.LinkActionShare)); + spannableStringBuilder.append(".").setSpan(new DialogCell.FixedWidthSpan(AndroidUtilities.dp(5)), spannableStringBuilder.length() - 1, spannableStringBuilder.length(), 0); + shareButton.setText(spannableStringBuilder); + shareButton.setOnClickListener(e -> share()); + shareButton.setAlpha(0); + shareButton.setVisibility(GONE); + buttonsBox.addView(shareButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.RIGHT)); + + generateButton = new TextView(context); + generateButton.setGravity(Gravity.CENTER); + generateButton.setTextColor(Theme.getColor(Theme.key_featuredStickers_buttonText)); + generateButton.setBackground(Theme.createRadSelectorDrawable(0x30ffffff, 8, 8)); + generateButton.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + generateButton.setTextSize(14); + generateButton.setText("Generate Invite Link"); + generateButton.setOnClickListener(e -> generate()); + generateButton.setAlpha(1); + generateButton.setVisibility(VISIBLE); + buttonsBox.addView(generateButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + } + + protected void generate() { + + } + + protected boolean isRevoked() { + return false; + } + + private String lastUrl; + + private float changeAlpha; + private ValueAnimator changeAnimator; + public void setLink(String _url, boolean animated) { + lastUrl = _url; + if (_url != null) { + if (_url.startsWith("http://")) + _url = _url.substring(7); + if (_url.startsWith("https://")) + _url = _url.substring(8); + } + final String url = _url; + textView.setText(url); + if (changeAlpha != (url != null ? 1 : 0)) { + if (changeAnimator != null) { + changeAnimator.cancel(); + changeAnimator = null; + } + + if (animated) { + generateButton.setVisibility(VISIBLE); + optionsIcon.setVisibility(VISIBLE); + copyButton.setVisibility(VISIBLE); + shareButton.setVisibility(VISIBLE); + + changeAnimator = ValueAnimator.ofFloat(changeAlpha, url != null ? 1 : 0); + changeAnimator.addUpdateListener(anm -> { + changeAlpha = (float) anm.getAnimatedValue(); + updateChangeAlpha(); + }); + changeAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (url == null) { + generateButton.setVisibility(VISIBLE); + optionsIcon.setVisibility(GONE); + copyButton.setVisibility(GONE); + shareButton.setVisibility(GONE); + } else { + generateButton.setVisibility(GONE); + optionsIcon.setVisibility(VISIBLE); + copyButton.setVisibility(VISIBLE); + shareButton.setVisibility(VISIBLE); + } + } + }); + changeAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + changeAnimator.setDuration(320); + changeAnimator.start(); + } else { + changeAlpha = url != null ? 1 : 0; + updateChangeAlpha(); + + if (url == null) { + generateButton.setVisibility(VISIBLE); + optionsIcon.setVisibility(GONE); + copyButton.setVisibility(GONE); + shareButton.setVisibility(GONE); + } else { + generateButton.setVisibility(GONE); + optionsIcon.setVisibility(VISIBLE); + copyButton.setVisibility(VISIBLE); + shareButton.setVisibility(VISIBLE); + } + } + } + } + + private void updateChangeAlpha() { + buttonsBox.setT(changeAlpha); + + copyButton.setAlpha(changeAlpha); + shareButton.setAlpha(changeAlpha); + optionsIcon.setAlpha(changeAlpha); + generateButton.setAlpha(1f - changeAlpha); + + textView.setAlpha(changeAlpha); + spoilerTextView.setAlpha(1f - changeAlpha); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure( + MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(dp(127), MeasureSpec.EXACTLY) + ); + } + + private ActionBarPopupWindow actionBarPopupWindow; + private float[] point = new float[2]; + + public void options() { + if (actionBarPopupWindow != null || lastUrl == null) { + return; + } + ActionBarPopupWindow.ActionBarPopupWindowLayout layout = new ActionBarPopupWindow.ActionBarPopupWindowLayout(getContext()); + + ActionBarMenuSubItem subItem; + + subItem = new ActionBarMenuSubItem(getContext(), true, false); + subItem.setTextAndIcon(LocaleController.getString("EditName", R.string.EditName), R.drawable.msg_edit); + layout.addView(subItem, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48)); + subItem.setOnClickListener(view12 -> { + if (actionBarPopupWindow != null) { + actionBarPopupWindow.dismiss(); + } + editname(); + }); + + subItem = new ActionBarMenuSubItem(getContext(), false, false); + subItem.setTextAndIcon(LocaleController.getString("GetQRCode", R.string.GetQRCode), R.drawable.msg_qrcode); + layout.addView(subItem, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48)); + subItem.setOnClickListener(view12 -> { + if (actionBarPopupWindow != null) { + actionBarPopupWindow.dismiss(); + } + qrcode(); + }); + + subItem = new ActionBarMenuSubItem(getContext(), false, true); +// if (!isRevoked()) { +// subItem.setTextAndIcon(LocaleController.getString("RevokeLink", R.string.RevokeLink), R.drawable.msg_delete); +// subItem.setColors(Theme.getColor(Theme.key_text_RedRegular), Theme.getColor(Theme.key_text_RedRegular)); +// subItem.setSelectorColor(Theme.multAlpha(Theme.getColor(Theme.key_text_RedRegular), .12f)); +// subItem.setOnClickListener(view1 -> { +// if (actionBarPopupWindow != null) { +// actionBarPopupWindow.dismiss(); +// } +// revoke(true); +// }); +// } else { +// subItem = new ActionBarMenuSubItem(getContext(), false, true); +// subItem.setTextAndIcon("Unrevoke", R.drawable.msg_reset); +// subItem.setOnClickListener(view1 -> { +// if (actionBarPopupWindow != null) { +// actionBarPopupWindow.dismiss(); +// } +// revoke(false); +// }); +// } + subItem.setTextAndIcon(LocaleController.getString("DeleteLink", R.string.DeleteLink), R.drawable.msg_delete); + subItem.setColors(Theme.getColor(Theme.key_text_RedRegular), Theme.getColor(Theme.key_text_RedRegular)); + subItem.setSelectorColor(Theme.multAlpha(Theme.getColor(Theme.key_text_RedRegular), .12f)); + subItem.setOnClickListener(view1 -> { + if (actionBarPopupWindow != null) { + actionBarPopupWindow.dismiss(); + } + deleteLink(); + }); + layout.addView(subItem, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48)); + + FrameLayout container = parentFragment.getParentLayout().getOverlayContainerView(); + + if (container != null) { + float x = 0; + float y; + getPointOnScreen(linkBox, container, point); + y = point[1]; + + final FrameLayout finalContainer = container; + View dimView = new View(getContext()) { + + @Override + protected void onDraw(Canvas canvas) { + canvas.drawColor(0x33000000); + getPointOnScreen(linkBox, finalContainer, point); + canvas.save(); + float clipTop = ((View) linkBox.getParent()).getY() + linkBox.getY(); + if (clipTop < 1) { + canvas.clipRect(0, point[1] - clipTop + 1, getMeasuredWidth(), getMeasuredHeight()); + } + canvas.translate(point[0], point[1]); + + linkBox.draw(canvas); + canvas.restore(); + } + }; + + ViewTreeObserver.OnPreDrawListener preDrawListener = new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + dimView.invalidate(); + return true; + } + }; + finalContainer.getViewTreeObserver().addOnPreDrawListener(preDrawListener); + container.addView(dimView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + dimView.setAlpha(0); + dimView.animate().alpha(1f).setDuration(150); + layout.measure(MeasureSpec.makeMeasureSpec(container.getMeasuredWidth(), MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(container.getMeasuredHeight(), MeasureSpec.UNSPECIFIED)); + + actionBarPopupWindow = new ActionBarPopupWindow(layout, LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT); + actionBarPopupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() { + @Override + public void onDismiss() { + actionBarPopupWindow = null; + dimView.animate().cancel(); + dimView.animate().alpha(0).setDuration(150).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (dimView.getParent() != null) { + finalContainer.removeView(dimView); + } + finalContainer.getViewTreeObserver().removeOnPreDrawListener(preDrawListener); + } + }); + } + }); + actionBarPopupWindow.setOutsideTouchable(true); + actionBarPopupWindow.setFocusable(true); + actionBarPopupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); + actionBarPopupWindow.setAnimationStyle(R.style.PopupContextAnimation); + actionBarPopupWindow.setInputMethodMode(ActionBarPopupWindow.INPUT_METHOD_NOT_NEEDED); + actionBarPopupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED); + + layout.setDispatchKeyEventListener(keyEvent -> { + if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_BACK && keyEvent.getRepeatCount() == 0 && actionBarPopupWindow.isShowing()) { + actionBarPopupWindow.dismiss(true); + } + }); + + if (AndroidUtilities.isTablet()) { + y += container.getPaddingTop(); + x -= container.getPaddingLeft(); + } + actionBarPopupWindow.showAtLocation(container, 0, (int) (container.getMeasuredWidth() - layout.getMeasuredWidth() - AndroidUtilities.dp(16) + container.getX() + x), (int) (y + linkBox.getMeasuredHeight() + container.getY())); + } + } + + + public void copy() { + if (lastUrl == null) { + return; + } + + AndroidUtilities.addToClipboard(lastUrl); + BulletinFactory.of(parentFragment).createCopyBulletin(LocaleController.getString("LinkCopied", R.string.LinkCopied)).show(); + } + + protected void share() { + if (lastUrl == null) { + return; + } + + try { + Intent intent = new Intent(Intent.ACTION_SEND); + intent.setType("text/plain"); + intent.putExtra(Intent.EXTRA_TEXT, lastUrl); + parentFragment.startActivityForResult(Intent.createChooser(intent, LocaleController.getString("InviteToGroupByLink", R.string.InviteToGroupByLink)), 500); + } catch (Exception e) { + FileLog.e(e); + } + } + + protected void revoke(boolean revoke) {} + + protected void deleteLink() {} + + public void editname() {} + + public void qrcode() { + if (lastUrl == null) { + return; + } + + QRCodeBottomSheet qrCodeBottomSheet = new QRCodeBottomSheet(getContext(), LocaleController.getString("InviteByQRCode", R.string.InviteByQRCode), lastUrl, LocaleController.getString("QRCodeLinkHelpFolder", R.string.QRCodeLinkHelpFolder), false); + qrCodeBottomSheet.setCenterAnimation(R.raw.qr_code_logo); + qrCodeBottomSheet.show(); + } + + private void getPointOnScreen(FrameLayout frameLayout, FrameLayout finalContainer, float[] point) { + float x = 0; + float y = 0; + View v = frameLayout; + while (v != finalContainer) { + y += v.getY(); + x += v.getX(); + if (v instanceof ScrollView) { + y -= v.getScrollY(); + } + if (!(v.getParent() instanceof View)) { + break; + } + v = (View) v.getParent(); + if (!(v instanceof ViewGroup)) { + return; + } + } + x -= finalContainer.getPaddingLeft(); + y -= finalContainer.getPaddingTop(); + point[0] = x; + point[1] = y; + } + } + +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/FilterCreateActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/FilterCreateActivity.java index 803350a200..aae92ebad5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/FilterCreateActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/FilterCreateActivity.java @@ -1,25 +1,51 @@ package org.telegram.ui; +import static org.telegram.messenger.AndroidUtilities.dp; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; import android.content.Context; import android.content.DialogInterface; +import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.os.Build; import android.text.Editable; +import android.text.Layout; +import android.text.SpannableString; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; import android.text.TextWatcher; +import android.text.style.DynamicDrawableSpan; +import android.text.style.ImageSpan; +import android.text.style.ReplacementSpan; +import android.util.TypedValue; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; +import android.view.accessibility.AccessibilityNodeInfo; import android.view.inputmethod.EditorInfo; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.BotWebViewVibrationEffect; import org.telegram.messenger.ChatObject; import org.telegram.messenger.DialogObject; import org.telegram.messenger.Emoji; @@ -29,6 +55,7 @@ import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; import org.telegram.messenger.support.LongSparseIntArray; +import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; @@ -44,10 +71,23 @@ import org.telegram.ui.Cells.TextCell; import org.telegram.ui.Cells.TextInfoPrivacyCell; import org.telegram.ui.Cells.UserCell; +import org.telegram.ui.Components.AnimatedTextView; +import org.telegram.ui.Components.BottomSheetWithRecyclerListView; +import org.telegram.ui.Components.Bulletin; +import org.telegram.ui.Components.BulletinFactory; +import org.telegram.ui.Components.CombinedDrawable; +import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.EditTextBoldCursor; +import org.telegram.ui.Components.FolderBottomSheet; +import org.telegram.ui.Components.HintView; +import org.telegram.ui.Components.ItemOptions; import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.ListView.AdapterWithDiffUtils; +import org.telegram.ui.Components.Premium.LimitReachedBottomSheet; +import org.telegram.ui.Components.QRCodeBottomSheet; import org.telegram.ui.Components.RLottieImageView; import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.Components.UndoView; import java.util.ArrayList; import java.util.Collections; @@ -60,34 +100,9 @@ public class FilterCreateActivity extends BaseFragment { private RecyclerListView listView; private ListAdapter adapter; private ActionBarMenuItem doneItem; + private UndoView undoView; - private int imageRow; - private int namePreSectionRow; - private int nameRow; - private int nameSectionRow; - private int includeHeaderRow; - private int includeAddRow; - private int includeContactsRow; - private int includeNonContactsRow; - private int includeGroupsRow; - private int includeChannelsRow; - private int includeBotsRow; - private int includeStartRow; - private int includeEndRow; - private int includeShowMoreRow; - private int includeSectionRow; - private int excludeHeaderRow; - private int excludeAddRow; - private int excludeMutedRow; - private int excludeReadRow; - private int excludeArchivedRow; - private int excludeStartRow; - private int excludeEndRow; - private int excludeShowMoreRow; - private int excludeSectionRow; - private int removeRow; - private int removeSectionRow; - private int rowCount = 0; + private int nameRow = -1; private boolean includeExpanded; private boolean excludeExpanded; @@ -97,12 +112,24 @@ public class FilterCreateActivity extends BaseFragment { private MessagesController.DialogFilter filter; private boolean creatingNew; + private boolean doNotCloseWhenSave; private String newFilterName; private String newFilterEmoticon; private int newFilterFlags; private ArrayList newAlwaysShow; private ArrayList newNeverShow; private LongSparseIntArray newPinned; + private CreateLinkCell createLinkCell; + private boolean canCreateLink() { + return ( + (!TextUtils.isEmpty(newFilterName) || !TextUtils.isEmpty(filter.name)) && + (newFilterFlags & ~(MessagesController.DIALOG_FILTER_FLAG_CHATLIST | MessagesController.DIALOG_FILTER_FLAG_CHATLIST_ADMIN)) == 0 && + newNeverShow.isEmpty() && + !newAlwaysShow.isEmpty() + ); + } + + private ArrayList invites = new ArrayList<>(); private static final int MAX_NAME_LENGTH = 12; @@ -131,7 +158,7 @@ public HintInnerCell(Context context) { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(156), MeasureSpec.EXACTLY)); + super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(dp(156), MeasureSpec.EXACTLY)); } } @@ -166,115 +193,164 @@ public FilterCreateActivity(MessagesController.DialogFilter dialogFilter, ArrayL newPinned = filter.pinnedDialogs.clone(); } + private int requestingInvitesReqId; + @Override public boolean onFragmentCreate() { updateRows(); return super.onFragmentCreate(); } - private void updateRows() { - rowCount = 0; + private boolean loadingInvites; + public void loadInvites() { + if (loadingInvites) { + return; + } + if (filter == null || !filter.isChatlist()) { + return; + } + loadingInvites = true; + TLRPC.TL_chatlists_getExportedInvites req = new TLRPC.TL_chatlists_getExportedInvites(); + req.chatlist = new TLRPC.TL_inputChatlistDialogFilter(); + req.chatlist.filter_id = filter.id; + requestingInvitesReqId = getConnectionsManager().sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + loadingInvites = false; + if (res instanceof TLRPC.TL_chatlists_exportedInvites) { + TLRPC.TL_chatlists_exportedInvites invs = (TLRPC.TL_chatlists_exportedInvites) res; + getMessagesController().putChats(invs.chats, false); + getMessagesController().putUsers(invs.users, false); + invites.clear(); + invites.addAll(((TLRPC.TL_chatlists_exportedInvites) res).invites); + updateRows(); + } + requestingInvitesReqId = 0; + })); + } - if (creatingNew) { - imageRow = rowCount++; - namePreSectionRow = -1; - } else { - imageRow = -1; - namePreSectionRow = rowCount++; + @Override + public void onFragmentDestroy() { + super.onFragmentDestroy(); + if (requestingInvitesReqId != 0) { + getConnectionsManager().cancelRequest(requestingInvitesReqId, true); } - nameRow = rowCount++; - nameSectionRow = rowCount++; - includeHeaderRow = rowCount++; - includeAddRow = rowCount++; + } + + private void updateRows() { + updateRows(true); + } + + private ArrayList oldItems = new ArrayList<>(); + private ArrayList items = new ArrayList<>(); + + private void updateRows(boolean animated) { + + oldItems.clear(); + oldItems.addAll(items); + + items.clear(); + + items.add(new ItemInner(VIEW_TYPE_HINT, false)); + nameRow = items.size(); + items.add(ItemInner.asEdit()); + items.add(ItemInner.asShadow(null)); + items.add(ItemInner.asHeader(LocaleController.getString("FilterInclude", R.string.FilterInclude))); + items.add(ItemInner.asButton(R.drawable.msg2_chats_add, LocaleController.getString("FilterAddChats", R.string.FilterAddChats), false).whenClicked(v -> selectChatsFor(true))); if ((newFilterFlags & MessagesController.DIALOG_FILTER_FLAG_CONTACTS) != 0) { - includeContactsRow = rowCount++; - } else { - includeContactsRow = -1; + items.add(ItemInner.asChat(true, LocaleController.getString("FilterContacts", R.string.FilterContacts), "contacts", MessagesController.DIALOG_FILTER_FLAG_CONTACTS)); } if ((newFilterFlags & MessagesController.DIALOG_FILTER_FLAG_NON_CONTACTS) != 0) { - includeNonContactsRow = rowCount++; - } else { - includeNonContactsRow = -1; + items.add(ItemInner.asChat(true, LocaleController.getString("FilterNonContacts", R.string.FilterNonContacts), "non_contacts", MessagesController.DIALOG_FILTER_FLAG_NON_CONTACTS)); } if ((newFilterFlags & MessagesController.DIALOG_FILTER_FLAG_GROUPS) != 0) { - includeGroupsRow = rowCount++; - } else { - includeGroupsRow = -1; + items.add(ItemInner.asChat(true, LocaleController.getString("FilterGroups", R.string.FilterGroups), "groups", MessagesController.DIALOG_FILTER_FLAG_GROUPS)); } if ((newFilterFlags & MessagesController.DIALOG_FILTER_FLAG_CHANNELS) != 0) { - includeChannelsRow = rowCount++; - } else { - includeChannelsRow = -1; + items.add(ItemInner.asChat(true, LocaleController.getString("FilterChannels", R.string.FilterChannels), "channels", MessagesController.DIALOG_FILTER_FLAG_CHANNELS)); } if ((newFilterFlags & MessagesController.DIALOG_FILTER_FLAG_BOTS) != 0) { - includeBotsRow = rowCount++; - } else { - includeBotsRow = -1; + items.add(ItemInner.asChat(true, LocaleController.getString("FilterBots", R.string.FilterBots), "bots", MessagesController.DIALOG_FILTER_FLAG_BOTS)); } if (!newAlwaysShow.isEmpty()) { - includeStartRow = rowCount; int count = includeExpanded || newAlwaysShow.size() < 8 ? newAlwaysShow.size() : Math.min(5, newAlwaysShow.size()); - rowCount += count; - includeEndRow = rowCount; + for (int i = 0; i < count; ++i) { + items.add(ItemInner.asChat(true, newAlwaysShow.get(i))); + } if (count != newAlwaysShow.size()) { - includeShowMoreRow = rowCount++; - } else { - includeShowMoreRow = -1; + items.add( + ItemInner.asButton(R.drawable.arrow_more, LocaleController.formatPluralString("FilterShowMoreChats", newAlwaysShow.size() - 5), false) + .whenClicked(v -> { + includeExpanded = true; + updateRows(); + }) + ); } - } else { - includeStartRow = -1; - includeEndRow = -1; - includeShowMoreRow = -1; - } - includeSectionRow = rowCount++; - excludeHeaderRow = rowCount++; - excludeAddRow = rowCount++; - if ((newFilterFlags & MessagesController.DIALOG_FILTER_FLAG_EXCLUDE_MUTED) != 0) { - excludeMutedRow = rowCount++; - } else { - excludeMutedRow = -1; } - if ((newFilterFlags & MessagesController.DIALOG_FILTER_FLAG_EXCLUDE_READ) != 0) { - excludeReadRow = rowCount++; - } else { - excludeReadRow = -1; + items.add(ItemInner.asShadow(LocaleController.getString("FilterIncludeInfo", R.string.FilterIncludeInfo))); + if (!filter.isChatlist()) { + items.add(ItemInner.asHeader(LocaleController.getString("FilterExclude", R.string.FilterExclude))); + items.add(ItemInner.asButton(R.drawable.msg2_chats_add, LocaleController.getString("FilterRemoveChats", R.string.FilterRemoveChats), false).whenClicked(v -> selectChatsFor(false))); + if ((newFilterFlags & MessagesController.DIALOG_FILTER_FLAG_EXCLUDE_MUTED) != 0) { + items.add(ItemInner.asChat(false, LocaleController.getString("FilterMuted", R.string.FilterMuted), "muted", MessagesController.DIALOG_FILTER_FLAG_EXCLUDE_MUTED)); + } + if ((newFilterFlags & MessagesController.DIALOG_FILTER_FLAG_EXCLUDE_READ) != 0) { + items.add(ItemInner.asChat(false, LocaleController.getString("FilterRead", R.string.FilterRead), "read", MessagesController.DIALOG_FILTER_FLAG_EXCLUDE_READ)); + } + if ((newFilterFlags & MessagesController.DIALOG_FILTER_FLAG_EXCLUDE_ARCHIVED) != 0) { + items.add(ItemInner.asChat(false, LocaleController.getString("FilterArchived", R.string.FilterArchived), "archived", MessagesController.DIALOG_FILTER_FLAG_EXCLUDE_ARCHIVED)); + } + if (!newNeverShow.isEmpty()) { + int count = excludeExpanded || newNeverShow.size() < 8 ? newNeverShow.size() : Math.min(5, newNeverShow.size()); + for (int i = 0; i < count; ++i) { + items.add(ItemInner.asChat(false, newNeverShow.get(i))); + } + if (count != newNeverShow.size()) { + items.add( + ItemInner.asButton(R.drawable.arrow_more, LocaleController.formatPluralString("FilterShowMoreChats", newNeverShow.size() - 5), false) + .whenClicked(v -> { + excludeExpanded = true; + updateRows(); + }) + ); + } + } + items.add(ItemInner.asShadow(LocaleController.getString("FilterExcludeInfo", R.string.FilterExcludeInfo))); } - if ((newFilterFlags & MessagesController.DIALOG_FILTER_FLAG_EXCLUDE_ARCHIVED) != 0) { - excludeArchivedRow = rowCount++; + + if (invites.isEmpty()) { + items.add(ItemInner.asHeader(LocaleController.getString("FilterShareFolder", R.string.FilterShareFolder), true)); + items.add(ItemInner.asButton(R.drawable.msg2_link2, LocaleController.getString("FilterShareFolderButton", R.string.FilterShareFolderButton), false)); + items.add(ItemInner.asShadow(LocaleController.getString("FilterInviteLinksHintNew", R.string.FilterInviteLinksHintNew))); } else { - excludeArchivedRow = -1; - } - if (!newNeverShow.isEmpty()) { - excludeStartRow = rowCount; - int count = excludeExpanded || newNeverShow.size() < 8 ? newNeverShow.size() : Math.min(5, newNeverShow.size()); - rowCount += count; - excludeEndRow = rowCount; - if (count != newNeverShow.size()) { - excludeShowMoreRow = rowCount++; - } else { - excludeShowMoreRow = -1; + items.add(ItemInner.asHeader(LocaleController.getString("FilterInviteLinks", R.string.FilterInviteLinks), true)); + items.add(ItemInner.asCreateLink()); + for (int i = 0; i < invites.size(); ++i) { + items.add(ItemInner.asLink(invites.get(i))); } - } else { - excludeStartRow = -1; - excludeEndRow = -1; - excludeShowMoreRow = -1; + items.add(ItemInner.asShadow( + filter != null && filter.isChatlist() ? + LocaleController.getString("FilterInviteLinksHintNew", R.string.FilterInviteLinksHintNew) : + LocaleController.getString("FilterInviteLinksHint", R.string.FilterInviteLinksHint) + )); } - excludeSectionRow = rowCount++; if (!creatingNew) { - removeRow = rowCount++; - removeSectionRow = rowCount++; - } else { - removeRow = -1; - removeSectionRow = -1; + items.add(ItemInner.asButton(0, LocaleController.getString("FilterDelete", R.string.FilterDelete), true).whenClicked(this::deleteFolder)); + items.add(ItemInner.asShadow(null)); } + if (adapter != null) { - adapter.notifyDataSetChanged(); + if (animated) { + adapter.setItems(oldItems, items); + } else { + adapter.notifyDataSetChanged(); + } } } + float shiftDp = -5; + @Override public View createView(Context context) { actionBar.setBackButtonImage(R.drawable.ic_ab_back); @@ -284,8 +360,8 @@ public View createView(Context context) { actionBar.setTitle(LocaleController.getString("FilterNew", R.string.FilterNew)); } else { TextPaint paint = new TextPaint(Paint.ANTI_ALIAS_FLAG); - paint.setTextSize(AndroidUtilities.dp(20)); - actionBar.setTitle(Emoji.replaceEmoji(filter.name, paint.getFontMetricsInt(), AndroidUtilities.dp(20), false)); + paint.setTextSize(dp(20)); + actionBar.setTitle(Emoji.replaceEmoji(filter.name, paint.getFontMetricsInt(), dp(20), false)); } actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { @Override @@ -306,6 +382,15 @@ public void onItemClick(int id) { frameLayout.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundGray)); listView = new RecyclerListView(context) { + @Override + public Integer getSelectorColor(int position) { + ItemInner item = position < 0 || position >= items.size() ? null : items.get(position); + if (item != null && item.isRed) { + return Theme.multAlpha(getThemedColor(Theme.key_text_RedRegular), .12f); + } + return getThemedColor(Theme.key_listSelector); + } + @Override public boolean requestFocus(int direction, Rect previouslyFocusedRect) { return false; @@ -319,110 +404,347 @@ public boolean requestFocus(int direction, Rect previouslyFocusedRect) { if (getParentActivity() == null) { return; } - if (position == includeShowMoreRow) { - includeExpanded = true; - updateRows(); - } else if (position == excludeShowMoreRow) { - excludeExpanded = true; - updateRows(); - } else if (position == includeAddRow || position == excludeAddRow) { - ArrayList arrayList = position == excludeAddRow ? newNeverShow : newAlwaysShow; - UsersSelectActivity fragment = new UsersSelectActivity(position == includeAddRow, arrayList, newFilterFlags); - fragment.setDelegate((ids, flags) -> { - newFilterFlags = flags; - if (position == excludeAddRow) { - newNeverShow = ids; - for (int a = 0; a < newNeverShow.size(); a++) { - Long id = newNeverShow.get(a); - newAlwaysShow.remove(id); - newPinned.delete(id); - } - } else { - newAlwaysShow = ids; - for (int a = 0; a < newAlwaysShow.size(); a++) { - newNeverShow.remove(newAlwaysShow.get(a)); - } - ArrayList toRemove = new ArrayList<>(); - for (int a = 0, N = newPinned.size(); a < N; a++) { - Long did = newPinned.keyAt(a); - if (DialogObject.isEncryptedDialog(did)) { - continue; - } - if (newAlwaysShow.contains(did)) { - continue; - } - toRemove.add(did); - } - for (int a = 0, N = toRemove.size(); a < N; a++) { - newPinned.delete(toRemove.get(a)); - } - } - fillFilterName(); - checkDoneButton(false); - updateRows(); - }); - presentFragment(fragment); - } else if (position == removeRow) { - AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); - builder.setTitle(LocaleController.getString("FilterDelete", R.string.FilterDelete)); - builder.setMessage(LocaleController.getString("FilterDeleteAlert", R.string.FilterDeleteAlert)); - builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - builder.setPositiveButton(LocaleController.getString("Delete", R.string.Delete), (dialog, which) -> { - AlertDialog progressDialog = null; - if (getParentActivity() != null) { - progressDialog = new AlertDialog(getParentActivity(), AlertDialog.ALERT_TYPE_SPINNER); - progressDialog.setCanCancel(false); - progressDialog.show(); - } - final AlertDialog progressDialogFinal = progressDialog; - TLRPC.TL_messages_updateDialogFilter req = new TLRPC.TL_messages_updateDialogFilter(); - req.id = filter.id; - getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { - try { - if (progressDialogFinal != null) { - progressDialogFinal.dismiss(); - } - } catch (Exception e) { - FileLog.e(e); - } - getMessagesController().removeFilter(filter); - getMessagesStorage().deleteDialogFilter(filter); - finishFragment(); - })); - }); - AlertDialog alertDialog = builder.create(); - showDialog(alertDialog); - TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); - if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); - } - } else if (position == nameRow) { + ItemInner item = items.get(position); + if (item == null) { + return; + } + if (item.onClickListener != null) { + item.onClickListener.onClick(view); + } else if (item.viewType == VIEW_TYPE_CHAT) { + UserCell cell = (UserCell) view; + showRemoveAlert(item, cell.getName(), cell.getCurrentObject(), item.include); + } else if (item.viewType == VIEW_TYPE_LINK) { + Runnable open = () -> { + FilterChatlistActivity fragment = new FilterChatlistActivity(filter, item.link); + fragment.setOnEdit(this::onEdit); + fragment.setOnDelete(this::onDelete); + presentFragment(fragment); + }; + if (doneItem.isEnabled()) { + save(false, open); + } else { + open.run(); + } + } else if (item.viewType == VIEW_TYPE_CREATE_LINK || item.viewType == VIEW_TYPE_BUTTON && item.iconResId == R.drawable.msg2_link2) { + onClickCreateLink(view); + } else if (item.viewType == VIEW_TYPE_EDIT) { PollEditTextCell cell = (PollEditTextCell) view; cell.getTextView().requestFocus(); AndroidUtilities.showKeyboard(cell.getTextView()); - } else if (view instanceof UserCell) { - UserCell cell = (UserCell) view; - showRemoveAlert(position, cell.getName(), cell.getCurrentObject(), position < includeSectionRow); } }); listView.setOnItemLongClickListener((view, position) -> { + ItemInner item = items.get(position); + if (item == null) { + return false; + } if (view instanceof UserCell) { UserCell cell = (UserCell) view; - showRemoveAlert(position, cell.getName(), cell.getCurrentObject(), position < includeSectionRow); + showRemoveAlert(item, cell.getName(), cell.getCurrentObject(), item.include); return true; } return false; }); + DefaultItemAnimator itemAnimator = new DefaultItemAnimator(); + itemAnimator.setSupportsChangeAnimations(false); + itemAnimator.setDelayAnimations(false); + itemAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + itemAnimator.setDurations(350); + listView.setItemAnimator(itemAnimator); checkDoneButton(false); + + loadInvites(); + return fragmentView; } + + public UndoView getUndoView() { + if (getContext() == null) { + return null; + } + if (undoView == null) { + ((FrameLayout) fragmentView).addView(undoView = new UndoView(getContext()), LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM | Gravity.LEFT, 8, 0, 8, 8)); + } + return undoView; + } + + private void onClickCreateLink(View view) { + if (creatingNew && doneItem.getAlpha() > 0) { + AndroidUtilities.shakeViewSpring(view, shiftDp = -shiftDp); + BotWebViewVibrationEffect.APP_ERROR.vibrate(); + doNotCloseWhenSave = true; + showSaveHint(); + return; + } + if (!canCreateLink()) { + AndroidUtilities.shakeViewSpring(view, shiftDp = -shiftDp); + BotWebViewVibrationEffect.APP_ERROR.vibrate(); + if (TextUtils.isEmpty(newFilterName) && TextUtils.isEmpty(filter.name)) { + BulletinFactory.of(this).createErrorBulletin(LocaleController.getString("FilterInviteErrorEmptyName", R.string.FilterInviteErrorEmptyName)).show(); + } else if ((newFilterFlags & ~(MessagesController.DIALOG_FILTER_FLAG_CHATLIST | MessagesController.DIALOG_FILTER_FLAG_CHATLIST_ADMIN)) != 0) { + if (!newNeverShow.isEmpty()) { + BulletinFactory.of(this).createErrorBulletin(LocaleController.getString("FilterInviteErrorTypesExcluded", R.string.FilterInviteErrorTypesExcluded)).show(); + } else { + BulletinFactory.of(this).createErrorBulletin(LocaleController.getString("FilterInviteErrorTypes", R.string.FilterInviteErrorTypes)).show(); + } + } else if (newAlwaysShow.isEmpty()) { + BulletinFactory.of(this).createErrorBulletin(LocaleController.getString("FilterInviteErrorEmpty", R.string.FilterInviteErrorEmpty)).show(); + } else { + BulletinFactory.of(this).createErrorBulletin(LocaleController.getString("FilterInviteErrorExcluded", R.string.FilterInviteErrorExcluded)).show(); + } + return; + } + save(false, () -> { + getMessagesController().updateFilterDialogs(filter); + + ArrayList peers = new ArrayList<>(); + for (int i = 0; i < filter.alwaysShow.size(); ++i) { + long did = filter.alwaysShow.get(i); + if (did < 0) { + TLRPC.Chat chat = getMessagesController().getChat(-did); + if (canAddToFolder(chat)) { + peers.add(getMessagesController().getInputPeer(did)); + } + } + } + + final int maxCount = getUserConfig().isPremium() ? getMessagesController().dialogFiltersChatsLimitPremium : getMessagesController().dialogFiltersChatsLimitDefault; + if (peers.size() > maxCount) { + showDialog(new LimitReachedBottomSheet(this, getContext(), LimitReachedBottomSheet.TYPE_CHATS_IN_FOLDER, currentAccount)); + return; + } + + if (!peers.isEmpty()) { + TLRPC.TL_chatlists_exportChatlistInvite req = new TLRPC.TL_chatlists_exportChatlistInvite(); + req.chatlist = new TLRPC.TL_inputChatlistDialogFilter(); + req.chatlist.filter_id = filter.id; + req.peers = peers; + req.title = ""; + getConnectionsManager().sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + if ( + processErrors(err, FilterCreateActivity.this, BulletinFactory.of(FilterCreateActivity.this)) && + res instanceof TLRPC.TL_chatlists_exportedChatlistInvite + ) { + FilterCreateActivity.hideNew(0); + + getMessagesController().loadRemoteFilters(true); + TLRPC.TL_chatlists_exportedChatlistInvite inv = (TLRPC.TL_chatlists_exportedChatlistInvite) res; + FilterChatlistActivity fragment = new FilterChatlistActivity(filter, inv.invite); + fragment.setOnEdit(this::onEdit); + fragment.setOnDelete(this::onDelete); + presentFragment(fragment); + + AndroidUtilities.runOnUIThread(() -> onEdit(inv.invite), 200); + } + })); + } else { + FilterChatlistActivity fragment = new FilterChatlistActivity(filter, null); + fragment.setOnEdit(this::onEdit); + fragment.setOnDelete(this::onDelete); + presentFragment(fragment); + } + }); + } + + private HintView saveHintView; + private void showSaveHint() { + if (saveHintView != null && saveHintView.getVisibility() == View.VISIBLE) { + return; + } + + saveHintView = new HintView(getContext(), 6, true) { + @Override + public void setVisibility(int visibility) { + super.setVisibility(visibility); + if (visibility != VISIBLE) { + try { + ((ViewGroup) getParent()).removeView(this); + } catch (Exception ignore) {} + } + } + }; + saveHintView.textView.setMaxWidth(AndroidUtilities.displaySize.x); + saveHintView.setExtraTranslationY(AndroidUtilities.dp(-16)); + saveHintView.setText(LocaleController.getString("FilterFinishCreating", R.string.FilterFinishCreating)); + ViewGroup.MarginLayoutParams params = new ViewGroup.MarginLayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + params.rightMargin = AndroidUtilities.dp(3); + getParentLayout().getOverlayContainerView().addView(saveHintView, params); + saveHintView.showForView(doneItem, true); + } + + public static boolean canAddToFolder(TLRPC.Chat chat) { + return ChatObject.canUserDoAdminAction(chat, ChatObject.ACTION_INVITE) || ChatObject.isPublic(chat) && !chat.join_request; + } + + private void onDelete(TLRPC.TL_exportedChatlistInvite editedInvite) { + if (editedInvite == null) { + return; + } + + int index = -1; + for (int i = 0; i < invites.size(); ++i) { + TLRPC.TL_exportedChatlistInvite invite = invites.get(i); + if (TextUtils.equals(invite.url, editedInvite.url)) { + index = i; + break; + } + } + + if (index >= 0) { + invites.remove(index); + + if (invites.isEmpty()) { + filter.flags &= ~MessagesController.DIALOG_FILTER_FLAG_CHATLIST; + } + + updateRows(); + } + } + + private void onEdit(TLRPC.TL_exportedChatlistInvite editedInvite) { + if (editedInvite == null) { + return; + } + + int index = -1; + for (int i = 0; i < invites.size(); ++i) { + TLRPC.TL_exportedChatlistInvite invite = invites.get(i); + if (TextUtils.equals(invite.url, editedInvite.url)) { + index = i; + break; + } + } + + if (index < 0) { + invites.add(editedInvite); + } else { + invites.set(index, editedInvite); + } + updateRows(); + } + + private void deleteFolder(View view) { + if (filter != null && filter.isChatlist()) { + FolderBottomSheet.showForDeletion(this, filter.id, success -> { + finishFragment(); + }); + } else { + AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); + builder.setTitle(LocaleController.getString("FilterDelete", R.string.FilterDelete)); + builder.setMessage(LocaleController.getString("FilterDeleteAlert", R.string.FilterDeleteAlert)); + builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + builder.setPositiveButton(LocaleController.getString("Delete", R.string.Delete), (dialog, which) -> { + AlertDialog progressDialog = null; + if (getParentActivity() != null) { + progressDialog = new AlertDialog(getParentActivity(), AlertDialog.ALERT_TYPE_SPINNER); + progressDialog.setCanCancel(false); + progressDialog.show(); + } + final AlertDialog progressDialogFinal = progressDialog; + TLRPC.TL_messages_updateDialogFilter req = new TLRPC.TL_messages_updateDialogFilter(); + req.id = filter.id; + getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + try { + if (progressDialogFinal != null) { + progressDialogFinal.dismiss(); + } + } catch (Exception e) { + FileLog.e(e); + } + getMessagesController().removeFilter(filter); + getMessagesStorage().deleteDialogFilter(filter); + finishFragment(); + })); + }); + AlertDialog alertDialog = builder.create(); + showDialog(alertDialog); + TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); + if (button != null) { + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); + } + } + } + + private void onUpdate(boolean include, ArrayList prev, ArrayList next) { + int added = 0, removed = 0; + if (prev != null && next != null) { + for (int i = 0; i < prev.size(); ++i) { + if (!next.contains(prev.get(i))) { + removed++; + } + } + for (int i = 0; i < next.size(); ++i) { + if (!prev.contains(next.get(i))) { + added++; + } + } + } else if (prev != null) { + removed = prev.size(); + } else if (next != null) { + added = next.size(); + } + if (include) { + if (added > 0 && added > removed) { + onUpdate(true, added); + } else if (removed > 0) { + onUpdate(false, removed); + } + } else if (added > 0) { + onUpdate(false, added); + } + } + + private void selectChatsFor(boolean include) { + ArrayList arrayList = include ? newAlwaysShow : newNeverShow; + UsersSelectActivity fragment = new UsersSelectActivity(include, arrayList, newFilterFlags); + fragment.noChatTypes = filter.isChatlist(); + fragment.setDelegate((ids, flags) -> { + newFilterFlags = flags; + if (include) { + onUpdate(true, newAlwaysShow, ids); + newAlwaysShow = ids; + for (int a = 0; a < newAlwaysShow.size(); a++) { + newNeverShow.remove(newAlwaysShow.get(a)); + } + ArrayList toRemove = new ArrayList<>(); + for (int a = 0, N = newPinned.size(); a < N; a++) { + Long did = newPinned.keyAt(a); + if (DialogObject.isEncryptedDialog(did)) { + continue; + } + if (newAlwaysShow.contains(did)) { + continue; + } + toRemove.add(did); + } + for (int a = 0, N = toRemove.size(); a < N; a++) { + newPinned.delete(toRemove.get(a)); + } + } else { + onUpdate(false, newNeverShow, ids); + newNeverShow = ids; + for (int a = 0; a < newNeverShow.size(); a++) { + Long id = newNeverShow.get(a); + newAlwaysShow.remove(id); + newPinned.delete(id); + } + } + fillFilterName(); + checkDoneButton(false); + updateRows(); + }); + presentFragment(fragment); + } + @Override public void onResume() { super.onResume(); - if (adapter != null) { - adapter.notifyDataSetChanged(); + updateRows(); + + if (showBulletinOnResume != null) { + showBulletinOnResume.run(); } } @@ -469,7 +791,7 @@ private boolean checkDiscard() { return true; } - private void showRemoveAlert(int position, CharSequence name, Object object, boolean include) { + private void showRemoveAlert(ItemInner item, CharSequence name, Object object, boolean include) { AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); if (include) { builder.setTitle(LocaleController.getString("FilterRemoveInclusionTitle", R.string.FilterRemoveInclusionTitle)); @@ -492,48 +814,60 @@ private void showRemoveAlert(int position, CharSequence name, Object object, boo } builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); builder.setPositiveButton(LocaleController.getString("StickersRemove", R.string.StickersRemove), (dialogInterface, i) -> { - if (position == includeContactsRow) { - newFilterFlags &=~ MessagesController.DIALOG_FILTER_FLAG_CONTACTS; - } else if (position == includeNonContactsRow) { - newFilterFlags &=~ MessagesController.DIALOG_FILTER_FLAG_NON_CONTACTS; - } else if (position == includeGroupsRow) { - newFilterFlags &=~ MessagesController.DIALOG_FILTER_FLAG_GROUPS; - } else if (position == includeChannelsRow) { - newFilterFlags &=~ MessagesController.DIALOG_FILTER_FLAG_CHANNELS; - } else if (position == includeBotsRow) { - newFilterFlags &=~ MessagesController.DIALOG_FILTER_FLAG_BOTS; - } else if (position == excludeArchivedRow) { - newFilterFlags &=~ MessagesController.DIALOG_FILTER_FLAG_EXCLUDE_ARCHIVED; - } else if (position == excludeMutedRow) { - newFilterFlags &=~ MessagesController.DIALOG_FILTER_FLAG_EXCLUDE_MUTED; - } else if (position == excludeReadRow) { - newFilterFlags &=~ MessagesController.DIALOG_FILTER_FLAG_EXCLUDE_READ; + if (item.flags > 0) { + newFilterFlags &=~ item.flags; } else { - if (include) { - newAlwaysShow.remove(position - includeStartRow); - } else { - newNeverShow.remove(position - excludeStartRow); - } + (include ? newAlwaysShow : newNeverShow).remove((Long) item.did); } fillFilterName(); updateRows(); checkDoneButton(true); + + if (include) { + onUpdate(false, 1); + } }); AlertDialog alertDialog = builder.create(); showDialog(alertDialog); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } private void processDone() { - saveFilterToServer(filter, newFilterFlags, newFilterEmoticon, newFilterName, newAlwaysShow, newNeverShow, newPinned, creatingNew, false, hasUserChanged, true, true, this, () -> { - getNotificationCenter().postNotificationName(NotificationCenter.dialogFiltersUpdated); + if (saveHintView != null) { + saveHintView.hide(true); + saveHintView = null; + } + save(true, () -> { + if (doNotCloseWhenSave) { + doNotCloseWhenSave = false; + TextPaint paint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + paint.setTextSize(dp(20)); + actionBar.setTitleAnimated(Emoji.replaceEmoji(filter.name, paint.getFontMetricsInt(), dp(20), false), true, 220); + return; + } finishFragment(); }); } + private void save(boolean progress, Runnable after) { + saveFilterToServer(filter, newFilterFlags, newFilterEmoticon, newFilterName, newAlwaysShow, newNeverShow, newPinned, creatingNew, false, hasUserChanged, true, progress, this, () -> { + + hasUserChanged = false; + creatingNew = false; + filter.flags = newFilterFlags; + checkDoneButton(true); + + getNotificationCenter().postNotificationName(NotificationCenter.dialogFiltersUpdated); + + if (after != null) { + after.run(); + } + }); + } + private static void processAddFilter(MessagesController.DialogFilter filter, int newFilterFlags, String newFilterEmoticon, String newFilterName, ArrayList newAlwaysShow, ArrayList newNeverShow, boolean creatingNew, boolean atBegin, boolean hasUserChanged, boolean resetUnreadCounter, BaseFragment fragment, Runnable onFinish) { if (filter.flags != newFilterFlags || hasUserChanged) { filter.pendingUnreadCount = -1; @@ -552,6 +886,14 @@ private static void processAddFilter(MessagesController.DialogFilter filter, int fragment.getMessagesController().onFilterUpdate(filter); } fragment.getMessagesStorage().saveDialogFilter(filter, atBegin, true); + if (atBegin) { + TLRPC.TL_messages_updateDialogFiltersOrder req = new TLRPC.TL_messages_updateDialogFiltersOrder(); + ArrayList filters = fragment.getMessagesController().getDialogFilters(); + for (int a = 0, N = filters.size(); a < N; a++) { + req.order.add(filters.get(a).id); + } + fragment.getConnectionsManager().sendRequest(req, null); + } if (onFinish != null) { onFinish.run(); } @@ -663,10 +1005,12 @@ public static void saveFilterToServer(MessagesController.DialogFilter filter, in FileLog.e(e); } processAddFilter(filter, newFilterFlags, newFilterEmoticon, newFilterName, newAlwaysShow, newNeverShow, creatingNew, atBegin, hasUserChanged, resetUnreadCounter, fragment, onFinish); + } else if (onFinish != null) { + onFinish.run(); } })); if (!progress) { - processAddFilter(filter, newFilterFlags, newFilterEmoticon, newFilterName, newAlwaysShow, newNeverShow, creatingNew, atBegin, hasUserChanged, resetUnreadCounter, fragment, onFinish); + processAddFilter(filter, newFilterFlags, newFilterEmoticon, newFilterName, newAlwaysShow, newNeverShow, creatingNew, atBegin, hasUserChanged, resetUnreadCounter, fragment, null); } } @@ -735,7 +1079,7 @@ private void setTextLeft(View cell) { if (left <= MAX_NAME_LENGTH - MAX_NAME_LENGTH * 0.7f) { textCell.setText2(String.format("%d", left)); SimpleTextView textView = textCell.getTextView2(); - String key = left < 0 ? Theme.key_windowBackgroundWhiteRedText5 : Theme.key_windowBackgroundWhiteGrayText3; + int key = left < 0 ? Theme.key_text_RedRegular : Theme.key_windowBackgroundWhiteGrayText3; textView.setTextColor(Theme.getColor(key)); textView.setTag(key); textView.setAlpha(((PollEditTextCell) cell).getTextView().isFocused() || left < 0 ? 1.0f : 0.0f); @@ -745,7 +1089,140 @@ private void setTextLeft(View cell) { } } - private class ListAdapter extends RecyclerListView.SelectionAdapter { + private static final int VIEW_TYPE_HEADER = 0; + private static final int VIEW_TYPE_CHAT = 1; + private static final int VIEW_TYPE_EDIT = 2; + private static final int VIEW_TYPE_SHADOW = 3; + private static final int VIEW_TYPE_BUTTON = 4; + private static final int VIEW_TYPE_HINT = 5; + private static final int VIEW_TYPE_SHADOW_TEXT = 6; + private static final int VIEW_TYPE_LINK = 7; + private static final int VIEW_TYPE_CREATE_LINK = 8; + + private static class ItemInner extends AdapterWithDiffUtils.Item { + + private View.OnClickListener onClickListener; + + private CharSequence text; + private boolean newSpan; + + private boolean include; // or exclude + private long did; + private String chatType; + private int flags; + + private int iconResId; + private boolean isRed; + + private TLRPC.TL_exportedChatlistInvite link; + + public ItemInner(int viewType, boolean selectable) { + super(viewType, selectable); + } + + public static ItemInner asHeader(CharSequence text) { + ItemInner item = new ItemInner(VIEW_TYPE_HEADER, false); + item.text = text; + return item; + } + + public static ItemInner asHeader(CharSequence text, boolean newSpan) { + ItemInner item = new ItemInner(VIEW_TYPE_HEADER, false); + item.text = text; + item.newSpan = newSpan; + return item; + } + + public static ItemInner asChat(boolean include, long did) { + ItemInner item = new ItemInner(VIEW_TYPE_CHAT, false); + item.include = include; + item.did = did; + return item; + } + + public static ItemInner asChat(boolean include, CharSequence name, String chatType, int flags) { + ItemInner item = new ItemInner(VIEW_TYPE_CHAT, false); + item.include = include; + item.text = name; + item.chatType = chatType; + item.flags = flags; + return item; + } + + public static ItemInner asEdit() { + return new ItemInner(VIEW_TYPE_EDIT, false); + } + + public static ItemInner asShadow(CharSequence text) { + ItemInner item = new ItemInner(TextUtils.isEmpty(text) ? VIEW_TYPE_SHADOW : VIEW_TYPE_SHADOW_TEXT, false); + item.text = text; + return item; + } + + public static ItemInner asLink(TLRPC.TL_exportedChatlistInvite invite) { + ItemInner item = new ItemInner(VIEW_TYPE_LINK, false); + item.link = invite; + return item; + } + + public static ItemInner asButton(int iconResId, CharSequence text, boolean red) { + ItemInner item = new ItemInner(VIEW_TYPE_BUTTON, false); + item.iconResId = iconResId; + item.text = text; + item.isRed = red; + return item; + } + + public static ItemInner asCreateLink() { + return new ItemInner(VIEW_TYPE_CREATE_LINK, false); + } + + public ItemInner whenClicked(View.OnClickListener onClickListener) { + this.onClickListener = onClickListener; + return this; + } + + public boolean isShadow() { + return viewType == VIEW_TYPE_SHADOW || viewType == VIEW_TYPE_SHADOW_TEXT; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ItemInner other = (ItemInner) o; + if (this.viewType != other.viewType) { + return false; + } + if (viewType == VIEW_TYPE_HEADER || viewType == VIEW_TYPE_CHAT || viewType == VIEW_TYPE_SHADOW || viewType == VIEW_TYPE_BUTTON) { + if (!TextUtils.equals(text, other.text)) { + return false; + } + } + if (viewType == VIEW_TYPE_HEADER) { + return newSpan == other.newSpan; + } + if (viewType == VIEW_TYPE_CHAT) { + return ( + did == other.did && + TextUtils.equals(chatType, other.chatType) && + flags == other.flags + ); + } + if (viewType == VIEW_TYPE_LINK) { + return ( + link == other.link || + TextUtils.equals(link.url, other.link.url) && + link.revoked == other.link.revoked && + TextUtils.equals(link.title, other.link.title) && + link.peers.size() == other.link.peers.size() + ); + } + return true; + } + } + + private class ListAdapter extends AdapterWithDiffUtils { private Context mContext; @@ -756,33 +1233,38 @@ public ListAdapter(Context context) { @Override public boolean isEnabled(RecyclerView.ViewHolder holder) { int type = holder.getItemViewType(); - return type != 3 && type != 0 && type != 2 && type != 5; + return ( + type != VIEW_TYPE_SHADOW && + type != VIEW_TYPE_HEADER && + type != VIEW_TYPE_EDIT && + type != VIEW_TYPE_HINT + ); } @Override public int getItemCount() { - return rowCount; + return items.size(); } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view; switch (viewType) { - case 0: - view = new HeaderCell(mContext); + case VIEW_TYPE_HEADER: + view = new HeaderCell(mContext, 22); view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); break; - case 1: { + case VIEW_TYPE_CHAT: { UserCell cell = new UserCell(mContext, 6, 0, false); cell.setSelfAsSavedMessages(true); cell.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); view = cell; break; } - case 2: { + case VIEW_TYPE_EDIT: { PollEditTextCell cell = new PollEditTextCell(mContext, false, null, view1 -> IconSelectorAlert.show(FilterCreateActivity.this, (emoticon) -> { newFilterEmoticon = emoticon; - adapter.notifyItemChanged(nameRow); + ((PollEditTextCell) view1.getParent()).setIcon(FolderIconHelper.getTabIcon(newFilterEmoticon), newFilterEmoticon); checkDoneButton(true); })); cell.createErrorTextView(); @@ -822,17 +1304,34 @@ public void afterTextChanged(Editable s) { view = cell; break; } - case 3: + case VIEW_TYPE_SHADOW: view = new ShadowSectionCell(mContext); break; - case 4: - view = new TextCell(mContext); + case VIEW_TYPE_BUTTON: + view = new ButtonCell(mContext); view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); break; - case 5: + case VIEW_TYPE_HINT: view = new HintInnerCell(mContext); break; - case 6: + case VIEW_TYPE_LINK: + view = new LinkCell(mContext, FilterCreateActivity.this, currentAccount, filter.id) { + @Override + protected void onDelete(TLRPC.TL_exportedChatlistInvite invite) { + FilterCreateActivity.this.onDelete(invite); + } + + @Override + protected void reload() { + FilterCreateActivity.this.loadInvites(); + } + }; + break; + case VIEW_TYPE_CREATE_LINK: + view = new CreateLinkCell(mContext); + view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + break; + case VIEW_TYPE_SHADOW_TEXT: default: view = new TextInfoPrivacyCell(mContext); break; @@ -866,66 +1365,28 @@ public void onViewDetachedFromWindow(RecyclerView.ViewHolder holder) { @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + ItemInner item = items.get(position); + if (item == null) { + return; + } + boolean divider = position + 1 < items.size() && !items.get(position + 1).isShadow(); switch (holder.getItemViewType()) { - case 0: { + case VIEW_TYPE_HEADER: { HeaderCell headerCell = (HeaderCell) holder.itemView; - if (position == includeHeaderRow) { - headerCell.setText(LocaleController.getString("FilterInclude", R.string.FilterInclude)); - } else if (position == excludeHeaderRow) { - headerCell.setText(LocaleController.getString("FilterExclude", R.string.FilterExclude)); + if (item.newSpan) { + headerCell.setText(withNew(0, item.text, false)); + } else { + headerCell.setText(item.text); } break; } - case 1: { + case VIEW_TYPE_CHAT: { UserCell userCell = (UserCell) holder.itemView; - Long id; - boolean divider; - if (position >= includeStartRow && position < includeEndRow) { - id = newAlwaysShow.get(position - includeStartRow); - divider = includeShowMoreRow != -1 || position != includeEndRow - 1; - } else if (position >= excludeStartRow && position < excludeEndRow) { - id = newNeverShow.get(position - excludeStartRow); - divider = excludeShowMoreRow != -1 || position != excludeEndRow - 1; - } else { - Object object; - int flag; - String name; - if (position == includeContactsRow) { - name = LocaleController.getString("FilterContacts", R.string.FilterContacts); - object = "contacts"; - divider = position + 1 != includeSectionRow; - } else if (position == includeNonContactsRow) { - name = LocaleController.getString("FilterNonContacts", R.string.FilterNonContacts); - object = "non_contacts"; - divider = position + 1 != includeSectionRow; - } else if (position == includeGroupsRow) { - name = LocaleController.getString("FilterGroups", R.string.FilterGroups); - object = "groups"; - divider = position + 1 != includeSectionRow; - } else if (position == includeChannelsRow) { - name = LocaleController.getString("FilterChannels", R.string.FilterChannels); - object = "channels"; - divider = position + 1 != includeSectionRow; - } else if (position == includeBotsRow) { - name = LocaleController.getString("FilterBots", R.string.FilterBots); - object = "bots"; - divider = position + 1 != includeSectionRow; - } else if (position == excludeMutedRow) { - name = LocaleController.getString("FilterMuted", R.string.FilterMuted); - object = "muted"; - divider = position + 1 != excludeSectionRow; - } else if (position == excludeReadRow) { - name = LocaleController.getString("FilterRead", R.string.FilterRead); - object = "read"; - divider = position + 1 != excludeSectionRow; - } else { - name = LocaleController.getString("FilterArchived", R.string.FilterArchived); - object = "archived"; - divider = position + 1 != excludeSectionRow; - } - userCell.setData(object, name, null, 0, divider); + if (item.chatType != null) { + userCell.setData(item.chatType, item.text, null, 0, divider); return; } + long id = item.did; if (id > 0) { TLRPC.User user = getMessagesController().getUser(id); if (user != null) { @@ -944,7 +1405,11 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (chat != null) { String status; if (chat.participants_count != 0) { - status = LocaleController.formatPluralString("Members", chat.participants_count); + if (ChatObject.isChannelAndNotMegaGroup(chat)) { + status = LocaleController.formatPluralStringComma("Subscribers", chat.participants_count); + } else { + status = LocaleController.formatPluralStringComma("Members", chat.participants_count); + } } else if (!ChatObject.isPublic(chat)) { if (ChatObject.isChannel(chat) && !chat.megagroup) { status = LocaleController.getString("ChannelPrivate", R.string.ChannelPrivate); @@ -963,49 +1428,33 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } break; } - case 3: { - if (position == removeSectionRow) { - holder.itemView.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); - } else { - holder.itemView.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); - } + case VIEW_TYPE_SHADOW: { + holder.itemView.setBackground(Theme.getThemedDrawableByKey(mContext, divider ? R.drawable.greydivider : R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); break; } - case 4: { - TextCell textCell = (TextCell) holder.itemView; - if (position == removeRow) { - textCell.setColors(null, Theme.key_windowBackgroundWhiteRedText5); - textCell.setText(LocaleController.getString("FilterDelete", R.string.FilterDelete), false); - } else if (position == includeShowMoreRow) { - textCell.setColors(Theme.key_switchTrackChecked, Theme.key_windowBackgroundWhiteBlueText4); - textCell.setTextAndIcon(LocaleController.formatPluralString("FilterShowMoreChats", newAlwaysShow.size() - 5), R.drawable.arrow_more, false); - } else if (position == excludeShowMoreRow) { - textCell.setColors(Theme.key_switchTrackChecked, Theme.key_windowBackgroundWhiteBlueText4); - textCell.setTextAndIcon(LocaleController.formatPluralString("FilterShowMoreChats", newNeverShow.size() - 5), R.drawable.arrow_more, false); - } else if (position == includeAddRow) { - textCell.setColors(Theme.key_switchTrackChecked, Theme.key_windowBackgroundWhiteBlueText4); - textCell.setTextAndIcon(LocaleController.getString("FilterAddChats", R.string.FilterAddChats), R.drawable.msg_chats_add, position + 1 != includeSectionRow); - } else if (position == excludeAddRow) { - textCell.setColors(Theme.key_switchTrackChecked, Theme.key_windowBackgroundWhiteBlueText4); - textCell.setTextAndIcon(LocaleController.getString("FilterRemoveChats", R.string.FilterRemoveChats), R.drawable.msg_chats_add, position + 1 != excludeSectionRow); - } + case VIEW_TYPE_BUTTON: { + ButtonCell buttonCell = (ButtonCell) holder.itemView; + buttonCell.setRed(item.isRed); + buttonCell.set(item.iconResId, item.text, divider); break; } - case 6: { + case VIEW_TYPE_SHADOW_TEXT: { TextInfoPrivacyCell cell = (TextInfoPrivacyCell) holder.itemView; - if (position == includeSectionRow) { - cell.setText(LocaleController.getString("FilterIncludeInfo", R.string.FilterIncludeInfo)); - } else if (position == excludeSectionRow) { - cell.setText(LocaleController.getString("FilterExcludeInfo", R.string.FilterExcludeInfo)); - } - if (position == excludeSectionRow && removeSectionRow == -1) { - holder.itemView.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); - } else { - holder.itemView.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); - } + cell.setText(item.text); + holder.itemView.setBackground(Theme.getThemedDrawableByKey(mContext, divider ? R.drawable.greydivider : R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + break; + } + case VIEW_TYPE_LINK: { + LinkCell linkCell = (LinkCell) holder.itemView; + linkCell.setInvite(item.link, divider); + break; + } + case VIEW_TYPE_CREATE_LINK: { + createLinkCell = (CreateLinkCell) holder.itemView; + createLinkCell.setDivider(divider); break; } - case 2: { + case VIEW_TYPE_EDIT: { PollEditTextCell cell = (PollEditTextCell) holder.itemView; cell.setIcon(FolderIconHelper.getTabIcon(newFilterEmoticon), newFilterEmoticon); break; @@ -1015,23 +1464,11 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { @Override public int getItemViewType(int position) { - if (position == includeHeaderRow || position == excludeHeaderRow) { - return 0; - } else if (position >= includeStartRow && position < includeEndRow || position >= excludeStartRow && position < excludeEndRow || - position == includeContactsRow || position == includeNonContactsRow || position == includeGroupsRow || position == includeChannelsRow || position == includeBotsRow || - position == excludeReadRow || position == excludeArchivedRow || position == excludeMutedRow) { - return 1; - } else if (position == nameRow) { - return 2; - } else if (position == nameSectionRow || position == namePreSectionRow || position == removeSectionRow) { - return 3; - } else if (position == imageRow) { - return 5; - } else if (position == includeSectionRow || position == excludeSectionRow) { - return 6; - } else { - return 4; + ItemInner item = items.get(position); + if (item == null) { + return VIEW_TYPE_SHADOW; } + return item.viewType; } } @@ -1067,7 +1504,7 @@ public ArrayList getThemeDescriptions() { themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{HeaderCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueHeader)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); - themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteRedText5)); + themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextCell.class}, new String[]{"textView"}, null, null, null, Theme.key_text_RedRegular)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueText4)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextCell.class}, new String[]{"ImageView"}, null, null, null, Theme.key_switchTrackChecked)); @@ -1092,4 +1529,942 @@ public ArrayList getThemeDescriptions() { return themeDescriptions; } + + private static class ButtonCell extends FrameLayout { + private ImageView imageView; + private TextView textView; + public ButtonCell(Context context) { + super(context); + + imageView = new ImageView(context); + imageView.setScaleType(ImageView.ScaleType.CENTER); + addView(imageView, LayoutHelper.createFrame(24, 24, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL, 24, 0, 24, 0)); + + textView = new TextView(context); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + textView.setLines(1); + textView.setSingleLine(); + textView.setPadding(LocaleController.isRTL ? 24 : 0, 0, LocaleController.isRTL ? 0 : 24, 0); + textView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); + addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.FILL_HORIZONTAL | Gravity.CENTER_VERTICAL, LocaleController.isRTL ? 0 : 72, 0, LocaleController.isRTL ? 72 : 0, 0)); + } + + public void setRed(boolean red) { + imageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(red ? Theme.key_text_RedBold : Theme.key_windowBackgroundWhiteBlueText2), PorterDuff.Mode.MULTIPLY)); + textView.setTextColor(Theme.getColor(red ? Theme.key_text_RedRegular : Theme.key_windowBackgroundWhiteBlueText4)); + } + + private int lastIconResId; + public void set(int iconResId, CharSequence text, boolean divider) { + final int rtl = LocaleController.isRTL ? -1 : 1; + + if (iconResId == 0) { + imageView.setVisibility(View.GONE); + } else { + imageView.setVisibility(View.VISIBLE); + imageView.setImageResource(iconResId); + } + if (LocaleController.isRTL) { + ((MarginLayoutParams) textView.getLayoutParams()).rightMargin = dp(iconResId == 0 ? 24 : 72); + } else { + ((MarginLayoutParams) textView.getLayoutParams()).leftMargin = dp(iconResId == 0 ? 24 : 72); + } + textView.setText(text); + + boolean translateText = !divider && iconResId != 0; + if (this.translateText == null || this.translateText != translateText) { + this.translateText = translateText; + if (lastIconResId == iconResId) { + textView.clearAnimation(); + textView.animate().translationX(translateText ? dp(-7 * rtl) : 0).setDuration(180).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).start(); + } else { + textView.setTranslationX(translateText ? dp(-7 * rtl) : 0); + } + } + setWillNotDraw(!(this.divider = divider)); + lastIconResId = iconResId; + } + + private boolean divider = true; + private Boolean translateText = null; + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + if (divider) { + canvas.drawRect(textView.getLeft(), getMeasuredHeight() - 1, textView.getRight(), getMeasuredHeight(), Theme.dividerPaint); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure( + MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(dp(48), MeasureSpec.EXACTLY) + ); + } + } + + private static class CreateLinkCell extends FrameLayout { + TextView textView; + ImageView imageView; + public CreateLinkCell(Context context) { + super(context); + + textView = new TextView(context); + textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlueText4)); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + textView.setText(LocaleController.getString("CreateNewLink", R.string.CreateNewLink)); + textView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); + textView.setPadding(LocaleController.isRTL ? 16 : 0, 0, LocaleController.isRTL ? 0 : 16, 0); + addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.FILL_HORIZONTAL | Gravity.CENTER_VERTICAL, LocaleController.isRTL ? 0 : 64, 0, LocaleController.isRTL ? 64 : 0, 0)); + + imageView = new ImageView(context); + Drawable drawable1 = context.getResources().getDrawable(R.drawable.poll_add_circle); + Drawable drawable2 = context.getResources().getDrawable(R.drawable.poll_add_plus); + drawable1.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_featuredStickers_addButton), PorterDuff.Mode.MULTIPLY)); + drawable2.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_checkboxCheck), PorterDuff.Mode.MULTIPLY)); + imageView.setImageDrawable(new CombinedDrawable(drawable1, drawable2)); + imageView.setScaleType(ImageView.ScaleType.CENTER); + addView(imageView, LayoutHelper.createFrame(32, 32, Gravity.CENTER_VERTICAL | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), LocaleController.isRTL ? 0 : 16, 0, LocaleController.isRTL ? 16 : 0, 0)); + } + + public void setText(String text) { + textView.setText(text); + } + + boolean needDivider; + + public void setDivider(boolean divider) { + if (needDivider != divider) { + setWillNotDraw(!(needDivider = divider)); + } + } + + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + textView.setAlpha(enabled ? 1f : 0.5f); + imageView.setAlpha(enabled ? 1f : 0.5f); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + if (needDivider) { + canvas.drawRect(textView.getLeft(), getMeasuredHeight() - 1, textView.getRight(), getMeasuredHeight(), Theme.dividerPaint); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(dp(45), MeasureSpec.EXACTLY)); + } + } + + private static class LinkCell extends FrameLayout { + + private BaseFragment fragment; + private int currentAccount; + private int filterId; + + Drawable linkIcon, revokedLinkIcon; + AnimatedTextView titleTextView; + AnimatedTextView subtitleTextView; + ImageView optionsIcon; + Paint paint, revokedPaint; + + float revokeT; + + boolean needDivider; + + public LinkCell(Context context, BaseFragment fragment, int currentAccount, int filterId) { + super(context); + + this.fragment = fragment; + this.currentAccount = currentAccount; + this.filterId = filterId; + + setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); + + setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + + titleTextView = new AnimatedTextView(context, true, true, false); + titleTextView.setTextSize(AndroidUtilities.dp(15.66f)); + titleTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + titleTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); + titleTextView.setEllipsizeByGradient(true); + addView(titleTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 20, Gravity.TOP | Gravity.FILL_HORIZONTAL, LocaleController.isRTL ? 56 : 64, 10.33f, LocaleController.isRTL ? 64 : 56, 0)); + + subtitleTextView = new AnimatedTextView(context, false, false, false); + subtitleTextView.setTextSize(AndroidUtilities.dp(13)); + subtitleTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2)); + subtitleTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); + addView(subtitleTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 16, Gravity.TOP | Gravity.FILL_HORIZONTAL, LocaleController.isRTL ? 56 : 64, 33.33f, LocaleController.isRTL ? 64 : 56, 0)); + + optionsIcon = new ImageView(context); + optionsIcon.setImageDrawable(getContext().getResources().getDrawable(R.drawable.ic_ab_other)); + optionsIcon.setScaleType(ImageView.ScaleType.CENTER); + optionsIcon.setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_listSelector))); + optionsIcon.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_stickers_menu), PorterDuff.Mode.SRC_IN)); + optionsIcon.setOnClickListener(e -> options()); + optionsIcon.setContentDescription(LocaleController.getString("AccDescrMoreOptions", R.string.AccDescrMoreOptions)); + addView(optionsIcon, LayoutHelper.createFrame(40, 40, Gravity.CENTER_VERTICAL | (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT), LocaleController.isRTL ? 8 : 4, 4, LocaleController.isRTL ? 4 : 8, 4)); + + paint = new Paint(); + paint.setColor(Theme.getColor(Theme.key_featuredStickers_addButton)); + revokedPaint = new Paint(); + revokedPaint.setColor(Theme.getColor(Theme.key_color_red)); + linkIcon = getContext().getResources().getDrawable(R.drawable.msg_link_1).mutate(); + linkIcon.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)); + revokedLinkIcon = getContext().getResources().getDrawable(R.drawable.msg_link_2).mutate(); + revokedLinkIcon.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)); + + setWillNotDraw(false); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + int cx = LocaleController.isRTL ? getMeasuredWidth() - dp(32) : dp(32); + + canvas.drawCircle(cx, getMeasuredHeight() / 2f, dp(16), paint); + if (revokeT > 0) { + canvas.drawCircle(cx, getMeasuredHeight() / 2f, dp(16) * revokeT, revokedPaint); + } + + if (revokeT < 1) { + linkIcon.setAlpha((int) (0xFF * (1f - revokeT))); + linkIcon.setBounds(cx - dp(14), getMeasuredHeight() / 2 - dp(14), cx + dp(14), getMeasuredHeight() / 2 + dp(14)); + linkIcon.draw(canvas); + } + if (revokeT > 0) { + revokedLinkIcon.setAlpha((int) (0xFF * revokeT)); + revokedLinkIcon.setBounds(cx - dp(14), getMeasuredHeight() / 2 - dp(14), cx + dp(14), getMeasuredHeight() / 2 + dp(14)); + revokedLinkIcon.draw(canvas); + } + + if (needDivider) { + canvas.drawRect(LocaleController.isRTL ? 0 : dp(64), getMeasuredHeight() - 1, getMeasuredWidth() - (LocaleController.isRTL ? dp(64) : 0), getMeasuredHeight(), Theme.dividerPaint); + } + } + + private boolean lastRevoked; + private ValueAnimator valueAnimator; + public void setRevoked(boolean value, boolean animated) { + lastRevoked = value; + if ((value ? 1 : 0) != revokeT) { + if (valueAnimator != null) { + valueAnimator.cancel(); + valueAnimator = null; + } + + if (animated) { + valueAnimator = ValueAnimator.ofFloat(revokeT, value ? 1 : 0); + valueAnimator.addUpdateListener(anm -> { + revokeT = (float) anm.getAnimatedValue(); + invalidate(); + }); + valueAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator anm) { + revokeT = value ? 1 : 0; + invalidate(); + } + }); + valueAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + valueAnimator.setDuration(350); + valueAnimator.start(); + } else { + revokeT = value ? 1 : 0; + invalidate(); + } + } + } + + protected String lastUrl; + private TLRPC.TL_exportedChatlistInvite lastInvite; + + public void setInvite(TLRPC.TL_exportedChatlistInvite invite, boolean divider) { + boolean animated = lastInvite == invite; + lastInvite = invite; + String url = lastUrl = invite.url; + if (url.startsWith("http://")) + url = url.substring(7); + if (url.startsWith("https://")) + url = url.substring(8); + if (TextUtils.isEmpty(invite.title)) { + titleTextView.setText(url, animated); + } else { + titleTextView.setText(invite.title, animated); + } + subtitleTextView.setText(LocaleController.formatPluralString("FilterInviteChats", invite.peers.size()), animated); + if (needDivider != divider) { + needDivider = divider; + invalidate(); + } + setRevoked(invite.revoked, animated); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(dp(60), MeasureSpec.EXACTLY)); + } + + public void options() { + if (fragment == null) { + return; + } + ItemOptions options = ItemOptions.makeOptions(fragment, this); + options.add(R.drawable.msg_qrcode, LocaleController.getString("GetQRCode", R.string.GetQRCode), this::qrcode); + options.add(R.drawable.msg_delete, LocaleController.getString("DeleteLink", R.string.DeleteLink), true, this::deleteLink); + if (LocaleController.isRTL) { + options.setGravity(Gravity.LEFT); + } + options.show(); + } + + private String getSlug() { + if (lastUrl == null) { + return null; + } + return lastUrl.substring(lastUrl.lastIndexOf('/') + 1); + } + + private void revoke(boolean revoke) { + String slug = getSlug(); + if (slug == null) { + return; + } + + TLRPC.TL_chatlists_editExportedInvite req = new TLRPC.TL_chatlists_editExportedInvite(); + req.chatlist = new TLRPC.TL_inputChatlistDialogFilter(); + req.chatlist.filter_id = filterId; + req.revoked = revoke; + req.slug = getSlug(); + final AlertDialog progressDialog = new AlertDialog(getContext(), AlertDialog.ALERT_TYPE_SPINNER); + progressDialog.showDelayed(180); + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + setRevoked(revoke, true); + if (lastInvite != null) { + lastInvite.revoked = revoke; + } + progressDialog.dismiss(); + })); + } + + public void deleteLink() { + String slug = getSlug(); + if (slug == null) { + return; + } + + TLRPC.TL_chatlists_deleteExportedInvite req = new TLRPC.TL_chatlists_deleteExportedInvite(); + req.chatlist = new TLRPC.TL_inputChatlistDialogFilter(); + req.chatlist.filter_id = filterId; + req.slug = slug; + Runnable update = () -> onDelete(lastInvite); + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + if (err != null) { + BulletinFactory.of(fragment).createErrorBulletin(LocaleController.getString("UnknownError", R.string.UnknownError)).show(); + AndroidUtilities.cancelRunOnUIThread(update); + } + })); + + AndroidUtilities.runOnUIThread(update, 150); + } + + protected void onDelete(TLRPC.TL_exportedChatlistInvite invite) { + + } + + protected void reload() { + + } + + public void qrcode() { + if (lastUrl == null) { + return; + } + + QRCodeBottomSheet qrCodeBottomSheet = new QRCodeBottomSheet(getContext(), LocaleController.getString("InviteByQRCode", R.string.InviteByQRCode), lastUrl, LocaleController.getString("QRCodeLinkHelpFolder", R.string.QRCodeLinkHelpFolder), false); + qrCodeBottomSheet.setCenterAnimation(R.raw.qr_code_logo); + qrCodeBottomSheet.show(); + } + + @Override + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(info); + info.setContentDescription( + (lastInvite != null && !TextUtils.isEmpty(lastInvite.title) ? lastInvite.title + "\n " : "") + + LocaleController.getString("InviteLink", R.string.InviteLink) + ", " + subtitleTextView.getText() + + (lastInvite != null && TextUtils.isEmpty(lastInvite.title) ? "\n\n" + lastInvite.url : "") + ); + } + } + + public static void hideNew(int type) { + MessagesController.getGlobalMainSettings().edit().putBoolean("n_" + type, true).apply(); + } + + public static CharSequence withNew(int type, CharSequence string, boolean outline) { + if (type < 0 || MessagesController.getGlobalMainSettings().getBoolean("n_" + type, false)) { + return string; + } + Context context = ApplicationLoader.applicationContext; + if (context == null) { + return string; + } + + SpannableStringBuilder text = new SpannableStringBuilder(string); + text.append(" "); + SpannableString newText = new SpannableString("NEW"); // new SpannableString(LocaleController.getString("New", R.string.New)); + if (outline) { + Drawable drawable = context.getResources().getDrawable(R.drawable.msg_other_new_outline).mutate(); + drawable.setBounds(0, -dp(8), drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight() - dp(8)); + newText.setSpan(new ColorImageSpan(drawable, DynamicDrawableSpan.ALIGN_BOTTOM), 0, newText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } else { + Drawable bg = context.getResources().getDrawable(R.drawable.msg_other_new_filled).mutate(); + Drawable txt = context.getResources().getDrawable(R.drawable.msg_other_new_filled_text).mutate(); + bg.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_featuredStickers_unread), PorterDuff.Mode.MULTIPLY)); + txt.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_featuredStickers_buttonText), PorterDuff.Mode.MULTIPLY)); + Drawable drawable = new CombinedDrawable(bg, txt); + drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); + newText.setSpan(new ImageSpan(drawable, DynamicDrawableSpan.ALIGN_BOTTOM), 0, newText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } +// newText.setSpan(new NewSpan(outline), 0, newText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + text.append(newText); + return text; + } + + public static class ColorImageSpan extends ImageSpan { + public ColorImageSpan(Drawable drawable) { + super(drawable); + } + public ColorImageSpan(Drawable drawable, int align) { + super(drawable, align); + } + + int lastColor; + @Override + public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) { + if (paint.getColor() != lastColor) { + if (getDrawable() != null) { + getDrawable().setColorFilter(new PorterDuffColorFilter(lastColor = paint.getColor(), PorterDuff.Mode.MULTIPLY)); + } + } + super.draw(canvas, text, start, end, x, top, y, bottom, paint); + } + } + + public static class NewSpan extends ReplacementSpan { + + TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + Paint bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + StaticLayout layout; + float width, height; + + private boolean outline; + + public NewSpan(boolean outline) { + this.outline = outline; + + textPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + if (outline) { + bgPaint.setStyle(Paint.Style.STROKE); + bgPaint.setStrokeWidth(AndroidUtilities.dpf2(1.33f)); + textPaint.setTextSize(dp(10)); + textPaint.setStyle(Paint.Style.FILL_AND_STROKE); + textPaint.setStrokeWidth(AndroidUtilities.dpf2(0.2f)); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + textPaint.setLetterSpacing(.03f); + } + } else { + bgPaint.setStyle(Paint.Style.FILL); + textPaint.setTextSize(dp(12)); + } + } + + public StaticLayout makeLayout() { + if (layout == null) { + layout = new StaticLayout("NEW"/*LocaleController.getString("New", R.string.New)*/, textPaint, AndroidUtilities.displaySize.x, Layout.Alignment.ALIGN_NORMAL, 1, 0, false); + width = layout.getLineWidth(0); + height = layout.getHeight(); + } + return layout; + } + + @Override + public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, @Nullable Paint.FontMetricsInt fm) { + makeLayout(); + return (int) (dp(10) + width); + } + + @Override + public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float _x, int top, int _y, int bottom, @NonNull Paint paint) { + makeLayout(); + + int color = paint.getColor(); + bgPaint.setColor(color); + if (outline) { + textPaint.setColor(color); + } else { + textPaint.setColor(AndroidUtilities.computePerceivedBrightness(color) > .721f ? Color.BLACK : Color.WHITE); + } + + float x = _x + dp(2), y = _y - height + dp(1); + AndroidUtilities.rectTmp.set(x, y, x + width, y + height); + float r; + if (outline) { + r = dp(3.66f); + AndroidUtilities.rectTmp.left -= dp(4); + AndroidUtilities.rectTmp.top -= dp(2.33f); + AndroidUtilities.rectTmp.right += dp(3.66f); + AndroidUtilities.rectTmp.bottom += dp(1.33f); + } else { + r = dp(4.4f); + AndroidUtilities.rectTmp.inset(dp(-4), dp(-2.33f)); + } + canvas.drawRoundRect(AndroidUtilities.rectTmp, r, r, bgPaint); + + canvas.save(); + canvas.translate(x, y); + layout.draw(canvas); + canvas.restore(); + } + } + + private boolean showedUpdateBulletin; + private Runnable showBulletinOnResume; + + private void onUpdate(boolean add, int count) { + if (showedUpdateBulletin) { + return; + } + + if (filter != null && filter.isChatlist() && filter.isMyChatlist()) { + showedUpdateBulletin = true; + showBulletinOnResume = () -> { + BulletinFactory.of(this).createSimpleBulletin( + add ? R.raw.folder_in : R.raw.folder_out, + add ? + LocaleController.formatPluralString("FolderLinkAddedChats", count) : + LocaleController.formatPluralString("FolderLinkRemovedChats", count), + LocaleController.getString("FolderLinkChatlistUpdate", R.string.FolderLinkChatlistUpdate) + ).setDuration(Bulletin.DURATION_PROLONG).show(); + }; + if (getLayoutContainer() != null) { + showBulletinOnResume.run(); + showBulletinOnResume = null; + } + } + } + + public static class FilterInvitesBottomSheet extends BottomSheetWithRecyclerListView { + + public static void show(BaseFragment fragment, MessagesController.DialogFilter filter, Runnable onLoaded) { + long start = System.currentTimeMillis(); + TLRPC.TL_chatlists_getExportedInvites req = new TLRPC.TL_chatlists_getExportedInvites(); + req.chatlist = new TLRPC.TL_inputChatlistDialogFilter(); + req.chatlist.filter_id = filter.id; + fragment.getConnectionsManager().sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + if (fragment == null || fragment.getContext() == null) { + return; + } + if (res instanceof TLRPC.TL_chatlists_exportedInvites) { + TLRPC.TL_chatlists_exportedInvites invs = (TLRPC.TL_chatlists_exportedInvites) res; + fragment.getMessagesController().putChats(invs.chats, false); + fragment.getMessagesController().putUsers(invs.users, false); + new FilterCreateActivity.FilterInvitesBottomSheet(fragment, filter, ((TLRPC.TL_chatlists_exportedInvites) res).invites).show(); + } else if (err != null && "FILTER_ID_INVALID".equals(err.text) && !filter.isDefault()) { + new FilterCreateActivity.FilterInvitesBottomSheet(fragment, filter, null).show(); + } else { + BulletinFactory.of(fragment).createErrorBulletin(LocaleController.getString("UnknownError", R.string.UnknownError)).show(); + } + if (onLoaded != null) { + AndroidUtilities.runOnUIThread(onLoaded, Math.max(0, 200 - (System.currentTimeMillis() - start))); + } + })); + } + + private MessagesController.DialogFilter filter; + private ArrayList invites = new ArrayList<>(); + + private FrameLayout bulletinContainer; + + private AdapterWithDiffUtils adapter; + + private TextView button; + + public FilterInvitesBottomSheet(BaseFragment fragment, MessagesController.DialogFilter filter, ArrayList loadedInvites) { + super(fragment, false, false); + + this.filter = filter; + + if (loadedInvites != null) { + invites.addAll(loadedInvites); + } + updateRows(false); + + actionBar.setTitle(getTitle()); + + fixNavigationBar(Theme.getColor(Theme.key_dialogBackground)); + + button = new TextView(getContext()); + button.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + button.setTextColor(Theme.getColor(Theme.key_featuredStickers_buttonText)); + button.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + button.setBackground(Theme.AdaptiveRipple.filledRectByKey(Theme.key_featuredStickers_addButton, 8)); + button.setText(LocaleController.getString("FolderLinkShareButton", R.string.FolderLinkShareButton)); + button.setGravity(Gravity.CENTER); + button.setOnClickListener(e -> createLink()); + containerView.addView(button, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.BOTTOM | Gravity.FILL_HORIZONTAL, 16, 10, 16, 10)); + + bulletinContainer = new FrameLayout(getContext()); + containerView.addView(bulletinContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 100, Gravity.BOTTOM, 6, 0, 6, 0)); + + updateCreateInviteButton(); + } + + private void updateCreateInviteButton() { + button.setVisibility(invites.isEmpty() ? View.VISIBLE : View.GONE); + recyclerListView.setPadding(AndroidUtilities.dp(6), 0, AndroidUtilities.dp(6), invites.isEmpty() ? AndroidUtilities.dp(68) : 0); + } + + @Override + protected CharSequence getTitle() { + return LocaleController.formatString("FolderLinkShareTitle", R.string.FolderLinkShareTitle, filter == null ? "" : filter.name); + } + + private ArrayList oldItems = new ArrayList<>(); + private ArrayList items = new ArrayList<>(); + + private void updateRows(boolean animated) { + oldItems.clear(); + oldItems.addAll(items); + + items.clear(); + + items.add(ItemInner.asHeader(null)); + if (!invites.isEmpty()) { + items.add(ItemInner.asShadow(null)); + items.add(ItemInner.asCreateLink()); + for (int i = 0; i < invites.size(); ++i) { + items.add(ItemInner.asLink(invites.get(i))); + } + } + + if (adapter != null) { + if (animated) { + adapter.setItems(oldItems, items); + } else { + notifyDataSetChanged(); + } + } + } + + @Override + protected RecyclerListView.SelectionAdapter createAdapter() { + return adapter = new AdapterWithDiffUtils() { + + private RecyclerListView.Adapter realAdapter() { + return recyclerListView.getAdapter(); + } + + @Override + public void notifyItemChanged(int position) { + realAdapter().notifyItemChanged(position + 1); + } + + @Override + public void notifyItemChanged(int position, @Nullable Object payload) { + realAdapter().notifyItemChanged(position + 1, payload); + } + + @Override + public void notifyItemInserted(int position) { + realAdapter().notifyItemInserted(position + 1); + } + + @Override + public void notifyItemMoved(int fromPosition, int toPosition) { + realAdapter().notifyItemMoved(fromPosition + 1, toPosition); + } + + @Override + public void notifyItemRangeChanged(int positionStart, int itemCount) { + realAdapter().notifyItemRangeChanged(positionStart + 1, itemCount); + } + + @Override + public void notifyItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) { + realAdapter().notifyItemRangeChanged(positionStart + 1, itemCount, payload); + } + + @Override + public void notifyItemRangeInserted(int positionStart, int itemCount) { + realAdapter().notifyItemRangeInserted(positionStart + 1, itemCount); + } + + @Override + public void notifyItemRangeRemoved(int positionStart, int itemCount) { + realAdapter().notifyItemRangeRemoved(positionStart + 1, itemCount); + } + + @Override + public void notifyItemRemoved(int position) { + realAdapter().notifyItemRemoved(position + 1); + } + + @Override + public void notifyDataSetChanged() { + realAdapter().notifyDataSetChanged(); + } + + @Override + public boolean isEnabled(RecyclerView.ViewHolder holder) { + int viewType = holder.getItemViewType(); + return viewType == VIEW_TYPE_CREATE_LINK || viewType == VIEW_TYPE_LINK; + } + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view; + if (viewType == VIEW_TYPE_CREATE_LINK) { + view = new CreateLinkCell(getContext()); + view.setBackgroundColor(Theme.getColor(Theme.key_dialogBackground)); + } else if (viewType == VIEW_TYPE_LINK) { + view = new LinkCell(getContext(), null, currentAccount, filter.id) { + @Override + public void options() { + ItemOptions options = ItemOptions.makeOptions(container, this); + options.add(R.drawable.msg_copy, LocaleController.getString("CopyLink", R.string.CopyLink), this::copy); + options.add(R.drawable.msg_qrcode, LocaleController.getString("GetQRCode", R.string.GetQRCode), this::qrcode); + options.add(R.drawable.msg_delete, LocaleController.getString("DeleteLink", R.string.DeleteLink), true, this::deleteLink); + if (LocaleController.isRTL) { + options.setGravity(Gravity.LEFT); + } + options.show(); + } + + public void copy() { + if (lastUrl == null) { + return; + } + + if (AndroidUtilities.addToClipboard(lastUrl)) { + BulletinFactory.of(bulletinContainer, null).createCopyLinkBulletin().show(); + } + } + + @Override + protected void onDelete(TLRPC.TL_exportedChatlistInvite invite) { + invites.remove(invite); + updateCreateInviteButton(); + updateRows(true); + } + }; + view.setBackgroundColor(Theme.getColor(Theme.key_dialogBackground)); + } else if (viewType == VIEW_TYPE_SHADOW_TEXT || viewType == VIEW_TYPE_SHADOW) { + view = new TextInfoPrivacyCell(getContext()); + view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundGray)); + } else { + view = new HeaderView(getContext()); +// TextView textView = new TextView(getContext()); +// textView.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); +// textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); +// textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 19); +// textView.setPadding(AndroidUtilities.dp(21), AndroidUtilities.dp(16), AndroidUtilities.dp(21), AndroidUtilities.dp(8)); +// view = textView; + } + return new RecyclerListView.Holder(view); + } + + @Override + public int getItemViewType(int position) { + return items.get(position).viewType; + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + int viewType = holder.getItemViewType(); + ItemInner item = items.get(position); + boolean divider = position + 1 < items.size() && !items.get(position + 1).isShadow(); + if (viewType == VIEW_TYPE_LINK) { + LinkCell linkCell = (LinkCell) holder.itemView; + linkCell.setInvite(item.link, divider); + } else if (viewType == VIEW_TYPE_SHADOW_TEXT || viewType == VIEW_TYPE_SHADOW) { + TextInfoPrivacyCell cell = (TextInfoPrivacyCell) holder.itemView; + if (viewType == VIEW_TYPE_SHADOW_TEXT) { + cell.setFixedSize(0); + cell.setText(item.text); + } else { + cell.setFixedSize(12); + cell.setText(""); + } + cell.setForeground(Theme.getThemedDrawableByKey(getContext(), divider ? R.drawable.greydivider : R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + } else if (viewType == VIEW_TYPE_HEADER) { +// HeaderView headerV = (HeaderView) holder.itemView; +// textView.setText(item.text); + } else if (viewType == VIEW_TYPE_CREATE_LINK) { + CreateLinkCell createLinkCell = (CreateLinkCell) holder.itemView; + createLinkCell.setText(LocaleController.getString("CreateNewInviteLink", R.string.CreateNewInviteLink)); + createLinkCell.setDivider(divider); + } + } + + @Override + public int getItemCount() { + return items.size(); + } + }; + } + + private class HeaderView extends FrameLayout { + + private final ImageView imageView; + private final TextView titleView; + private final TextView subtitleView; + private final ImageView closeImageView; + + public HeaderView(Context context) { + super(context); + + imageView = new ImageView(context); + imageView.setScaleType(ImageView.ScaleType.CENTER); + imageView.setImageResource(R.drawable.msg_limit_links); + imageView.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)); + imageView.setBackground(Theme.createRoundRectDrawable(dp(22), Theme.getColor(Theme.key_featuredStickers_addButton))); + addView(imageView, LayoutHelper.createFrame(54, 44, Gravity.CENTER_HORIZONTAL | Gravity.TOP, 0, 22, 0, 0)); + + titleView = new TextView(context); + titleView.setText(getTitle()); + titleView.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); + titleView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); + titleView.setGravity(Gravity.CENTER_HORIZONTAL); + addView(titleView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL | Gravity.TOP, 20, 84, 20, 0)); + + subtitleView = new TextView(context); + subtitleView.setText(invites.isEmpty() ? + LocaleController.getString("FolderLinkShareSubtitleEmpty", R.string.FolderLinkShareSubtitleEmpty) : + LocaleController.getString("FolderLinkShareSubtitle", R.string.FolderLinkShareSubtitle) + ); + subtitleView.setLines(2); + subtitleView.setGravity(Gravity.CENTER_HORIZONTAL); + subtitleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + subtitleView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); + addView(subtitleView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL | Gravity.TOP, 30, 117, 30, 0)); + + closeImageView = new ImageView(context); + closeImageView.setScaleType(ImageView.ScaleType.CENTER); + closeImageView.setImageResource(R.drawable.msg_close); + closeImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText5), PorterDuff.Mode.MULTIPLY)); + closeImageView.setOnClickListener(e -> dismiss()); + addView(closeImageView, LayoutHelper.createFrame(48, 48, Gravity.RIGHT | Gravity.TOP, 0, -4, 2, 0)); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure( + MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(dp(171), MeasureSpec.EXACTLY) + ); + } + } + + private void createLink() { + ArrayList peers = new ArrayList<>(); + for (int i = 0; i < filter.alwaysShow.size(); ++i) { + long did = filter.alwaysShow.get(i); + if (did < 0) { + TLRPC.Chat chat = getBaseFragment().getMessagesController().getChat(-did); + if (canAddToFolder(chat)) { + peers.add(getBaseFragment().getMessagesController().getInputPeer(did)); + } + } + } + + if (peers.isEmpty()) { + dismiss(); + getBaseFragment().presentFragment(new FilterChatlistActivity(filter, null)); + } else { + TLRPC.TL_chatlists_exportChatlistInvite req = new TLRPC.TL_chatlists_exportChatlistInvite(); + req.chatlist = new TLRPC.TL_inputChatlistDialogFilter(); + req.chatlist.filter_id = filter.id; + req.peers = peers; + req.title = ""; + getBaseFragment().getConnectionsManager().sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + if ( + processErrors(err, getBaseFragment(), BulletinFactory.of(bulletinContainer, null)) && + res instanceof TLRPC.TL_chatlists_exportedChatlistInvite + ) { + FilterCreateActivity.hideNew(0); + dismiss(); + + getBaseFragment().getMessagesController().loadRemoteFilters(true); + TLRPC.TL_chatlists_exportedChatlistInvite inv = (TLRPC.TL_chatlists_exportedChatlistInvite) res; + getBaseFragment().presentFragment(new FilterChatlistActivity(filter, inv.invite)); + } + })); + } + } + + @Override + public void onViewCreated(FrameLayout containerView) { + super.onViewCreated(containerView); + recyclerListView.setOverScrollMode(View.OVER_SCROLL_NEVER); + recyclerListView.setOnItemClickListener((view, position) -> { + position--; + if (position < 0 || position >= items.size()) { + return; + } + ItemInner item = items.get(position); + if (item.viewType == VIEW_TYPE_LINK) { + dismiss(); + getBaseFragment().presentFragment(new FilterChatlistActivity(filter, item.link)); + } else if (item.viewType == VIEW_TYPE_CREATE_LINK) { + createLink(); + } + }); + + DefaultItemAnimator itemAnimator = new DefaultItemAnimator(); + itemAnimator.setSupportsChangeAnimations(false); + itemAnimator.setDelayAnimations(false); + itemAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + itemAnimator.setDurations(350); + recyclerListView.setItemAnimator(itemAnimator); + } + } + + public static boolean processErrors(TLRPC.TL_error err, BaseFragment fragment, BulletinFactory factory) { + if (err == null || TextUtils.isEmpty(err.text)) { + return true; + } + if ("INVITE_PEERS_TOO_MUCH".equals(err.text)) { + new LimitReachedBottomSheet(fragment, fragment.getContext(), LimitReachedBottomSheet.TYPE_CHATS_IN_FOLDER, fragment.getCurrentAccount()).show(); + } else if ("PEERS_LIST_EMPTY".equals(err.text)) { + factory.createErrorBulletin(LocaleController.getString("FolderLinkNoChatsError", R.string.FolderLinkNoChatsError)).show(); + } else if ("USER_CHANNELS_TOO_MUCH".equals(err.text)) { + factory.createErrorBulletin(LocaleController.getString("FolderLinkOtherAdminLimitError", R.string.FolderLinkOtherAdminLimitError)).show(); + } else if ("CHANNELS_TOO_MUCH".equals(err.text)) { + new LimitReachedBottomSheet(fragment, fragment.getContext(), LimitReachedBottomSheet.TYPE_TO0_MANY_COMMUNITIES, fragment.getCurrentAccount()).show(); + } else if ("INVITES_TOO_MUCH".equals(err.text)) { + new LimitReachedBottomSheet(fragment, fragment.getContext(), LimitReachedBottomSheet.TYPE_FOLDER_INVITES, fragment.getCurrentAccount()).show(); + } else if ("CHATLISTS_TOO_MUCH".equals(err.text)) { + new LimitReachedBottomSheet(fragment, fragment.getContext(), LimitReachedBottomSheet.TYPE_SHARED_FOLDERS, fragment.getCurrentAccount()).show(); + } else if ("INVITE_SLUG_EXPIRED".equals(err.text)) { + factory.createErrorBulletin(LocaleController.getString("NoFolderFound", R.string.NoFolderFound)).show(); + } else if ("FILTER_INCLUDE_TOO_MUCH".equals(err.text)) { + new LimitReachedBottomSheet(fragment, fragment.getContext(), LimitReachedBottomSheet.TYPE_CHATS_IN_FOLDER, fragment.getCurrentAccount()).show(); + } else if ("DIALOG_FILTERS_TOO_MUCH".equals(err.text)) { + new LimitReachedBottomSheet(fragment, fragment.getContext(), LimitReachedBottomSheet.TYPE_FOLDERS, fragment.getCurrentAccount()).show(); + } else { + factory.createErrorBulletin(LocaleController.getString("UnknownError", R.string.UnknownError)).show(); + } + return true; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/FilteredSearchView.java b/TMessagesProj/src/main/java/org/telegram/ui/FilteredSearchView.java index e14ad13cb4..bd8c03d46a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/FilteredSearchView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/FilteredSearchView.java @@ -12,6 +12,7 @@ import android.graphics.drawable.Drawable; import android.os.Build; import android.text.SpannableStringBuilder; +import android.text.TextPaint; import android.text.TextUtils; import android.util.SparseArray; import android.view.Gravity; @@ -27,6 +28,7 @@ import org.telegram.messenger.AccountInstance; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.ChatObject; import org.telegram.messenger.ContactsController; @@ -42,6 +44,7 @@ import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; import org.telegram.messenger.UserConfig; +import org.telegram.messenger.UserObject; import org.telegram.messenger.browser.Browser; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLObject; @@ -118,7 +121,7 @@ public class FilteredSearchView extends FrameLayout implements NotificationCente private String currentDataQuery; - private static SpannableStringBuilder arrowSpan; + private static SpannableStringBuilder[] arrowSpan = new SpannableStringBuilder[3]; private int photoViewerClassGuid; @@ -248,7 +251,7 @@ public PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageObj @Override public CharSequence getTitleFor(int i) { - return createFromInfoString(messages.get(i)); + return createFromInfoString(messages.get(i), 0); } @Override @@ -262,7 +265,7 @@ public CharSequence getSubtitleFor(int i) { public final LinearLayoutManager layoutManager; private final FlickerLoadingView loadingView; private boolean firstLoading = true; - private int animationIndex = -1; + private AnimationNotificationsLocker notificationsLocker = new AnimationNotificationsLocker(); public int keyboardHeight; private final ChatActionCell floatingDateView; @@ -424,21 +427,52 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { emptyView.setVisibility(View.GONE); } - public static CharSequence createFromInfoString(MessageObject messageObject) { - if (arrowSpan == null) { - arrowSpan = new SpannableStringBuilder("-"); - arrowSpan.setSpan(new ColoredImageSpan(ContextCompat.getDrawable(ApplicationLoader.applicationContext, R.drawable.search_arrow).mutate()), 0, 1, 0); + public static CharSequence createFromInfoString(MessageObject messageObject, int arrowType) { + return createFromInfoString(messageObject, true, arrowType); + } + + public static CharSequence createFromInfoString(MessageObject messageObject, boolean includeChat, int arrowType) { + return createFromInfoString(messageObject, includeChat, arrowType, null); + } + + public static CharSequence createFromInfoString(MessageObject messageObject, boolean includeChat, int arrowType, TextPaint textPaint) { + if (messageObject == null) { + return ""; + } + if (arrowSpan[arrowType] == null) { + arrowSpan[arrowType] = new SpannableStringBuilder(">"); + int resId; + if (arrowType == 0) { + resId = R.drawable.attach_arrow_right; + } else if (arrowType == 1) { + resId = R.drawable.msg_mini_arrow_mediathin; + } else if (arrowType == 2) { + resId = R.drawable.msg_mini_arrow_mediabold; + } else { + return ""; + } + Drawable arrowDrawable = ContextCompat.getDrawable(ApplicationLoader.applicationContext, resId).mutate(); + ColoredImageSpan span = new ColoredImageSpan(arrowDrawable, arrowType == 0 ? ColoredImageSpan.ALIGN_CENTER : ColoredImageSpan.ALIGN_BASELINE); +// arrowDrawable.setBounds(0, 0, AndroidUtilities.dp(13), AndroidUtilities.dp(13)); + if (arrowType == 1 || arrowType == 2) { + span.setScale(.85f); + } + arrowSpan[arrowType].setSpan(span, 0, arrowSpan[arrowType].length(), 0); } CharSequence fromName = null; TLRPC.User user = messageObject.messageOwner.from_id.user_id != 0 ? MessagesController.getInstance(UserConfig.selectedAccount).getUser(messageObject.messageOwner.from_id.user_id) : null; - TLRPC.Chat chatFrom = messageObject.messageOwner.from_id.chat_id != 0 ? MessagesController.getInstance(UserConfig.selectedAccount).getChat(messageObject.messageOwner.peer_id.chat_id) : null; + TLRPC.Chat chatFrom = null, chatTo = null; + chatFrom = messageObject.messageOwner.from_id.chat_id != 0 ? MessagesController.getInstance(UserConfig.selectedAccount).getChat(messageObject.messageOwner.peer_id.chat_id) : null; if (chatFrom == null) { chatFrom = messageObject.messageOwner.from_id.channel_id != 0 ? MessagesController.getInstance(UserConfig.selectedAccount).getChat(messageObject.messageOwner.peer_id.channel_id) : null; } - TLRPC.Chat chatTo = messageObject.messageOwner.peer_id.channel_id != 0 ? MessagesController.getInstance(UserConfig.selectedAccount).getChat(messageObject.messageOwner.peer_id.channel_id) : null; + chatTo = messageObject.messageOwner.peer_id.channel_id != 0 ? MessagesController.getInstance(UserConfig.selectedAccount).getChat(messageObject.messageOwner.peer_id.channel_id) : null; if (chatTo == null) { chatTo = messageObject.messageOwner.peer_id.chat_id != 0 ? MessagesController.getInstance(UserConfig.selectedAccount).getChat(messageObject.messageOwner.peer_id.chat_id) : null; } + if (!ChatObject.isChannelAndNotMegaGroup(chatTo) && !includeChat) { + chatTo = null; + } if (user != null && chatTo != null) { CharSequence chatTitle = chatTo.title; if (ChatObject.isForum(chatTo)) { @@ -447,15 +481,17 @@ public static CharSequence createFromInfoString(MessageObject messageObject) { chatTitle = ForumUtilities.getTopicSpannedName(topic, null); } } - chatTitle = Emoji.replaceEmoji(chatTitle, null, AndroidUtilities.dp(12), false); + chatTitle = Emoji.replaceEmoji(chatTitle, textPaint == null ? null : textPaint.getFontMetricsInt(), false); SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(); spannableStringBuilder - .append(ContactsController.formatName(user.first_name, user.last_name)) - .append(' ').append(arrowSpan).append(' ') + .append(Emoji.replaceEmoji(UserObject.getFirstName(user), textPaint == null ? null : textPaint.getFontMetricsInt(), false)) + .append(' ') + .append(arrowSpan[arrowType]) + .append(' ') .append(chatTitle); fromName = spannableStringBuilder; } else if (user != null) { - fromName = ContactsController.formatName(user.first_name, user.last_name); + fromName = Emoji.replaceEmoji(UserObject.getUserName(user), textPaint == null ? null : textPaint.getFontMetricsInt(), false); } else if (chatFrom != null) { CharSequence chatTitle = chatFrom.title; if (ChatObject.isForum(chatFrom)) { @@ -464,7 +500,7 @@ public static CharSequence createFromInfoString(MessageObject messageObject) { chatTitle = ForumUtilities.getTopicSpannedName(topic, null); } } - chatTitle = Emoji.replaceEmoji(chatTitle, null, AndroidUtilities.dp(12), false); + chatTitle = Emoji.replaceEmoji(chatTitle, textPaint == null ? null : textPaint.getFontMetricsInt(), false); fromName = chatTitle; } return fromName == null ? "" : fromName; @@ -783,10 +819,10 @@ public boolean onPreDraw() { animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - NotificationCenter.getInstance(currentAccount).onAnimationFinish(animationIndex); + notificationsLocker.unlock(); } }); - animationIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(animationIndex, null); + notificationsLocker.lock(); animatorSet.start(); if (finalProgressView != null && finalProgressView.getParent() == null) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/FiltersSetupActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/FiltersSetupActivity.java index 2845a93f6c..5c27b9b68f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/FiltersSetupActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/FiltersSetupActivity.java @@ -3,13 +3,10 @@ import android.annotation.SuppressLint; import android.content.Context; import android.content.DialogInterface; -import android.content.SharedPreferences; import android.graphics.Canvas; -import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.drawable.Drawable; -import android.text.TextPaint; import android.text.TextUtils; import android.util.TypedValue; import android.view.Gravity; @@ -22,6 +19,7 @@ import android.widget.ImageView; import android.widget.TextView; +import androidx.annotation.NonNull; import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.ItemTouchHelper; @@ -51,12 +49,17 @@ import org.telegram.ui.Components.CombinedDrawable; import org.telegram.ui.Components.EmojiTextView; import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.FolderBottomSheet; +import org.telegram.ui.Components.ItemOptions; import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.ListView.AdapterWithDiffUtils; +import org.telegram.ui.Components.LoadingDrawable; import org.telegram.ui.Components.Premium.LimitReachedBottomSheet; import org.telegram.ui.Components.Premium.PremiumFeatureBottomSheet; import org.telegram.ui.Components.ProgressButton; import org.telegram.ui.Components.RLottieImageView; import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.Components.UndoView; import java.util.ArrayList; @@ -69,21 +72,9 @@ public class FiltersSetupActivity extends BaseFragment implements NotificationCe private RecyclerListView listView; private ListAdapter adapter; private ItemTouchHelper itemTouchHelper; + private UndoView undoView; private boolean orderChanged; - private boolean showAllChats; - - private int filterHelpRow; - private int recommendedHeaderRow; - private int recommendedStartRow; - private int recommendedEndRow; - private int recommendedSectionRow; - private int filtersHeaderRow; - private int filtersStartRow; - private int filtersEndRow; - private int createFilterRow; - private int createSectionRow; - private int rowCount = 0; private boolean ignoreUpdates; @@ -227,11 +218,11 @@ public static class HintInnerCell extends FrameLayout { private RLottieImageView imageView; private TextView messageTextView; - public HintInnerCell(Context context) { + public HintInnerCell(Context context, int resId, CharSequence text) { super(context); imageView = new RLottieImageView(context); - imageView.setAnimation(R.raw.filters, 90, 90); + imageView.setAnimation(resId, 90, 90); imageView.setScaleType(ImageView.ScaleType.CENTER); imageView.playAnimation(); imageView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); @@ -247,7 +238,7 @@ public HintInnerCell(Context context) { messageTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText4)); messageTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); messageTextView.setGravity(Gravity.CENTER); - messageTextView.setText(AndroidUtilities.replaceTags(LocaleController.formatString("CreateNewFilterInfo", R.string.CreateNewFilterInfo))); + messageTextView.setText(text); addView(messageTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 40, 121, 40, 24)); } @@ -257,14 +248,17 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } } - public static class FilterCell extends FrameLayout { + public class FilterCell extends FrameLayout { - private SimpleTextView textView; - private TextView valueTextView; + private final SimpleTextView textView; + private final TextView valueTextView; @SuppressWarnings("FieldCanBeLocal") - private ImageView moveImageView; + private final ImageView moveImageView; @SuppressWarnings("FieldCanBeLocal") - private ImageView optionsImageView; + private final ImageView optionsImageView; + private final ImageView shareImageView; + private boolean shareLoading = false; + private final LoadingDrawable shareLoadingDrawable; private boolean needDivider; float progressToLock; @@ -305,11 +299,64 @@ public FilterCell(Context context) { addView(valueTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 80 : 64, 35, LocaleController.isRTL ? 64 : 80, 0)); valueTextView.setVisibility(GONE); + shareLoadingDrawable = new LoadingDrawable(); + shareLoadingDrawable.setAppearByGradient(true); + shareLoadingDrawable.setGradientScale(2f); + int selector = Theme.getColor(Theme.key_listSelector); + shareLoadingDrawable.setColors( + Theme.multAlpha(selector, 0.4f), + Theme.multAlpha(selector, 1), + Theme.multAlpha(selector, 0.9f), + Theme.multAlpha(selector, 1.7f) + ); + int stroke = AndroidUtilities.dp(1); + shareLoadingDrawable.strokePaint.setStrokeWidth(stroke); + shareLoadingDrawable.setRadiiDp(40); + shareImageView = new ImageView(context) { + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + if (shareLoading) { + shareLoadingDrawable.setBounds(stroke / 2, stroke / 2, getWidth() - stroke / 2, getHeight() - stroke / 2); + shareLoadingDrawable.draw(canvas); + } + } + + @Override + protected boolean verifyDrawable(@NonNull Drawable dr) { + return dr == shareLoadingDrawable || super.verifyDrawable(dr); + } + }; + shareLoadingDrawable.setCallback(shareImageView); + shareImageView.setFocusable(false); + shareImageView.setScaleType(ImageView.ScaleType.CENTER); + shareImageView.setBackground(Theme.createSelectorDrawable(selector)); + shareImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_stickers_menu), PorterDuff.Mode.MULTIPLY)); + shareImageView.setContentDescription(LocaleController.getString("FilterShare", R.string.FilterShare)); + shareImageView.setVisibility(View.GONE); + shareImageView.setImageResource(R.drawable.msg_link_folder); + shareImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_stickers_menu), PorterDuff.Mode.MULTIPLY)); + addView(shareImageView, LayoutHelper.createFrame(40, 40, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.CENTER_VERTICAL, LocaleController.isRTL ? 52 : 6, 0, LocaleController.isRTL ? 6 : 52, 0)); + shareImageView.setOnClickListener(e -> { + if (shareLoading && !shareLoadingDrawable.isDisappeared() || currentFilter == null) { + return; + } + shareLoading = true; + shareLoadingDrawable.reset(); + shareLoadingDrawable.resetDisappear(); + shareImageView.invalidate(); + FilterCreateActivity.FilterInvitesBottomSheet.show(FiltersSetupActivity.this, currentFilter, () -> { + shareLoadingDrawable.disappear(); + shareImageView.invalidate(); + updateRows(true); + }); + }); + optionsImageView = new ImageView(context); optionsImageView.setFocusable(false); optionsImageView.setScaleType(ImageView.ScaleType.CENTER); - optionsImageView.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.getColor(Theme.key_stickers_menuSelector))); - optionsImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_stickers_menu), PorterDuff.Mode.SRC_IN)); + optionsImageView.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.getColor(Theme.key_listSelector))); + optionsImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_stickers_menu), PorterDuff.Mode.MULTIPLY)); optionsImageView.setImageResource(R.drawable.msg_actions); optionsImageView.setContentDescription(LocaleController.getString("AccDescrMoreOptions", R.string.AccDescrMoreOptions)); addView(optionsImageView, LayoutHelper.createFrame(40, 40, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.CENTER_VERTICAL, 6, 0, 6, 0)); @@ -326,6 +373,8 @@ public void setFilter(MessagesController.DialogFilter filter, boolean divider) { int newId = currentFilter == null ? -1 : currentFilter.id; boolean animated = oldId != newId; + shareImageView.setVisibility(filter.isChatlist() ? VISIBLE : GONE); + StringBuilder info = new StringBuilder(); if (filter.isDefault() || (filter.flags & MessagesController.DIALOG_FILTER_FLAG_ALL_CHATS) == MessagesController.DIALOG_FILTER_FLAG_ALL_CHATS) { info.append(LocaleController.getString("FilterAllChats", R.string.FilterAllChats)); @@ -426,7 +475,7 @@ public void setOnReorderButtonTouchListener(OnTouchListener listener) { @Override public boolean onFragmentCreate() { - updateRows(true); + updateRows(false); getMessagesController().loadRemoteFilters(true); getNotificationCenter().addObserver(this, NotificationCenter.dialogFiltersUpdated); getNotificationCenter().addObserver(this, NotificationCenter.suggestedFiltersLoaded); @@ -436,44 +485,49 @@ public boolean onFragmentCreate() { return super.onFragmentCreate(); } - private void updateRows(boolean notify) { - recommendedHeaderRow = -1; - recommendedStartRow = -1; - recommendedEndRow = -1; - recommendedSectionRow = -1; + private ArrayList oldItems = new ArrayList<>(); + private ArrayList items = new ArrayList<>(); + + private int filtersStartPosition; + private int filtersSectionStart = -1, filtersSectionEnd = -1; + + private void updateRows(boolean animated) { + oldItems.clear(); + oldItems.addAll(items); + items.clear(); ArrayList suggestedFilters = getMessagesController().suggestedFilters; - rowCount = 0; - filterHelpRow = rowCount++; - int count = getMessagesController().dialogFilters.size(); - showAllChats = true; - if (!suggestedFilters.isEmpty() && count < 10) { - recommendedHeaderRow = rowCount++; - recommendedStartRow = rowCount; - rowCount += suggestedFilters.size(); - recommendedEndRow = rowCount; - recommendedSectionRow = rowCount++; + ArrayList dialogFilters = getMessagesController().getDialogFilters(); + items.add(ItemInner.asHint()); + if (!suggestedFilters.isEmpty() && dialogFilters.size() < 10) { + items.add(ItemInner.asHeader(LocaleController.getString("FilterRecommended", R.string.FilterRecommended))); + for (int i = 0; i < suggestedFilters.size(); ++i) { + items.add(ItemInner.asSuggested(suggestedFilters.get(i))); + } + items.add(ItemInner.asShadow(null)); } - - if (count != 0) { - filtersHeaderRow = rowCount++; - filtersStartRow = rowCount; - rowCount += count; - filtersEndRow = rowCount; + if (!dialogFilters.isEmpty()) { + filtersSectionStart = items.size(); + items.add(ItemInner.asHeader(LocaleController.getString("Filters", R.string.Filters))); + filtersStartPosition = items.size(); + for (int i = 0; i < dialogFilters.size(); ++i) { + items.add(ItemInner.asFilter(dialogFilters.get(i))); + } + filtersSectionEnd = items.size(); } else { - filtersHeaderRow = -1; - filtersStartRow = -1; - filtersEndRow = -1; + filtersSectionStart = filtersSectionEnd = -1; } - if (count < getMessagesController().dialogFiltersLimitPremium) { - createFilterRow = rowCount++; - } else { - createFilterRow = -1; + if (dialogFilters.size() < getMessagesController().dialogFiltersLimitPremium) { + items.add(ItemInner.asButton(LocaleController.getString("CreateNewFilter", R.string.CreateNewFilter))); } + items.add(ItemInner.asShadow(null)); - createSectionRow = rowCount++; - if (notify && adapter != null) { - adapter.notifyDataSetChanged(); + if (adapter != null) { + if (animated) { + adapter.setItems(oldItems, items); + } else { + adapter.notifyDataSetChanged(); + } } } @@ -485,7 +539,7 @@ public void onFragmentDestroy() { getNotificationCenter().postNotificationName(NotificationCenter.dialogFiltersUpdated); getMessagesStorage().saveDialogFiltersOrder(); TLRPC.TL_messages_updateDialogFiltersOrder req = new TLRPC.TL_messages_updateDialogFiltersOrder(); - ArrayList filters = getMessagesController().dialogFilters; + ArrayList filters = getMessagesController().getDialogFilters(); for (int a = 0, N = filters.size(); a < N; a++) { MessagesController.DialogFilter filter = filters.get(a); req.order.add(filter.id); @@ -515,7 +569,6 @@ public void onItemClick(int id) { FrameLayout frameLayout = (FrameLayout) fragmentView; frameLayout.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundGray)); - LinearLayoutManager layoutManager; listView = new RecyclerListView(context) { @Override public boolean onTouchEvent(MotionEvent e) { @@ -529,7 +582,7 @@ public boolean onTouchEvent(MotionEvent e) { @Override protected void dispatchDraw(Canvas canvas) { - drawSectionBackground(canvas, filtersStartRow, filtersEndRow, getThemedColor(Theme.key_windowBackgroundWhite)); + drawSectionBackground(canvas, filtersSectionStart, filtersSectionEnd, Theme.getColor(Theme.key_windowBackgroundWhite)); super.dispatchDraw(canvas); } }; @@ -540,29 +593,36 @@ protected void dispatchDraw(Canvas canvas) { itemAnimator.setSupportsChangeAnimations(false); listView.setItemAnimator(itemAnimator); ((DefaultItemAnimator) listView.getItemAnimator()).setDelayAnimations(false); - listView.setLayoutManager(layoutManager = new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)); + listView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)); listView.setVerticalScrollBarEnabled(false); itemTouchHelper = new ItemTouchHelper(new TouchHelperCallback()); itemTouchHelper.attachToRecyclerView(listView); frameLayout.addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); listView.setAdapter(adapter = new ListAdapter(context)); listView.setOnItemClickListener((view, position, x, y) -> { - if (position >= filtersStartRow && position < filtersEndRow) { - int filterPosition = position - filtersStartRow; - if (!showAllChats) { - filterPosition++; - } - if (getMessagesController().dialogFilters.get(filterPosition).isDefault()) { + if (position < 0 || position >= items.size()) { + return; + } + ItemInner item = items.get(position); + if (item == null) { + return; + } + if (item.viewType == VIEW_TYPE_FILTER) { + MessagesController.DialogFilter filter = item.filter; + if (filter == null || filter.isDefault()) { return; } - MessagesController.DialogFilter filter = getMessagesController().dialogFilters.get(filterPosition); if (filter.locked) { showDialog(new LimitReachedBottomSheet(this, context, LimitReachedBottomSheet.TYPE_FOLDERS, currentAccount)); } else { - presentFragment(new FilterCreateActivity(getMessagesController().dialogFilters.get(filterPosition))); + presentFragment(new FilterCreateActivity(filter)); } - } else if (position == createFilterRow) { - if ((getMessagesController().dialogFilters.size() - 1 >= getMessagesController().dialogFiltersLimitDefault && !getUserConfig().isPremium()) || getMessagesController().dialogFilters.size() >= getMessagesController().dialogFiltersLimitPremium) { + } else if (item.viewType == VIEW_TYPE_BUTTON) { + final int count = getMessagesController().getDialogFilters().size(); + if ( + count - 1 >= getMessagesController().dialogFiltersLimitDefault && !getUserConfig().isPremium() || + count >= getMessagesController().dialogFiltersLimitPremium + ) { showDialog(new LimitReachedBottomSheet(this, context, LimitReachedBottomSheet.TYPE_FOLDERS, currentAccount)); } else { presentFragment(new FilterCreateActivity()); @@ -573,6 +633,16 @@ protected void dispatchDraw(Canvas canvas) { return fragmentView; } + public UndoView getUndoView() { + if (getContext() == null) { + return null; + } + if (undoView == null) { + ((FrameLayout) fragmentView).addView(undoView = new UndoView(getContext()), LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM | Gravity.LEFT, 8, 0, 8, 8)); + } + return undoView; + } + @Override public void onResume() { super.onResume(); @@ -587,19 +657,95 @@ public void didReceivedNotification(int id, int account, Object... args) { if (ignoreUpdates) { return; } - int rowCount = this.rowCount; - updateRows(false); - if (rowCount != this.rowCount) { - adapter.notifyDataSetChanged(); - } else { - adapter.notifyItemRangeChanged(0, rowCount); - } + updateRows(true); } else if (id == NotificationCenter.suggestedFiltersLoaded) { updateRows(true); } } - private class ListAdapter extends RecyclerListView.SelectionAdapter { + private static final int VIEW_TYPE_HEADER = 0; + private static final int VIEW_TYPE_HINT = 1; + private static final int VIEW_TYPE_FILTER = 2; + private static final int VIEW_TYPE_SHADOW = 3; + private static final int VIEW_TYPE_BUTTON = 4; + private static final int VIEW_TYPE_FILTER_SUGGESTION = 5; + + private static class ItemInner extends AdapterWithDiffUtils.Item { + public ItemInner(int viewType) { + super(viewType, false); + } + + CharSequence text; + MessagesController.DialogFilter filter; + TLRPC.TL_dialogFilterSuggested suggested; + + public static ItemInner asHeader(CharSequence text) { + ItemInner i = new ItemInner(VIEW_TYPE_HEADER); + i.text = text; + return i; + } + public static ItemInner asHint() { + return new ItemInner(VIEW_TYPE_HINT); + } + public static ItemInner asShadow(CharSequence text) { + ItemInner i = new ItemInner(VIEW_TYPE_SHADOW); + i.text = text; + return i; + } + public static ItemInner asFilter(MessagesController.DialogFilter filter) { + ItemInner i = new ItemInner(VIEW_TYPE_FILTER); + i.filter = filter; + return i; + } + public static ItemInner asButton(CharSequence text) { + ItemInner i = new ItemInner(VIEW_TYPE_BUTTON); + i.text = text; + return i; + } + public static ItemInner asSuggested(TLRPC.TL_dialogFilterSuggested suggested) { + ItemInner i = new ItemInner(VIEW_TYPE_FILTER_SUGGESTION); + i.suggested = suggested; + return i; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof ItemInner)) { + return false; + } + ItemInner other = (ItemInner) obj; + if (other.viewType != viewType) { + return false; + } + if (viewType == VIEW_TYPE_HEADER || viewType == VIEW_TYPE_BUTTON || viewType == VIEW_TYPE_SHADOW) { + if (!TextUtils.equals(text, other.text)) { + return false; + } + } + if (viewType == VIEW_TYPE_FILTER) { + if ((filter == null) != (other.filter == null)) { + return false; + } + if (filter != null && filter.id != other.filter.id) { + return false; + } + } + if (viewType == VIEW_TYPE_FILTER_SUGGESTION) { + if ((suggested == null) != (other.suggested == null)) { + return false; + } + if (suggested != null && suggested.filter.id != other.suggested.filter.id) { + return false; + } + } + return true; + } + } + + private class ListAdapter extends AdapterWithDiffUtils { private Context mContext; @@ -610,27 +756,27 @@ public ListAdapter(Context context) { @Override public boolean isEnabled(RecyclerView.ViewHolder holder) { int type = holder.getItemViewType(); - return type != 3 && type != 0 && type != 5 && type != 1; + return type != VIEW_TYPE_SHADOW && type != VIEW_TYPE_HEADER && type != VIEW_TYPE_FILTER_SUGGESTION && type != VIEW_TYPE_HINT; } @Override public int getItemCount() { - return rowCount; + return items.size(); } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view; switch (viewType) { - case 0: + case VIEW_TYPE_HEADER: view = new HeaderCell(mContext); view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); break; - case 1: - view = new HintInnerCell(mContext); - view.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_top, Theme.key_windowBackgroundGrayShadow)); + case VIEW_TYPE_HINT: + view = new HintInnerCell(mContext, R.raw.filters, AndroidUtilities.replaceTags(LocaleController.formatString("CreateNewFilterInfo", R.string.CreateNewFilterInfo))); + view.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_top, Theme.key_windowBackgroundGrayShadow)); break; - case 2: + case VIEW_TYPE_FILTER: FilterCell filterCell = new FilterCell(mContext); filterCell.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); filterCell.setOnReorderButtonTouchListener((v, event) -> { @@ -642,100 +788,70 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType filterCell.setOnOptionsClick(v -> { FilterCell cell = (FilterCell) v.getParent(); MessagesController.DialogFilter filter = cell.getCurrentFilter(); - AlertDialog.Builder builder1 = new AlertDialog.Builder(getParentActivity()); - TextPaint paint = new TextPaint(Paint.ANTI_ALIAS_FLAG); - paint.setTextSize(AndroidUtilities.dp(20)); - builder1.setTitle(Emoji.replaceEmoji(filter.name, paint.getFontMetricsInt(), AndroidUtilities.dp(20), false)); - final CharSequence[] items = new CharSequence[]{ - LocaleController.getString("FilterEditItem", R.string.FilterEditItem), - LocaleController.getString("FilterDeleteItem", R.string.FilterDeleteItem), - }; - final int[] icons = new int[]{ - R.drawable.msg_edit, - R.drawable.msg_delete - }; - builder1.setItems(items, icons, (dialog, which) -> { - if (which == 0) { - if (filter.locked) { - showDialog(new LimitReachedBottomSheet(FiltersSetupActivity.this, mContext, LimitReachedBottomSheet.TYPE_FOLDERS, currentAccount)); - } else { - presentFragment(new FilterCreateActivity(filter)); - } - } else if (which == 1) { - AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); - builder.setTitle(LocaleController.getString("FilterDelete", R.string.FilterDelete)); - builder.setMessage(LocaleController.getString("FilterDeleteAlert", R.string.FilterDeleteAlert)); - builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - builder.setPositiveButton(LocaleController.getString("Delete", R.string.Delete), (dialog2, which2) -> { - AlertDialog progressDialog = null; - if (getParentActivity() != null) { - progressDialog = new AlertDialog(getParentActivity(), AlertDialog.ALERT_TYPE_SPINNER); - progressDialog.setCanCancel(false); - progressDialog.show(); - } - final AlertDialog progressDialogFinal = progressDialog; - TLRPC.TL_messages_updateDialogFilter req = new TLRPC.TL_messages_updateDialogFilter(); - req.id = filter.id; - getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { - try { - if (progressDialogFinal != null) { - progressDialogFinal.dismiss(); - } - } catch (Exception e) { - FileLog.e(e); - } - int idx = getMessagesController().dialogFilters.indexOf(filter); - if (idx >= 0) { - idx += filtersStartRow; - } - if (!showAllChats) { - idx--; - } - ignoreUpdates = true; - getMessagesController().removeFilter(filter); - getMessagesStorage().deleteDialogFilter(filter); - ignoreUpdates = false; - - int prevAddRow = createFilterRow; - int prevRecommendedHeaderRow = recommendedHeaderRow; - updateRows(idx == -1); - if (idx != -1) { - if (filtersStartRow == -1) { - adapter.notifyItemRangeRemoved(idx - 1, 2); - } else { - adapter.notifyItemRemoved(idx); - } - if (prevRecommendedHeaderRow == -1 && recommendedHeaderRow != -1) { - adapter.notifyItemRangeInserted(prevRecommendedHeaderRow, recommendedSectionRow - recommendedHeaderRow + 1); - } - if (prevAddRow == -1 && createFilterRow != -1) { - adapter.notifyItemInserted(createFilterRow); - } - } - })); + ItemOptions options = ItemOptions.makeOptions(FiltersSetupActivity.this, cell); + options.add(R.drawable.msg_edit, LocaleController.getString("FilterEditItem", R.string.FilterEditItem), () -> { + if (filter.locked) { + showDialog(new LimitReachedBottomSheet(FiltersSetupActivity.this, mContext, LimitReachedBottomSheet.TYPE_FOLDERS, currentAccount)); + } else { + presentFragment(new FilterCreateActivity(filter)); + } + }); + options.add(R.drawable.msg_delete, LocaleController.getString("FilterDeleteItem", R.string.FilterDeleteItem), true, () -> { + if (filter.isChatlist()) { + FolderBottomSheet.showForDeletion(FiltersSetupActivity.this, filter.id, success -> { + updateRows(true); }); - AlertDialog alertDialog = builder.create(); - showDialog(alertDialog); - TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); - if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + return; + } + + AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); + builder.setTitle(LocaleController.getString("FilterDelete", R.string.FilterDelete)); + builder.setMessage(LocaleController.getString("FilterDeleteAlert", R.string.FilterDeleteAlert)); + builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + builder.setPositiveButton(LocaleController.getString("Delete", R.string.Delete), (dialog2, which2) -> { + AlertDialog progressDialog = null; + if (getParentActivity() != null) { + progressDialog = new AlertDialog(getParentActivity(), AlertDialog.ALERT_TYPE_SPINNER); + progressDialog.setCanCancel(false); + progressDialog.show(); } + final AlertDialog progressDialogFinal = progressDialog; + TLRPC.TL_messages_updateDialogFilter req = new TLRPC.TL_messages_updateDialogFilter(); + req.id = filter.id; + getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + try { + if (progressDialogFinal != null) { + progressDialogFinal.dismiss(); + } + } catch (Exception e) { + FileLog.e(e); + } + getMessagesController().removeFilter(filter); + getMessagesStorage().deleteDialogFilter(filter); + })); + }); + AlertDialog alertDialog = builder.create(); + showDialog(alertDialog); + TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); + if (button != null) { + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } }); - final AlertDialog dialog = builder1.create(); - showDialog(dialog); - dialog.setItemColor(items.length - 1, Theme.getColor(Theme.key_dialogTextRed), Theme.getColor(Theme.key_dialogRedIcon)); + if (LocaleController.isRTL) { + options.setGravity(Gravity.LEFT); + } + options.show(); }); view = filterCell; break; - case 3: + case VIEW_TYPE_SHADOW: view = new ShadowSectionCell(mContext); break; - case 4: + case VIEW_TYPE_BUTTON: view = new TextCell(mContext); view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); break; - case 5: + case VIEW_TYPE_FILTER_SUGGESTION: default: SuggestedFilterCell suggestedFilterCell = new SuggestedFilterCell(mContext); suggestedFilterCell.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); @@ -747,7 +863,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType while (getMessagesController().dialogFiltersById.get(filter.id) != null) { filter.id++; } - filter.order = getMessagesController().dialogFilters.size(); + filter.order = getMessagesController().getDialogFilters().size(); filter.pendingUnreadCount = filter.unreadCount = -1; for (int b = 0; b < 2; b++) { ArrayList fromArray = b == 0 ? suggested.filter.include_peers : suggested.filter.exclude_peers; @@ -791,46 +907,9 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType } filter.emoticon = TextUtils.isEmpty(suggested.filter.emoticon) ? FolderIconHelper.getEmoticonFromFlags(filter.flags).second : suggested.filter.emoticon; ignoreUpdates = true; - FilterCreateActivity.saveFilterToServer(filter, filter.flags, filter.emoticon, filter.name, filter.alwaysShow, filter.neverShow, filter.pinnedDialogs, true, true, true, true, false, FiltersSetupActivity.this, () -> { + FilterCreateActivity.saveFilterToServer(filter, filter.flags, filter.emoticon, filter.name, filter.alwaysShow, filter.neverShow, filter.pinnedDialogs, true, true, true, true, true, FiltersSetupActivity.this, () -> { + getMessagesController().suggestedFilters.remove(suggested); getNotificationCenter().postNotificationName(NotificationCenter.dialogFiltersUpdated); - ignoreUpdates = false; - ArrayList suggestedFilters = getMessagesController().suggestedFilters; - int index = suggestedFilters.indexOf(suggested); - if (index != -1) { - boolean wasEmpty = filtersStartRow == -1; - suggestedFilters.remove(index); - index += recommendedStartRow; - int prevAddRow = createFilterRow; - int prevRecommendedHeaderRow = recommendedHeaderRow; - int prevRecommendedSectionRow = recommendedSectionRow; - updateRows(false); - if (prevAddRow != -1 && createFilterRow == -1) { - adapter.notifyItemRemoved(prevAddRow); - } - if (prevRecommendedHeaderRow != -1 && recommendedHeaderRow == -1) { - adapter.notifyItemRangeRemoved(prevRecommendedHeaderRow, prevRecommendedSectionRow - prevRecommendedHeaderRow + 1); - } else { - adapter.notifyItemRemoved(index); - } - if (wasEmpty) { - adapter.notifyItemInserted(filtersHeaderRow); - } - int indexToInsert = 0; - for (int i = 0; i < getMessagesController().dialogFilters.size(); i++) { - if (filter.id == getMessagesController().dialogFilters.get(i).id) { - indexToInsert = i; - } - } - if (!getUserConfig().isPremium()) { - indexToInsert--; - } - if (indexToInsert < 0) { - indexToInsert = 0; - } - adapter.notifyItemInserted(filtersStartRow + indexToInsert); - } else { - updateRows(true); - } }); }); view = suggestedFilterCell; @@ -841,50 +920,41 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + ItemInner item = items.get(position); + if (item == null) { + return; + } + boolean divider = position + 1 < items.size() && items.get(position + 1).viewType != VIEW_TYPE_SHADOW; switch (holder.getItemViewType()) { - case 0: { + case VIEW_TYPE_HEADER: { HeaderCell headerCell = (HeaderCell) holder.itemView; - if (position == filtersHeaderRow) { - headerCell.setText(LocaleController.getString("Filters", R.string.Filters)); - } else if (position == recommendedHeaderRow) { - headerCell.setText(LocaleController.getString("FilterRecommended", R.string.FilterRecommended)); - } + headerCell.setText(item.text); break; } - case 2: { + case VIEW_TYPE_FILTER: { FilterCell filterCell = (FilterCell) holder.itemView; - int filterPosition = position - filtersStartRow; - if (!showAllChats) { - filterPosition++; - } - filterCell.setFilter(getMessagesController().dialogFilters.get(filterPosition), true); + filterCell.setFilter(item.filter, divider); break; } - case 3: { - if (position == createSectionRow) { - holder.itemView.setBackground(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); - } else { - holder.itemView.setBackground(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); - } + case VIEW_TYPE_SHADOW: { + holder.itemView.setBackground(Theme.getThemedDrawableByKey(mContext, divider ? R.drawable.greydivider : R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); break; } - case 4: { + case VIEW_TYPE_BUTTON: { TextCell textCell = (TextCell) holder.itemView; - SharedPreferences preferences = MessagesController.getNotificationsSettings(currentAccount); - if (position == createFilterRow) { - Drawable drawable1 = mContext.getResources().getDrawable(R.drawable.poll_add_circle); - Drawable drawable2 = mContext.getResources().getDrawable(R.drawable.poll_add_plus); - drawable1.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_switchTrackChecked), PorterDuff.Mode.SRC_IN)); - drawable2.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_checkboxCheck), PorterDuff.Mode.SRC_IN)); - CombinedDrawable combinedDrawable = new CombinedDrawable(drawable1, drawable2); - - textCell.setTextAndIcon(LocaleController.getString("CreateNewFilter", R.string.CreateNewFilter), combinedDrawable, false); - } + + Drawable drawable1 = mContext.getResources().getDrawable(R.drawable.poll_add_circle); + Drawable drawable2 = mContext.getResources().getDrawable(R.drawable.poll_add_plus); + drawable1.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_switchTrackChecked), PorterDuff.Mode.MULTIPLY)); + drawable2.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_checkboxCheck), PorterDuff.Mode.MULTIPLY)); + CombinedDrawable combinedDrawable = new CombinedDrawable(drawable1, drawable2); + + textCell.setTextAndIcon(item.text + "", combinedDrawable, false); break; } - case 5: { + case VIEW_TYPE_FILTER_SUGGESTION: { SuggestedFilterCell filterCell = (SuggestedFilterCell) holder.itemView; - filterCell.setFilter(getMessagesController().suggestedFilters.get(position - recommendedStartRow), position < recommendedEndRow - 1); + filterCell.setFilter(item.suggested, divider); break; } } @@ -892,64 +962,48 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { @Override public int getItemViewType(int position) { - if (position == filtersHeaderRow || position == recommendedHeaderRow) { - return 0; - } else if (position == filterHelpRow) { - return 1; - } else if (position >= filtersStartRow && position < filtersEndRow) { - return 2; - } else if (position == createSectionRow || position == recommendedSectionRow) { - return 3; - } else if (position == createFilterRow) { - return 4; - } else { - return 5; + if (position < 0 || position >= items.size()) { + return VIEW_TYPE_SHADOW; + } + ItemInner item = items.get(position); + if (item == null) { + return VIEW_TYPE_SHADOW; } + return item.viewType; } - public void swapElements(int fromIndex, int toIndex) { - int idx1 = fromIndex - filtersStartRow; - int idx2 = toIndex - filtersStartRow; - int count = filtersEndRow - filtersStartRow; - if (!showAllChats) { - idx1++; - idx2++; - count++; + public void swapElements(int fromPosition, int toPosition) { + if (fromPosition < filtersStartPosition || toPosition < filtersStartPosition) { + return; } - - if (idx1 < 0 || idx2 < 0 || idx1 >= count || idx2 >= count) { + ItemInner from = items.get(fromPosition); + ItemInner to = items.get(toPosition); + if (from == null || to == null || from.filter == null || to.filter == null) { return; } + int temp = from.filter.order; + from.filter.order = to.filter.order; + to.filter.order = temp; ArrayList filters = getMessagesController().dialogFilters; - MessagesController.DialogFilter filter1 = filters.get(idx1); - MessagesController.DialogFilter filter2 = filters.get(idx2); - int temp = filter1.order; - filter1.order = filter2.order; - filter2.order = temp; - filters.set(idx1, filter2); - filters.set(idx2, filter1); + try { + filters.set(fromPosition - filtersStartPosition, to.filter); + filters.set(toPosition - filtersStartPosition, from.filter); + } catch (Exception ignore) {} orderChanged = true; - notifyItemMoved(fromIndex, toIndex); + updateRows(true); } public void moveElementToStart(int index) { - int idx1 = index; - int count = filtersEndRow - filtersStartRow; - if (!showAllChats) { - idx1++; - count++; - } - - if (idx1 < 0 || idx1 >= count) { + ArrayList filters = getMessagesController().dialogFilters; + if (index < 0 || index >= filters.size()) { return; } - ArrayList filters = getMessagesController().dialogFilters; filters.add(0, filters.remove(index)); for (int i = 0; i <= index; ++i) { filters.get(i).order = i; } orderChanged = true; - notifyItemMoved(filtersStartRow + index, filtersStartRow); + updateRows(true); } } @@ -962,8 +1016,7 @@ public boolean isLongPressDragEnabled() { @Override public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { - boolean canMove = getUserConfig().isPremium() || !((viewHolder.itemView instanceof FilterCell) && ((FilterCell) viewHolder.itemView).currentFilter.isDefault()) || true; - if (viewHolder.getItemViewType() != 2 || !canMove) { + if (viewHolder.getItemViewType() != VIEW_TYPE_FILTER) { return makeMovementFlags(0, 0); } return makeMovementFlags(ItemTouchHelper.UP | ItemTouchHelper.DOWN, 0); @@ -971,8 +1024,7 @@ public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder v @Override public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source, RecyclerView.ViewHolder target) { - boolean canMove = getUserConfig().isPremium() || !((target.itemView instanceof FilterCell) && ((FilterCell) target.itemView).currentFilter.isDefault()) || true; - if (source.getItemViewType() != target.getItemViewType() || !canMove) { + if (source.getItemViewType() != target.getItemViewType()) { return false; } adapter.swapElements(source.getAdapterPosition(), target.getAdapterPosition()); @@ -988,7 +1040,7 @@ private void resetDefaultPosition() { if (UserConfig.getInstance(UserConfig.selectedAccount).isPremium()) { return; } - ArrayList filters = getMessagesController().dialogFilters; + ArrayList filters = getMessagesController().getDialogFilters(); for (int i = 0; i < filters.size(); ++i) { if (filters.get(i).isDefault() && i != 0) { adapter.moveElementToStart(i); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/GroupCallActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/GroupCallActivity.java index cb05580de9..9d0f65b615 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/GroupCallActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/GroupCallActivity.java @@ -96,6 +96,7 @@ import org.telegram.messenger.support.LongSparseIntArray; import org.telegram.messenger.voip.Instance; import org.telegram.messenger.voip.VoIPService; +import org.telegram.messenger.voip.VoipAudioManager; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; @@ -158,6 +159,8 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Locale; +import java.util.concurrent.CountDownLatch; + import tw.nekomimi.nekogram.NekoConfig; public class GroupCallActivity extends BottomSheet implements NotificationCenter.NotificationCenterDelegate, VoIPService.StateListener { @@ -1724,6 +1727,7 @@ public static void create(LaunchActivity activity, AccountInstance account, TLRP private GroupCallActivity(Context context, AccountInstance account, ChatObject.Call groupCall, TLRPC.Chat chat, TLRPC.InputPeer schedulePeer, boolean scheduleHasFewPeers, String scheduledHash) { super(context, false); + setOpenNoDelay(true); this.accountInstance = account; this.call = groupCall; this.schedulePeer = schedulePeer; @@ -1743,6 +1747,14 @@ public void onOpenAnimationStart() { @Override public void onOpenAnimationEnd() { + VoIPService voipService = VoIPService.getSharedInstance(); + if (voipService != null) { + CountDownLatch latch = voipService.getGroupCallBottomSheetLatch(); + if (latch != null) { + latch.countDown(); + } + } + if (muteButtonState == MUTE_BUTTON_STATE_SET_REMINDER) { showReminderHint(); } @@ -1759,7 +1771,7 @@ public boolean canDismiss() { getWindow().setNavigationBarColor(0xff000000); } scrollNavBar = true; - navBarColorKey = null; + navBarColorKey = -1; scrimPaint = new Paint() { @Override @@ -4421,8 +4433,8 @@ public void onClick(View v) { soundItem.setIcon(VoIPService.getSharedInstance().isHeadsetPlugged() ? R.drawable.msg_voice_headphones : R.drawable.msg_voice_phone); soundItem.setSubtext(VoIPService.getSharedInstance().isHeadsetPlugged() ? LocaleController.getString("VoipAudioRoutingHeadset", R.string.VoipAudioRoutingHeadset) : LocaleController.getString("VoipAudioRoutingPhone", R.string.VoipAudioRoutingPhone)); } else if (rout == VoIPService.AUDIO_ROUTE_SPEAKER) { - AudioManager am = (AudioManager) context.getSystemService(AUDIO_SERVICE); - if (am.isSpeakerphoneOn()) { + VoipAudioManager vam = VoipAudioManager.get(); + if (vam.isSpeakerphoneOn()) { soundItem.setIcon(R.drawable.msg_voice_speaker); soundItem.setSubtext(LocaleController.getString("VoipAudioRoutingSpeaker", R.string.VoipAudioRoutingSpeaker)); } else { @@ -5001,7 +5013,7 @@ protected CharSequence getContentDescription(int value) { scheduleButtonTextView.setSingleLine(true); scheduleButtonTextView.setEllipsize(TextUtils.TruncateAt.END); scheduleButtonTextView.setGravity(Gravity.CENTER); - scheduleButtonTextView.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(4), 0, 0x3f000000)); + scheduleButtonTextView.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(8), 0, 0x3f000000)); scheduleButtonTextView.setTextColor(0xffffffff); scheduleButtonTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); scheduleButtonTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); @@ -5132,7 +5144,11 @@ public void requestLayout() { calendar.setTimeInMillis(date); int year = calendar.get(Calendar.YEAR); if (year == currentYear) { - return LocaleController.getInstance().formatterScheduleDay.format(date); + return ( + LocaleController.getInstance().formatterWeek.format(date) + + " " + + LocaleController.getInstance().formatterScheduleDay.format(date) + ); } else { return LocaleController.getInstance().formatterScheduleYear.format(date); } @@ -7155,7 +7171,7 @@ private void processSelectedOption(TLRPC.TL_groupCallParticipant participant, lo } private boolean showMenuForCell(View rendererCell) { - if (itemAnimator.isRunning()) { + if (itemAnimator.isRunning() || getContext() == null) { return false; } if (avatarPriviewTransitionInProgress || avatarsPreviewShowed) { @@ -7221,6 +7237,9 @@ private boolean showMenuForCell(View rendererCell) { boolean showWithAvatarPreview = !isLandscapeMode && !isTabletMode && !AndroidUtilities.isInMultiwindow; TLRPC.TL_groupCallParticipant participant = view.getParticipant(); + if (participant == null) { + return false; + } Rect rect = new Rect(); @@ -7967,12 +7986,12 @@ public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) { int type = holder.getItemViewType(); if (type == VIEW_TYPE_PARTICIPANT) { GroupCallUserCell cell = (GroupCallUserCell) holder.itemView; - String key = actionBar.getTag() != null ? Theme.key_voipgroup_mutedIcon : Theme.key_voipgroup_mutedIconUnscrolled; + int key = actionBar.getTag() != null ? Theme.key_voipgroup_mutedIcon : Theme.key_voipgroup_mutedIconUnscrolled; cell.setGrayIconColor(key, Theme.getColor(key)); cell.setDrawDivider(holder.getAdapterPosition() != getItemCount() - 2); } else if (type == VIEW_TYPE_CALL_INVITED) { GroupCallInvitedCell cell = (GroupCallInvitedCell) holder.itemView; - String key = actionBar.getTag() != null ? Theme.key_voipgroup_mutedIcon : Theme.key_voipgroup_mutedIconUnscrolled; + int key = actionBar.getTag() != null ? Theme.key_voipgroup_mutedIcon : Theme.key_voipgroup_mutedIconUnscrolled; cell.setGrayIconColor(key, Theme.getColor(key)); cell.setDrawDivider(holder.getAdapterPosition() != getItemCount() - 2); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateFinalActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateFinalActivity.java index 8fa47d32fd..fcb0bd458b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateFinalActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateFinalActivity.java @@ -987,7 +987,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType switch (viewType) { case VIEW_TYPE_SHADOW_SECTION_CELL: { view = new ShadowSectionCell(context); - Drawable drawable = Theme.getThemedDrawable(context, R.drawable.greydivider_top, Theme.key_windowBackgroundGrayShadow); + Drawable drawable = Theme.getThemedDrawableByKey(context, R.drawable.greydivider_top, Theme.key_windowBackgroundGrayShadow); CombinedDrawable combinedDrawable = new CombinedDrawable(new ColorDrawable(Theme.getColor(Theme.key_windowBackgroundGray)), drawable); combinedDrawable.setFullsize(true); view.setBackgroundDrawable(combinedDrawable); @@ -1006,7 +1006,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType break; case VIEW_TYPE_TEXT_INFO_CELL: view = new TextInfoPrivacyCell(context); - Drawable drawable = Theme.getThemedDrawable(context, selectedContacts.size() == 0 ? R.drawable.greydivider_bottom : R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow); + Drawable drawable = Theme.getThemedDrawableByKey(context, selectedContacts.size() == 0 ? R.drawable.greydivider_bottom : R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow); CombinedDrawable combinedDrawable = new CombinedDrawable(new ColorDrawable(Theme.getColor(Theme.key_windowBackgroundGray)), drawable); combinedDrawable.setFullsize(true); view.setBackgroundDrawable(combinedDrawable); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/GroupInviteActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/GroupInviteActivity.java index b6855f643a..77b6b501c5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/GroupInviteActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/GroupInviteActivity.java @@ -282,7 +282,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { TextInfoPrivacyCell privacyCell = (TextInfoPrivacyCell) holder.itemView; if (position == shadowRow) { privacyCell.setText(""); - privacyCell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + privacyCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } else if (position == linkInfoRow) { TLRPC.Chat chat = getMessagesController().getChat(chatId); if (ChatObject.isChannel(chat) && !chat.megagroup) { @@ -290,7 +290,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } else { privacyCell.setText(LocaleController.getString("LinkInfo", R.string.LinkInfo)); } - privacyCell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + privacyCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); } break; case 2: diff --git a/TMessagesProj/src/main/java/org/telegram/ui/GroupStickersActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/GroupStickersActivity.java index d0ab0a93ed..002829475f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/GroupStickersActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/GroupStickersActivity.java @@ -235,18 +235,18 @@ private void onStickerSetClicked(View view, TLRPC.TL_messages_stickerSet sticker boolean isSelected = ((StickerSetCell) view).isChecked(); stickersAlert.setCustomButtonDelegate(new StickersAlert.StickersAlertCustomButtonDelegate() { @Override - public String getCustomButtonTextColorKey() { - return isSelected ? Theme.key_dialogTextRed : Theme.key_featuredStickers_buttonText; + public int getCustomButtonTextColorKey() { + return isSelected ? Theme.key_text_RedBold : Theme.key_featuredStickers_buttonText; } @Override - public String getCustomButtonRippleColorKey() { - return !isSelected ? Theme.key_featuredStickers_addButtonPressed : null; + public int getCustomButtonRippleColorKey() { + return !isSelected ? Theme.key_featuredStickers_addButtonPressed : -1; } @Override - public String getCustomButtonColorKey() { - return !isSelected ? Theme.key_featuredStickers_addButton : null; + public int getCustomButtonColorKey() { + return !isSelected ? Theme.key_featuredStickers_addButton : -1; } @Override @@ -549,7 +549,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int default: case TYPE_MY_STICKERS_HEADER: view = new HeaderCell(mContext, Theme.key_windowBackgroundWhiteGrayText4, 21, 0, 0, false, getResourceProvider()); - view.setBackground(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + view.setBackground(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); ((HeaderCell) view).setText(LocaleController.getString(R.string.ChooseStickerMyStickerSets)); break; } @@ -685,7 +685,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int break; case TYPE_INFO: view = new TextInfoPrivacyCell(mContext); - view.setBackground(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + view.setBackground(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); break; case TYPE_CHOOSE_HEADER: default: diff --git a/TMessagesProj/src/main/java/org/telegram/ui/KeepMediaPopupView.java b/TMessagesProj/src/main/java/org/telegram/ui/KeepMediaPopupView.java index d4a6395f35..79d78a7664 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/KeepMediaPopupView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/KeepMediaPopupView.java @@ -1,5 +1,7 @@ package org.telegram.ui; +import static org.telegram.ui.CacheControlActivity.KEEP_MEDIA_TYPE_STORIES; + import android.content.Context; import android.os.Bundle; import android.text.method.LinkMovementMethod; @@ -34,6 +36,7 @@ public class KeepMediaPopupView extends ActionBarPopupWindow.ActionBarPopupWindo ActionBarMenuSubItem oneMonth; ActionBarMenuSubItem oneWeek; ActionBarMenuSubItem oneDay; + ActionBarMenuSubItem twoDay; ActionBarMenuSubItem oneMinute; ArrayList checkItems = new ArrayList<>(); @@ -57,12 +60,14 @@ public KeepMediaPopupView(BaseFragment baseFragment, Context context) { // checkItems.add(new CheckItem(oneMinute, CacheByChatsController.KEEP_MEDIA_ONE_MINUTE)); // } oneDay = ActionBarMenuItem.addItem(this, R.drawable.msg_autodelete_1d, LocaleController.formatPluralString("Days", 1), false, null); + twoDay = ActionBarMenuItem.addItem(this, R.drawable.msg_autodelete_2d, LocaleController.formatPluralString("Days", 2), false, null); oneWeek = ActionBarMenuItem.addItem(this, R.drawable.msg_autodelete_1w, LocaleController.formatPluralString("Weeks", 1), false, null); oneMonth = ActionBarMenuItem.addItem(this, R.drawable.msg_autodelete_1m, LocaleController.formatPluralString("Months", 1), false, null); forever = ActionBarMenuItem.addItem(this, R.drawable.msg_cancel, LocaleController.getString("AutoDeleteMediaNever", R.string.AutoDeleteMediaNever), false, null); delete = ActionBarMenuItem.addItem(this, R.drawable.msg_delete, LocaleController.getString("DeleteException", R.string.DeleteException), false, null); - delete.setColors(Theme.getColor(Theme.key_windowBackgroundWhiteRedText), Theme.getColor(Theme.key_windowBackgroundWhiteRedText)); + delete.setColors(Theme.getColor(Theme.key_text_RedRegular), Theme.getColor(Theme.key_text_RedRegular)); checkItems.add(new CheckItem(oneDay, CacheByChatsController.KEEP_MEDIA_ONE_DAY)); + checkItems.add(new CheckItem(twoDay, CacheByChatsController.KEEP_MEDIA_TWO_DAY)); checkItems.add(new CheckItem(oneWeek, CacheByChatsController.KEEP_MEDIA_ONE_WEEK)); checkItems.add(new CheckItem(oneMonth, CacheByChatsController.KEEP_MEDIA_ONE_MONTH)); checkItems.add(new CheckItem(forever, CacheByChatsController.KEEP_MEDIA_FOREVER)); @@ -72,7 +77,7 @@ public KeepMediaPopupView(BaseFragment baseFragment, Context context) { gap = new FrameLayout(context); gap.setBackgroundColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuSeparator)); View gapShadow = new View(context); - gapShadow.setBackground(Theme.getThemedDrawable(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow, null)); + gapShadow.setBackground(Theme.getThemedDrawableByKey(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow, null)); gap.addView(gapShadow, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); gap.setTag(R.id.fit_width_tag, 1); addView(gap, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 8)); @@ -164,6 +169,19 @@ public void onTransitionAnimationEnd(boolean isOpen, boolean backward) { public void update(int type) { currentType = type; + if (type == KEEP_MEDIA_TYPE_STORIES) { + twoDay.setVisibility(View.VISIBLE); + oneMonth.setVisibility(View.GONE); + gap.setVisibility(View.GONE); + exceptionsView.setVisibility(View.GONE); + description.setVisibility(View.GONE); + } else { + twoDay.setVisibility(View.GONE); + oneMonth.setVisibility(View.VISIBLE); + gap.setVisibility(View.VISIBLE); + exceptionsView.setVisibility(View.VISIBLE); + description.setVisibility(View.VISIBLE); + } exceptions = cacheByChatsController.getKeepMediaExceptions(type); if (exceptions.isEmpty()) { exceptionsView.titleView.setText(LocaleController.getString("AddAnException", R.string.AddAnException)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LanguageSelectActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LanguageSelectActivity.java index c4c77dfcee..c8ac956a0c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LanguageSelectActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LanguageSelectActivity.java @@ -10,7 +10,6 @@ import android.content.Context; import android.content.DialogInterface; -import android.content.SharedPreferences; import android.graphics.Canvas; import android.text.TextUtils; import android.view.View; @@ -207,7 +206,7 @@ protected void onMoveAnimationUpdate(RecyclerView.ViewHolder holder) { showDialog(new PremiumFeatureBottomSheet(LanguageSelectActivity.this, PremiumPreviewFragment.PREMIUM_FEATURE_TRANSLATIONS, false)); return; } - MessagesController.getMainSettings(currentAccount).edit().putBoolean("translate_chat_button", value).apply(); + getMessagesController().getTranslateController().setChatTranslateEnabled(value); NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.updateSearchSettings); ((TextCheckCell) view).setChecked(value); } @@ -278,7 +277,6 @@ protected void onMoveAnimationUpdate(RecyclerView.ViewHolder holder) { String langCode = localeInfo.pluralLangCode, prevLangCode = prevLocale.pluralLangCode; - SharedPreferences preferences = MessagesController.getGlobalMainSettings(); HashSet selectedLanguages = RestrictedLanguagesSelectActivity.getRestrictedLanguages(); HashSet newSelectedLanguages = new HashSet(selectedLanguages); @@ -288,7 +286,7 @@ protected void onMoveAnimationUpdate(RecyclerView.ViewHolder holder) { if (langCode != null && !"null".equals(langCode)) { newSelectedLanguages.add(langCode); } - preferences.edit().putStringSet("translate_button_restricted_languages", newSelectedLanguages).apply(); + RestrictedLanguagesSelectActivity.updateRestrictedLanguages(newSelectedLanguages, false); MessagesController.getInstance(currentAccount).getTranslateController().checkRestrictedLanguagesUpdate(); MessagesController.getInstance(currentAccount).getTranslateController().cleanup(); @@ -345,7 +343,7 @@ protected void onMoveAnimationUpdate(RecyclerView.ViewHolder holder) { showDialog(alertDialog); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } catch (Exception e) { FileLog.e(e); @@ -634,9 +632,9 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { position--; ShadowSectionCell sectionCell = (ShadowSectionCell) holder.itemView; if (!unofficialLanguages.isEmpty() && position == unofficialLanguages.size()) { - sectionCell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + sectionCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); } else { - sectionCell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + sectionCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } break; } @@ -684,6 +682,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { cell.updateRTL(); if (position == 1) { cell.setTextAndCheck(LocaleController.getString("ShowTranslateButton", R.string.ShowTranslateButton), getContextValue(), true); + cell.setCheckBoxIcon(0); } else if (position == 2) { cell.setTextAndCheck(LocaleController.getString("ShowTranslateChatButton", R.string.ShowTranslateChatButton), getChatValue(), getContextValue() || getChatValue()); cell.setCheckBoxIcon(!getUserConfig().isPremium() ? R.drawable.permission_locked : 0); @@ -695,14 +694,14 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { infoCell.updateRTL(); if (position == (!getMessagesController().premiumLocked && (getContextValue() || getChatValue()) ? 4 : 3)) { infoCell.setText(LocaleController.getString("TranslateMessagesInfo1", R.string.TranslateMessagesInfo1)); - infoCell.setBackground(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + infoCell.setBackground(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); infoCell.setTopPadding(11); infoCell.setBottomPadding(16); } else { infoCell.setTopPadding(0); infoCell.setBottomPadding(16); infoCell.setText(LocaleController.getString("TranslateMessagesInfo2", R.string.TranslateMessagesInfo2)); - infoCell.setBackground(Theme.getThemedDrawable(mContext, R.drawable.greydivider_top, Theme.key_windowBackgroundGrayShadow)); + infoCell.setBackground(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_top, Theme.key_windowBackgroundGrayShadow)); } break; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java index 8f60a83571..fbd26fe3ab 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java @@ -54,6 +54,7 @@ import android.text.TextUtils; import android.text.style.ClickableSpan; import android.util.Base64; +import android.util.Log; import android.util.SparseArray; import android.util.SparseIntArray; import android.util.TypedValue; @@ -102,6 +103,7 @@ import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; import org.telegram.messenger.FingerprintController; +import org.telegram.messenger.FlagSecureReason; import org.telegram.messenger.GenericProvider; import org.telegram.messenger.ImageLoader; import org.telegram.messenger.LiteMode; @@ -158,11 +160,13 @@ import org.telegram.ui.Components.EmojiPacksAlert; import org.telegram.ui.Components.FireworksOverlay; import org.telegram.ui.Components.FloatingDebug.FloatingDebugController; +import org.telegram.ui.Components.FolderBottomSheet; import org.telegram.ui.Components.Forum.ForumUtilities; import org.telegram.ui.Components.GroupCallPip; import org.telegram.ui.Components.JoinGroupAlert; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.MediaActionDrawable; +import org.telegram.ui.Components.MediaActivity; import org.telegram.ui.Components.PasscodeView; import org.telegram.ui.Components.PhonebookShareAlert; import org.telegram.ui.Components.PipRoundVideoView; @@ -181,6 +185,10 @@ import org.telegram.ui.Components.UndoView; import org.telegram.ui.Components.UpdateAppAlertDialog; import org.telegram.ui.Components.voip.VoIPHelper; +import org.telegram.ui.Stories.StoriesController; +import org.telegram.ui.Stories.StoriesListPlaceProvider; +import org.telegram.ui.Stories.StoryViewer; +import org.telegram.ui.Stories.recorder.StoryRecorder; import org.webrtc.voiceengine.WebRtcAudioTrack; import java.io.BufferedReader; @@ -221,6 +229,7 @@ import xyz.nextalone.nagram.NaConfig; public class LaunchActivity extends BasePermissionsActivity implements INavigationLayout.INavigationLayoutDelegate, NotificationCenter.NotificationCenterDelegate, DialogsActivity.DialogsActivityDelegate { + public final static String EXTRA_FORCE_NOT_INTERNAL_APPS = "force_not_internal_apps"; public final static Pattern PREFIX_T_ME_PATTERN = Pattern.compile("^(?:http(?:s|)://|)([A-z0-9-]+?)\\.t\\.me"); public static boolean isResumed; @@ -232,6 +241,7 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati final private Pattern locationRegex = Pattern.compile("geo: ?(-?\\d+\\.\\d+),(-?\\d+\\.\\d+)(,|\\?z=)(-?\\d+)"); private Location sendingLocation; private String videoPath; + private String voicePath; private String sendingText; private ArrayList photoPathsArray; private ArrayList documentsPathsArray; @@ -326,13 +336,17 @@ public void accept(Boolean aBoolean) { } }; + private FlagSecureReason flagSecureReason; + public static LaunchActivity instance; + private View customNavigationBar; @Override protected void onCreate(Bundle savedInstanceState) { if (BuildVars.DEBUG_VERSION) { StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder(StrictMode.getVmPolicy()) .detectLeakedClosableObjects() + .penaltyLog() .build()); } instance = this; @@ -376,13 +390,8 @@ protected void onCreate(Bundle savedInstanceState) { } } getWindow().setBackgroundDrawableResource(R.drawable.transparent); - if (SharedConfig.passcodeHash.length() > 0 && !SharedConfig.allowScreenCapture && !NekoXConfig.disableFlagSecure) { - try { - getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE); - } catch (Exception e) { - FileLog.e(e); - } - } + flagSecureReason = new FlagSecureReason(getWindow(), () -> SharedConfig.passcodeHash.length() > 0 && !SharedConfig.allowScreenCapture); + flagSecureReason.attach(); super.onCreate(savedInstanceState); if (Build.VERSION.SDK_INT >= 24) { @@ -447,6 +456,7 @@ public void closeDrawer(boolean fast) { } }; drawerLayoutContainer.setBehindKeyboardColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + frameLayout.addView(drawerLayoutContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); if (Build.VERSION.SDK_INT >= 21) { @@ -501,12 +511,13 @@ public boolean drawChild(Canvas canvas, View child, long drawingTime) { itemAnimator = new SideMenultItemAnimator(sideMenu); sideMenu.setItemAnimator(itemAnimator); sideMenu.setBackgroundColor(Theme.getColor(Theme.key_chats_menuBackground)); + sideMenuContainer.setBackgroundColor(Theme.getColor(Theme.key_chats_menuBackground)); sideMenu.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)); sideMenu.setAllowItemsInteractionDuringAnimation(false); sideMenu.setAdapter(drawerLayoutAdapter = new DrawerLayoutAdapter(this, itemAnimator, drawerLayoutContainer)); drawerLayoutAdapter.setOnPremiumDrawableClick(e -> showSelectStatusDialog()); sideMenuContainer.addView(sideMenu, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); - drawerLayoutContainer.setDrawerLayout(sideMenuContainer); + drawerLayoutContainer.setDrawerLayout(sideMenuContainer, sideMenu); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) sideMenuContainer.getLayoutParams(); Point screenSize = AndroidUtilities.getRealScreenSize(); layoutParams.width = AndroidUtilities.isTablet() ? AndroidUtilities.dp(320) : Math.min(AndroidUtilities.dp(320), Math.min(screenSize.x, screenSize.y) - AndroidUtilities.dp(56)); @@ -631,6 +642,12 @@ public boolean drawChild(Canvas canvas, View child, long drawingTime) { drawerLayoutContainer.closeDrawer(false); } else if (id == 15) { showSelectStatusDialog(); + } else if (id == 16) { + Bundle args = new Bundle(); + args.putLong("dialog_id", UserConfig.getInstance(currentAccount).getClientUserId()); + args.putInt("type", MediaActivity.TYPE_STORIES); + drawerLayoutContainer.closeDrawer(true); + presentFragment(new MediaActivity(args, null)); } } }); @@ -749,9 +766,12 @@ public void onPreviewOpenAnimationEnd() { actionBarLayout.setFragmentStack(mainFragmentsStack); actionBarLayout.setFragmentStackChangedListener(() -> { checkSystemBarColors(true, false); + if (getLastFragment() != null && getLastFragment().storyViewer != null && getLastFragment().storyViewer.isShown()) { + getLastFragment().storyViewer.updatePlayingMode(); + } }); actionBarLayout.setDelegate(this); - Theme.loadWallpaper(); + Theme.loadWallpaper(true); checkCurrentAccount(); updateCurrentConnectionState(currentAccount); @@ -772,6 +792,7 @@ public void onPreviewOpenAnimationEnd() { NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.showBulletin); NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.appUpdateAvailable); NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.requestPermissions); + NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.billingConfirmPurchaseError); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.currentUserPremiumStatusChanged); NotificationCenter.getGlobalInstance().addObserver(drawerLayoutAdapter, NotificationCenter.proxySettingsChanged); @@ -1210,15 +1231,13 @@ protected void onEmojiSelected(View emojiView, Long documentId, TLRPC.Document d boolean hasStatus = user.emoji_status instanceof TLRPC.TL_emojiStatus || user.emoji_status instanceof TLRPC.TL_emojiStatusUntil && ((TLRPC.TL_emojiStatusUntil) user.emoji_status).until > (int) (System.currentTimeMillis() / 1000); - ((DrawerActionCell) child).updateText( - hasStatus ? - LocaleController.getString("ChangeEmojiStatus", R.string.ChangeEmojiStatus) : - LocaleController.getString("SetEmojiStatus", R.string.SetEmojiStatus) - ); - ((DrawerActionCell) child).updateIcon( - hasStatus ? - R.raw.emoji_status_change_to_set : - R.raw.emoji_status_set_to_change + ((DrawerActionCell) child).updateTextAndIcon( + hasStatus ? + LocaleController.getString("ChangeEmojiStatus", R.string.ChangeEmojiStatus) : + LocaleController.getString("SetEmojiStatus", R.string.SetEmojiStatus), + hasStatus ? + R.drawable.msg_status_edit : + R.drawable.msg_status_set ); } } @@ -1262,18 +1281,18 @@ private void openSettings(boolean expanded) { } private void checkSystemBarColors() { - checkSystemBarColors(false, true, !isNavigationBarColorFrozen); + checkSystemBarColors(false, true, !isNavigationBarColorFrozen, true); } - private void checkSystemBarColors(boolean useCurrentFragment) { - checkSystemBarColors(useCurrentFragment, true, !isNavigationBarColorFrozen); + public void checkSystemBarColors(boolean useCurrentFragment) { + checkSystemBarColors(useCurrentFragment, true, !isNavigationBarColorFrozen, true); } private void checkSystemBarColors(boolean checkStatusBar, boolean checkNavigationBar) { - checkSystemBarColors(false, checkStatusBar, checkNavigationBar); + checkSystemBarColors(false, checkStatusBar, checkNavigationBar, true); } - private void checkSystemBarColors(boolean useCurrentFragment, boolean checkStatusBar, boolean checkNavigationBar) { + public void checkSystemBarColors(boolean useCurrentFragment, boolean checkStatusBar, boolean checkNavigationBar, boolean checkButtons) { BaseFragment currentFragment = !mainFragmentsStack.isEmpty() ? mainFragmentsStack.get(mainFragmentsStack.size() - 1) : null; if (currentFragment != null && (currentFragment.isRemovingFromStack() || currentFragment.isInPreviewMode())) { currentFragment = mainFragmentsStack.size() > 1 ? mainFragmentsStack.get(mainFragmentsStack.size() - 2) : null; @@ -1291,20 +1310,8 @@ private void checkSystemBarColors(boolean useCurrentFragment, boolean checkStatu AndroidUtilities.setLightStatusBar(getWindow(), enable, forceLightStatusBar); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && checkNavigationBar && (!useCurrentFragment || currentFragment == null || !currentFragment.isInPreviewMode())) { - final Window window = getWindow(); final int color = currentFragment != null && useCurrentFragment ? currentFragment.getNavigationBarColor() : Theme.getColor(Theme.key_windowBackgroundGray, null, true); -// Theme.ResourcesProvider resourcesProvider = currentFragment != null ? currentFragment.getResourceProvider() : null; -// if (resourcesProvider != null) { -// color = resourcesProvider.getColor(Theme.key_windowBackgroundGray); -// } -// if (color == null) { -// color = Theme.getColor(Theme.key_windowBackgroundGray, null, true); -// } - if (window.getNavigationBarColor() != color) { - window.setNavigationBarColor(color); - final float brightness = AndroidUtilities.computePerceivedBrightness(color); - AndroidUtilities.setLightNavigationBar(getWindow(), brightness >= 0.721f); - } + setNavigationBarColor(color, checkButtons); } } if ((SharedConfig.noStatusBar || forceLightStatusBar) && Build.VERSION.SDK_INT >= 21 && checkStatusBar) { @@ -1434,6 +1441,7 @@ private void checkCurrentAccount() { NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.newSuggestionsAvailable); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.currentUserPremiumStatusChanged); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.chatSwithcedToForum); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.storiesEnabledUpdate); } currentAccount = UserConfig.selectedAccount; NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.appDidLogout); @@ -1454,6 +1462,7 @@ private void checkCurrentAccount() { NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.currentUserShowLimitReachedDialog); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.currentUserPremiumStatusChanged); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.chatSwithcedToForum); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.storiesEnabledUpdate); } private void checkLayout() { @@ -1587,6 +1596,7 @@ public void showPasscodeActivity(boolean fingerprint, boolean animated, int x, i } else if (ArticleViewer.hasInstance() && ArticleViewer.getInstance().isVisible()) { ArticleViewer.getInstance().close(false, true); } + StoryRecorder.destroyInstance(); MessageObject messageObject = MediaController.getInstance().getPlayingMessageObject(); if (messageObject != null && messageObject.isRoundVideo()) { MediaController.getInstance().cleanupPlayer(true, true); @@ -1654,15 +1664,21 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool return handleIntent(intent, isNew, restore, fromPassword, null); } - @SuppressLint("Range") private boolean handleIntent(Intent intent, boolean isNew, boolean restore, boolean fromPassword, Browser.Progress progress) { + return handleIntent(intent, isNew, restore, fromPassword, progress, true); + } + + @SuppressLint("Range") + private boolean handleIntent(Intent intent, boolean isNew, boolean restore, boolean fromPassword, Browser.Progress progress, boolean rebuildFragments) { if (AndroidUtilities.handleProxyIntent(this, intent)) { return true; } - if (isNew && PhotoViewer.hasInstance() && PhotoViewer.getInstance().isVisible()) { - if (intent == null || !Intent.ACTION_MAIN.equals(intent.getAction())) { + + if (intent == null || !Intent.ACTION_MAIN.equals(intent.getAction())) { + if (PhotoViewer.hasInstance() && PhotoViewer.getInstance().isVisible()) { PhotoViewer.getInstance().closePhoto(false, true); } + StoryRecorder.destroyInstance(); } int flags = intent.getFlags(); String action = intent.getAction(); @@ -1682,6 +1698,8 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool boolean pushOpened = false; long push_user_id = 0; long push_chat_id = 0; + long[] push_story_dids = null; + int push_story_id = 0; int push_topic_id = 0; int push_enc_id = 0; int push_msg_id = 0; @@ -1705,9 +1723,11 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool String callSearchQuery = null; String newContactName = null; String newContactPhone = null; + boolean forceNotInternalForApps = intent.getBooleanExtra(EXTRA_FORCE_NOT_INTERNAL_APPS, false); photoPathsArray = null; videoPath = null; + voicePath = null; sendingText = null; sendingLocation = null; documentsPathsArray = null; @@ -1865,6 +1885,8 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool } if (type != null && type.startsWith("video/")) { videoPath = path; + } else if (type != null && type.startsWith("audio/ogg") && type.contains("codecs=opus") && MediaController.isOpusFile(path) == 1) { + voicePath = path; } else { if (documentsPathsArray == null) { documentsPathsArray = new ArrayList<>(); @@ -2033,6 +2055,7 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool String theme = null; String code = null; String contactToken = null; + String folderSlug = null; TLRPC.TL_wallPaper wallPaper = null; String inputInvoiceSlug = null; Integer messageId = null; @@ -2044,6 +2067,7 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool String setAsAttachBot = null; String attachMenuBotToOpen = null; String attachMenuBotChoose = null; + int storyId = 0; final String scheme = data.getScheme(); boolean internal = intent.getExtras() != null && intent.getExtras().get("internal") != null && (boolean) intent.getExtras().get("internal"); if (scheme != null) { @@ -2238,6 +2262,10 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool } } else if (path.startsWith("contact/")) { contactToken = path.substring(8); + } else if (path.startsWith("folder/")) { + folderSlug = path.substring(7); + } else if (path.startsWith("addlist/")) { + folderSlug = path.substring(8); } else if (path.length() >= 1) { botAppMaybe = null; ArrayList segments = new ArrayList<>(data.getPathSegments()); @@ -2246,7 +2274,11 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool } if (segments.size() > 0) { username = segments.get(0); - if (segments.size() > 1) { + if (segments.size() >= 3 && "s".equals(segments.get(1))) { + try { + storyId = Integer.parseInt(segments.get(2)); + } catch (Exception ignore) {} + } else if (segments.size() > 1) { botAppMaybe = segments.get(1); startApp = data.getQueryParameter("startapp"); try { @@ -2273,6 +2305,7 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool attachMenuBotChoose = data.getQueryParameter("choose"); attachMenuBotToOpen = data.getQueryParameter("attach"); threadId = Utilities.parseInt(data.getQueryParameter("thread")); +// storyId = Utilities.parseInt(data.getQueryParameter("story")); if (threadId == 0) { threadId = null; } @@ -2342,6 +2375,7 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool attachMenuBotChoose = data.getQueryParameter("choose"); attachMenuBotToOpen = data.getQueryParameter("attach"); messageId = Utilities.parseInt(data.getQueryParameter("post")); + storyId = Utilities.parseInt(data.getQueryParameter("story")); if (messageId == 0) { messageId = null; } @@ -2669,6 +2703,10 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool newContactName = data.getQueryParameter("name"); newContactPhone = data.getQueryParameter("phone"); newContact = true; + } else if (url.startsWith("tg:addlist") || url.startsWith("tg://addlist")) { + url = url.replace("tg:addlist", "tg://telegram.org").replace("tg://addlist", "tg://telegram.org"); + data = Uri.parse(url); + folderSlug = data.getQueryParameter("slug"); } else { unsupportedUrl = url.replace("tg://", "").replace("tg:", ""); int index; @@ -2713,11 +2751,11 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool AlertsCreator.processError(currentAccount, error, getActionBarLayout().getLastFragment(), req); } }), ConnectionsManager.RequestFlagFailOnServerErrors); - } else if (username != null || group != null || sticker != null || emoji != null || contactToken != null || message != null || game != null || voicechat != null || auth != null || unsupportedUrl != null || lang != null || code != null || wallPaper != null || inputInvoiceSlug != null || channelId != null || theme != null || login != null) { + } else if (username != null || group != null || sticker != null || emoji != null || contactToken != null || folderSlug != null || message != null || game != null || voicechat != null || auth != null || unsupportedUrl != null || lang != null || code != null || wallPaper != null || inputInvoiceSlug != null || channelId != null || theme != null || login != null) { if (message != null && message.startsWith("@")) { message = " " + message; } - runLinkRequest(intentAccount[0], username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, login, wallPaper, inputInvoiceSlug, theme, voicechat, livestream, 0, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, startApp, progress); + runLinkRequest(intentAccount[0], username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, login, wallPaper, inputInvoiceSlug, theme, voicechat, livestream, 0, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, startApp, progress, forceNotInternalForApps, storyId); } else { try (Cursor cursor = getContentResolver().query(intent.getData(), null, null, null, null)) { if (cursor != null) { @@ -2755,6 +2793,7 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool // Integer chatIdInt = intent.getIntExtra("chatId", 0); long chatId = intent.getLongExtra("chatId", 0); // Integer userIdInt = intent.getIntExtra("userId", 0); + long[] storyDialogIds = intent.getLongArrayExtra("storyDialogIds"); long userId = intent.getLongExtra("userId", 0); int encId = intent.getIntExtra("encId", 0); int widgetId = intent.getIntExtra("appWidgetId", 0); @@ -2767,7 +2806,12 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool if (push_msg_id == 0) { push_msg_id = intent.getIntExtra("message_id", 0); } - if (chatId != 0) { + if (storyDialogIds != null) { + NotificationCenter.getInstance(intentAccount[0]).postNotificationName(NotificationCenter.closeChats); + push_story_dids = storyDialogIds; +// push_story_id = intent.getIntExtra("storyId", 0); + showDialogsList = true; + } else if (chatId != 0) { NotificationCenter.getInstance(intentAccount[0]).postNotificationName(NotificationCenter.closeChats); push_chat_id = chatId; push_topic_id = topicId; @@ -2807,7 +2851,10 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool } } - if (push_user_id != 0) { + if (push_story_dids != null) { + NotificationCenter.getInstance(intentAccount[0]).postNotificationName(NotificationCenter.closeChats); + openStories(push_story_dids, true); + } else if (push_user_id != 0) { if (audioCallUser || videoCallUser) { if (needCallAlert) { final BaseFragment lastFragment = actionBarLayout.getLastFragment(); @@ -2900,7 +2947,7 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool LocationActivity locationActivity = new LocationActivity(2); locationActivity.setMessageObject(info.messageObject); final long dialog_id = info.messageObject.getDialogId(); - locationActivity.setDelegate((location, live, notify, scheduleDate) -> SendMessagesHelper.getInstance(intentAccount[0]).sendMessage(location, dialog_id, null, null, null, null, notify, scheduleDate)); + locationActivity.setDelegate((location, live, notify, scheduleDate) -> SendMessagesHelper.getInstance(intentAccount[0]).sendMessage(SendMessagesHelper.SendMessageParams.of(location, dialog_id, null, null, null, null, notify, scheduleDate))); presentFragment(locationActivity); }, null)); } @@ -2915,7 +2962,7 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool } }); pushOpened = false; - } else if (videoPath != null || photoPathsArray != null || sendingText != null || sendingLocation != null || documentsPathsArray != null || contactsToSend != null || documentsUrisArray != null) { + } else if (videoPath != null || voicePath != null || photoPathsArray != null || sendingText != null || documentsPathsArray != null || contactsToSend != null || documentsUrisArray != null) { if (!AndroidUtilities.isTablet()) { NotificationCenter.getInstance(intentAccount[0]).postNotificationName(NotificationCenter.closeChats); } @@ -3140,10 +3187,12 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool } } } - actionBarLayout.rebuildFragments(INavigationLayout.REBUILD_FLAG_REBUILD_LAST); - if (AndroidUtilities.isTablet()) { - layersActionBarLayout.rebuildFragments(INavigationLayout.REBUILD_FLAG_REBUILD_LAST); - rightActionBarLayout.rebuildFragments(INavigationLayout.REBUILD_FLAG_REBUILD_LAST); + if (rebuildFragments) { + actionBarLayout.rebuildFragments(INavigationLayout.REBUILD_FLAG_REBUILD_LAST); + if (AndroidUtilities.isTablet()) { + layersActionBarLayout.rebuildFragments(INavigationLayout.REBUILD_FLAG_REBUILD_LAST); + rightActionBarLayout.rebuildFragments(INavigationLayout.REBUILD_FLAG_REBUILD_LAST); + } } } if (isVoipIntent) { @@ -3207,18 +3256,12 @@ private void openDialogsToSend(boolean animated) { public boolean shouldShowNextButton(DialogsActivity dialogsFragment, ArrayList dids, CharSequence message, boolean param) { if (exportingChatUri != null) { return false; - } else { - if (contactsToSend != null && contactsToSend.size() == 1 && !mainFragmentsStack.isEmpty()) { - return true; - } else { - if (dids.size() <= 1) { - if (videoPath != null) { - return true; - } else if (photoPathsArray != null && photoPathsArray.size() > 0) { - return true; - } - } - } + } + if (contactsToSend != null && contactsToSend.size() == 1 && !mainFragmentsStack.isEmpty()) { + return true; + } + if (dids.size() <= 1) { + return videoPath != null || photoPathsArray != null && photoPathsArray.size() > 0; } return false; } @@ -3238,6 +3281,7 @@ public boolean shouldShowNextButton(DialogsActivity dialogsFragment, ArrayList runLinkRequest(intentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, livestream, 1, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, botAppStartParam, progress); + Runnable runnable = () -> runLinkRequest(intentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, livestream, 1, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, botAppStartParam, progress, forceNotInternalForApps, storyId); progress.init(); progress.onCancel(() -> AndroidUtilities.cancelRunOnUIThread(runnable)); AndroidUtilities.runOnUIThread(runnable, 7500); @@ -3536,7 +3584,7 @@ private void runLinkRequest(final int intentAccount, if (account != intentAccount) { switchToAccount(account, true); } - runLinkRequest(account, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, livestream, 1, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, botAppStartParam, progress); + runLinkRequest(account, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, livestream, 1, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, botAppStartParam, progress, forceNotInternalForApps, storyId); }).show(); return; } else if (code != null) { @@ -3585,6 +3633,44 @@ private void runLinkRequest(final int intentAccount, BulletinFactory.of(mainFragmentsStack.get(mainFragmentsStack.size() - 1)).createErrorBulletin(LocaleController.getString(R.string.NoUsernameFound)).show(); } + try { + dismissLoading.run(); + } catch (Exception e) { + FileLog.e(e); + } + })); + } else if (folderSlug != null) { + TLRPC.TL_chatlists_checkChatlistInvite req = new TLRPC.TL_chatlists_checkChatlistInvite(); + req.slug = folderSlug; + requestId[0] = ConnectionsManager.getInstance(intentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + BaseFragment fragment = mainFragmentsStack.get(mainFragmentsStack.size() - 1); + if (response instanceof TLRPC.chatlist_ChatlistInvite) { + TLRPC.chatlist_ChatlistInvite inv = (TLRPC.chatlist_ChatlistInvite) response; + ArrayList chats = null; + ArrayList users = null; + if (inv instanceof TLRPC.TL_chatlists_chatlistInvite) { + chats = ((TLRPC.TL_chatlists_chatlistInvite) inv).chats; + users = ((TLRPC.TL_chatlists_chatlistInvite) inv).users; + } else if (inv instanceof TLRPC.TL_chatlists_chatlistInviteAlready) { + chats = ((TLRPC.TL_chatlists_chatlistInviteAlready) inv).chats; + users = ((TLRPC.TL_chatlists_chatlistInviteAlready) inv).users; + } + MessagesController.getInstance(intentAccount).putChats(chats, false); + MessagesController.getInstance(intentAccount).putUsers(users, false); + if (!(inv instanceof TLRPC.TL_chatlists_chatlistInvite && ((TLRPC.TL_chatlists_chatlistInvite) inv).peers.isEmpty())) { + final FolderBottomSheet sheet = new FolderBottomSheet(fragment, folderSlug, inv); + if (fragment != null) { + fragment.showDialog(sheet); + } else { + sheet.show(); + } + } else { + BulletinFactory.of(fragment).createErrorBulletin(LocaleController.getString(R.string.NoFolderFound)).show(); + } + } else { + BulletinFactory.of(fragment).createErrorBulletin(LocaleController.getString(R.string.NoFolderFound)).show(); + } + try { dismissLoading.run(); } catch (Exception e) { @@ -3617,10 +3703,49 @@ private void runLinkRequest(final int intentAccount, } })); } else if (username != null) { + if (progress != null) { + progress.init(); + } MessagesController.getInstance(intentAccount).getUserNameResolver().resolve(username, (peerId) -> { if (!LaunchActivity.this.isFinishing()) { boolean hideProgressDialog = true; - if (peerId != null && actionBarLayout != null && (game == null && voicechat == null || game != null && peerId > 0 || voicechat != null && peerId > 0 || livestream != null && peerId < 0)) { + if (storyId != 0 && peerId != null) { + hideProgressDialog = false; + MessagesController.getInstance(currentAccount).getStoriesController().resolveStoryLink(peerId, storyId, storyItem -> { + try { + dismissLoading.run(); + } catch (Exception e) { + FileLog.e(e); + } + BaseFragment baseFragment = getLastFragment(); + if (storyItem == null) { + BulletinFactory factory = BulletinFactory.global(); + if (factory != null) { + factory.createSimpleBulletin(R.raw.story_bomb2, LocaleController.getString("StoryNotFound", R.string.StoryNotFound)).show(); + } + return; + } else if (storyItem instanceof TLRPC.TL_storyItemDeleted) { + BulletinFactory factory = BulletinFactory.global(); + if (factory != null) { + factory.createSimpleBulletin(R.raw.story_bomb1, LocaleController.getString("StoryNotFound", R.string.StoryNotFound)).show(); + } + return; + } + if (baseFragment != null) { + storyItem.dialogId = peerId; + StoryViewer storyViewer = baseFragment.getOrCreateStoryViewer(); + if (storyViewer.isShown() && storyViewer.attachedToParent()) { + StoryViewer overlayStoryViewer = baseFragment.getOrCreateOverlayStoryViewer(); + final StoryViewer storyViewer1 = storyViewer; + overlayStoryViewer.setOnCloseListener(() -> storyViewer1.setOverlayVisible(false)); + storyViewer.setOverlayVisible(true); + storyViewer = overlayStoryViewer; + } + storyViewer.instantClose(); + storyViewer.open(this, storyItem, null); + } + }); + } else if (peerId != null && actionBarLayout != null && (game == null && voicechat == null || game != null && peerId > 0 || voicechat != null && peerId > 0 || livestream != null && peerId < 0)) { if (!TextUtils.isEmpty(botAppMaybe)) { TLRPC.User user = MessagesController.getInstance(intentAccount).getUser(peerId); if (user != null && user.bot) { @@ -3630,8 +3755,11 @@ private void runLinkRequest(final int intentAccount, app.short_name = botAppMaybe; getBotApp.app = app; ConnectionsManager.getInstance(currentAccount).sendRequest(getBotApp, (response1, error1) -> { + if (progress != null) { + progress.end(); + } if (error1 != null) { - AndroidUtilities.runOnUIThread(()-> runLinkRequest(currentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, livestream, state, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, null, null, progress)); + AndroidUtilities.runOnUIThread(() -> runLinkRequest(currentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, livestream, state, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, null, null, progress, forceNotInternalForApps, storyId)); } else { TLRPC.TL_messages_botApp botApp = (TLRPC.TL_messages_botApp) response1; AndroidUtilities.runOnUIThread(()->{ @@ -3646,30 +3774,8 @@ private void runLinkRequest(final int intentAccount, sheet.show(); }; - if (botApp.inactive) { - AlertDialog.Builder builder = new AlertDialog.Builder(LaunchActivity.this) - .setTopAnimation(R.raw.permission_request_apk, AlertsCreator.PERMISSIONS_REQUEST_TOP_ICON_SIZE, false, Theme.getColor(Theme.key_dialogTopBackground)) - .setMessage(AndroidUtilities.replaceTags(LocaleController.formatString(R.string.BotStartAppPermission, botApp.app.title, UserObject.getUserName(user)))) - .setPositiveButton(LocaleController.getString(R.string.Start), (dialog, which) -> loadBotSheet.run()) - .setNegativeButton(LocaleController.getString(R.string.Cancel), null); - - if (botApp.request_write_access) { - allowWrite.set(true); - - CheckBoxCell cell = new CheckBoxCell(LaunchActivity.this, 5, lastFragment.getResourceProvider()); - cell.setBackground(Theme.getSelectorDrawable(false)); - cell.setMultiline(true); - cell.setText(AndroidUtilities.replaceTags(LocaleController.formatString("OpenUrlOption2", R.string.OpenUrlOption2, UserObject.getUserName(user))), "", true, false); - cell.setPadding(LocaleController.isRTL ? AndroidUtilities.dp(16) : AndroidUtilities.dp(8), 0, LocaleController.isRTL ? AndroidUtilities.dp(8) : AndroidUtilities.dp(16), 0); - cell.setOnClickListener(v -> { - boolean allow = !cell.isChecked(); - cell.setChecked(allow, true); - allowWrite.set(allow); - }); - - builder.setView(cell); - } - builder.show(); + if (botApp.inactive || forceNotInternalForApps) { + AlertsCreator.createBotLaunchAlert(lastFragment, botApp, user, allowWrite, loadBotSheet); } else { loadBotSheet.run(); } @@ -3709,6 +3815,8 @@ private void runLinkRequest(final int intentAccount, args.putBoolean("onlySelect", true); args.putBoolean("allowGroups", chooserTargets.contains("groups")); + args.putBoolean("allowMegagroups", chooserTargets.contains("groups")); + args.putBoolean("allowLegacyGroups", chooserTargets.contains("groups")); args.putBoolean("allowUsers", chooserTargets.contains("users")); args.putBoolean("allowChannels", chooserTargets.contains("channels")); args.putBoolean("allowBots", chooserTargets.contains("bots")); @@ -3863,6 +3971,7 @@ private void runLinkRequest(final int intentAccount, } else if (ArticleViewer.hasInstance() && ArticleViewer.getInstance().isVisible()) { ArticleViewer.getInstance().close(false, true); } + StoryRecorder.destroyInstance(); if (GroupCallActivity.groupCallInstance != null) { GroupCallActivity.groupCallInstance.dismiss(); } @@ -4062,8 +4171,11 @@ public void didChangeOwner(TLRPC.User user) { } BaseFragment lastFragment = !mainFragmentsStack.isEmpty() && voicechat == null ? mainFragmentsStack.get(mainFragmentsStack.size() - 1) : null; if (lastFragment == null || MessagesController.getInstance(intentAccount).checkCanOpenChat(args, lastFragment)) { - if (isBot && lastFragment instanceof ChatActivity && ((ChatActivity) lastFragment).getDialogId() == dialog_id) { + final boolean sameDialogId = lastFragment instanceof ChatActivity && ((ChatActivity) lastFragment).getDialogId() == dialog_id; + if (isBot && sameDialogId) { ((ChatActivity) lastFragment).setBotUser(botUser); + } else if (attachMenuBotToOpen != null && sameDialogId) { + ((ChatActivity) lastFragment).openAttachBotLayout(attachMenuBotToOpen); } else { TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-dialog_id); if (chat != null && chat.forum) { @@ -4521,7 +4633,7 @@ public void onError() { object = res; } ThemePreviewActivity wallpaperActivity = new ThemePreviewActivity(object, null, true, false); - wallpaperActivity.setInitialModes(wallPaper.settings.blur, wallPaper.settings.motion); + wallpaperActivity.setInitialModes(wallPaper.settings.blur, wallPaper.settings.motion, wallPaper.settings.intensity); presentFragment(wallpaperActivity); } else { showAlertDialog(AlertsCreator.createSimpleAlert(LaunchActivity.this, LocaleController.getString("ErrorOccurred", R.string.ErrorOccurred) + "\n" + error.text)); @@ -5214,6 +5326,9 @@ public boolean didSelectDialogs(DialogsActivity dialogsFragment, ArrayList 0)); + boolean withoutAnimation = dialogsFragment == null || videoPath != null || (photoPathsArray != null && photoPathsArray.size() > 0); actionBarLayout.presentFragment(fragment, dialogsFragment != null, withoutAnimation, true, false); presentedFragmentWithRemoveLast = dialogsFragment != null; if (videoPath != null && topicId == 0) { @@ -5303,7 +5418,7 @@ public boolean didSelectDialogs(DialogsActivity dialogsFragment, ArrayList arrayList = new ArrayList<>(); arrayList.add(videoPath); - SendMessagesHelper.prepareSendingDocuments(accountInstance, arrayList, arrayList, null, captionToSend, null, did, replyToMsg, replyToMsg, null, null, notify, 0); + SendMessagesHelper.prepareSendingDocuments(accountInstance, arrayList, arrayList, null, captionToSend, null, did, replyToMsg, replyToMsg, null, null, notify, 0, null); } } if (photoPathsArray != null && !photosEditorOpened) { @@ -5311,14 +5426,40 @@ public boolean didSelectDialogs(DialogsActivity dialogsFragment, ArrayList= 0) { @@ -5588,6 +5745,7 @@ protected void onPause() { if (PhotoViewer.hasInstance() && PhotoViewer.getInstance().isVisible()) { PhotoViewer.getInstance().onPause(); } + StoryRecorder.onPause(); if (VoIPFragment.getInstance() != null) { VoIPFragment.onPause(); @@ -5673,6 +5831,9 @@ protected void onDestroy() { super.onDestroy(); onFinish(); FloatingDebugController.onDestroy(); + if (flagSecureReason != null) { + flagSecureReason.detach(); + } } @Override @@ -5755,6 +5916,7 @@ protected void onResume() { if (PhotoViewer.hasInstance() && PhotoViewer.getInstance().isVisible()) { PhotoViewer.getInstance().onResume(); } + StoryRecorder.onResume(); PipRoundVideoView pipRoundVideoView = PipRoundVideoView.getInstance(); if (pipRoundVideoView != null && MediaController.getInstance().isMessagePaused()) { MessageObject messageObject = MediaController.getInstance().getPlayingMessageObject(); @@ -5981,7 +6143,7 @@ public void updateDrawState(@NonNull TextPaint ds) { fragment.setDelegate((location, live, notify, scheduleDate) -> { for (HashMap.Entry entry : waitingForLocation.entrySet()) { MessageObject messageObject = entry.getValue(); - SendMessagesHelper.getInstance(account).sendMessage(location, messageObject.getDialogId(), messageObject, null, null, null, notify, scheduleDate); + SendMessagesHelper.getInstance(account).sendMessage(SendMessagesHelper.SendMessageParams.of(location, messageObject.getDialogId(), messageObject, null, null, null, notify, scheduleDate)); } }); presentFragment(fragment); @@ -6001,19 +6163,7 @@ public void updateDrawState(@NonNull TextPaint ds) { backgroundTablet.setBackgroundImage(Theme.getCachedWallpaper(), Theme.isWallpaperMotion()); } } else if (id == NotificationCenter.didSetPasscode) { - if (SharedConfig.passcodeHash.length() > 0 && !SharedConfig.allowScreenCapture && !NekoXConfig.disableFlagSecure) { - try { - getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE); - } catch (Exception e) { - FileLog.e(e); - } - } else if (!AndroidUtilities.hasFlagSecureFragment()) { - try { - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE); - } catch (Exception e) { - FileLog.e(e); - } - } + flagSecureReason.invalidate(); } else if (id == NotificationCenter.reloadInterface) { boolean last = mainFragmentsStack.size() > 1 && mainFragmentsStack.get(mainFragmentsStack.size() - 1) instanceof ProfileActivity; if (last) { @@ -6055,6 +6205,9 @@ public void updateDrawState(@NonNull TextPaint ds) { Boolean nightTheme = (Boolean) args[0]; if (!nightTheme) { if (sideMenu != null) { + if (sideMenuContainer != null) { + sideMenuContainer.setBackgroundColor(Theme.getColor(Theme.key_chats_menuBackground)); + } sideMenu.setBackgroundColor(Theme.getColor(Theme.key_chats_menuBackground)); sideMenu.setGlowColor(Theme.getColor(Theme.key_chats_menuBackground)); sideMenu.setListSelectorColor(Theme.getColor(Theme.key_listSelector)); @@ -6073,7 +6226,7 @@ public void updateDrawState(@NonNull TextPaint ds) { if (args.length > 1) { checkNavigationBarColor = (boolean) args[1]; } - checkSystemBarColors(args.length > 2 && (boolean) args[2], true, checkNavigationBarColor && !isNavigationBarColorFrozen && !actionBarLayout.isTransitionAnimationInProgress()); + checkSystemBarColors(args.length > 2 && (boolean) args[2], true, checkNavigationBarColor && !isNavigationBarColorFrozen && !actionBarLayout.isTransitionAnimationInProgress(), true); } else if (id == NotificationCenter.needSetDayNightTheme) { boolean instant = false; if (Build.VERSION.SDK_INT >= 21 && args[2] != null) { @@ -6105,6 +6258,7 @@ public void updateDrawState(@NonNull TextPaint ds) { rippleAbove.getBackground().setAlpha(255); } frameLayout.removeView(themeSwitchImageView); + themeSwitchImageView = new ImageView(this); if (toDark) { frameLayout.addView(themeSwitchImageView, 0, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); themeSwitchSunView.setVisibility(View.GONE); @@ -6171,6 +6325,9 @@ public void onAnimationEnd(Animator animation) { boolean nightTheme = (Boolean) args[1]; int accentId = (Integer) args[3]; Runnable calcInBackgroundEnd = args.length > 7 ? (Runnable) args[7] : null; + if (actionBarLayout == null) { + return; + } actionBarLayout.animateThemedValues(theme, accentId, nightTheme, instant, calcInBackgroundEnd); if (AndroidUtilities.isTablet()) { layersActionBarLayout.animateThemedValues(theme, accentId, nightTheme, instant); @@ -6285,6 +6442,8 @@ public void onAnimationEnd(Animator animation) { if (args.length > 1 && !mainFragmentsStack.isEmpty()) { AlertsCreator.processError(currentAccount, (TLRPC.TL_error) args[2], mainFragmentsStack.get(mainFragmentsStack.size() - 1), (TLObject) args[1]); } + } else if (id == NotificationCenter.billingConfirmPurchaseError) { + AlertsCreator.processError(currentAccount, (TLRPC.TL_error) args[1], mainFragmentsStack.get(mainFragmentsStack.size() - 1), (TLObject) args[0]); } else if (id == NotificationCenter.stickersImportComplete) { MediaDataController.getInstance(account).toggleStickerSet(this, (TLObject) args[0], 2, !mainFragmentsStack.isEmpty() ? mainFragmentsStack.get(mainFragmentsStack.size() - 1) : null, false, true); } else if (id == NotificationCenter.newSuggestionsAvailable) { @@ -6415,6 +6574,10 @@ public void onAnimationEnd(Animator animation) { } else if (id == NotificationCenter.chatSwithcedToForum) { long chatId = (long) args[0]; ForumUtilities.switchAllFragmentsInStackToForum(chatId, actionBarLayout); + } else if (id == NotificationCenter.storiesEnabledUpdate) { + if (drawerLayoutAdapter != null) { + drawerLayoutAdapter.notifyDataSetChanged(); + } } } @@ -6468,21 +6631,22 @@ private void showVoiceChatTooltip(int action) { } TLRPC.Chat chat = voIPService.getChat(); BaseFragment fragment = actionBarLayout.getFragmentStack().get(actionBarLayout.getFragmentStack().size() - 1); + UndoView undoView = null; if (fragment instanceof ChatActivity) { ChatActivity chatActivity = (ChatActivity) fragment; if (chatActivity.getDialogId() == -chat.id) { chat = null; } - UndoView undoView = chatActivity.getUndoView(); - if (undoView != null) { - undoView.showWithAction(0, action, chat); - } + undoView = chatActivity.getUndoView(); } else if (fragment instanceof DialogsActivity) { DialogsActivity dialogsActivity = (DialogsActivity) fragment; - dialogsActivity.getUndoView().showWithAction(0, action, chat); + undoView = dialogsActivity.getUndoView(); } else if (fragment instanceof ProfileActivity) { ProfileActivity profileActivity = (ProfileActivity) fragment; - profileActivity.getUndoView().showWithAction(0, action, chat); + undoView = profileActivity.getUndoView(); + } + if (undoView != null) { + undoView.showWithAction(0, action, chat); } if (action == UndoView.ACTION_VOIP_CAN_NOW_SPEAK && VoIPService.getSharedInstance() != null) { @@ -7058,6 +7222,13 @@ public boolean onPreIme() { @Override public boolean dispatchKeyEvent(KeyEvent event) { int keyCode = event.getKeyCode(); + if (event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP || event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_DOWN) { + BaseFragment baseFragment = getLastFragment(); + if (baseFragment != null && baseFragment.storyViewer != null && baseFragment.storyViewer.isShown()) { + baseFragment.storyViewer.dispatchKeyEvent(event); + return true; + } + } if (event.getAction() == KeyEvent.ACTION_DOWN && (event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP || event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_DOWN)) { if (VoIPService.getSharedInstance() != null) { if (Build.VERSION.SDK_INT >= 32) { @@ -7229,8 +7400,8 @@ public boolean needPresentFragment(INavigationLayout layout, INavigationLayout.N } } else { boolean allow = true; // TODO: Make it a flag inside fragment itself, maybe BaseFragment#isDrawerOpenAllowed()? - if (fragment instanceof LoginActivity || fragment instanceof IntroActivity) { - if (mainFragmentsStack.size() == 0 || mainFragmentsStack.get(0) instanceof IntroActivity) { + if (fragment instanceof LoginActivity || fragment instanceof IntroActivity || fragment instanceof ProxyListActivity) { + if (mainFragmentsStack.size() == 0 || mainFragmentsStack.get(0) instanceof IntroActivity || mainFragmentsStack.get(0) instanceof LoginActivity) { allow = false; } } else if (fragment instanceof CountrySelectActivity) { @@ -7246,7 +7417,7 @@ public boolean needPresentFragment(INavigationLayout layout, INavigationLayout.N @Override public boolean needAddFragmentToStack(BaseFragment fragment, INavigationLayout layout) { if (AndroidUtilities.isTablet()) { - drawerLayoutContainer.setAllowOpenDrawer(!(fragment instanceof LoginActivity || fragment instanceof IntroActivity || fragment instanceof CountrySelectActivity) && layersActionBarLayout.getView().getVisibility() != View.VISIBLE, true); + drawerLayoutContainer.setAllowOpenDrawer(!(fragment instanceof LoginActivity || fragment instanceof IntroActivity || fragment instanceof CountrySelectActivity || fragment instanceof ProxyListActivity) && layersActionBarLayout.getView().getVisibility() != View.VISIBLE, true); if (fragment instanceof DialogsActivity) { DialogsActivity dialogsActivity = (DialogsActivity) fragment; if (dialogsActivity.isMainDialogList() && layout != actionBarLayout) { @@ -7312,7 +7483,7 @@ public boolean needAddFragmentToStack(BaseFragment fragment, INavigationLayout l } } else { boolean allow = true; - if (fragment instanceof LoginActivity || fragment instanceof IntroActivity) { + if (fragment instanceof LoginActivity || fragment instanceof IntroActivity || fragment instanceof ProxyListActivity) { if (mainFragmentsStack.size() == 0 || mainFragmentsStack.get(0) instanceof IntroActivity) { allow = false; } @@ -7381,4 +7552,171 @@ public static BaseFragment getLastFragment() { return null; } + //work faster that window.setNavigationBarColor + public void requestCustomNavigationBar() { + if (customNavigationBar == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + customNavigationBar = drawerLayoutContainer.createNavigationBar(); + if (customNavigationBar != null) { + FrameLayout decorView = (FrameLayout) getWindow().getDecorView(); + decorView.addView(customNavigationBar); + if (customNavigationBar.getLayoutParams().height != AndroidUtilities.navigationBarHeight || ((FrameLayout.LayoutParams)customNavigationBar.getLayoutParams()).topMargin != customNavigationBar.getHeight()) { + customNavigationBar.getLayoutParams().height = AndroidUtilities.navigationBarHeight; + ((FrameLayout.LayoutParams)customNavigationBar.getLayoutParams()).topMargin = drawerLayoutContainer.getMeasuredHeight(); + customNavigationBar.requestLayout(); + } + } + } + } + + public void setNavigationBarColor(int color, boolean checkButtons) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + final Window window = getWindow(); + if (customNavigationBar != null) { + if (drawerLayoutContainer.getNavigationBarColor() != color) { + drawerLayoutContainer.setNavigationBarColor(color); + if (checkButtons) { + final float brightness = AndroidUtilities.computePerceivedBrightness(color); + AndroidUtilities.setLightNavigationBar(window, brightness >= 0.721f); + } + } + } else { + if (window.getNavigationBarColor() != color) { + window.setNavigationBarColor(color); + if (checkButtons) { + final float brightness = AndroidUtilities.computePerceivedBrightness(color); + AndroidUtilities.setLightNavigationBar(window, brightness >= 0.721f); + } + } + } + } + } + + public void setLightNavigationBar(boolean lightNavigationBar) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + final Window window = getWindow(); + AndroidUtilities.setLightNavigationBar(window, lightNavigationBar); + } + } + + public boolean isLightNavigationBar() { + return AndroidUtilities.getLightNavigationBar(getWindow()); + } + + private void openStories(long[] dialogIds, boolean requestWhenNeeded) { + boolean onlyArchived = true; + for (int i = 0; i < dialogIds.length; ++i) { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogIds[i]); + if (user != null && !user.stories_hidden) { + onlyArchived = false; + break; + } + } + NotificationsController.getInstance(currentAccount).processIgnoreStories(); + List fragments = actionBarLayout.getFragmentStack(); + DialogsActivity dialogsActivity = null; + for (int i = fragments.size() - 1; i >= 0; --i) { + BaseFragment fragment = fragments.get(i); + if (fragment instanceof DialogsActivity && (!((DialogsActivity) fragment).isArchive() || onlyArchived) && ((DialogsActivity) fragment).getType() == DialogsActivity.DIALOGS_TYPE_DEFAULT) { + dialogsActivity = (DialogsActivity) fragment; + break; + } else { + fragment.removeSelfFromStack(true); + } + } + if (dialogsActivity != null) { + if (drawerLayoutContainer != null) { + drawerLayoutContainer.closeDrawer(true); + } + if (onlyArchived) { + MessagesController.getInstance(dialogsActivity.getCurrentAccount()).getStoriesController().loadHiddenStories(); + } else { + MessagesController.getInstance(dialogsActivity.getCurrentAccount()).getStoriesController().loadStories(); + } + if (dialogsActivity.rightSlidingDialogContainer.hasFragment()) { + dialogsActivity.rightSlidingDialogContainer.finishPreview(); + } + if (onlyArchived && !dialogsActivity.isArchive()) { + Bundle args = new Bundle(); + args.putInt("folderId", 1); + presentFragment(dialogsActivity = new DialogsActivity(args)); + } + final DialogsActivity dialogsActivity1 = dialogsActivity; + dialogsActivity1.scrollToTop(false, false); + AndroidUtilities.runOnUIThread(() -> { + dialogsActivity1.scrollToTop(true, true); + }, 500); + return; + } + + BaseFragment lastFragment = getLastFragment(); + if (lastFragment == null) { + return; + } + StoriesController storiesController = MessagesController.getInstance(currentAccount).getStoriesController(); + ArrayList stories = new ArrayList<>(storiesController.getDialogListStories()); + stories.addAll(storiesController.getHiddenList()); + ArrayList peerIds = new ArrayList<>(); + ArrayList toLoadPeerIds = new ArrayList<>(); + if (requestWhenNeeded) { + for (int i = 0; i < dialogIds.length; ++i) { + toLoadPeerIds.add(dialogIds[i]); + } + } else { + for (int i = 0; i < dialogIds.length; ++i) { + peerIds.add(dialogIds[i]); + } + } + if (!toLoadPeerIds.isEmpty() && requestWhenNeeded) { + final MessagesController messagesController = MessagesController.getInstance(currentAccount); + final int[] loaded = new int[] { toLoadPeerIds.size() }; + final Runnable whenDone = () -> { + loaded[0]--; + if (loaded[0] == 0) { + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.storiesUpdated); + openStories(dialogIds, false); + } + }; + for (int i = 0; i < toLoadPeerIds.size(); ++i) { + long did = toLoadPeerIds.get(i); + TLRPC.TL_stories_getUserStories req = new TLRPC.TL_stories_getUserStories(); + req.user_id = messagesController.getInputUser(did); + if (req.user_id instanceof TLRPC.TL_inputUserEmpty) { + loaded[0]--; + continue; + } + if (req.user_id == null) { + loaded[0]--; + continue; + } + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + if (res instanceof TLRPC.TL_stories_userStories) { + TLRPC.TL_stories_userStories r = (TLRPC.TL_stories_userStories) res; + messagesController.putUsers(r.users, false); + messagesController.getStoriesController().putStories(did, r.stories); + whenDone.run(); + } else { + whenDone.run(); + } + })); + } + } else { + long me = UserConfig.getInstance(currentAccount).getClientUserId(); + for (int i = 0; i < stories.size(); ++i) { + TLRPC.TL_userStories userStories = stories.get(i); + if (userStories.user_id != me && !peerIds.contains(userStories.user_id) && storiesController.hasUnreadStories(userStories.user_id)) { + peerIds.add(userStories.user_id); + } + } + if (!peerIds.isEmpty()) { + StoryViewer.PlaceProvider placeProvider = null; + if (lastFragment instanceof DialogsActivity) { + try { + placeProvider = StoriesListPlaceProvider.of(((DialogsActivity) lastFragment).dialogStoriesCell.recyclerListView); + } catch (Exception ignore) {} + } + lastFragment.getOrCreateStoryViewer().instantClose(); + lastFragment.getOrCreateStoryViewer().open(this, null, peerIds, 0, null, null, placeProvider, false); + } + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LinkEditActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LinkEditActivity.java index 4afb14a467..57265ba63f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LinkEditActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LinkEditActivity.java @@ -316,7 +316,7 @@ protected void onDraw(Canvas canvas) { } TextInfoPrivacyCell hintCell = new TextInfoPrivacyCell(context); - hintCell.setBackground(Theme.getThemedDrawable(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + hintCell.setBackground(Theme.getThemedDrawableByKey(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); if (hasApproveCell) { hintCell.setText(LocaleController.getString("ApproveNewMembersDescription", R.string.ApproveNewMembersDescription)); } @@ -452,7 +452,7 @@ public void afterTextChanged(Editable s) { linearLayout.addView(nameEditText, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 50)); dividerName = new TextInfoPrivacyCell(context); - dividerName.setBackground(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + dividerName.setBackground(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); dividerName.setText(LocaleController.getString("LinkNameHelp", R.string.LinkNameHelp)); linearLayout.addView(dividerName); @@ -460,7 +460,7 @@ public void afterTextChanged(Editable s) { revokeLink = new TextSettingsCell(context); revokeLink.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); revokeLink.setText(LocaleController.getString("RevokeLink", R.string.RevokeLink), false); - revokeLink.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText5)); + revokeLink.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); revokeLink.setOnClickListener(view -> { AlertDialog.Builder builder2 = new AlertDialog.Builder(getParentActivity()); builder2.setMessage(LocaleController.getString("RevokeAlert", R.string.RevokeAlert)); @@ -491,8 +491,8 @@ public void afterTextChanged(Editable s) { buttonTextView.setOnClickListener(this::onCreateClicked); buttonTextView.setTextColor(Theme.getColor(Theme.key_featuredStickers_buttonText)); - dividerUses.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); - divider.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + dividerUses.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + divider.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); buttonTextView.setBackgroundDrawable(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(6), Theme.getColor(Theme.key_featuredStickers_addButton), Theme.getColor(Theme.key_featuredStickers_addButtonPressed))); usesEditText.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); @@ -794,7 +794,7 @@ private void setUsesVisible(boolean isVisible) { usesChooseView.setVisibility(isVisible ? View.VISIBLE : View.GONE); usesEditText.setVisibility(isVisible ? View.VISIBLE : View.GONE); dividerUses.setVisibility(isVisible ? View.VISIBLE : View.GONE); - divider.setBackground(Theme.getThemedDrawable(getParentActivity(), isVisible ? R.drawable.greydivider : R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + divider.setBackground(Theme.getThemedDrawableByKey(getParentActivity(), isVisible ? R.drawable.greydivider : R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } public interface Callback { @@ -820,8 +820,8 @@ public ArrayList getThemeDescriptions() { ThemeDescription.ThemeDescriptionDelegate descriptionDelegate = () -> { if (dividerUses != null) { Context context = dividerUses.getContext(); - dividerUses.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); - divider.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + dividerUses.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + divider.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); buttonTextView.setBackgroundDrawable(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(6), Theme.getColor(Theme.key_featuredStickers_addButton), Theme.getColor(Theme.key_featuredStickers_addButtonPressed))); usesEditText.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); @@ -831,11 +831,11 @@ public ArrayList getThemeDescriptions() { timeEditText.setHintTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText)); buttonTextView.setTextColor(Theme.getColor(Theme.key_featuredStickers_buttonText)); if (revokeLink != null) { - revokeLink.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText5)); + revokeLink.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); } createTextView.setTextColor(Theme.getColor(Theme.key_actionBarDefaultTitle)); - dividerName.setBackground(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + dividerName.setBackground(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); nameEditText.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); nameEditText.setHintTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText)); } @@ -868,7 +868,7 @@ public ArrayList getThemeDescriptions() { themeDescriptions.add(new ThemeDescription(null, 0, null, null, null, descriptionDelegate, Theme.key_windowBackgroundWhiteBlackText)); themeDescriptions.add(new ThemeDescription(null, 0, null, null, null, descriptionDelegate, Theme.key_windowBackgroundWhiteGrayText)); themeDescriptions.add(new ThemeDescription(null, 0, null, null, null, descriptionDelegate, Theme.key_featuredStickers_buttonText)); - themeDescriptions.add(new ThemeDescription(null, 0, null, null, null, descriptionDelegate, Theme.key_windowBackgroundWhiteRedText5)); + themeDescriptions.add(new ThemeDescription(null, 0, null, null, null, descriptionDelegate, Theme.key_text_RedRegular)); return themeDescriptions; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LiteModeSettingsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LiteModeSettingsActivity.java index 0285ce26ef..1764383f93 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LiteModeSettingsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LiteModeSettingsActivity.java @@ -177,6 +177,43 @@ private int getExpandedIndex(int flags) { return -1; } + public void setExpanded(int flags, boolean expand) { + int i = getExpandedIndex(flags); + if (i == -1) { + return; + } + expanded[i] = expand; + updateValues(); + updateItems(); + } + + public void scrollToType(int type) { + for (int i = 0; i < items.size(); i++) { + Item item = items.get(i); + if (item.type == type) { + highlightRow(i); + break; + } + } + } + + public void scrollToFlags(int flags) { + for (int i = 0; i < items.size(); i++) { + Item item = items.get(i); + if (item.flags == flags) { + highlightRow(i); + break; + } + } + } + + private void highlightRow(int index) { + RecyclerListView.IntReturnCallback callback = () -> { + layoutManager.scrollToPositionWithOffset(index, AndroidUtilities.dp(60)); + return index; + }; + listView.highlightRow(callback); + } private ArrayList oldItems = new ArrayList<>(); private ArrayList items = new ArrayList<>(); @@ -285,7 +322,7 @@ private void updateValues() { private static final int VIEW_TYPE_CHECKBOX = 4; private static final int VIEW_TYPE_SWITCH2 = 5; - private static final int SWITCH_TYPE_SMOOTH_TRANSITIONS = 0; + public static final int SWITCH_TYPE_SMOOTH_TRANSITIONS = 1; private class Adapter extends AdapterWithDiffUtils { @@ -354,11 +391,11 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi boolean top = position > 0 && items.get(position - 1).viewType != VIEW_TYPE_INFO; boolean bottom = position + 1 < items.size() && items.get(position + 1).viewType != VIEW_TYPE_INFO; if (top && bottom) { - textInfoPrivacyCell.setBackground(Theme.getThemedDrawable(getContext(), R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + textInfoPrivacyCell.setBackground(Theme.getThemedDrawableByKey(getContext(), R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); } else if (top) { - textInfoPrivacyCell.setBackground(Theme.getThemedDrawable(getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + textInfoPrivacyCell.setBackground(Theme.getThemedDrawableByKey(getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } else if (bottom) { - textInfoPrivacyCell.setBackground(Theme.getThemedDrawable(getContext(), R.drawable.greydivider_top, Theme.key_windowBackgroundGrayShadow)); + textInfoPrivacyCell.setBackground(Theme.getThemedDrawableByKey(getContext(), R.drawable.greydivider_top, Theme.key_windowBackgroundGrayShadow)); } else { textInfoPrivacyCell.setBackground(null); } @@ -1016,6 +1053,6 @@ public void onFragmentDestroy() { super.onFragmentDestroy(); LiteMode.savePreference(); AnimatedEmojiDrawable.updateAll(); - Theme.reloadWallpaper(); + Theme.reloadWallpaper(true); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LocationActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LocationActivity.java index 25bf447de3..3a04731309 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LocationActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LocationActivity.java @@ -186,6 +186,7 @@ public class LocationActivity extends BaseFragment implements NotificationCenter private ArrayList markers = new ArrayList<>(); private LongSparseArray markersMap = new LongSparseArray<>(); + private long selectedMarkerId = -1; private ArrayList placeMarkers = new ArrayList<>(); @@ -984,6 +985,8 @@ protected void onDirectionClick() { openDirections(null); } + private boolean firstSet = true; + @Override public void setLiveLocations(ArrayList liveLocations) { if (messageObject != null && messageObject.isLiveLocation()) { @@ -996,6 +999,10 @@ public void setLiveLocations(ArrayList liveLocations) { } } } + if (firstSet && otherPeopleLocations == 1) { + selectedMarkerId = liveLocations.get(0).id; + } + firstSet = false; otherItem.setVisibility(otherPeopleLocations == 1 ? View.VISIBLE : View.GONE); } super.setLiveLocations(liveLocations); @@ -1067,6 +1074,7 @@ public void dismiss() { return false; }); listView.setOnItemClickListener((view, position) -> { + selectedMarkerId = -1; if (locationType == LOCATION_TYPE_GROUP) { if (position == 1) { TLRPC.TL_messageMediaVenue venue = (TLRPC.TL_messageMediaVenue) adapter.getItem(position); @@ -1147,6 +1155,7 @@ public void dismiss() { } } else if (object instanceof LiveLocation) { LiveLocation liveLocation = (LiveLocation) object; + selectedMarkerId = liveLocation.id; map.animateCamera(ApplicationLoader.getMapsProvider().newCameraUpdateLatLngZoom(liveLocation.marker.getPosition(), map.getMaxZoomLevel() - 4)); } } @@ -1740,6 +1749,9 @@ private LiveLocation addUserMarker(TLRPC.Message message) { } else { liveLocation.object = message; liveLocation.marker.setPosition(latLng); + if (selectedMarkerId == liveLocation.id) { + map.animateCamera(ApplicationLoader.getMapsProvider().newCameraUpdateLatLng(liveLocation.marker.getPosition())); + } } if (proximitySheet != null) { proximitySheet.updateText(true, true); @@ -1835,6 +1847,8 @@ private void onMapInit() { showSearchPlacesButton(true); removeInfoView(); + selectedMarkerId = -1; + if (!scrolling && (locationType == LOCATION_TYPE_SEND || locationType == LOCATION_TYPE_SEND_WITH_LIVE) && listView.getChildCount() > 0) { View view = listView.getChildAt(0); if (view != null) { @@ -1867,6 +1881,14 @@ private void onMapInit() { locationButton.setTag(Theme.key_location_actionIcon); userLocationMoved = true; } + for (int i = 0; i < markers.size(); ++i) { + LiveLocation loc = markers.get(i); + if (loc != null && loc.marker == marker) { + selectedMarkerId = loc.id; + map.animateCamera(ApplicationLoader.getMapsProvider().newCameraUpdateLatLngZoom(loc.marker.getPosition(), map.getMaxZoomLevel() - 4)); + break; + } + } overlayView.addInfoView(marker); return true; }); @@ -2212,6 +2234,9 @@ private void positionMarker(Location location) { if (liveLocation.directionMarker != null) { liveLocation.directionMarker.setPosition(latLng); } + if (selectedMarkerId == liveLocation.id) { + map.animateCamera(ApplicationLoader.getMapsProvider().newCameraUpdateLatLng(liveLocation.marker.getPosition())); + } } if (messageObject == null && chatLocation == null && map != null) { IMapsProvider.LatLng latLng = new IMapsProvider.LatLng(location.getLatitude(), location.getLongitude()); @@ -2542,6 +2567,9 @@ public void didReceivedNotification(int id, int account, Object... args) { liveLocation.object = messageObject.messageOwner; IMapsProvider.LatLng latLng = new IMapsProvider.LatLng(messageObject.messageOwner.media.geo.lat, messageObject.messageOwner.media.geo._long); liveLocation.marker.setPosition(latLng); + if (selectedMarkerId == liveLocation.id) { + map.animateCamera(ApplicationLoader.getMapsProvider().newCameraUpdateLatLng(liveLocation.marker.getPosition())); + } if (liveLocation.directionMarker != null) { IMapsProvider.LatLng oldLocation = liveLocation.directionMarker.getPosition(); liveLocation.directionMarker.setPosition(latLng); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java index 6a8d7ec050..1cbb643293 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java @@ -93,10 +93,12 @@ //import com.google.android.gms.auth.api.signin.GoogleSignInOptions; //import com.google.android.gms.common.api.ApiException; //import com.google.android.gms.safetynet.SafetyNet; +//import com.google.zxing.common.detector.MathUtils; import org.json.JSONException; import org.json.JSONObject; import org.telegram.PhoneFormat.PhoneFormat; +import org.telegram.messenger.AccountInstance; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.AuthTokensHelper; @@ -147,6 +149,7 @@ import org.telegram.ui.Components.LoadingDrawable; import org.telegram.ui.Components.LoginOrView; import org.telegram.ui.Components.OutlineTextContainerView; +import org.telegram.ui.Components.ProxyDrawable; import org.telegram.ui.Components.RLottieDrawable; import org.telegram.ui.Components.RLottieImageView; import org.telegram.ui.Components.RadialProgressView; @@ -320,6 +323,9 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No private ImageView backButtonView; private RadialProgressView radialProgressView; + private ImageView proxyButtonView; + private ProxyDrawable proxyDrawable; + // Open animation stuff private LinearLayout keyboardLinearLayout; private FrameLayout slideViewsContainer; @@ -484,6 +490,7 @@ public void onFragmentDestroy() { } SharedConfig.loginingAccount = -1; + getNotificationCenter().removeObserver(this, NotificationCenter.didUpdateConnectionState); } @Override @@ -525,6 +532,9 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { marginLayoutParams = (MarginLayoutParams) backButtonView.getLayoutParams(); marginLayoutParams.topMargin = AndroidUtilities.dp(16) + statusBarHeight; + marginLayoutParams = (MarginLayoutParams) proxyButtonView.getLayoutParams(); + marginLayoutParams.topMargin = AndroidUtilities.dp(16) + statusBarHeight; + marginLayoutParams = (MarginLayoutParams) radialProgressView.getLayoutParams(); marginLayoutParams.topMargin = AndroidUtilities.dp(16) + statusBarHeight; @@ -696,7 +706,7 @@ public void getOutline(View view, Outline outline) { menu.setSubMenuOpenSide(1); menu.setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_listSelector))); - menu.addSubItem(menu_proxy, R.drawable.proxy_on, LocaleController.getString("Proxy", R.string.Proxy)) + menu.addSubItem(menu_proxy, R.drawable.msg2_proxy_on, LocaleController.getString("Proxy", R.string.Proxy)) .setContentDescription(LocaleController.getString("Proxy", R.string.Proxy)); menu.addSubItem(menu_language, R.drawable.ic_translate, LocaleController.getString("Language", R.string.Language)) .setContentDescription(LocaleController.getString("Language", R.string.Language)); @@ -2006,7 +2016,7 @@ protected void onFocusChanged(boolean focused, int direction, Rect previouslyFoc codeField.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL); codeField.setImeOptions(EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NO_EXTRACT_UI); codeField.setBackground(null); -// codeField.setLineColors(getThemedColor(Theme.key_windowBackgroundWhiteInputField), getThemedColor(Theme.key_windowBackgroundWhiteInputFieldActivated), getThemedColor(Theme.key_windowBackgroundWhiteRedText3)); +// codeField.setLineColors(getThemedColor(Theme.key_windowBackgroundWhiteInputField), getThemedColor(Theme.key_windowBackgroundWhiteInputFieldActivated), getThemedColor(Theme.key_text_RedRegular)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { codeField.setShowSoftInputOnFocus(!(hasCustomKeyboard() && !isCustomKeyboardForceDisabled())); } @@ -2201,7 +2211,7 @@ protected void onFocusChanged(boolean focused, int direction, Rect previouslyFoc phoneField.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL); phoneField.setImeOptions(EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NO_EXTRACT_UI); phoneField.setBackground(null); -// phoneField.setLineColors(getThemedColor(Theme.key_windowBackgroundWhiteInputField), getThemedColor(Theme.key_windowBackgroundWhiteInputFieldActivated), getThemedColor(Theme.key_windowBackgroundWhiteRedText3)); +// phoneField.setLineColors(getThemedColor(Theme.key_windowBackgroundWhiteInputField), getThemedColor(Theme.key_windowBackgroundWhiteInputFieldActivated), getThemedColor(Theme.key_text_RedRegular)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { phoneField.setShowSoftInputOnFocus(!(hasCustomKeyboard() && !isCustomKeyboardForceDisabled())); } @@ -2669,7 +2679,10 @@ private void invalidateCountryHint() { String hint = phoneFormatMap.get(code).get(index); int ss = phoneField.getSelectionStart(), se = phoneField.getSelectionEnd(); phoneField.setHintText(hint != null ? hint.replace('X', '0') : null); - phoneField.setSelection(ss, se); + phoneField.setSelection( + Math.max(0, Math.min(phoneField.length(), ss)), + Math.max(0, Math.min(phoneField.length(), se)) + ); wasCountryHintIndex = index; } } else if (wasCountryHintIndex != -1) { @@ -3914,14 +3927,16 @@ public void updateColors() { codeFieldContainer.invalidate(); } - String timeTextColorTag = (String) timeText.getTag(); - if (timeTextColorTag == null) timeTextColorTag = Theme.key_windowBackgroundWhiteGrayText6; + Integer timeTextColorTag = (Integer) timeText.getTag(); + if (timeTextColorTag == null) { + timeTextColorTag = Theme.key_windowBackgroundWhiteGrayText6; + } timeText.setTextColor(Theme.getColor(timeTextColorTag)); if (currentType != AUTH_TYPE_FRAGMENT_SMS) { problemText.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlueText4)); } - wrongCode.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + wrongCode.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } private void applyLottieColors(RLottieDrawable drawable) { @@ -5025,6 +5040,9 @@ public LoginActivityPasswordView(Context context) { needHideProgress(false); if (error == null) { final TLRPC.TL_auth_passwordRecovery res = (TLRPC.TL_auth_passwordRecovery) response; + if (getParentActivity() == null) { + return; + } AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); String rawPattern = res.email_pattern; @@ -6008,7 +6026,7 @@ public void updateColors() { resendCodeView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlueText4)); cantAccessEmailView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlueText4)); emailResetInView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText6)); - wrongCodeView.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + wrongCodeView.setTextColor(Theme.getColor(Theme.key_text_RedBold)); codeFieldContainer.invalidate(); } @@ -7931,6 +7949,10 @@ private void updateColors() { backButtonView.setColorFilter(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); backButtonView.setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_listSelector))); + proxyDrawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText), PorterDuff.Mode.SRC_IN)); + proxyDrawable.setColorKey(Theme.key_windowBackgroundWhiteBlackText); + proxyButtonView.setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_listSelector))); + radialProgressView.setProgressColor(Theme.getColor(Theme.key_chats_actionBackground)); floatingButtonIcon.setColor(Theme.getColor(Theme.key_chats_actionIcon)); @@ -7953,8 +7975,8 @@ public ArrayList getThemeDescriptions() { return SimpleThemeDescription.createThemeDescriptions(this::updateColors, Theme.key_windowBackgroundWhiteBlackText, Theme.key_windowBackgroundWhiteGrayText6, Theme.key_windowBackgroundWhiteHintText, Theme.key_listSelector, Theme.key_chats_actionBackground, Theme.key_chats_actionIcon, Theme.key_windowBackgroundWhiteInputField, Theme.key_windowBackgroundWhiteInputFieldActivated, Theme.key_windowBackgroundWhiteValueText, - Theme.key_dialogTextRed, Theme.key_windowBackgroundWhiteGrayText, Theme.key_checkbox, Theme.key_windowBackgroundWhiteBlueText4, - Theme.key_changephoneinfo_image2, Theme.key_chats_actionPressedBackground, Theme.key_windowBackgroundWhiteRedText2, Theme.key_windowBackgroundWhiteLinkText, + Theme.key_text_RedBold, Theme.key_windowBackgroundWhiteGrayText, Theme.key_checkbox, Theme.key_windowBackgroundWhiteBlueText4, + Theme.key_changephoneinfo_image2, Theme.key_chats_actionPressedBackground, Theme.key_text_RedRegular, Theme.key_windowBackgroundWhiteLinkText, Theme.key_checkboxSquareUnchecked, Theme.key_checkboxSquareBackground, Theme.key_checkboxSquareCheck, Theme.key_dialogBackground, Theme.key_dialogTextGray2, Theme.key_dialogTextBlack); } @@ -8277,6 +8299,7 @@ public void didReceivedNotification(int id, int account, Object... args) { public boolean onFragmentCreate() { SharedConfig.loginingAccount = currentAccount; ApplicationLoader.loadAccount(currentAccount); + getNotificationCenter().addObserver(this, NotificationCenter.didUpdateConnectionState); return true; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LogoutActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LogoutActivity.java index 72da07dea9..570adaadbb 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LogoutActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LogoutActivity.java @@ -155,7 +155,7 @@ public static AlertDialog makeLogOutDialog(Context context, int currentAccount) AlertDialog alertDialog = builder.create(); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } return alertDialog; } @@ -214,7 +214,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { case 3: { TextSettingsCell view = (TextSettingsCell) holder.itemView; if (position == logoutRow) { - view.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText5)); + view.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); view.setText(LocaleController.getString("LogOutTitle", R.string.LogOutTitle), false); } break; @@ -263,7 +263,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType case 4: default: { view = new TextInfoPrivacyCell(mContext); - view.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + view.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); break; } } @@ -306,7 +306,7 @@ public ArrayList getThemeDescriptions() { themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{ShadowSectionCell.class}, null, null, null, Theme.key_windowBackgroundGrayShadow)); - themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{TextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteRedText5)); + themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{TextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_text_RedRegular)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{HeaderCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueHeader)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ManageLinksActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ManageLinksActivity.java index 4d290c5fc6..4bf6412eb3 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ManageLinksActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ManageLinksActivity.java @@ -32,6 +32,7 @@ import androidx.recyclerview.widget.RecyclerView; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.ChatObject; import org.telegram.messenger.ContactsController; @@ -743,7 +744,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType case 0: default: view = new HintInnerCell(mContext); - view.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundWhite)); + view.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundWhite)); break; case 1: view = new HeaderCell(mContext, 23); @@ -787,19 +788,19 @@ public void showUsersForPermanentLink() { break; case 7: view = new ShadowSectionCell(mContext); - view.setBackground(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + view.setBackground(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); break; case 8: TextSettingsCell revokeAll = new TextSettingsCell(mContext); revokeAll.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); revokeAll.setText(LocaleController.getString("DeleteAllRevokedLinks", R.string.DeleteAllRevokedLinks), false); - revokeAll.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText5)); + revokeAll.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); view = revokeAll; break; case 9: TextInfoPrivacyCell cell = new TextInfoPrivacyCell(mContext); cell.setText(LocaleController.getString("CreateNewLinkHelp", R.string.CreateNewLinkHelp)); - cell.setBackground(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + cell.setBackground(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); view = cell; break; case 10: @@ -1125,7 +1126,7 @@ public LinkCell(@NonNull Context context) { AlertDialog alert = builder.create(); builder.show(); if (redLastItem) { - alert.setItemColor(items.size() - 1, Theme.getColor(Theme.key_dialogTextRed), Theme.getColor(Theme.key_dialogRedIcon)); + alert.setItemColor(items.size() - 1, Theme.getColor(Theme.key_text_RedBold), Theme.getColor(Theme.key_text_RedRegular)); } }); optionsView.setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_listSelector), 1)); @@ -1685,7 +1686,7 @@ public boolean needDelayOpenAnimation() { return true; } - int animationIndex = -1; + AnimationNotificationsLocker notificationsLocker = new AnimationNotificationsLocker(); @Override public void onTransitionAnimationEnd(boolean isOpen, boolean backward) { @@ -1696,12 +1697,12 @@ public void onTransitionAnimationEnd(boolean isOpen, boolean backward) { inviteLinkBottomSheet.show(); } } - NotificationCenter.getInstance(currentAccount).onAnimationFinish(animationIndex); + notificationsLocker.unlock(); } @Override public void onTransitionAnimationStart(boolean isOpen, boolean backward) { super.onTransitionAnimationStart(isOpen, backward); - animationIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(animationIndex, null); + notificationsLocker.lock(); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/MessageSeenView.java b/TMessagesProj/src/main/java/org/telegram/ui/MessageSeenView.java index 5e33ed9312..1b579e56c8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/MessageSeenView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/MessageSeenView.java @@ -42,6 +42,7 @@ import org.telegram.messenger.UserConfig; import org.telegram.messenger.UserObject; import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.SimpleTextView; import org.telegram.ui.ActionBar.Theme; @@ -63,7 +64,7 @@ public class MessageSeenView extends FrameLayout { ArrayList peerIds = new ArrayList<>(); ArrayList dates = new ArrayList<>(); - public ArrayList users = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); AvatarsImageView avatarsImageView; SimpleTextView titleView; ImageView iconView; @@ -77,7 +78,7 @@ public MessageSeenView(@NonNull Context context, int currentAccount, MessageObje this.currentAccount = currentAccount; isVoice = (messageObject.isRoundVideo() || messageObject.isVoice()); flickerLoadingView = new FlickerLoadingView(context); - flickerLoadingView.setColors(Theme.key_actionBarDefaultSubmenuBackground, Theme.key_listSelector, null); + flickerLoadingView.setColors(Theme.key_actionBarDefaultSubmenuBackground, Theme.key_listSelector, -1); flickerLoadingView.setViewType(FlickerLoadingView.MESSAGE_SEEN_TYPE); flickerLoadingView.setIsSingleCell(false); addView(flickerLoadingView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT)); @@ -117,7 +118,8 @@ public MessageSeenView(@NonNull Context context, int currentAccount, MessageObje if (error == null) { TLRPC.Vector vector = (TLRPC.Vector) response; ArrayList unknownUsers = new ArrayList<>(); - HashMap usersLocal = new HashMap<>(); + ArrayList unknownChats = new ArrayList<>(); + HashMap usersLocal = new HashMap<>(); ArrayList> allPeers = new ArrayList<>(); for (int i = 0, n = vector.objects.size(); i < n; i++) { Object object = vector.objects.get(i); @@ -139,12 +141,22 @@ public MessageSeenView(@NonNull Context context, int currentAccount, MessageObje if (finalFromId == peerId) { continue; } - TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(peerId); - allPeers.add(new Pair<>(peerId, 0)); - if (true || user == null) { - unknownUsers.add(peerId); + if (peerId > 0) { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(peerId); + allPeers.add(new Pair<>(peerId, 0)); + if (true || user == null) { + unknownUsers.add(peerId); + } else { + usersLocal.put(peerId, user); + } } else { - usersLocal.put(peerId, user); + TLRPC.Chat chat1 = MessagesController.getInstance(currentAccount).getChat(-peerId); + allPeers.add(new Pair<>(peerId, 0)); + if (true || chat1 == null) { + unknownChats.add(peerId); + } else { + usersLocal.put(peerId, chat1); + } } } } @@ -266,7 +278,7 @@ private void updateView() { avatarsImageView.commitTransition(false); if (peerIds.size() == 1 && users.get(0) != null) { - titleView.setText(ContactsController.formatName(users.get(0).first_name, users.get(0).last_name)); + titleView.setText(ContactsController.formatName(users.get(0))); } else { if (peerIds.size() == 0) { titleView.setText(LocaleController.getString("NobodyViewed", R.string.NobodyViewed)); @@ -351,7 +363,7 @@ private static class UserCell extends FrameLayout implements NotificationCenter. AvatarDrawable avatarDrawable = new AvatarDrawable(); AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable rightDrawable; - TLRPC.User user; + TLObject object; private static MessageSeenCheckDrawable seenDrawable = new MessageSeenCheckDrawable(R.drawable.msg_mini_checks, Theme.key_windowBackgroundWhiteGrayText); @@ -359,18 +371,16 @@ public UserCell(Context context) { super(context); avatarImageView = new BackupImageView(context); avatarImageView.setRoundRadius(AndroidUtilities.dp(18)); - addView(avatarImageView, LayoutHelper.createFrame(34, 34, Gravity.CENTER_VERTICAL, 10f, 0, 0, 0)); nameView = new SimpleTextView(context); nameView.setTextSize(16); - nameView.setEllipsizeByGradient(true); + nameView.setEllipsizeByGradient(!LocaleController.isRTL); nameView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); nameView.setTextColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuItem)); - addView(nameView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 55, 6.33f, 8, 0)); + nameView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); rightDrawable = new AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable(this, AndroidUtilities.dp(18)); nameView.setDrawablePadding(AndroidUtilities.dp(3)); - nameView.setRightDrawable(rightDrawable); readView = new TextView(context); readView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); @@ -378,7 +388,17 @@ public UserCell(Context context) { readView.setEllipsize(TextUtils.TruncateAt.END); readView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); readView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText)); - addView(readView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 55, 20, 13, 0)); + readView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); + + if (LocaleController.isRTL) { + addView(avatarImageView, LayoutHelper.createFrame(34, 34, Gravity.RIGHT | Gravity.CENTER_VERTICAL, 0, 0, 10, 0)); + addView(nameView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.RIGHT | Gravity.TOP, 8, 6.33f, 55, 0)); + addView(readView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.RIGHT | Gravity.TOP, 13, 20, 55, 0)); + } else { + addView(avatarImageView, LayoutHelper.createFrame(34, 34, Gravity.LEFT | Gravity.CENTER_VERTICAL, 10f, 0, 0, 0)); + addView(nameView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 55, 6.33f, 8, 0)); + addView(readView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 55, 20, 13, 0)); + } } @Override @@ -386,22 +406,22 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(50), View.MeasureSpec.EXACTLY)); } - public void setUser(TLRPC.User user, int date) { - this.user = user; + public void setUser(TLObject object, int date) { + this.object = object; updateStatus(false); - if (user != null) { - avatarDrawable.setInfo(user); - ImageLocation imageLocation = ImageLocation.getForUser(user, ImageLocation.TYPE_SMALL); - avatarImageView.setImage(imageLocation, "50_50", avatarDrawable, user); - nameView.setText(ContactsController.formatName(user.first_name, user.last_name)); + if (object != null) { + avatarDrawable.setInfo(object); + ImageLocation imageLocation = ImageLocation.getForUserOrChat(object, ImageLocation.TYPE_SMALL); + avatarImageView.setImage(imageLocation, "50_50", avatarDrawable, object); + nameView.setText(ContactsController.formatName(object)); } if (date <= 0) { readView.setVisibility(GONE); nameView.setTranslationY(AndroidUtilities.dp(9)); } else { - readView.setText(TextUtils.concat(seenDrawable.getSpanned(getContext()), LocaleController.formatSeenDate(date))); + readView.setText(TextUtils.concat(seenDrawable.getSpanned(getContext(), null), LocaleController.formatSeenDate(date))); readView.setVisibility(VISIBLE); nameView.setTranslationY(0); } @@ -421,18 +441,25 @@ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { public void didReceivedNotification(int id, int account, Object... args) { if (id == NotificationCenter.userEmojiStatusUpdated) { TLRPC.User user = (TLRPC.User) args[0]; - if (this.user != null && user != null && this.user.id == user.id) { - this.user = user; + TLRPC.User currentUser = object instanceof TLRPC.User ? (TLRPC.User) object : null; + if (currentUser != null && user != null && currentUser.id == user.id) { + this.object = user; updateStatus(true); } } } private void updateStatus(boolean animated) { - Long documentId = UserObject.getEmojiStatusDocumentId(user); + TLRPC.User currentUser = object instanceof TLRPC.User ? (TLRPC.User) object : null; + if (currentUser == null) { + return; + } + Long documentId = UserObject.getEmojiStatusDocumentId(currentUser); if (documentId == null) { + nameView.setRightDrawable(null); rightDrawable.set((Drawable) null, animated); } else { + nameView.setRightDrawable(rightDrawable); rightDrawable.set(documentId, animated); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/MessageStatisticActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/MessageStatisticActivity.java index 51ed78aeca..72f551ccbb 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/MessageStatisticActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/MessageStatisticActivity.java @@ -776,7 +776,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } break; case 1: - holder.itemView.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + holder.itemView.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); break; case 2: HeaderCell headerCell = (HeaderCell) holder.itemView; @@ -969,7 +969,7 @@ private void recolorRecyclerItem(View child) { ((StatisticActivity.BaseChartCell) child).recolor(); child.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); } else if (child instanceof ShadowSectionCell) { - Drawable shadowDrawable = Theme.getThemedDrawable(ApplicationLoader.applicationContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow); + Drawable shadowDrawable = Theme.getThemedDrawableByKey(ApplicationLoader.applicationContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow); Drawable background = new ColorDrawable(Theme.getColor(Theme.key_windowBackgroundGray)); CombinedDrawable combinedDrawable = new CombinedDrawable(background, shadowDrawable, 0, 0); combinedDrawable.setFullsize(true); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/NotificationsCustomSettingsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/NotificationsCustomSettingsActivity.java index 07ed8f7706..d7b32bbf14 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/NotificationsCustomSettingsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/NotificationsCustomSettingsActivity.java @@ -8,6 +8,11 @@ package org.telegram.ui; +import static org.telegram.messenger.NotificationsController.TYPE_CHANNEL; +import static org.telegram.messenger.NotificationsController.TYPE_GROUP; +import static org.telegram.messenger.NotificationsController.TYPE_PRIVATE; +import static org.telegram.messenger.NotificationsController.TYPE_STORIES; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; @@ -16,6 +21,7 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; +import android.graphics.Canvas; import android.media.Ringtone; import android.media.RingtoneManager; import android.net.Uri; @@ -26,10 +32,12 @@ import android.util.LongSparseArray; import android.view.View; import android.view.ViewGroup; +import android.view.animation.OvershootInterpolator; import android.widget.EditText; import android.widget.FrameLayout; import android.widget.TextView; +import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -40,6 +48,7 @@ import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MediaDataController; import org.telegram.messenger.MessagesController; import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.NotificationsController; @@ -63,18 +72,27 @@ import org.telegram.ui.Cells.TextCell; import org.telegram.ui.Cells.TextCheckCell; import org.telegram.ui.Cells.TextColorCell; +import org.telegram.ui.Cells.TextInfoPrivacyCell; import org.telegram.ui.Cells.TextSettingsCell; import org.telegram.ui.Cells.UserCell; import org.telegram.ui.Components.AlertsCreator; import org.telegram.ui.Components.BulletinFactory; import org.telegram.ui.Components.ChatNotificationsPopupWrapper; +import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.EmptyTextProgressView; +import org.telegram.ui.Components.ItemOptions; import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.ListView.AdapterWithDiffUtils; import org.telegram.ui.Components.RecyclerListView; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; import java.util.Map; +import java.util.Objects; public class NotificationsCustomSettingsActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { @@ -89,41 +107,47 @@ public class NotificationsCustomSettingsActivity extends BaseFragment implements private final static int search_button = 0; - private int alertRow; - private int alertSection2Row; - private int messageSectionRow; - private int previewRow; - private int messageVibrateRow; - private int messageSoundRow; - private int messageLedRow; - private int messagePopupNotificationRow; - private int messagePriorityRow; - private int groupSection2Row; - private int exceptionsAddRow; - private int exceptionsStartRow; - private int exceptionsEndRow; - private int exceptionsSection2Row; - private int deleteAllRow; - private int deleteAllSectionRow; - private int rowCount = 0; + private int exceptionsStart, exceptionsEnd; + + private boolean showAutoExceptions = true; + private Boolean storiesEnabled; + private boolean storiesAuto; private int currentType; + private ArrayList autoExceptions; private ArrayList exceptions; private HashMap exceptionsDict = new HashMap<>(); + public void toggleShowAutoExceptions() { + if (listView == null || adapter == null) { + return; + } + showAutoExceptions = !showAutoExceptions; + updateRows(true); + } + int topicId = 0; - public NotificationsCustomSettingsActivity(int type, ArrayList notificationExceptions) { - this(type, notificationExceptions, false); + public NotificationsCustomSettingsActivity(int type, ArrayList notificationExceptions, ArrayList autoNotificationExceptions) { + this(type, notificationExceptions, autoNotificationExceptions, false); } - public NotificationsCustomSettingsActivity(int type, ArrayList notificationExceptions, boolean load) { + public NotificationsCustomSettingsActivity(int type, ArrayList notificationExceptions, ArrayList autoNotificationExceptions, boolean load) { super(); currentType = type; + autoExceptions = autoNotificationExceptions; exceptions = notificationExceptions; - for (int a = 0, N = exceptions.size(); a < N; a++) { - NotificationsSettingsActivity.NotificationException exception = exceptions.get(a); - exceptionsDict.put(exception.did, exception); + if (exceptions != null) { + for (int a = 0, N = exceptions.size(); a < N; a++) { + NotificationsSettingsActivity.NotificationException exception = exceptions.get(a); + exceptionsDict.put(exception.did, exception); + } + } + if (autoExceptions != null) { + for (int a = 0, N = autoExceptions.size(); a < N; a++) { + NotificationsSettingsActivity.NotificationException exception = autoExceptions.get(a); + exceptionsDict.put(exception.did, exception); + } } if (load) { loadExceptions(); @@ -132,10 +156,178 @@ public NotificationsCustomSettingsActivity(int type, ArrayList topPeers = new ArrayList<>(MediaDataController.getInstance(currentAccount).hints); + Collections.sort(topPeers, Comparator.comparingDouble(a -> a.rating)); + int index = -1; + for (int i = 0; i < topPeers.size(); ++i) { + long did2 = DialogObject.getPeerDialogId(topPeers.get(i).peer); + if (did2 == did) { + index = i; + } + } + return index >= 0 && index >= topPeers.size() - 5; + } + + public static boolean areStoriesNotMuted(int currentAccount, long did) { + SharedPreferences prefs = MessagesController.getNotificationsSettings(currentAccount); + if (prefs.contains("stories_" + did)) { + return prefs.getBoolean("stories_" + did, true); + } + if (prefs.contains("EnableAllStories")) { + return prefs.getBoolean("EnableAllStories", true); + } + return isTop5Peer(currentAccount, did); + } + + private void deleteException(NotificationsSettingsActivity.NotificationException exception, View view, int position) { + final String key = NotificationsController.getSharedPrefKey(exception.did, 0); + final SharedPreferences prefs = getNotificationsSettings(); + prefs.edit().remove("stories_" + key).commit(); + if (autoExceptions != null) { + autoExceptions.remove(exception); + } + if (exceptions != null) { + exceptions.remove(exception); + } + if (isTop5Peer(currentAccount, exception.did)) { + exception.auto = true; + exception.notify = 0; + autoExceptions.add(exception); + } + if (view instanceof UserCell) { + ((UserCell) view).setException(exception, null, ((UserCell) view).needDivider); + } + getNotificationsController().updateServerNotificationsSettings(exception.did, 0, false); + updateRows(true); + } + + private void updateMute(NotificationsSettingsActivity.NotificationException exception, View view, int position, boolean isNew, boolean mute) { + final String key = NotificationsController.getSharedPrefKey(exception.did, 0); + final SharedPreferences prefs = getNotificationsSettings(); + final SharedPreferences.Editor edit = prefs.edit(); + + boolean isTopPeer = isTop5Peer(currentAccount, exception.did); + exception.notify = mute ? Integer.MAX_VALUE : 0; + if (exception.auto) { + exception.auto = false; + edit.putBoolean("stories_" + key, !mute).commit(); + if (autoExceptions != null) { + autoExceptions.remove(exception); + } + if (exceptions == null) { + exceptions = new ArrayList<>(); + } + exceptions.add(0, exception); + // autoExceptions -> exceptions + // auto = false + // (un)mute + } else if (isTopPeer) { + edit.putBoolean("stories_" + key, !mute).commit(); + } else if (mute ? (storiesEnabled == null || !storiesEnabled) : (storiesEnabled != null && storiesEnabled)) { + deleteException(exception, view, position); + return; + } else { + edit.putBoolean("stories_" + key, !mute).commit(); + } + + if (view instanceof UserCell) { + ((UserCell) view).setException(exception, null, ((UserCell) view).needDivider); + } + getNotificationsController().updateServerNotificationsSettings(exception.did, 0, false); + updateRows(true); + } + + private int getLedColor() { + int color = 0xff0000ff; + switch (currentType) { + case TYPE_PRIVATE: color = getNotificationsSettings().getInt("MessagesLed", color); break; + case TYPE_GROUP: color = getNotificationsSettings().getInt("GroupLed", color); break; + case TYPE_STORIES: color = getNotificationsSettings().getInt("StoriesLed", color); break; + case TYPE_CHANNEL: color = getNotificationsSettings().getInt("ChannelLed", color); break; + } + for (int a = 0; a < 9; a++) { + if (TextColorCell.colorsToSave[a] == color) { + color = TextColorCell.colors[a]; + break; + } + } + return color; + } + + private String getPopupOption() { + int option = 0; + switch (currentType) { + case TYPE_PRIVATE: option = getNotificationsSettings().getInt("popupAll", 0); break; + case TYPE_GROUP: option = getNotificationsSettings().getInt("popupGroup", 0); break; + case TYPE_CHANNEL: option = getNotificationsSettings().getInt("popupChannel", 0); break; + } + return LocaleController.getString(popupOptions[Utilities.clamp(option, popupOptions.length - 1, 0)]); + } + + private String getSound() { + final SharedPreferences prefs = getNotificationsSettings(); + String value = LocaleController.getString("SoundDefault", R.string.SoundDefault); + long documentId; + switch (currentType) { + case TYPE_PRIVATE: + value = prefs.getString("GlobalSound", value); + documentId = prefs.getLong("GlobalSoundDocId", 0); + break; + case TYPE_GROUP: + value = prefs.getString("GroupSound", value); + documentId = prefs.getLong("GroupSoundDocId", 0); + break; + case TYPE_STORIES: + value = prefs.getString("StoriesSound", value); + documentId = prefs.getLong("StoriesSoundDocId", 0); + break; + case TYPE_CHANNEL: + default: + value = prefs.getString("ChannelSound", value); + documentId = prefs.getLong("ChannelDocId", 0); + } + if (documentId != 0) { + TLRPC.Document document = getMediaDataController().ringtoneDataStore.getDocument(documentId); + if (document == null) { + return LocaleController.getString("CustomSound", R.string.CustomSound); + } else { + return NotificationsSoundActivity.trimTitle(document, FileLoader.getDocumentFileName(document)); + } + } else if (value.equals("NoSound")) { + return LocaleController.getString("NoSound", R.string.NoSound); + } else if (value.equals("Default")) { + return LocaleController.getString("SoundDefault", R.string.SoundDefault); + } + return value; + } + + private String getPriorityOption() { + int option = 1; + switch (currentType) { + case TYPE_PRIVATE: option = getNotificationsSettings().getInt("priority_messages", 1); break; + case TYPE_GROUP: option = getNotificationsSettings().getInt("priority_group", 1); break; + case TYPE_STORIES: option = getNotificationsSettings().getInt("priority_stories", 1); break; + case TYPE_CHANNEL: option = getNotificationsSettings().getInt("priority_channel", 1); break; + } + return LocaleController.getString(priorityOptions[Utilities.clamp(option, priorityOptions.length - 1, 0)]); + } + @Override public View createView(Context context) { searching = false; @@ -213,7 +405,16 @@ public void onTextChanged(EditText editText) { emptyView.showTextView(); frameLayout.addView(emptyView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); - listView = new RecyclerListView(context); + listView = new RecyclerListView(context) { + @Override + protected void dispatchDraw(Canvas canvas) { + if (currentType != -1) { + drawSectionBackground(canvas, exceptionsStart, exceptionsEnd, getThemedColor(Theme.key_windowBackgroundWhite)); + } + super.dispatchDraw(canvas); + } + }; + listView.setTranslateSelector(true); listView.setEmptyView(emptyView); listView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)); listView.setVerticalScrollBarEnabled(false); @@ -224,7 +425,78 @@ public void onTextChanged(EditText editText) { if (getParentActivity() == null) { return; } - if (listView.getAdapter() == searchAdapter || position >= exceptionsStartRow && position < exceptionsEndRow) { + ItemInner item = null; + if (listView.getAdapter() == adapter && position >= 0 && position < items.size()) { + item = items.get(position); + } + if (currentType == TYPE_STORIES && item != null && item.exception != null) { + NotificationsSettingsActivity.NotificationException exception = item.exception; + ItemOptions.makeOptions(NotificationsCustomSettingsActivity.this, view) + .addIf(exception.notify <= 0 || exception.auto, R.drawable.msg_mute, LocaleController.getString(R.string.NotificationsStoryMute), false, () -> { + updateMute(exception, view, position, false, true); + }) + .addIf(exception.notify > 0 || exception.auto, R.drawable.msg_unmute, LocaleController.getString(R.string.NotificationsStoryUnmute), false, () -> { + updateMute(exception, view, position, false, false); + }) + .addIf(!exception.auto, R.drawable.msg_delete, LocaleController.getString("DeleteException", R.string.DeleteException), true, () -> { + deleteException(exception, view, position); + }) + .show(); + return; + } + if (currentType == TYPE_STORIES && listView.getAdapter() == searchAdapter) { + NotificationsSettingsActivity.NotificationException exception; + boolean newException; + Object object = searchAdapter.getObject(position); + if (object instanceof NotificationsSettingsActivity.NotificationException) { + exception = (NotificationsSettingsActivity.NotificationException) object; + newException = false; + } else { + long did; + if (object instanceof TLRPC.User) { + TLRPC.User user = (TLRPC.User) object; + did = user.id; + } else { + TLRPC.Chat chat = (TLRPC.Chat) object; + did = -chat.id; + } + if (exceptionsDict.containsKey(did)) { + exception = exceptionsDict.get(did); + newException = false; + } else { + newException = true; + exception = new NotificationsSettingsActivity.NotificationException(); + exception.story = true; + exception.did = did; + if (object instanceof TLRPC.User) { + TLRPC.User user = (TLRPC.User) object; + exception.did = user.id; + } else { + TLRPC.Chat chat = (TLRPC.Chat) object; + exception.did = -chat.id; + } + } + } + if (exception == null) { + return; + } + + ItemOptions.makeOptions(NotificationsCustomSettingsActivity.this, view) + .addIf(exception.notify <= 0 || exception.auto, R.drawable.msg_mute, LocaleController.getString(R.string.NotificationsStoryMute), false, () -> { + actionBar.closeSearchField(); + updateMute(exception, view, -1, newException, true); + }) + .addIf(exception.notify > 0 || exception.auto, R.drawable.msg_unmute, LocaleController.getString(R.string.NotificationsStoryUnmute), false, () -> { + actionBar.closeSearchField(); + updateMute(exception, view, -1, newException, false); + }) + .addIf(!newException && !exception.auto, R.drawable.msg_delete, LocaleController.getString("DeleteException", R.string.DeleteException), true, () -> { + deleteException(exception, view, position); + }) + .show(); + return; + } + if (listView.getAdapter() == searchAdapter || item != null && item.exception != null) { ArrayList arrayList; NotificationsSettingsActivity.NotificationException exception; boolean newException; @@ -261,12 +533,11 @@ public void onTextChanged(EditText editText) { arrayList = exceptions; } } else { - arrayList = exceptions; - int index = position - exceptionsStartRow; - if (index < 0 || index >= arrayList.size()) { + if (item.exception.auto) { return; } - exception = arrayList.get(index); + arrayList = exceptions; + exception = item.exception; newException = false; } if (exception == null) { @@ -373,13 +644,7 @@ private void setDefault() { } arrayList.remove(exception); if (arrayList == exceptions) { - if (exceptionsAddRow != -1 && arrayList.isEmpty()) { - listView.getAdapter().notifyItemChanged(exceptionsAddRow); - listView.getAdapter().notifyItemRemoved(deleteAllRow); - listView.getAdapter().notifyItemRemoved(deleteAllSectionRow); - } - listView.getAdapter().notifyItemRemoved(position); - updateRows(false); + updateRows(true); checkRowsEnabled(); } else { updateRows(true); @@ -392,32 +657,67 @@ private void setDefault() { chatNotificationsPopupWrapper.showAsOptions(NotificationsCustomSettingsActivity.this, view, x, y); return; } - if (position == exceptionsAddRow) { + if (item == null) { + return; + } + + if (item.id == 6) { Bundle args = new Bundle(); args.putBoolean("onlySelect", true); args.putBoolean("checkCanWrite", false); - if (currentType == NotificationsController.TYPE_GROUP) { + if (currentType == TYPE_GROUP) { args.putInt("dialogsType", DialogsActivity.DIALOGS_TYPE_GROUPS_ONLY); - } else if (currentType == NotificationsController.TYPE_CHANNEL) { + } else if (currentType == TYPE_CHANNEL) { args.putInt("dialogsType", DialogsActivity.DIALOGS_TYPE_CHANNELS_ONLY); } else { args.putInt("dialogsType", DialogsActivity.DIALOGS_TYPE_USERS_ONLY); } DialogsActivity activity = new DialogsActivity(args); activity.setDelegate((fragment, dids, message, param, topicsFragment) -> { - Bundle args2 = new Bundle(); - args2.putLong("dialog_id", dids.get(0).dialogId); - args2.putBoolean("exception", true); - ProfileNotificationsActivity profileNotificationsActivity = new ProfileNotificationsActivity(args2, getResourceProvider()); - profileNotificationsActivity.setDelegate(exception -> { - exceptions.add(0, exception); + long did = dids.get(0).dialogId; + if (currentType == TYPE_STORIES) { + if (autoExceptions != null) { + Iterator i = autoExceptions.iterator(); + while (i.hasNext()) { + NotificationsSettingsActivity.NotificationException n = i.next(); + if (n.did == did) { + i.remove(); + } + } + } + if (exceptions != null) { + Iterator i = exceptions.iterator(); + while (i.hasNext()) { + NotificationsSettingsActivity.NotificationException n = i.next(); + if (n.did == did) { + i.remove(); + } + } + } + NotificationsSettingsActivity.NotificationException n = new NotificationsSettingsActivity.NotificationException(); + n.did = did; + n.story = true; + n.notify = storiesEnabled != null && storiesEnabled ? Integer.MAX_VALUE : 0; + if (exceptions == null) { + exceptions = new ArrayList<>(); + } + exceptions.add(n); updateRows(true); - }); - presentFragment(profileNotificationsActivity, true); + } else { + Bundle args2 = new Bundle(); + args2.putLong("dialog_id", did); + args2.putBoolean("exception", true); + ProfileNotificationsActivity profileNotificationsActivity = new ProfileNotificationsActivity(args2, getResourceProvider()); + profileNotificationsActivity.setDelegate(exception -> { + exceptions.add(0, exception); + updateRows(true); + }); + presentFragment(profileNotificationsActivity, true); + } return true; }); presentFragment(activity); - } else if (position == deleteAllRow) { + } else if (item.id == 7) { AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); builder.setTitle(LocaleController.getString("NotificationsDeleteAllExceptionTitle", R.string.NotificationsDeleteAllExceptionTitle)); builder.setMessage(LocaleController.getString("NotificationsDeleteAllExceptionAlert", R.string.NotificationsDeleteAllExceptionAlert)); @@ -426,7 +726,11 @@ private void setDefault() { SharedPreferences.Editor editor = preferences.edit(); for (int a = 0, N = exceptions.size(); a < N; a++) { NotificationsSettingsActivity.NotificationException exception = exceptions.get(a); - editor.remove("notify2_" + exception.did).remove("custom_" + exception.did); + if (currentType == TYPE_STORIES) { + editor.remove("stories_" + exception.did); + } else { + editor.remove("notify2_" + exception.did).remove("custom_" + exception.did); + } getMessagesStorage().setDialogFlags(exception.did, 0); TLRPC.Dialog dialog = getMessagesController().dialogs_dict.get(exception.did); if (dialog != null) { @@ -449,13 +753,49 @@ private void setDefault() { showDialog(alertDialog); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } - } else if (position == alertRow) { + } else if (item.viewType == VIEW_TYPE_CHECK2) { enabled = getNotificationsController().isGlobalNotificationsEnabled(currentType); - NotificationsCheckCell checkCell = (NotificationsCheckCell) view; RecyclerView.ViewHolder holder = listView.findViewHolderForAdapterPosition(position); + + if (currentType == TYPE_STORIES) { + SharedPreferences preferences = getNotificationsSettings(); + SharedPreferences.Editor editor = preferences.edit(); + enabled = storiesEnabled != null && storiesEnabled; + if (storiesAuto && enabled) { + editor.remove("EnableAllStories"); + storiesEnabled = null; + } else { + editor.putBoolean("EnableAllStories", !enabled); + storiesEnabled = !enabled; + } + editor.commit(); + getNotificationsController().updateServerNotificationsSettings(currentType); + checkCell.setChecked(!enabled); + if (holder != null) { + adapter.onBindViewHolder(holder, position); + } + for (int i = 0; i < items.size(); ++i) { + ItemInner item2 = items.get(i); + if (item2.id == 5) { + item2.checked = storiesAuto && (storiesEnabled == null || !storiesEnabled); + RecyclerView.ViewHolder holder2 = listView.findViewHolderForAdapterPosition(i); + if (holder2 != null && holder2.itemView instanceof TextCheckCell) { + TextCheckCell checkCell1 = (TextCheckCell) holder2.itemView; + checkCell1.setChecked(item2.checked); + } + break; + } + } + if (showAutoExceptions != (storiesEnabled == null)) { + toggleShowAutoExceptions(); + } + checkRowsEnabled(); + return; + } + if (!enabled) { getNotificationsController().setGlobalNotificationsEnabled(currentType, 0); checkCell.setChecked(true); @@ -464,12 +804,12 @@ private void setDefault() { } checkRowsEnabled(); } else { - AlertsCreator.showCustomNotificationsDialog(NotificationsCustomSettingsActivity.this, 0, 0, currentType, exceptions, currentAccount, param -> { + AlertsCreator.showCustomNotificationsDialog(NotificationsCustomSettingsActivity.this, 0, 0, currentType, exceptions, autoExceptions, currentAccount, param -> { int offUntil; SharedPreferences preferences = getNotificationsSettings(); - if (currentType == NotificationsController.TYPE_PRIVATE) { + if (currentType == TYPE_PRIVATE) { offUntil = preferences.getInt("EnableAll2", 0); - } else if (currentType == NotificationsController.TYPE_GROUP) { + } else if (currentType == TYPE_GROUP) { offUntil = preferences.getInt("EnableGroup2", 0); } else { offUntil = preferences.getInt("EnableChannel2", 0); @@ -490,25 +830,7 @@ private void setDefault() { checkRowsEnabled(); }); } - } else if (position == previewRow) { - if (!view.isEnabled()) { - return; - } - SharedPreferences preferences = getNotificationsSettings(); - SharedPreferences.Editor editor = preferences.edit(); - if (currentType == NotificationsController.TYPE_PRIVATE) { - enabled = preferences.getBoolean("EnablePreviewAll", true); - editor.putBoolean("EnablePreviewAll", !enabled); - } else if (currentType == NotificationsController.TYPE_GROUP) { - enabled = preferences.getBoolean("EnablePreviewGroup", true); - editor.putBoolean("EnablePreviewGroup", !enabled); - } else { - enabled = preferences.getBoolean("EnablePreviewChannel", true); - editor.putBoolean("EnablePreviewChannel", !enabled); - } - editor.commit(); - getNotificationsController().updateServerNotificationsSettings(currentType); - } else if (position == messageSoundRow) { + } else if (item.id == 3) { if (!view.isEnabled()) { return; } @@ -516,95 +838,144 @@ private void setDefault() { Bundle bundle = new Bundle(); bundle.putInt("type", currentType); presentFragment(new NotificationsSoundActivity(bundle, getResourceProvider())); -// SharedPreferences preferences = getNotificationsSettings(); -// Intent tmpIntent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER); -// tmpIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_NOTIFICATION); -// tmpIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true); -// tmpIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true); -// tmpIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)); -// Uri currentSound = null; -// -// String defaultPath = null; -// Uri defaultUri = Settings.System.DEFAULT_NOTIFICATION_URI; -// if (defaultUri != null) { -// defaultPath = defaultUri.getPath(); -// } -// -// String path; -// if (currentType == NotificationsController.TYPE_PRIVATE) { -// path = preferences.getString("GlobalSoundPath", defaultPath); -// } else if (currentType == NotificationsController.TYPE_GROUP) { -// path = preferences.getString("GroupSoundPath", defaultPath); -// } else { -// path = preferences.getString("ChannelSoundPath", defaultPath); -// } -// -// if (path != null && !path.equals("NoSound")) { -// if (path.equals(defaultPath)) { -// currentSound = defaultUri; -// } else { -// currentSound = Uri.parse(path); -// } -// } -// -// tmpIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, currentSound); -// startActivityForResult(tmpIntent, position); } catch (Exception e) { FileLog.e(e); } - } else if (position == messageLedRow) { + } else if (item.viewType == VIEW_TYPE_COLOR) { if (!view.isEnabled()) { return; } showDialog(AlertsCreator.createColorSelectDialog(getParentActivity(), 0, 0, currentType, () -> { - RecyclerView.ViewHolder holder = listView.findViewHolderForAdapterPosition(position); - if (holder != null) { - adapter.onBindViewHolder(holder, position); + if (view instanceof TextColorCell) { + if (position >= 0 && position < items.size()) { + items.get(position).color = getLedColor(); + } + ((TextColorCell) view).setTextAndColor(LocaleController.getString("LedColor", R.string.LedColor), getLedColor(), true); + } else { + updateRows(true); } })); - } else if (position == messagePopupNotificationRow) { + } else if (item.id == 2) { if (!view.isEnabled()) { return; } showDialog(AlertsCreator.createPopupSelectDialog(getParentActivity(), currentType, () -> { - RecyclerView.ViewHolder holder = listView.findViewHolderForAdapterPosition(position); - if (holder != null) { - adapter.onBindViewHolder(holder, position); + if (view instanceof TextSettingsCell) { + if (position >= 0 && position < items.size()) { + items.get(position).text2 = getPopupOption(); + } + ((TextSettingsCell) view).setTextAndValue(LocaleController.getString("PopupNotification", R.string.PopupNotification), getPopupOption(), true, ((TextSettingsCell) view).needDivider); + } else { + updateRows(true); } })); - } else if (position == messageVibrateRow) { + } else if (item.id == 1) { if (!view.isEnabled()) { return; } - String key; - if (currentType == NotificationsController.TYPE_PRIVATE) { + final String key; + if (currentType == TYPE_PRIVATE) { key = "vibrate_messages"; - } else if (currentType == NotificationsController.TYPE_GROUP) { + } else if (currentType == TYPE_GROUP) { key = "vibrate_group"; + } else if (currentType == TYPE_STORIES) { + key = "vibrate_stories"; } else { key = "vibrate_channel"; } showDialog(AlertsCreator.createVibrationSelectDialog(getParentActivity(), 0, 0, key, () -> { - RecyclerView.ViewHolder holder = listView.findViewHolderForAdapterPosition(position); - if (holder != null) { - adapter.onBindViewHolder(holder, position); + if (view instanceof TextSettingsCell) { + String value = LocaleController.getString(vibrateLabels[Utilities.clamp(getNotificationsSettings().getInt(key, 0), vibrateLabels.length - 1, 0)]); + if (position >= 0 && position < items.size()) { + items.get(position).text2 = value; + } + ((TextSettingsCell) view).setTextAndValue(LocaleController.getString("Vibrate", R.string.Vibrate), value, true, true); + } else { + updateRows(true); } })); - } else if (position == messagePriorityRow) { + } else if (item.id == 4) { if (!view.isEnabled()) { return; } showDialog(AlertsCreator.createPrioritySelectDialog(getParentActivity(), 0, 0, currentType, () -> { - RecyclerView.ViewHolder holder = listView.findViewHolderForAdapterPosition(position); - if (holder != null) { - adapter.onBindViewHolder(holder, position); + if (view instanceof TextSettingsCell) { + if (position >= 0 && position < items.size()) { + items.get(position).text2 = getPriorityOption(); + } + ((TextSettingsCell) view).setTextAndValue(LocaleController.getString("NotificationsImportance", R.string.NotificationsImportance), getPriorityOption(), true, ((TextSettingsCell) view).needDivider); + } else { + updateRows(true); } })); - } - if (view instanceof TextCheckCell) { - ((TextCheckCell) view).setChecked(!enabled); + } else if (item.id == 5) { + if (!view.isEnabled()) { + return; + } + SharedPreferences preferences = getNotificationsSettings(); + if (preferences.getBoolean("EnableAllStories", false)) { + return; + } + SharedPreferences.Editor editor = preferences.edit(); + if (storiesEnabled != null) { + editor.remove("EnableAllStories"); + storiesEnabled = null; + item.checked = storiesAuto = true; + } else { + editor.putBoolean("EnableAllStories", false); + storiesEnabled = false; + item.checked = storiesAuto = false; + } + if (view instanceof TextCheckCell) { + ((TextCheckCell) view).setChecked(storiesAuto); + } + editor.commit(); + if (storiesAuto != showAutoExceptions) { + toggleShowAutoExceptions(); + } + getNotificationsController().updateServerNotificationsSettings(currentType); + checkRowsEnabled(); + } else if (item.id == 0) { + if (!view.isEnabled()) { + return; + } + SharedPreferences preferences = getNotificationsSettings(); + SharedPreferences.Editor editor = preferences.edit(); + if (currentType == TYPE_PRIVATE) { + enabled = preferences.getBoolean("EnablePreviewAll", true); + editor.putBoolean("EnablePreviewAll", !enabled); + } else if (currentType == TYPE_GROUP) { + enabled = preferences.getBoolean("EnablePreviewGroup", true); + editor.putBoolean("EnablePreviewGroup", !enabled); + } else if (currentType == TYPE_STORIES) { + enabled = !preferences.getBoolean("EnableHideStoriesSenders", false); + editor.putBoolean("EnableHideStoriesSenders", enabled); + } else { + enabled = preferences.getBoolean("EnablePreviewChannel", true); + editor.putBoolean("EnablePreviewChannel", !enabled); + } + editor.commit(); + getNotificationsController().updateServerNotificationsSettings(currentType); + if (view instanceof TextCheckCell) { + ((TextCheckCell) view).setChecked(!enabled); + } } }); + DefaultItemAnimator itemAnimator = new DefaultItemAnimator() { + @Override + protected void onMoveAnimationUpdate(RecyclerView.ViewHolder holder) { + listView.invalidate(); + } + }; + itemAnimator.setAddDuration(150); + itemAnimator.setMoveDuration(350); + itemAnimator.setChangeDuration(0); + itemAnimator.setRemoveDuration(0); + itemAnimator.setDelayAnimations(false); + itemAnimator.setMoveInterpolator(new OvershootInterpolator(1.1f)); + itemAnimator.setTranslationInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + itemAnimator.setSupportsChangeAnimations(false); + listView.setItemAnimator(itemAnimator); listView.setOnScrollListener(new RecyclerView.OnScrollListener() { @Override @@ -624,21 +995,35 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { } private void checkRowsEnabled() { - if (!exceptions.isEmpty()) { + if (!exceptions.isEmpty() && currentType != TYPE_STORIES) { return; } int count = listView.getChildCount(); ArrayList animators = new ArrayList<>(); - boolean enabled = getNotificationsController().isGlobalNotificationsEnabled(currentType); + final boolean globalEnabled; + if (currentType == TYPE_STORIES) { + globalEnabled = storiesEnabled == null || storiesEnabled || exceptions != null && !exceptions.isEmpty(); + } else { + globalEnabled = getNotificationsController().isGlobalNotificationsEnabled(currentType); + } for (int a = 0; a < count; a++) { View child = listView.getChildAt(a); RecyclerListView.Holder holder = (RecyclerListView.Holder) listView.getChildViewHolder(child); + int position = listView.getChildAdapterPosition(child); + ItemInner item = null; + if (position >= 0 && position < items.size()) { + item = items.get(position); + } + final boolean enabled; + if (item != null && item.id == 5) { + enabled = storiesEnabled == null || !storiesEnabled; + } else { + enabled = globalEnabled; + } switch (holder.getItemViewType()) { case 0: { HeaderCell headerCell = (HeaderCell) holder.itemView; - if (holder.getAdapterPosition() == messageSectionRow) { - headerCell.setEnabled(enabled, animators); - } + headerCell.setEnabled(enabled, animators); break; } case 1: { @@ -678,9 +1063,18 @@ public void onAnimationEnd(Animator animator) { } private void loadExceptions() { + final ArrayList topPeers; + if (currentType == TYPE_STORIES) { + MediaDataController.getInstance(currentAccount).loadHints(true); + topPeers = new ArrayList<>(MediaDataController.getInstance(currentAccount).hints); + } else { + topPeers = null; + } getMessagesStorage().getStorageQueue().postRunnable(() -> { ArrayList usersResult = new ArrayList<>(); ArrayList chatsResult = new ArrayList<>(); + ArrayList storiesResult = new ArrayList<>(); + ArrayList storiesAutoResult = new ArrayList<>(); ArrayList channelsResult = new ArrayList<>(); LongSparseArray waitingForLoadExceptions = new LongSparseArray<>(); @@ -756,6 +1150,58 @@ private void loadExceptions() { } } } + final HashSet customStories = new HashSet<>(); + for (Map.Entry entry : values.entrySet()) { + String key = entry.getKey(); + if (key.startsWith("stories_")) { + key = key.substring(8); + try { + long did = Utilities.parseLong(key); + if (did != 0 && did != selfId) { + NotificationsSettingsActivity.NotificationException exception = new NotificationsSettingsActivity.NotificationException(); + exception.did = did; + exception.story = true; + exception.notify = ((Boolean) entry.getValue()) ? 0 : Integer.MAX_VALUE; + if (DialogObject.isUserDialog(did)) { + TLRPC.User user = getMessagesController().getUser(did); + if (user == null) { + usersToLoad.add(did); + waitingForLoadExceptions.put(did, exception); + } else if (user.deleted) { + continue; + } + storiesResult.add(exception); + customStories.add(did); + } + } + } catch (Exception ignore) {} + } + } + if (topPeers != null) { + Collections.sort(topPeers, Comparator.comparingDouble(a -> a.rating)); + for (int i = Math.max(0, topPeers.size() - 6); i < topPeers.size(); ++i) { + TLRPC.TL_topPeer topPeer = topPeers.get(i); + final long did = DialogObject.getPeerDialogId(topPeer.peer); + if (!customStories.contains(did)) { + NotificationsSettingsActivity.NotificationException exception = new NotificationsSettingsActivity.NotificationException(); + exception.did = did; + exception.story = true; + exception.notify = 0; + exception.auto = true; + if (DialogObject.isUserDialog(did)) { + TLRPC.User user = getMessagesController().getUser(did); + if (user == null) { + usersToLoad.add(did); + waitingForLoadExceptions.put(did, exception); + } else if (user.deleted) { + continue; + } + storiesAutoResult.add(0, exception); + customStories.add(did); + } + } + } + } if (waitingForLoadExceptions.size() != 0) { try { if (!encryptedChatsToLoad.isEmpty()) { @@ -811,10 +1257,13 @@ private void loadExceptions() { getMessagesController().putUsers(users, true); getMessagesController().putChats(chats, true); getMessagesController().putEncryptedChats(encryptedChats, true); - if (currentType == NotificationsController.TYPE_PRIVATE) { + if (currentType == TYPE_PRIVATE) { exceptions = usersResult; - } else if (currentType == NotificationsController.TYPE_GROUP) { + } else if (currentType == TYPE_GROUP) { exceptions = chatsResult; + } else if (currentType == TYPE_STORIES) { + exceptions = storiesResult; + autoExceptions = storiesAutoResult; } else { exceptions = channelsResult; } @@ -823,66 +1272,115 @@ private void loadExceptions() { }); } - private void updateRows(boolean notify) { - rowCount = 0; + private final int[] vibrateLabels = new int[] { + R.string.VibrationDefault, + R.string.Short, + R.string.VibrationDisabled, + R.string.Long, + R.string.OnlyIfSilent + }; + + private final int[] popupOptions = new int[] { + R.string.NoPopup, + R.string.OnlyWhenScreenOn, + R.string.OnlyWhenScreenOff, + R.string.AlwaysShowPopup + }; + + private final int[] priorityOptions = new int[] { + R.string.NotificationsPriorityHigh, + R.string.NotificationsPriorityUrgent, + R.string.NotificationsPriorityUrgent, + R.string.NotificationsPriorityMedium, + R.string.NotificationsPriorityLow, + R.string.NotificationsPriorityMedium + }; + + private void updateRows(boolean animated) { + oldItems.clear(); + oldItems.addAll(items); + items.clear(); + SharedPreferences prefs = getNotificationsSettings(); + boolean enabled = false; if (currentType != -1) { - alertRow = rowCount++; - alertSection2Row = rowCount++; - messageSectionRow = rowCount++; - previewRow = rowCount++; - messageLedRow = rowCount++; - messageVibrateRow = rowCount++; - if (currentType == NotificationsController.TYPE_CHANNEL) { - messagePopupNotificationRow = -1; + items.add(ItemInner.asCheck2()); + items.add(ItemInner.asShadow(null)); + items.add(ItemInner.asHeader(LocaleController.getString(R.string.SETTINGS))); + + if (currentType == TYPE_STORIES) { + items.add(ItemInner.asCheck(0, LocaleController.getString(R.string.NotificationShowSenderNames), !prefs.getBoolean("EnableHideStoriesSenders", false))); } else { - messagePopupNotificationRow = rowCount++; + switch (currentType) { + case TYPE_PRIVATE: enabled = prefs.getBoolean("EnablePreviewAll", true); break; + case TYPE_GROUP: enabled = prefs.getBoolean("EnablePreviewGroup", true); break; + case TYPE_CHANNEL: enabled = prefs.getBoolean("EnablePreviewChannel", true); break; + } + items.add(ItemInner.asCheck(0, LocaleController.getString(R.string.MessagePreview), enabled)); + } + + items.add(ItemInner.asColor(LocaleController.getString("LedColor", R.string.LedColor), getLedColor())); + + int vibrate = 0; + switch (currentType) { + case TYPE_PRIVATE: vibrate = prefs.getInt("vibrate_messages", 0); break; + case TYPE_GROUP: vibrate = prefs.getInt("vibrate_group", 0); break; + case TYPE_STORIES: vibrate = prefs.getInt("vibrate_stories", 0); break; + case TYPE_CHANNEL: vibrate = prefs.getInt("vibrate_channel", 0); break; + } + items.add(ItemInner.asSetting(1, LocaleController.getString("Vibrate", R.string.Vibrate), LocaleController.getString(vibrateLabels[Utilities.clamp(vibrate, vibrateLabels.length - 1, 0)]))); + + if (currentType == TYPE_PRIVATE || currentType == TYPE_GROUP) { + items.add(ItemInner.asSetting(2, LocaleController.getString("PopupNotification", R.string.PopupNotification), getPopupOption())); } - messageSoundRow = rowCount++; + + items.add(ItemInner.asSetting(3, LocaleController.getString("Sound", R.string.Sound), getSound())); + if (Build.VERSION.SDK_INT >= 21) { - messagePriorityRow = rowCount++; + items.add(ItemInner.asSetting(4, LocaleController.getString("NotificationsImportance", R.string.NotificationsImportance), getPriorityOption())); + } + + if (currentType == TYPE_STORIES) { + items.add(ItemInner.asCheck(5, LocaleController.getString(R.string.StoryAutoExceptions), storiesAuto && (storiesEnabled == null || !storiesEnabled))); + items.add(ItemInner.asShadow(LocaleController.getString(R.string.StoryAutoExceptionsInfo))); } else { - messagePriorityRow = -1; + items.add(ItemInner.asShadow(null)); } - groupSection2Row = rowCount++; - exceptionsAddRow = rowCount++; - } else { - alertRow = -1; - alertSection2Row = -1; - messageSectionRow = -1; - previewRow = -1; - messageLedRow = -1; - messageVibrateRow = -1; - messagePopupNotificationRow = -1; - messageSoundRow = -1; - messagePriorityRow = -1; - groupSection2Row = -1; - exceptionsAddRow = -1; + + items.add(ItemInner.asButton(6, R.drawable.msg_contact_add, LocaleController.getString("NotificationsAddAnException", R.string.NotificationsAddAnException))); } - if (exceptions != null && !exceptions.isEmpty()) { - exceptionsStartRow = rowCount; - rowCount += exceptions.size(); - exceptionsEndRow = rowCount; - } else { - exceptionsStartRow = -1; - exceptionsEndRow = -1; + exceptionsStart = items.size() - 1; + if (autoExceptions != null && showAutoExceptions) { + for (int i = 0; i < autoExceptions.size(); ++i) { + items.add(ItemInner.asException(autoExceptions.get(i))); + } } + if (exceptions != null) { + for (int i = 0; i < exceptions.size(); ++i) { + items.add(ItemInner.asException(exceptions.get(i))); + } + } + exceptionsEnd = items.size() - 1; if (currentType != -1 || exceptions != null && !exceptions.isEmpty()) { - exceptionsSection2Row = rowCount++; - } else { - exceptionsSection2Row = -1; + items.add(ItemInner.asShadow(null)); } if (exceptions != null && !exceptions.isEmpty()) { - deleteAllRow = rowCount++; - deleteAllSectionRow = rowCount++; - } else { - deleteAllRow = -1; - deleteAllSectionRow = -1; + items.add(ItemInner.asButton(7, 0, LocaleController.getString("NotificationsDeleteAllException", R.string.NotificationsDeleteAllException))); } - if (notify && adapter != null) { - adapter.notifyDataSetChanged(); + if (adapter != null) { + if (animated) { + adapter.setItems(oldItems, items); + } else { + adapter.notifyDataSetChanged(); + } } } + @Override + public void onBecomeFullyVisible() { + super.onBecomeFullyVisible(); + updateRows(true); + } + @Override public void onActivityResultFragment(int requestCode, int resultCode, Intent data) { if (resultCode == Activity.RESULT_OK) { @@ -903,7 +1401,7 @@ public void onActivityResultFragment(int requestCode, int resultCode, Intent dat SharedPreferences preferences = getNotificationsSettings(); SharedPreferences.Editor editor = preferences.edit(); - if (currentType == NotificationsController.TYPE_PRIVATE) { + if (currentType == TYPE_PRIVATE) { if (name != null && ringtone != null) { editor.putString("GlobalSound", name); editor.putString("GlobalSoundPath", ringtone.toString()); @@ -911,7 +1409,7 @@ public void onActivityResultFragment(int requestCode, int resultCode, Intent dat editor.putString("GlobalSound", "NoSound"); editor.putString("GlobalSoundPath", "NoSound"); } - } else if (currentType == NotificationsController.TYPE_GROUP) { + } else if (currentType == TYPE_GROUP) { if (name != null && ringtone != null) { editor.putString("GroupSound", name); editor.putString("GroupSoundPath", ringtone.toString()); @@ -919,7 +1417,7 @@ public void onActivityResultFragment(int requestCode, int resultCode, Intent dat editor.putString("GroupSound", "NoSound"); editor.putString("GroupSoundPath", "NoSound"); } - } else if (currentType == NotificationsController.TYPE_CHANNEL) { + } else if (currentType == TYPE_CHANNEL) { if (name != null && ringtone != null) { editor.putString("ChannelSound", name); editor.putString("ChannelSoundPath", ringtone.toString()); @@ -927,6 +1425,14 @@ public void onActivityResultFragment(int requestCode, int resultCode, Intent dat editor.putString("ChannelSound", "NoSound"); editor.putString("ChannelSoundPath", "NoSound"); } + } else if (currentType == TYPE_STORIES) { + if (name != null && ringtone != null) { + editor.putString("StoriesSound", name); + editor.putString("StoriesSoundPath", ringtone.toString()); + } else { + editor.putString("StoriesSound", "NoSound"); + editor.putString("StoriesSoundPath", "NoSound"); + } } getNotificationsController().deleteNotificationChannelGlobal(currentType); editor.commit(); @@ -945,12 +1451,14 @@ public void onResume() { adapter.notifyDataSetChanged(); } getNotificationCenter().addObserver(this, NotificationCenter.notificationsSettingsUpdated); + getNotificationCenter().addObserver(this, NotificationCenter.reloadHints); } @Override public void onPause() { super.onPause(); getNotificationCenter().removeObserver(this, NotificationCenter.notificationsSettingsUpdated); + getNotificationCenter().removeObserver(this, NotificationCenter.reloadHints); } @Override @@ -959,6 +1467,8 @@ public void didReceivedNotification(int id, int account, Object... args) { if (adapter != null) { adapter.notifyDataSetChanged(); } + } else if (id == NotificationCenter.reloadHints) { + loadExceptions(); } } @@ -990,7 +1500,7 @@ public void searchDialogs(final String query) { searchResult.clear(); searchResultNames.clear(); searchAdapterHelper.mergeResults(null); - searchAdapterHelper.queryServerSearch(null, true, currentType != NotificationsController.TYPE_PRIVATE, true, false, false, 0, false, 0, 0); + searchAdapterHelper.queryServerSearch(null, true, currentType != TYPE_PRIVATE && currentType != TYPE_STORIES, true, false, false, 0, false, 0, 0); notifyDataSetChanged(); } else { Utilities.searchQueue.postRunnable(searchRunnable = () -> processSearch(query), 300); @@ -999,7 +1509,7 @@ public void searchDialogs(final String query) { private void processSearch(final String query) { AndroidUtilities.runOnUIThread(() -> { - searchAdapterHelper.queryServerSearch(query, true, currentType != NotificationsController.TYPE_PRIVATE, true, false, false, 0, false, 0, 0); + searchAdapterHelper.queryServerSearch(query, true, currentType != TYPE_PRIVATE && currentType != TYPE_STORIES, true, false, false, 0, false, 0, 0); final ArrayList contactsCopy = new ArrayList<>(exceptions); Utilities.searchQueue.postRunnable(() -> { String search1 = query.trim().toLowerCase(); @@ -1188,7 +1698,87 @@ public int getItemViewType(int position) { } } - private class ListAdapter extends RecyclerListView.SelectionAdapter { + private static final int VIEW_TYPE_HEADER = 0; + private static final int VIEW_TYPE_CHECK = 1; + private static final int VIEW_TYPE_USER = 2; + private static final int VIEW_TYPE_COLOR = 3; + private static final int VIEW_TYPE_SHADOW = 4; + private static final int VIEW_TYPE_SETTING = 5; + private static final int VIEW_TYPE_CHECK2 = 6; + private static final int VIEW_TYPE_BUTTON = 7; + + private static class ItemInner extends AdapterWithDiffUtils.Item { + + public int id; + public int resId; + public CharSequence text, text2; + public NotificationsSettingsActivity.NotificationException exception; + public int color; + public boolean checked; + + private ItemInner(int viewType) { + super(viewType, true); + } + + public static ItemInner asHeader(CharSequence text) { + ItemInner item = new ItemInner(VIEW_TYPE_HEADER); + item.text = text; + return item; + } + public static ItemInner asCheck(int id, CharSequence text, boolean checked) { + ItemInner item = new ItemInner(VIEW_TYPE_CHECK); + item.id = id; + item.text = text; + item.checked = checked; + return item; + } + public static ItemInner asException(NotificationsSettingsActivity.NotificationException exception) { + ItemInner item = new ItemInner(VIEW_TYPE_USER); + item.exception = exception; + return item; + } + public static ItemInner asColor(CharSequence text, int color) { + ItemInner item = new ItemInner(VIEW_TYPE_COLOR); + item.text = text; + item.color = color; + return item; + } + public static ItemInner asShadow(CharSequence text) { + ItemInner item = new ItemInner(VIEW_TYPE_SHADOW); + item.text = text; + return item; + } + public static ItemInner asSetting(int id, CharSequence text, CharSequence value) { + ItemInner item = new ItemInner(VIEW_TYPE_SETTING); + item.id = id; + item.text = text; + item.text2 = value; + return item; + } + public static ItemInner asCheck2() { + return new ItemInner(VIEW_TYPE_CHECK2); + } + public static ItemInner asButton(int id, int resId, CharSequence text) { + ItemInner item = new ItemInner(VIEW_TYPE_BUTTON); + item.id = id; + item.resId = resId; + item.text = text; + return item; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ItemInner itemInner = (ItemInner) o; + return id == itemInner.id && resId == itemInner.resId && color == itemInner.color && checked == itemInner.checked && Objects.equals(text, itemInner.text) && Objects.equals(text2, itemInner.text2) && exception == itemInner.exception; + } + } + + private final ArrayList oldItems = new ArrayList<>(); + private final ArrayList items = new ArrayList<>(); + + private class ListAdapter extends AdapterWithDiffUtils { private Context mContext; @@ -1199,46 +1789,46 @@ public ListAdapter(Context context) { @Override public boolean isEnabled(RecyclerView.ViewHolder holder) { int type = holder.getItemViewType(); - return type != 0 && type != 4; + return type != VIEW_TYPE_HEADER && type != VIEW_TYPE_SHADOW; } @Override public int getItemCount() { - return rowCount; + return items.size(); } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view; switch (viewType) { - case 0: + case VIEW_TYPE_HEADER: view = new HeaderCell(mContext); view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); break; - case 1: + case VIEW_TYPE_CHECK: view = new TextCheckCell(mContext); view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); break; - case 2: + case VIEW_TYPE_USER: view = new UserCell(mContext, 6, 0, false); view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); break; - case 3: + case VIEW_TYPE_COLOR: view = new TextColorCell(mContext); view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); break; - case 4: - view = new ShadowSectionCell(mContext); + case VIEW_TYPE_SHADOW: + view = new TextInfoPrivacyCell(mContext); break; - case 5: + case VIEW_TYPE_SETTING: view = new TextSettingsCell(mContext); view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); break; - case 6: + case VIEW_TYPE_CHECK2: view = new NotificationsCheckCell(mContext); view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); break; - case 7: + case VIEW_TYPE_BUTTON: default: view = new TextCell(mContext); view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); @@ -1249,155 +1839,54 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + if (position < 0 || position >= items.size()) { + return; + } + final ItemInner item = items.get(position); + final boolean divider = position + 1 < items.size() && items.get(position + 1).viewType != VIEW_TYPE_SHADOW; switch (holder.getItemViewType()) { - case 0: { + case VIEW_TYPE_HEADER: { HeaderCell headerCell = (HeaderCell) holder.itemView; - if (position == messageSectionRow) { - headerCell.setText(LocaleController.getString("SETTINGS", R.string.SETTINGS)); - } + headerCell.setText(item.text); break; } - case 1: { + case VIEW_TYPE_CHECK: { TextCheckCell checkCell = (TextCheckCell) holder.itemView; - SharedPreferences preferences = getNotificationsSettings(); - if (position == previewRow) { - boolean enabled; - if (currentType == NotificationsController.TYPE_PRIVATE) { - enabled = preferences.getBoolean("EnablePreviewAll", true); - } else if (currentType == NotificationsController.TYPE_GROUP) { - enabled = preferences.getBoolean("EnablePreviewGroup", true); - } else { - enabled = preferences.getBoolean("EnablePreviewChannel", true); - } - checkCell.setTextAndCheck(LocaleController.getString("MessagePreview", R.string.MessagePreview), enabled, true); - } + checkCell.setTextAndCheck("" + item.text, item.checked, divider); break; } - case 2: { + case VIEW_TYPE_USER: { UserCell cell = (UserCell) holder.itemView; - NotificationsSettingsActivity.NotificationException exception = exceptions.get(position - exceptionsStartRow); - cell.setException(exception, null, position != exceptionsEndRow - 1); + cell.setException(item.exception, null, divider); break; } - case 3: { + case VIEW_TYPE_COLOR: { TextColorCell textColorCell = (TextColorCell) holder.itemView; - SharedPreferences preferences = getNotificationsSettings(); - int color; - if (currentType == NotificationsController.TYPE_PRIVATE) { - color = preferences.getInt("MessagesLed", 0xff0000ff); - } else if (currentType == NotificationsController.TYPE_GROUP) { - color = preferences.getInt("GroupLed", 0xff0000ff); - } else { - color = preferences.getInt("ChannelLed", 0xff0000ff); - } - for (int a = 0; a < 9; a++) { - if (TextColorCell.colorsToSave[a] == color) { - color = TextColorCell.colors[a]; - break; - } - } - textColorCell.setTextAndColor(LocaleController.getString("LedColor", R.string.LedColor), color, true); + textColorCell.setTextAndColor("" + item.text, item.color, divider); break; } - case 4: { - if (position == deleteAllSectionRow || position == groupSection2Row && exceptionsSection2Row == -1 || position == exceptionsSection2Row && deleteAllRow == -1) { - holder.itemView.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + case VIEW_TYPE_SHADOW: { + TextInfoPrivacyCell cell = (TextInfoPrivacyCell) holder.itemView; + if (item.text == null) { + cell.setFixedSize(12); + cell.setText(null); } else { - holder.itemView.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + cell.setFixedSize(0); + cell.setText(item.text); + } + if (!divider) { + holder.itemView.setBackground(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + } else { + holder.itemView.setBackground(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); } break; } - case 5: { + case VIEW_TYPE_SETTING: { TextSettingsCell textCell = (TextSettingsCell) holder.itemView; - SharedPreferences preferences = getNotificationsSettings(); - if (position == messageSoundRow) { - String value; - long documentId; - if (currentType == NotificationsController.TYPE_PRIVATE) { - value = preferences.getString("GlobalSound", LocaleController.getString("SoundDefault", R.string.SoundDefault)); - documentId = preferences.getLong("GlobalSoundDocId", 0); - } else if (currentType == NotificationsController.TYPE_GROUP) { - value = preferences.getString("GroupSound", LocaleController.getString("SoundDefault", R.string.SoundDefault)); - documentId = preferences.getLong("GroupSoundDocId", 0); - } else { - value = preferences.getString("ChannelSound", LocaleController.getString("SoundDefault", R.string.SoundDefault)); - documentId = preferences.getLong("ChannelDocId", 0); - } - if (documentId != 0) { - TLRPC.Document document = getMediaDataController().ringtoneDataStore.getDocument(documentId); - 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")) { - value = LocaleController.getString("SoundDefault", R.string.SoundDefault); - } - textCell.setTextAndValue(LocaleController.getString("Sound", R.string.Sound), value, true); - } else if (position == messageVibrateRow) { - int value; - if (currentType == NotificationsController.TYPE_PRIVATE) { - value = preferences.getInt("vibrate_messages", 0); - } else if (currentType == NotificationsController.TYPE_GROUP) { - value = preferences.getInt("vibrate_group", 0); - } else { - value = preferences.getInt("vibrate_channel", 0); - } - if (value == 0) { - textCell.setTextAndValue(LocaleController.getString("Vibrate", R.string.Vibrate), LocaleController.getString("VibrationDefault", R.string.VibrationDefault), true); - } else if (value == 1) { - textCell.setTextAndValue(LocaleController.getString("Vibrate", R.string.Vibrate), LocaleController.getString("Short", R.string.Short), true); - } else if (value == 2) { - textCell.setTextAndValue(LocaleController.getString("Vibrate", R.string.Vibrate), LocaleController.getString("VibrationDisabled", R.string.VibrationDisabled), true); - } else if (value == 3) { - textCell.setTextAndValue(LocaleController.getString("Vibrate", R.string.Vibrate), LocaleController.getString("Long", R.string.Long), true); - } else if (value == 4) { - textCell.setTextAndValue(LocaleController.getString("Vibrate", R.string.Vibrate), LocaleController.getString("OnlyIfSilent", R.string.OnlyIfSilent), true); - } - } else if (position == messagePriorityRow) { - int value; - if (currentType == NotificationsController.TYPE_PRIVATE) { - value = preferences.getInt("priority_messages", 1); - } else if (currentType == NotificationsController.TYPE_GROUP) { - value = preferences.getInt("priority_group", 1); - } else { - value = preferences.getInt("priority_channel", 1); - } - if (value == 0) { - textCell.setTextAndValue(LocaleController.getString("NotificationsImportance", R.string.NotificationsImportance), LocaleController.getString("NotificationsPriorityHigh", R.string.NotificationsPriorityHigh), false); - } else if (value == 1 || value == 2) { - textCell.setTextAndValue(LocaleController.getString("NotificationsImportance", R.string.NotificationsImportance), LocaleController.getString("NotificationsPriorityUrgent", R.string.NotificationsPriorityUrgent), false); - } else if (value == 4) { - textCell.setTextAndValue(LocaleController.getString("NotificationsImportance", R.string.NotificationsImportance), LocaleController.getString("NotificationsPriorityLow", R.string.NotificationsPriorityLow), false); - } else if (value == 5) { - textCell.setTextAndValue(LocaleController.getString("NotificationsImportance", R.string.NotificationsImportance), LocaleController.getString("NotificationsPriorityMedium", R.string.NotificationsPriorityMedium), false); - } - } else if (position == messagePopupNotificationRow) { - int option; - if (currentType == NotificationsController.TYPE_PRIVATE) { - option = preferences.getInt("popupAll", 0); - } else if (currentType == NotificationsController.TYPE_GROUP) { - option = preferences.getInt("popupGroup", 0); - } else { - option = preferences.getInt("popupChannel", 0); - } - String value; - if (option == 0) { - value = LocaleController.getString("NoPopup", R.string.NoPopup); - } else if (option == 1) { - value = LocaleController.getString("OnlyWhenScreenOn", R.string.OnlyWhenScreenOn); - } else if (option == 2) { - value = LocaleController.getString("OnlyWhenScreenOff", R.string.OnlyWhenScreenOff); - } else { - value = LocaleController.getString("AlwaysShowPopup", R.string.AlwaysShowPopup); - } - textCell.setTextAndValue(LocaleController.getString("PopupNotification", R.string.PopupNotification), value, true); - } + textCell.setTextAndValue(item.text, item.text2, divider); break; } - case 6: { + case VIEW_TYPE_CHECK2: { NotificationsCheckCell checkCell = (NotificationsCheckCell) holder.itemView; checkCell.setDrawLine(false); String text; @@ -1405,12 +1894,15 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { int offUntil; SharedPreferences preferences = getNotificationsSettings(); - if (currentType == NotificationsController.TYPE_PRIVATE) { + if (currentType == TYPE_PRIVATE) { text = LocaleController.getString("NotificationsForPrivateChats", R.string.NotificationsForPrivateChats); offUntil = preferences.getInt("EnableAll2", 0); - } else if (currentType == NotificationsController.TYPE_GROUP) { + } else if (currentType == TYPE_GROUP) { text = LocaleController.getString("NotificationsForGroups", R.string.NotificationsForGroups); offUntil = preferences.getInt("EnableGroup2", 0); + } else if (currentType == TYPE_STORIES) { + text = LocaleController.getString("NotificationsForStories", R.string.NotificationsForStories); + offUntil = preferences.getBoolean("EnableAllStories", false) ? 0 : Integer.MAX_VALUE; } else { text = LocaleController.getString("NotificationsForChannels", R.string.NotificationsForChannels); offUntil = preferences.getInt("EnableChannel2", 0); @@ -1431,14 +1923,14 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { checkCell.setTextAndValueAndCheck(text, builder, enabled, iconType, false); break; } - case 7: { + case VIEW_TYPE_BUTTON: { TextCell textCell = (TextCell) holder.itemView; - if (position == exceptionsAddRow) { - textCell.setTextAndIcon(LocaleController.getString("NotificationsAddAnException", R.string.NotificationsAddAnException), R.drawable.actions_addmember2, exceptionsStartRow != -1); + if (item.resId == 0) { + textCell.setColors(-1, Theme.key_text_RedRegular); + textCell.setText("" + item.text, divider); + } else { textCell.setColors(Theme.key_windowBackgroundWhiteBlueIcon, Theme.key_windowBackgroundWhiteBlueButton); - } else if (position == deleteAllRow) { - textCell.setText(LocaleController.getString("NotificationsDeleteAllException", R.string.NotificationsDeleteAllException), false); - textCell.setColors(null, Theme.key_windowBackgroundWhiteRedText5); + textCell.setTextAndIcon("" + item.text, item.resId, divider); } break; } @@ -1447,18 +1939,30 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { @Override public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) { - if (exceptions == null || !exceptions.isEmpty()) { + if (currentType != TYPE_STORIES && (exceptions == null || !exceptions.isEmpty())) { return; } - boolean enabled = getNotificationsController().isGlobalNotificationsEnabled(currentType); + final boolean globalEnabled; + if (currentType == TYPE_STORIES) { + globalEnabled = storiesEnabled == null || storiesEnabled || exceptions != null && !exceptions.isEmpty();; + } else { + globalEnabled = getNotificationsController().isGlobalNotificationsEnabled(currentType); + } + final int position = holder.getAdapterPosition(); + ItemInner item = null; + if (position >= 0 && position < items.size()) { + item = items.get(position); + } + final boolean enabled; + if (item != null && item.id == 5) { + enabled = storiesEnabled == null || !storiesEnabled; + } else { + enabled = globalEnabled; + } switch (holder.getItemViewType()) { case 0: { HeaderCell headerCell = (HeaderCell) holder.itemView; - if (holder.getAdapterPosition() == messageSectionRow) { - headerCell.setEnabled(enabled, null); - } else { - headerCell.setEnabled(true, null); - } + headerCell.setEnabled(enabled, null); break; } case 1: { @@ -1481,23 +1985,10 @@ public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) { @Override public int getItemViewType(int position) { - if (position == messageSectionRow) { - return 0; - } else if (position == previewRow) { - return 1; - } else if (position >= exceptionsStartRow && position < exceptionsEndRow) { - return 2; - } else if (position == messageLedRow) { - return 3; - } else if (position == groupSection2Row || position == alertSection2Row || position == exceptionsSection2Row || position == deleteAllSectionRow) { - return 4; - } else if (position == alertRow) { - return 6; - } else if (position == exceptionsAddRow || position == deleteAllRow) { - return 7; - } else { + if (position < 0 || position >= items.size()) { return 5; } + return items.get(position).viewType; } } @@ -1566,7 +2057,7 @@ public ArrayList getThemeDescriptions() { themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{ShadowSectionCell.class}, null, null, null, Theme.key_windowBackgroundGrayShadow)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueButton)); - themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteRedText5)); + themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextCell.class}, new String[]{"textView"}, null, null, null, Theme.key_text_RedRegular)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextCell.class}, new String[]{"imageView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueIcon)); return themeDescriptions; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/NotificationsSettingsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/NotificationsSettingsActivity.java index bf9029b2d6..7a6dd1aa9a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/NotificationsSettingsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/NotificationsSettingsActivity.java @@ -37,6 +37,7 @@ import org.telegram.messenger.DialogObject; import org.telegram.messenger.FileLog; import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MediaDataController; import org.telegram.messenger.MessagesController; import org.telegram.messenger.MessagesStorage; import org.telegram.messenger.NotificationCenter; @@ -64,6 +65,9 @@ import org.telegram.ui.Components.RecyclerListView; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; import java.util.Map; import tw.nekomimi.nekogram.utils.AlertUtil; @@ -75,6 +79,8 @@ public static class NotificationException { public boolean hasCustom; public int notify; public long did; + public boolean story; + public boolean auto; } private RecyclerListView listView; @@ -85,6 +91,8 @@ public static class NotificationException { private ArrayList exceptionUsers = null; private ArrayList exceptionChats = null; private ArrayList exceptionChannels = null; + private ArrayList exceptionStories = null; + private ArrayList exceptionAutoStories = null; private int accountsSectionRow; private int accountsAllRow; @@ -97,6 +105,7 @@ public static class NotificationException { private int privateRow; private int groupRow; private int channelsRow; + private int storiesRow; private int notificationsSection2Row; private int inappSectionRow; @@ -151,6 +160,7 @@ public boolean onFragmentCreate() { privateRow = rowCount++; groupRow = rowCount++; channelsRow = rowCount++; + storiesRow = rowCount++; notificationsSection2Row = rowCount++; callsSectionRow = rowCount++; @@ -197,10 +207,14 @@ public boolean onFragmentCreate() { } private void loadExceptions() { + MediaDataController.getInstance(currentAccount).loadHints(true); + final ArrayList topPeers = new ArrayList<>(MediaDataController.getInstance(currentAccount).hints); MessagesStorage.getInstance(currentAccount).getStorageQueue().postRunnable(() -> { ArrayList usersResult = new ArrayList<>(); ArrayList chatsResult = new ArrayList<>(); ArrayList channelsResult = new ArrayList<>(); + ArrayList storiesResult = new ArrayList<>(); + ArrayList storiesAutoResult = new ArrayList<>(); LongSparseArray waitingForLoadExceptions = new LongSparseArray<>(); ArrayList usersToLoad = new ArrayList<>(); @@ -279,6 +293,58 @@ private void loadExceptions() { } } } + final HashSet customStories = new HashSet<>(); + for (Map.Entry entry : values.entrySet()) { + String key = entry.getKey(); + if (key.startsWith("stories_")) { + key = key.substring(8); + try { + long did = Utilities.parseLong(key); + if (did != 0 && did != selfId) { + NotificationsSettingsActivity.NotificationException exception = new NotificationsSettingsActivity.NotificationException(); + exception.did = did; + exception.notify = ((Boolean) entry.getValue()) ? 0 : Integer.MAX_VALUE; + exception.story = true; + if (DialogObject.isUserDialog(did)) { + TLRPC.User user = getMessagesController().getUser(did); + if (user == null) { + usersToLoad.add(did); + waitingForLoadExceptions.put(did, exception); + } else if (user.deleted) { + continue; + } + storiesResult.add(exception); + customStories.add(did); + } + } + } catch (Exception ignore) {} + } + } + if (topPeers != null) { + Collections.sort(topPeers, Comparator.comparingDouble(a -> a.rating)); + for (int i = Math.max(0, topPeers.size() - 5); i < topPeers.size(); ++i) { + TLRPC.TL_topPeer topPeer = topPeers.get(i); + final long did = DialogObject.getPeerDialogId(topPeer.peer); + if (!customStories.contains(did)) { + NotificationsSettingsActivity.NotificationException exception = new NotificationsSettingsActivity.NotificationException(); + exception.did = did; + exception.notify = 0; + exception.auto = true; + exception.story = true; + if (DialogObject.isUserDialog(did)) { + TLRPC.User user = getMessagesController().getUser(did); + if (user == null) { + usersToLoad.add(did); + waitingForLoadExceptions.put(did, exception); + } else if (user.deleted) { + continue; + } + storiesAutoResult.add(0, exception); + customStories.add(did); + } + } + } + } if (waitingForLoadExceptions.size() != 0) { try { if (!encryptedChatsToLoad.isEmpty()) { @@ -337,11 +403,17 @@ private void loadExceptions() { exceptionUsers = usersResult; exceptionChats = chatsResult; exceptionChannels = channelsResult; + exceptionStories = storiesResult; + exceptionAutoStories = storiesAutoResult; adapter.notifyItemChanged(privateRow); adapter.notifyItemChanged(groupRow); adapter.notifyItemChanged(channelsRow); + adapter.notifyItemChanged(storiesRow); }); }); + + // stories exceptions + // adapter.notifyItemChanged(storiesRow); } @Override @@ -385,32 +457,53 @@ public boolean supportsPredictiveItemAnimations() { if (getParentActivity() == null) { return; } - if (position == privateRow || position == groupRow || position == channelsRow) { + if (position == privateRow || position == groupRow || position == channelsRow || position == storiesRow) { int type; ArrayList exceptions; + ArrayList autoExceptions = null; if (position == privateRow) { type = NotificationsController.TYPE_PRIVATE; exceptions = exceptionUsers; + enabled = getNotificationsController().isGlobalNotificationsEnabled(type); } else if (position == groupRow) { type = NotificationsController.TYPE_GROUP; exceptions = exceptionChats; + enabled = getNotificationsController().isGlobalNotificationsEnabled(type); + } else if (position == storiesRow) { + type = NotificationsController.TYPE_STORIES; + exceptions = exceptionStories; + autoExceptions = exceptionAutoStories; + enabled = getNotificationsSettings().getBoolean("EnableAllStories", false); } else { type = NotificationsController.TYPE_CHANNEL; exceptions = exceptionChannels; + enabled = getNotificationsController().isGlobalNotificationsEnabled(type); } if (exceptions == null) { return; } NotificationsCheckCell checkCell = (NotificationsCheckCell) view; - enabled = getNotificationsController().isGlobalNotificationsEnabled(type); if (LocaleController.isRTL && x <= AndroidUtilities.dp(76) || !LocaleController.isRTL && x >= view.getMeasuredWidth() - AndroidUtilities.dp(76)) { - getNotificationsController().setGlobalNotificationsEnabled(type, !enabled ? 0 : Integer.MAX_VALUE); - showExceptionsAlert(position); - checkCell.setChecked(!enabled, 0); - adapter.notifyItemChanged(position); + final boolean enabledFinal = enabled; + showExceptionsAlert(position, () -> { + if (type == NotificationsController.TYPE_STORIES) { + SharedPreferences.Editor edit = getNotificationsSettings().edit(); + if (enabledFinal) { + edit.remove("EnableAllStories"); + } else { + edit.putBoolean("EnableAllStories", true); + } + edit.apply(); + getNotificationsController().updateServerNotificationsSettings(type); + } else { + getNotificationsController().setGlobalNotificationsEnabled(type, !enabledFinal ? 0 : Integer.MAX_VALUE); + } + checkCell.setChecked(!enabledFinal, 0); + adapter.notifyItemChanged(position); + }); } else { - presentFragment(new NotificationsCustomSettingsActivity(type, exceptions)); + presentFragment(new NotificationsCustomSettingsActivity(type, exceptions, autoExceptions)); } } else if (position == callsRingtoneRow) { try { @@ -472,7 +565,7 @@ public boolean supportsPredictiveItemAnimations() { showDialog(alertDialog); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } else if (position == inappSoundRow) { SharedPreferences preferences = MessagesController.getNotificationsSettings(currentAccount); @@ -517,7 +610,14 @@ public boolean supportsPredictiveItemAnimations() { ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> { }); - } else if (position == pinnedMessageRow) { + } /*else if (position == storiesRow) { + SharedPreferences preferences = getNotificationsSettings(); + SharedPreferences.Editor editor = preferences.edit(); + enabled = preferences.getBoolean("EnableAllStories", true); + editor.putBoolean("EnableAllStories", !enabled); + editor.commit(); + getNotificationsController().updateServerNotificationsSettings(NotificationsController.TYPE_PRIVATE); + } */else if (position == pinnedMessageRow) { SharedPreferences preferences = MessagesController.getNotificationsSettings(currentAccount); SharedPreferences.Editor editor = preferences.edit(); enabled = preferences.getBoolean("PinnedMessages", true); @@ -709,27 +809,38 @@ public void onActivityResultFragment(int requestCode, int resultCode, Intent dat } } - private void showExceptionsAlert(int position) { + private void showExceptionsAlert(int position, Runnable whenDone) { ArrayList exceptions; + final ArrayList autoExceptions; String alertText = null; - if (position == privateRow) { + if (position == storiesRow) { + exceptions = exceptionStories; + autoExceptions = exceptionAutoStories; + if (exceptions != null && !exceptions.isEmpty()) { + alertText = LocaleController.formatPluralString("ChatsException", exceptions.size()); + } + } else if (position == privateRow) { exceptions = exceptionUsers; + autoExceptions = null; if (exceptions != null && !exceptions.isEmpty()) { alertText = LocaleController.formatPluralString("ChatsException", exceptions.size()); } } else if (position == groupRow) { exceptions = exceptionChats; + autoExceptions = null; if (exceptions != null && !exceptions.isEmpty()) { alertText = LocaleController.formatPluralString("Groups", exceptions.size()); } } else { exceptions = exceptionChannels; + autoExceptions = null; if (exceptions != null && !exceptions.isEmpty()) { alertText = LocaleController.formatPluralString("Channels", exceptions.size()); } } if (alertText == null) { + whenDone.run(); return; } AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); @@ -739,8 +850,8 @@ private void showExceptionsAlert(int position) { builder.setMessage(AndroidUtilities.replaceTags(LocaleController.formatString("NotificationsExceptionsAlert", R.string.NotificationsExceptionsAlert, alertText))); } builder.setTitle(LocaleController.getString("NotificationsExceptions", R.string.NotificationsExceptions)); - builder.setNeutralButton(LocaleController.getString("ViewExceptions", R.string.ViewExceptions), (dialogInterface, i) -> presentFragment(new NotificationsCustomSettingsActivity(-1, exceptions))); - builder.setNegativeButton(LocaleController.getString("OK", R.string.OK), null); + builder.setNeutralButton(LocaleController.getString("ViewExceptions", R.string.ViewExceptions), (dialogInterface, i) -> presentFragment(new NotificationsCustomSettingsActivity(-1, exceptions, autoExceptions))); + builder.setNegativeButton(LocaleController.getString("OK", R.string.OK), (di, i) -> whenDone.run()); showDialog(builder.create()); } @@ -774,7 +885,8 @@ public boolean isEnabled(RecyclerView.ViewHolder holder) { position == eventsSectionRow || position == otherSectionRow || position == resetSectionRow || position == badgeNumberSection || position == otherSection2Row || position == resetSection2Row || position == callsSection2Row || position == callsSectionRow || position == badgeNumberSection2Row || - position == accountsSectionRow || position == accountsInfoRow || position == resetNotificationsSectionRow); + position == accountsSectionRow || position == accountsInfoRow || position == resetNotificationsSectionRow || + position == eventsSection2Row); } @Override @@ -812,7 +924,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType case 6: default: view = new TextInfoPrivacyCell(mContext); - view.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + view.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); break; } return new RecyclerListView.Holder(view); @@ -894,7 +1006,9 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { String text; int offUntil; ArrayList exceptions; + ArrayList autoExceptions = null; boolean enabled; + boolean allAuto = false; if (position == privateRow) { text = LocaleController.getString("NotificationsPrivateChats", R.string.NotificationsPrivateChats); exceptions = exceptionUsers; @@ -903,6 +1017,11 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { text = LocaleController.getString("NotificationsGroups", R.string.NotificationsGroups); exceptions = exceptionChats; offUntil = preferences.getInt("EnableGroup2", 0); + } else if (position == storiesRow) { + text = LocaleController.getString("NotificationStories", R.string.NotificationStories); + exceptions = exceptionStories; + autoExceptions = exceptionAutoStories; + offUntil = preferences.getBoolean("EnableAllStories", false) ? 0 : Integer.MAX_VALUE; } else { text = LocaleController.getString("NotificationsChannels", R.string.NotificationsChannels); exceptions = exceptionChannels; @@ -928,18 +1047,32 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (builder.length() != 0) { builder.append(", "); } - builder.append(LocaleController.formatPluralString("Exception", exceptions.size())); + int exceptionsCount = exceptions.size(); + if (position == storiesRow && !preferences.contains("EnableAllStories") && autoExceptions != null) { + exceptionsCount += autoExceptions.size(); + } + builder.append(LocaleController.formatPluralString("Exception", exceptionsCount)); + } else if (autoExceptions != null && !autoExceptions.isEmpty()) { + if (offUntil > 0) { + builder.append(LocaleController.getString("NotificationsOff", R.string.NotificationsOff)); + } else { + builder.append(LocaleController.getString("NotificationsOn", R.string.NotificationsOn)); + } + if (autoExceptions != null && !autoExceptions.isEmpty() && !preferences.contains("EnableAllStories")) { + builder.append(", "); + builder.append(LocaleController.formatPluralString("AutoException", autoExceptions.size())); + } } else { builder.append(LocaleController.getString("TapToChange", R.string.TapToChange)); } - checkCell.setTextAndValueAndCheck(text, builder, enabled, iconType, position != channelsRow); + checkCell.setTextAndValueAndCheck(text, builder, enabled, iconType, position != storiesRow); break; } case 4: { if (position == resetNotificationsSectionRow) { - holder.itemView.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + holder.itemView.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } else { - holder.itemView.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + holder.itemView.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); } break; } @@ -1006,7 +1139,7 @@ public int getItemViewType(int position) { return 1; } else if (position == resetNotificationsRow) { return 2; - } else if (position == privateRow || position == groupRow || position == channelsRow) { + } else if (position == privateRow || position == groupRow || position == channelsRow || position == storiesRow) { return 3; } else if (position == eventsSection2Row || position == notificationsSection2Row || position == otherSection2Row || position == resetSection2Row || position == callsSection2Row || position == badgeNumberSection2Row || diff --git a/TMessagesProj/src/main/java/org/telegram/ui/NotificationsSoundActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/NotificationsSoundActivity.java index ca27ef4f74..afeff27b99 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/NotificationsSoundActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/NotificationsSoundActivity.java @@ -151,6 +151,9 @@ public boolean onFragmentCreate() { } else if (currentType == NotificationsController.TYPE_CHANNEL) { prefPath = "ChannelSoundPath"; prefDocId = "ChannelSoundDocId"; + } else if (currentType == NotificationsController.TYPE_STORIES) { + prefPath = "StoriesSoundPath"; + prefDocId = "StoriesSoundDocId"; } else { throw new RuntimeException("Unsupported type"); } @@ -204,7 +207,7 @@ public void onItemClick(int id) { AlertDialog dialog = builder.show(); TextView button = (TextView) dialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed, resourcesProvider)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold, resourcesProvider)); } } else if (id == shareId) { if (selectedTones.size() == 1) { @@ -292,6 +295,8 @@ private void deleteSelectedMessages() { actionBar.setTitle(LocaleController.getString("NotificationsSoundGroup", R.string.NotificationsSoundGroup)); } else if (currentType == NotificationsController.TYPE_CHANNEL) { actionBar.setTitle(LocaleController.getString("NotificationsSoundChannels", R.string.NotificationsSoundChannels)); + } else if (currentType == NotificationsController.TYPE_STORIES) { + actionBar.setTitle(LocaleController.getString("NotificationsSoundStories", R.string.NotificationsSoundStories)); } } else { avatarContainer = new ChatAvatarContainer(context, null, false, resourcesProvider); @@ -343,7 +348,7 @@ private void deleteSelectedMessages() { listView.setLayoutManager(new LinearLayoutManager(context)); listView.setOnItemClickListener((view, position) -> { if (position == uploadRow) { - chatAttachAlert = new ChatAttachAlert(context, NotificationsSoundActivity.this, false, false, resourcesProvider); + chatAttachAlert = new ChatAttachAlert(context, NotificationsSoundActivity.this, false, false, true, resourcesProvider); chatAttachAlert.setSoundPicker(); chatAttachAlert.init(); chatAttachAlert.show(); @@ -445,7 +450,7 @@ private void updateActionMode() { } private void loadTones() { - getMediaDataController().ringtoneDataStore.loadUserRingtones(); + getMediaDataController().ringtoneDataStore.loadUserRingtones(false); serverTones.clear(); systemTones.clear(); @@ -473,9 +478,6 @@ private void loadTones() { manager.setType(RingtoneManager.TYPE_NOTIFICATION); Cursor cursor = manager.getCursor(); - - - Tone noSoundTone = new Tone(); noSoundTone.stableId = stableIds++; noSoundTone.title = LocaleController.getString("NoSound", R.string.NoSound); @@ -522,6 +524,32 @@ private void loadTones() { updateRows(); } + public static String findRingtonePathByName(String title) { + if (title == null) { + return null; + } + + try { + RingtoneManager manager = new RingtoneManager(ApplicationLoader.applicationContext); + manager.setType(RingtoneManager.TYPE_NOTIFICATION); + Cursor cursor = manager.getCursor(); + + while (cursor.moveToNext()) { + String notificationTitle = cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX); + String notificationUri = cursor.getString(RingtoneManager.URI_COLUMN_INDEX) + "/" + cursor.getString(RingtoneManager.ID_COLUMN_INDEX); + + if (title.equalsIgnoreCase(notificationTitle)) { + return notificationUri; + } + } + } catch (Throwable e) { + // Exception java.lang.NullPointerException: Attempt to invoke interface method 'void android.database.Cursor.registerDataSetObserver(android.database.DataSetObserver)' on a null object reference + // ignore + FileLog.e(e); + } + return null; + } + private void updateRows() { serverTonesHeaderRow = -1; serverTonesStartRow = -1; @@ -711,7 +739,7 @@ public ToneCell(Context context, Theme.ResourcesProvider resourcesProvider) { checkBox = new CheckBox2(context, 24, resourcesProvider); - checkBox.setColor(null, Theme.key_windowBackgroundWhite, Theme.key_checkboxCheck); + checkBox.setColor(-1, Theme.key_windowBackgroundWhite, Theme.key_checkboxCheck); checkBox.setDrawUnchecked(false); checkBox.setDrawBackgroundAsArc(3); addView(checkBox, LayoutHelper.createFrame(26, 26, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL, (LocaleController.isRTL ? 0 : 18), 0, (LocaleController.isRTL ? 18 : 0), 0)); @@ -762,11 +790,6 @@ public void onPause() { getNotificationCenter().removeObserver(this, NotificationCenter.onUserRingtonesUpdated); } - @Override - public int getNavigationBarColor() { - return getThemedColor(Theme.key_windowBackgroundGray); - } - @Override public void didReceivedNotification(int id, int account, Object... args) { if (id == NotificationCenter.onUserRingtonesUpdated) { @@ -861,6 +884,10 @@ public void onFragmentDestroy() { prefName = "ChannelSound"; prefPath = "ChannelSoundPath"; prefDocId = "ChannelSoundDocId"; + } else if (currentType == NotificationsController.TYPE_STORIES) { + prefName = "StoriesSound"; + prefPath = "StoriesSoundPath"; + prefDocId = "StoriesSoundDocId"; } else { throw new RuntimeException("Unsupported type"); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PasscodeActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PasscodeActivity.java index 585ae795d2..6df3afe791 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PasscodeActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PasscodeActivity.java @@ -313,7 +313,7 @@ public boolean supportsPredictiveItemAnimations() { finishFragment(); }).create(); alertDialog.show(); - ((TextView)alertDialog.getButton(Dialog.BUTTON_POSITIVE)).setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + ((TextView)alertDialog.getButton(Dialog.BUTTON_POSITIVE)).setTextColor(Theme.getColor(Theme.key_text_RedBold)); } else if (position == changePasscodeRow) { presentFragment(new PasscodeActivity(TYPE_SETUP_CODE)); } else if (position == autoLockRow) { @@ -1241,8 +1241,8 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { textCell.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); } else if (position == disablePasscodeRow) { textCell.setText(LocaleController.getString(R.string.DisablePasscode), false); - textCell.setTag(Theme.key_dialogTextRed); - textCell.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + textCell.setTag(Theme.key_text_RedBold); + textCell.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } break; } @@ -1268,11 +1268,11 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { cell.getTextView().setGravity(Gravity.CENTER_HORIZONTAL); } else if (position == autoLockDetailRow) { cell.setText(LocaleController.getString(R.string.AutoLockInfo)); - cell.setBackground(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + cell.setBackground(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); cell.getTextView().setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); } else if (position == captureDetailRow) { cell.setText(LocaleController.getString(R.string.ScreenCaptureInfo)); - cell.setBackground(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + cell.setBackground(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); cell.getTextView().setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); } break; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PassportActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PassportActivity.java index bd55c43ce2..98e438ee53 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PassportActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PassportActivity.java @@ -1496,7 +1496,7 @@ public void afterTextChanged(Editable s) { } bottomCell = new TextInfoPrivacyCell(context); - bottomCell.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + bottomCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); bottomCell.setText(LocaleController.formatString("PassportEmailVerifyInfo", R.string.PassportEmailVerifyInfo, currentValues.get("email"))); linearLayout2.addView(bottomCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); } @@ -1622,7 +1622,7 @@ public boolean onActionItemClicked(ActionMode mode, MenuItem item) { } passwordInfoRequestTextView = new TextInfoPrivacyCell(context); - passwordInfoRequestTextView.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + passwordInfoRequestTextView.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); passwordInfoRequestTextView.setText(LocaleController.formatString("PassportRequestPasswordInfo", R.string.PassportRequestPasswordInfo)); linearLayout2.addView(passwordInfoRequestTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); @@ -2009,7 +2009,7 @@ private void createRequestInterface(Context context) { avatarImageView.setForUserOrChat(botUser, avatarDrawable); bottomCell = new TextInfoPrivacyCell(context); - bottomCell.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_top, Theme.key_windowBackgroundGrayShadow)); + bottomCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_top, Theme.key_windowBackgroundGrayShadow)); bottomCell.setText(AndroidUtilities.replaceTags(LocaleController.formatString("PassportRequest", R.string.PassportRequest, UserObject.getFirstName(botUser)))); bottomCell.getTextView().setGravity(Gravity.CENTER_HORIZONTAL); ((FrameLayout.LayoutParams) bottomCell.getTextView().getLayoutParams()).gravity = Gravity.CENTER_HORIZONTAL; @@ -2158,7 +2158,7 @@ private void createRequestInterface(Context context) { if (botUser != null) { bottomCell = new TextInfoPrivacyCell(context); - bottomCell.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + bottomCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); bottomCell.setLinkTextColorKey(Theme.key_windowBackgroundWhiteGrayText4); if (!TextUtils.isEmpty(currentForm.privacy_policy_url)) { String str2 = LocaleController.formatString("PassportPolicy", R.string.PassportPolicy, UserObject.getFirstName(botUser), botUser.username); @@ -2427,7 +2427,7 @@ private void createManageInterface(Context context) { linearLayout2.addView(headerCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); sectionCell = new ShadowSectionCell(context); - sectionCell.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + sectionCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); linearLayout2.addView(sectionCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); addDocumentCell = new TextSettingsCell(context); @@ -2437,7 +2437,7 @@ private void createManageInterface(Context context) { addDocumentCell.setOnClickListener(v -> openAddDocumentAlert()); deletePassportCell = new TextSettingsCell(context); - deletePassportCell.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText3)); + deletePassportCell.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); deletePassportCell.setBackgroundDrawable(Theme.getSelectorDrawable(true)); deletePassportCell.setText(LocaleController.getString("TelegramPassportDelete", R.string.TelegramPassportDelete), false); linearLayout2.addView(deletePassportCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); @@ -2471,18 +2471,18 @@ private void createManageInterface(Context context) { showDialog(alertDialog); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } }); addDocumentSectionCell = new ShadowSectionCell(context); - addDocumentSectionCell.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + addDocumentSectionCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); linearLayout2.addView(addDocumentSectionCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); emptyLayout = new LinearLayout(context); emptyLayout.setOrientation(LinearLayout.VERTICAL); emptyLayout.setGravity(Gravity.CENTER); - emptyLayout.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + emptyLayout.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); if (AndroidUtilities.isTablet()) { linearLayout2.addView(emptyLayout, new LinearLayout.LayoutParams(LayoutHelper.MATCH_PARENT, AndroidUtilities.dp(528) - ActionBar.getCurrentActionBarHeight())); } else { @@ -2729,7 +2729,7 @@ private void createEmailInterface(Context context) { }); bottomCell = new TextInfoPrivacyCell(context); - bottomCell.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + bottomCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); bottomCell.setText(LocaleController.getString("PassportPhoneUseSameEmailInfo", R.string.PassportPhoneUseSameEmailInfo)); linearLayout2.addView(bottomCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); } @@ -2773,7 +2773,7 @@ private void createEmailInterface(Context context) { } bottomCell = new TextInfoPrivacyCell(context); - bottomCell.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + bottomCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); bottomCell.setText(LocaleController.getString("PassportEmailUploadInfo", R.string.PassportEmailUploadInfo)); linearLayout2.addView(bottomCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); } @@ -2815,7 +2815,7 @@ private void createPhoneInterface(Context context) { }); bottomCell = new TextInfoPrivacyCell(context); - bottomCell.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + bottomCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); bottomCell.setText(LocaleController.getString("PassportPhoneUseSameInfo", R.string.PassportPhoneUseSameInfo)); linearLayout2.addView(bottomCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); @@ -3119,7 +3119,7 @@ public void afterTextChanged(Editable s) { } bottomCell = new TextInfoPrivacyCell(context); - bottomCell.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + bottomCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); bottomCell.setText(LocaleController.getString("PassportPhoneUploadInfo", R.string.PassportPhoneUploadInfo)); linearLayout2.addView(bottomCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); } @@ -3139,7 +3139,7 @@ private void createAddressInterface(Context context) { } topErrorCell = new TextInfoPrivacyCell(context); - topErrorCell.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_top, Theme.key_windowBackgroundGrayShadow)); + topErrorCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_top, Theme.key_windowBackgroundGrayShadow)); topErrorCell.setPadding(0, AndroidUtilities.dp(7), 0, 0); linearLayout2.addView(topErrorCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); checkTopErrorCell(true); @@ -3175,7 +3175,7 @@ private void createAddressInterface(Context context) { }); bottomCell = new TextInfoPrivacyCell(context); - bottomCell.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + bottomCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); if (currentBotId != 0) { noAllDocumentsErrorText = LocaleController.getString("PassportAddAddressUploadInfo", R.string.PassportAddAddressUploadInfo); @@ -3203,7 +3203,7 @@ private void createAddressInterface(Context context) { stringBuilder.append("\n\n"); stringBuilder.append(noAllDocumentsErrorText); text = stringBuilder; - stringBuilder.setSpan(new ForegroundColorSpan(Theme.getColor(Theme.key_windowBackgroundWhiteRedText3)), 0, errorText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + stringBuilder.setSpan(new ForegroundColorSpan(Theme.getColor(Theme.key_text_RedRegular)), 0, errorText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); errorsValues.put("files_all", ""); } } @@ -3229,7 +3229,7 @@ private void createAddressInterface(Context context) { }); bottomCellTranslation = new TextInfoPrivacyCell(context); - bottomCellTranslation.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + bottomCellTranslation.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); if (currentBotId != 0) { noAllTranslationErrorText = LocaleController.getString("PassportAddTranslationUploadInfo", R.string.PassportAddTranslationUploadInfo); @@ -3257,7 +3257,7 @@ private void createAddressInterface(Context context) { stringBuilder.append("\n\n"); stringBuilder.append(noAllTranslationErrorText); text = stringBuilder; - stringBuilder.setSpan(new ForegroundColorSpan(Theme.getColor(Theme.key_windowBackgroundWhiteRedText3)), 0, errorText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + stringBuilder.setSpan(new ForegroundColorSpan(Theme.getColor(Theme.key_text_RedRegular)), 0, errorText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); errorsValues.put("translation_all", ""); } } @@ -3349,7 +3349,7 @@ protected void onDraw(Canvas canvas) { inputFields[a].setCursorColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); inputFields[a].setCursorSize(AndroidUtilities.dp(20)); inputFields[a].setCursorWidth(1.5f); - inputFields[a].setLineColors(Theme.getColor(Theme.key_windowBackgroundWhiteInputField), Theme.getColor(Theme.key_windowBackgroundWhiteInputFieldActivated), Theme.getColor(Theme.key_windowBackgroundWhiteRedText3)); + inputFields[a].setLineColors(Theme.getColor(Theme.key_windowBackgroundWhiteInputField), Theme.getColor(Theme.key_windowBackgroundWhiteInputFieldActivated), Theme.getColor(Theme.key_text_RedRegular)); if (a == FIELD_COUNTRY) { inputFields[a].setOnTouchListener((v, event) -> { if (getParentActivity() == null) { @@ -3498,10 +3498,10 @@ public void afterTextChanged(Editable s) { addDocumentViews(currentDocumentsTypeValue.files); addTranslationDocumentViews(currentDocumentsTypeValue.translation); } - sectionCell.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + sectionCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); TextSettingsCell settingsCell1 = new TextSettingsCell(context); - settingsCell1.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText3)); + settingsCell1.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); settingsCell1.setBackgroundDrawable(Theme.getSelectorDrawable(true)); if (currentDocumentsType == null) { settingsCell1.setText(LocaleController.getString("PassportDeleteInfo", R.string.PassportDeleteInfo), false); @@ -3512,12 +3512,12 @@ public void afterTextChanged(Editable s) { settingsCell1.setOnClickListener(v -> createDocumentDeleteAlert()); sectionCell = new ShadowSectionCell(context); - sectionCell.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + sectionCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); linearLayout2.addView(sectionCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); } else { - sectionCell.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + sectionCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); if (documentOnly && currentDocumentsType != null) { - bottomCell.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + bottomCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } } updateUploadText(UPLOADING_TYPE_DOCUMENTS); @@ -3877,7 +3877,7 @@ private void createIdentityInterface(final Context context) { } topErrorCell = new TextInfoPrivacyCell(context); - topErrorCell.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_top, Theme.key_windowBackgroundGrayShadow)); + topErrorCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_top, Theme.key_windowBackgroundGrayShadow)); topErrorCell.setPadding(0, AndroidUtilities.dp(7), 0, 0); linearLayout2.addView(topErrorCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); checkTopErrorCell(true); @@ -3935,7 +3935,7 @@ private void createIdentityInterface(final Context context) { } bottomCell = new TextInfoPrivacyCell(context); - bottomCell.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + bottomCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); bottomCell.setText(LocaleController.getString("PassportPersonalUploadInfo", R.string.PassportPersonalUploadInfo)); linearLayout2.addView(bottomCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); @@ -3958,7 +3958,7 @@ private void createIdentityInterface(final Context context) { }); bottomCellTranslation = new TextInfoPrivacyCell(context); - bottomCellTranslation.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + bottomCellTranslation.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); if (currentBotId != 0) { noAllTranslationErrorText = LocaleController.getString("PassportAddTranslationUploadInfo", R.string.PassportAddTranslationUploadInfo); @@ -3984,7 +3984,7 @@ private void createIdentityInterface(final Context context) { stringBuilder.append("\n\n"); stringBuilder.append(noAllTranslationErrorText); text = stringBuilder; - stringBuilder.setSpan(new ForegroundColorSpan(Theme.getColor(Theme.key_windowBackgroundWhiteRedText3)), 0, errorText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + stringBuilder.setSpan(new ForegroundColorSpan(Theme.getColor(Theme.key_text_RedRegular)), 0, errorText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); errorsValues.put("translation_all", ""); } } @@ -4049,7 +4049,7 @@ public void didFindMrzInfo(MrzRecognizer.Result result) { }); bottomCell = new TextInfoPrivacyCell(context); - bottomCell.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + bottomCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); bottomCell.setText(LocaleController.getString("PassportScanPassportInfo", R.string.PassportScanPassportInfo)); linearLayout2.addView(bottomCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); } @@ -4141,7 +4141,7 @@ protected void onDraw(Canvas canvas) { inputFields[a].setCursorColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); inputFields[a].setCursorSize(AndroidUtilities.dp(20)); inputFields[a].setCursorWidth(1.5f); - inputFields[a].setLineColors(Theme.getColor(Theme.key_windowBackgroundWhiteInputField), Theme.getColor(Theme.key_windowBackgroundWhiteInputFieldActivated), Theme.getColor(Theme.key_windowBackgroundWhiteRedText3)); + inputFields[a].setLineColors(Theme.getColor(Theme.key_windowBackgroundWhiteInputField), Theme.getColor(Theme.key_windowBackgroundWhiteInputFieldActivated), Theme.getColor(Theme.key_text_RedRegular)); if (a == FIELD_CITIZENSHIP || a == FIELD_RESIDENCE) { inputFields[a].setOnTouchListener((v, event) -> { if (getParentActivity() == null) { @@ -4481,7 +4481,7 @@ protected void onDraw(Canvas canvas) { inputExtraFields[a].setCursorColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); inputExtraFields[a].setCursorSize(AndroidUtilities.dp(20)); inputExtraFields[a].setCursorWidth(1.5f); - inputExtraFields[a].setLineColors(Theme.getColor(Theme.key_windowBackgroundWhiteInputField), Theme.getColor(Theme.key_windowBackgroundWhiteInputFieldActivated), Theme.getColor(Theme.key_windowBackgroundWhiteRedText3)); + inputExtraFields[a].setLineColors(Theme.getColor(Theme.key_windowBackgroundWhiteInputField), Theme.getColor(Theme.key_windowBackgroundWhiteInputFieldActivated), Theme.getColor(Theme.key_text_RedRegular)); inputExtraFields[a].setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES); inputExtraFields[a].setImeOptions(EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NO_EXTRACT_UI); @@ -4573,7 +4573,7 @@ public void afterTextChanged(Editable s) { } TextSettingsCell settingsCell1 = new TextSettingsCell(context); - settingsCell1.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText3)); + settingsCell1.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); settingsCell1.setBackgroundDrawable(Theme.getSelectorDrawable(true)); if (currentDocumentsType == null) { settingsCell1.setText(LocaleController.getString("PassportDeleteInfo", R.string.PassportDeleteInfo), false); @@ -4583,13 +4583,13 @@ public void afterTextChanged(Editable s) { linearLayout2.addView(settingsCell1, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); settingsCell1.setOnClickListener(v -> createDocumentDeleteAlert()); - nativeInfoCell.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + nativeInfoCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); sectionCell = new ShadowSectionCell(context); - sectionCell.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + sectionCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); linearLayout2.addView(sectionCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); } else { - nativeInfoCell.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + nativeInfoCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } updateInterfaceStringsForDocumentType(); @@ -4688,7 +4688,7 @@ private void checkTopErrorCell(boolean init) { } } if (stringBuilder != null) { - stringBuilder.setSpan(new ForegroundColorSpan(Theme.getColor(Theme.key_windowBackgroundWhiteRedText3)), 0, stringBuilder.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + stringBuilder.setSpan(new ForegroundColorSpan(Theme.getColor(Theme.key_text_RedRegular)), 0, stringBuilder.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); topErrorCell.setText(stringBuilder); topErrorCell.setVisibility(View.VISIBLE); } else if (topErrorCell.getVisibility() != View.GONE) { @@ -4851,7 +4851,7 @@ private void addDocumentView(final SecureDocument document, final int type) { if (key == null || documentsErrors == null || (value = documentsErrors.get(key)) == null) { value = LocaleController.formatDateForBan(document.secureFile.date); } else { - cell.valueTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText3)); + cell.valueTextView.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); errorsValues.put(key, ""); } @@ -5272,7 +5272,7 @@ private void setTypeValue(TLRPC.TL_secureRequiredType requiredType, String text, } } view.setValue(value); - view.valueTextView.setTextColor(Theme.getColor(isError ? Theme.key_windowBackgroundWhiteRedText3 : Theme.key_windowBackgroundWhiteGrayText2)); + view.valueTextView.setTextColor(Theme.getColor(isError ? Theme.key_text_RedRegular : Theme.key_windowBackgroundWhiteGrayText2)); view.setChecked(!isError && currentActivityType != TYPE_MANAGE && (documentOnly && documentRequiredType != null || !documentOnly && requiredTypeValue != null) && (documentRequiredType == null || documentRequiredTypeValue != null)); } @@ -5294,9 +5294,9 @@ private void checkNativeFields(boolean byEdit) { } if ((currentBotId != 0 || currentDocumentsType == null) && currentTypeValue != null && !documentOnly || currentDocumentsTypeValue != null) { - sectionCell2.setBackgroundDrawable(Theme.getThemedDrawable(getParentActivity(), R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + sectionCell2.setBackgroundDrawable(Theme.getThemedDrawableByKey(getParentActivity(), R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); } else { - sectionCell2.setBackgroundDrawable(Theme.getThemedDrawable(getParentActivity(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + sectionCell2.setBackgroundDrawable(Theme.getThemedDrawableByKey(getParentActivity(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } } } else { @@ -5317,7 +5317,7 @@ private void checkNativeFields(boolean byEdit) { } } } - sectionCell2.setBackgroundDrawable(Theme.getThemedDrawable(getParentActivity(), R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + sectionCell2.setBackgroundDrawable(Theme.getThemedDrawableByKey(getParentActivity(), R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); } nativeInfoCell.setText(LocaleController.formatString("PassportNativeInfo", R.string.PassportNativeInfo, country)); @@ -7963,7 +7963,7 @@ public ArrayList getThemeDescriptions() { arrayList.add(new ThemeDescription(inputFields[a], ThemeDescription.FLAG_HINTTEXTCOLOR | ThemeDescription.FLAG_PROGRESSBAR, null, null, null, null, Theme.key_windowBackgroundWhiteBlueHeader)); arrayList.add(new ThemeDescription(inputFields[a], ThemeDescription.FLAG_BACKGROUNDFILTER, null, null, null, null, Theme.key_windowBackgroundWhiteInputField)); arrayList.add(new ThemeDescription(inputFields[a], ThemeDescription.FLAG_BACKGROUNDFILTER | ThemeDescription.FLAG_DRAWABLESELECTEDSTATE, null, null, null, null, Theme.key_windowBackgroundWhiteInputFieldActivated)); - arrayList.add(new ThemeDescription(inputFields[a], ThemeDescription.FLAG_BACKGROUNDFILTER | ThemeDescription.FLAG_PROGRESSBAR, null, null, null, null, Theme.key_windowBackgroundWhiteRedText3)); + arrayList.add(new ThemeDescription(inputFields[a], ThemeDescription.FLAG_BACKGROUNDFILTER | ThemeDescription.FLAG_PROGRESSBAR, null, null, null, null, Theme.key_text_RedRegular)); } } else { arrayList.add(new ThemeDescription(null, ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); @@ -7971,7 +7971,7 @@ public ArrayList getThemeDescriptions() { arrayList.add(new ThemeDescription(null, ThemeDescription.FLAG_HINTTEXTCOLOR | ThemeDescription.FLAG_PROGRESSBAR, null, null, null, null, Theme.key_windowBackgroundWhiteBlueHeader)); arrayList.add(new ThemeDescription(null, ThemeDescription.FLAG_BACKGROUNDFILTER, null, null, null, null, Theme.key_windowBackgroundWhiteInputField)); arrayList.add(new ThemeDescription(null, ThemeDescription.FLAG_BACKGROUNDFILTER | ThemeDescription.FLAG_DRAWABLESELECTEDSTATE, null, null, null, null, Theme.key_windowBackgroundWhiteInputFieldActivated)); - arrayList.add(new ThemeDescription(null, ThemeDescription.FLAG_BACKGROUNDFILTER | ThemeDescription.FLAG_PROGRESSBAR, null, null, null, null, Theme.key_windowBackgroundWhiteRedText3)); + arrayList.add(new ThemeDescription(null, ThemeDescription.FLAG_BACKGROUNDFILTER | ThemeDescription.FLAG_PROGRESSBAR, null, null, null, null, Theme.key_text_RedRegular)); } if (inputExtraFields != null) { @@ -7982,7 +7982,7 @@ public ArrayList getThemeDescriptions() { arrayList.add(new ThemeDescription(inputExtraFields[a], ThemeDescription.FLAG_HINTTEXTCOLOR | ThemeDescription.FLAG_PROGRESSBAR, null, null, null, null, Theme.key_windowBackgroundWhiteBlueHeader)); arrayList.add(new ThemeDescription(inputExtraFields[a], ThemeDescription.FLAG_BACKGROUNDFILTER, null, null, null, null, Theme.key_windowBackgroundWhiteInputField)); arrayList.add(new ThemeDescription(inputExtraFields[a], ThemeDescription.FLAG_BACKGROUNDFILTER | ThemeDescription.FLAG_DRAWABLESELECTEDSTATE, null, null, null, null, Theme.key_windowBackgroundWhiteInputFieldActivated)); - arrayList.add(new ThemeDescription(inputExtraFields[a], ThemeDescription.FLAG_BACKGROUNDFILTER | ThemeDescription.FLAG_PROGRESSBAR, null, null, null, null, Theme.key_windowBackgroundWhiteRedText3)); + arrayList.add(new ThemeDescription(inputExtraFields[a], ThemeDescription.FLAG_BACKGROUNDFILTER | ThemeDescription.FLAG_PROGRESSBAR, null, null, null, null, Theme.key_text_RedRegular)); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PeopleNearbyActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PeopleNearbyActivity.java index 0349400982..52df4e2d0e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PeopleNearbyActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PeopleNearbyActivity.java @@ -995,11 +995,11 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { case 1: ShadowSectionCell privacyCell = (ShadowSectionCell) holder.itemView; if (position == usersSectionRow) { - privacyCell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + privacyCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); } else if (position == chatsSectionRow) { - privacyCell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + privacyCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } else if (position == helpSectionRow) { - privacyCell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + privacyCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); } break; case 2: @@ -1010,7 +1010,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } else if (position == showMeRow) { if (showingMe = (getUserConfig().sharingMyLocationUntil > getConnectionsManager().getCurrentTime())) { actionCell.setText(LocaleController.getString("StopShowingMe", R.string.StopShowingMe), null, R.drawable.msg_nearby_off, usersStartRow != -1); - actionCell.setColors(Theme.key_windowBackgroundWhiteRedText5, Theme.key_windowBackgroundWhiteRedText5); + actionCell.setColors(Theme.key_text_RedRegular, Theme.key_text_RedRegular); } else { actionCell.setText(LocaleController.getString("MakeMyselfVisible", R.string.MakeMyselfVisible), null, R.drawable.msg_nearby, usersStartRow != -1); } @@ -1110,8 +1110,8 @@ public ArrayList getThemeDescriptions() { themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{ManageChatTextCell.class}, new String[]{"imageView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayIcon)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{ManageChatTextCell.class}, new String[]{"imageView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueButton)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{ManageChatTextCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueIcon)); - themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{ManageChatTextCell.class}, new String[]{"imageView"}, null, null, null, Theme.key_windowBackgroundWhiteRedText5)); - themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{ManageChatTextCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteRedText5)); + themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{ManageChatTextCell.class}, new String[]{"imageView"}, null, null, null, Theme.key_text_RedRegular)); + themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{ManageChatTextCell.class}, new String[]{"textView"}, null, null, null, Theme.key_text_RedRegular)); themeDescriptions.add(new ThemeDescription(undoView, ThemeDescription.FLAG_BACKGROUNDFILTER, null, null, null, null, Theme.key_undo_background)); themeDescriptions.add(new ThemeDescription(undoView, 0, new Class[]{UndoView.class}, new String[]{"undoImageView"}, null, null, null, Theme.key_undo_cancelColor)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PhotoPickerActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PhotoPickerActivity.java index 62a80861d2..4102bc8dd8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PhotoPickerActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PhotoPickerActivity.java @@ -213,9 +213,9 @@ public interface PhotoPickerActivitySearchDelegate { private PhotoPickerActivityDelegate delegate; private PhotoPickerActivitySearchDelegate searchDelegate; - private final String dialogBackgroundKey; - private final String textKey; - private final String selectorKey; + private final int dialogBackgroundKey; + private final int textKey; + private final int selectorKey; private PhotoViewer.PhotoViewerProvider provider = new PhotoViewer.EmptyPhotoViewerProvider() { @Override @@ -254,7 +254,7 @@ public void updatePhotoAtIndex(int index) { if (photoEntry.thumbPath != null) { imageView.setImage(photoEntry.thumbPath, null, Theme.chat_attachEmptyDrawable); } else if (photoEntry.path != null) { - imageView.setOrientation(photoEntry.orientation, true); + imageView.setOrientation(photoEntry.orientation, photoEntry.invert, true); if (photoEntry.isVideo) { imageView.setImage("vthumb://" + photoEntry.imageId + ":" + photoEntry.path, null, Theme.chat_attachEmptyDrawable); } else { @@ -832,7 +832,7 @@ public int getSpanSize(int position) { showDialog(dialog); TextView button = (TextView) dialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } return; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java index 5035e4b9e0..b01a415bb4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java @@ -30,6 +30,7 @@ import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.ColorFilter; import android.graphics.Matrix; import android.graphics.Outline; import android.graphics.Paint; @@ -67,6 +68,7 @@ import android.transition.TransitionSet; import android.transition.TransitionValues; import android.util.FloatProperty; +import android.util.Pair; import android.util.Property; import android.util.Range; import android.util.SparseArray; @@ -118,7 +120,6 @@ import androidx.dynamicanimation.animation.FloatValueHolder; import androidx.dynamicanimation.animation.SpringAnimation; import androidx.dynamicanimation.animation.SpringForce; -import androidx.exifinterface.media.ExifInterface; import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearSmoothScrollerEnd; @@ -131,6 +132,7 @@ import org.jetbrains.annotations.NotNull; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.Bitmaps; import org.telegram.messenger.BringAppForegroundService; @@ -164,6 +166,7 @@ import org.telegram.messenger.WebFile; import org.telegram.messenger.browser.Browser; import org.telegram.messenger.utils.PhotoUtilities; +import org.telegram.messenger.camera.Size; import org.telegram.messenger.video.VideoPlayerRewinder; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLObject; @@ -182,10 +185,13 @@ import org.telegram.ui.Adapters.MentionsAdapter; import org.telegram.ui.Cells.CheckBoxCell; import org.telegram.ui.Cells.PhotoPickerPhotoCell; +import org.telegram.ui.Cells.TextSelectionHelper; import org.telegram.ui.Components.AlertsCreator; import org.telegram.ui.Components.AnimatedEmojiDrawable; import org.telegram.ui.Components.AnimatedEmojiSpan; import org.telegram.ui.Components.AnimatedFileDrawable; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.AnimatedTextView; import org.telegram.ui.Components.AnimationProperties; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.Components.Bulletin; @@ -197,7 +203,6 @@ import org.telegram.ui.Components.Crop.CropTransform; import org.telegram.ui.Components.Crop.CropView; import org.telegram.ui.Components.CubicBezierInterpolator; -import org.telegram.ui.Components.FadingTextViewLayout; import org.telegram.ui.Components.FilterShaders; import org.telegram.ui.Components.FloatSeekBarAccessibilityDelegate; import org.telegram.ui.Components.Forum.ForumUtilities; @@ -253,7 +258,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -278,6 +282,7 @@ @SuppressWarnings("unchecked") public class PhotoViewer implements NotificationCenter.NotificationCenterDelegate, GestureDetector2.OnGestureListener, GestureDetector2.OnDoubleTapListener { private final static float ZOOM_SCALE = 0.1f; + private final static int MARK_DEFERRED_IMAGE_LOADING = 1; private int classGuid; private PhotoViewerProvider placeProvider; @@ -303,6 +308,425 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private boolean isActionBarVisible = true; private boolean isPhotosListViewVisible; private AnimatorSet actionBarAnimator; + private PhotoViewerActionBarContainer actionBarContainer; + private PhotoCountView countView; + public boolean closePhotoAfterSelect = true; + private TextSelectionHelper.SimpleTextSelectionHelper textSelectionHelper; + + private static class PhotoViewerActionBarContainer extends FrameLayout implements NotificationCenter.NotificationCenterDelegate { + + private FrameLayout container; + private FrameLayout titleLayout; + SimpleTextView[] titleTextView; + AnimatedTextView subtitleTextView; + + public PhotoViewerActionBarContainer(Context context) { + super(context); + + container = new FrameLayout(context); + container.setPadding(AndroidUtilities.dp((AndroidUtilities.isTablet() ? 80 : 72) - 16), 0, 0, 0); + addView(container, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); + + titleLayout = new FrameLayout(context); + titleLayout.setPivotX(0); + titleLayout.setPadding(AndroidUtilities.dp(16), 0, 0, 0); + titleLayout.setClipToPadding(false); + container.addView(titleLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); + + titleTextView = new SimpleTextView[2]; + for (int i = 0; i < 2; ++i) { + titleTextView[i] = new SimpleTextView(context); + titleTextView[i].setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL); + titleTextView[i].setTextColor(0xffffffff); + titleTextView[i].setTextSize(20); + titleTextView[i].setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + titleTextView[i].setDrawablePadding(AndroidUtilities.dp(4)); + titleTextView[i].setScrollNonFitText(true); + titleLayout.addView(titleTextView[i], LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.CENTER_VERTICAL)); + } + + subtitleTextView = new AnimatedTextView(context, true, false, false); + subtitleTextView.setAnimationProperties(.4f, 0, 320, CubicBezierInterpolator.EASE_OUT_QUINT); + subtitleTextView.setTextSize(AndroidUtilities.dp(14)); + subtitleTextView.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL); + subtitleTextView.setTextColor(0xffffffff); + subtitleTextView.setEllipsizeByGradient(true); + container.addView(subtitleTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 20, Gravity.LEFT | Gravity.TOP, 16, 0, 0, 0)); + } + + public void setTitle(CharSequence title) { + titleTextView[1].setAlpha(0); + titleTextView[1].setVisibility(View.GONE); + + if (!areStringsEqual(titleTextView[0].getText(), title)) { + titleTextView[0].resetScrolling(); + } + + titleTextView[0].setText(title); + titleTextView[0].setAlpha(1); + titleTextView[0].setTranslationX(0); + titleTextView[0].setTranslationY(0); + } + + public CharSequence getTitle() { + return titleTextView[0].getText(); + } + + private AnimatorSet titleAnimator; + + private boolean areStringsEqual(CharSequence a, CharSequence b) { + if (a == null && b == null) { + return true; + } + if ((a == null) != (b == null)) { + return false; + } + return TextUtils.equals(a.toString(), b.toString()); + } + + // axis: true = top-bottom, false = left-right + // direction: true = from top/from left, false = from bottom/from right + public void setTitleAnimated(CharSequence title, boolean axis, boolean direction) { + if (areStringsEqual(titleTextView[0].getText(), title)) { + return; + } + + if (titleAnimator != null) { + titleAnimator.cancel(); + titleAnimator = null; + } + + titleTextView[1].copyScrolling(titleTextView[0]); + titleTextView[1].setText(titleTextView[0].getText()); + titleTextView[1].setRightPadding((int) rightPadding); + titleTextView[0].resetScrolling(); + titleTextView[0].setText(title); + + float amplitude = AndroidUtilities.dp(8) * (direction ? 1 : -1); + + titleTextView[1].setTranslationX(0); + titleTextView[1].setTranslationY(0); + if (axis) { + titleTextView[0].setTranslationX(0); + titleTextView[0].setTranslationY(-amplitude); + } else { + titleTextView[0].setTranslationX(-amplitude); + titleTextView[0].setTranslationY(0); + } + + titleTextView[0].setAlpha(0); + titleTextView[1].setAlpha(1); + titleTextView[0].setVisibility(View.VISIBLE); + titleTextView[1].setVisibility(View.VISIBLE); + + ArrayList arrayList = new ArrayList<>(); + arrayList.add(ObjectAnimator.ofFloat(titleTextView[1], View.ALPHA, 0)); + arrayList.add(ObjectAnimator.ofFloat(titleTextView[0], View.ALPHA, 1)); + arrayList.add(ObjectAnimator.ofFloat(titleTextView[1], axis ? View.TRANSLATION_Y : View.TRANSLATION_X, amplitude)); + arrayList.add(ObjectAnimator.ofFloat(titleTextView[0], axis ? View.TRANSLATION_Y : View.TRANSLATION_X, 0)); + titleAnimator = new AnimatorSet(); + titleAnimator.playTogether(arrayList); + titleAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (titleAnimator == animation) { + titleTextView[1].setVisibility(View.GONE); + titleAnimator = null; + } + } + }); + titleAnimator.setDuration(320); + titleAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + titleAnimator.start(); + } + + private AnimatorSet subtitleAnimator; + private boolean hasSubtitle; + public void setSubtitle(CharSequence subtitle) { + setSubtitle(subtitle, true); + } + public void setSubtitle(CharSequence subtitle, boolean animated) { + final boolean haveSubtitle = !TextUtils.isEmpty(subtitle); + if (haveSubtitle != hasSubtitle) { + hasSubtitle = haveSubtitle; + + if (subtitleAnimator != null) { + subtitleAnimator.cancel(); + } + + final boolean isLandscape = AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y; + final int subtitleTranslation = AndroidUtilities.dp((haveSubtitle ? 30 : 33) - (isLandscape ? 6 : 0)); + + if (animated) { + ArrayList arrayList = new ArrayList<>(); + arrayList.add(ObjectAnimator.ofFloat(subtitleTextView, View.ALPHA, haveSubtitle ? 1 : 0)); + arrayList.add(ObjectAnimator.ofFloat(subtitleTextView, View.TRANSLATION_Y, subtitleTranslation)); + arrayList.add(ObjectAnimator.ofFloat(titleLayout, View.TRANSLATION_Y, haveSubtitle ? AndroidUtilities.dp(-9) : 0)); + arrayList.add(ObjectAnimator.ofFloat(titleLayout, View.SCALE_X, haveSubtitle ? .95f : 1)); + arrayList.add(ObjectAnimator.ofFloat(titleLayout, View.SCALE_Y, haveSubtitle ? .95f : 1)); + subtitleAnimator = new AnimatorSet(); + subtitleAnimator.playTogether(arrayList); + subtitleAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + subtitleAnimator.start(); + } else { + subtitleTextView.setAlpha(haveSubtitle ? 1 : 0); + subtitleTextView.setTranslationY(subtitleTranslation); + titleLayout.setTranslationY(haveSubtitle ? AndroidUtilities.dp(-9) : 0); + titleLayout.setScaleX(haveSubtitle ? .95f : 1); + titleLayout.setScaleY(haveSubtitle ? .95f : 1); + } + } + subtitleTextView.setText(subtitle, animated); + } + + public void updateOrientation() { + hasSubtitle = !hasSubtitle; + setSubtitle(subtitleTextView.getText(), false); + } + + public void updateRightPadding(int rightPadding) { + updateRightPadding(rightPadding, true); + } + + private ValueAnimator rightPaddingAnimator; + private float rightPadding; + public void updateRightPadding(float rightPadding, boolean animated) { + if (rightPaddingAnimator != null) { + rightPaddingAnimator.cancel(); + rightPaddingAnimator = null; + } + if (animated) { + rightPaddingAnimator = ValueAnimator.ofFloat(this.rightPadding, rightPadding); + rightPaddingAnimator.addUpdateListener(anm -> { + this.rightPadding = (float) anm.getAnimatedValue(); + titleTextView[0].setRightPadding((int) rightPadding); + titleTextView[1].setRightPadding((int) rightPadding); + subtitleTextView.setRightPadding(rightPadding); + }); + rightPaddingAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + updateRightPadding(rightPadding, false); + } + }); + rightPaddingAnimator.setDuration(320); + rightPaddingAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + rightPaddingAnimator.start(); + } else { + this.rightPadding = rightPadding; + titleTextView[0].setRightPadding((int) rightPadding); + subtitleTextView.setRightPadding(rightPadding); + } + } + + int lastHeight; + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int width = MeasureSpec.getSize(widthMeasureSpec); + int height = MeasureSpec.getSize(heightMeasureSpec); + int t = (Build.VERSION.SDK_INT >= 21 ? AndroidUtilities.statusBarHeight : 0); + if (lastHeight != AndroidUtilities.displaySize.y) { + lastHeight = AndroidUtilities.displaySize.y; + updateOrientation(); + } + container.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height - t, MeasureSpec.EXACTLY)); + setMeasuredDimension(width, height); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + int t = (Build.VERSION.SDK_INT >= 21 ? AndroidUtilities.statusBarHeight : 0); + container.layout(0, t, right - left, bottom - top); + } + + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.emojiLoaded) { + titleTextView[0].invalidate(); + titleTextView[1].invalidate(); + subtitleTextView.invalidate(); + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.emojiLoaded); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.emojiLoaded); + } + } + + private static class PhotoCountView extends View { + + Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + AnimatedTextView.AnimatedTextDrawable left; + TextPaint paint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + StaticLayout center; + float centerWidth, centerTop; + AnimatedTextView.AnimatedTextDrawable right; + private String lng; + + public PhotoCountView(Context context) { + super(context); + + backgroundPaint.setColor(Theme.ACTION_BAR_PHOTO_VIEWER_COLOR); + + left = new AnimatedTextView.AnimatedTextDrawable(false, true, true); + left.setAnimationProperties(.3f, 0, 320, CubicBezierInterpolator.EASE_OUT_QUINT); + left.setTextColor(0xffffffff); + left.setTextSize(AndroidUtilities.dp(14)); + left.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + left.setCallback(this); + left.setText("0"); + left.setOverrideFullWidth(AndroidUtilities.displaySize.x); + + paint.setColor(0xffffffff); + paint.setTextSize(AndroidUtilities.dp(14)); + paint.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + setCenterText(); + + right = new AnimatedTextView.AnimatedTextDrawable(false, true, true); + right.setAnimationProperties(.3f, 0, 320, CubicBezierInterpolator.EASE_OUT_QUINT); + right.setTextColor(0xffffffff); + right.setTextSize(AndroidUtilities.dp(14)); + right.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + right.setCallback(this); + right.setText("0"); + right.setOverrideFullWidth(AndroidUtilities.displaySize.x); + } + + private void setCenterText() { + center = new StaticLayout(getOf(), paint, AndroidUtilities.dp(200), Layout.Alignment.ALIGN_CENTER, 1, 0, false); + if (center.getLineCount() >= 1) { + centerWidth = center.getLineWidth(0); + centerTop = center.getLineDescent(0); + } else { + centerWidth = 0; + centerTop = 0; + } + } + + private String getOf() { + lng = LocaleController.getInstance().getCurrentLocaleInfo().shortName; + String text = LocaleController.getString("Of"); // %1$d of %2$d + text = text.replace("%1$d", ""); + text = text.replace("%2$d", ""); + return text; + } + + public void set(int left, int right) { + set(left, right, true); + } + + public void set(int left, int right, boolean animated) { + left = Math.max(0, left); + right = Math.max(left, right); + if ( + LocaleController.getInstance().getCurrentLocaleInfo() != null && + !TextUtils.equals(lng, LocaleController.getInstance().getCurrentLocaleInfo().shortName) + ) { + setCenterText(); + } + this.left.setText(String.format("%d", (LocaleController.isRTL ? right : left)), animated && !nextNotAnimate && !LocaleController.isRTL); + this.right.setText(String.format("%d", (LocaleController.isRTL ? left : right)), animated && !nextNotAnimate && !LocaleController.isRTL); + nextNotAnimate = !animated; + } + + @Override + protected boolean verifyDrawable(@NonNull Drawable who) { + return left == who || right == who || super.verifyDrawable(who); + } + + private boolean shown = false; + private AnimatedFloat showT = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + private boolean nextNotAnimate; + + public void updateShow(boolean show, boolean animated) { + if (shown != show) { + shown = show; + if (!show) { + nextNotAnimate = true; + } + if (!animated) { + showT.set(show ? 1 : 0, true); + } + invalidate(); + } + } + + public boolean isShown() { + return shown; + } + + private int marginTop; + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + float show = this.showT.set(shown ? 1 : 0); + + if (show <= 0) { + return; + } + + float width = left.getCurrentWidth() + centerWidth + right.getCurrentWidth() + AndroidUtilities.dp(9 + 9); + float marginTop = this.marginTop + (1f - show) * -AndroidUtilities.dp(8); + + AndroidUtilities.rectTmp.set( + (getWidth() - width) / 2f, + marginTop + AndroidUtilities.dpf2(10), + (getWidth() + width) / 2f, + marginTop + AndroidUtilities.dpf2(10 + 23) + ); + int wasAlpha = backgroundPaint.getAlpha(); + backgroundPaint.setAlpha((int) (wasAlpha * show)); + canvas.drawRoundRect( + AndroidUtilities.rectTmp, + AndroidUtilities.dpf2(12), + AndroidUtilities.dpf2(12), + backgroundPaint + ); + backgroundPaint.setAlpha(wasAlpha); + + canvas.save(); + canvas.translate((getWidth() - width) / 2f + AndroidUtilities.dp(9), marginTop + AndroidUtilities.dp(10)); + left.setBounds(0, 0, (int) left.getCurrentWidth(), AndroidUtilities.dp(23)); + left.setAlpha((int) (0xFF * show)); + left.draw(canvas); + + canvas.translate(left.getCurrentWidth(), 0); + canvas.save(); + canvas.translate(-(center.getWidth() - centerWidth) / 2f, (AndroidUtilities.dp(23) - center.getHeight() + centerTop / 2f) / 2f); + paint.setAlpha((int) (0xFF * show)); + center.draw(canvas); + canvas.restore(); + + canvas.translate(centerWidth, 0); + right.setBounds(0, 0, (int) right.getCurrentWidth(), AndroidUtilities.dp(23)); + right.setAlpha((int) (0xFF * show)); + right.draw(canvas); + canvas.restore(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int width = MeasureSpec.getSize(widthMeasureSpec); + marginTop = ActionBar.getCurrentActionBarHeight() + (Build.VERSION.SDK_INT >= 21 ? AndroidUtilities.statusBarHeight : 0); + left.setOverrideFullWidth(width); + right.setOverrideFullWidth(width); + super.onMeasure( + MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(marginTop + AndroidUtilities.dp(10 + 23 + 10), MeasureSpec.EXACTLY) + ); + } + } private int currentAccount; @@ -314,10 +738,10 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private static final int PROGRESS_PAUSE = 4; private static Drawable[] progressDrawables; - private static final int EDIT_MODE_NONE = 0; - private static final int EDIT_MODE_CROP = 1; - private static final int EDIT_MODE_FILTER = 2; - private static final int EDIT_MODE_PAINT = 3; + public static final int EDIT_MODE_NONE = 0; + public static final int EDIT_MODE_CROP = 1; + public static final int EDIT_MODE_FILTER = 2; + public static final int EDIT_MODE_PAINT = 3; private int videoWidth, videoHeight; private float inlineOutAnimationProgress; @@ -330,8 +754,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private FrameLayout bottomLayout; private View navigationBar; private int navigationBarHeight; - private FadingTextViewLayout nameTextView; - private FadingTextViewLayout dateTextView; private TextView docNameTextView; private TextView docInfoTextView; private TextView doneButtonFullWidth; @@ -341,16 +763,14 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private ActionBarMenuItem sendNoQuoteItem; private ActionBarMenuSubItem speedItem; private ActionBarPopupWindow.GapView speedGap; + private ActionBarMenu menu; private ActionBarMenuItem sendItem; + private ActionBarMenuItem editItem; private ActionBarMenuItem pipItem; private ActionBarMenuItem masksItem; - private ActionBarMenuItem shareItem; private LinearLayout itemsLayout; ChooseSpeedLayout chooseSpeedLayout; private Map actionBarItemsVisibility = new HashMap<>(3); - private LinearLayout bottomButtonsLayout; - private ImageView shareButton; - private ImageView paintButton; private BackgroundDrawable backgroundDrawable = new BackgroundDrawable(0xff000000); private Paint blackPaint = new Paint(); private CheckBox checkImageView; @@ -1129,7 +1549,7 @@ public boolean onPreDraw() { private PlaceProviderObject hideAfterAnimation; private boolean disableShowCheck; - private String lastTitle; + private CharSequence lastTitle; private boolean isEmbedVideo; @@ -1141,7 +1561,7 @@ public boolean onPreDraw() { } }; - private static class EditState { + public static class EditState { public String paintPath; public String croppedPaintPath; public MediaController.CropState cropState; @@ -1244,6 +1664,7 @@ public void restore() { private boolean[] endReached = new boolean[]{false, true}; private boolean startReached = false; private boolean opennedFromMedia; + private boolean openedFromProfile; private boolean attachedToWindow; @@ -1362,6 +1783,7 @@ public interface PageBlocksAdapter { 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_paint = 20; private final static int gallery_menu_copy = 100; private final static int gallery_menu_set_photo = 101; @@ -1369,7 +1791,13 @@ public interface PageBlocksAdapter { private static DecelerateInterpolator decelerateInterpolator; private static Paint progressPaint; - private int transitionIndex; + private AnimationNotificationsLocker transitionNotificationLocker = new AnimationNotificationsLocker(new int[]{ + NotificationCenter.dialogsNeedReload, + NotificationCenter.closeChats, + NotificationCenter.mediaCountDidLoad, + NotificationCenter.mediaDidLoad, + NotificationCenter.dialogPhotosLoaded + }); private class BackgroundDrawable extends ColorDrawable { @@ -2153,6 +2581,12 @@ default void spoilerPressed() { } default void onPreOpen() {} default void onPreClose() {} + default boolean onDeletePhoto(int index) { + return true; + } + default boolean canLoadMoreAvatars() { + return true; + } } private class FrameLayoutDrawer extends SizeNotifierFrameLayoutPhoto { @@ -2167,6 +2601,9 @@ protected void onPanTranslationUpdate(float y, float progress, boolean keyboardV currentPanTranslationY = y; if (currentEditMode != EDIT_MODE_PAINT) { actionBar.setTranslationY(y); + if (countView != null) { + countView.setTranslationY(y); + } } if (miniProgressView != null) { miniProgressView.setTranslationY(y); @@ -2242,9 +2679,9 @@ protected void onTransitionStart(boolean keyboardVisible, int contentHeight) { if (captionEditText.getTag() != null && keyboardVisible) { if (isCurrentVideo) { CharSequence title = muteVideo ? LocaleController.getString("GifCaption", R.string.GifCaption) : LocaleController.getString("VideoCaption", R.string.VideoCaption); - actionBar.setTitleAnimated(title, true, 220); + actionBarContainer.setTitleAnimated(title, true, false); } else { - actionBar.setTitleAnimated(LocaleController.getString("PhotoCaption", R.string.PhotoCaption), true, 220); + actionBarContainer.setTitleAnimated(LocaleController.getString("PhotoCaption", R.string.PhotoCaption), true, false); } captionEditText.setAlpha(0f); @@ -2256,7 +2693,7 @@ protected void onTransitionStart(boolean keyboardVisible, int contentHeight) { photosCounterView.animate().alpha(1f).setDuration(220).start(); if (lastTitle != null) { if (!isCurrentVideo) { - actionBar.setTitleAnimated(lastTitle, false, 220); + actionBarContainer.setTitleAnimated(lastTitle, true, false); lastTitle = null; } } @@ -2279,8 +2716,8 @@ protected boolean heightAnimationEnabled() { } }; - public FrameLayoutDrawer(Context context) { - super(context, false); + public FrameLayoutDrawer(Context context, Activity activity) { + super(context, activity, false); setWillNotDraw(false); paint.setColor(0x33000000); } @@ -2495,6 +2932,28 @@ protected void onLayout(boolean changed, int _l, int t, int _r, int _b) { } notifyHeightChanged(); + updateExclusionRects(); + } + + private ArrayList exclusionRects; + + public void updateExclusionRects() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + if (exclusionRects == null) { + exclusionRects = new ArrayList<>(); + } + exclusionRects.clear(); + if (currentEditMode == EDIT_MODE_CROP || switchingToMode == EDIT_MODE_CROP) { + int h = getMeasuredHeight(), w = getMeasuredWidth(); +// exclusionRects.add(new Rect(0, 0, AndroidUtilities.dp(50), h)); +// exclusionRects.add(new Rect(0, 0, w, AndroidUtilities.dp(100))); +// exclusionRects.add(new Rect(w - AndroidUtilities.dp(50), 0, w, h)); + exclusionRects.add(new Rect(0, (h - AndroidUtilities.dp(200)) / 2, AndroidUtilities.dp(100), (h + AndroidUtilities.dp(200)) / 2)); + exclusionRects.add(new Rect(w - AndroidUtilities.dp(100), (h - AndroidUtilities.dp(200)) / 2, w, (h + AndroidUtilities.dp(200)) / 2)); + } + setSystemGestureExclusionRects(exclusionRects); + invalidate(); + } } @Override @@ -2575,7 +3034,7 @@ protected boolean drawChildInternal(Canvas canvas, View child, long drawingTime) canvas.restore(); return r; } - } else if (child == cameraItem || child == muteItem || child == pickerView || child == videoTimelineView || child == pickerViewSendButton || child == captionLimitView || child == captionTextViewSwitcher || muteItem.getVisibility() == VISIBLE && child == bottomLayout) { + } else if (child == cameraItem || child == muteItem || child == pickerView || child == videoTimelineView || child == pickerViewSendButton || child == captionLimitView || child == captionTextViewSwitcher) { if (captionEditText.isPopupAnimating()) { child.setTranslationY(captionEditText.getEmojiPadding()); bottomTouchEnabled = false; @@ -3081,7 +3540,7 @@ public int calculateNewContainerMarginTop(int width, int height) { cycle: while (i > 1) { for (int j = layout.getLineStart(i - 1); j < layout.getLineEnd(i - 1); j++) { - if (Character.isLetterOrDigit(text.charAt(j))) { + if (!Character.isWhitespace(text.charAt(j))) { break cycle; } } @@ -3488,7 +3947,7 @@ public void didReceivedNotification(int id, int account, Object... args) { } } } - if (fromCache) { + if (fromCache && placeProvider.canLoadMoreAvatars()) { MessagesController.getInstance(currentAccount).loadDialogPhotos(avatarsDialogId, 80, 0, false, classGuid); } } @@ -3507,17 +3966,7 @@ public void didReceivedNotification(int id, int account, Object... args) { loadingMoreImages = true; MediaDataController.getInstance(currentAccount).loadMedia(currentDialogId, 20, 0, 0, sharedMediaType, topicId, 1, classGuid, 0); } else if (!imagesArr.isEmpty()) { - if (opennedFromMedia) { - actionBar.setTitle(LocaleController.formatString("Of", R.string.Of, startOffset + currentIndex + 1, totalImagesCount + totalImagesCountMerge)); - } else if (currentMessageObject != null && currentMessageObject.hasRevealedExtendedMedia()) { - if (currentMessageObject.isVideo()) { - actionBar.setTitle(LocaleController.formatString(R.string.AttachVideo)); - } else { - actionBar.setTitle(LocaleController.formatString(R.string.AttachPhoto)); - } - } else { - actionBar.setTitle(LocaleController.formatString("Of", R.string.Of, (totalImagesCount + totalImagesCountMerge - imagesArr.size()) + currentIndex + 1, totalImagesCount + totalImagesCountMerge)); - } + setIsAboutToSwitchToIndex(switchingToIndex, true, true); } } } @@ -3655,7 +4104,7 @@ public void didReceivedNotification(int id, int account, Object... args) { preparePlayer(currentPlayingVideoFile, false, false, editState.savedFilterState); } else if (tryStartRequestPreviewOnFinish) { releasePlayer(false); - tryStartRequestPreviewOnFinish = !MediaController.getInstance().scheduleVideoConvert(videoPreviewMessageObject, true); + tryStartRequestPreviewOnFinish = !MediaController.getInstance().scheduleVideoConvert(videoPreviewMessageObject, true, true); } else if (messageObject == videoPreviewMessageObject) { requestingPreview = false; progressView.setVisibility(View.INVISIBLE); @@ -4074,7 +4523,7 @@ protected void dispatchDraw(Canvas canvas) { animatingImageView.setAnimationValues(animationValues); windowView.addView(animatingImageView, LayoutHelper.createFrame(40, 40)); - containerView = new FrameLayoutDrawer(activity) { + containerView = new FrameLayoutDrawer(activity, activity) { private Bulletin.Delegate delegate = new Bulletin.Delegate() { @Override public int getBottomOffset(int tag) { @@ -4085,6 +4534,19 @@ public int getBottomOffset(int tag) { } }; + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + if (textSelectionHelper.isInSelectionMode()) { + textSelectionHelper.getOverlayView(getContext()).checkCancelAction(ev); + + if (textSelectionHelper.getOverlayView(getContext()).onTouchEvent(ev)) { + return true; + } + return true; + } + return super.dispatchTouchEvent(ev); + } + @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); @@ -4098,6 +4560,22 @@ protected void onDetachedFromWindow() { Bulletin.removeDelegate(this); } + + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + View overlay = textSelectionHelper.getOverlayView(windowView.getContext()); + if (child == overlay) { + return false; + } + return super.drawChild(canvas, child, drawingTime); + } + + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + View overlay = textSelectionHelper.getOverlayView(windowView.getContext()); + overlay.draw(canvas); + } }; containerView.setFocusable(false); @@ -4190,15 +4668,18 @@ public void setAlpha(float alpha) { actionBar.setOccupyStatusBar(isStatusBarVisible()); actionBar.setItemsBackgroundColor(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR, false); actionBar.setBackButtonImage(R.drawable.ic_ab_back); - actionBar.setTitle(LocaleController.formatString("Of", R.string.Of, 1, 1)); + actionBarContainer = new PhotoViewerActionBarContainer(activity); + actionBar.addView(actionBarContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); containerView.addView(actionBar, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + countView = new PhotoCountView(activity); + containerView.addView(countView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.FILL_HORIZONTAL | Gravity.TOP)); + actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { @Override public void onItemClick(int id) { if (id == -1) { - if (photoPaintView != null) { - photoPaintView.onBackPressed(); + if (photoPaintView != null && photoPaintView.onBackPressed()) { return; } if (needCaptionLayout && (captionEditText.isPopupShowing() || captionEditText.isKeyboardVisible())) { @@ -4252,7 +4733,7 @@ public void onItemClick(int id) { } if (f != null && f.exists()) { - MediaController.saveFile(f.toString(), parentActivity, isVideo ? 1 : 0, null, null, () -> BulletinFactory.createSaveToGalleryBulletin(containerView, isVideo, 0xf9222222, 0xffffffff).show()); + MediaController.saveFile(f.toString(), parentActivity, isVideo ? 1 : 0, null, null, uri -> BulletinFactory.createSaveToGalleryBulletin(containerView, isVideo, 0xf9222222, 0xffffffff).show()); } else { showDownloadAlert(); } @@ -4283,7 +4764,7 @@ public void onItemClick(int id) { boolean isThisVideo = currentMessageObject.isVideo(); if (f != null && f.exists()) { - MediaController.saveFile(f.toString(), parentActivity, isThisVideo ? 1 : 0, null, null, () -> BulletinFactory.createSaveToGalleryBulletin(containerView, isThisVideo, 0xf9222222, 0xffffffff).show()); + MediaController.saveFile(f.toString(), parentActivity, isThisVideo ? 1 : 0, null, null, uri -> BulletinFactory.createSaveToGalleryBulletin(containerView, isThisVideo, 0xf9222222, 0xffffffff).show()); } else { showDownloadAlert(); } @@ -4313,7 +4794,7 @@ public void onItemClick(int id) { if (f != null && f.exists()) { count[0]++; - MediaController.saveFile(f.toString(), parentActivity, isThisVideo ? 1 : 0, null, null, () -> AndroidUtilities.runOnUIThread(bulletin)); + MediaController.saveFile(f.toString(), parentActivity, isThisVideo ? 1 : 0, null, null, uri -> AndroidUtilities.runOnUIThread(bulletin)); } } }) @@ -4324,8 +4805,8 @@ public void onItemClick(int id) { dialog.show(); View neutralButton = dialog.getButton(Dialog.BUTTON_NEUTRAL); if (neutralButton instanceof TextView) { - ((TextView) neutralButton).setTextColor(getThemedColor(Theme.key_dialogTextRed)); - neutralButton.setBackground(Theme.getRoundRectSelectorDrawable(getThemedColor(Theme.key_dialogTextRed))); + ((TextView) neutralButton).setTextColor(getThemedColor(Theme.key_text_RedBold)); + neutralButton.setBackground(Theme.getRoundRectSelectorDrawable(getThemedColor(Theme.key_text_RedBold))); if (dialog.getButtonsLayout() instanceof LinearLayout && ((LinearLayout) dialog.getButtonsLayout()).getOrientation() == LinearLayout.VERTICAL) { neutralButton.bringToFront(); } @@ -4461,8 +4942,8 @@ public void onItemClick(int id) { dialog.show(); View neutralButton = dialog.getButton(Dialog.BUTTON_NEUTRAL); if (neutralButton instanceof TextView) { - ((TextView) neutralButton).setTextColor(getThemedColor(Theme.key_dialogTextRed)); - neutralButton.setBackground(Theme.getRoundRectSelectorDrawable(getThemedColor(Theme.key_dialogTextRed))); + ((TextView) neutralButton).setTextColor(getThemedColor(Theme.key_text_RedBold)); + neutralButton.setBackground(Theme.getRoundRectSelectorDrawable(getThemedColor(Theme.key_text_RedBold))); if (dialog.getButtonsLayout() instanceof LinearLayout && ((LinearLayout) dialog.getButtonsLayout()).getOrientation() == LinearLayout.VERTICAL) { neutralButton.bringToFront(); } @@ -4482,7 +4963,7 @@ public void onItemClick(int id) { for (int a = 0; a < dids.size(); a++) { long did = dids.get(a).dialogId; if (message != null) { - SendMessagesHelper.getInstance(currentAccount).sendMessage(message.toString(), did, null, null, null, true, null, null, null, true, 0, null, false); + SendMessagesHelper.getInstance(currentAccount).sendMessage(SendMessagesHelper.SendMessageParams.of(message.toString(), did, null, null, null, true, null, null, null, true, 0, null, false)); } if (id == gallery_menu_send_noquote) { //MessageHelper.getInstance(currentAccount).processForwardFromMyName(fmessages, did, true, 0); @@ -4531,6 +5012,8 @@ public void onItemClick(int id) { ((LaunchActivity) parentActivity).presentFragment(fragment, false, true); closePhoto(false, false); } + } else if (id == gallery_menu_paint) { + openCurrentPhotoInPaintModeForSelect(); } else if (id == gallery_menu_delete) { if (parentActivity == null || placeProvider == null) { return; @@ -4620,6 +5103,10 @@ public void onItemClick(int id) { } } builder.setPositiveButton(LocaleController.getString("Delete", R.string.Delete), (dialogInterface, i) -> { + if (!placeProvider.onDeletePhoto(currentIndex)) { + closePhoto(false, false); + return; + } if (!imagesArr.isEmpty()) { if (currentIndex < 0 || currentIndex >= imagesArr.size()) { return; @@ -4720,7 +5207,7 @@ public void onItemClick(int id) { showAlertDialog(builder); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(getThemedColor(Theme.key_dialogTextRed)); + button.setTextColor(getThemedColor(Theme.key_text_RedBold)); } } else if (id == gallery_menu_share || id == gallery_menu_share2) { onSharePressed(); @@ -4998,7 +5485,9 @@ public boolean canOpenMenu() { } }); - ActionBarMenu menu = actionBar.createMenu(); + menu = actionBar.createMenu(); + menu.setOnLayoutListener(this::updateActionBarTitlePadding); + masksItem = menu.addItem(gallery_menu_masks, R.drawable.msg_mask); masksItem.setContentDescription(LocaleController.getString("Masks", R.string.Masks)); masksItem.setIconColor(0xfffafafa); @@ -5015,9 +5504,8 @@ public boolean canOpenMenu() { sendItem.setContentDescription(LocaleController.getString("Forward", R.string.Forward)); sendItem.setIconColor(0xfffafafa); - shareItem = menu.addItem(gallery_menu_share2, R.drawable.share); - shareItem.setContentDescription(LocaleController.getString("ShareFile", R.string.ShareFile)); - shareItem.setIconColor(0xfffafafa); + editItem = menu.addItem(gallery_menu_paint, R.drawable.msg_header_draw); + editItem.setContentDescription(LocaleController.getString("AccDescrPhotoEditor", R.string.AccDescrPhotoEditor)); menuItem = menu.addItem(0, menuItemIcon = new OptionsSpeedIconDrawable()); menuItem.setOnMenuDismiss(byClick -> checkProgress(0, false, false)); @@ -5095,15 +5583,7 @@ public void onHideSubMenu() { } }); - bottomLayout = new FrameLayout(activityContext) { - @Override - protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { - if (child == nameTextView || child == dateTextView) { - widthUsed = bottomButtonsLayout.getMeasuredWidth(); - } - super.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed); - } - }; + bottomLayout = new FrameLayout(activityContext); bottomLayout.setBackgroundColor(0x7f000000); containerView.addView(bottomLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.BOTTOM | Gravity.LEFT)); @@ -5272,86 +5752,102 @@ public void invalidate() { miniProgressView.setAlpha(0.0f); containerView.addView(miniProgressView, LayoutHelper.createFrame(64, 64, Gravity.CENTER)); - bottomButtonsLayout = new LinearLayout(containerView.getContext()); - bottomButtonsLayout.setOrientation(LinearLayout.HORIZONTAL); - bottomLayout.addView(bottomButtonsLayout, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.RIGHT)); - - paintButton = new ImageView(containerView.getContext()); - paintButton.setImageResource(R.drawable.msg_photo_draw); - paintButton.setScaleType(ImageView.ScaleType.CENTER); - paintButton.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR)); - bottomButtonsLayout.addView(paintButton, LayoutHelper.createFrame(50, LayoutHelper.MATCH_PARENT)); - paintButton.setOnClickListener(v -> openCurrentPhotoInPaintModeForSelect()); - paintButton.setContentDescription(LocaleController.getString("AccDescrPhotoEditor", R.string.AccDescrPhotoEditor)); - - shareButton = new ImageView(containerView.getContext()); - shareButton.setImageResource(R.drawable.share); - shareButton.setColorFilter(new PorterDuffColorFilter(0xfffafafa, PorterDuff.Mode.SRC_IN)); - shareButton.setScaleType(ImageView.ScaleType.CENTER); - shareButton.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR)); - bottomButtonsLayout.addView(shareButton, LayoutHelper.createFrame(50, LayoutHelper.MATCH_PARENT)); - shareButton.setOnClickListener(v -> onSharePressed()); - shareButton.setContentDescription(LocaleController.getString("ShareFile", R.string.ShareFile)); - - nameTextView = new FadingTextViewLayout(containerView.getContext()) { - @Override - protected void onTextViewCreated(TextView textView) { - super.onTextViewCreated(textView); - textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - textView.setEllipsize(TextUtils.TruncateAt.END); - textView.setTextColor(0xffffffff); - textView.setGravity(Gravity.LEFT); - } - }; - - bottomLayout.addView(nameTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.LEFT, 16, 5, 8, 0)); - - dateTextView = new FadingTextViewLayout(containerView.getContext(), true) { - - private LocaleController.LocaleInfo lastLocaleInfo = null; - private int staticCharsCount = 0; - - @Override - protected void onTextViewCreated(TextView textView) { - super.onTextViewCreated(textView); - textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); - textView.setEllipsize(TextUtils.TruncateAt.END); - textView.setTextColor(0xffffffff); - textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - textView.setGravity(Gravity.LEFT); - } - - @Override - protected int getStaticCharsCount() { - final LocaleController.LocaleInfo localeInfo = LocaleController.getInstance().getCurrentLocaleInfo(); - if (lastLocaleInfo != localeInfo) { - lastLocaleInfo = localeInfo; - staticCharsCount = LocaleController.formatString("formatDateAtTime", R.string.formatDateAtTime, LocaleController.getInstance().formatterYear.format(new Date()), LocaleController.getInstance().formatterDay.format(new Date())).length(); - } - return staticCharsCount; - } - - @Override - public void setText(CharSequence text, boolean animated) { - if (animated) { - boolean dontAnimateUnchangedStaticChars = true; - if (LocaleController.isRTL) { - final int staticCharsCount = getStaticCharsCount(); - if (staticCharsCount > 0) { - if (text.length() != staticCharsCount || getText() == null || getText().length() != staticCharsCount) { - dontAnimateUnchangedStaticChars = false; - } - } - } - setText(text, true, dontAnimateUnchangedStaticChars); - } else { - setText(text, false, false); - } - } - }; - - bottomLayout.addView(dateTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.LEFT, 16, 25, 8, 0)); +// bottomButtonsLayout = new LinearLayout(containerView.getContext()); +// bottomButtonsLayout.setOrientation(LinearLayout.HORIZONTAL); +// bottomLayout.addView(bottomButtonsLayout, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.RIGHT)); +// +// paintButton = new ImageView(containerView.getContext()); +// paintButton.setImageResource(R.drawable.msg_photo_draw); +// paintButton.setScaleType(ImageView.ScaleType.CENTER); +// paintButton.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR)); +// bottomButtonsLayout.addView(paintButton, LayoutHelper.createFrame(50, LayoutHelper.MATCH_PARENT)); +// paintButton.setOnClickListener(v -> openCurrentPhotoInPaintModeForSelect()); +// paintButton.setContentDescription(LocaleController.getString("AccDescrPhotoEditor", R.string.AccDescrPhotoEditor)); +// +// shareButton = new ImageView(containerView.getContext()); +// shareButton.setImageResource(R.drawable.share); +// shareButton.setScaleType(ImageView.ScaleType.CENTER); +// shareButton.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR)); +// bottomButtonsLayout.addView(shareButton, LayoutHelper.createFrame(50, LayoutHelper.MATCH_PARENT)); +// shareButton.setOnClickListener(v -> onSharePressed()); +// shareButton.setContentDescription(LocaleController.getString("ShareFile", R.string.ShareFile)); + +// paintButton = new ImageView(containerView.getContext()); +// paintButton.setImageResource(R.drawable.msg_photo_draw); +// paintButton.setScaleType(ImageView.ScaleType.CENTER); +// paintButton.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR)); +// bottomButtonsLayout.addView(paintButton, LayoutHelper.createFrame(50, LayoutHelper.MATCH_PARENT)); +// paintButton.setOnClickListener(v -> openCurrentPhotoInPaintModeForSelect()); +// paintButton.setContentDescription(LocaleController.getString("AccDescrPhotoEditor", R.string.AccDescrPhotoEditor)); +// +// shareButton = new ImageView(containerView.getContext()); +// shareButton.setImageResource(R.drawable.share); +// shareButton.setColorFilter(new PorterDuffColorFilter(0xfffafafa, PorterDuff.Mode.SRC_IN)); +// shareButton.setScaleType(ImageView.ScaleType.CENTER); +// shareButton.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR)); +// bottomButtonsLayout.addView(shareButton, LayoutHelper.createFrame(50, LayoutHelper.MATCH_PARENT)); +// shareButton.setOnClickListener(v -> onSharePressed()); +// shareButton.setContentDescription(LocaleController.getString("ShareFile", R.string.ShareFile)); +// +// nameTextView = new FadingTextViewLayout(containerView.getContext()) { +// @Override +// protected void onTextViewCreated(TextView textView) { +// super.onTextViewCreated(textView); +// textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); +// textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); +// textView.setEllipsize(TextUtils.TruncateAt.END); +// textView.setTextColor(0xffffffff); +// textView.setGravity(Gravity.LEFT); +// } +// }; +// +// bottomLayout.addView(nameTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.LEFT, 16, 5, 8, 0)); +// +// dateTextView = new FadingTextViewLayout(containerView.getContext(), true) { +// +// private LocaleController.LocaleInfo lastLocaleInfo = null; +// private int staticCharsCount = 0; +// +// @Override +// protected void onTextViewCreated(TextView textView) { +// super.onTextViewCreated(textView); +// textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); +// textView.setEllipsize(TextUtils.TruncateAt.END); +// textView.setTextColor(0xffffffff); +// textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); +// textView.setGravity(Gravity.LEFT); +// } +// +// @Override +// protected int getStaticCharsCount() { +// final LocaleController.LocaleInfo localeInfo = LocaleController.getInstance().getCurrentLocaleInfo(); +// if (lastLocaleInfo != localeInfo) { +// lastLocaleInfo = localeInfo; +// staticCharsCount = LocaleController.formatString("formatDateAtTime", R.string.formatDateAtTime, LocaleController.getInstance().formatterYear.format(new Date()), LocaleController.getInstance().formatterDay.format(new Date())).length(); +// } +// return staticCharsCount; +// } +// +// @Override +// public void setText(CharSequence text, boolean animated) { +// if (animated) { +// boolean dontAnimateUnchangedStaticChars = true; +// if (LocaleController.isRTL) { +// final int staticCharsCount = getStaticCharsCount(); +// if (staticCharsCount > 0) { +// if (text.length() != staticCharsCount || getText() == null || getText().length() != staticCharsCount) { +// dontAnimateUnchangedStaticChars = false; +// } +// } +// } +// setText(text, true, dontAnimateUnchangedStaticChars); +// } else { +// setText(text, false, false); +// } +// } +// }; +// +// bottomLayout.addView(dateTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.LEFT, 16, 25, 8, 0)); createVideoControlsInterface(); @@ -5912,7 +6408,6 @@ public void setAlpha(float alpha) { return false; }); - captionLimitView = new TextView(parentActivity); captionLimitView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); captionLimitView.setTextColor(0xffEC7777); @@ -6549,9 +7044,9 @@ public void onEmojiViewCloseStart() { setOffset(captionEditText.getEmojiPadding()); if (captionEditText.getTag() != null) { if (isCurrentVideo) { - actionBar.setTitleAnimated(muteVideo ? LocaleController.getString("GifCaption", R.string.GifCaption) : LocaleController.getString("VideoCaption", R.string.VideoCaption), true, 220); + actionBarContainer.setTitleAnimated(muteVideo ? LocaleController.getString("GifCaption", R.string.GifCaption) : LocaleController.getString("VideoCaption", R.string.VideoCaption),true, false); } else { - actionBar.setTitleAnimated(LocaleController.getString("PhotoCaption", R.string.PhotoCaption), true, 220); + actionBarContainer.setTitleAnimated(LocaleController.getString("PhotoCaption", R.string.PhotoCaption), true, false); } checkImageView.animate().alpha(0f).setDuration(220).start(); photosCounterView.animate().alpha(0f).setDuration(220).start(); @@ -6560,7 +7055,7 @@ public void onEmojiViewCloseStart() { checkImageView.animate().alpha(1f).setDuration(220).start(); photosCounterView.animate().alpha(1f).setDuration(220).start(); if (lastTitle != null) { - actionBar.setTitleAnimated(lastTitle, false, 220); + actionBarContainer.setTitleAnimated(lastTitle, true, false); lastTitle = null; } } @@ -6575,7 +7070,7 @@ public void onEmojiViewCloseEnd() { private void setOffset(int offset) { for (int i = 0; i < containerView.getChildCount(); i++) { View child = containerView.getChildAt(i); - if (child == cameraItem || child == muteItem || child == pickerView || child == videoTimelineView || child == pickerViewSendButton || child == captionTextViewSwitcher || muteItem.getVisibility() == View.VISIBLE && child == bottomLayout) { + if (child == cameraItem || child == muteItem || child == pickerView || child == videoTimelineView || child == pickerViewSendButton || child == captionTextViewSwitcher) { child.setTranslationY(offset); } } @@ -6817,6 +7312,16 @@ public void onContextClick(TLRPC.BotInlineResult result) { doneButtonFullWidth.setBackground(Theme.AdaptiveRipple.filledRect(getThemedColor(Theme.key_featuredStickers_addButton), 6)); doneButtonFullWidth.setTextColor(getThemedColor(Theme.key_featuredStickers_buttonText)); + + textSelectionHelper = new TextSelectionHelper.SimpleTextSelectionHelper(null, resourcesProvider); + textSelectionHelper.useMovingOffset = false; + View overlay = textSelectionHelper.getOverlayView(windowView.getContext()); + if (overlay != null) { + AndroidUtilities.removeFromParent(overlay); + containerView.addView(overlay); + } + textSelectionHelper.setParentView(containerView); + textSelectionHelper.setInvalidateParent(); } public void showCaptionLimitBulletin(FrameLayout view) { @@ -7074,7 +7579,9 @@ public void onDismiss(DialogInterface dialog) { } else { placeProvider.replaceButtonPressed(currentIndex, videoEditedInfo); } - closePhoto(false, false); + if (closePhotoAfterSelect) { + closePhoto(false, false); + } } } @@ -7185,6 +7692,26 @@ public void dismissInternal() { alert.show(); } + private void updateActionBarTitlePadding() { + if (menu == null || actionBarContainer == null) { + return; + } + float width = 0; + for (int i = 0; i < menu.getChildCount(); ++i) { + View child = menu.getChildAt(i); + if (child.getVisibility() == View.VISIBLE) { + width += 2 * Math.min(.5f, child.getAlpha()) * child.getWidth(); + } + } + if (checkImageView != null && checkImageView.getVisibility() == View.VISIBLE) { + width = Math.max(width, AndroidUtilities.dp(48)); + } + if (photosCounterView != null && photosCounterView.getVisibility() == View.VISIBLE) { + width = Math.max(width, AndroidUtilities.dp(100)); + } + actionBarContainer.updateRightPadding(width, false); + } + private void setMenuItemIcon(boolean animated, boolean isFinal) { if (speedItem.getVisibility() != View.VISIBLE) { menuItemIcon.setSpeed(null, animated); @@ -7265,7 +7792,7 @@ public void onAnimationCancel(Animator animation) { } private TextView createCaptionTextView() { - TextView textView = new SpoilersTextView(activityContext) { + SpoilersTextView textView = new SpoilersTextView(activityContext) { private LinkSpanDrawable pressedLink; private LinkSpanDrawable.LinkCollector links = new LinkSpanDrawable.LinkCollector(this); @@ -7275,6 +7802,11 @@ public boolean onTouchEvent(MotionEvent event) { if (getLayout() == null) { return false; } + if (textSelectionHelper != null && getStaticTextLayout() != null) { + textSelectionHelper.setSelectabeleView(this); + textSelectionHelper.update(getPaddingLeft(), getPaddingTop()); + textSelectionHelper.onTouchEvent(event); + } boolean linkResult = false; if (event.getAction() == MotionEvent.ACTION_DOWN || pressedLink != null && event.getAction() == MotionEvent.ACTION_UP) { int x = (int) (event.getX() - getPaddingLeft()); @@ -7329,6 +7861,7 @@ public boolean onTouchEvent(MotionEvent event) { return bottomTouchEnabled && b; } + @Override public void setPressed(boolean pressed) { final boolean needsRefresh = pressed != isPressed(); @@ -7342,6 +7875,15 @@ public void setPressed(boolean pressed) { @Override protected void onDraw(Canvas canvas) { + if (textSelectionHelper != null && textSelectionHelper.isInSelectionMode()) { + canvas.save(); + canvas.translate(getPaddingLeft(), getPaddingTop()); + if (textSelectionHelper != null && getStaticTextLayout() != null && textSelectionHelper.isCurrent(this)) { + textSelectionHelper.draw(canvas); + } + canvas.restore(); + } + canvas.save(); canvas.translate(getPaddingLeft(), 0); if (links.draw(canvas)) { @@ -7369,6 +7911,8 @@ protected void onTextChanged(CharSequence text, int start, int lengthBefore, int animatedEmojiDrawables = AnimatedEmojiSpan.update(AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES, this, animatedEmojiDrawables, getLayout()); } + private final ColorFilter emojiTextColorFilter = new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN); + @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); @@ -7379,6 +7923,7 @@ protected void dispatchDraw(Canvas canvas) { canvas.restore(); } }; + ViewHelper.setPadding(textView, 16, 8, 16, 8); textView.setLinkTextColor(0xff79c4fc); textView.setTextColor(0xffffffff); @@ -7387,6 +7932,7 @@ protected void dispatchDraw(Canvas canvas) { textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); textView.setOnClickListener((v) -> { if (!needCaptionLayout) { + captionScrollView.smoothScrollBy(0, AndroidUtilities.dp(64)); return; } openCaptionEnter(); @@ -8037,7 +8583,7 @@ private void openCaptionEnter() { captionEditText.setTag(1); captionEditText.openKeyboard(); captionEditText.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO); - lastTitle = actionBar.getTitle(); + lastTitle = actionBarContainer.getTitle(); captionEditText.setVisibility(View.VISIBLE); } @@ -8250,8 +8796,8 @@ private void closeCaptionEnter(boolean apply) { } captionEditText.setTag(null); if (isCurrentVideo && customTitle == null) { - actionBar.setTitleAnimated(lastTitle, false, 220); - actionBar.setSubtitle(muteVideo ? LocaleController.getString("SoundMuted", R.string.SoundMuted) : currentSubtitle); + actionBarContainer.setTitleAnimated(lastTitle, true, true); + actionBarContainer.setSubtitle(muteVideo ? LocaleController.getString("SoundMuted", R.string.SoundMuted) : currentSubtitle); } updateCaptionTextForCurrentPhoto(object); if (captionEditText.isPopupShowing()) { @@ -8332,7 +8878,7 @@ private void checkBufferedProgress(float progress) { if (document == null) { return; } - int innerDuration = currentMessageObject.getDuration(); + double innerDuration = currentMessageObject.getDuration(); if (innerDuration < 20) { return; } @@ -8967,7 +9513,7 @@ public void onAnimationEnd(Animator animation) { videoPlayerSeekbar.setBufferedProgress(0); if (currentMessageObject != null) { - final int duration = currentMessageObject.getDuration(); + final int duration = (int) currentMessageObject.getDuration(); final String name = currentMessageObject.getFileName(); if (!TextUtils.isEmpty(name)) { if (duration >= 10 * 60) { @@ -9251,43 +9797,34 @@ private void releasePlayer(boolean onClose) { private void setVideoPlayerControlVisible(boolean visible, boolean animated) { if (videoPlayerControlVisible != visible) { + + if (visible) { + bottomLayout.setTag(1); + } else { + bottomLayout.setTag(null); + } + if (videoPlayerControlAnimator != null) { videoPlayerControlAnimator.cancel(); } videoPlayerControlVisible = visible; + if (animated) { if (visible) { videoPlayerControlFrameLayout.setVisibility(View.VISIBLE); - } else { - dateTextView.setVisibility(View.VISIBLE); - nameTextView.setVisibility(View.VISIBLE); - if (allowShare) { - bottomButtonsLayout.setVisibility(View.VISIBLE); - } } - final boolean shareWasAllowed = allowShare; + final ValueAnimator anim = ValueAnimator.ofFloat(videoPlayerControlFrameLayout.getAlpha(), visible ? 1f : 0f); anim.setDuration(200); anim.addUpdateListener(a -> { final float alpha = (float) a.getAnimatedValue(); videoPlayerControlFrameLayout.setAlpha(alpha); - dateTextView.setAlpha(1f - alpha); - nameTextView.setAlpha(1f - alpha); - if (shareWasAllowed) { - bottomButtonsLayout.setAlpha(1f - alpha); - } }); anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (!visible) { videoPlayerControlFrameLayout.setVisibility(View.GONE); - } else { - dateTextView.setVisibility(View.GONE); - nameTextView.setVisibility(View.GONE); - if (shareWasAllowed) { - bottomButtonsLayout.setVisibility(View.GONE); - } } } }); @@ -9296,14 +9833,6 @@ public void onAnimationEnd(Animator animation) { } else { videoPlayerControlFrameLayout.setVisibility(visible ? View.VISIBLE : View.GONE); videoPlayerControlFrameLayout.setAlpha(visible ? 1f : 0f); - dateTextView.setVisibility(visible ? View.GONE : View.VISIBLE); - dateTextView.setAlpha(visible ? 0f : 1f); - nameTextView.setVisibility(visible ? View.GONE : View.VISIBLE); - nameTextView.setAlpha(visible ? 0f : 1f); - if (allowShare) { - bottomButtonsLayout.setVisibility(visible ? View.GONE : View.VISIBLE); - bottomButtonsLayout.setAlpha(visible ? 0f : 1f); - } } if (allowShare && pageBlocksAdapter == null) { if (visible) { @@ -9456,6 +9985,7 @@ private boolean hasAnimatedMediaEntities() { private Bitmap createCroppedBitmap(Bitmap bitmap, MediaController.CropState cropState, int[] extraTransform, boolean mirror) { try { int tr = (cropState.transformRotation + (extraTransform != null ? extraTransform[0] : 0)) % 360; + int inv = extraTransform != null && extraTransform.length > 1 ? extraTransform[1] : 0; int w = bitmap.getWidth(); int h = bitmap.getHeight(); int fw = w, rotatedW = w; @@ -9477,6 +10007,11 @@ private Bitmap createCroppedBitmap(Bitmap bitmap, MediaController.CropState crop matrix.postScale(-1, 1); } } + if (inv == 1) { + matrix.postScale(-1, 1); + } else if (inv == 2) { + matrix.postScale(1, -1); + } matrix.postRotate(cropState.cropRotate + tr); matrix.postTranslate(cropState.cropPx * rotatedW, cropState.cropPy * rotatedH); matrix.postScale(cropState.cropScale, cropState.cropScale); @@ -9524,7 +10059,7 @@ private void applyCurrentEditMode() { } } else { bitmap = centerImage.getBitmap(); - orientation = new int[]{centerImage.getOrientation()}; + orientation = new int[]{centerImage.getOrientation(), centerImage.getInvert()}; } } else if (currentEditMode == EDIT_MODE_FILTER) { bitmap = photoFilterView.getBitmap(); @@ -9720,7 +10255,7 @@ private void applyCurrentEditMode() { } recycleCropped = true; } else { - orientation = new int[]{centerImage.getOrientation()}; + orientation = new int[]{centerImage.getOrientation(), centerImage.getInvert()}; if (entry.cropState != null) { croppedBitmap = createCroppedBitmap(centerImage.getBitmap(), entry.cropState, orientation, true); recycleCropped = true; @@ -9729,6 +10264,11 @@ private void applyCurrentEditMode() { if (orientation[0] != 0) { Matrix matrix = new Matrix(); matrix.postRotate(orientation[0]); + if (orientation[1] == 1) { + matrix.postScale(-1, 1); + } else if (orientation[1] == 2) { + matrix.postScale(1, -1); + } croppedBitmap = Bitmaps.createBitmap(croppedBitmap, 0, 0, croppedBitmap.getWidth(), croppedBitmap.getHeight(), matrix, true); recycleCropped = true; } else { @@ -9943,7 +10483,9 @@ private void startVideoPlayer() { private void detectFaces() { } - private void switchToEditMode(final int mode) { + private boolean wasCountViewShown; + + public void switchToEditMode(final int mode) { if (currentEditMode == mode || (isCurrentVideo && photoProgressViews[0].backgroundState != 3) && !isCurrentVideo && (centerImage.getBitmap() == null || photoProgressViews[0].backgroundState != -1) || changeModeAnimation != null || imageMoveAnimation != null || captionEditText.getTag() != null) { return; } @@ -9962,6 +10504,12 @@ private void switchToEditMode(final int mode) { } navigationBar.setVisibility(mode != EDIT_MODE_FILTER ? View.VISIBLE : View.INVISIBLE); switchingToMode = mode; + if (currentEditMode == EDIT_MODE_NONE) { + wasCountViewShown = countView != null && countView.isShown(); + } + if (countView != null) { + countView.updateShow(mode == EDIT_MODE_NONE && wasCountViewShown, true); + } if (mode == EDIT_MODE_NONE) { Bitmap bitmap = centerImage.getBitmap(); if (bitmap != null) { @@ -10084,18 +10632,23 @@ private void switchToEditMode(final int mode) { animators.add(ObjectAnimator.ofFloat(photoFilterView.getBlurControl(), View.ALPHA, 0.0f)); animators.add(ObjectAnimator.ofFloat(PhotoViewer.this, AnimationProperties.PHOTO_VIEWER_ANIMATION_VALUE, 0, 1)); } else if (currentEditMode == EDIT_MODE_PAINT) { - ValueAnimator animator = ValueAnimator.ofFloat(photoPaintView.getOffsetTranslationY(), AndroidUtilities.dp(126)); - animator.addUpdateListener(animation -> photoPaintView.setOffsetTranslationY((Float) animation.getAnimatedValue(), 0, 0,false)); + ValueAnimator animatorY = ValueAnimator.ofFloat(photoPaintView.getOffsetTranslationY(), AndroidUtilities.dp(126)); + ValueAnimator animatorX = ValueAnimator.ofFloat(0, -AndroidUtilities.dp(12)); + animatorY.addUpdateListener(animation -> photoPaintView.setOffsetTranslationY((Float) animation.getAnimatedValue(), 0, 0,false)); + animatorX.addUpdateListener(animation -> photoPaintView.setOffsetTranslationX((Float) animation.getAnimatedValue())); paintingOverlay.showAll(); containerView.invalidate(); photoPaintView.shutdown(); - animators.add(animator); + animators.add(animatorY); + animators.add(animatorX); animators.add(ObjectAnimator.ofFloat(PhotoViewer.this, AnimationProperties.PHOTO_VIEWER_ANIMATION_VALUE, 0, 1)); + photoPaintView.onAnimationStateChanged(true); } animators.add(ObjectAnimator.ofObject(navigationBar, "backgroundColor", new ArgbEvaluator(), navigationBarColorFrom, navigationBarColorTo)); imageMoveAnimation.playTogether(animators); imageMoveAnimation.setDuration(200); imageMoveAnimation.addListener(new AnimatorListenerAdapter() { + @Override public void onAnimationEnd(Animator animation) { if (currentEditMode == EDIT_MODE_CROP) { @@ -10112,6 +10665,7 @@ public void onAnimationEnd(Animator animation) { } photoFilterView = null; } else if (currentEditMode == EDIT_MODE_PAINT) { + photoPaintView.onAnimationStateChanged(false); try { containerView.removeView(photoPaintView.getView()); } catch (Exception e) { @@ -10188,8 +10742,14 @@ public void onAnimationStart(Animator animation) { if (sendPhotoType == 0 || sendPhotoType == 4 || (sendPhotoType == 2 || sendPhotoType == 5) && imagesArrLocals.size() > 1) { checkImageView.setVisibility(View.VISIBLE); photosCounterView.setVisibility(View.VISIBLE); + updateActionBarTitlePadding(); } } + + @Override + public void onAnimationEnd(Animator animation) { + if (videoConvertSupported && isCurrentVideo) updateVideoInfo(); + } }); animatorSet.start(); } @@ -10260,6 +10820,7 @@ public void onAnimationEnd(Animator animation) { if (sendPhotoType == 0 || sendPhotoType == 4 || (sendPhotoType == 2 || sendPhotoType == 5) && imagesArrLocals.size() > 1) { checkImageView.setVisibility(View.GONE); photosCounterView.setVisibility(View.GONE); + updateActionBarTitlePadding(); } final Bitmap bitmap = centerImage.getBitmap(); @@ -10369,7 +10930,7 @@ public void onAnimationEnd(Animator animation) { } else { hasFaces = currentImageHasFace == 1 ? 1 : 0; } - photoFilterView = new PhotoFilterView(parentActivity, videoTextureView != null ? (VideoEditTextureView) videoTextureView : null, bitmap, orientation, state, isCurrentVideo ? null : paintingOverlay, hasFaces, videoTextureView == null && (editState.cropState != null && editState.cropState.mirrored || cropTransform.isMirrored()), resourcesProvider); + photoFilterView = new PhotoFilterView(parentActivity, videoTextureView != null ? (VideoEditTextureView) videoTextureView : null, bitmap, orientation, state, isCurrentVideo ? null : paintingOverlay, hasFaces, videoTextureView == null && (editState.cropState != null && editState.cropState.mirrored || cropTransform.isMirrored()), true, resourcesProvider); containerView.addView(photoFilterView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); photoFilterView.getDoneTextView().setOnClickListener(v -> { applyCurrentEditMode(); @@ -10441,6 +11002,7 @@ public void onAnimationEnd(Animator animation) { if (sendPhotoType == 0 || sendPhotoType == 4 || (sendPhotoType == 2 || sendPhotoType == 5) && imagesArrLocals.size() > 1) { checkImageView.setVisibility(View.GONE); photosCounterView.setVisibility(View.GONE); + updateActionBarTitlePadding(); } Bitmap bitmap = centerImage.getBitmap(); @@ -10537,6 +11099,10 @@ public void onAnimationEnd(Animator animation) { }); changeModeAnimation.start(); } + + if (containerView != null) { + containerView.updateExclusionRects(); + } } private void createPaintView() { @@ -10566,7 +11132,7 @@ private void createPaintView() { } else { state = editState.cropState; } - photoPaintView = new LPhotoPaintView(parentActivity, currentAccount, bitmap, isCurrentVideo ? null : centerImage.getBitmap(), centerImage.getOrientation(), editState.mediaEntities, state, () -> paintingOverlay.hideBitmap(), resourcesProvider) { + photoPaintView = new LPhotoPaintView(parentActivity, parentActivity, currentAccount, bitmap, isCurrentVideo ? null : centerImage.getBitmap(), centerImage.getOrientation(), editState.mediaEntities, state, () -> paintingOverlay.hideBitmap(), resourcesProvider) { @Override protected void onOpenCloseStickersAlert(boolean open) { if (videoPlayer == null) { @@ -10604,6 +11170,7 @@ protected void onTextAdd() { }); photoPaintView.getCancelView().setOnClickListener(v -> closePaintMode()); photoPaintView.setOffsetTranslationY(AndroidUtilities.dp(126), 0, 0, false); + photoPaintView.setOffsetTranslationX(-AndroidUtilities.dp(12)); } } @@ -10634,6 +11201,7 @@ private void switchToPaintMode() { if (sendPhotoType == 0 || sendPhotoType == 4 || (sendPhotoType == 2 || sendPhotoType == 5) && imagesArrLocals.size() > 1) { checkImageView.setVisibility(View.GONE); photosCounterView.setVisibility(View.GONE); + updateActionBarTitlePadding(); } Bitmap bitmap = centerImage.getBitmap(); @@ -10681,21 +11249,22 @@ private void switchToPaintMode() { windowView.setClipChildren(true); navigationBar.setVisibility(View.INVISIBLE); imageMoveAnimation = new AnimatorSet(); - ValueAnimator animator = ValueAnimator.ofFloat(AndroidUtilities.dp(126), 0); - animator.addUpdateListener(animation -> photoPaintView.setOffsetTranslationY((Float) animation.getAnimatedValue(), 0, 0,false)); + ValueAnimator animatorY = ValueAnimator.ofFloat(AndroidUtilities.dp(126), 0); + ValueAnimator animatorX = ValueAnimator.ofFloat(-AndroidUtilities.dp(12), 0); + animatorY.addUpdateListener(animation -> photoPaintView.setOffsetTranslationY((Float) animation.getAnimatedValue(), 0, 0,false)); + animatorX.addUpdateListener(animation -> photoPaintView.setOffsetTranslationX((Float) animation.getAnimatedValue())); imageMoveAnimation.playTogether( ObjectAnimator.ofFloat(PhotoViewer.this, AnimationProperties.PHOTO_VIEWER_ANIMATION_VALUE, 0, 1), - animator + animatorY, + animatorX ); + photoPaintView.onAnimationStateChanged(true); imageMoveAnimation.setDuration(200); imageMoveAnimation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - - } @Override public void onAnimationEnd(Animator animation) { + photoPaintView.onAnimationStateChanged(false); photoPaintView.init(); paintingOverlay.hideEntities(); imageMoveAnimation = null; @@ -10912,6 +11481,14 @@ private void toggleActionBar(boolean show, final boolean animated, @NonNull fina bottomLayout.setTranslationY(0); } } + if (countView != null) { + arrayList.add(ObjectAnimator.ofFloat(countView, View.ALPHA, show ? 1.0f : 0.0f)); + if (params.enableTranslationAnimation) { + arrayList.add(ObjectAnimator.ofFloat(countView, View.TRANSLATION_Y, show ? 0 : -offsetY)); + } else { + countView.setTranslationY(0); + } + } if (navigationBar != null) { arrayList.add(ObjectAnimator.ofFloat(navigationBar, View.ALPHA, show ? 1.0f : 0.0f)); } @@ -10982,11 +11559,15 @@ public void onAnimationCancel(Animator animation) { fullscreenButton[a].setTranslationY(show ? 0 : offsetY); } actionBar.setTranslationY(show ? 0 : -offsetY); + if (countView != null) { + countView.setAlpha(show ? 1.0f : 0.0f); + countView.setTranslationY(show ? 0 : -offsetY); + } bottomLayout.setAlpha(show ? 1.0f : 0.0f); bottomLayout.setTranslationY(show ? 0 : offsetY); navigationBar.setAlpha(show ? 1.0f : 0.0f); - groupedPhotosListView.setAlpha(show ? 1.0f : 0.0f); - groupedPhotosListView.setTranslationY(show ? 0 : offsetY); + groupedPhotosListView.setAlpha(show && aboutToSwitchTo != EDIT_MODE_PAINT ? 1.0f : 0.0f); + groupedPhotosListView.setTranslationY(show && aboutToSwitchTo != EDIT_MODE_PAINT ? 0 : offsetY); if (!needCaptionLayout && captionScrollView != null) { captionScrollView.setAlpha(show ? 1.0f : 0.0f); captionScrollView.setTranslationY(show ? 0 : offsetY); @@ -11052,11 +11633,11 @@ private void toggleVideoPlayer() { pauseVideoOrWeb(); } else { if (isCurrentVideo) { - if (Math.abs(videoTimelineView.getProgress() - videoTimelineView.getRightProgress()) < 0.01f || videoPlayer.getCurrentPosition() == videoPlayer.getDuration()) { + if (Math.abs(videoTimelineView.getProgress() - videoTimelineView.getRightProgress()) < 0.01f || videoPlayer != null && videoPlayer.getCurrentPosition() == videoPlayer.getDuration()) { seekVideoOrWebToProgress(videoTimelineView.getLeftProgress()); } } else { - if (Math.abs(videoPlayerSeekbar.getProgress() - videoTimelineView.getRightProgress()) < 0.01f || videoPlayer.getCurrentPosition() == videoPlayer.getDuration()) { + if (Math.abs(videoPlayerSeekbar.getProgress() - videoTimelineView.getRightProgress()) < 0.01f || videoPlayer != null && videoPlayer.getCurrentPosition() == videoPlayer.getDuration()) { seekVideoOrWebToProgress(0); } scheduleActionBarHide(); @@ -11322,14 +11903,16 @@ private void setItemVisible(View itemView, boolean visible, boolean animate, flo if (visible) { itemView.setVisibility(View.VISIBLE); } - itemView.animate().alpha(alpha).setDuration(100).setInterpolator(new LinearInterpolator()).withEndAction(() -> { + itemView.animate().alpha(alpha).setDuration(100).setUpdateListener(anm -> updateActionBarTitlePadding()).setInterpolator(new LinearInterpolator()).withEndAction(() -> { if (!visible) { itemView.setVisibility(View.GONE); } + updateActionBarTitlePadding(); }).start(); } else { itemView.setVisibility(visible ? View.VISIBLE : View.GONE); itemView.setAlpha(alpha); + updateActionBarTitlePadding(); } } } @@ -11362,6 +11945,7 @@ private void onPhotoShow(final MessageObject messageObject, final TLRPC.FileLoca endReached[0] = false; endReached[1] = mergeDialogId == 0; opennedFromMedia = false; + openedFromProfile = false; needCaptionLayout = false; containerView.setTag(1); playerAutoStarted = false; @@ -11409,12 +11993,14 @@ private void onPhotoShow(final MessageObject messageObject, final TLRPC.FileLoca actionBar.setBackgroundColor(Theme.ACTION_BAR_PHOTO_VIEWER_COLOR); cameraItem.setVisibility(View.GONE); cameraItem.setTag(null); - bottomLayout.setVisibility(View.VISIBLE); - bottomLayout.setTag(1); + bottomLayout.setVisibility(View.GONE); + bottomLayout.setTag(0); + if (countView != null) { + countView.updateShow(false, false); + } bottomLayout.setTranslationY(0); captionTextViewSwitcher.setTranslationY(0); - bottomButtonsLayout.setVisibility(View.GONE); - paintButton.setVisibility(View.GONE); + setItemVisible(editItem, false, false); if (qualityChooseView != null) { qualityChooseView.setVisibility(View.INVISIBLE); qualityPicker.setVisibility(View.INVISIBLE); @@ -11449,6 +12035,7 @@ private void onPhotoShow(final MessageObject messageObject, final TLRPC.FileLoca photosCounterView.setAlpha(1.0f); photosCounterView.setTranslationY(0.0f); photosCounterView.setVisibility(View.GONE); + updateActionBarTitlePadding(); pickerView.setVisibility(View.GONE); pickerViewSendButton.setVisibility(View.GONE); @@ -11477,9 +12064,8 @@ private void onPhotoShow(final MessageObject messageObject, final TLRPC.FileLoca mentionListView.setVisibility(View.GONE); AndroidUtilities.updateViewVisibilityAnimated(muteItem, false, 1f, false); - actionBar.setSubtitle(null); + actionBarContainer.setSubtitle(null); setItemVisible(masksItem, false, true); - shareItem.setVisibility(View.GONE); muteVideo = false; muteItem.setImageResource(R.drawable.video_send_unmute); editorDoneLayout.setVisibility(View.GONE); @@ -11608,14 +12194,10 @@ private void onPhotoShow(final MessageObject messageObject, final TLRPC.FileLoca imagesArrLocationsSizes.add(object != null ? object.size : 0); imagesArrMessages.add(null); avatarsArr.add(new TLRPC.TL_photoEmpty()); - bottomButtonsLayout.setVisibility(!videoPlayerControlVisible ? View.VISIBLE : View.GONE); +// bottomButtonsLayout.setVisibility(!videoPlayerControlVisible ? View.VISIBLE : View.GONE); allowShare = true; menuItem.hideSubItem(gallery_menu_showall); - if (bottomButtonsLayout.getVisibility() == View.VISIBLE) { - menuItem.hideSubItem(gallery_menu_share); - } else { - menuItem.showSubItem(gallery_menu_share); - } + menuItem.showSubItem(gallery_menu_share); setImageIndex(0); if (sendPhotoType == SELECT_TYPE_AVATAR) { @@ -11642,7 +12224,10 @@ private void onPhotoShow(final MessageObject messageObject, final TLRPC.FileLoca } MessageObject openingObject = imagesArr.get(index); if (!openingObject.scheduled && (parentChatActivity == null || !parentChatActivity.isThreadChat())) { - opennedFromMedia = true; + opennedFromMedia = parentChatActivity == null; + if (parentFragment instanceof ProfileActivity) { + openedFromProfile = true; + } if (object != null) { startOffset = object.starOffset; } @@ -11666,7 +12251,7 @@ private void onPhotoShow(final MessageObject messageObject, final TLRPC.FileLoca if (sendPhotoType != SELECT_TYPE_NO_SELECT && (sendPhotoType == 0 || sendPhotoType == 4 || (sendPhotoType == 2 || sendPhotoType == 5) && photos.size() > 1)) { checkImageView.setVisibility(View.VISIBLE); photosCounterView.setVisibility(View.VISIBLE); - actionBar.setTitleRightMargin(AndroidUtilities.dp(100)); + updateActionBarTitlePadding(); } if (sendPhotoType != SELECT_TYPE_NO_SELECT && (sendPhotoType == 2 || sendPhotoType == 5) && placeProvider.canCaptureMorePhotos()) { cameraItem.setVisibility(View.VISIBLE); @@ -11722,6 +12307,13 @@ private void onPhotoShow(final MessageObject messageObject, final TLRPC.FileLoca captionEditText.onCreate(); } } + if (parentAlert != null && parentAlert.allowEnterCaption) { + needCaptionLayout = allowCaption && (placeProvider == null || placeProvider.allowCaption()); + captionEditText.setVisibility(needCaptionLayout ? View.VISIBLE : View.GONE); + if (needCaptionLayout) { + captionEditText.onCreate(); + } + } if (sendPhotoType != SELECT_TYPE_NO_SELECT) { pickerView.setVisibility(View.VISIBLE); if (useFullWidthSendButton()) { @@ -11764,7 +12356,7 @@ private void onPhotoShow(final MessageObject messageObject, final TLRPC.FileLoca MediaDataController.getInstance(currentAccount).getMediaCount(mergeDialogId, topicId, sharedMediaType, classGuid, true); } } - } else if (avatarsDialogId != 0) { + } else if (avatarsDialogId != 0 && placeProvider.canLoadMoreAvatars()) { MessagesController.getInstance(currentAccount).loadDialogPhotos(avatarsDialogId, 80, 0, true, classGuid); } } @@ -11818,6 +12410,7 @@ private void setIsAboutToSwitchToIndex(int index, boolean init, boolean animated return; } boolean animateCaption = animated; + int wasIndex = switchingToIndex; switchingToIndex = index; boolean isVideo = false; @@ -11836,6 +12429,7 @@ private void setIsAboutToSwitchToIndex(int index, boolean init, boolean animated } + CharSequence title = null; if (!imagesArr.isEmpty()) { if (switchingToIndex < 0 || switchingToIndex >= imagesArr.size()) { return; @@ -11843,19 +12437,32 @@ private void setIsAboutToSwitchToIndex(int index, boolean init, boolean animated newMessageObject = imagesArr.get(switchingToIndex); newMessageObject.updateTranslation(); isVideo = newMessageObject.isVideo(); + + title = FilteredSearchView.createFromInfoString(newMessageObject, opennedFromMedia && !openedFromProfile, 0); + CharSequence subtitle = null; + if (newMessageObject.messageOwner != null) { + subtitle = LocaleController.formatDateAudio(newMessageObject.messageOwner.date, false); + } + actionBarContainer.setSubtitle(subtitle, animated); + boolean isInvoice = newMessageObject.isInvoice(); boolean noforwards = MessagesController.getInstance(currentAccount).isChatNoForwards(newMessageObject.getChatId()) || (newMessageObject.messageOwner != null && newMessageObject.messageOwner.noforwards) || newMessageObject.hasRevealedExtendedMedia(); boolean noforwardsOverride = noforwards && !NekoXConfig.disableFlagSecure && !NaConfig.INSTANCE.getForceCopy().Bool(); + if (isVideo) { + bottomLayout.setVisibility(View.VISIBLE); + bottomLayout.setTag(1); + } else { + bottomLayout.setVisibility(View.GONE); + bottomLayout.setTag(null); + } if (isInvoice) { - setItemVisible(masksItem, false, true); + setItemVisible(masksItem, false, animated); + setItemVisible(editItem, false, animated); menuItem.hideSubItem(gallery_menu_delete); menuItem.hideSubItem(gallery_menu_openin); caption = MessageObject.getMedia(newMessageObject.messageOwner).description; allowShare = false; - bottomLayout.setTranslationY(AndroidUtilities.dp(48)); - captionTextViewSwitcher.setTranslationY(AndroidUtilities.dp(48)); - nameTextView.setText(""); - dateTextView.setText(""); +// captionTextViewSwitcher.setTranslationY(AndroidUtilities.dp(48)); } else { allowShare = !noforwardsOverride; if (newMessageObject.isNewGif() && allowShare && !DialogObject.isEncryptedDialog(newMessageObject.getDialogId())) { @@ -11867,8 +12474,10 @@ private void setIsAboutToSwitchToIndex(int index, boolean init, boolean animated menuItem.hideSubItem(gallery_menu_delete); } menuItem.checkHideMenuItem(); + boolean canPaint = (newMessageObject.getDocument() == null || newMessageObject.canPreviewDocument() || newMessageObject.getMimeType().startsWith("video/")) && !(isEmbedVideo || newMessageObject.messageOwner.ttl != 0 && newMessageObject.messageOwner.ttl < 60 * 60 || noforwards) && canSendMediaToParentChatActivity() && !opennedFromMedia; if (isEmbedVideo) { menuItem.showSubItem(gallery_menu_openin); + setItemVisible(editItem, false, false); setItemVisible(pipItem, true, false); } else if (isVideo) { if (!noforwardsOverride || (slideshowMessageId == 0 ? MessageObject.getMedia(newMessageObject.messageOwner).webpage != null && MessageObject.getMedia(newMessageObject.messageOwner).webpage.url != null : @@ -11883,10 +12492,11 @@ private void setIsAboutToSwitchToIndex(int index, boolean init, boolean animated } if (!pipAvailable) { pipItem.setEnabled(false); - setItemVisible(pipItem, true, !masksItemVisible, 0.5f); + setItemVisible(pipItem, true, !masksItemVisible && editItem.getAlpha() <= 0, 0.5f); } else { - setItemVisible(pipItem, true, !masksItemVisible); + setItemVisible(pipItem, true, !masksItemVisible && editItem.getAlpha() <= 0); } + setItemVisible(editItem, false, false); if (newMessageObject.hasAttachedStickers() && !DialogObject.isEncryptedDialog(newMessageObject.getDialogId())) { menuItem.showSubItem(gallery_menu_masks2); } else { @@ -11901,60 +12511,11 @@ private void setIsAboutToSwitchToIndex(int index, boolean init, boolean animated final boolean pipItemVisible = pipItem.getVisibility() == View.VISIBLE; final boolean shouldMasksItemBeVisible = newMessageObject.hasAttachedStickers() && !DialogObject.isEncryptedDialog(newMessageObject.getDialogId()); if (pipItemVisible) { - setItemVisible(pipItem, false, !shouldMasksItemBeVisible); + setItemVisible(pipItem, false, !shouldMasksItemBeVisible && !canPaint); } + setItemVisible(editItem, canPaint, animated && !pipItemVisible && !shouldMasksItemBeVisible); setItemVisible(masksItem, shouldMasksItemBeVisible, !pipItemVisible); } - final boolean shouldAutoPlayed = shouldMessageObjectAutoPlayed(newMessageObject); - if (!shouldAutoPlayed && TextUtils.isEmpty(placeProvider.getTitleFor(switchingToIndex))) { - final boolean animatedLocal = !playerWasPlaying; - if (nameOverride != null) { - nameTextView.setText(nameOverride); - } else { - if (newMessageObject.messageOwner.fwd_from != null && newMessageObject.messageOwner.fwd_from.from_name != null) { - nameTextView.setText(newMessageObject.messageOwner.fwd_from.from_name, animatedLocal); - } else if (newMessageObject.isFromUser()) { - TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(newMessageObject.messageOwner.from_id.user_id); - if (user != null) { - nameTextView.setText(UserObject.getUserName(user), animatedLocal); - } else { - nameTextView.setText("", animatedLocal); - } - } else { - TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-newMessageObject.getSenderId()); - if (ChatObject.isChannel(chat) && chat.megagroup && newMessageObject.isForwardedChannelPost()) { - chat = MessagesController.getInstance(currentAccount).getChat(newMessageObject.messageOwner.fwd_from.from_id.channel_id); - } - if (chat != null) { - nameTextView.setText(chat.title, animatedLocal); - } else { - nameTextView.setText("", animatedLocal); - } - } - } - long date; - if (dateOverride != 0) { - date = (long) dateOverride * 1000; - } else { - date = (long) newMessageObject.messageOwner.date * 1000; - } - String dateString = LocaleController.formatString("formatDateAtTime", R.string.formatDateAtTime, LocaleController.getInstance().formatterYear.format(new Date(date)), LocaleController.getInstance().formatterDay.format(new Date(date))); - if (newMessageObject.messageOwner.media.document != null) { - int dc = newMessageObject.messageOwner.media.document.dc_id; - dateString = String.format(Locale.US, "%s @DC%d %s, %s", dateString, dc, MessageHelper.getDCName(dc), MessageHelper.getDCLocation(dc)); - } else if (newMessageObject.messageOwner.media.photo != null) { - int dc = newMessageObject.messageOwner.media.photo.dc_id; - dateString = String.format(Locale.US, "%s @DC%d %s, %s", dateString, dc, MessageHelper.getDCName(dc), MessageHelper.getDCLocation(dc)); - } - if (newFileName != null && isVideo) { - dateTextView.setText(String.format("%s (%s)", dateString, AndroidUtilities.formatFileSize(newMessageObject.getDocument().size)), animated); - } else { - dateTextView.setText(dateString, animatedLocal); - } - } else if (!TextUtils.isEmpty(placeProvider.getTitleFor(switchingToIndex))) { - nameTextView.setText(""); - dateTextView.setText(""); - } String restrictionReason = MessagesController.getRestrictionReason(newMessageObject.messageOwner.restriction_reason); if (!TextUtils.isEmpty(restrictionReason)) { caption = restrictionReason; @@ -11969,112 +12530,60 @@ private void setIsAboutToSwitchToIndex(int index, boolean init, boolean animated menuItem.hideSubItem(gallery_menu_set_photo); menuItem.hideSubItem(gallery_menu_share); menuItem.hideSubItem(gallery_menu_scan); + setItemVisible(editItem, false, animated); if (!newMessageObject.canDeleteMessage(parentChatActivity != null && parentChatActivity.isInScheduleMode(), null)) { menuItem.hideSubItem(gallery_menu_delete); } allowShare = !noforwardsOverride; - bottomButtonsLayout.setVisibility(View.VISIBLE); - paintButton.setVisibility(View.GONE); - shareItem.setVisibility(allowShare ? View.VISIBLE : View.GONE); - shareButton.setVisibility(allowShare && shareItem.getVisibility() != View.VISIBLE ? View.VISIBLE : View.GONE); actionBar.setTitle(LocaleController.getString("AttachGif", R.string.AttachGif)); - } else { - if (totalImagesCount + totalImagesCountMerge != 0 && !needSearchImageInArr) { - if (opennedFromMedia) { - if (startOffset + imagesArr.size() < totalImagesCount + totalImagesCountMerge && !loadingMoreImages && switchingToIndex > imagesArr.size() - 5) { - int loadFromMaxId = imagesArr.isEmpty() ? 0 : imagesArr.get(imagesArr.size() - 1).getId(); - int loadIndex = 0; - if (endReached[loadIndex] && mergeDialogId != 0) { - loadIndex = 1; - if (!imagesArr.isEmpty() && imagesArr.get(imagesArr.size() - 1).getDialogId() != mergeDialogId) { - loadFromMaxId = 0; - } - } - - if (!placeProvider.loadMore()) { - MediaDataController.getInstance(currentAccount).loadMedia(loadIndex == 0 ? currentDialogId : mergeDialogId, 40, loadFromMaxId, 0, sharedMediaType, topicId,1, classGuid, 0); - loadingMoreImages = true; + } + if (totalImagesCount + totalImagesCountMerge != 0 && !needSearchImageInArr) { + if (opennedFromMedia) { + if (startOffset + imagesArr.size() < totalImagesCount + totalImagesCountMerge && !loadingMoreImages && switchingToIndex > imagesArr.size() - 5) { + int loadFromMaxId = imagesArr.isEmpty() ? 0 : imagesArr.get(imagesArr.size() - 1).getId(); + int loadIndex = 0; + if (endReached[loadIndex] && mergeDialogId != 0) { + loadIndex = 1; + if (!imagesArr.isEmpty() && imagesArr.get(imagesArr.size() - 1).getDialogId() != mergeDialogId) { + loadFromMaxId = 0; } } - if (startOffset > 0 && switchingToIndex < 5 && !imagesArr.isEmpty()) { - int loadFromMinId = imagesArr.get(0).getId(); - int loadIndex = 0; - if (!placeProvider.loadMore()) { - MediaDataController.getInstance(currentAccount).loadMedia(loadIndex == 0 ? currentDialogId : mergeDialogId, 40, 0, loadFromMinId, sharedMediaType, topicId, 1, classGuid, 0); - loadingMoreImages = true; - } - } - CharSequence title = placeProvider.getTitleFor(switchingToIndex); - if (title != null) { - actionBar.setTitle(title); - CharSequence subtitle = placeProvider.getSubtitleFor(switchingToIndex); - actionBar.setSubtitle(subtitle); - actionBar.setTitleScrollNonFitText(true); - } else { - actionBar.setTitle(LocaleController.formatString("Of", R.string.Of, startOffset + switchingToIndex + 1, totalImagesCount + totalImagesCountMerge)); + if (!placeProvider.loadMore()) { + MediaDataController.getInstance(currentAccount).loadMedia(loadIndex == 0 ? currentDialogId : mergeDialogId, 40, loadFromMaxId, 0, sharedMediaType, topicId, 1, classGuid, 0); + loadingMoreImages = true; } - } else { - if (imagesArr.size() < totalImagesCount + totalImagesCountMerge && !loadingMoreImages && switchingToIndex < 5) { - int loadFromMaxId = imagesArr.isEmpty() ? 0 : imagesArr.get(0).getId(); - int loadIndex = 0; - if (endReached[loadIndex] && mergeDialogId != 0) { - loadIndex = 1; - if (!imagesArr.isEmpty() && imagesArr.get(0).getDialogId() != mergeDialogId) { - loadFromMaxId = 0; - } - } + } - MediaDataController.getInstance(currentAccount).loadMedia(loadIndex == 0 ? currentDialogId : mergeDialogId, 80, loadFromMaxId, 0, sharedMediaType, topicId, 1, classGuid, 0); + if (startOffset > 0 && switchingToIndex < 5 && !imagesArr.isEmpty()) { + int loadFromMinId = imagesArr.get(0).getId(); + int loadIndex = 0; + if (!placeProvider.loadMore()) { + MediaDataController.getInstance(currentAccount).loadMedia(loadIndex == 0 ? currentDialogId : mergeDialogId, 40, 0, loadFromMinId, sharedMediaType, topicId, 1, classGuid, 0); loadingMoreImages = true; } - actionBar.setTitle(LocaleController.formatString("Of", R.string.Of, (totalImagesCount + totalImagesCountMerge - imagesArr.size()) + switchingToIndex + 1, totalImagesCount + totalImagesCountMerge)); } - } else if (slideshowMessageId == 0 && MessageObject.getMedia(newMessageObject.messageOwner) instanceof TLRPC.TL_messageMediaWebPage) { - if (isEmbedVideo) { - actionBar.setTitle("YouTube"); - } else if (newMessageObject.canPreviewDocument()) { - actionBar.setTitle(LocaleController.getString("AttachDocument", R.string.AttachDocument)); - } else if (newMessageObject.isVideo()) { - actionBar.setTitle(LocaleController.getString("AttachVideo", R.string.AttachVideo)); - } else { - actionBar.setTitle(LocaleController.getString("AttachPhoto", R.string.AttachPhoto)); + if (countView != null) { + countView.updateShow(openedFromProfile, animated); + countView.set((totalImagesCount + totalImagesCountMerge) - (startOffset + switchingToIndex), (totalImagesCount + totalImagesCountMerge)); } - } else if (isInvoice) { - actionBar.setTitle(MessageObject.getMedia(newMessageObject.messageOwner).title); - } else if (newMessageObject.isVideo()) { - actionBar.setTitle(LocaleController.getString("AttachVideo", R.string.AttachVideo)); - } else if (newMessageObject.getDocument() != null) { - actionBar.setTitle(LocaleController.getString("AttachDocument", R.string.AttachDocument)); - } - if (DialogObject.isEncryptedDialog(currentDialogId) && !isEmbedVideo || noforwards) { - setItemVisible(sendNoQuoteItem, false, false); - setItemVisible(sendItem, false, false); - } - if (isEmbedVideo || newMessageObject.messageOwner.ttl != 0 && newMessageObject.messageOwner.ttl < 60 * 60 || noforwardsOverride) { - allowShare = false; - menuItem.hideSubItem(gallery_menu_save); - menuItem.hideSubItem(gallery_menu_copy); - menuItem.hideSubItem(gallery_menu_set_photo); - bottomButtonsLayout.setVisibility(View.GONE); - menuItem.hideSubItem(gallery_menu_scan); - menuItem.hideSubItem(gallery_menu_share); } else { - allowShare = true; - menuItem.showSubItem(gallery_menu_save); - menuItem.showSubItem(gallery_menu_scan); - - boolean canPaint = newMessageObject.getDocument() == null || newMessageObject.canPreviewDocument() || newMessageObject.getMimeType().startsWith("video/"); - paintButton.setVisibility(canPaint && canSendMediaToParentChatActivity() ? View.VISIBLE : View.GONE); - shareButton.setVisibility(allowShare && shareItem.getVisibility() != View.VISIBLE ? View.VISIBLE : View.GONE); - bottomButtonsLayout.setVisibility(!videoPlayerControlVisible ? View.VISIBLE : View.GONE); - if (bottomButtonsLayout.getVisibility() == View.VISIBLE) { - menuItem.hideSubItem(gallery_menu_share); - if (shareItem != null) { - shareItem.setVisibility(View.VISIBLE); + if (imagesArr.size() < totalImagesCount + totalImagesCountMerge && !loadingMoreImages && switchingToIndex < 5) { + int loadFromMaxId = imagesArr.isEmpty() ? 0 : imagesArr.get(0).getId(); + int loadIndex = 0; + if (endReached[loadIndex] && mergeDialogId != 0) { + loadIndex = 1; + if (!imagesArr.isEmpty() && imagesArr.get(0).getDialogId() != mergeDialogId) { + loadFromMaxId = 0; + } } - } else { - menuItem.showSubItem(gallery_menu_share); + + MediaDataController.getInstance(currentAccount).loadMedia(loadIndex == 0 ? currentDialogId : mergeDialogId, 80, loadFromMaxId, 0, sharedMediaType, topicId, 1, classGuid, 0); + loadingMoreImages = true; + } + if (countView != null) { + countView.updateShow(true, animated); + countView.set((totalImagesCount + totalImagesCountMerge - imagesArr.size()) + switchingToIndex + 1, totalImagesCount + totalImagesCountMerge); } if (newMessageObject.isPhoto()) { menuItem.showSubItem(gallery_menu_copy); @@ -12084,8 +12593,60 @@ private void setIsAboutToSwitchToIndex(int index, boolean init, boolean animated menuItem.showSubItem(gallery_menu_set_photo); } } - groupedPhotosListView.fillList(); + } else if (slideshowMessageId == 0 && MessageObject.getMedia(newMessageObject.messageOwner) instanceof TLRPC.TL_messageMediaWebPage) { + if (countView != null) { + countView.updateShow(false, animated); + } + if (isEmbedVideo) { + title = "YouTube"; + } else if (newMessageObject.canPreviewDocument()) { + title = LocaleController.getString("AttachDocument", R.string.AttachDocument); + } else if (newMessageObject.isVideo()) { + title = LocaleController.getString("AttachVideo", R.string.AttachVideo); + } else if (newMessageObject.isGif()) { + title = LocaleController.getString("AttachGif", R.string.AttachGif); + } else { + title = LocaleController.getString("AttachPhoto", R.string.AttachPhoto); + } + } else if (isInvoice) { + if (countView != null) { + countView.updateShow(false, animated); + } + title = MessageObject.getMedia(newMessageObject.messageOwner).title; + } else if (newMessageObject.isVideo()) { + if (countView != null) { + countView.updateShow(false, animated); + } + title = LocaleController.getString("AttachVideo", R.string.AttachVideo); + } else if (newMessageObject.isGif()) { + if (countView != null) { + countView.updateShow(false, animated); + } + title = LocaleController.getString("AttachGif", R.string.AttachGif); + } else if (newMessageObject.getDocument() != null) { + if (countView != null) { + countView.updateShow(false, animated); + } + title = LocaleController.getString("AttachDocument", R.string.AttachDocument); + } + if (DialogObject.isEncryptedDialog(currentDialogId) && !isEmbedVideo || noforwards) { + setItemVisible(sendItem, false, false); } + if (isEmbedVideo || newMessageObject.messageOwner.ttl != 0 && newMessageObject.messageOwner.ttl < 60 * 60 || noforwards) { + allowShare = false; + menuItem.hideSubItem(gallery_menu_save); + menuItem.hideSubItem(gallery_menu_copy); + menuItem.hideSubItem(gallery_menu_set_photo); + menuItem.hideSubItem(gallery_menu_scan); + menuItem.hideSubItem(gallery_menu_share); + setItemVisible(editItem, false, animated); + } else { + allowShare = true; + menuItem.showSubItem(gallery_menu_save); + menuItem.showSubItem(gallery_menu_share); + menuItem.showSubItem(gallery_menu_scan); + } + groupedPhotosListView.fillList(); } else if (!secureDocuments.isEmpty()) { allowShare = false; menuItem.showSubItem(gallery_menu_delete); @@ -12093,36 +12654,17 @@ private void setIsAboutToSwitchToIndex(int index, boolean init, boolean animated menuItem.hideSubItem(gallery_menu_copy); menuItem.hideSubItem(gallery_menu_set_photo); menuItem.hideSubItem(gallery_menu_scan); - nameTextView.setText(""); - dateTextView.setText(""); - actionBar.setTitle(LocaleController.formatString("Of", R.string.Of, switchingToIndex + 1, secureDocuments.size())); + if (countView != null) { + countView.updateShow(secureDocuments.size() > 1, true); + countView.set(switchingToIndex + 1, secureDocuments.size()); + } + title = null; + actionBarContainer.setTitle(""); + actionBarContainer.setSubtitle("", animated); } else if (!imagesArrLocations.isEmpty()) { if (index < 0 || index >= imagesArrLocations.size()) { return; } - if (avatarsDialogId < 0) { - TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-avatarsDialogId); - if (chat != null) { - nameTextView.setText(chat.title); - } else { - nameTextView.setText(""); - } - } else { - TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(avatarsDialogId); - if (user != null) { - nameTextView.setText(UserObject.getUserName(user)); - } else { - nameTextView.setText(""); - } - } - TLRPC.Photo avatar = avatarsArr.get(switchingToIndex); - long date = (long) avatar.date * 1000; - if (date != 0) { - String dateString = LocaleController.formatString("formatDateAtTime", R.string.formatDateAtTime, LocaleController.getInstance().formatterYear.format(new Date(date)), LocaleController.getInstance().formatterDay.format(new Date(date))); - int dc = avatarsArr.get(switchingToIndex).dc_id; - dateString = String.format(Locale.US, "%s @DC%d %s, %s", dateString, dc, MessageHelper.getDCName(dc), MessageHelper.getDCLocation(dc)); - dateTextView.setText(dateString); - } if (canEditAvatar && !avatarsArr.isEmpty()) { menuItem.showSubItem(gallery_menu_edit_avatar); boolean currentSet = isCurrentAvatarSet(); @@ -12148,13 +12690,32 @@ private void setIsAboutToSwitchToIndex(int index, boolean init, boolean animated menuItem.hideSubItem(gallery_menu_set_as_main); menuItem.hideSubItem(gallery_menu_delete); } + if (countView != null) { + countView.updateShow(imagesArrLocations.size() > 1, true); + countView.set(switchingToIndex + 1, imagesArrLocations.size()); + } if (customTitle != null) { - actionBar.setTitle(customTitle); + title = customTitle; } else if (isEvent) { - actionBar.setTitle(LocaleController.getString("AttachPhoto", R.string.AttachPhoto)); + title = LocaleController.getString("AttachPhoto", R.string.AttachPhoto); + } else if (avatarsDialogId < 0) { + TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-avatarsDialogId); + if (chat != null) { + title = chat.title; + } } else { - actionBar.setTitle(LocaleController.formatString("Of", R.string.Of, switchingToIndex + 1, imagesArrLocations.size())); + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(avatarsDialogId); + if (user != null) { + title = UserObject.getUserName(user); + } } + CharSequence subtitle = null; + TLRPC.Photo avatar = avatarsArr.get(switchingToIndex); + if (avatar.date != 0) { + subtitle = LocaleController.formatDateAudio(avatar.date, false); + subtitle = String.format(Locale.US, "%s, DC%d", subtitle, avatar.dc_id); + } + actionBarContainer.setSubtitle(subtitle, animated); boolean noforwardsOverrided = avatarsDialogId != 0 && MessagesController.getInstance(currentAccount).isChatNoForwardsWithOverride(-avatarsDialogId); if (noforwardsOverrided) { menuItem.hideSubItem(gallery_menu_save); @@ -12170,13 +12731,7 @@ private void setIsAboutToSwitchToIndex(int index, boolean init, boolean animated } menuItem.showSubItem(gallery_menu_scan); allowShare = !noforwardsOverrided; - shareButton.setVisibility(allowShare && shareItem.getVisibility() != View.VISIBLE ? View.VISIBLE : View.GONE); - bottomButtonsLayout.setVisibility(!videoPlayerControlVisible ? View.VISIBLE : View.GONE); - if (bottomButtonsLayout.getVisibility() == View.VISIBLE) { - menuItem.hideSubItem(gallery_menu_share); - } else { - menuItem.showSubItem(gallery_menu_share); - } + menuItem.showSubItem(gallery_menu_share); menuItem.checkHideMenuItem(); groupedPhotosListView.fillList(); } else if (!imagesArrLocals.isEmpty()) { @@ -12313,7 +12868,7 @@ private void setIsAboutToSwitchToIndex(int index, boolean init, boolean animated if (animated) { actionBar.beginDelayedTransition(); } - actionBar.setSubtitle(null); + actionBarContainer.setSubtitle(""); } if (object instanceof MediaController.PhotoEntry) { MediaController.PhotoEntry photoEntry = ((MediaController.PhotoEntry) object); @@ -12340,26 +12895,30 @@ private void setIsAboutToSwitchToIndex(int index, boolean init, boolean animated bottomLayout.setVisibility(View.GONE); } bottomLayout.setTag(null); + if (countView != null) { + countView.updateShow(false, animated); + } if (fromCamera) { if (isVideo) { - actionBar.setTitle(LocaleController.getString("AttachVideo", R.string.AttachVideo)); + title = LocaleController.getString("AttachVideo", R.string.AttachVideo); } else { - actionBar.setTitle(LocaleController.getString("AttachPhoto", R.string.AttachPhoto)); + title = LocaleController.getString("AttachPhoto", R.string.AttachPhoto); } - } else { - actionBar.setTitle(LocaleController.formatString("Of", R.string.Of, switchingToIndex + 1, imagesArrLocals.size())); } if (parentChatActivity != null) { TLRPC.Chat chat = parentChatActivity.getCurrentChat(); if (chat != null) { - actionBar.setTitle(chat.title); + title = null; + actionBarContainer.setTitle(chat.title); } else { TLRPC.User user = parentChatActivity.getCurrentUser(); if (user != null) { if (user.self) { - actionBar.setTitle(LocaleController.getString("SavedMessages", R.string.SavedMessages)); + title = null; + actionBarContainer.setTitle(LocaleController.getString("SavedMessages", R.string.SavedMessages)); } else { - actionBar.setTitle(ContactsController.formatName(user.first_name, user.last_name)); + title = null; + actionBarContainer.setTitle(ContactsController.formatName(user.first_name, user.last_name)); } } } @@ -12411,9 +12970,10 @@ private void setIsAboutToSwitchToIndex(int index, boolean init, boolean animated bottomLayout.setVisibility(View.GONE); } bottomLayout.setTag(null); - - shareItem.setVisibility(allowShare ? View.VISIBLE : View.GONE); - + if (countView != null) { + countView.updateShow(size > 1, true); + countView.set(switchingToIndex + 1, size); + } if (currentAnimation != null) { menuItem.hideSubItem(gallery_menu_save); if (allowShare) { @@ -12422,16 +12982,14 @@ private void setIsAboutToSwitchToIndex(int index, boolean init, boolean animated menuItem.hideSubItem(gallery_menu_savegif); } menuItem.checkHideMenuItem(); - actionBar.setTitle(LocaleController.getString("AttachGif", R.string.AttachGif)); + title = LocaleController.getString("AttachGif", R.string.AttachGif); } else { if (size == 1) { if (isVideo) { - actionBar.setTitle(LocaleController.getString("AttachVideo", R.string.AttachVideo)); + title = LocaleController.getString("AttachVideo", R.string.AttachVideo); } else { - actionBar.setTitle(LocaleController.getString("AttachPhoto", R.string.AttachPhoto)); + title = LocaleController.getString("AttachPhoto", R.string.AttachPhoto); } - } else { - actionBar.setTitle(LocaleController.formatString("Of", R.string.Of, switchingToIndex + 1, size)); } menuItem.showSubItem(gallery_menu_save); menuItem.hideSubItem(gallery_menu_savegif); @@ -12441,6 +12999,17 @@ private void setIsAboutToSwitchToIndex(int index, boolean init, boolean animated pageBlocksAdapter.updateSlideshowCell(pageBlock); } + if (title != null) { + if (animated) { + if (wasIndex == index) { + actionBarContainer.setTitleAnimated(title, true, true); + } else { + actionBarContainer.setTitleAnimated(title, false, wasIndex > index); + } + } else { + actionBarContainer.setTitle(title); + } + } setCurrentCaption(newMessageObject, caption, animateCaption); } @@ -12801,6 +13370,7 @@ private void setImageIndex(int index, boolean init, boolean animateCaption) { setIndexToPaintingOverlay(currentIndex, paintingOverlay); setIndexToPaintingOverlay(currentIndex + 1, rightPaintingOverlay); setIndexToImage(leftImage, currentIndex - 1, leftCropTransform); + resetIndexForDeferredImageLoading(); updateAccessibilityOverlayVisibility(); checkProgress(1, true, false); @@ -12829,6 +13399,7 @@ private void setImageIndex(int index, boolean init, boolean animateCaption) { setIndexToPaintingOverlay(currentIndex, paintingOverlay); setIndexToPaintingOverlay(currentIndex + 1, rightPaintingOverlay); setIndexToImage(rightImage, currentIndex + 1, rightCropTransform); + resetIndexForDeferredImageLoading(); updateAccessibilityOverlayVisibility(); checkProgress(1, true, false); @@ -12842,8 +13413,14 @@ private void setImageIndex(int index, boolean init, boolean animateCaption) { detectFaces(); } - private void setCurrentCaption(MessageObject messageObject, final CharSequence _caption, - boolean animated) { + private void resetIndexForDeferredImageLoading() { + Object mark = centerImage.getMark(); + if (mark != null && mark.equals(MARK_DEFERRED_IMAGE_LOADING)) { + setIndexToImage(centerImage, currentIndex, null); + } + } + + private void setCurrentCaption(MessageObject messageObject, final CharSequence _caption, boolean animated) { final CharSequence caption = AnimatedEmojiSpan.cloneSpans(_caption); if (needCaptionLayout) { if (captionTextViewSwitcher.getParent() != pickerView) { @@ -13040,7 +13617,7 @@ public void onAnimationEnd(Animator animation) { Spannable spannableString = new SpannableString(caption); messageObject.addEntitiesToText(spannableString, true, false); if (messageObject.isVideo()) { - MessageObject.addUrlsByPattern(messageObject.isOutOwner(), spannableString, false, 3, messageObject.getDuration(), false); + MessageObject.addUrlsByPattern(messageObject.isOutOwner(), spannableString, false, 3, (int) messageObject.getDuration(), false); } str = Emoji.replaceEmoji(spannableString, captionTextView.getPaint().getFontMetricsInt(), AndroidUtilities.dp(20), false); } else { @@ -13057,7 +13634,7 @@ public void onAnimationEnd(Animator animation) { } captionTextView.setScrollY(0); captionTextView.setTextColor(0xffffffff); - boolean visible = isActionBarVisible && (bottomLayout.getVisibility() == View.VISIBLE || pickerView.getVisibility() == View.VISIBLE || pageBlocksAdapter != null); + boolean visible = isActionBarVisible && (!isCurrentVideo || pickerView.getVisibility() == View.VISIBLE || pageBlocksAdapter != null); captionTextViewSwitcher.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); } else { if (needCaptionLayout) { @@ -13365,7 +13942,7 @@ private void setIndexToImage(ImageReceiver imageReceiver, int index, CropTransfo if (photoEntry.filterPath != null) { path = photoEntry.filterPath; } else { - imageReceiver.setOrientation(photoEntry.orientation, false); + imageReceiver.setOrientation(photoEntry.orientation, photoEntry.invert, false); path = photoEntry.path; } filter = String.format(Locale.US, "%d_%d", size, size); @@ -13475,8 +14052,14 @@ private void setIndexToImage(ImageReceiver imageReceiver, int index, CropTransfo if (size[0] == 0) { size[0] = -1; } - TLRPC.PhotoSize thumbLocation = FileLoader.getClosestPhotoSizeWithSize(photo.sizes, 80); - imageReceiver.setImage(ImageLocation.getForPhoto(fileLocation, photo), null, ImageLocation.getForPhoto(thumbLocation, photo), "b", placeHolder != null ? new BitmapDrawable(placeHolder.bitmap) : null, size[0], null, pageBlocksAdapter.getParentObject(), 1); + + boolean autoDownload = (DownloadController.getInstance(currentAccount).getAutodownloadMask() & DownloadController.AUTODOWNLOAD_TYPE_PHOTO) != 0; + boolean needFullImage = autoDownload || currentIndex == index || FileLoader.getInstance(currentAccount).getPathToAttach(fileLocation, true).exists(); + ImageLocation imageThumbLocation = ImageLocation.getForPhoto(FileLoader.getClosestPhotoSizeWithSize(photo.sizes, 80), photo); + BitmapDrawable thumbPlaceHolder = placeHolder != null ? new BitmapDrawable(placeHolder.bitmap) : null; + ImageLocation imageLocation = needFullImage ? ImageLocation.getForPhoto(fileLocation, photo) : null; + imageReceiver.setImage(imageLocation, null, imageThumbLocation, "b", thumbPlaceHolder, size[0], null, pageBlocksAdapter.getParentObject(), 1); + imageReceiver.setMark(needFullImage ? null : MARK_DEFERRED_IMAGE_LOADING); } else if (pageBlocksAdapter.isVideo(index)) { if (!(fileLocation.location instanceof TLRPC.TL_fileLocationUnavailable)) { ImageReceiver.BitmapHolder placeHolder = null; @@ -13540,9 +14123,15 @@ private void setIndexToImage(ImageReceiver imageReceiver, int index, CropTransfo if (currentThumb != null && imageReceiver == centerImage) { placeHolder = currentThumb; } - TLRPC.PhotoSize thumbLocation = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, 100); int size = (int) (2048 / AndroidUtilities.density); - imageReceiver.setImage(ImageLocation.getForDocument(document), String.format(Locale.US, "%d_%d", size, size), placeHolder == null ? ImageLocation.getForDocument(thumbLocation, document) : null, "b", placeHolder != null ? new BitmapDrawable(placeHolder.bitmap) : null, document.size, null, messageObject, 0); + + boolean autoDownload = (DownloadController.getInstance(currentAccount).getAutodownloadMask() & DownloadController.AUTODOWNLOAD_TYPE_DOCUMENT) != 0; + boolean needFullImage = autoDownload || currentIndex == index || FileLoader.getInstance(currentAccount).getPathToAttach(document).exists(); + ImageLocation imageThumbLocation = placeHolder == null ? ImageLocation.getForDocument(FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, 100), document) : null; + BitmapDrawable thumbPlaceHolder = placeHolder != null ? new BitmapDrawable(placeHolder.bitmap) : null; + ImageLocation imageLocation = needFullImage ? ImageLocation.getForDocument(document) : null; + imageReceiver.setImage(imageLocation, String.format(Locale.US, "%d_%d", size, size), imageThumbLocation, "b", thumbPlaceHolder, document.size, null, messageObject, 0); + imageReceiver.setMark(needFullImage ? null : MARK_DEFERRED_IMAGE_LOADING); } else { OtherDocumentPlaceholderDrawable drawable = new OtherDocumentPlaceholderDrawable(parentActivity, containerView, messageObject); imageReceiver.setImageBitmap(drawable); @@ -13622,7 +14211,14 @@ private void setIndexToImage(ImageReceiver imageReceiver, int index, CropTransfo } else { filter = null; } - imageReceiver.setImage(imageLocation, filter, placeHolder == null ? ImageLocation.getForObject(thumbLocation, photoObject) : null, "b", placeHolder != null ? new BitmapDrawable(placeHolder.bitmap) : null, size[0], null, parentObject, cacheOnly ? 1 : 0); + boolean autoDownload = (DownloadController.getInstance(currentAccount).getAutodownloadMask() & DownloadController.AUTODOWNLOAD_TYPE_PHOTO) != 0; + boolean needFullImage = autoDownload || currentIndex == index || FileLoader.getInstance(currentAccount).getPathToAttach(fileLocation).exists(); + ImageLocation imageThumbLocation = placeHolder == null ? ImageLocation.getForObject(thumbLocation, photoObject) : null; + BitmapDrawable thumbPlaceHolder = placeHolder != null ? new BitmapDrawable(placeHolder.bitmap) : null; + int cacheType = cacheOnly ? 1 : 0; + ImageLocation fullImage = needFullImage ? imageLocation : null; + imageReceiver.setImage(fullImage, filter, imageThumbLocation, "b", thumbPlaceHolder, size[0], null, parentObject, cacheType); + imageReceiver.setMark(needFullImage ? null : MARK_DEFERRED_IMAGE_LOADING); } } else { if (size[0] == 0) { @@ -13772,7 +14368,8 @@ public boolean openPhotoForSelect(final TLRPC.FileLocation fileLocation, final I if (sendPhotoType != SELECT_TYPE_AVATAR && type == SELECT_TYPE_AVATAR && isVisible) { sendPhotoType = type; doneButtonPressed = false; - actionBar.setTitle(LocaleController.formatString("Of", R.string.Of, 1, 1)); + actionBarContainer.setTitle(""); + actionBarContainer.setSubtitle("", false); placeProvider = provider; mergeDialogId = 0; currentDialogId = 0; @@ -13807,8 +14404,10 @@ public boolean openPhotoForSelect(final TLRPC.FileLocation fileLocation, final I return openPhoto(null, fileLocation, imageLocation, null, null, null, photos, index, provider, chatActivity, 0, 0, 0, true, null, null); } + private int aboutToSwitchTo; + public void setTitle(CharSequence title) { - actionBar.setTitle(customTitle = title); + actionBarContainer.setTitle(customTitle = title); toggleActionBar(true, false); } private void openCurrentPhotoInPaintModeForSelect() { @@ -13847,25 +14446,8 @@ private void openCurrentPhotoInPaintModeForSelect() { boolean finalCanReplace = capReplace; MessageObject finalMessageObject = messageObject; AndroidUtilities.runOnUIThread(() -> { - int orientation = 0; - try { - ExifInterface ei = new ExifInterface(finalFile.getAbsolutePath()); - int exif = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); - switch (exif) { - case ExifInterface.ORIENTATION_ROTATE_90: - orientation = 90; - break; - case ExifInterface.ORIENTATION_ROTATE_180: - orientation = 180; - break; - case ExifInterface.ORIENTATION_ROTATE_270: - orientation = 270; - break; - } - } catch (Exception e) { - FileLog.e(e); - } - final MediaController.PhotoEntry photoEntry = new MediaController.PhotoEntry(0, lastImageId--, 0, finalFile.getAbsolutePath(), orientation, finalIsVideo, 0, 0, 0); + Pair orientation = AndroidUtilities.getImageOrientation(finalFile); + final MediaController.PhotoEntry photoEntry = new MediaController.PhotoEntry(0, lastImageId--, 0, finalFile.getAbsolutePath(), finalIsVideo ? 0 : orientation.first, finalIsVideo, 0, 0, 0).setOrientation(orientation); sendPhotoType = 2; doneButtonPressed = false; @@ -13920,15 +14502,15 @@ private void sendMedia(VideoEditedInfo videoEditedInfo, boolean notify, int sche } if (photoEntry.isVideo) { if (videoEditedInfo != null) { - SendMessagesHelper.prepareSendingVideo(parentChatActivity.getAccountInstance(), photoEntry.path, videoEditedInfo, parentChatActivity.getDialogId(), parentChatActivity.getReplyMessage(), parentChatActivity.getThreadMessage(), photoEntry.caption, photoEntry.entities, photoEntry.ttl, editingMessageObject, notify, scheduleDate, forceDocument, photoEntry.hasSpoiler); + SendMessagesHelper.prepareSendingVideo(parentChatActivity.getAccountInstance(), photoEntry.path, videoEditedInfo, parentChatActivity.getDialogId(), parentChatActivity.getReplyMessage(), parentChatActivity.getThreadMessage(), null, photoEntry.entities, photoEntry.ttl, editingMessageObject, notify, scheduleDate, forceDocument, photoEntry.hasSpoiler, photoEntry.caption); } else { - SendMessagesHelper.prepareSendingVideo(parentChatActivity.getAccountInstance(), photoEntry.path, null, parentChatActivity.getDialogId(), parentChatActivity.getReplyMessage(), parentChatActivity.getThreadMessage(), photoEntry.caption, photoEntry.entities, photoEntry.ttl, editingMessageObject, notify, scheduleDate, forceDocument, photoEntry.hasSpoiler); + SendMessagesHelper.prepareSendingVideo(parentChatActivity.getAccountInstance(), photoEntry.path, null, parentChatActivity.getDialogId(), parentChatActivity.getReplyMessage(), parentChatActivity.getThreadMessage(), null, photoEntry.entities, photoEntry.ttl, editingMessageObject, notify, scheduleDate, forceDocument, photoEntry.hasSpoiler, photoEntry.caption); } } else { if (photoEntry.imagePath != null) { - SendMessagesHelper.prepareSendingPhoto(parentChatActivity.getAccountInstance(), photoEntry.imagePath, photoEntry.thumbPath, null, parentChatActivity.getDialogId(), parentChatActivity.getReplyMessage(), parentChatActivity.getThreadMessage(), photoEntry.caption, photoEntry.entities, photoEntry.stickers, null, photoEntry.ttl, editingMessageObject, videoEditedInfo, notify, scheduleDate, forceDocument); + SendMessagesHelper.prepareSendingPhoto(parentChatActivity.getAccountInstance(), photoEntry.imagePath, photoEntry.thumbPath, null, parentChatActivity.getDialogId(), parentChatActivity.getReplyMessage(), parentChatActivity.getThreadMessage(), null, photoEntry.entities, photoEntry.stickers, null, photoEntry.ttl, editingMessageObject, videoEditedInfo, notify, scheduleDate, forceDocument, photoEntry.caption); } else if (photoEntry.path != null) { - SendMessagesHelper.prepareSendingPhoto(parentChatActivity.getAccountInstance(), photoEntry.path, photoEntry.thumbPath, null, parentChatActivity.getDialogId(), parentChatActivity.getReplyMessage(), parentChatActivity.getThreadMessage(), photoEntry.caption, photoEntry.entities, photoEntry.stickers, null, photoEntry.ttl, editingMessageObject, videoEditedInfo, notify, scheduleDate, forceDocument); + SendMessagesHelper.prepareSendingPhoto(parentChatActivity.getAccountInstance(), photoEntry.path, photoEntry.thumbPath, null, parentChatActivity.getDialogId(), parentChatActivity.getReplyMessage(), parentChatActivity.getThreadMessage(), null, photoEntry.entities, photoEntry.stickers, null, photoEntry.ttl, editingMessageObject, videoEditedInfo, notify, scheduleDate, forceDocument, photoEntry.caption); } } } @@ -13940,6 +14522,8 @@ private void sendMedia(VideoEditedInfo videoEditedInfo, boolean notify, int sche velocityTracker = VelocityTracker.obtain(); } + aboutToSwitchTo = EDIT_MODE_PAINT; + togglePhotosListView(false, false); toggleActionBar(true, false); @@ -13960,6 +14544,7 @@ private void sendMedia(VideoEditedInfo videoEditedInfo, boolean notify, int sche createPaintView(); switchToPaintMode(); + aboutToSwitchTo = EDIT_MODE_NONE; }, toggleParams.animationDuration); } else { showDownloadAlert(); @@ -14070,12 +14655,18 @@ public boolean openPhoto(final MessageObject messageObject, hasCaptionForAllMedia = false; doneButtonPressed = false; + closePhotoAfterSelect = true; allowShowFullscreenButton = true; parentChatActivity = chatActivity; lastTitle = null; isEmbedVideo = embedSeekTime != null; - actionBar.setTitle(LocaleController.formatString("Of", R.string.Of, 1, 1)); + actionBarContainer.setTitle(""); + actionBarContainer.setSubtitle("", false); + if (countView != null) { + countView.set(0, 0, false); + countView.updateShow(false, false); + } actionBar.setTitleScrollNonFitText(false); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.fileLoadFailed); @@ -14167,7 +14758,7 @@ public boolean openPhoto(final MessageObject messageObject, animatingImageViews[i].setAnimationValues(animationValues); animatingImageViews[i].setVisibility(View.VISIBLE); animatingImageViews[i].setRadius(object.radius); - animatingImageViews[i].setOrientation(orientation); + animatingImageViews[i].setOrientation(orientation, object.imageReceiver.getInvert()); animatingImageViews[i].setImageBitmap(object.thumb); } @@ -14362,12 +14953,11 @@ public boolean onPreDraw() { } animatorSet.playTogether(animators); animatorSet.setDuration(200); - int account = currentAccount; animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { AndroidUtilities.runOnUIThread(() -> { - NotificationCenter.getInstance(account).onAnimationFinish(transitionIndex); + transitionNotificationLocker.unlock(); if (animationEndRunnable != null) { animationEndRunnable.run(); animationEndRunnable = null; @@ -14376,13 +14966,12 @@ public void onAnimationEnd(Animator animation) { }); } }); - if (Build.VERSION.SDK_INT >= 18) { - containerView.setLayerType(View.LAYER_TYPE_HARDWARE, null); - } + + containerView.setLayerType(View.LAYER_TYPE_HARDWARE, null); setCaptionHwLayerEnabled(false); transitionAnimationStartTime = System.currentTimeMillis(); AndroidUtilities.runOnUIThread(() -> { - transitionIndex = NotificationCenter.getInstance(account).setAnimationInProgress(transitionIndex, new int[]{NotificationCenter.dialogsNeedReload, NotificationCenter.closeChats, NotificationCenter.mediaCountDidLoad, NotificationCenter.mediaDidLoad, NotificationCenter.dialogPhotosLoaded}); + transitionNotificationLocker.lock(); animatorSet.start(); }); } else { @@ -14796,14 +15385,14 @@ public void closePhoto(boolean animated, boolean fromEditMode) { orientation = animatedOrientation; } for (int i = 0; i < animatingImageViews.length; i++) { - animatingImageViews[i].setOrientation(orientation); + animatingImageViews[i].setOrientation(orientation, object.imageReceiver.getInvert()); animatingImageViews[i].setImageBitmap(object.thumb); } } else { layoutParams.width = (int) centerImage.getImageWidth(); layoutParams.height = (int) centerImage.getImageHeight(); for (int i = 0; i < animatingImageViews.length; i++) { - animatingImageViews[i].setOrientation(centerImage.getOrientation()); + animatingImageViews[i].setOrientation(centerImage.getOrientation(), centerImage.getInvert()); animatingImageViews[i].setImageBitmap(centerImage.getBitmapSafe()); } } @@ -14936,11 +15525,10 @@ public void closePhoto(boolean animated, boolean fromEditMode) { } animationEndRunnable = () -> { animationEndRunnable = null; - if (Build.VERSION.SDK_INT >= 18) { - containerView.setLayerType(View.LAYER_TYPE_NONE, null); - } + containerView.setLayerType(View.LAYER_TYPE_NONE, null); animationInProgress = 0; onPhotoClosed(object); + MediaController.getInstance().tryResumePausedAudio(); }; animatorSet.setDuration(200); @@ -14956,9 +15544,7 @@ public void onAnimationEnd(Animator animation) { } }); transitionAnimationStartTime = System.currentTimeMillis(); - if (Build.VERSION.SDK_INT >= 18) { - containerView.setLayerType(View.LAYER_TYPE_HARDWARE, null); - } + containerView.setLayerType(View.LAYER_TYPE_HARDWARE, null); animatorSet.start(); } else { AnimatorSet animatorSet = new AnimatorSet(); @@ -14981,13 +15567,12 @@ public void onAnimationEnd(Animator animation) { if (containerView == null) { return; } - if (Build.VERSION.SDK_INT >= 18) { - containerView.setLayerType(View.LAYER_TYPE_NONE, null); - } + containerView.setLayerType(View.LAYER_TYPE_NONE, null); animationInProgress = 0; onPhotoClosed(object); containerView.setScaleX(1.0f); containerView.setScaleY(1.0f); + MediaController.getInstance().tryResumePausedAudio(); }; animatorSet.setDuration(200); animatorSet.addListener(new AnimatorListenerAdapter() { @@ -15012,9 +15597,7 @@ public void onAnimationEnd(Animator animation) { } }); transitionAnimationStartTime = System.currentTimeMillis(); - if (Build.VERSION.SDK_INT >= 18) { - containerView.setLayerType(View.LAYER_TYPE_HARDWARE, null); - } + containerView.setLayerType(View.LAYER_TYPE_HARDWARE, null); animatorSet.start(); if (object != null) { object.imageReceiver.setVisible(true, true); @@ -15717,7 +16300,7 @@ private void cancelMoveZoomAnimation() { zoomAnimation = false; containerView.invalidate(); } - + public void zoomOut() { animateTo(1f, 0, 0, false); } @@ -16094,13 +16677,13 @@ public void onAnimationEnd(Animator animation) { sideImage.draw(canvas); if (rightPaintingOverlay != null && rightPaintingOverlay.getVisibility() == View.VISIBLE) { - canvas.clipRect(-width/2, -height/2, width/2, height/2); + canvas.clipRect(-width / 2, -height / 2, width / 2, height / 2); if (rightPaintingOverlay.getMeasuredWidth() != bitmapWidth || rightPaintingOverlay.getMeasuredHeight() != bitmapHeight) { rightPaintingOverlay.measure(View.MeasureSpec.makeMeasureSpec(bitmapWidth, View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(bitmapHeight, View.MeasureSpec.EXACTLY)); rightPaintingOverlay.layout(0, 0, bitmapWidth, bitmapHeight); } canvas.scale(scale, scale); - canvas.translate(-bitmapWidth/2, -bitmapHeight/2); + canvas.translate(-bitmapWidth / 2, -bitmapHeight / 2); rightPaintingOverlay.setAlpha(1.0f); rightPaintingOverlay.draw(canvas); } @@ -16903,7 +17486,7 @@ public boolean onSingleTapConfirmed(MotionEvent e) { float x = e.getX(); float y = e.getY(); if (checkImageView.getVisibility() != View.VISIBLE) { - if (y > ActionBar.getCurrentActionBarHeight() + AndroidUtilities.statusBarHeight + AndroidUtilities.dp(40)) { + if (SharedConfig.nextMediaTap && y > ActionBar.getCurrentActionBarHeight() + AndroidUtilities.statusBarHeight + AndroidUtilities.dp(40)) { int side = NekoConfig.disablePhotoSideAction.Bool() ? 0 : Math.min(135, containerView.getMeasuredWidth() / 8); if (x < side) { if (leftImage.hasImageSet()) { @@ -17106,20 +17689,21 @@ public boolean onDoubleTapEvent(MotionEvent e) { private long captureFrameReadyAtTime = -1; private long needCaptureFrameReadyAtTime = -1; - private int selectedCompression; - private int compressionsCount = -1; + private volatile int selectedCompression; + private volatile int compressionsCount = -1; private int previousCompression; private int rotationValue; - private int originalWidth; - private int originalHeight; - private int resultWidth; - private int resultHeight; - private int bitrate; - private int originalBitrate; + private volatile int originalWidth; + private volatile int originalHeight; + private volatile int resultWidth; + private volatile int resultHeight; + private volatile int bitrate; + private volatile int originalBitrate; private float videoDuration; private int videoFramerate; - private boolean videoConvertSupported; + private volatile boolean videoConvertSupported; + private volatile boolean isH264Video; private long startTime; private long endTime; private float videoCutStart; @@ -17255,7 +17839,7 @@ public void updateMuteButton() { muteItem.animate().alpha(1f).setDuration(180).start(); if (muteVideo) { if (customTitle == null) { - actionBar.setSubtitle(LocaleController.getString("SoundMuted", R.string.SoundMuted)); + actionBarContainer.setSubtitle(LocaleController.getString("SoundMuted", R.string.SoundMuted)); } muteItem.setImageResource(R.drawable.video_send_mute); if (compressItem.getTag() != null) { @@ -17272,7 +17856,7 @@ public void updateMuteButton() { } muteItem.setContentDescription(LocaleController.getString("NoSound", R.string.NoSound)); } else { - actionBar.setSubtitle(currentSubtitle); + actionBarContainer.setSubtitle(currentSubtitle); muteItem.setImageResource(R.drawable.video_send_unmute); muteItem.setContentDescription(LocaleController.getString("Sound", R.string.Sound)); if (compressItem.getTag() != null) { @@ -17297,16 +17881,35 @@ private void didChangedCompressionLevel(boolean request) { } } + private void calculateEstimatedVideoSize(boolean needEncoding, boolean isMute) { + if (needEncoding) { + estimatedSize = (long) (((isMute ? 0 : audioFramesSize) + videoFramesSize) * ((float) estimatedDuration / videoDuration)); + estimatedSize += estimatedSize / (32 * 1024) * 16; + } else { + estimatedSize = (long) (originalSize * ((float) estimatedDuration / videoDuration)); + if (isMute) + estimatedSize -= (long) (audioFramesSize * ((float) estimatedDuration / videoDuration)); + } + } + + private boolean needEncoding() { + Object mediaEntities = editState.croppedPaintPath != null + ? (editState.croppedMediaEntities != null && !editState.croppedMediaEntities.isEmpty() ? editState.croppedMediaEntities : null) + : (editState.mediaEntities != null && !editState.mediaEntities.isEmpty() ? editState.mediaEntities : null); + Object paintPath = editState.croppedPaintPath != null ? editState.croppedPaintPath : editState.paintPath; + return !isH264Video || videoCutStart != 0 || rotationValue != 0 || resultWidth != originalWidth || resultHeight != originalHeight + || editState.cropState != null || mediaEntities != null || paintPath != null || editState.savedFilterState != null || sendPhotoType == SELECT_TYPE_AVATAR; + } + private void updateVideoInfo() { if (actionBar == null) { return; } if (compressionsCount == 0) { - actionBar.setSubtitle(null); + actionBarContainer.setSubtitle(null); return; } - int compressIconWidth = 64; if (selectedCompression < 2) { compressItem.setImageResource(R.drawable.video_quality1); } else if (selectedCompression == 2) { @@ -17317,13 +17920,14 @@ private void updateVideoInfo() { itemsLayout.requestLayout(); estimatedDuration = (long) Math.ceil((videoTimelineView.getRightProgress() - videoTimelineView.getLeftProgress()) * videoDuration); + videoCutStart = videoTimelineView.getLeftProgress(); + videoCutEnd = videoTimelineView.getRightProgress(); - int width; - int height; + int width = rotationValue == 90 || rotationValue == 270 ? resultHeight : resultWidth; + int height = rotationValue == 90 || rotationValue == 270 ? resultWidth : resultHeight; + boolean needEncoding = needEncoding(); if (muteVideo) { - width = rotationValue == 90 || rotationValue == 270 ? resultHeight : resultWidth; - height = rotationValue == 90 || rotationValue == 270 ? resultWidth : resultHeight; int bitrate; if (sendPhotoType == SELECT_TYPE_AVATAR) { if (estimatedDuration <= 2000) { @@ -17338,20 +17942,10 @@ private void updateVideoInfo() { } estimatedSize = (long) (bitrate / 8 * (estimatedDuration / 1000.0f)); estimatedSize += estimatedSize / (32 * 1024) * 16; - } else if (compressItem.getTag() == null) { - width = rotationValue == 90 || rotationValue == 270 ? originalHeight : originalWidth; - height = rotationValue == 90 || rotationValue == 270 ? originalWidth : originalHeight; - estimatedSize = (long) (originalSize * ((float) estimatedDuration / videoDuration)); } else { - width = rotationValue == 90 || rotationValue == 270 ? resultHeight : resultWidth; - height = rotationValue == 90 || rotationValue == 270 ? resultWidth : resultHeight; - - estimatedSize = (long) (((sendPhotoType == SELECT_TYPE_AVATAR ? 0 : audioFramesSize) + videoFramesSize) * ((float) estimatedDuration / videoDuration)); - estimatedSize += estimatedSize / (32 * 1024) * 16; + calculateEstimatedVideoSize(needEncoding, sendPhotoType == SELECT_TYPE_AVATAR); } - videoCutStart = videoTimelineView.getLeftProgress(); - videoCutEnd = videoTimelineView.getRightProgress(); if (videoCutStart == 0) { startTime = -1; } else { @@ -17368,7 +17962,7 @@ private void updateVideoInfo() { currentSubtitle = String.format("%s, %s", videoDimension, videoTimeSize); actionBar.beginDelayedTransition(); if (customTitle == null) { - actionBar.setSubtitle(muteVideo ? LocaleController.getString("SoundMuted", R.string.SoundMuted) : currentSubtitle); + actionBarContainer.setSubtitle(muteVideo ? LocaleController.getString("SoundMuted", R.string.SoundMuted) : currentSubtitle); } } @@ -17426,7 +18020,7 @@ private void requestVideoPreview(int request) { videoPreviewMessageObject.videoEditedInfo.needUpdateProgress = true; videoPreviewMessageObject.videoEditedInfo.originalDuration = (long) (videoDuration * 1000); - if (!MediaController.getInstance().scheduleVideoConvert(videoPreviewMessageObject, true)) { + if (!MediaController.getInstance().scheduleVideoConvert(videoPreviewMessageObject, true, true)) { tryStartRequestPreviewOnFinish = true; } requestingPreview = true; @@ -17445,6 +18039,51 @@ private void requestVideoPreview(int request) { containerView.invalidate(); } + private Size calculateResultVideoSize() { + if (compressionsCount == 1) { + return new Size(originalWidth, originalHeight); + } + float maxSize; + int resultWidth; + int resultHeight; + switch (selectedCompression) { + case 0: + maxSize = 480.0f; + break; + case 1: + maxSize = 854.0f; + break; + case 2: + maxSize = 1280.0f; + break; + case 3: + default: + maxSize = 1920.0f; + break; + } + float scale = originalWidth > originalHeight ? maxSize / originalWidth : maxSize / originalHeight; + if (selectedCompression == compressionsCount - 1 && scale >= 1f) { + resultWidth = originalWidth; + resultHeight = originalHeight; + } else { + resultWidth = Math.round(originalWidth * scale / 2) * 2; + resultHeight = Math.round(originalHeight * scale / 2) * 2; + } + return new Size(resultWidth, resultHeight); + } + + private void prepareRealEncoderBitrate() { + if (bitrate != 0 && sendPhotoType != SELECT_TYPE_AVATAR) { + Size resultSize = calculateResultVideoSize(); + if (resultSize.getWidth() == originalWidth && resultSize.getHeight() == originalHeight) { + MediaController.extractRealEncoderBitrate(resultSize.getWidth(), resultSize.getHeight(), originalBitrate, false); + } else { + int targetBitrate = MediaController.makeVideoBitrate(originalHeight, originalWidth, originalBitrate, resultSize.getHeight(), resultSize.getWidth()); + MediaController.extractRealEncoderBitrate(resultSize.getWidth(), resultSize.getHeight(), targetBitrate, false); + } + } + } + private void updateWidthHeightBitrateForCompression() { if (compressionsCount <= 0) { return; @@ -17458,41 +18097,24 @@ private void updateWidthHeightBitrateForCompression() { resultWidth = Math.round(originalWidth * scale / 2) * 2; resultHeight = Math.round(originalHeight * scale / 2) * 2; } else { - float maxSize; - switch (selectedCompression) { - case 0: - maxSize = 480.0f; - break; - case 1: - maxSize = 854.0f; - break; - case 2: - maxSize = 1280.0f; - break; - case 3: - default: - maxSize = 1920.0f; - break; - } - float scale = originalWidth > originalHeight ? maxSize / originalWidth : maxSize / originalHeight; - if (selectedCompression == compressionsCount - 1 && scale >= 1f) { - resultWidth = originalWidth; - resultHeight = originalHeight; - } else { - resultWidth = Math.round(originalWidth * scale / 2) * 2; - resultHeight = Math.round(originalHeight * scale / 2) * 2; - } + Size resultSize = calculateResultVideoSize(); + resultWidth = resultSize.getWidth(); + resultHeight = resultSize.getHeight(); } if (bitrate != 0) { + final int encoderBitrate; if (sendPhotoType == SELECT_TYPE_AVATAR) { bitrate = 1560000; + encoderBitrate = bitrate; } else if (resultWidth == originalWidth && resultHeight == originalHeight) { bitrate = originalBitrate; + encoderBitrate = MediaController.extractRealEncoderBitrate(resultWidth, resultHeight, bitrate, false); } else { bitrate = MediaController.makeVideoBitrate(originalHeight, originalWidth, originalBitrate, resultHeight, resultWidth); + encoderBitrate = MediaController.extractRealEncoderBitrate(resultWidth, resultHeight, bitrate, false); } - videoFramesSize = (long) (bitrate / 8 * videoDuration / 1000); + videoFramesSize = (long) (encoderBitrate / 8 * videoDuration / 1000); } } @@ -17512,15 +18134,13 @@ private void showQualityView(final boolean show) { qualityChooseView.setTag(1); qualityChooseViewAnimation.playTogether( ObjectAnimator.ofFloat(pickerView, View.TRANSLATION_Y, 0, AndroidUtilities.dp(152)), - ObjectAnimator.ofFloat(pickerViewSendButton, View.TRANSLATION_Y, 0, AndroidUtilities.dp(152)), - ObjectAnimator.ofFloat(bottomLayout, View.TRANSLATION_Y, -AndroidUtilities.dp(48), AndroidUtilities.dp(104)) + ObjectAnimator.ofFloat(pickerViewSendButton, View.TRANSLATION_Y, 0, AndroidUtilities.dp(152)) ); } else { qualityChooseView.setTag(null); qualityChooseViewAnimation.playTogether( ObjectAnimator.ofFloat(qualityChooseView, View.TRANSLATION_Y, 0, AndroidUtilities.dp(166)), - ObjectAnimator.ofFloat(qualityPicker, View.TRANSLATION_Y, 0, AndroidUtilities.dp(166)), - ObjectAnimator.ofFloat(bottomLayout, View.TRANSLATION_Y, -AndroidUtilities.dp(48), AndroidUtilities.dp(118)) + ObjectAnimator.ofFloat(qualityPicker, View.TRANSLATION_Y, 0, AndroidUtilities.dp(166)) ); } qualityChooseViewAnimation.addListener(new AnimatorListenerAdapter() { @@ -17535,16 +18155,14 @@ public void onAnimationEnd(Animator animation) { qualityPicker.setVisibility(View.VISIBLE); qualityChooseViewAnimation.playTogether( ObjectAnimator.ofFloat(qualityChooseView, View.TRANSLATION_Y, 0), - ObjectAnimator.ofFloat(qualityPicker, View.TRANSLATION_Y, 0), - ObjectAnimator.ofFloat(bottomLayout, View.TRANSLATION_Y, -AndroidUtilities.dp(48)) + ObjectAnimator.ofFloat(qualityPicker, View.TRANSLATION_Y, 0) ); } else { qualityChooseView.setVisibility(View.INVISIBLE); qualityPicker.setVisibility(View.INVISIBLE); qualityChooseViewAnimation.playTogether( ObjectAnimator.ofFloat(pickerView, View.TRANSLATION_Y, 0), - ObjectAnimator.ofFloat(pickerViewSendButton, View.TRANSLATION_Y, 0), - ObjectAnimator.ofFloat(bottomLayout, View.TRANSLATION_Y, -AndroidUtilities.dp(48)) + ObjectAnimator.ofFloat(pickerViewSendButton, View.TRANSLATION_Y, 0) ); } qualityChooseViewAnimation.addListener(new AnimatorListenerAdapter() { @@ -17603,7 +18221,7 @@ private ByteArrayInputStream cleanBuffer(byte[] data) { return new ByteArrayInputStream(output, 0, outPos); } - private void processOpenVideo(final String videoPath, boolean muted, float start, float end, int compressQality) { + private void processOpenVideo(final String videoPath, boolean muted, float start, float end, final int compressQuality) { if (currentLoadingVideoRunnable != null) { Utilities.globalQueue.cancelRunnable(currentLoadingVideoRunnable); currentLoadingVideoRunnable = null; @@ -17625,9 +18243,22 @@ public void run() { return; } int videoBitrate = MediaController.getVideoBitrate(videoPath); - int[] params = new int[AnimatedFileDrawable.PARAM_NUM_COUNT]; AnimatedFileDrawable.getVideoInfo(videoPath, params); + + final boolean hasAudio = params[AnimatedFileDrawable.PARAM_NUM_HAS_AUDIO] != 0; + videoConvertSupported = params[AnimatedFileDrawable.PARAM_NUM_SUPPORTED_VIDEO_CODEC] != 0 && (!hasAudio || params[AnimatedFileDrawable.PARAM_NUM_SUPPORTED_AUDIO_CODEC] != 0); + originalBitrate = bitrate = videoBitrate == -1 ? params[AnimatedFileDrawable.PARAM_NUM_BITRATE] : videoBitrate; + + if (videoConvertSupported) { + resultWidth = originalWidth = params[AnimatedFileDrawable.PARAM_NUM_WIDTH]; + resultHeight = originalHeight = params[AnimatedFileDrawable.PARAM_NUM_HEIGHT]; + updateCompressionsCount(originalWidth, originalHeight); + selectedCompression = compressQuality == -1 ? selectCompression() : compressQuality; + prepareRealEncoderBitrate(); + isH264Video = MediaController.isH264Video(videoPath); + } + if (currentLoadingVideoRunnable != this) { return; } @@ -17637,30 +18268,13 @@ public void run() { return; } currentLoadingVideoRunnable = null; - boolean hasAudio = params[AnimatedFileDrawable.PARAM_NUM_HAS_AUDIO] != 0; - videoConvertSupported = params[AnimatedFileDrawable.PARAM_NUM_SUPPORTED_VIDEO_CODEC] != 0 && - (!hasAudio || params[AnimatedFileDrawable.PARAM_NUM_SUPPORTED_AUDIO_CODEC] != 0); audioFramesSize = params[AnimatedFileDrawable.PARAM_NUM_AUDIO_FRAME_SIZE]; videoDuration = params[AnimatedFileDrawable.PARAM_NUM_DURATION]; - if (videoBitrate == -1) { - originalBitrate = bitrate = params[AnimatedFileDrawable.PARAM_NUM_BITRATE]; - } else { - originalBitrate = bitrate = videoBitrate; - } videoFramerate = params[AnimatedFileDrawable.PARAM_NUM_FRAMERATE]; videoFramesSize = (long) (bitrate / 8 * videoDuration / 1000); if (videoConvertSupported) { rotationValue = params[AnimatedFileDrawable.PARAM_NUM_ROTATION]; - resultWidth = originalWidth = params[AnimatedFileDrawable.PARAM_NUM_WIDTH]; - resultHeight = originalHeight = params[AnimatedFileDrawable.PARAM_NUM_HEIGHT]; - - updateCompressionsCount(originalWidth, originalHeight); - if (compressQality == -1) { - selectedCompression = selectCompression(); - } else { - selectedCompression = compressQality; - } updateWidthHeightBitrateForCompression(); if (selectedCompression > compressionsCount - 1) { @@ -17671,10 +18285,6 @@ public void run() { if (BuildVars.LOGS_ENABLED) { FileLog.d("compressionsCount = " + compressionsCount + " w = " + originalWidth + " h = " + originalHeight + " r = " + rotationValue); } - if (Build.VERSION.SDK_INT < 18 && compressItem.getTag() != null) { - videoConvertSupported = false; - setCompressItemEnabled(false, true); - } qualityChooseView.invalidate(); } else { setCompressItemEnabled(false, true); @@ -17836,7 +18446,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (photoEntry.thumbPath != null) { imageView.setImage(photoEntry.thumbPath, null, mContext.getResources().getDrawable(R.drawable.nophotos)); } else if (photoEntry.path != null) { - imageView.setOrientation(photoEntry.orientation, true); + imageView.setOrientation(photoEntry.orientation, photoEntry.invert, true); if (photoEntry.isVideo) { cell.videoInfoContainer.setVisibility(View.VISIBLE); cell.videoTextView.setText(AndroidUtilities.formatShortDuration(photoEntry.duration)); @@ -17892,7 +18502,7 @@ public void checkFromPlayer(VideoPlayer videoPlayer) { clear(); } - if (videoPlayer != null) { + if (videoPlayer != null && !videoPlayer.isHDR()) { long timeToEnd = videoPlayer.getDuration() - videoPlayer.getCurrentPosition(); if (!hasFrame && !gotError && !gettingFrame && timeToEnd < 1000 * 5 + fadeDuration) { // 5 seconds to get the first frame final Uri uri = videoPlayer.getCurrentUri(); @@ -17969,8 +18579,10 @@ private void updateAlpha() { } } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + if (resourcesProvider != null) { + return resourcesProvider.getColor(key); } + return Theme.getColor(key); +} } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PinchToZoomHelper.java b/TMessagesProj/src/main/java/org/telegram/ui/PinchToZoomHelper.java index 08b0a7c26e..f995c14dba 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PinchToZoomHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PinchToZoomHelper.java @@ -48,6 +48,7 @@ public class PinchToZoomHelper { private final ViewGroup parentView; private final ViewGroup fragmentView; + private final boolean isSimple; private ZoomOverlayView overlayView; private View child; @@ -106,13 +107,20 @@ public class PinchToZoomHelper { public PinchToZoomHelper(ViewGroup parentView, ViewGroup fragmentView) { this.parentView = parentView; this.fragmentView = fragmentView; + this.isSimple = false; + } + + public PinchToZoomHelper() { + this.parentView = null; + this.fragmentView = null; + this.isSimple = true; } public void startZoom(View child, ImageReceiver image, MessageObject messageObject) { this.child = child; this.messageObject = messageObject; - if (overlayView == null) { + if (overlayView == null && !isSimple) { overlayView = new ZoomOverlayView(parentView.getContext()); overlayView.setFocusable(false); overlayView.setFocusableInTouchMode(false); @@ -132,78 +140,81 @@ public void startZoom(View child, ImageReceiver image, MessageObject messageObje } inOverlayMode = true; - parentView.addView(overlayView); finishProgress = 1f; progressToFullView = 0f; - hasMediaSpoiler = messageObject != null && messageObject.hasMediaSpoilers() && !messageObject.isMediaSpoilersRevealed; - if (blurImage.getBitmap() != null) { - blurImage.getBitmap().recycle(); - blurImage.setImageBitmap((Bitmap) null); - } + if (!isSimple) { + parentView.addView(overlayView); - if (image.getBitmap() != null && !image.getBitmap().isRecycled() && hasMediaSpoiler) { - blurImage.setImageBitmap(Utilities.stackBlurBitmapMax(image.getBitmap())); - } + hasMediaSpoiler = messageObject != null && messageObject.hasMediaSpoilers() && !messageObject.isMediaSpoilersRevealed; + if (blurImage.getBitmap() != null) { + blurImage.getBitmap().recycle(); + blurImage.setImageBitmap((Bitmap) null); + } - setFullImage(messageObject); + if (image.getBitmap() != null && !image.getBitmap().isRecycled() && hasMediaSpoiler) { + blurImage.setImageBitmap(Utilities.stackBlurBitmapMax(image.getBitmap())); + } - imageX = image.getImageX(); - imageY = image.getImageY(); - imageHeight = image.getImageHeight(); - imageWidth = image.getImageWidth(); - fullImageHeight = image.getBitmapHeight(); - fullImageWidth = image.getBitmapWidth(); + setFullImage(messageObject); - if (fullImageHeight / fullImageWidth != imageHeight / imageWidth) { - if (fullImageHeight / fullImageWidth < imageHeight / imageWidth) { - fullImageWidth = fullImageWidth / fullImageHeight * imageHeight; - fullImageHeight = imageHeight; + imageX = image.getImageX(); + imageY = image.getImageY(); + imageHeight = image.getImageHeight(); + imageWidth = image.getImageWidth(); + fullImageHeight = image.getBitmapHeight(); + fullImageWidth = image.getBitmapWidth(); + + if (fullImageHeight / fullImageWidth != imageHeight / imageWidth) { + if (fullImageHeight / fullImageWidth < imageHeight / imageWidth) { + fullImageWidth = fullImageWidth / fullImageHeight * imageHeight; + fullImageHeight = imageHeight; + } else { + fullImageHeight = fullImageHeight / fullImageWidth * imageWidth; + fullImageWidth = imageWidth; + } } else { - fullImageHeight = fullImageHeight / fullImageWidth * imageWidth; + fullImageHeight = imageHeight; fullImageWidth = imageWidth; } - } else { - fullImageHeight = imageHeight; - fullImageWidth = imageWidth; - } - - - if (messageObject != null && messageObject.isVideo() && MediaController.getInstance().isPlayingMessage(messageObject)) { - isHardwareVideo = true; - MediaController.getInstance().setTextureView(overlayView.videoTextureView, overlayView.aspectRatioFrameLayout, overlayView.videoPlayerContainer, true); - FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) overlayView.videoPlayerContainer.getLayoutParams(); - overlayView.videoPlayerContainer.setTag(R.id.parent_tag, image); - if (layoutParams.width != image.getImageWidth() || layoutParams.height != image.getImageHeight()) { - overlayView.aspectRatioFrameLayout.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FILL); - layoutParams.width = (int) image.getImageWidth(); - layoutParams.height = (int) image.getImageHeight(); - overlayView.videoPlayerContainer.setLayoutParams(layoutParams); - } - overlayView.videoTextureView.setScaleX(1f); - overlayView.videoTextureView.setScaleY(1f); - if (callback != null) { - overlayView.backupImageView.setImageBitmap(callback.getCurrentTextureView().getBitmap((int) fullImageWidth, (int) fullImageHeight)); - overlayView.backupImageView.setSize((int) fullImageWidth, (int) fullImageHeight); - overlayView.backupImageView.getImageReceiver().setRoundRadius(image.getRoundRadius()); - } - overlayView.videoPlayerContainer.setVisibility(View.VISIBLE); - } else { - isHardwareVideo = false; - this.childImage = new ImageReceiver(); - this.childImage.onAttachedToWindow(); - Drawable drawable = image.getDrawable(); - this.childImage.setImageBitmap(drawable); - if (drawable instanceof AnimatedFileDrawable) { - ((AnimatedFileDrawable) drawable).addSecondParentView(overlayView); - ((AnimatedFileDrawable) drawable).setInvalidateParentViewWithSecond(true); - } - this.childImage.setImageCoords(imageX, imageY, imageWidth, imageHeight); - this.childImage.setRoundRadius(image.getRoundRadius()); - this.fullImage.setRoundRadius(image.getRoundRadius()); - overlayView.videoPlayerContainer.setVisibility(View.GONE); + if (messageObject != null && messageObject.isVideo() && MediaController.getInstance().isPlayingMessage(messageObject)) { + isHardwareVideo = true; + MediaController.getInstance().setTextureView(overlayView.videoTextureView, overlayView.aspectRatioFrameLayout, overlayView.videoPlayerContainer, true); + FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) overlayView.videoPlayerContainer.getLayoutParams(); + overlayView.videoPlayerContainer.setTag(R.id.parent_tag, image); + if (layoutParams.width != image.getImageWidth() || layoutParams.height != image.getImageHeight()) { + overlayView.aspectRatioFrameLayout.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FILL); + layoutParams.width = (int) image.getImageWidth(); + layoutParams.height = (int) image.getImageHeight(); + overlayView.videoPlayerContainer.setLayoutParams(layoutParams); + } + overlayView.videoTextureView.setScaleX(1f); + overlayView.videoTextureView.setScaleY(1f); + + if (callback != null) { + overlayView.backupImageView.setImageBitmap(callback.getCurrentTextureView().getBitmap((int) fullImageWidth, (int) fullImageHeight)); + overlayView.backupImageView.setSize((int) fullImageWidth, (int) fullImageHeight); + overlayView.backupImageView.getImageReceiver().setRoundRadius(image.getRoundRadius()); + } + overlayView.videoPlayerContainer.setVisibility(View.VISIBLE); + } else { + isHardwareVideo = false; + this.childImage = new ImageReceiver(); + this.childImage.onAttachedToWindow(); + Drawable drawable = image.getDrawable(); + this.childImage.setImageBitmap(drawable); + if (drawable instanceof AnimatedFileDrawable) { + ((AnimatedFileDrawable) drawable).addSecondParentView(overlayView); + ((AnimatedFileDrawable) drawable).setInvalidateParentViewWithSecond(true); + } + this.childImage.setImageCoords(imageX, imageY, imageWidth, imageHeight); + this.childImage.setRoundRadius(image.getRoundRadius()); + + this.fullImage.setRoundRadius(image.getRoundRadius()); + overlayView.videoPlayerContainer.setVisibility(View.GONE); + } } if (callback != null) { @@ -271,8 +282,10 @@ public void finishZoom() { if (finishTransition != null || !inOverlayMode) { return; } - if (!updateViewsLocation()) { - clear(); + if (!isSimple) { + if (!updateViewsLocation()) { + clear(); + } } finishTransition = ValueAnimator.ofFloat(1f, 0); finishTransition.addUpdateListener(valueAnimator -> { @@ -339,7 +352,6 @@ public boolean inOverlayMode() { return inOverlayMode; } - public boolean isInOverlayMode() { return inOverlayMode; } @@ -368,6 +380,9 @@ public ImageReceiver getPhotoImage() { } protected boolean zoomEnabled(View child, ImageReceiver receiver) { + if (isSimple) { + return true; + } Drawable drawable = receiver.getDrawable(); if (drawable instanceof AnimatedFileDrawable) { if (((AnimatedFileDrawable)receiver.getDrawable()).isLoadingStream()) { @@ -772,11 +787,23 @@ private boolean checkPointerIds(MotionEvent ev) { } protected void invalidateViews() { + if (isSimple && child != null) { + child.invalidate(); + } if (overlayView != null) { overlayView.invalidate(); } } + public void applyTransform(Canvas canvas) { + if (inOverlayMode) { + canvas.save(); + float s = pinchScale * finishProgress + 1f * 1f - finishProgress; + canvas.scale(s, s, parentOffsetX + pinchCenterX, parentOffsetY + pinchCenterY); + canvas.translate(parentOffsetX + pinchTranslationX * finishProgress, parentOffsetY + pinchTranslationY * finishProgress); + } + } + public View getChild() { return child; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PollCreateActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PollCreateActivity.java index f89f64a73f..0c26b00a45 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PollCreateActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PollCreateActivity.java @@ -534,7 +534,7 @@ private void setTextLeft(View cell, int index) { if (left <= max - max * 0.7f) { textCell.setText2(String.format("%d", left)); SimpleTextView textView = textCell.getTextView2(); - String key = left < 0 ? Theme.key_windowBackgroundWhiteRedText5 : Theme.key_windowBackgroundWhiteGrayText3; + int key = left < 0 ? Theme.key_text_RedRegular : Theme.key_windowBackgroundWhiteGrayText3; textView.setTextColor(Theme.getColor(key)); textView.setTag(key); } else { @@ -588,7 +588,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { case 2: { TextInfoPrivacyCell cell = (TextInfoPrivacyCell) holder.itemView; cell.setFixedSize(0); - cell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + cell.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); if (position == solutionInfoRow) { cell.setText(LocaleController.getString("AddAnExplanationInfo", R.string.AddAnExplanationInfo)); } else if (position == settingsSectionRow) { @@ -607,7 +607,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } case 3: { TextCell textCell = (TextCell) holder.itemView; - textCell.setColors(null, Theme.key_windowBackgroundWhiteBlueText4); + textCell.setColors(-1, Theme.key_windowBackgroundWhiteBlueText4); Drawable drawable1 = mContext.getResources().getDrawable(R.drawable.poll_add_circle); Drawable drawable2 = mContext.getResources().getDrawable(R.drawable.poll_add_plus); drawable1.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_switchTrackChecked), PorterDuff.Mode.SRC_IN)); @@ -744,7 +744,7 @@ protected void onActionModeStart(EditTextBoldCursor editText, ActionMode actionM if (menu.findItem(android.R.id.copy) == null) { return; } - parentFragment.fillActionModeMenu(menu); + ChatActivity.fillActionModeMenu(menu, parentFragment.getCurrentEncryptedChat()); } } }; @@ -991,7 +991,7 @@ public ArrayList getThemeDescriptions() { themeDescriptions.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SELECTORCOLOR, null, null, null, null, Theme.key_actionBarDefaultSelector)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{HeaderCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueHeader)); - themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{HeaderCell.class}, new String[]{"textView2"}, null, null, null, Theme.key_windowBackgroundWhiteRedText5)); + themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{HeaderCell.class}, new String[]{"textView2"}, null, null, null, Theme.key_text_RedRegular)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{HeaderCell.class}, new String[]{"textView2"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText3)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_TEXTCOLOR, new Class[]{PollEditTextCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); @@ -999,7 +999,7 @@ public ArrayList getThemeDescriptions() { themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_HINTTEXTCOLOR, new Class[]{PollEditTextCell.class}, new String[]{"deleteImageView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayIcon)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_HINTTEXTCOLOR, new Class[]{PollEditTextCell.class}, new String[]{"moveImageView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayIcon)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_USEBACKGROUNDDRAWABLE | ThemeDescription.FLAG_DRAWABLESELECTEDSTATE, new Class[]{PollEditTextCell.class}, new String[]{"deleteImageView"}, null, null, null, Theme.key_stickers_menuSelector)); - themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{PollEditTextCell.class}, new String[]{"textView2"}, null, null, null, Theme.key_windowBackgroundWhiteRedText5)); + themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{PollEditTextCell.class}, new String[]{"textView2"}, null, null, null, Theme.key_text_RedRegular)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{PollEditTextCell.class}, new String[]{"checkBox"}, null, null, null, Theme.key_windowBackgroundWhiteGrayIcon)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{PollEditTextCell.class}, new String[]{"checkBox"}, null, null, null, Theme.key_checkboxCheck)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PremiumPreviewFragment.java b/TMessagesProj/src/main/java/org/telegram/ui/PremiumPreviewFragment.java index ee4cec7964..d63e3ce018 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PremiumPreviewFragment.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PremiumPreviewFragment.java @@ -245,7 +245,7 @@ public PremiumPreviewFragment(String source) { } { - tiersGradientTools = new PremiumGradient.PremiumGradientTools(Theme.key_premiumGradient1, Theme.key_premiumGradient2, null, null); + tiersGradientTools = new PremiumGradient.PremiumGradientTools(Theme.key_premiumGradient1, Theme.key_premiumGradient2, -1, -1); tiersGradientTools.exactly = true; tiersGradientTools.x1 = 0; tiersGradientTools.y1 = 0f; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PrivacyControlActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PrivacyControlActivity.java index 1b919d33fe..5d8b4439a2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PrivacyControlActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PrivacyControlActivity.java @@ -69,6 +69,7 @@ import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.Components.BulletinFactory; import org.telegram.ui.Components.CombinedDrawable; +import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.HintView; import org.telegram.ui.Components.ImageUpdater; import org.telegram.ui.Components.LayoutHelper; @@ -135,6 +136,7 @@ public class PrivacyControlActivity extends BaseFragment implements Notification public final static int PRIVACY_RULES_TYPE_PHONE = 6; public final static int PRIVACY_RULES_TYPE_ADDED_BY_PHONE = 7; public final static int PRIVACY_RULES_TYPE_VOICE_MESSAGES = 8; + public final static int PRIVACY_RULES_TYPE_BIO = 9; public final static int TYPE_EVERYBODY = 0; public final static int TYPE_NOBODY = 1; @@ -256,7 +258,7 @@ public MessageCell(Context context) { setWillNotDraw(false); setClipToPadding(false); - shadowDrawable = Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow); + shadowDrawable = Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow); setPadding(0, AndroidUtilities.dp(11), 0, AndroidUtilities.dp(11)); int date = (int) (System.currentTimeMillis() / 1000) - 60 * 60; @@ -443,6 +445,8 @@ public View createView(Context context) { actionBar.setTitle(LocaleController.getString("PrivacyForwards", R.string.PrivacyForwards)); } else if (rulesType == PRIVACY_RULES_TYPE_PHOTO) { actionBar.setTitle(LocaleController.getString("PrivacyProfilePhoto", R.string.PrivacyProfilePhoto)); + } else if (rulesType == PRIVACY_RULES_TYPE_BIO) { + actionBar.setTitle(LocaleController.getString("PrivacyBio", R.string.PrivacyBio)); } else if (rulesType == PRIVACY_RULES_TYPE_P2P) { actionBar.setTitle(LocaleController.getString("PrivacyP2P", R.string.PrivacyP2P)); } else if (rulesType == PRIVACY_RULES_TYPE_CALLS) { @@ -481,7 +485,13 @@ public void onItemClick(int id) { FrameLayout frameLayout = (FrameLayout) fragmentView; frameLayout.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundGray)); - listView = new RecyclerListView(context); + listView = new RecyclerListView(context) { + @Override + protected void dispatchDraw(Canvas canvas) { + drawSectionBackground(canvas, shareSectionRow, shareDetailRow - 1, getThemedColor(Theme.key_windowBackgroundWhite)); + super.dispatchDraw(canvas); + } + }; listView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)); listView.setVerticalScrollBarEnabled(false); ((DefaultItemAnimator) listView.getItemAnimator()).setDelayAnimations(false); @@ -594,7 +604,7 @@ public void onItemClick(int id) { }); presentFragment(fragment); } else { - PrivacyUsersActivity fragment = new PrivacyUsersActivity(PrivacyUsersActivity.TYPE_PRIVACY, createFromArray, rulesType != PRIVACY_RULES_TYPE_LASTSEEN && rulesType != PRIVACY_RULES_TYPE_PHOTO, position == alwaysShareRow); + PrivacyUsersActivity fragment = new PrivacyUsersActivity(PrivacyUsersActivity.TYPE_PRIVACY, createFromArray, rulesType != PRIVACY_RULES_TYPE_LASTSEEN && rulesType != PRIVACY_RULES_TYPE_PHOTO && rulesType != PRIVACY_RULES_TYPE_BIO, position == alwaysShareRow); fragment.setDelegate((ids, added) -> { if (position == neverShareRow) { currentMinus = ids; @@ -620,6 +630,17 @@ public void onItemClick(int id) { presentFragment(new PrivacyControlActivity(ContactsController.PRIVACY_RULES_TYPE_P2P)); } }); + DefaultItemAnimator itemAnimator = new DefaultItemAnimator() { + @Override + protected void onMoveAnimationUpdate(RecyclerView.ViewHolder holder) { + super.onMoveAnimationUpdate(holder); + listView.invalidate(); + } + }; + itemAnimator.setDurations(350); + itemAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + itemAnimator.setDelayAnimations(false); + listView.setItemAnimator(itemAnimator); setMessageText(); @@ -668,6 +689,8 @@ private void applyCurrentPrivacySettings() { req.key = new TLRPC.TL_inputPrivacyKeyForwards(); } else if (rulesType == PRIVACY_RULES_TYPE_PHOTO) { req.key = new TLRPC.TL_inputPrivacyKeyProfilePhoto(); + } else if (rulesType == PRIVACY_RULES_TYPE_BIO) { + req.key = new TLRPC.TL_inputPrivacyKeyAbout(); } else if (rulesType == PRIVACY_RULES_TYPE_P2P) { req.key = new TLRPC.TL_inputPrivacyKeyPhoneP2P(); } else if (rulesType == PRIVACY_RULES_TYPE_CALLS) { @@ -890,11 +913,20 @@ private void updateRows(boolean animated) { sectionRow = rowCount++; everybodyRow = rowCount++; myContactsRow = rowCount++; - if (rulesType != PRIVACY_RULES_TYPE_PHOTO && rulesType != PRIVACY_RULES_TYPE_LASTSEEN && rulesType != PRIVACY_RULES_TYPE_CALLS && rulesType != PRIVACY_RULES_TYPE_P2P && - rulesType != PRIVACY_RULES_TYPE_FORWARDS && rulesType != PRIVACY_RULES_TYPE_PHONE && rulesType != PRIVACY_RULES_TYPE_VOICE_MESSAGES) { - nobodyRow = -1; - } else { + if ( + rulesType == PRIVACY_RULES_TYPE_PHOTO || + rulesType == PRIVACY_RULES_TYPE_BIO || + rulesType == PRIVACY_RULES_TYPE_LASTSEEN || + rulesType == PRIVACY_RULES_TYPE_CALLS || + rulesType == PRIVACY_RULES_TYPE_P2P || + rulesType == PRIVACY_RULES_TYPE_FORWARDS || + rulesType == PRIVACY_RULES_TYPE_PHONE || + rulesType == PRIVACY_RULES_TYPE_VOICE_MESSAGES || + rulesType == PRIVACY_RULES_TYPE_INVITE + ) { nobodyRow = rowCount++; + } else { + nobodyRow = -1; } if (rulesType == PRIVACY_RULES_TYPE_PHONE && currentType == TYPE_NOBODY) { phoneDetailRow = rowCount++; @@ -1115,7 +1147,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType case 5: default: view = new ShadowSectionCell(mContext); - Drawable drawable = Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow); + Drawable drawable = Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow); CombinedDrawable combinedDrawable = new CombinedDrawable(new ColorDrawable(Theme.getColor(Theme.key_windowBackgroundGray)), drawable); combinedDrawable.setFullsize(true); view.setBackgroundDrawable(combinedDrawable); @@ -1130,7 +1162,6 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType setAvatarCell.setBackgroundDrawable(Theme.getSelectorDrawable(false)); setAvatarCell.setColors(Theme.key_windowBackgroundWhiteBlueIcon, Theme.key_windowBackgroundWhiteBlueButton); cameraDrawable = new RLottieDrawable(R.raw.camera_outline, "" + R.raw.camera_outline, AndroidUtilities.dp(50), AndroidUtilities.dp(50), false, null); - setAvatarCell.imageView.setTranslationY(-AndroidUtilities.dp(9)); setAvatarCell.imageView.setTranslationX(-AndroidUtilities.dp(8)); setAvatarCell.imageView.setAnimation(cameraDrawable); setAvatarCell.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); @@ -1166,7 +1197,7 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto oldPhotoCell.setText(LocaleController.getString("RemovePublicPhoto", R.string.RemovePublicPhoto), false); oldPhotoCell.getImageView().setVisibility(View.VISIBLE); oldPhotoCell.setBackgroundDrawable(Theme.getSelectorDrawable(false)); - oldPhotoCell.setColors(Theme.key_windowBackgroundWhiteRedText, Theme.key_windowBackgroundWhiteRedText); + oldPhotoCell.setColors(Theme.key_text_RedRegular, Theme.key_text_RedRegular); oldPhotoCell.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); view = oldPhotoCell; break; @@ -1202,7 +1233,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } else { value = LocaleController.getString("EmpryUsersPlaceholder", R.string.EmpryUsersPlaceholder); } - if (rulesType != PRIVACY_RULES_TYPE_LASTSEEN && rulesType != PRIVACY_RULES_TYPE_PHOTO) { + if (rulesType != PRIVACY_RULES_TYPE_LASTSEEN && rulesType != PRIVACY_RULES_TYPE_PHOTO && rulesType != PRIVACY_RULES_TYPE_BIO) { textCell.setTextAndValue(LocaleController.getString("AlwaysAllow", R.string.AlwaysAllow), value, neverShareRow != -1); } else { textCell.setTextAndValue(LocaleController.getString("AlwaysShareWith", R.string.AlwaysShareWith), value, neverShareRow != -1); @@ -1215,7 +1246,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } else { value = LocaleController.getString("EmpryUsersPlaceholder", R.string.EmpryUsersPlaceholder); } - if (rulesType != PRIVACY_RULES_TYPE_LASTSEEN && rulesType != PRIVACY_RULES_TYPE_PHOTO) { + if (rulesType != PRIVACY_RULES_TYPE_LASTSEEN && rulesType != PRIVACY_RULES_TYPE_PHOTO && rulesType != PRIVACY_RULES_TYPE_BIO) { textCell.setTextAndValue(LocaleController.getString("NeverAllow", R.string.NeverAllow), value, false); } else { textCell.setTextAndValue(LocaleController.getString("NeverShareWith", R.string.NeverShareWith), value, false); @@ -1263,6 +1294,8 @@ public void onClick(@NonNull View view) { privacyCell.setText(LocaleController.getString("PrivacyForwardsInfo", R.string.PrivacyForwardsInfo)); } else if (rulesType == PRIVACY_RULES_TYPE_PHOTO) { privacyCell.setText(LocaleController.getString("PrivacyProfilePhotoInfo", R.string.PrivacyProfilePhotoInfo)); + } else if (rulesType == PRIVACY_RULES_TYPE_BIO) { + privacyCell.setText(LocaleController.getString("PrivacyBioInfo", R.string.PrivacyBioInfo)); } else if (rulesType == PRIVACY_RULES_TYPE_P2P) { privacyCell.setText(LocaleController.getString("PrivacyCallsP2PHelp", R.string.PrivacyCallsP2PHelp)); } else if (rulesType == PRIVACY_RULES_TYPE_CALLS) { @@ -1290,6 +1323,8 @@ public void onClick(@NonNull View view) { } } else if (rulesType == PRIVACY_RULES_TYPE_P2P) { privacyCell.setText(LocaleController.getString("CustomP2PInfo", R.string.CustomP2PInfo)); + } else if (rulesType == PRIVACY_RULES_TYPE_BIO) { + privacyCell.setText(LocaleController.getString("PrivacyBioInfo", R.string.PrivacyBioInfo)); } else if (rulesType == PRIVACY_RULES_TYPE_CALLS) { privacyCell.setText(LocaleController.getString("CustomCallInfo", R.string.CustomCallInfo)); } else if (rulesType == PRIVACY_RULES_TYPE_INVITE) { @@ -1310,7 +1345,7 @@ public void onClick(@NonNull View view) { privacyCell.setText(LocaleController.getString("PhotoForRestDescription", R.string.PhotoForRestDescription)); } if (backgroundResId != 0) { - Drawable drawable = Theme.getThemedDrawable(mContext, backgroundResId, Theme.key_windowBackgroundGrayShadow); + Drawable drawable = Theme.getThemedDrawableByKey(mContext, backgroundResId, Theme.key_windowBackgroundGrayShadow); CombinedDrawable combinedDrawable = new CombinedDrawable(new ColorDrawable(Theme.getColor(Theme.key_windowBackgroundGray)), drawable); combinedDrawable.setFullsize(true); privacyCell.setBackgroundDrawable(combinedDrawable); @@ -1325,6 +1360,8 @@ public void onClick(@NonNull View view) { headerCell.setText(LocaleController.getString("PrivacyForwardsTitle", R.string.PrivacyForwardsTitle)); } else if (rulesType == PRIVACY_RULES_TYPE_PHOTO) { headerCell.setText(LocaleController.getString("PrivacyProfilePhotoTitle", R.string.PrivacyProfilePhotoTitle)); + } else if (rulesType == PRIVACY_RULES_TYPE_BIO) { + headerCell.setText(LocaleController.getString("PrivacyBioTitle", R.string.PrivacyBioTitle)); } else if (rulesType == PRIVACY_RULES_TYPE_P2P) { headerCell.setText(LocaleController.getString("P2PEnabledWith", R.string.P2PEnabledWith)); } else if (rulesType == PRIVACY_RULES_TYPE_CALLS) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PrivacySettingsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PrivacySettingsActivity.java index b42cabfab1..8c7115d3d9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PrivacySettingsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PrivacySettingsActivity.java @@ -76,6 +76,7 @@ public class PrivacySettingsActivity extends BaseFragment implements Notificatio private int phoneNumberRow; private int lastSeenRow; private int profilePhotoRow; + private int bioRow; private int forwardsRow; private int callsRow; private int voicesRow; @@ -331,6 +332,8 @@ public boolean supportsPredictiveItemAnimations() { presentFragment(new PrivacyControlActivity(ContactsController.PRIVACY_RULES_TYPE_CALLS)); } else if (position == profilePhotoRow) { presentFragment(new PrivacyControlActivity(ContactsController.PRIVACY_RULES_TYPE_PHOTO)); + } else if (position == bioRow) { + presentFragment(new PrivacyControlActivity(ContactsController.PRIVACY_RULES_TYPE_BIO)); } else if (position == forwardsRow) { presentFragment(new PrivacyControlActivity(ContactsController.PRIVACY_RULES_TYPE_FORWARDS)); } else if (position == voicesRow) { @@ -436,7 +439,7 @@ public boolean supportsPredictiveItemAnimations() { showDialog(alertDialog); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } else if (position == contactsSuggestRow) { final TextCheckCell cell = (TextCheckCell) view; @@ -460,7 +463,7 @@ public boolean supportsPredictiveItemAnimations() { showDialog(alertDialog); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } else { cell.setChecked(newSuggest = true); @@ -548,7 +551,7 @@ public boolean supportsPredictiveItemAnimations() { showDialog(alertDialog); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); @@ -558,7 +561,7 @@ public boolean supportsPredictiveItemAnimations() { showDialog(alertDialog); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } else if (position == passportRow) { presentFragment(new PassportActivity(PassportActivity.TYPE_PASSWORD, 0, "", "", null, null, null, null, null)); @@ -629,6 +632,7 @@ private void updateRows(boolean notify) { phoneNumberRow = rowCount++; lastSeenRow = rowCount++; profilePhotoRow = rowCount++; + bioRow = rowCount++; forwardsRow = rowCount++; callsRow = rowCount++; groupsRow = rowCount++; @@ -857,6 +861,7 @@ public boolean isEnabled(RecyclerView.ViewHolder holder) { position == lastSeenRow && !getContactsController().getLoadingPrivacyInfo(ContactsController.PRIVACY_RULES_TYPE_LASTSEEN) || position == callsRow && !getContactsController().getLoadingPrivacyInfo(ContactsController.PRIVACY_RULES_TYPE_CALLS) || position == profilePhotoRow && !getContactsController().getLoadingPrivacyInfo(ContactsController.PRIVACY_RULES_TYPE_PHOTO) || + position == bioRow && !getContactsController().getLoadingPrivacyInfo(ContactsController.PRIVACY_RULES_TYPE_BIO) || position == forwardsRow && !getContactsController().getLoadingPrivacyInfo(ContactsController.PRIVACY_RULES_TYPE_FORWARDS) || position == phoneNumberRow && !getContactsController().getLoadingPrivacyInfo(ContactsController.PRIVACY_RULES_TYPE_PHONE) || position == voicesRow && !getContactsController().getLoadingPrivacyInfo(ContactsController.PRIVACY_RULES_TYPE_VOICE_MESSAGES) || @@ -954,6 +959,14 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { value = formatRulesString(getAccountInstance(), ContactsController.PRIVACY_RULES_TYPE_PHOTO); } textCell.setTextAndValue(LocaleController.getString("PrivacyProfilePhoto", R.string.PrivacyProfilePhoto), value, true); + } else if (position == bioRow) { + if (getContactsController().getLoadingPrivacyInfo(ContactsController.PRIVACY_RULES_TYPE_BIO)) { + showLoading = true; + loadingLen = 30; + } else { + value = formatRulesString(getAccountInstance(), ContactsController.PRIVACY_RULES_TYPE_BIO); + } + textCell.setTextAndValue(LocaleController.getString("PrivacyBio", R.string.PrivacyBio), value, true); } else if (position == forwardsRow) { if (getContactsController().getLoadingPrivacyInfo(ContactsController.PRIVACY_RULES_TYPE_FORWARDS)) { showLoading = true; @@ -1026,7 +1039,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { case 1: TextInfoPrivacyCell privacyCell = (TextInfoPrivacyCell) holder.itemView; boolean last = position == getItemCount() - 1; - privacyCell.setBackground(Theme.getThemedDrawable(mContext, last ? R.drawable.greydivider_bottom : R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + privacyCell.setBackground(Theme.getThemedDrawableByKey(mContext, last ? R.drawable.greydivider_bottom : R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); if (position == deleteAccountDetailRow) { privacyCell.setText(LocaleController.getString("DeleteAccountHelp", R.string.DeleteAccountHelp)); } else if (position == groupsDetailRow) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PrivacyUsersActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PrivacyUsersActivity.java index 557e876d4b..977751a7b1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PrivacyUsersActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PrivacyUsersActivity.java @@ -9,6 +9,7 @@ package org.telegram.ui; import android.content.Context; +import android.graphics.drawable.ColorDrawable; import android.os.Bundle; import android.view.View; import android.view.ViewGroup; @@ -40,6 +41,7 @@ import org.telegram.ui.Cells.TextInfoPrivacyCell; import org.telegram.ui.Components.AlertsCreator; import org.telegram.ui.Components.EmptyTextProgressView; +import org.telegram.ui.Components.ItemOptions; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.RecyclerListView; @@ -188,6 +190,12 @@ public void onItemClick(int id) { frameLayout.addView(emptyView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); listView = new RecyclerListView(context); + listView.setItemSelectorColorProvider(position -> { + if (position == deleteAllRow) { + return Theme.multAlpha(Theme.getColor(Theme.key_text_RedRegular), .12f); + } + return null; + }); listView.setEmptyView(emptyView); listView.setLayoutManager(layoutManager = new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)); listView.setVerticalScrollBarEnabled(false); @@ -255,9 +263,9 @@ public void onItemClick(int id) { listView.setOnItemLongClickListener((view, position) -> { if (position >= usersStartRow && position < usersEndRow) { if (currentType == TYPE_BLOCKED) { - showUnblockAlert(getMessagesController().blockePeers.keyAt(position - usersStartRow)); + showUnblockAlert(getMessagesController().blockePeers.keyAt(position - usersStartRow), view); } else { - showUnblockAlert(uidArray.get(position - usersStartRow)); + showUnblockAlert(uidArray.get(position - usersStartRow), view); } return true; } @@ -297,34 +305,25 @@ public void setDelegate(PrivacyActivityDelegate privacyActivityDelegate) { delegate = privacyActivityDelegate; } - private void showUnblockAlert(Long uid) { + private void showUnblockAlert(Long uid, View view) { if (getParentActivity() == null) { return; } - AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); - CharSequence[] items; - if (currentType == TYPE_BLOCKED) { - items = new CharSequence[]{LocaleController.getString("Unblock", R.string.Unblock)}; - } else { - items = new CharSequence[]{LocaleController.getString("Delete", R.string.Delete)}; - } - builder.setItems(items, (dialogInterface, i) -> { - if (i == 0) { - if (currentType == TYPE_BLOCKED) { - getMessagesController().unblockPeer(uid); - } else { - uidArray.remove(uid); - updateRows(); - if (delegate != null) { - delegate.didUpdateUserList(uidArray, false); - } - if (uidArray.isEmpty()) { - finishFragment(); - } + ItemOptions.makeOptions(this, view) + .setScrimViewBackground(new ColorDrawable(Theme.getColor(Theme.key_windowBackgroundWhite))) + .addIf(currentType == TYPE_BLOCKED, 0, LocaleController.getString("Unblock", R.string.Unblock), () -> getMessagesController().unblockPeer(uid)) + .addIf(currentType != TYPE_BLOCKED, currentType == TYPE_PRIVACY ? R.drawable.msg_user_remove : 0, LocaleController.getString("Remove", R.string.Remove), true, () -> { + uidArray.remove(uid); + updateRows(); + if (delegate != null) { + delegate.didUpdateUserList(uidArray, false); } - } - }); - showDialog(builder.create()); + if (uidArray.isEmpty()) { + finishFragment(); + } + }) + .setMinWidth(190) + .show(); } private void updateRows() { @@ -440,7 +439,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); ((ManageChatUserCell) view).setDelegate((cell, click) -> { if (click) { - showUnblockAlert((Long) cell.getTag()); + showUnblockAlert((Long) cell.getTag(), cell); } return true; }); @@ -462,7 +461,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType case 4: TextCell textCell = new TextCell(parent.getContext()); textCell.setText(LocaleController.getString("NotificationsDeleteAllException", R.string.NotificationsDeleteAllException), false); - textCell.setColors(null, Theme.key_windowBackgroundWhiteRedText5); + textCell.setColors(-1, Theme.key_text_RedRegular); view = textCell; view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); break; @@ -523,14 +522,14 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { privacyCell.setText(null); } if (usersStartRow == -1) { - privacyCell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + privacyCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } else { - privacyCell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + privacyCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); } } else if (position == usersDetailRow) { privacyCell.setFixedSize(12); privacyCell.setText(""); - privacyCell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + privacyCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } break; case 2: diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java index 8f46def98d..31c65c267c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java @@ -43,6 +43,8 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; +import android.media.MediaCodecInfo; +import android.media.MediaCodecList; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -88,6 +90,7 @@ import androidx.core.view.NestedScrollingParent3; import androidx.core.view.NestedScrollingParentHelper; import androidx.core.view.ViewCompat; +import androidx.interpolator.view.animation.FastOutSlowInInterpolator; import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.DiffUtil; import androidx.recyclerview.widget.LinearLayoutManager; @@ -116,6 +119,8 @@ import org.telegram.messenger.ImageLoader; import org.telegram.messenger.ImageLocation; import org.telegram.messenger.ImageReceiver; +//import org.telegram.messenger.LanguageDetector; +import org.telegram.messenger.LiteMode; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MediaController; import org.telegram.messenger.MediaDataController; @@ -194,8 +199,11 @@ import org.telegram.ui.Components.IdenticonDrawable; import org.telegram.ui.Components.ImageUpdater; import org.telegram.ui.Components.InstantCameraView; +import org.telegram.ui.Components.ItemOptions; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.LinkSpanDrawable; +import org.telegram.ui.Components.Paint.PersistColorPalette; +import org.telegram.ui.Components.Premium.GiftPremiumBottomSheet; import org.telegram.ui.Components.Premium.PremiumFeatureBottomSheet; import org.telegram.ui.Components.Premium.PremiumGradient; import org.telegram.ui.Components.Premium.PremiumPreviewBottomSheet; @@ -217,6 +225,10 @@ import org.telegram.ui.Components.UndoView; import org.telegram.ui.Components.VectorAvatarThumbDrawable; import org.telegram.ui.Components.voip.VoIPHelper; +import org.telegram.ui.Stories.ProfileStoriesView; +import org.telegram.ui.Stories.StoriesListPlaceProvider; +import org.telegram.ui.Stories.StoryViewer; +import org.telegram.ui.Stories.recorder.DualCameraView; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; @@ -315,6 +327,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. private ImageUpdater imageUpdater; private int avatarColor; TimerDrawable autoDeleteItemDrawable; + private ProfileStoriesView storyView; private View scrimView = null; private Paint scrimPaint = new Paint(Paint.ANTI_ALIAS_FLAG) { @@ -663,6 +676,16 @@ public void openPhotoForEdit(String file, String thumb, boolean isVideo) { private boolean hasCustomPhoto; private ImageReceiver fallbackImage; + public static ProfileActivity of(long dialogId) { + Bundle bundle = new Bundle(); + if (dialogId >= 0) { + bundle.putLong("user_id", dialogId); + } else { + bundle.putLong("chat_id", -dialogId); + } + return new ProfileActivity(bundle); + } + public int getTopicId() { return topicId; } @@ -1384,6 +1407,7 @@ public void onAnimationEnd(Animator animation) { } else { setVisibility(GONE); } + updateStoriesViewBounds(false); } @Override @@ -1404,6 +1428,7 @@ public void onAnimationStart(Animator animation) { videoCallItem.setVisibility(VISIBLE); } setVisibility(VISIBLE); + updateStoriesViewBounds(false); } }); avatarsViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @@ -1561,7 +1586,7 @@ public boolean onFragmentCreate() { banFromGroup = arguments.getLong("ban_chat_id", 0); reportReactionMessageId = arguments.getInt("report_reaction_message_id", 0); reportReactionFromDialogId = arguments.getLong("report_reaction_from_dialog_id", 0); - showAddToContacts = arguments.getBoolean("show_add_to_contacts"); + showAddToContacts = arguments.getBoolean("show_add_to_contacts", true); vcardPhone = PhoneFormat.stripExceptNumbers(arguments.getString("vcard_phone")); vcardFirstName = arguments.getString("vcard_first_name"); vcardLastName = arguments.getString("vcard_last_name"); @@ -1640,6 +1665,7 @@ public boolean onFragmentCreate() { getNotificationCenter().addObserver(this, NotificationCenter.chatInfoDidLoad); getNotificationCenter().addObserver(this, NotificationCenter.chatOnlineCountDidLoad); getNotificationCenter().addObserver(this, NotificationCenter.groupCallUpdated); + getNotificationCenter().addObserver(this, NotificationCenter.channelRightsUpdated); sortedUsers = new ArrayList<>(); updateOnlineCount(true); @@ -1778,6 +1804,7 @@ public void onFragmentDestroy() { getNotificationCenter().removeObserver(this, NotificationCenter.chatInfoDidLoad); getNotificationCenter().removeObserver(this, NotificationCenter.chatOnlineCountDidLoad); getNotificationCenter().removeObserver(this, NotificationCenter.groupCallUpdated); + getNotificationCenter().removeObserver(this, NotificationCenter.channelRightsUpdated); } if (avatarImage != null) { avatarImage.setImageDrawable(null); @@ -1813,6 +1840,12 @@ public void setItemsColor(int color, boolean isActionMode) { ttlIconView.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY)); } } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + updateStoriesViewBounds(false); + } }; actionBar.setColorFilterMode(PorterDuff.Mode.SRC_IN); actionBar.setForceSkipTouches(true); @@ -1900,21 +1933,23 @@ public void onItemClick(final int id) { showDialog(dialog); TextView button = (TextView) dialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(getThemedColor(Theme.key_dialogTextRed)); + button.setTextColor(getThemedColor(Theme.key_text_RedBold)); } } } } else { if (!userBlocked) { AlertsCreator.createClearOrDeleteDialogAlert(ProfileActivity.this, false, currentChat, user, currentEncryptedChat != null, true, true, (param) -> { - if (getParentLayout().getFragmentStack().get(getParentLayout().getFragmentStack().size() - 2) instanceof ChatActivity) { - getParentLayout().removeFragmentFromStack(getParentLayout().getFragmentStack().size() - 2); + List fragmentStack = getParentLayout().getFragmentStack(); + BaseFragment prevFragment = fragmentStack == null || fragmentStack.size() < 2 ? null : fragmentStack.get(fragmentStack.size() - 2); + if (prevFragment instanceof ChatActivity) { + getParentLayout().removeFragmentFromStack(fragmentStack.size() - 2); } finishFragment(); getNotificationCenter().postNotificationName(NotificationCenter.needDeleteDialog, dialogId, user, currentChat, param); }, getResourceProvider()); } else { - getMessagesController().unblockPeer(userId, ()-> getSendMessagesHelper().sendMessage("/start", userId, null, null, null, false, null, null, null, true, 0, null, false)); + getMessagesController().unblockPeer(userId, ()-> getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of("/start", userId, null, null, null, false, null, null, null, true, 0, null, false))); finishFragment(); } } @@ -1924,6 +1959,7 @@ public void onItemClick(final int id) { args.putLong("user_id", user.id); args.putBoolean("addContact", true); presentFragment(new ContactAddActivity(args, resourcesProvider)); + wentToAddContacts = true; } else if (id == share_contact) { Bundle args = new Bundle(); args.putBoolean("onlySelect", true); @@ -1937,6 +1973,7 @@ public void onItemClick(final int id) { Bundle args = new Bundle(); args.putLong("user_id", userId); presentFragment(new ContactAddActivity(args, resourcesProvider)); + wentToAddContacts = true; } else if (id == delete_contact) { final TLRPC.User user = getMessagesController().getUser(userId); if (user == null || getParentActivity() == null) { @@ -1949,13 +1986,17 @@ public void onItemClick(final int id) { ArrayList arrayList = new ArrayList<>(); arrayList.add(user); getContactsController().deleteContact(arrayList, true); + if (user != null) { + user.contact = false; + updateListAnimated(false); + } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); AlertDialog dialog = builder.create(); showDialog(dialog); TextView button = (TextView) dialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(getThemedColor(Theme.key_dialogTextRed)); + button.setTextColor(getThemedColor(Theme.key_text_RedBold)); } } else if (id == leave_group) { leaveChatPressed(); @@ -2002,7 +2043,7 @@ public void onClick(DialogInterface dialog, int which) { alertDialog.show(); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } else if (id == edit_channel) { if (isTopic) { @@ -2012,9 +2053,17 @@ public void onClick(DialogInterface dialog, int which) { presentFragment(fragment); } else { Bundle args = new Bundle(); - args.putLong("chat_id", chatId); + if (chatId != 0) { + args.putLong("chat_id", chatId); + } else if (isBot) { + args.putLong("user_id", userId); + } ChatEditActivity fragment = new ChatEditActivity(args); - fragment.setInfo(chatInfo); + if (chatInfo != null) { + fragment.setInfo(chatInfo); + } else { + fragment.setInfo(userInfo); + } presentFragment(fragment); } } else if (id == invite_to_group) { @@ -2198,7 +2247,7 @@ public void didChangeOwner(TLRPC.User user) { final boolean isVideo = location.imageType == FileLoader.IMAGE_TYPE_ANIMATION; File f = FileLoader.getInstance(currentAccount).getPathToAttach(location.location, isVideo ? "mp4" : null, true); if (f.exists()) { - MediaController.saveFile(f.toString(), getParentActivity(), 0, null, null, () -> { + MediaController.saveFile(f.toString(), getParentActivity(), 0, null, null, uri -> { if (getParentActivity() == null) { return; } @@ -2356,7 +2405,7 @@ public void didChangeOwner(TLRPC.User user) { showDialog(alertDialog); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(getThemedColor(Theme.key_dialogTextRed)); + button.setTextColor(getThemedColor(Theme.key_text_RedBold)); } } else if (id == add_photo) { onWriteButtonClick(); @@ -2496,6 +2545,9 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { avatarContainer.setVisibility(View.GONE); avatarsViewPager.resetCurrentItem(); avatarsViewPager.setVisibility(View.VISIBLE); + if (storyView != null) { + storyView.setExpandProgress(1f); + } expandPhoto = false; } @@ -2802,7 +2854,7 @@ protected void onDetachedFromWindow() { }; ArrayList users = chatInfo != null && chatInfo.participants != null && chatInfo.participants.participants.size() > 5 ? sortedUsers : null; - sharedMediaLayout = new SharedMediaLayout(context, did, sharedMediaPreloader, userInfo != null ? userInfo.common_chats_count : 0, sortedUsers, chatInfo, users != null, this, this, SharedMediaLayout.VIEW_TYPE_PROFILE_ACTIVITY, resourcesProvider) { + sharedMediaLayout = new SharedMediaLayout(context, did, sharedMediaPreloader, userInfo != null ? userInfo.common_chats_count : 0, sortedUsers, chatInfo, userInfo, users != null, this, this, SharedMediaLayout.VIEW_TYPE_PROFILE_ACTIVITY, resourcesProvider) { @Override protected void onSelectedTabChanged() { updateSelectedMediaTabText(); @@ -2833,11 +2885,12 @@ protected void onSearchStateChanged(boolean expanded) { // if (qrItem != null) { // qrItem.setVisibility(expanded ? GONE : INVISIBLE); // } + updateStoriesViewBounds(false); } @Override - protected boolean onMemberClick(TLRPC.ChatParticipant participant, boolean isLong) { - return ProfileActivity.this.onMemberClick(participant, isLong); + protected boolean onMemberClick(TLRPC.ChatParticipant participant, boolean isLong, View view) { + return ProfileActivity.this.onMemberClick(participant, isLong, view); } @Override @@ -2847,7 +2900,14 @@ protected void drawBackgroundWithBlur(Canvas canvas, float y, Rect rectTmp2, Pai @Override protected void invalidateBlur() { - contentView.invalidateBlur(); + if (contentView != null) { + contentView.invalidateBlur(); + } + } + + @Override + protected int getInitialTab() { + return TAB_STORIES; } }; sharedMediaLayout.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.MATCH_PARENT)); @@ -2944,7 +3004,7 @@ public void onTextChanged(EditText editText) { contentView.needBlur = true; FrameLayout frameLayout = (FrameLayout) fragmentView; - listView = new RecyclerListView(context) { + listView = new ClippedListView(context) { private VelocityTracker velocityTracker; @@ -3063,30 +3123,15 @@ public void runPendingAnimations() { protected long getAddAnimationDelay(long removeDuration, long moveDuration, long changeDuration) { return 0; } - - @Override - protected long getMoveAnimationDelay() { - return 0; - } - - @Override - public long getMoveDuration() { - return 220; - } - - @Override - public long getRemoveDuration() { - return 220; - } - - @Override - public long getAddDuration() { - return 220; - } }; listView.setItemAnimator(defaultItemAnimator); + defaultItemAnimator.setMoveDelay(0); + defaultItemAnimator.setMoveDuration(320); + defaultItemAnimator.setRemoveDuration(320); + defaultItemAnimator.setAddDuration(320); defaultItemAnimator.setSupportsChangeAnimations(false); defaultItemAnimator.setDelayAnimations(false); + defaultItemAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); listView.setClipToPadding(false); listView.setHideIfEmpty(false); @@ -3149,6 +3194,7 @@ public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerVi args.putString("first_name_card", vcardFirstName); args.putString("last_name_card", vcardLastName); presentFragment(new ContactAddActivity(args, resourcesProvider)); + wentToAddContacts = true; } else if (position == reportReactionRow) { AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity(), resourcesProvider); builder.setTitle(LocaleController.getString("ReportReaction", R.string.ReportReaction)); @@ -3200,7 +3246,7 @@ public void onClick(DialogInterface dialog, int which) { AlertDialog dialog = builder.show(); TextView button = (TextView) dialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } if (position == settingsKeyRow) { Bundle args = new Bundle(); @@ -3351,7 +3397,7 @@ public void openExceptions() { } else if (position == sendMessageRow) { onWriteButtonClick(); } else if (position == reportRow) { - AlertsCreator.createReportAlert(getParentActivity(), getDialogId(), 0, ProfileActivity.this, resourcesProvider, null); + AlertsCreator.createReportAlert(getParentActivity(), getDialogId(), 0, 0, ProfileActivity.this, resourcesProvider, null); } else if (position >= membersStartRow && position < membersEndRow) { TLRPC.ChatParticipant participant; if (!sortedUsers.isEmpty()) { @@ -3359,7 +3405,7 @@ public void openExceptions() { } else { participant = chatInfo.participants.participants.get(position - membersStartRow); } - onMemberClick(participant, false); + onMemberClick(participant, false, view); } else if (position == addMemberRow) { openAddMember(); } else if (position == usernameRow) { @@ -3710,9 +3756,11 @@ public boolean onItemClick(View view, int position) { (AndroidUtilities.isTabletInternal() && BuildVars.DEBUG_PRIVATE_VERSION) ? (SharedConfig.forceDisableTabletMode ? "Enable tablet mode" : "Disable tablet mode") : null, BuildVars.DEBUG_PRIVATE_VERSION ? LocaleController.getString(SharedConfig.isFloatingDebugActive ? R.string.FloatingDebugDisable : R.string.FloatingDebugEnable) : null, BuildVars.DEBUG_PRIVATE_VERSION ? "Force remove premium suggestions" : null, - BuildVars.DEBUG_PRIVATE_VERSION ? "Share device info" : null, /* No. 25 */ + BuildVars.DEBUG_PRIVATE_VERSION ? "Share device info" : null, "Force performance class", - BuildVars.DEBUG_PRIVATE_VERSION && !InstantCameraView.allowBigSizeCameraDebug() ? (!SharedConfig.bigCameraForRound ? "Force big camera for round" : "Disable big camera for round") : null + BuildVars.DEBUG_PRIVATE_VERSION && !InstantCameraView.allowBigSizeCameraDebug() ? (!SharedConfig.bigCameraForRound ? "Force big camera for round" : "Disable big camera for round") : null, + LocaleController.getString(DualCameraView.dualAvailableStatic(getContext()) ? "DebugMenuDualOff" : "DebugMenuDualOn"), + BuildVars.DEBUG_VERSION ? (SharedConfig.useSurfaceInStories ? "back to TextureView in stories" : "use SurfaceView in stories") : null, }; builder.setItems(items, (dialog, which) -> { @@ -3737,7 +3785,7 @@ public boolean onItemClick(View view, int position) { getMessagesStorage().clearSentMedia(); SharedConfig.setNoSoundHintShowed(false); SharedPreferences.Editor editor = MessagesController.getGlobalMainSettings().edit(); - editor.remove("archivehint").remove("proximityhint").remove("archivehint_l").remove("speedhint").remove("gifhint").remove("reminderhint").remove("soundHint").remove("themehint").remove("bganimationhint").remove("filterhint").commit(); + editor.remove("archivehint").remove("proximityhint").remove("archivehint_l").remove("speedhint").remove("gifhint").remove("reminderhint").remove("soundHint").remove("themehint").remove("bganimationhint").remove("filterhint").remove("n_0").remove("storyprvhint").remove("storyhint").remove("storyhint2").remove("storydualhint").remove("storysvddualhint").remove("stories_camera").remove("dualcam").remove("dualmatrix").remove("dual_available").remove("archivehint").commit(); MessagesController.getEmojiSettings(currentAccount).edit().remove("featured_hidden").remove("emoji_featured_hidden").commit(); SharedConfig.textSelectionHintShows = 0; SharedConfig.lockRecordAudioVideoHint = 0; @@ -3750,6 +3798,7 @@ public boolean onItemClick(View view, int position) { ChatThemeController.getInstance(currentAccount).clearCache(); getNotificationCenter().postNotificationName(NotificationCenter.newSuggestionsAvailable); RestrictedLanguagesSelectActivity.cleanup(); + PersistColorPalette.getInstance(currentAccount).cleanup(); } else if (which == 7) { VoIPHelper.showCallDebugSettings(getParentActivity()); } else if (which == 8) { @@ -3937,6 +3986,9 @@ public boolean onItemClick(View view, int position) { } info.append("\n").append(cpusInfo); + listCodecs("video/avc", info); + listCodecs("video/hevc", info); + showDialog(new ShareAlert(getParentActivity(), null, info.toString(), false, null, false) { @Override protected void onSend(LongSparseArray dids, int count, TLRPC.TL_forumTopic topic) { @@ -3966,6 +4018,17 @@ protected void onSend(LongSparseArray dids, int count, TLRPC.TL_fo builder2.show(); } else if (which == 27) { SharedConfig.toggleRoundCamera(); + } else if (which == 22) { + boolean enabled = DualCameraView.dualAvailableStatic(getContext()); + MessagesController.getGlobalMainSettings().edit().putBoolean("dual_available", !enabled).apply(); + try { + Toast.makeText(getParentActivity(), LocaleController.getString(!enabled ? R.string.DebugMenuDualOnToast : R.string.DebugMenuDualOffToast), Toast.LENGTH_SHORT).show(); + } catch (Exception e) {} + } else if (which == 23) { + SharedConfig.toggleSurfaceInStories(); + for (int i = 0; i < getParentLayout().getFragmentStack().size(); i++) { + getParentLayout().getFragmentStack().get(i).storyViewer = null; + } } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); @@ -3985,7 +4048,7 @@ protected void onSend(LongSparseArray dids, int count, TLRPC.TL_fo } else { participant = visibleChatParticipants.get(position - membersStartRow); } - return onMemberClick(participant, true); + return onMemberClick(participant, true, view); } else { return processOnClickOrPress(position, view, view.getWidth() / 2f, (int) (view.getHeight() * .75f)); } @@ -4056,7 +4119,7 @@ protected void onSend(LongSparseArray dids, int count, TLRPC.TL_fo showDialog(dialog); TextView button = (TextView) dialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } return true; }); @@ -4120,7 +4183,7 @@ public void didChangeOwner(TLRPC.User user) { }); TextView textView = new TextView(context); - textView.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteRedText)); + textView.setTextColor(getThemedColor(Theme.key_text_RedRegular)); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); textView.setGravity(Gravity.CENTER); textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); @@ -4271,7 +4334,7 @@ protected void dispatchDraw(Canvas canvas) { if (document == null) { return; } - Bulletin bulletin = BulletinFactory.of(ProfileActivity.this).createContainsEmojiBulletin(document, true, set -> { + Bulletin bulletin = BulletinFactory.of(ProfileActivity.this).createContainsEmojiBulletin(document, BulletinFactory.CONTAINS_EMOJI_IN_TOPIC, set -> { ArrayList inputSets = new ArrayList<>(1); inputSets.add(set); EmojiPacksAlert alert = new EmojiPacksAlert(ProfileActivity.this, getParentActivity(), resourcesProvider, inputSets); @@ -4514,6 +4577,21 @@ protected TextView createTextView() { }; mediaCounterTextView.setAlpha(0.0f); avatarContainer2.addView(mediaCounterTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 118, 0, 8, 0)); + storyView = new ProfileStoriesView(context, currentAccount, userId, avatarContainer, avatarImage, resourcesProvider) { + @Override + protected void onTap(StoryViewer.PlaceProvider provider) { + if (getMessagesController().getStoriesController().getStories(userId) != null) { + getOrCreateStoryViewer().open(context, userId, provider); + } else if (userInfo != null && userInfo.stories != null && userId != getUserConfig().clientUserId) { + getOrCreateStoryViewer().open(context, userInfo.stories, provider); + } else { + getOrCreateStoryViewer().open(context, userId, provider); + } + } + }; + updateStoriesViewBounds(false); + storyView.setUserFull(userInfo); + avatarContainer2.addView(storyView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); updateProfileData(true); writeButton = new RLottieImageView(context); @@ -4675,8 +4753,8 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { idTextView.setTranslationY(idTextViewY); final Object onlineTextViewTag = onlineTextView[1].getTag(); int statusColor; - if (onlineTextViewTag instanceof String) { - statusColor = getThemedColor((String) onlineTextViewTag); + if (onlineTextViewTag instanceof Integer) { + statusColor = getThemedColor((Integer) onlineTextViewTag); } else { statusColor = getThemedColor(Theme.key_avatar_subtitleInProfileBlue); } @@ -4702,7 +4780,7 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { avatarContainer.requestLayout(); setAvatarExpandProgress(anim.getAnimatedFraction()); }); - expandAnimator.setInterpolator(CubicBezierInterpolator.EASE_BOTH); + expandAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); expandAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { @@ -4714,6 +4792,7 @@ public void onAnimationEnd(Animator animation) { actionBar.setItemsBackgroundColor(isPulledDown ? Theme.ACTION_BAR_WHITE_SELECTOR_COLOR : getThemedColor(Theme.key_avatar_actionBarSelectorBlue), false); avatarImage.clearForeground(); doNotSetForeground = false; + updateStoriesViewBounds(false); } }); updateRowsIds(); @@ -4818,6 +4897,9 @@ private void setAvatarExpandProgress(float animatedFracture) { avatarContainer.setTranslationX(AndroidUtilities.lerp(avatarX, 0f, value)); avatarContainer.setTranslationY(AndroidUtilities.lerp((float) Math.ceil(avatarY), 0f, value)); avatarImage.setRoundRadius((int) AndroidUtilities.lerp(getSmallAvatarRoundRadius(), 0f, value)); + if (storyView != null) { + storyView.setExpandProgress(value); + } if (searchItem != null) { searchItem.setAlpha(1.0f - value); searchItem.setScaleY(1.0f - value); @@ -4878,8 +4960,8 @@ private void setAvatarExpandProgress(float animatedFracture) { mediaCounterTextView.setTranslationY(onlineTextViewY); final Object onlineTextViewTag = onlineTextView[1].getTag(); int statusColor; - if (onlineTextViewTag instanceof String) { - statusColor = getThemedColor((String) onlineTextViewTag); + if (onlineTextViewTag instanceof Integer) { + statusColor = getThemedColor((Integer) onlineTextViewTag); } else { statusColor = getThemedColor(Theme.key_avatar_subtitleInProfileBlue); } @@ -5005,7 +5087,7 @@ public void showStatusSelect() { xoff = MathUtils.clamp(ecenter - popupWidth / 2, 0, AndroidUtilities.displaySize.x - popupWidth); ecenter -= xoff; boolean hasEmoji = emojiStatusDrawable[1] != null && emojiStatusDrawable[1].getDrawable() instanceof AnimatedEmojiDrawable; - SelectAnimatedEmojiDialog popupLayout = new SelectAnimatedEmojiDialog(this, getContext(), true, Math.max(0, ecenter), SelectAnimatedEmojiDialog.TYPE_EMOJI_STATUS, resourcesProvider, topMarginDp) { + SelectAnimatedEmojiDialog popupLayout = new SelectAnimatedEmojiDialog(this, getContext(), true, Math.max(0, ecenter), SelectAnimatedEmojiDialog.TYPE_EMOJI_STATUS, true, resourcesProvider, topMarginDp) { @Override protected void onEmojiSelected(View emojiView, Long documentId, TLRPC.Document document, Integer until) { TLRPC.TL_account_updateEmojiStatus req = new TLRPC.TL_account_updateEmojiStatus(); @@ -5174,11 +5256,15 @@ private void openDiscussion() { presentFragment(new ChatActivity(args)); } - public boolean onMemberClick(TLRPC.ChatParticipant participant, boolean isLong) { - return onMemberClick(participant, isLong, false); + public boolean onMemberClick(TLRPC.ChatParticipant participant, boolean isLong, View view) { + return onMemberClick(participant, isLong, false, view); } public boolean onMemberClick(TLRPC.ChatParticipant participant, boolean isLong, boolean resultOnly) { + return onMemberClick(participant, isLong, resultOnly, null); + } + + public boolean onMemberClick(TLRPC.ChatParticipant participant, boolean isLong, boolean resultOnly, View view) { if (getParentActivity() == null) { return false; } @@ -5214,76 +5300,41 @@ public boolean onMemberClick(TLRPC.ChatParticipant participant, boolean isLong, editingAdmin = participant instanceof TLRPC.TL_chatParticipantAdmin; } - ArrayList items = resultOnly ? null : new ArrayList<>(); - ArrayList icons = resultOnly ? null : new ArrayList<>(); - final ArrayList actions = resultOnly ? null : new ArrayList<>(); - boolean hasRemove = false; - - if (canEditAdmin) { - if (resultOnly) { - return true; - } - items.add(editingAdmin ? LocaleController.getString("EditAdminRights", R.string.EditAdminRights) : LocaleController.getString("SetAsAdmin", R.string.SetAsAdmin)); - icons.add(R.drawable.msg_admins); - actions.add(0); - } - if (canRestrict) { - if (resultOnly) { - return true; - } - items.add(LocaleController.getString("ChangePermissions", R.string.ChangePermissions)); - icons.add(R.drawable.msg_permissions); - actions.add(1); - } - if (allowKick) { - if (resultOnly) { - return true; - } - items.add(LocaleController.getString("KickFromGroup", R.string.KickFromGroup)); - icons.add(R.drawable.msg_remove); - actions.add(2); - hasRemove = true; - } - if (resultOnly) { - return false; + boolean result = (canEditAdmin || canRestrict || allowKick); + if (resultOnly || !result) { + return result; } - if (items.isEmpty()) { - return false; - } - AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity(), resourcesProvider); - builder.setItems(items.toArray(new CharSequence[0]), AndroidUtilities.toIntArray(icons), (dialogInterface, i) -> { - if (actions.get(i) == 2) { - kickUser(selectedUser, participant); + Utilities.Callback openRightsEdit = action -> { + if (channelParticipant != null) { + openRightsEdit(action, user, participant, channelParticipant.admin_rights, channelParticipant.banned_rights, channelParticipant.rank, editingAdmin); } else { - int action = actions.get(i); - if (action == 1 && (channelParticipant instanceof TLRPC.TL_channelParticipantAdmin || participant instanceof TLRPC.TL_chatParticipantAdmin)) { - AlertDialog.Builder builder2 = new AlertDialog.Builder(getParentActivity(), resourcesProvider); - builder2.setTitle(LocaleController.getString("NekoX", R.string.NekoX)); - builder2.setMessage(LocaleController.formatString("AdminWillBeRemoved", R.string.AdminWillBeRemoved, ContactsController.formatName(user.first_name, user.last_name))); - builder2.setPositiveButton(LocaleController.getString("OK", R.string.OK), (dialog, which) -> { - if (channelParticipant != null) { - openRightsEdit(action, user, participant, channelParticipant.admin_rights, channelParticipant.banned_rights, channelParticipant.rank, editingAdmin); - } else { - openRightsEdit(action, user, participant, null, null, "", editingAdmin); - } - }); - builder2.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showDialog(builder2.create()); + openRightsEdit(action, user, participant, null, null, "", editingAdmin); + } + }; + + ItemOptions.makeOptions(this, view) + .setScrimViewBackground(new ColorDrawable(Theme.getColor(Theme.key_windowBackgroundWhite))) + .addIf(canEditAdmin, R.drawable.msg_admins, editingAdmin ? LocaleController.getString("EditAdminRights", R.string.EditAdminRights) : LocaleController.getString("SetAsAdmin", R.string.SetAsAdmin), () -> openRightsEdit.run(0)) + .addIf(canRestrict, R.drawable.msg_permissions, LocaleController.getString("ChangePermissions", R.string.ChangePermissions), () -> { + if (channelParticipant instanceof TLRPC.TL_channelParticipantAdmin || participant instanceof TLRPC.TL_chatParticipantAdmin) { + showDialog( + new AlertDialog.Builder(getParentActivity(), resourcesProvider) + .setTitle(LocaleController.getString("AppName", R.string.AppName)) + .setMessage(LocaleController.formatString("AdminWillBeRemoved", R.string.AdminWillBeRemoved, ContactsController.formatName(user.first_name, user.last_name))) + .setPositiveButton(LocaleController.getString("OK", R.string.OK), (dialog, which) -> openRightsEdit.run(1)) + .setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null) + .create() + ); } else { - if (channelParticipant != null) { - openRightsEdit(action, user, participant, channelParticipant.admin_rights, channelParticipant.banned_rights, channelParticipant.rank, editingAdmin); - } else { - openRightsEdit(action, user, participant, null, null, "", editingAdmin); - } + openRightsEdit.run(1); } - } - }); - AlertDialog alertDialog = builder.create(); - showDialog(alertDialog); - if (hasRemove) { - alertDialog.setItemColor(items.size() - 1, getThemedColor(Theme.key_dialogTextRed), getThemedColor(Theme.key_dialogRedIcon)); - } + }) + .addIf(allowKick, R.drawable.msg_remove, LocaleController.getString("KickFromGroup", R.string.KickFromGroup), true, () -> { + kickUser(selectedUser, participant); + }) + .setMinWidth(190) + .show(); } else { if (participant.user_id == getUserConfig().getClientUserId()) { return false; @@ -5396,7 +5447,7 @@ private boolean processOnClickOrPress(final int position, final View view, final username = username1; } else if (chatId != 0) { final TLRPC.Chat chat = getMessagesController().getChat(chatId); - if (chat == null || !ChatObject.isPublic(chat)) { + if (chat == null || topicId == 0 && !ChatObject.isPublic(chat)) { return false; } username = ChatObject.getPublicUsername(chat); @@ -5791,6 +5842,9 @@ private void getChannelParticipants(boolean reload) { @Override public void setValue(ActionBar object, float value) { mediaHeaderAnimationProgress = value; + if (storyView != null) { + storyView.setActionBarActionMode(value); + } topView.invalidate(); int color1 = getThemedColor(Theme.key_profile_title); @@ -5887,6 +5941,7 @@ private void setMediaHeaderVisible(boolean visible) { sharedMediaLayout.photoVideoOptionsItem.setVisibility(View.INVISIBLE); } } + updateStoriesViewBounds(false); if (actionBar != null) { actionBar.createMenu().requestLayout(); @@ -5914,6 +5969,11 @@ private void setMediaHeaderVisible(boolean visible) { if (visible) { animators.add(ObjectAnimator.ofFloat(this, HEADER_SHADOW, 0.0f)); } + if (storyView != null) { + ValueAnimator va = ValueAnimator.ofFloat(0, 1); + va.addUpdateListener(a -> updateStoriesViewBounds(true)); + animators.add(va); + } headerAnimatorSet = new AnimatorSet(); headerAnimatorSet.playTogether(animators); @@ -5955,6 +6015,7 @@ public void onAnimationEnd(Animator animation) { headerShadowAnimatorSet.start(); } } + updateStoriesViewBounds(false); headerAnimatorSet = null; } @@ -6086,7 +6147,7 @@ public void updateSelectedMediaTabText() { } int id = sharedMediaLayout.getClosestTab(); int[] mediaCount = sharedMediaPreloader.getLastMediaCount(); - if (id == 0) { + if (id == SharedMediaLayout.TAB_PHOTOVIDEO) { if (mediaCount[MediaDataController.MEDIA_VIDEOS_ONLY] == 0 && mediaCount[MediaDataController.MEDIA_PHOTOS_ONLY] == 0) { mediaCounterTextView.setText(LocaleController.formatPluralString("Media", mediaCount[MediaDataController.MEDIA_PHOTOVIDEO])); } else if (sharedMediaLayout.getPhotosVideosTypeFilter() == SharedMediaLayout.FILTER_PHOTOS_ONLY || mediaCount[MediaDataController.MEDIA_VIDEOS_ONLY] == 0) { @@ -6097,20 +6158,22 @@ public void updateSelectedMediaTabText() { String str = String.format("%s, %s", LocaleController.formatPluralString("Photos", mediaCount[MediaDataController.MEDIA_PHOTOS_ONLY]), LocaleController.formatPluralString("Videos", mediaCount[MediaDataController.MEDIA_VIDEOS_ONLY])); mediaCounterTextView.setText(str); } - } else if (id == 1) { + } else if (id == SharedMediaLayout.TAB_FILES) { mediaCounterTextView.setText(LocaleController.formatPluralString("Files", mediaCount[MediaDataController.MEDIA_FILE])); - } else if (id == 2) { + } else if (id == SharedMediaLayout.TAB_VOICE) { mediaCounterTextView.setText(LocaleController.formatPluralString("Voice", mediaCount[MediaDataController.MEDIA_AUDIO])); - } else if (id == 3) { + } else if (id == SharedMediaLayout.TAB_LINKS) { mediaCounterTextView.setText(LocaleController.formatPluralString("Links", mediaCount[MediaDataController.MEDIA_URL])); - } else if (id == 4) { + } else if (id == SharedMediaLayout.TAB_AUDIO) { mediaCounterTextView.setText(LocaleController.formatPluralString("MusicFiles", mediaCount[MediaDataController.MEDIA_MUSIC])); - } else if (id == 5) { + } else if (id == SharedMediaLayout.TAB_GIF) { mediaCounterTextView.setText(LocaleController.formatPluralString("GIFs", mediaCount[MediaDataController.MEDIA_GIF])); - } else if (id == 6) { + } else if (id == SharedMediaLayout.TAB_COMMON_GROUPS) { mediaCounterTextView.setText(LocaleController.formatPluralString("CommonGroups", userInfo.common_chats_count)); - } else if (id == 7) { + } else if (id == SharedMediaLayout.TAB_GROUPUSERS) { mediaCounterTextView.setText(onlineTextView[1].getText()); + } else if (id == SharedMediaLayout.TAB_STORIES) { + mediaCounterTextView.setText(LocaleController.formatPluralString("ProfileStoriesCount", sharedMediaLayout.getStoriesCount(id))); } } @@ -6136,6 +6199,10 @@ private void needLayout(boolean animated) { if (writeButton != null) { writeButton.setTranslationY((actionBar.getOccupyStatusBar() ? AndroidUtilities.statusBarHeight : 0) + ActionBar.getCurrentActionBarHeight() + extraHeight + searchTransitionOffset - AndroidUtilities.dp(29.5f)); + if (storyView != null) { + storyView.setExpandCoords(avatarContainer2.getMeasuredWidth() - AndroidUtilities.dp(111), (actionBar.getOccupyStatusBar() ? AndroidUtilities.statusBarHeight : 0) + ActionBar.getCurrentActionBarHeight() + extraHeight + searchTransitionOffset); + } + if (!openAnimationInProgress) { boolean setVisible = diff > 0.2f && !searchMode && (imageUpdater == null || setAvatarRow == -1); if (setVisible && chatId != 0) { @@ -6200,6 +6267,9 @@ public void onAnimationEnd(Animator animation) { if (h > AndroidUtilities.dp(88f) || isPulledDown) { expandProgress = Math.max(0f, Math.min(1f, (h - AndroidUtilities.dp(88f)) / (listView.getMeasuredWidth() - newTop - AndroidUtilities.dp(88f)))); avatarScale = AndroidUtilities.lerp((42f + 18f) / 42f, (42f + 42f + 18f) / 42f, Math.min(1f, expandProgress * 3f)); + if (storyView != null) { + storyView.invalidate(); + } final float durationFactor = Math.min(AndroidUtilities.dpf2(2000f), Math.max(AndroidUtilities.dpf2(1100f), Math.abs(listViewVelocityY))) / AndroidUtilities.dpf2(1100f); @@ -6232,7 +6302,13 @@ public void onAnimationEnd(Animator animation) { float value = AndroidUtilities.lerp(expandAnimatorValues, currentExpanAnimatorFracture); expandAnimatorValues[0] = value; expandAnimatorValues[1] = 1f; - expandAnimator.setDuration((long) ((1f - value) * 250f / durationFactor)); + if (storyView != null && !storyView.isEmpty()) { + expandAnimator.setInterpolator(new FastOutSlowInInterpolator()); + expandAnimator.setDuration((long) ((1f - value) * 1.3f * 250f / durationFactor)); + } else { + expandAnimator.setInterpolator(CubicBezierInterpolator.EASE_BOTH); + expandAnimator.setDuration((long) ((1f - value) * 250f / durationFactor)); + } expandAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { @@ -6282,7 +6358,7 @@ public void onAnimationEnd(Animator animation) { otherItem.hideSubItem(delete_avatar); otherItem.showSubItem(add_photo); otherItem.showSubItem(logout); - otherItem.showSubItem(edit_name); +// otherItem.showSubItem(edit_name); } } if (searchItem != null) { @@ -6297,6 +6373,7 @@ public void onAnimationEnd(Animator animation) { float value = AndroidUtilities.lerp(expandAnimatorValues, currentExpanAnimatorFracture); expandAnimatorValues[0] = value; expandAnimatorValues[1] = 0f; + expandAnimator.setInterpolator(CubicBezierInterpolator.EASE_BOTH); if (!isInLandscapeMode) { expandAnimator.setDuration((long) (value * 250f / durationFactor)); } else { @@ -6354,6 +6431,9 @@ public void onAnimationEnd(Animator animation) { nameTextView[1].setScaleY(1.67f); avatarScale = AndroidUtilities.lerp(1.0f, (42f + 42f + 18f) / 42f, avatarAnimationProgress); + if (storyView != null) { + storyView.setExpandProgress(1f); + } avatarImage.setRoundRadius((int) AndroidUtilities.lerp(getSmallAvatarRoundRadius(), 0f, avatarAnimationProgress)); avatarContainer.setTranslationX(AndroidUtilities.lerp(avX, 0, avatarAnimationProgress)); @@ -6389,6 +6469,9 @@ public void onAnimationEnd(Animator animation) { avatarContainer.requestLayout(); } else if (extraHeight <= AndroidUtilities.dp(88f)) { avatarScale = (42 + 18 * diff) / 42.0f; + if (storyView != null) { + storyView.invalidate(); + } float nameScale = 1.0f + 0.12f * diff; if (expandAnimator == null || !expandAnimator.isRunning()) { avatarContainer.setScaleX(avatarScale); @@ -6651,7 +6734,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { chatInfo.online_count = (Integer) args[1]; updateOnlineCount(true); updateProfileData(false); - } else if (id == NotificationCenter.contactsDidLoad) { + } else if (id == NotificationCenter.contactsDidLoad || id == NotificationCenter.channelRightsUpdated) { createActionBarMenu(true); } else if (id == NotificationCenter.encryptedChatCreated) { if (creatingChat) { @@ -6736,6 +6819,12 @@ public void didReceivedNotification(int id, int account, final Object... args) { long uid = (Long) args[0]; if (uid == userId) { userInfo = (TLRPC.UserFull) args[1]; + if (storyView != null) { + storyView.setUserFull(userInfo); + } + if (sharedMediaLayout != null) { + sharedMediaLayout.setUserInfo(userInfo); + } if (imageUpdater != null) { if (listAdapter != null && !TextUtils.equals(userInfo.about, currentBio)) { listAdapter.notifyItemChanged(bioRow); @@ -6962,6 +7051,9 @@ public UndoView getUndoView() { } public boolean onBackPressed() { + if (closeStoryViewer()) { + return false; + } return actionBar.isEnabled() && (sharedMediaRow == -1 || sharedMediaLayout == null || !sharedMediaLayout.closeActionMode()); } @@ -6999,6 +7091,7 @@ private void updateSharedMediaRows() { @Override public void onTransitionAnimationStart(boolean isOpen, boolean backward) { + super.onTransitionAnimationStart(isOpen, backward); isFragmentOpened = isOpen; if ((!isOpen && backward || isOpen && !backward) && playProfileAnimation != 0 && allowProfileAnimation && !isPulledDown) { openAnimationInProgress = true; @@ -7121,11 +7214,6 @@ public void setAvatarAnimationProgress(float progress) { } } - @Override - public int getNavigationBarColor() { - return Theme.getColor(Theme.key_windowBackgroundGray, resourcesProvider); - } - boolean profileTransitionInProgress; @Override @@ -7209,6 +7297,10 @@ public AnimatorSet onCustomTransitionAnimation(final boolean isOpen, final Runna nameTextView[a].setAlpha(a == 0 ? 1.0f : 0.0f); animators.add(ObjectAnimator.ofFloat(nameTextView[a], View.ALPHA, a == 0 ? 0.0f : 1.0f)); } + if (storyView != null) { + storyView.setAlpha(0.0f); + animators.add(ObjectAnimator.ofFloat(storyView, View.ALPHA, 1.0f)); + } if (timeItem.getTag() != null) { animators.add(ObjectAnimator.ofFloat(timeItem, View.ALPHA, 1.0f, 0.0f)); animators.add(ObjectAnimator.ofFloat(timeItem, View.SCALE_X, 1.0f, 0.0f)); @@ -7272,6 +7364,9 @@ public AnimatorSet onCustomTransitionAnimation(final boolean isOpen, final Runna for (int a = 0; a < 2; a++) { animators.add(ObjectAnimator.ofFloat(nameTextView[a], View.ALPHA, a == 0 ? 1.0f : 0.0f)); } + if (storyView != null) { + animators.add(ObjectAnimator.ofFloat(storyView, View.ALPHA, 0.0f)); + } if (timeItem.getTag() != null) { timeItem.setAlpha(0f); animators.add(ObjectAnimator.ofFloat(timeItem, View.ALPHA, 0.0f, 1.0f)); @@ -7326,6 +7421,7 @@ public AnimatorSet onCustomTransitionAnimation(final boolean isOpen, final Runna if (fragmentView != null) { fragmentView.invalidate(); } + updateStoriesViewBounds(true); }); animatorSet.playTogether(valueAnimator); @@ -7388,50 +7484,19 @@ private void updateOnlineCount(boolean notify) { } try { - Collections.sort(sortedUsers, (lhs, rhs) -> { - TLRPC.User user1 = getMessagesController().getUser(chatInfo.participants.participants.get(rhs).user_id); - TLRPC.User user2 = getMessagesController().getUser(chatInfo.participants.participants.get(lhs).user_id); - int status1 = 0; - int status2 = 0; - if (user1 != null) { - if (user1.bot) { - status1 = -110; - } else if (user1.self) { - status1 = currentTime + 50000; - } else if (user1.status != null) { - status1 = user1.status.expires; - } - } - if (user2 != null) { - if (user2.bot) { - status2 = -110; - } else if (user2.self) { - status2 = currentTime + 50000; - } else if (user2.status != null) { - status2 = user2.status.expires; - } - } - if (status1 > 0 && status2 > 0) { - if (status1 > status2) { - return 1; - } else if (status1 < status2) { - return -1; - } - return 0; - } else if (status1 < 0 && status2 < 0) { - if (status1 > status2) { - return 1; - } else if (status1 < status2) { - return -1; - } - return 0; - } else if (status1 < 0 && status2 > 0 || status1 == 0 && status2 != 0) { - return -1; - } else if (status2 < 0 && status1 > 0 || status2 == 0 && status1 != 0) { - return 1; + Collections.sort(sortedUsers, Comparator.comparingInt(hs -> { + TLRPC.User user = getMessagesController().getUser(chatInfo.participants.participants.get((int) hs).user_id); + if (user != null) { + if (user.bot) { + return -110; + } else if (user.self) { + return currentTime + 50000; + } else if (user.status != null) { + return user.status.expires; + } } - return 0; - }); + return Integer.MIN_VALUE; + }).reversed()); } catch (Exception e) { FileLog.e(e); } @@ -7464,6 +7529,12 @@ public void setChatInfo(TLRPC.ChatFull value) { public void setUserInfo(TLRPC.UserFull value) { userInfo = value; + if (storyView != null) { + storyView.setUserFull(userInfo); + } + if (sharedMediaLayout != null) { + sharedMediaLayout.setUserInfo(userInfo); + } } public boolean canSearchMembers() { @@ -7601,6 +7672,9 @@ private void updateRowsIds() { } } } + if (!hasMedia && userInfo != null) { + hasMedia = userInfo.stories_pinned_available; + } if (userId != 0) { if (LocaleController.isRTL) { @@ -7633,14 +7707,16 @@ private void updateRowsIds() { } settingsSectionRow2 = rowCount++; + chatRow = rowCount++; + privacyRow = rowCount++; notificationRow = rowCount++; dataRow = rowCount++; - privacyRow = rowCount++; - chatRow = rowCount++; - stickersRow = rowCount++; - devicesRow = rowCount++; - filtersRow = rowCount++; liteModeRow = rowCount++; +// stickersRow = rowCount++; + if (getMessagesController().filtersEnabled || !getMessagesController().dialogFilters.isEmpty() || true) { + filtersRow = rowCount++; + } + devicesRow = rowCount++; nekoRow = rowCount++; languageRow = rowCount++; devicesSectionRow = rowCount++; @@ -7706,13 +7782,16 @@ private void updateRowsIds() { addToGroupInfoRow = rowCount++; } + boolean divider = false; + if (showAddToContacts && user != null && !user.contact && !user.bot && !UserObject.isService(user.id)) { + addToContactsRow = rowCount++; + divider = true; + } if (reportReactionMessageId != 0 && !ContactsController.getInstance(currentAccount).isContact(userId)) { reportReactionRow = rowCount++; - reportDividerRow = rowCount++; + divider = true; } - - if (showAddToContacts) { - addToContactsRow = rowCount++; + if (divider) { reportDividerRow = rowCount++; } @@ -8054,7 +8133,7 @@ private void updateProfileData(boolean reload) { isOnline[0] = false; newString2 = LocaleController.formatUserStatus(currentAccount, user, isOnline, shortStatus ? new boolean[1] : null); if (onlineTextView[1] != null && !mediaHeaderVisible) { - String key = isOnline[0] ? Theme.key_profile_status : Theme.key_avatar_subtitleInProfileBlue; + int key = isOnline[0] ? Theme.key_profile_status : Theme.key_avatar_subtitleInProfileBlue; onlineTextView[1].setTag(key); if (!isPulledDown) { onlineTextView[1].setTextColor(getThemedColor(key)); @@ -8226,7 +8305,7 @@ private void updateProfileData(boolean reload) { idTextView.setText("ID: " + userId); } id = userId; - avatarImage.getImageReceiver().setVisible(!PhotoViewer.isShowingImage(photoBig), false); + avatarImage.getImageReceiver().setVisible(!PhotoViewer.isShowingImage(photoBig) && (storyViewer == null || !storyViewer.isShown() || storyViewer.transitionViewHolder.view != avatarImage), storyView != null); } else if (chatId != 0) { TLRPC.Chat chat = getMessagesController().getChat(chatId); if (chat != null) { @@ -8446,7 +8525,7 @@ private void updateProfileData(boolean reload) { prevLoadedImageLocation = imageLocation; getFileLoader().loadFile(imageLocation, chat, null, FileLoader.PRIORITY_LOW, 1); } - avatarImage.getImageReceiver().setVisible(!PhotoViewer.isShowingImage(photoBig), false); + avatarImage.getImageReceiver().setVisible(!PhotoViewer.isShowingImage(photoBig) && (storyViewer == null || !storyViewer.isShown() || storyViewer.transitionViewHolder.view != avatarImage), storyView != null); if (chat.photo != null && chat.photo.dc_id != 0) { dc = chat.photo.dc_id; idTextView.setText("ID: " + chatId + ", DC: " + dc); @@ -8528,6 +8607,10 @@ private void createActionBarMenu(boolean animated) { otherItem.addSubItem(edit_name, R.drawable.msg_edit, LocaleController.getString("EditName", R.string.EditName)); selfUser = true; } else { + if (user.bot && user.bot_can_edit) { + editItemVisible = true; + } + if (userInfo != null && userInfo.phone_calls_available) { callItemVisible = true; videoCallItemVisible = Build.VERSION.SDK_INT >= 18 && userInfo.video_calls_available; @@ -8555,7 +8638,7 @@ private void createActionBarMenu(boolean animated) { } if (isBot) { if (!userBlocked) { - otherItem.addSubItem(block_contact, R.drawable.msg_block, LocaleController.getString("BotStop", R.string.BotStop)).setColors(getThemedColor(Theme.key_dialogTextRed), getThemedColor(Theme.key_dialogTextRed)); + otherItem.addSubItem(block_contact, R.drawable.msg_block2, LocaleController.getString(R.string.DeleteAndBlock)).setColors(getThemedColor(Theme.key_text_RedRegular), getThemedColor(Theme.key_text_RedRegular)); } else { otherItem.addSubItem(block_contact, R.drawable.msg_retry, LocaleController.getString("BotRestart", R.string.BotRestart)); } @@ -8592,7 +8675,8 @@ private void createActionBarMenu(boolean animated) { } else if (chatId != 0) { TLRPC.Chat chat = getMessagesController().getChat(chatId); hasVoiceChatItem = false; - if (topicId == 0 && ChatObject.canUserDoAdminAction(chat, ChatObject.ACTION_DELETE_MESSAGES)) { + + if (topicId == 0 && ChatObject.canChangeChatInfo(chat)) { createAutoDeleteItem(context); } createAutoTranslateItem(-chatId, topicId, !isTopic || chatInfo == null || !chatInfo.participants_hidden || ChatObject.hasAdminRights(chat)); @@ -8784,6 +8868,7 @@ private void createActionBarMenu(boolean animated) { if (sharedMediaLayout != null) { sharedMediaLayout.getSearchItem().requestLayout(); } + updateStoriesViewBounds(false); } private void createAutoDeleteItem(Context context) { @@ -8823,7 +8908,7 @@ public Theme.ResourcesProvider getResourceProvider() { return resourcesProvider; } - public int getThemedColor(String key) { + public int getThemedColor(int key) { return Theme.getColor(key, resourcesProvider); } @@ -8880,7 +8965,7 @@ public boolean didSelectDialogs(DialogsActivity presentFragment(new ChatActivity(args), true); removeSelfFromStack(); TLRPC.User user = getMessagesController().getUser(userId); - getSendMessagesHelper().sendMessage(user, did, null, null, null, null, true, 0); + getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of(user, did, null, null, null, null, true, 0)); if (!TextUtils.isEmpty(message)) { AccountInstance accountInstance = AccountInstance.getInstance(currentAccount); SendMessagesHelper.prepareSendingText(accountInstance, message.toString(), did, true, 0); @@ -9011,6 +9096,9 @@ private Animator searchExpandTransition(boolean enter) { emptyView.setAlpha(1f - progressHalf); avatarContainer.setAlpha(progressHalf); + if (storyView != null) { + storyView.setAlpha(progressHalf); + } nameTextView[1].setAlpha(progressHalf); onlineTextView[1].setAlpha(progressHalf); idTextView.setAlpha(progressHalf); @@ -9087,6 +9175,9 @@ private void updateSearchViewState(boolean enter) { actionBar.onSearchFieldVisibilityChanged(enter); avatarContainer.setVisibility(hide); + if (storyView != null) { + storyView.setVisibility(hide); + } nameTextView[1].setVisibility(hide); onlineTextView[1].setVisibility(hide); idTextView.setVisibility(hide); @@ -9101,6 +9192,9 @@ private void updateSearchViewState(boolean enter) { searchItem.setVisibility(hide); avatarContainer.setAlpha(1f); + if (storyView != null) { + storyView.setAlpha(1f); + } nameTextView[1].setAlpha(1f); onlineTextView[1].setAlpha(1f); idTextView.setAlpha(1f); @@ -9491,7 +9585,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { totalHeight += listView.getChildAt(i).getMeasuredHeight(); } } - int paddingHeight = fragmentView.getMeasuredHeight() - ActionBar.getCurrentActionBarHeight() - AndroidUtilities.statusBarHeight - totalHeight; + int paddingHeight = (fragmentView == null ? 0 : fragmentView.getMeasuredHeight()) - ActionBar.getCurrentActionBarHeight() - AndroidUtilities.statusBarHeight - totalHeight; if (paddingHeight > AndroidUtilities.dp(88)) { paddingHeight = 0; } @@ -9608,15 +9702,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { case VIEW_TYPE_TEXT_DETAIL_MULTILINE: case VIEW_TYPE_TEXT_DETAIL: TextDetailCell detailCell = (TextDetailCell) holder.itemView; - if (position == usernameRow) { - Drawable drawable = ContextCompat.getDrawable(detailCell.getContext(), R.drawable.msg_qr_mini); - drawable.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_switch2TrackChecked), PorterDuff.Mode.MULTIPLY)); - detailCell.setImage(drawable, LocaleController.getString("GetQRCode", R.string.GetQRCode)); - detailCell.setImageClickListener(ProfileActivity.this::onTextDetailCellImageClicked); - } else { - detailCell.setImage(null); - detailCell.setImageClickListener(null); - } + boolean containsQr = false; if (position == phoneRow) { String text; TLRPC.User user = getMessagesController().getUser(userId); @@ -9636,10 +9722,8 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } else if (position == usernameRow) { String text, username = null; CharSequence value; - boolean isUser = false; ArrayList usernames = new ArrayList<>(); if (userId != 0) { - isUser = true; final TLRPC.User user = getMessagesController().getUser(userId); if (user != null) { usernames.addAll(user.usernames); @@ -9648,36 +9732,40 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { username = user.username; } usernames = user == null ? new ArrayList<>() : new ArrayList<>(user.usernames); - value = LocaleController.getString("Username", R.string.Username); - } else if (currentChat != null) { - TLRPC.Chat chat = getMessagesController().getChat(chatId); - username = chat.username; - usernames.addAll(chat.usernames); - value = LocaleController.getString("InviteLink", R.string.InviteLink); - } else { - text = ""; - value = ""; - usernames = new ArrayList<>(); - } - if (TextUtils.isEmpty(username) && usernames != null) { - for (int i = 0; i < usernames.size(); ++i) { - TLRPC.TL_username u = usernames.get(i); - if (u != null && u.active && !TextUtils.isEmpty(u.username)) { - username = u.username; - break; + if (TextUtils.isEmpty(username) && usernames != null) { + for (int i = 0; i < usernames.size(); ++i) { + TLRPC.TL_username u = usernames.get(i); + if (u != null && u.active && !TextUtils.isEmpty(u.username)) { + username = u.username; + break; + } } } - } - if (isUser) { + value = LocaleController.getString("Username", R.string.Username); if (username != null) { text = "@" + username; } else { text = "—"; } + containsQr = true; + } else if (currentChat != null) { + TLRPC.Chat chat = getMessagesController().getChat(chatId); + username = ChatObject.getPublicUsername(chat); + usernames.addAll(chat.usernames); + if (ChatObject.isPublic(chat)) { + containsQr = true; + text = getMessagesController().linkPrefix + "/" + username + (topicId != 0 ? "/" + topicId : ""); + value = LocaleController.getString("InviteLink", R.string.InviteLink); + } else { + text = getMessagesController().linkPrefix + "/c/" + chat.id + (topicId != 0 ? "/" + topicId : ""); + value = LocaleController.getString("InviteLinkPrivate", R.string.InviteLinkPrivate); + } } else { - text = getLink(username, topicId); + text = ""; + value = ""; + usernames = new ArrayList<>(); } - detailCell.setTextAndValue(text, alsoUsernamesString(username, usernames, value), false); + detailCell.setTextAndValue(text, alsoUsernamesString(username, usernames, value), isTopic); } else if (position == restrictionReasonRow) { ArrayList reasons = new ArrayList<>(); if (userId != 0) { @@ -9747,6 +9835,15 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { detailCell.setTextAndValue(text, value, true); detailCell.setContentDescriptionValueFirst(true); } + if (containsQr) { + Drawable drawable = ContextCompat.getDrawable(detailCell.getContext(), R.drawable.msg_qr_mini); + drawable.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_switch2TrackChecked), PorterDuff.Mode.MULTIPLY)); + detailCell.setImage(drawable, LocaleController.getString("GetQRCode", R.string.GetQRCode)); + detailCell.setImageClickListener(ProfileActivity.this::onTextDetailCellImageClicked); + } else { + detailCell.setImage(null); + detailCell.setImageClickListener(null); + } detailCell.setTag(position); break; case VIEW_TYPE_ABOUT_LINK: @@ -9789,14 +9886,14 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { textCell.setTextAndValue(LocaleController.getString("MessageLifetime", R.string.MessageLifetime), value, false,false); } else if (position == unblockRow) { textCell.setText(LocaleController.getString("Unblock", R.string.Unblock), false); - textCell.setColors(null, Theme.key_windowBackgroundWhiteRedText5); + textCell.setColors(-1, Theme.key_text_RedRegular); } else if (position == settingsKeyRow) { IdenticonDrawable identiconDrawable = new IdenticonDrawable(); TLRPC.EncryptedChat encryptedChat = getMessagesController().getEncryptedChat(DialogObject.getEncryptedChatId(dialogId)); identiconDrawable.setEncryptedChat(encryptedChat); textCell.setTextAndValueDrawable(LocaleController.getString("EncryptionKey", R.string.EncryptionKey), identiconDrawable, false); } else if (position == joinRow) { - textCell.setColors(null, Theme.key_windowBackgroundWhiteBlueText2); + textCell.setColors(-1, Theme.key_windowBackgroundWhiteBlueText2); if (currentChat.megagroup) { textCell.setText(LocaleController.getString("ProfileJoinGroup", R.string.ProfileJoinGroup), false); } else { @@ -9838,22 +9935,22 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } else if (position == sendMessageRow) { textCell.setText(LocaleController.getString("SendMessageLocation", R.string.SendMessageLocation), true); } else if (position == addToContactsRow) { - textCell.setText(LocaleController.getString(R.string.AddToContacts), false); - textCell.setColors(null, Theme.key_windowBackgroundWhiteBlackText); + textCell.setTextAndIcon(LocaleController.getString(R.string.AddToContacts), R.drawable.msg_contact_add, false); + textCell.setColors(Theme.key_windowBackgroundWhiteBlueIcon, Theme.key_windowBackgroundWhiteBlueButton); } else if (position == reportReactionRow) { TLRPC.Chat chat = getMessagesController().getChat(-reportReactionFromDialogId); if (chat != null && ChatObject.canBlockUsers(chat)) { - textCell.setText(LocaleController.getString("ReportReactionAndBan", R.string.ReportReactionAndBan), false); + textCell.setTextAndIcon(LocaleController.getString("ReportReactionAndBan", R.string.ReportReactionAndBan), R.drawable.msg_block2, false); } else { - textCell.setText(LocaleController.getString("ReportReaction", R.string.ReportReaction), false); + textCell.setTextAndIcon(LocaleController.getString("ReportReaction", R.string.ReportReaction), R.drawable.msg_report,false); } - textCell.setColors(null, Theme.key_windowBackgroundWhiteRedText5); - textCell.setColors(null, Theme.key_windowBackgroundWhiteRedText5); + textCell.setColors(Theme.key_text_RedBold, Theme.key_text_RedRegular); + textCell.setColors(Theme.key_text_RedBold, Theme.key_text_RedRegular); } else if (position == reportRow) { textCell.setText(LocaleController.getString("ReportUserLocation", R.string.ReportUserLocation), false); - textCell.setColors(null, Theme.key_windowBackgroundWhiteRedText5); - textCell.setColors(null, Theme.key_windowBackgroundWhiteRedText5); + textCell.setColors(-1, Theme.key_text_RedRegular); + textCell.setColors(-1, Theme.key_text_RedRegular); } else if (position == languageRow) { textCell.setTextAndIcon(LocaleController.getString("Language", R.string.Language), R.drawable.msg_language, false); } else if (position == notificationRow) { @@ -10340,9 +10437,9 @@ private SearchResult[] onCreateSearchArray() { new SearchResult(504, LocaleController.getString(R.string.AddPhoto), 0, ProfileActivity.this::onWriteButtonClick), new SearchResult(1, LocaleController.getString("NotificationsAndSounds", R.string.NotificationsAndSounds), R.drawable.msg_notifications, () -> presentFragment(new NotificationsSettingsActivity())), - new SearchResult(2, LocaleController.getString("NotificationsPrivateChats", R.string.NotificationsPrivateChats), LocaleController.getString("NotificationsAndSounds", R.string.NotificationsAndSounds), R.drawable.msg_notifications, () -> presentFragment(new NotificationsCustomSettingsActivity(NotificationsController.TYPE_PRIVATE, new ArrayList<>(), true))), - new SearchResult(3, LocaleController.getString("NotificationsGroups", R.string.NotificationsGroups), LocaleController.getString("NotificationsAndSounds", R.string.NotificationsAndSounds), R.drawable.msg_notifications, () -> presentFragment(new NotificationsCustomSettingsActivity(NotificationsController.TYPE_GROUP, new ArrayList<>(), true))), - new SearchResult(4, LocaleController.getString("NotificationsChannels", R.string.NotificationsChannels), LocaleController.getString("NotificationsAndSounds", R.string.NotificationsAndSounds), R.drawable.msg_notifications, () -> presentFragment(new NotificationsCustomSettingsActivity(NotificationsController.TYPE_CHANNEL, new ArrayList<>(), true))), + new SearchResult(2, LocaleController.getString("NotificationsPrivateChats", R.string.NotificationsPrivateChats), LocaleController.getString("NotificationsAndSounds", R.string.NotificationsAndSounds), R.drawable.msg_notifications, () -> presentFragment(new NotificationsCustomSettingsActivity(NotificationsController.TYPE_PRIVATE, new ArrayList<>(), null, true))), + new SearchResult(3, LocaleController.getString("NotificationsGroups", R.string.NotificationsGroups), LocaleController.getString("NotificationsAndSounds", R.string.NotificationsAndSounds), R.drawable.msg_notifications, () -> presentFragment(new NotificationsCustomSettingsActivity(NotificationsController.TYPE_GROUP, new ArrayList<>(), null, true))), + new SearchResult(4, LocaleController.getString("NotificationsChannels", R.string.NotificationsChannels), LocaleController.getString("NotificationsAndSounds", R.string.NotificationsAndSounds), R.drawable.msg_notifications, () -> presentFragment(new NotificationsCustomSettingsActivity(NotificationsController.TYPE_CHANNEL, new ArrayList<>(), null, true))), new SearchResult(5, LocaleController.getString("VoipNotificationSettings", R.string.VoipNotificationSettings), "callsSectionRow", LocaleController.getString("NotificationsAndSounds", R.string.NotificationsAndSounds), R.drawable.msg_notifications, () -> presentFragment(new NotificationsSettingsActivity())), new SearchResult(6, LocaleController.getString("BadgeNumber", R.string.BadgeNumber), "badgeNumberSection", LocaleController.getString("NotificationsAndSounds", R.string.NotificationsAndSounds), R.drawable.msg_notifications, () -> presentFragment(new NotificationsSettingsActivity())), new SearchResult(7, LocaleController.getString("InAppNotifications", R.string.InAppNotifications), "inappSectionRow", LocaleController.getString("NotificationsAndSounds", R.string.NotificationsAndSounds), R.drawable.msg_notifications, () -> presentFragment(new NotificationsSettingsActivity())), @@ -10354,7 +10451,16 @@ private SearchResult[] onCreateSearchArray() { new SearchResult(13, LocaleController.getString(R.string.RepeatNotifications), "repeatRow", LocaleController.getString("NotificationsAndSounds", R.string.NotificationsAndSounds), R.drawable.msg_notifications, () -> presentFragment(new NotificationsSettingsActivity())), new SearchResult(100, LocaleController.getString("PrivacySettings", R.string.PrivacySettings), R.drawable.msg_secret, () -> presentFragment(new PrivacySettingsActivity())), + new SearchResult(109, LocaleController.getString("TwoStepVerification", R.string.TwoStepVerification), LocaleController.getString("PrivacySettings", R.string.PrivacySettings), R.drawable.msg2_secret, () -> presentFragment(new TwoStepVerificationActivity())), + new SearchResult(124, LocaleController.getString(R.string.AutoDeleteMessages), LocaleController.getString(R.string.PrivacySettings), R.drawable.msg2_secret, () -> { + if (getUserConfig().getGlobalTTl() >= 0) { + presentFragment(new AutoDeleteMessagesActivity()); + } + }), + new SearchResult(108, LocaleController.getString("Passcode", R.string.Passcode), LocaleController.getString("PrivacySettings", R.string.PrivacySettings), R.drawable.msg2_secret, () -> presentFragment(PasscodeActivity.determineOpenFragment())), + SharedConfig.hasEmailLogin ? new SearchResult(125, LocaleController.getString(R.string.EmailLogin), "emailLoginRow", LocaleController.getString("PrivacySettings", R.string.PrivacySettings), R.drawable.msg2_secret, () -> presentFragment(new PrivacySettingsActivity())) : null, new SearchResult(101, LocaleController.getString("BlockedUsers", R.string.BlockedUsers), LocaleController.getString("PrivacySettings", R.string.PrivacySettings), R.drawable.msg_secret, () -> presentFragment(new PrivacyUsersActivity())), + new SearchResult(110, LocaleController.getString("SessionsTitle", R.string.SessionsTitle), R.drawable.msg2_secret, () -> presentFragment(new SessionsActivity(0))), new SearchResult(105, LocaleController.getString("PrivacyPhone", R.string.PrivacyPhone), LocaleController.getString("PrivacySettings", R.string.PrivacySettings), R.drawable.msg_secret, () -> presentFragment(new PrivacyControlActivity(ContactsController.PRIVACY_RULES_TYPE_PHONE, true))), new SearchResult(102, LocaleController.getString("PrivacyLastSeen", R.string.PrivacyLastSeen), LocaleController.getString("PrivacySettings", R.string.PrivacySettings), R.drawable.msg_secret, () -> presentFragment(new PrivacyControlActivity(ContactsController.PRIVACY_RULES_TYPE_LASTSEEN, true))), new SearchResult(103, LocaleController.getString("PrivacyProfilePhoto", R.string.PrivacyProfilePhoto), LocaleController.getString("PrivacySettings", R.string.PrivacySettings), R.drawable.msg_secret, () -> presentFragment(new PrivacyControlActivity(ContactsController.PRIVACY_RULES_TYPE_PHOTO, true))), @@ -10375,9 +10481,6 @@ private SearchResult[] onCreateSearchArray() { } presentFragment(new PrivacyControlActivity(ContactsController.PRIVACY_RULES_TYPE_VOICE_MESSAGES, true)); }), - new SearchResult(108, LocaleController.getString("Passcode", R.string.Passcode), LocaleController.getString("PrivacySettings", R.string.PrivacySettings), R.drawable.msg2_secret, () -> presentFragment(PasscodeActivity.determineOpenFragment())), - new SearchResult(109, LocaleController.getString("TwoStepVerification", R.string.TwoStepVerification), LocaleController.getString("PrivacySettings", R.string.PrivacySettings), R.drawable.msg2_secret, () -> presentFragment(new TwoStepVerificationActivity())), - new SearchResult(110, LocaleController.getString("SessionsTitle", R.string.SessionsTitle), R.drawable.msg2_secret, () -> presentFragment(new SessionsActivity(0))), getMessagesController().autoarchiveAvailable ? new SearchResult(121, LocaleController.getString("ArchiveAndMute", R.string.ArchiveAndMute), "newChatsRow", LocaleController.getString("PrivacySettings", R.string.PrivacySettings), R.drawable.msg2_secret, () -> presentFragment(new PrivacySettingsActivity())) : null, new SearchResult(112, LocaleController.getString("DeleteAccountIfAwayFor2", R.string.DeleteAccountIfAwayFor2), "deleteAccountRow", LocaleController.getString("PrivacySettings", R.string.PrivacySettings), R.drawable.msg2_secret, () -> presentFragment(new PrivacySettingsActivity())), new SearchResult(113, LocaleController.getString("PrivacyPaymentsClear", R.string.PrivacyPaymentsClear), "paymentsClearRow", LocaleController.getString("PrivacySettings", R.string.PrivacySettings), R.drawable.msg2_secret, () -> presentFragment(new PrivacySettingsActivity())), @@ -10404,9 +10507,6 @@ private SearchResult[] onCreateSearchArray() { new SearchResult(209, LocaleController.getString("WhenConnectedOnWiFi", R.string.WhenConnectedOnWiFi), LocaleController.getString("DataSettings", R.string.DataSettings), R.drawable.msg2_data, () -> presentFragment(new DataAutoDownloadActivity(1))), new SearchResult(210, LocaleController.getString("WhenRoaming", R.string.WhenRoaming), LocaleController.getString("DataSettings", R.string.DataSettings), R.drawable.msg2_data, () -> presentFragment(new DataAutoDownloadActivity(2))), new SearchResult(211, LocaleController.getString("ResetAutomaticMediaDownload", R.string.ResetAutomaticMediaDownload), "resetDownloadRow", LocaleController.getString("DataSettings", R.string.DataSettings), R.drawable.msg2_data, () -> presentFragment(new DataSettingsActivity())), - new SearchResult(212, LocaleController.getString("AutoplayMedia", R.string.AutoplayMedia), "autoplayHeaderRow", LocaleController.getString("DataSettings", R.string.DataSettings), R.drawable.msg2_data, () -> presentFragment(new DataSettingsActivity())), - new SearchResult(213, LocaleController.getString("AutoplayGIF", R.string.AutoplayGIF), "autoplayGifsRow", LocaleController.getString("DataSettings", R.string.DataSettings), R.drawable.msg2_data, () -> presentFragment(new DataSettingsActivity())), - new SearchResult(214, LocaleController.getString("AutoplayVideo", R.string.AutoplayVideo), "autoplayVideoRow", LocaleController.getString("DataSettings", R.string.DataSettings), R.drawable.msg2_data, () -> presentFragment(new DataSettingsActivity())), new SearchResult(215, LocaleController.getString("Streaming", R.string.Streaming), "streamSectionRow", LocaleController.getString("DataSettings", R.string.DataSettings), R.drawable.msg2_data, () -> presentFragment(new DataSettingsActivity())), new SearchResult(216, LocaleController.getString("EnableStreaming", R.string.EnableStreaming), "enableStreamRow", LocaleController.getString("DataSettings", R.string.DataSettings), R.drawable.msg2_data, () -> presentFragment(new DataSettingsActivity())), new SearchResult(217, LocaleController.getString("Calls", R.string.Calls), "callsSectionRow", LocaleController.getString("DataSettings", R.string.DataSettings), R.drawable.msg2_data, () -> presentFragment(new DataSettingsActivity())), @@ -10434,12 +10534,13 @@ private SearchResult[] onCreateSearchArray() { new SearchResult(324, LocaleController.getString(R.string.AppIcon), "appIconHeaderRow", LocaleController.getString("ChatSettings", R.string.ChatSettings), R.drawable.msg2_discussion, () -> presentFragment(new ThemeActivity(ThemeActivity.THEME_TYPE_BASIC))), new SearchResult(305, LocaleController.getString("AutoNightTheme", R.string.AutoNightTheme), LocaleController.getString("ChatSettings", R.string.ChatSettings), R.drawable.msg2_discussion, () -> presentFragment(new ThemeActivity(ThemeActivity.THEME_TYPE_NIGHT))), new SearchResult(307, LocaleController.getString("ChromeCustomTabs", R.string.ChromeCustomTabs), "customTabsRow", LocaleController.getString("ChatSettings", R.string.ChatSettings), R.drawable.msg2_discussion, () -> presentFragment(new ThemeActivity(ThemeActivity.THEME_TYPE_BASIC))), - new SearchResult(308, LocaleController.getString("DirectShare", R.string.DirectShare), "directShareRow", LocaleController.getString("ChatSettings", R.string.ChatSettings), R.drawable.msg2_discussion, () -> presentFragment(new ThemeActivity(ThemeActivity.THEME_TYPE_BASIC))), - new SearchResult(309, LocaleController.getString("EnableAnimations", R.string.EnableAnimations), "enableAnimationsRow", LocaleController.getString("ChatSettings", R.string.ChatSettings), R.drawable.msg2_discussion, () -> presentFragment(new ThemeActivity(ThemeActivity.THEME_TYPE_BASIC))), + new SearchResult(328, LocaleController.getString(R.string.NextMediaTap), "nextMediaTapRow", LocaleController.getString("ChatSettings", R.string.ChatSettings), R.drawable.msg2_discussion, () -> presentFragment(new ThemeActivity(ThemeActivity.THEME_TYPE_BASIC))), + new SearchResult(327, LocaleController.getString(R.string.RaiseToListen), "raiseToListenRow", LocaleController.getString("ChatSettings", R.string.ChatSettings), R.drawable.msg2_discussion, () -> presentFragment(new ThemeActivity(ThemeActivity.THEME_TYPE_BASIC))), new SearchResult(310, LocaleController.getString("RaiseToSpeak", R.string.RaiseToSpeak), "raiseToSpeakRow", LocaleController.getString("ChatSettings", R.string.ChatSettings), R.drawable.msg2_discussion, () -> presentFragment(new ThemeActivity(ThemeActivity.THEME_TYPE_BASIC))), + new SearchResult(326, LocaleController.getString(R.string.PauseMusicOnMedia), "pauseOnMediaRow", LocaleController.getString("ChatSettings", R.string.ChatSettings), R.drawable.msg2_discussion, () -> presentFragment(new ThemeActivity(ThemeActivity.THEME_TYPE_BASIC))), new SearchResult(325, LocaleController.getString(R.string.MicrophoneForVoiceMessages), "bluetoothScoRow", LocaleController.getString("ChatSettings", R.string.ChatSettings), R.drawable.msg2_discussion, () -> presentFragment(new ThemeActivity(ThemeActivity.THEME_TYPE_BASIC))), + new SearchResult(308, LocaleController.getString("DirectShare", R.string.DirectShare), "directShareRow", LocaleController.getString("ChatSettings", R.string.ChatSettings), R.drawable.msg2_discussion, () -> presentFragment(new ThemeActivity(ThemeActivity.THEME_TYPE_BASIC))), new SearchResult(311, LocaleController.getString("SendByEnter", R.string.SendByEnter), "sendByEnterRow", LocaleController.getString("ChatSettings", R.string.ChatSettings), R.drawable.msg2_discussion, () -> presentFragment(new ThemeActivity(ThemeActivity.THEME_TYPE_BASIC))), - SharedConfig.canBlurChat() ? new SearchResult(326, LocaleController.getString(R.string.BlurInChat), "chatBlurRow", LocaleController.getString("ChatSettings", R.string.ChatSettings), R.drawable.msg2_discussion, () -> presentFragment(new ThemeActivity(ThemeActivity.THEME_TYPE_BASIC))) : null, new SearchResult(318, LocaleController.getString("DistanceUnits", R.string.DistanceUnits), "distanceRow", LocaleController.getString("ChatSettings", R.string.ChatSettings), R.drawable.msg2_discussion, () -> presentFragment(new ThemeActivity(ThemeActivity.THEME_TYPE_BASIC))), new SearchResult(600, LocaleController.getString(R.string.StickersName), R.drawable.msg2_sticker, () -> presentFragment(new StickersActivity(MediaDataController.TYPE_IMAGE, null))), @@ -10471,6 +10572,103 @@ private SearchResult[] onCreateSearchArray() { new SearchResult(610, LocaleController.getString(R.string.FeaturedEmojiPacks), "featuredStickersHeaderRow", LocaleController.getString(R.string.StickersName), LocaleController.getString(R.string.Emoji), R.drawable.input_smile, () -> presentFragment(new StickersActivity(MediaDataController.TYPE_EMOJIPACKS, null))), new SearchResult(611, LocaleController.getString(R.string.DoubleTapSetting), null, LocaleController.getString(R.string.StickersName), R.drawable.msg_sticker, () -> presentFragment(new ReactionsDoubleTapManageActivity())), + new SearchResult(900, LocaleController.getString(R.string.PowerUsage), null, R.drawable.msg2_battery, () -> presentFragment(new LiteModeSettingsActivity())), + new SearchResult(901, LocaleController.getString(R.string.LiteOptionsStickers), LocaleController.getString(R.string.PowerUsage), R.drawable.msg2_battery, () -> { + LiteModeSettingsActivity set = new LiteModeSettingsActivity(); + presentFragment(set); + set.scrollToFlags(LiteMode.FLAGS_ANIMATED_STICKERS); + }), + new SearchResult(902, LocaleController.getString(R.string.LiteOptionsAutoplayKeyboard), null, LocaleController.getString(R.string.PowerUsage), LocaleController.getString(R.string.LiteOptionsStickers), R.drawable.msg2_battery, () -> { + LiteModeSettingsActivity set = new LiteModeSettingsActivity(); + presentFragment(set); + set.setExpanded(LiteMode.FLAGS_ANIMATED_STICKERS, true); + set.scrollToFlags(LiteMode.FLAG_ANIMATED_STICKERS_KEYBOARD); + }), + new SearchResult(903, LocaleController.getString(R.string.LiteOptionsAutoplayChat), null, LocaleController.getString(R.string.PowerUsage), LocaleController.getString(R.string.LiteOptionsStickers), R.drawable.msg2_battery, () -> { + LiteModeSettingsActivity set = new LiteModeSettingsActivity(); + presentFragment(set); + set.setExpanded(LiteMode.FLAGS_ANIMATED_STICKERS, true); + set.scrollToFlags(LiteMode.FLAG_ANIMATED_STICKERS_CHAT); + }), + new SearchResult(904, LocaleController.getString(R.string.LiteOptionsEmoji), LocaleController.getString(R.string.PowerUsage), R.drawable.msg2_battery, () -> { + LiteModeSettingsActivity set = new LiteModeSettingsActivity(); + presentFragment(set); + set.scrollToFlags(LiteMode.FLAGS_ANIMATED_EMOJI); + }), + new SearchResult(905, LocaleController.getString(R.string.LiteOptionsAutoplayKeyboard), null, LocaleController.getString(R.string.PowerUsage), LocaleController.getString(R.string.LiteOptionsEmoji), R.drawable.msg2_battery, () -> { + LiteModeSettingsActivity set = new LiteModeSettingsActivity(); + presentFragment(set); + set.setExpanded(LiteMode.FLAGS_ANIMATED_EMOJI, true); + set.scrollToFlags(LiteMode.FLAG_ANIMATED_EMOJI_KEYBOARD); + }), + new SearchResult(906, LocaleController.getString(R.string.LiteOptionsAutoplayReactions), null, LocaleController.getString(R.string.PowerUsage), LocaleController.getString(R.string.LiteOptionsEmoji), R.drawable.msg2_battery, () -> { + LiteModeSettingsActivity set = new LiteModeSettingsActivity(); + presentFragment(set); + set.setExpanded(LiteMode.FLAGS_ANIMATED_EMOJI, true); + set.scrollToFlags(LiteMode.FLAG_ANIMATED_EMOJI_REACTIONS); + }), + new SearchResult(907, LocaleController.getString(R.string.LiteOptionsAutoplayChat), null, LocaleController.getString(R.string.PowerUsage), LocaleController.getString(R.string.LiteOptionsEmoji), R.drawable.msg2_battery, () -> { + LiteModeSettingsActivity set = new LiteModeSettingsActivity(); + presentFragment(set); + set.setExpanded(LiteMode.FLAGS_ANIMATED_EMOJI, true); + set.scrollToFlags(LiteMode.FLAG_ANIMATED_EMOJI_CHAT); + }), + new SearchResult(908, LocaleController.getString(R.string.LiteOptionsChat), LocaleController.getString(R.string.PowerUsage), R.drawable.msg2_battery, () -> { + LiteModeSettingsActivity set = new LiteModeSettingsActivity(); + presentFragment(set); + set.scrollToFlags(LiteMode.FLAGS_CHAT); + }), + new SearchResult(909, LocaleController.getString(R.string.LiteOptionsBackground), null, LocaleController.getString(R.string.PowerUsage), LocaleController.getString(R.string.LiteOptionsChat), R.drawable.msg2_battery, () -> { + LiteModeSettingsActivity set = new LiteModeSettingsActivity(); + presentFragment(set); + set.setExpanded(LiteMode.FLAGS_CHAT, true); + set.scrollToFlags(LiteMode.FLAG_CHAT_BACKGROUND); + }), + new SearchResult(910, LocaleController.getString(R.string.LiteOptionsTopics), null, LocaleController.getString(R.string.PowerUsage), LocaleController.getString(R.string.LiteOptionsChat), R.drawable.msg2_battery, () -> { + LiteModeSettingsActivity set = new LiteModeSettingsActivity(); + presentFragment(set); + set.setExpanded(LiteMode.FLAGS_CHAT, true); + set.scrollToFlags(LiteMode.FLAG_CHAT_FORUM_TWOCOLUMN); + }), + new SearchResult(911, LocaleController.getString(R.string.LiteOptionsSpoiler), null, LocaleController.getString(R.string.PowerUsage), LocaleController.getString(R.string.LiteOptionsChat), R.drawable.msg2_battery, () -> { + LiteModeSettingsActivity set = new LiteModeSettingsActivity(); + presentFragment(set); + set.setExpanded(LiteMode.FLAGS_CHAT, true); + set.scrollToFlags(LiteMode.FLAG_CHAT_SPOILER); + }), + SharedConfig.getDevicePerformanceClass() >= SharedConfig.PERFORMANCE_CLASS_AVERAGE ? new SearchResult(326 /* for compatibility */, LocaleController.getString(R.string.LiteOptionsBlur), null, LocaleController.getString(R.string.PowerUsage), LocaleController.getString(R.string.LiteOptionsChat), R.drawable.msg2_battery, () -> { + LiteModeSettingsActivity set = new LiteModeSettingsActivity(); + presentFragment(set); + set.setExpanded(LiteMode.FLAGS_CHAT, true); + set.scrollToFlags(LiteMode.FLAG_CHAT_BLUR); + }) : null, + new SearchResult(912, LocaleController.getString(R.string.LiteOptionsScale), null, LocaleController.getString(R.string.PowerUsage), LocaleController.getString(R.string.LiteOptionsChat), R.drawable.msg2_battery, () -> { + LiteModeSettingsActivity set = new LiteModeSettingsActivity(); + presentFragment(set); + set.setExpanded(LiteMode.FLAGS_CHAT, true); + set.scrollToFlags(LiteMode.FLAG_CHAT_SCALE); + }), + new SearchResult(913, LocaleController.getString(R.string.LiteOptionsCalls), LocaleController.getString(R.string.PowerUsage), R.drawable.msg2_battery, () -> { + LiteModeSettingsActivity set = new LiteModeSettingsActivity(); + presentFragment(set); + set.scrollToFlags(LiteMode.FLAG_CALLS_ANIMATIONS); + }), + new SearchResult(214 /* for compatibility */, LocaleController.getString(R.string.LiteOptionsAutoplayVideo), LocaleController.getString(R.string.PowerUsage), R.drawable.msg2_battery, () -> { + LiteModeSettingsActivity set = new LiteModeSettingsActivity(); + presentFragment(set); + set.scrollToFlags(LiteMode.FLAG_AUTOPLAY_VIDEOS); + }), + new SearchResult(213 /* for compatibility */, LocaleController.getString(R.string.LiteOptionsAutoplayGifs), LocaleController.getString(R.string.PowerUsage), R.drawable.msg2_battery, () -> { + LiteModeSettingsActivity set = new LiteModeSettingsActivity(); + presentFragment(set); + set.scrollToFlags(LiteMode.FLAG_AUTOPLAY_GIFS); + }), + new SearchResult(914, LocaleController.getString(R.string.LiteSmoothTransitions), LocaleController.getString(R.string.PowerUsage), R.drawable.msg2_battery, () -> { + LiteModeSettingsActivity set = new LiteModeSettingsActivity(); + presentFragment(set); + set.scrollToType(LiteModeSettingsActivity.SWITCH_TYPE_SMOOTH_TRANSITIONS); + }), + new SearchResult(400, LocaleController.getString("Language", R.string.Language), R.drawable.msg2_language, () -> presentFragment(new LanguageSelectActivity())), new SearchResult(405, LocaleController.getString(R.string.ShowTranslateButton), LocaleController.getString(R.string.Language), R.drawable.msg2_language, () -> presentFragment(new LanguageSelectActivity())), MessagesController.getInstance(currentAccount).getTranslateController().isContextTranslateEnabled() ? new SearchResult(406, LocaleController.getString(R.string.DoNotTranslate), LocaleController.getString(R.string.Language), R.drawable.msg2_language, () -> presentFragment(new LanguageSelectActivity())) : null, @@ -10915,8 +11113,8 @@ public ArrayList getThemeDescriptions() { if (onlineTextView[1] != null) { final Object onlineTextViewTag = onlineTextView[1].getTag(); for (int i = 0; i < 2; i++) { - if (onlineTextViewTag instanceof String) { - onlineTextView[i + 1].setTextColor(getThemedColor((String) onlineTextViewTag)); + if (onlineTextViewTag instanceof Integer) { + onlineTextView[i + 1].setTextColor(getThemedColor((Integer) onlineTextViewTag)); } else { onlineTextView[i + 1].setTextColor(getThemedColor(Theme.key_avatar_subtitleInProfileBlue)); } @@ -10977,7 +11175,7 @@ public ArrayList getThemeDescriptions() { arrayList.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); arrayList.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteGreenText2)); - arrayList.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteRedText5)); + arrayList.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextCell.class}, new String[]{"textView"}, null, null, null, Theme.key_text_RedRegular)); arrayList.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueText2)); arrayList.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueButton)); arrayList.add(new ThemeDescription(listView, 0, new Class[]{TextCell.class}, new String[]{"valueTextView"}, null, null, null, Theme.key_windowBackgroundWhiteValueText)); @@ -11133,6 +11331,8 @@ private void onTextDetailCellImageClicked(View view) { } } + private boolean wentToAddContacts; + @Override public void onBecomeFullyVisible() { super.onBecomeFullyVisible(); @@ -11146,6 +11346,11 @@ public void onBecomeFullyVisible() { combinedDrawable.setIconSize(AndroidUtilities.dp(56), AndroidUtilities.dp(56)); writeButton.setBackground(combinedDrawable); } catch (Exception e) {} + + if (wentToAddContacts) { + updateListAnimated(false); + wentToAddContacts = false; + } } private boolean isQrNeedVisible() { @@ -11354,6 +11559,120 @@ private void checkPhotoDescriptionAlpha() { } } + } + + private void updateStoriesViewBounds(boolean animated) { + if (storyView == null || actionBar == null) { + return; + } + float atop = actionBar.getOccupyStatusBar() ? AndroidUtilities.statusBarHeight : 0; + float aleft = 0; + float aright = actionBar.getWidth(); + + if (actionBar.getBackButton() != null) { + aleft = Math.max(aleft, actionBar.getBackButton().getRight()); + } + if (actionBar.menu != null) { + for (int i = 0; i < actionBar.menu.getChildCount(); ++i) { + View child = actionBar.menu.getChildAt(i); + if (child.getAlpha() <= 0 || child.getVisibility() != View.VISIBLE) { + continue; + } + int left = actionBar.menu.getLeft() + (int) child.getX(); + if (left < aright) { + aright = AndroidUtilities.lerp(aright, left, child.getAlpha()); + } + } + } + storyView.setBounds(aleft, aright, atop + (actionBar.getHeight() - atop) / 2f, !animated); + } + + private class ClippedListView extends RecyclerListView implements StoriesListPlaceProvider.ClippedView { + public ClippedListView(Context context) { + super(context); + } + @Override + public void updateClip(int[] clip) { + clip[0] = actionBar.getMeasuredHeight(); + clip[1] = getMeasuredHeight() - getPaddingBottom(); + } + } + + private void listCodecs(String type, StringBuilder info) { + if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) { + return; + } + try { + final int allCodecsCount = MediaCodecList.getCodecCount(); + final ArrayList decoderIndexes = new ArrayList<>(); + final ArrayList encoderIndexes = new ArrayList<>(); + boolean first = true; + for (int i = 0; i < allCodecsCount; ++i) { + MediaCodecInfo codec = MediaCodecList.getCodecInfoAt(i); + if (codec == null) { + continue; + } + String[] types = codec.getSupportedTypes(); + if (types == null) { + continue; + } + boolean found = false; + for (int j = 0; j < types.length; ++j) { + if (types[j].equals(type)) { + found = true; + break; + } + } + if (found) { + (codec.isEncoder() ? encoderIndexes : decoderIndexes).add(i); + } + } + if (decoderIndexes.isEmpty() && encoderIndexes.isEmpty()) { + return; + } + info.append("\n").append(decoderIndexes.size()).append("+").append(encoderIndexes.size()).append(" ").append(type.substring(6)).append(" codecs:\n"); + for (int a = 0; a < decoderIndexes.size(); ++a) { + if (a > 0) { + info.append("\n"); + } + MediaCodecInfo codec = MediaCodecList.getCodecInfoAt(decoderIndexes.get(a)); + info.append("{d} ").append(codec.getName()).append(" ("); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { + if (codec.isHardwareAccelerated()) { + info.append("gpu"); // as Gpu + } + if (codec.isSoftwareOnly()) { + info.append("cpu"); // as Cpu + } + if (codec.isVendor()) { + info.append(", v"); // as Vendor + } + } + MediaCodecInfo.CodecCapabilities capabilities = codec.getCapabilitiesForType(type); + info.append("; mi=").append(capabilities.getMaxSupportedInstances()).append(")"); + } + for (int a = 0; a < encoderIndexes.size(); ++a) { + if (a > 0 || !decoderIndexes.isEmpty()) { + info.append("\n"); + } + MediaCodecInfo codec = MediaCodecList.getCodecInfoAt(encoderIndexes.get(a)); + info.append("{e} ").append(codec.getName()).append(" ("); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { + if (codec.isHardwareAccelerated()) { + info.append("gpu"); // as Gpu + } + if (codec.isSoftwareOnly()) { + info.append("cpu"); // as Cpu + } + if (codec.isVendor()) { + info.append(", v"); // as Vendor + } + } + MediaCodecInfo.CodecCapabilities capabilities = codec.getCapabilitiesForType(type); + info.append("; mi=").append(capabilities.getMaxSupportedInstances()).append(")"); + } + info.append("\n"); + } catch (Exception ignore) {} } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ProfileNotificationsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ProfileNotificationsActivity.java index 9ea76815f5..f4f6f27523 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ProfileNotificationsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ProfileNotificationsActivity.java @@ -101,6 +101,7 @@ public class ProfileNotificationsActivity extends BaseFragment implements Notifi private int popupEnabledRow; private int popupDisabledRow; private int popupInfoRow; + private int storiesRow; private int callsRow; private int ringtoneRow; private int callsVibrateRow; @@ -112,6 +113,8 @@ public class ProfileNotificationsActivity extends BaseFragment implements Notifi private int customResetShadowRow; private int rowCount; + private boolean isInTop5Peers; + private boolean needReset; private final static int done_button = 1; @@ -136,6 +139,17 @@ public ProfileNotificationsActivity(Bundle args, Theme.ResourcesProvider resourc @Override public boolean onFragmentCreate() { + if (DialogObject.isUserDialog(dialogId)) { + ArrayList topPeers = getMediaDataController().hints; + for (int i = 0; i < topPeers.size(); ++i) { + TLRPC.Peer peer = topPeers.get(i).peer; + if (peer instanceof TLRPC.TL_peerUser && peer.user_id == dialogId) { + isInTop5Peers = i < 5; + break; + } + } + } + rowCount = 0; if (addingException) { avatarRow = rowCount++; @@ -150,8 +164,12 @@ public boolean onFragmentCreate() { } else { enableRow = -1; } + storiesRow = -1; if (!DialogObject.isEncryptedDialog(dialogId)) { previewRow = rowCount++; + if (DialogObject.isUserDialog(dialogId)) { + storiesRow = rowCount++; + } } else { previewRow = -1; } @@ -381,7 +399,7 @@ public boolean supportsPredictiveItemAnimations() { showDialog(dialog); TextView button = (TextView) dialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } else if (position == soundRow) { Bundle bundle = new Bundle(); @@ -487,6 +505,17 @@ public boolean supportsPredictiveItemAnimations() { if (view != null) { ((RadioCell) view).setChecked(false, true); } + } else if (position == storiesRow) { + TextCheckCell checkCell = (TextCheckCell) view; + boolean value = !checkCell.isChecked(); + checkCell.setChecked(value); + SharedPreferences.Editor edit = MessagesController.getNotificationsSettings(currentAccount).edit(); + if (isInTop5Peers && value) { + edit.remove("stories_" + key); + } else { + edit.putBoolean("stories_" + key, value); + } + edit.apply();getNotificationsController().updateServerNotificationsSettings(dialogId, topicId); } }); @@ -738,7 +767,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { SharedPreferences preferences = MessagesController.getNotificationsSettings(currentAccount); if (position == customResetRow) { textCell.setText(LocaleController.getString(R.string.ResetCustomNotifications), false); - textCell.setTextColor(getThemedColor(Theme.key_dialogTextRed)); + textCell.setTextColor(getThemedColor(Theme.key_text_RedBold)); } else { textCell.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteBlackText)); if (position == soundRow) { @@ -813,22 +842,23 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } case VIEW_TYPE_INFO: { TextInfoPrivacyCell textCell = (TextInfoPrivacyCell) holder.itemView; + textCell.setFixedSize(0); if (position == popupInfoRow) { textCell.setText(LocaleController.getString("ProfilePopupNotificationInfo", R.string.ProfilePopupNotificationInfo)); - textCell.setBackground(Theme.getThemedDrawable(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + textCell.setBackground(Theme.getThemedDrawableByKey(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); } else if (position == ledInfoRow) { textCell.setText(LocaleController.getString("NotificationsLedInfo", R.string.NotificationsLedInfo)); - textCell.setBackground(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + textCell.setBackground(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } else if (position == priorityInfoRow) { if (priorityRow == -1) { textCell.setText(""); } else { textCell.setText(LocaleController.getString("PriorityInfo", R.string.PriorityInfo)); } - textCell.setBackground(Theme.getThemedDrawable(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + textCell.setBackground(Theme.getThemedDrawableByKey(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); } else if (position == ringtoneInfoRow) { textCell.setText(LocaleController.getString("VoipRingtoneInfo", R.string.VoipRingtoneInfo)); - textCell.setBackground(Theme.getThemedDrawable(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + textCell.setBackground(Theme.getThemedDrawableByKey(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); } break; } @@ -896,6 +926,10 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } else if (position == previewRow) { String key = NotificationsController.getSharedPrefKey(dialogId, topicId); checkCell.setTextAndCheck(LocaleController.getString("MessagePreview", R.string.MessagePreview), preferences.getBoolean("content_preview_" + key, true), true); + } else if (position == storiesRow) { + String key = NotificationsController.getSharedPrefKey(dialogId, topicId); + boolean value = preferences.getBoolean("stories_" + key, isInTop5Peers || preferences.contains("EnableAllStories") && preferences.getBoolean("EnableAllStories", true)); + checkCell.setTextAndCheck(LocaleController.getString("StoriesSoundEnabled", R.string.StoriesSoundEnabled), value, true); } break; } @@ -943,6 +977,8 @@ public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) { TextCheckCell checkCell = (TextCheckCell) holder.itemView; if (holder.getAdapterPosition() == previewRow) { checkCell.setEnabled(notificationsEnabled, null); + } else if (holder.getAdapterPosition() == storiesRow) { + checkCell.setEnabled(notificationsEnabled, null); } else { checkCell.setEnabled(true, null); } @@ -966,18 +1002,13 @@ public int getItemViewType(int position) { return VIEW_TYPE_USER; } else if (position == avatarSectionRow || position == customResetShadowRow) { return VIEW_TYPE_SHADOW; - } else if (position == enableRow || position == previewRow) { + } else if (position == enableRow || position == previewRow || position == storiesRow) { return VIEW_TYPE_TEXT_CHECK; } return VIEW_TYPE_HEADER; } } - @Override - public int getNavigationBarColor() { - return getThemedColor(Theme.key_windowBackgroundGray); - } - @Override public ArrayList getThemeDescriptions() { ArrayList themeDescriptions = new ArrayList<>(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ProxyListActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ProxyListActivity.java index 847191fac3..a2d35bf3ef 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ProxyListActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ProxyListActivity.java @@ -289,7 +289,7 @@ public void onClick(View widget) { } public void updateStatus() { - String colorKey; + int colorKey; if (SharedConfig.currentProxy == currentInfo && useProxySettings) { if (currentConnectionState == ConnectionsManager.ConnectionStateConnected || currentConnectionState == ConnectionsManager.ConnectionStateUpdating) { colorKey = Theme.key_windowBackgroundWhiteBlueText6; @@ -318,7 +318,7 @@ public void updateStatus() { colorKey = Theme.key_windowBackgroundWhiteGreenText; } else { valueTextView.setText(LocaleController.getString("Unavailable", R.string.Unavailable)); - colorKey = Theme.key_windowBackgroundWhiteRedText4; + colorKey = Theme.key_text_RedRegular; } } color = Theme.getColor(colorKey); @@ -1472,9 +1472,9 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { switch (holder.getItemViewType()) { case VIEW_TYPE_SHADOW: { if (position == proxyShadowRow && callsRow == -1) { - holder.itemView.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + holder.itemView.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } else { - holder.itemView.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + holder.itemView.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); } break; } @@ -1484,7 +1484,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (position == proxyAddRow) { textCell.setText(LocaleController.getString("AddProxy", R.string.AddProxy), deleteAllRow != -1); } else if (position == deleteAllRow) { - textCell.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText4)); + textCell.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); textCell.setText(LocaleController.getString(R.string.DeleteAllProxies), false); } break; @@ -1513,10 +1513,10 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { TextInfoPrivacyCell cell = (TextInfoPrivacyCell) holder.itemView; if (position == callsDetailRow) { cell.setText(LocaleController.getString("UseProxyForCallsInfo", R.string.UseProxyForCallsInfo)); - cell.setBackground(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + cell.setBackground(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } else if (position == rotationTimeoutInfoRow) { cell.setText(LocaleController.getString(R.string.ProxyRotationTimeoutInfo)); - cell.setBackground(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + cell.setBackground(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } break; } @@ -1621,7 +1621,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType break; case VIEW_TYPE_INFO: view = new TextInfoPrivacyCell(mContext); - view.setBackground(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + view.setBackground(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); break; case VIEW_TYPE_SLIDE_CHOOSER: view = new SlideChooseView(mContext); @@ -1714,7 +1714,7 @@ public ArrayList getThemeDescriptions() { themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_TEXTCOLOR | ThemeDescription.FLAG_CHECKTAG | ThemeDescription.FLAG_IMAGECOLOR, new Class[]{TextDetailProxyCell.class}, new String[]{"valueTextView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueText6)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_TEXTCOLOR | ThemeDescription.FLAG_CHECKTAG | ThemeDescription.FLAG_IMAGECOLOR, new Class[]{TextDetailProxyCell.class}, new String[]{"valueTextView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText2)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_TEXTCOLOR | ThemeDescription.FLAG_CHECKTAG | ThemeDescription.FLAG_IMAGECOLOR, new Class[]{TextDetailProxyCell.class}, new String[]{"valueTextView"}, null, null, null, Theme.key_windowBackgroundWhiteGreenText)); - themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_TEXTCOLOR | ThemeDescription.FLAG_CHECKTAG | ThemeDescription.FLAG_IMAGECOLOR, new Class[]{TextDetailProxyCell.class}, new String[]{"valueTextView"}, null, null, null, Theme.key_windowBackgroundWhiteRedText4)); + themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_TEXTCOLOR | ThemeDescription.FLAG_CHECKTAG | ThemeDescription.FLAG_IMAGECOLOR, new Class[]{TextDetailProxyCell.class}, new String[]{"valueTextView"}, null, null, null, Theme.key_text_RedRegular)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_IMAGECOLOR, new Class[]{TextDetailProxyCell.class}, new String[]{"checkImageView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText3)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{HeaderCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueHeader)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ProxySettingsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ProxySettingsActivity.java index 971562ca19..0ab2fe2e6f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ProxySettingsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ProxySettingsActivity.java @@ -301,7 +301,7 @@ public void onItemClick(int id) { inputFields[a].setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); inputFields[a].setHeaderHintColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlueHeader)); inputFields[a].setTransformHintToHeader(true); - inputFields[a].setLineColors(Theme.getColor(Theme.key_windowBackgroundWhiteInputField), Theme.getColor(Theme.key_windowBackgroundWhiteInputFieldActivated), Theme.getColor(Theme.key_windowBackgroundWhiteRedText3)); + inputFields[a].setLineColors(Theme.getColor(Theme.key_windowBackgroundWhiteInputField), Theme.getColor(Theme.key_windowBackgroundWhiteInputFieldActivated), Theme.getColor(Theme.key_text_RedRegular)); if (a == FIELD_IP) { inputFields[a].setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType.TYPE_TEXT_VARIATION_URI); @@ -427,7 +427,7 @@ public void afterTextChanged(Editable s) { for (int i = 0; i < 2; i++) { bottomCells[i] = new TextInfoPrivacyCell(context); - bottomCells[i].setBackground(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + bottomCells[i].setBackground(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); if (i == 0) { bottomCells[i].setText(LocaleController.getString("UseProxyInfo", R.string.UseProxyInfo)); } else { @@ -478,12 +478,12 @@ public void afterTextChanged(Editable s) { linearLayout2.addView(pasteCell, 0, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); pasteCell.setVisibility(View.GONE); sectionCell[2] = new ShadowSectionCell(fragmentView.getContext()); - sectionCell[2].setBackground(Theme.getThemedDrawable(fragmentView.getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + sectionCell[2].setBackground(Theme.getThemedDrawableByKey(fragmentView.getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); linearLayout2.addView(sectionCell[2], 1, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); sectionCell[2].setVisibility(View.GONE); sectionCell[1] = new ShadowSectionCell(context); - sectionCell[1].setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + sectionCell[1].setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); linearLayout2.addView(sectionCell[1], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); clipboardManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); @@ -711,7 +711,7 @@ public ArrayList getThemeDescriptions() { for (int i = 0; i < inputFields.length; i++) { inputFields[i].setLineColors(Theme.getColor(Theme.key_windowBackgroundWhiteInputField), Theme.getColor(Theme.key_windowBackgroundWhiteInputFieldActivated), - Theme.getColor(Theme.key_windowBackgroundWhiteRedText3)); + Theme.getColor(Theme.key_text_RedRegular)); } } }; @@ -739,7 +739,7 @@ public ArrayList getThemeDescriptions() { arrayList.add(new ThemeDescription(inputFields[a], ThemeDescription.FLAG_CURSORCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); arrayList.add(new ThemeDescription(null, 0, null, null, null, delegate, Theme.key_windowBackgroundWhiteInputField)); arrayList.add(new ThemeDescription(null, 0, null, null, null, delegate, Theme.key_windowBackgroundWhiteInputFieldActivated)); - arrayList.add(new ThemeDescription(null, 0, null, null, null, delegate, Theme.key_windowBackgroundWhiteRedText3)); + arrayList.add(new ThemeDescription(null, 0, null, null, null, delegate, Theme.key_text_RedRegular)); } } else { arrayList.add(new ThemeDescription(null, ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/QrActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/QrActivity.java index 9e1dfc26ec..ba5e90281b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/QrActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/QrActivity.java @@ -32,6 +32,7 @@ import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; +import android.util.SparseIntArray; import android.util.TypedValue; import android.view.Gravity; import android.view.HapticFeedbackConstants; @@ -489,11 +490,6 @@ private void restoreScreenSettings() { } } - @Override - public int getNavigationBarColor() { - return getThemedColor(Theme.key_windowBackgroundGray); - } - @Override public Theme.ResourcesProvider getResourceProvider() { return resourcesProvider; @@ -782,15 +778,15 @@ public ArrayList getThemeDescriptions() { private class ThemeResourcesProvider implements Theme.ResourcesProvider { - private HashMap colors; + private SparseIntArray colors; void initColors(EmojiThemes theme, boolean isDark) { colors = theme.createColors(currentAccount, isDark ? 1 : 0); } @Override - public Integer getColor(String key) { - return colors != null ? colors.get(key) : null; + public int getColor(int key) { + return colors != null ? colors.get(key) : Theme.getColor(key); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/QuickRepliesSettingsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/QuickRepliesSettingsActivity.java index 11f6d0f9db..6b73b38aff 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/QuickRepliesSettingsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/QuickRepliesSettingsActivity.java @@ -47,7 +47,6 @@ public class QuickRepliesSettingsActivity extends BaseFragment { private int explanationRow; private int rowCount; - private EditTextSettingsCell[] textCells = new EditTextSettingsCell[4]; @Override @@ -139,7 +138,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { switch (holder.getItemViewType()) { case 0: { TextInfoPrivacyCell cell = (TextInfoPrivacyCell) holder.itemView; - cell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + cell.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); cell.setText(LocaleController.getString("VoipQuickRepliesExplain", R.string.VoipQuickRepliesExplain)); break; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ReactionsDoubleTapManageActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ReactionsDoubleTapManageActivity.java index aeb66f47c4..e8d03d4359 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ReactionsDoubleTapManageActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ReactionsDoubleTapManageActivity.java @@ -107,7 +107,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int case 2: TextInfoPrivacyCell cell = new TextInfoPrivacyCell(context); cell.setText(LocaleController.getString("DoubleTapPreviewRational", R.string.DoubleTapPreviewRational)); - cell.setBackground(Theme.getThemedDrawable(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + cell.setBackground(Theme.getThemedDrawableByKey(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); view = cell; break; case 3: @@ -125,7 +125,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { ); } }; - view.setBackground(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + view.setBackground(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); break; default: case 1: { @@ -372,7 +372,7 @@ public ArrayList getThemeDescriptions() { Theme.key_listSelector, Theme.key_windowBackgroundGray, Theme.key_windowBackgroundWhiteGrayText4, - Theme.key_windowBackgroundWhiteRedText4, + Theme.key_text_RedRegular, Theme.key_windowBackgroundChecked, Theme.key_windowBackgroundCheckText, Theme.key_switchTrackBlue, diff --git a/TMessagesProj/src/main/java/org/telegram/ui/RecyclerListViewScroller.java b/TMessagesProj/src/main/java/org/telegram/ui/RecyclerListViewScroller.java new file mode 100644 index 0000000000..cad97efbff --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/RecyclerListViewScroller.java @@ -0,0 +1,56 @@ +package org.telegram.ui; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; + +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.RecyclerListView; + +public class RecyclerListViewScroller { + + ValueAnimator valueAnimator; + final RecyclerListView recyclerListView; + + int lastScrolled; + + public RecyclerListViewScroller(RecyclerListView recyclerListView) { + this.recyclerListView = recyclerListView; + } + + public void smoothScrollBy(int dy) { + if (valueAnimator != null) { + valueAnimator.removeAllListeners(); + valueAnimator.cancel(); + } + lastScrolled = 0; + valueAnimator = ValueAnimator.ofFloat(0, 1f); + valueAnimator.addUpdateListener(animation -> { + int currentScroll = (int) (dy * (float) animation.getAnimatedValue()); + recyclerListView.scrollBy(0, currentScroll - lastScrolled); + lastScrolled = currentScroll; + }); + valueAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + recyclerListView.scrollBy(0, dy - lastScrolled); + valueAnimator = null; + } + }); + valueAnimator.setDuration(200); + valueAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); + valueAnimator.start(); + } + + public void cancel() { + if (valueAnimator != null) { + valueAnimator.removeAllListeners(); + valueAnimator.cancel(); + valueAnimator = null; + } + } + + public boolean isRunning() { + return valueAnimator != null; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/RestrictedLanguagesSelectActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/RestrictedLanguagesSelectActivity.java index 958b549f84..ec785a26e7 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/RestrictedLanguagesSelectActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/RestrictedLanguagesSelectActivity.java @@ -23,9 +23,10 @@ import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import com.google.common.collect.Sets; + import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ApplicationLoader; -import org.telegram.messenger.BotWebViewVibrationEffect; import org.telegram.messenger.FileLog; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MessagesController; @@ -50,14 +51,10 @@ import org.telegram.ui.Components.RecyclerListView; import org.telegram.ui.Components.TranslateAlert2; -import java.io.BufferedReader; -import java.io.InputStreamReader; import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Timer; +import java.util.Set; public class RestrictedLanguagesSelectActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { @@ -66,27 +63,51 @@ public class RestrictedLanguagesSelectActivity extends BaseFragment implements N private ListAdapter searchListViewAdapter; private EmptyTextProgressView emptyView; - private boolean searchWas; - private boolean searching; - - private Timer searchTimer; private int separatorRow = -1; private ArrayList searchResult; private ArrayList allLanguages; - private SharedPreferences preferences; private HashSet firstSelectedLanguages; private HashSet selectedLanguages; + private static boolean gotRestrictedLanguages; + private static HashSet restrictedLanguages; + public static HashSet getRestrictedLanguages() { - String currentLangCode = LocaleController.getInstance().getCurrentLocaleInfo().pluralLangCode; - String[] onlyCurrentLang = new String[] { currentLangCode }; - return new HashSet<>(MessagesController.getGlobalMainSettings().getStringSet("translate_button_restricted_languages", new HashSet(Arrays.asList(onlyCurrentLang)))); + if (!gotRestrictedLanguages) { + Set set = MessagesController.getGlobalMainSettings().getStringSet("translate_button_restricted_languages", null); + restrictedLanguages = set == null ? null : new HashSet<>(set); + gotRestrictedLanguages = true; + } + if (restrictedLanguages == null) { + restrictedLanguages = Sets.newHashSet(LocaleController.getInstance().getCurrentLocaleInfo().pluralLangCode); + } + return restrictedLanguages; + } + + public static void invalidateRestrictedLanguages() { + gotRestrictedLanguages = false; + } + + public static void updateRestrictedLanguages(HashSet value, Boolean changed) { + restrictedLanguages = value; + gotRestrictedLanguages = true; + SharedPreferences.Editor edit = MessagesController.getGlobalMainSettings().edit(); + if (value == null) { + edit.remove("translate_button_restricted_languages"); + } else { + edit.putStringSet("translate_button_restricted_languages", value); + } + if (changed == null) { + edit.remove("translate_button_restricted_languages_changed"); + } else if (changed) { + edit.putBoolean("translate_button_restricted_languages_changed", true); + } + edit.apply(); } @Override public boolean onFragmentCreate() { - preferences = MessagesController.getGlobalMainSettings(); firstSelectedLanguages = getRestrictedLanguages(); selectedLanguages = getRestrictedLanguages(); @@ -139,9 +160,9 @@ public static boolean toggleLanguage(String language, boolean doNotTranslate) { selectedLanguages.add(language); } if (selectedLanguages.size() == 1 && selectedLanguages.contains(currentLocaleInfo.pluralLangCode)) { - MessagesController.getGlobalMainSettings().edit().remove("translate_button_restricted_languages").commit(); + updateRestrictedLanguages(null, false); } else { - MessagesController.getGlobalMainSettings().edit().putStringSet("translate_button_restricted_languages", selectedLanguages).commit(); + updateRestrictedLanguages(selectedLanguages, false); } TranslateController.invalidateSuggestedLanguageCodes(); return true; @@ -149,9 +170,6 @@ public static boolean toggleLanguage(String language, boolean doNotTranslate) { @Override public View createView(Context context) { - searching = false; - searchWas = false; - actionBar.setBackButtonImage(R.drawable.ic_ab_back); actionBar.setAllowOverlayTitle(true); actionBar.setTitle(LocaleController.getString("DoNotTranslate", R.string.DoNotTranslate)); @@ -169,14 +187,11 @@ public void onItemClick(int id) { ActionBarMenuItem item = menu.addItem(0, R.drawable.ic_ab_search).setIsSearchField(true).setActionBarMenuItemSearchListener(new ActionBarMenuItem.ActionBarMenuItemSearchListener() { @Override public void onSearchExpand() { - searching = true; } @Override public void onSearchCollapse() { search(null); - searching = false; - searchWas = false; if (listView != null) { emptyView.setVisibility(View.GONE); listView.setAdapter(listAdapter); @@ -188,13 +203,10 @@ public void onTextChanged(EditText editText) { String text = editText.getText().toString(); search(text); if (text.length() != 0) { - searchWas = true; if (listView != null) { listView.setAdapter(searchListViewAdapter); } } else { - searching = false; - searchWas = false; if (listView != null) { emptyView.setVisibility(View.GONE); listView.setAdapter(listAdapter); @@ -256,9 +268,9 @@ public void onTextChanged(EditText editText) { selectedLanguages.add(langCode); } if (selectedLanguages.size() == 1 && selectedLanguages.contains(currentLocaleInfo.pluralLangCode)) { - preferences.edit().remove("translate_button_restricted_languages").remove("translate_button_restricted_languages_changed").apply(); + updateRestrictedLanguages(null, null); } else { - preferences.edit().putStringSet("translate_button_restricted_languages", selectedLanguages).putBoolean("translate_button_restricted_languages_changed", true).apply(); + updateRestrictedLanguages(selectedLanguages, true); } if (search) { @@ -358,13 +370,6 @@ public void search(final String query) { if (query == null) { searchResult = null; } else { - try { - if (searchTimer != null) { - searchTimer.cancel(); - } - } catch (Exception e) { - FileLog.e(e); - } processSearch(query); } } @@ -471,7 +476,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } case 1: { ShadowSectionCell sectionCell = (ShadowSectionCell) holder.itemView; - sectionCell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + sectionCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); break; } case 2: { @@ -523,6 +528,7 @@ public ArrayList getThemeDescriptions() { } public static void cleanup() { + invalidateRestrictedLanguages(); MessagesController.getGlobalMainSettings().edit() .remove("translate_button_restricted_languages_changed") .remove("translate_button_restricted_languages_version") @@ -548,6 +554,7 @@ public static void checkRestrictedLanguages(boolean accountsChanged) { edit.putStringSet("translate_button_restricted_languages", languages); } edit.putInt("translate_button_restricted_languages_version", LAST_DO_NOT_TRANSLATE_VERSION).apply(); + invalidateRestrictedLanguages(); for (int i : SharedConfig.activeAccounts) { final int account = i; @@ -566,29 +573,6 @@ public static void getExtendedDoNotTranslate(Utilities.Callback> final HashSet result = new HashSet<>(); - final HashMap countries = new HashMap<>(); -// final HashMap languages = new HashMap<>(); - final HashMap uniquePhoneCodes = new HashMap<>(); - -// final Utilities.Callback pushCountry = countryCode -> { -// if (countryCode == null) { -// return; -// } -// String[] countryLanguages = languages.get(countryCode.toUpperCase()); -// if (countryLanguages == null) { -// return; -// } -// for (int j = 1; j < Math.min(2, countryLanguages.length); ++j) { -// String language = countryLanguages[j]; -// if (language.contains("-")) { -// language = language.split("-")[0]; -// } -// if (TranslateAlert2.languageName(language) != null) { -// result.add(language); -// } -// } -// }; - Utilities.doCallbacks( next -> { try { @@ -612,89 +596,6 @@ public static void getExtendedDoNotTranslate(Utilities.Callback> } next.run(); }, - next -> { - try { - BufferedReader reader = new BufferedReader(new InputStreamReader(ApplicationLoader.applicationContext.getResources().getAssets().open("countries.txt"))); - ArrayList multipleCodes = new ArrayList<>(); - String line; - while ((line = reader.readLine()) != null) { - String[] args = line.split(";"); - if (args.length >= 3) { - countries.put(args[2], args[1]); - if (uniquePhoneCodes.containsKey(args[0]) && !"7".equals(args[0])) { - multipleCodes.add(args[0]); - uniquePhoneCodes.remove(args[0]); - } else if (!multipleCodes.contains(args[0])) { - uniquePhoneCodes.put(args[0], args[1]); - } - } - } - reader.close(); - -// reader = new BufferedReader(new InputStreamReader(ApplicationLoader.applicationContext.getResources().getAssets().open("languages.txt"))); -// while ((line = reader.readLine()) != null) { -// String[] args = line.split(","); -// if (args.length >= 2) { -// languages.put(args[0], args); -// } -// } -// reader.close(); - } catch (Exception e) { - FileLog.e(e); - } - next.run(); - }, -// next -> { -// ArrayList> getAuthorizationsCallbacks = new ArrayList<>(); -// for (int i = 0; i < UserConfig.MAX_ACCOUNT_COUNT; ++i) { -// final int account = i; -// if (UserConfig.getInstance(account).getClientUserId() != 0 && !ConnectionsManager.getInstance(account).isTestBackend()) { -// getAuthorizationsCallbacks.add(nextInternal -> { -// try { -// ConnectionsManager.getInstance(account).sendRequest(new TLRPC.TL_account_getAuthorizations(), (response, error) -> AndroidUtilities.runOnUIThread(() -> { -// if (error == null) { -// TLRPC.TL_account_authorizations res = (TLRPC.TL_account_authorizations) response; -// if (!res.authorizations.isEmpty()) { -// TLRPC.TL_authorization auth = res.authorizations.get(0); -// String[] separated = auth.country.split(", "); -// if (separated.length > 0) { -// pushCountry.run(countries.get(separated[separated.length - 1])); -// } -// } -// } -// nextInternal.run(); -// })); -// } catch (Exception e2) { -// FileLog.e(e2); -// nextInternal.run(); -// } -// }); -// } -// } -// getAuthorizationsCallbacks.add(n -> next.run()); -// Utilities.doCallbacks(getAuthorizationsCallbacks.toArray(new Utilities.Callback[0])); -// }, -// next -> { -// for (int i = 0; i < UserConfig.MAX_ACCOUNT_COUNT; ++i) { -// final int account = i; -// try { -// TLRPC.User user = UserConfig.getInstance(account).getCurrentUser(); -// if (user != null && user.phone != null) { -// for (int j = 4; j > 0; j--) { -// String code = user.phone.substring(0, j); -// String countryCode = uniquePhoneCodes.get(code); -// if (countryCode != null) { -// pushCountry.run(countryCode); -// break; -// } -// } -// } -// } catch (Exception e3) { -// FileLog.e(e3); -// } -// } -// next.run(); -// }, next -> { try { InputMethodManager imm = (InputMethodManager) ApplicationLoader.applicationContext.getSystemService(Context.INPUT_METHOD_SERVICE); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/RightSlidingDialogContainer.java b/TMessagesProj/src/main/java/org/telegram/ui/RightSlidingDialogContainer.java index 064c875312..55b9cfa63d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/RightSlidingDialogContainer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/RightSlidingDialogContainer.java @@ -19,6 +19,7 @@ import androidx.dynamicanimation.animation.SpringForce; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.SharedConfig; import org.telegram.messenger.UserConfig; @@ -39,7 +40,7 @@ public abstract class RightSlidingDialogContainer extends FrameLayout { float openedProgress = 0; boolean isOpenned; ValueAnimator openAnimator; - private int animationIndex = -1; + private AnimationNotificationsLocker notificationsLocker = new AnimationNotificationsLocker(); private int currentAccount = UserConfig.selectedAccount; public static long fragmentDialogId; boolean isPaused; @@ -90,10 +91,10 @@ public void presentFragment(INavigationLayout navigationLayout, BaseFragment fra fragment.onTransitionAnimationEnd(true, false); openedProgress = 1f; updateOpenAnimationProgress(); - openAnimationFinished(); + openAnimationFinished(false); return; } - animationIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(animationIndex, null); + notificationsLocker.lock(); openAnimator = ValueAnimator.ofFloat(0, 1f); openedProgress = 0; openAnimationStarted(true); @@ -110,11 +111,11 @@ public void onAnimationEnd(Animator animation) { return; } openAnimator = null; - NotificationCenter.getInstance(currentAccount).onAnimationFinish(animationIndex); + notificationsLocker.unlock(); fragment.onTransitionAnimationEnd(true, false); openedProgress = 1f; updateOpenAnimationProgress(); - openAnimationFinished(); + openAnimationFinished(false); } }); openAnimator.setDuration(250); @@ -144,7 +145,7 @@ private void animateReplace(BaseFragment oldFragment) { oldFragment.onFragmentDestroy(); removeView(oldFragment.getFragmentView()); removeView(oldFragment.getActionBar()); - NotificationCenter.getInstance(currentAccount).onAnimationFinish(animationIndex); + notificationsLocker.unlock(); return; } if (replaceAnimation != null) { @@ -153,7 +154,7 @@ private void animateReplace(BaseFragment oldFragment) { currentFragment.onTransitionAnimationStart(true, false); replacingFragment = oldFragment; replaceAnimationInProgress = true; - animationIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(animationIndex, null); + notificationsLocker.lock(); replaceAnimation = new SpringAnimation(new FloatValueHolder(0f)); replaceAnimation.setSpring(new SpringForce(1000f) .setStiffness(400f) @@ -177,7 +178,7 @@ private void animateReplace(BaseFragment oldFragment) { oldFragment.onFragmentDestroy(); removeView(oldFragment.getFragmentView()); removeView(oldFragment.getActionBar()); - NotificationCenter.getInstance(currentAccount).onAnimationFinish(animationIndex); + notificationsLocker.unlock(); }); replaceAnimation.start(); } @@ -277,11 +278,11 @@ public void finishPreviewInernal() { NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.needCheckSystemBarColors); } - openAnimationFinished(); + openAnimationFinished(false); return; } - animationIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(animationIndex, null); + notificationsLocker.lock(); openAnimator = ValueAnimator.ofFloat(openedProgress, 0); openAnimator.addUpdateListener(animation -> { openedProgress = (float) animation.getAnimatedValue(); @@ -296,7 +297,7 @@ public void onAnimationEnd(Animator animation) { openAnimator = null; openedProgress = 0; updateOpenAnimationProgress(); - NotificationCenter.getInstance(currentAccount).onAnimationFinish(animationIndex); + notificationsLocker.unlock(); if (currentFragment != null) { currentFragment.onPause(); currentFragment.onFragmentDestroy(); @@ -305,7 +306,7 @@ public void onAnimationEnd(Animator animation) { NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.needCheckSystemBarColors); } - openAnimationFinished(); + openAnimationFinished(false); } }); @@ -402,7 +403,7 @@ public void onAnimationEnd(Animator animation) { return; } openAnimator = null; - openAnimationFinished(); + openAnimationFinished(true); } }); openAnimator.setDuration(250); @@ -448,7 +449,7 @@ public void setCurrentTop(int top) { } } - public void openAnimationFinished() { + public void openAnimationFinished(boolean backward) { } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SaveToGallerySettingsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/SaveToGallerySettingsActivity.java index 3bea750482..72c901891e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SaveToGallerySettingsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SaveToGallerySettingsActivity.java @@ -215,7 +215,7 @@ public void onItemClick(int id) { ActionBarPopupWindow.ActionBarPopupWindowLayout actionBarPopupWindowLayout = new ActionBarPopupWindow.ActionBarPopupWindowLayout(getContext()); ActionBarMenuSubItem edit = ActionBarMenuItem.addItem(actionBarPopupWindowLayout, R.drawable.msg_customize, LocaleController.getString("EditException", R.string.EditException), false, null); ActionBarMenuSubItem delete = ActionBarMenuItem.addItem(actionBarPopupWindowLayout, R.drawable.msg_delete, LocaleController.getString("DeleteException", R.string.DeleteException), false, null); - delete.setColors(Theme.getColor(Theme.key_windowBackgroundWhiteRedText), Theme.getColor(Theme.key_windowBackgroundWhiteRedText)); + delete.setColors(Theme.getColor(Theme.key_text_RedRegular), Theme.getColor(Theme.key_text_RedRegular)); ActionBarPopupWindow popupWindow = AlertsCreator.createSimplePopup(SaveToGallerySettingsActivity.this, actionBarPopupWindowLayout, view, x, y); actionBarPopupWindowLayout.setParentWindow(popupWindow); @@ -245,7 +245,7 @@ public void onItemClick(int id) { // ((ViewGroup.MarginLayoutParams)recyclerListView.getLayoutParams()).bottomMargin = AndroidUtilities.dp() FrameLayout button = new FrameLayout(getContext()); - button.setBackground(Theme.AdaptiveRipple.filledRect(Theme.key_featuredStickers_addButton, 8)); + button.setBackground(Theme.AdaptiveRipple.filledRectByKey(Theme.key_featuredStickers_addButton, 8)); TextView textView = new TextView(getContext()); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); @@ -373,7 +373,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int case VIEW_TYPE_DELETE_ALL: textCell = new TextCell(parent.getContext()); textCell.setText(LocaleController.getString("NotificationsDeleteAllException", R.string.NotificationsDeleteAllException), false); - textCell.setColors(null, Theme.key_windowBackgroundWhiteRedText5); + textCell.setColors(-1, Theme.key_text_RedRegular); view = textCell; view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); break; @@ -399,7 +399,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int SelectableAnimatedTextView lowerTextView = new SelectableAnimatedTextView(getContext()); lowerTextView.setTextSize(AndroidUtilities.dp(13)); - lowerTextView.setText(AndroidUtilities.formatFileSize(1024 * 512, true)); + lowerTextView.setText(AndroidUtilities.formatFileSize(1024 * 512, true, false)); textContainer.addView(lowerTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.BOTTOM)); SelectableAnimatedTextView midTextView = new SelectableAnimatedTextView(getContext()); @@ -409,7 +409,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int SelectableAnimatedTextView topTextView = new SelectableAnimatedTextView(getContext()); topTextView.setTextSize(AndroidUtilities.dp(13)); - topTextView.setText(AndroidUtilities.formatFileSize(SaveToGallerySettingsHelper.MAX_VIDEO_LIMIT, true)); + topTextView.setText(AndroidUtilities.formatFileSize(SaveToGallerySettingsHelper.MAX_VIDEO_LIMIT, true, false)); textContainer.addView(topTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.RIGHT | Gravity.BOTTOM)); @@ -450,7 +450,7 @@ public void onSeekBarDrag(boolean stop, float progress) { } else { midTextView.setText( LocaleController.formatString("UpToFileSize", R.string.UpToFileSize, - AndroidUtilities.formatFileSize(value, true) + AndroidUtilities.formatFileSize(value, true, false) ), false); lowerTextView.setSelectedInternal(false, animated); midTextView.setSelectedInternal(true, animated); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SecretMediaViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/SecretMediaViewer.java index 5c4e57a0c4..fefc794ebe 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SecretMediaViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SecretMediaViewer.java @@ -828,7 +828,7 @@ public void openMedia(MessageObject messageObject, PhotoViewer.PhotoViewerProvid long destroyTime = (long) messageObject.messageOwner.destroyTime * 1000; long currentTime = System.currentTimeMillis() + ConnectionsManager.getInstance(currentAccount).getTimeDifference() * 1000L; long timeToDestroy = destroyTime - currentTime; - long duration = messageObject.getDuration() * 1000L; + long duration = (long) (messageObject.getDuration() * 1000L); if (duration > timeToDestroy) { secretDeleteTimer.setDestroyTime(-1, -1, true); } else { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SelectAnimatedEmojiDialog.java b/TMessagesProj/src/main/java/org/telegram/ui/SelectAnimatedEmojiDialog.java index 82de7c0c5f..eb15cc031d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SelectAnimatedEmojiDialog.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SelectAnimatedEmojiDialog.java @@ -13,12 +13,15 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.ColorFilter; +import android.graphics.LinearGradient; import android.graphics.Outline; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; +import android.graphics.PorterDuffXfermode; import android.graphics.Rect; +import android.graphics.Shader; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Parcelable; @@ -26,7 +29,6 @@ import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; -import android.util.Log; import android.util.LongSparseArray; import android.util.SparseArray; import android.util.SparseIntArray; @@ -61,6 +63,7 @@ import androidx.recyclerview.widget.RecyclerView; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.DocumentObject; import org.telegram.messenger.Emoji; @@ -107,6 +110,7 @@ import org.telegram.ui.Components.Premium.PremiumFeatureBottomSheet; import org.telegram.ui.Components.Premium.PremiumLockIconView; import org.telegram.ui.Components.RLottieImageView; +import org.telegram.ui.Components.Reactions.HwEmojis; import org.telegram.ui.Components.Reactions.ReactionsEffectOverlay; import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble; import org.telegram.ui.Components.Reactions.ReactionsUtils; @@ -169,6 +173,7 @@ public class SelectAnimatedEmojiDialog extends FrameLayout implements Notificati public boolean forUser; private ArrayList stickerSets = new ArrayList<>(); private boolean enterAnimationInProgress; + private BackgroundDelegate backgroundDelegate; public void putAnimatedEmojiToCache(AnimatedEmojiDrawable animatedEmojiDrawable) { emojiGridView.animatedEmojiDrawables.put(animatedEmojiDrawable.getDocumentId(), animatedEmojiDrawable); @@ -190,6 +195,10 @@ public void setForUser(boolean forUser) { updateRows(false, false); } + public void invalidateSearchBox() { + searchBox.invalidate(); + } + public static class SelectAnimatedEmojiDialogWindow extends PopupWindow { private static final Field superListenerField; private ViewTreeObserver.OnScrollChangedListener mSuperScrollListener; @@ -326,19 +335,20 @@ public void dismiss() { } } - private final static int currentAccount = UserConfig.selectedAccount; + private final int currentAccount = UserConfig.selectedAccount; private int type; public FrameLayout contentView; private View backgroundView; private EmojiTabsStrip[] cachedEmojiTabs = new EmojiTabsStrip[2]; public EmojiTabsStrip emojiTabs; - private View emojiTabsShadow; - private SearchBox searchBox; + public View emojiTabsShadow; + public SearchBox searchBox; public FrameLayout gridViewContainer; public EmojiListView emojiGridView; public EmojiListView emojiSearchGridView; public FrameLayout emojiSearchEmptyView; + public FrameLayout emojiGridViewContainer; private BackupImageView emojiSearchEmptyViewImageView; private View bubble1View; private View bubble2View; @@ -397,10 +407,10 @@ public SelectAnimatedEmojiDialog(BaseFragment baseFragment, Context context, boo } public SelectAnimatedEmojiDialog(BaseFragment baseFragment, Context context, boolean includeEmpty, Integer emojiX, int type, Theme.ResourcesProvider resourcesProvider) { - this(baseFragment, context, includeEmpty, emojiX, type, resourcesProvider, 16); + this(baseFragment, context, includeEmpty, emojiX, type, true, resourcesProvider, 16); } - public SelectAnimatedEmojiDialog(BaseFragment baseFragment, Context context, boolean includeEmpty, Integer emojiX, int type, Theme.ResourcesProvider resourcesProvider, int topPaddingDp) { + public SelectAnimatedEmojiDialog(BaseFragment baseFragment, Context context, boolean includeEmpty, Integer emojiX, int type, boolean shouldDrawBackground, Theme.ResourcesProvider resourcesProvider, int topPaddingDp) { super(context); this.resourcesProvider = resourcesProvider; this.type = type; @@ -437,23 +447,24 @@ public SelectAnimatedEmojiDialog(BaseFragment baseFragment, Context context, boo addView(bubble1View, LayoutHelper.createFrame(10, 10, Gravity.TOP | Gravity.LEFT, bubbleX / AndroidUtilities.density + (bubbleRight ? -12 : 4), topMarginDp, 0, 0)); } -// backgroundView = new View(context) { -// -// @Override -// protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { -// super.onMeasure(widthMeasureSpec, heightMeasureSpec); -// setPivotY(getPaddingTop()); -// if (bubbleX != null) { -// setPivotX(bubbleX); -// } -// } -// }; -// backgroundView.setPadding(AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8)); -// addView(backgroundView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL, 0, 12, 0, 0)); + backgroundView = new View(context) { + + @Override + protected void onDraw(Canvas canvas) { + if (!drawBackground) { + super.dispatchDraw(canvas); + return; + } + canvas.drawColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuBackground, resourcesProvider)); + } + }; + + boolean needBackgroundShadow = type == TYPE_TOPIC_ICON || type == TYPE_AVATAR_CONSTRUCTOR; contentView = new FrameLayout(context) { - private Path path = new Path(); - private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Path pathApi20 = new Path(); + private final Paint paintApi20 = new Paint(Paint.ANTI_ALIAS_FLAG); + private final boolean beforeLollipop = Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP; @Override protected void dispatchDraw(Canvas canvas) { @@ -461,36 +472,68 @@ protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); return; } - canvas.save(); - Theme.applyDefaultShadow(paint); - paint.setColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuBackground, resourcesProvider)); - paint.setAlpha((int) (255 * getAlpha())); - float px = (bubbleX == null ? getWidth() / 2f : bubbleX) + AndroidUtilities.dp(20); - float w = getWidth() - getPaddingLeft() - getPaddingRight(); - float h = getHeight() - getPaddingBottom() - getPaddingTop(); - AndroidUtilities.rectTmp.set( - getPaddingLeft() + (px - px * scaleX), - getPaddingTop(), - getPaddingLeft() + px + (w - px) * scaleX, - getPaddingTop() + h * scaleY - ); - path.rewind(); - path.addRoundRect(AndroidUtilities.rectTmp, AndroidUtilities.dp(12), AndroidUtilities.dp(12), Path.Direction.CW); - canvas.drawPath(path, paint); -// if (enterAnimationInProgress() { - canvas.clipPath(path); -// } - super.dispatchDraw(canvas); - canvas.restore(); + if (beforeLollipop) { + canvas.save(); + if (needBackgroundShadow) { + Theme.applyDefaultShadow(paintApi20); + } + paintApi20.setColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuBackground, resourcesProvider)); + paintApi20.setAlpha((int) (255 * getAlpha())); + float px = (bubbleX == null ? getWidth() / 2f : bubbleX) + AndroidUtilities.dp(20); + float w = getWidth() - getPaddingLeft() - getPaddingRight(); + float h = getHeight() - getPaddingBottom() - getPaddingTop(); + AndroidUtilities.rectTmp.set( + getPaddingLeft() + (px - px * scaleX), + getPaddingTop(), + getPaddingLeft() + px + (w - px) * scaleX, + getPaddingTop() + h * scaleY + ); + pathApi20.rewind(); + pathApi20.addRoundRect(AndroidUtilities.rectTmp, AndroidUtilities.dp(12), AndroidUtilities.dp(12), Path.Direction.CW); + canvas.drawPath(pathApi20, paintApi20); + canvas.clipPath(pathApi20); + super.dispatchDraw(canvas); + canvas.restore(); + } else { + super.dispatchDraw(canvas); + } } }; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + contentView.setOutlineProvider(new ViewOutlineProvider() { + private final Rect rect = new Rect(); + + @Override + public void getOutline(View view, Outline outline) { + float px = (bubbleX == null ? view.getWidth() / 2f : bubbleX) + AndroidUtilities.dp(20); + float w = view.getWidth() - view.getPaddingLeft() - view.getPaddingRight(); + float h = view.getHeight() - view.getPaddingBottom() - view.getPaddingTop(); + rect.set( + (int) (view.getPaddingLeft() + (px - px * scaleX)), + view.getPaddingTop(), + (int) (view.getPaddingLeft() + px + (w - px) * scaleX), + (int) (view.getPaddingTop() + h * scaleY) + ); + outline.setRoundRect(rect, dp(12)); + } + }); + contentView.setClipToOutline(true); + if (needBackgroundShadow) { + contentView.setElevation(2f); + } + } + if (type == TYPE_EMOJI_STATUS || type == TYPE_SET_DEFAULT_REACTION) { contentView.setPadding(AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8)); } + + contentView.addView(backgroundView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); addView(contentView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL, 0, type == TYPE_EMOJI_STATUS || type == TYPE_SET_DEFAULT_REACTION ? 6 + topMarginDp : 0, 0, 0)); if (bubbleX != null) { bubble2View = new View(context) { + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); @@ -504,9 +547,9 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { addView(bubble2View, LayoutHelper.createFrame(17, 9, Gravity.TOP | Gravity.LEFT, bubbleX / AndroidUtilities.density + (bubbleRight ? -25 : 10), 6 + 8 - 9 + topMarginDp, 0, 0)); } - boolean showSettings = baseFragment != null && type != TYPE_TOPIC_ICON && type != TYPE_AVATAR_CONSTRUCTOR; + boolean showSettings = baseFragment != null && type != TYPE_TOPIC_ICON && type != TYPE_AVATAR_CONSTRUCTOR && shouldDrawBackground; for (int i = 0; i < 2; i++) { - EmojiTabsStrip emojiTabs = new EmojiTabsStrip(context, null, false, true, type, showSettings ? () -> { + EmojiTabsStrip emojiTabs = new EmojiTabsStrip(context, resourcesProvider, true, false, true, type, showSettings ? () -> { search(null, false, false); onSettings(); baseFragment.presentFragment(new StickersActivity(MediaDataController.TYPE_EMOJIPACKS, frozenEmojiPacks)); @@ -585,6 +628,7 @@ protected void onTabCreate(EmojiTabsStrip.EmojiTabButton button) { cachedEmojiTabs[1].setVisibility(View.GONE); emojiTabsShadow = new View(context) { + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); @@ -634,7 +678,10 @@ protected float animateByScale(View view) { emojiItemAnimator.setDelayAnimations(false); emojiGridView.setItemAnimator(emojiItemAnimator); emojiGridView.setPadding(dp(5), dp(2), dp(5), dp(2 + 36)); - emojiGridView.setAdapter(adapter = new Adapter()); + adapter = new Adapter(); +// adapter.setHasStableIds(true); + emojiGridView.setAdapter(adapter); + emojiGridView.setLayoutManager(layoutManager = new GridLayoutManager(context, SPAN_COUNT) { @Override public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) { @@ -669,7 +716,59 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec) + AndroidUtilities.dp(36), MeasureSpec.EXACTLY)); } }; - gridViewContainer.addView(emojiGridView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL, 0, 0, 0, 0)); + + emojiGridViewContainer = new FrameLayout(context) { + private final Rect rect = new Rect(); + + /** + * The child does not redraw and uses hardware acceleration during animation. + * We simply display the pieces we need from a large image for cascade animation. + */ + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + if (child == emojiGridView && HwEmojis.isHwEnabled() && HwEmojis.isCascade()) { + for (int i = 0; i < emojiGridView.getChildCount(); i++) { + View view = emojiGridView.getChildAt(i); + if (view instanceof ImageViewEmoji) { + ImageViewEmoji viewEmoji = (ImageViewEmoji) view; + if (viewEmoji.getAnimatedScale() == 1f) { + rect.set(view.getLeft(), view.getTop(), view.getRight(), view.getBottom()); + canvas.save(); + canvas.clipRect(rect); + super.drawChild(canvas, child, drawingTime); + canvas.restore(); + } else if (viewEmoji.getAnimatedScale() > 0f) { + rect.set(view.getLeft(), view.getTop(), view.getRight(), view.getBottom()); + rect.set( + (int) (rect.centerX() - rect.width() / 2f * viewEmoji.getAnimatedScale()), + (int) (rect.centerY() - rect.height() / 2f * viewEmoji.getAnimatedScale()), + (int) (rect.centerX() + rect.width() / 2f * viewEmoji.getAnimatedScale()), + (int) (rect.centerY() + rect.height() / 2f * viewEmoji.getAnimatedScale()) + ); + canvas.save(); + canvas.clipRect(rect); + canvas.scale(viewEmoji.getAnimatedScale(), viewEmoji.getAnimatedScale(), rect.centerX(), rect.centerY()); + super.drawChild(canvas, child, drawingTime); + canvas.restore(); + } + } else if (view instanceof TextView || view instanceof EmojiPackExpand || view instanceof EmojiPackButton || view instanceof HeaderView) { + rect.set(view.getLeft(), view.getTop(), view.getRight(), view.getBottom()); + canvas.save(); + canvas.clipRect(rect); + super.drawChild(canvas, child, drawingTime); + canvas.restore(); + } + } + + return false; + } + + return super.drawChild(canvas, child, drawingTime); + } + }; + + emojiGridViewContainer.addView(emojiGridView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL, 0, 0, 0, 0)); + gridViewContainer.addView(emojiGridViewContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL, 0, 0, 0, 0)); emojiSearchGridView = new EmojiListView(context) { @Override @@ -752,7 +851,7 @@ public void onEndAnimation() { } }); scrollHelper.setScrollListener(() -> { - invalidateParent(); + invalidateParent(); }); RecyclerListView.OnItemLongClickListenerExtended onItemLongClick = new RecyclerListView.OnItemLongClickListenerExtended() { @@ -896,8 +995,26 @@ public void onAnimationEnd(Animator animation) { emojiGridView.setOnItemClickListener(onItemClick); emojiSearchGridView.setOnItemClickListener(onItemClick); - searchBox = new SearchBox(context); - searchBox.setTranslationY(-AndroidUtilities.dp( 52)); + searchBox = new SearchBox(context, shouldDrawBackground) { + @Override + protected void dispatchDraw(Canvas canvas) { + if (backgroundDelegate != null) { + backgroundDelegate.drawRect(canvas, 0, 0, getMeasuredWidth(), getMeasuredHeight(), searchBox.getX() + gridViewContainer.getX(), searchBox.getY() + gridViewContainer.getY()); + } + super.dispatchDraw(canvas); + } + + @Override + public void setTranslationY(float translationY) { + if (translationY != getTranslationY()) { + super.setTranslationY(translationY); + if (backgroundDelegate != null) { + invalidate(); + } + } + } + }; + searchBox.setTranslationY(-AndroidUtilities.dp(52)); searchBox.setVisibility(View.INVISIBLE); gridViewContainer.addView(searchBox, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 52, Gravity.TOP, 0, -4, 0, 0)); @@ -919,7 +1036,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { bottomGradientView = new View(context); Drawable bottomGradient = getResources().getDrawable(R.drawable.gradient_bottom); bottomGradient.setColorFilter(new PorterDuffColorFilter(AndroidUtilities.multiplyAlphaComponent(Theme.getColor(Theme.key_actionBarDefaultSubmenuBackground, resourcesProvider), .8f), PorterDuff.Mode.SRC_IN)); - bottomGradientView.setBackground(bottomGradient); + // bottomGradientView.setBackground(bottomGradient); bottomGradientView.setAlpha(0); contentView.addView(bottomGradientView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 20, Gravity.BOTTOM | Gravity.FILL_HORIZONTAL)); @@ -932,6 +1049,9 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { bigReactionImageReceiver.setLayerNum(7); + if (isAnimatedShow()) { + HwEmojis.beforePreparing(); + } updateRows(true, false); } @@ -1113,16 +1233,16 @@ protected void dispatchDraw(Canvas canvas) { drawableToBounds = new Rect(); } drawableToBounds.set( - (int) (bounds.centerX() - bounds.width() / 2f * scale - bounds.centerX() + emojiX + (scale > 1f && scale < 1.5f ? 2 : 0)), - (int) ((h - (h - bounds.bottom)) * scale - (scale > 1.5f ? (bounds.height() * .81f + 1) : 0) - bounds.top - bounds.height() / 2f + AndroidUtilities.dp(topMarginDp) - bounds.height() * scale), - (int) (bounds.centerX() + bounds.width() / 2f * scale - bounds.centerX() + emojiX + (scale > 1f && scale < 1.5f ? 2 : 0)), - (int) ((h - (h - bounds.bottom)) * scale - (scale > 1.5f ? bounds.height() * .81f + 1 : 0) - bounds.top - bounds.height() / 2f + AndroidUtilities.dp(topMarginDp)) + (int) (bounds.centerX() - bounds.width() / 2f * scale - bounds.centerX() + emojiX + (scale > 1f && scale < 1.5f ? 2 : 0)), + (int) ((h - (h - bounds.bottom)) * scale - (scale > 1.5f ? (bounds.height() * .81f + 1) : 0) - bounds.top - bounds.height() / 2f + AndroidUtilities.dp(topMarginDp) - bounds.height() * scale), + (int) (bounds.centerX() + bounds.width() / 2f * scale - bounds.centerX() + emojiX + (scale > 1f && scale < 1.5f ? 2 : 0)), + (int) ((h - (h - bounds.bottom)) * scale - (scale > 1.5f ? bounds.height() * .81f + 1 : 0) - bounds.top - bounds.height() / 2f + AndroidUtilities.dp(topMarginDp)) ); scrimDrawable.setBounds( - drawableToBounds.left, - drawableToBounds.top, - (int) (drawableToBounds.left + drawableToBounds.width() / scale), - (int) (drawableToBounds.top + drawableToBounds.height() / scale) + drawableToBounds.left, + drawableToBounds.top, + (int) (drawableToBounds.left + drawableToBounds.width() / scale), + (int) (drawableToBounds.top + drawableToBounds.height() / scale) ); canvas.scale(scale, scale, drawableToBounds.left, drawableToBounds.top); scrimDrawable.draw(canvas); @@ -1143,6 +1263,7 @@ protected void dispatchDraw(Canvas canvas) { } private ValueAnimator emojiSelectAnimator; + public void animateEmojiSelect(ImageViewEmoji view, Runnable onDone) { if (emojiSelectAnimator != null || scrimDrawable == null) { onDone.run(); @@ -1152,16 +1273,16 @@ public void animateEmojiSelect(ImageViewEmoji view, Runnable onDone) { view.notDraw = true; final Rect from = new Rect(); from.set( - contentView.getLeft() + emojiGridView.getLeft() + view.getLeft(), - contentView.getTop() + emojiGridView.getTop() + view.getTop(), - contentView.getLeft() + emojiGridView.getLeft() + view.getRight(), - contentView.getTop() + emojiGridView.getTop() + view.getBottom() + contentView.getLeft() + emojiGridView.getLeft() + view.getLeft(), + contentView.getTop() + emojiGridView.getTop() + view.getTop(), + contentView.getLeft() + emojiGridView.getLeft() + view.getRight(), + contentView.getTop() + emojiGridView.getTop() + view.getBottom() ); final AnimatedEmojiDrawable statusDrawable = - view.drawable instanceof AnimatedEmojiDrawable ? - AnimatedEmojiDrawable.make(currentAccount, AnimatedEmojiDrawable.CACHE_TYPE_EMOJI_STATUS, ((AnimatedEmojiDrawable) view.drawable).getDocumentId()) : - null; + view.drawable instanceof AnimatedEmojiDrawable ? + AnimatedEmojiDrawable.make(currentAccount, AnimatedEmojiDrawable.CACHE_TYPE_EMOJI_STATUS, ((AnimatedEmojiDrawable) view.drawable).getDocumentId()) : + null; emojiSelectView = view; emojiSelectRect = new Rect(); @@ -1176,10 +1297,10 @@ public void animateEmojiSelect(ImageViewEmoji view, Runnable onDone) { AndroidUtilities.lerp(from, drawableToBounds, t, emojiSelectRect); float scale = Math.max(1, overshootInterpolator.getInterpolation(MathUtils.clamp(3 * t - (3 - 1), 0, 1f))) * view.getScaleX(); emojiSelectRect.set( - (int) (emojiSelectRect.centerX() - emojiSelectRect.width() / 2f * scale), - (int) (emojiSelectRect.centerY() - emojiSelectRect.height() / 2f * scale), - (int) (emojiSelectRect.centerX() + emojiSelectRect.width() / 2f * scale), - (int) (emojiSelectRect.centerY() + emojiSelectRect.height() / 2f * scale) + (int) (emojiSelectRect.centerX() - emojiSelectRect.width() / 2f * scale), + (int) (emojiSelectRect.centerY() - emojiSelectRect.height() / 2f * scale), + (int) (emojiSelectRect.centerX() + emojiSelectRect.width() / 2f * scale), + (int) (emojiSelectRect.centerY() + emojiSelectRect.height() / 2f * scale) ); invalidate(); @@ -1259,6 +1380,7 @@ public void switchGrids(boolean search) { } private boolean gridSearch = false; + public void switchGrids(boolean search, boolean liftUp) { if (gridSearch == search) { return; @@ -1301,11 +1423,11 @@ public void onAnimationEnd(Animator animation) { gridSwitchAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); gridSwitchAnimator.start(); ((View) emojiGridView.getParent()).animate() - .translationY(gridSearch && liftUp ? -AndroidUtilities.dp(36) : 0) - .setUpdateListener(anm -> invalidateParent()) - .setInterpolator(CubicBezierInterpolator.DEFAULT) - .setDuration(160) - .start(); + .translationY(gridSearch && liftUp ? -AndroidUtilities.dp(36) : 0) + .setUpdateListener(anm -> invalidateParent()) + .setInterpolator(CubicBezierInterpolator.DEFAULT) + .setDuration(160) + .start(); if (gridSearch && liftUp) { emojiSearchGridView.setPadding(dp(5), dp(52 + 2), dp(5), dp(2)); } else { @@ -1314,15 +1436,8 @@ public void onAnimationEnd(Animator animation) { checkScroll(); } - private ArrayList emptyViewEmojis = new ArrayList(4); { - emptyViewEmojis.add("😖"); - emptyViewEmojis.add("😫"); - emptyViewEmojis.add("🫠"); - emptyViewEmojis.add("😨"); - emptyViewEmojis.add("❓"); - }; - public void updateSearchEmptyViewImage() { - if (emojiSearchEmptyViewImageView == null) { + public static void updateSearchEmptyViewImage(int currentAccount, BackupImageView imageView) { + if (imageView == null) { return; } @@ -1391,14 +1506,17 @@ public void updateSearchEmptyViewImage() { mediaLocation = ImageLocation.getForDocument(document); mediaFilter = filter; } - emojiSearchEmptyViewImageView.setLayerNum(7); - emojiSearchEmptyViewImageView.setRoundRadius(AndroidUtilities.dp(4)); - emojiSearchEmptyViewImageView.setImage(mediaLocation, mediaFilter, ImageLocation.getForDocument(thumb, document), "36_36", thumbDrawable, document); + imageView.setLayerNum(7); + imageView.setRoundRadius(AndroidUtilities.dp(4)); + imageView.setImage(mediaLocation, mediaFilter, ImageLocation.getForDocument(thumb, document), "36_36", thumbDrawable, document); } } + private static final List emptyViewEmojis = Arrays.asList("😖", "😫", "🫠", "😨", "❓"); + private boolean searchEmptyViewVisible = false; private ValueAnimator searchEmptyViewAnimator; + public void switchSearchEmptyView(boolean empty) { if (searchEmptyViewVisible == empty) { return; @@ -1428,7 +1546,7 @@ public void onAnimationEnd(Animator animation) { searchEmptyViewAnimator.start(); if (empty) { - updateSearchEmptyViewImage(); + updateSearchEmptyViewImage(currentAccount, emojiSearchEmptyViewImageView); } } @@ -1733,7 +1851,7 @@ public int getItemViewType(int position) { return VIEW_TYPE_EMOJI; } if (position > emojiStartRow && position - emojiStartRow - 1 < searchResult.size()) { - if (searchResult.get(position - emojiStartRow - 1 ).documentId != 0) { + if (searchResult.get(position - emojiStartRow - 1).documentId != 0) { return VIEW_TYPE_EMOJI; } } @@ -1886,31 +2004,31 @@ public void updateRows(boolean diff) { } } - if (diff) { - DiffUtil.calculateDiff(new DiffUtil.Callback() { - @Override - public int getOldListSize() { - return prevRowHashCodes.size(); - } - - @Override - public int getNewListSize() { - return rowHashCodes.size(); - } - - @Override - public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { - return prevRowHashCodes.get(oldItemPosition).equals(rowHashCodes.get(newItemPosition)); - } - - @Override - public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { - return true; - } - }, false).dispatchUpdatesTo(SearchAdapter.this); - } else { - this.notifyDataSetChanged(); - } +// if (diff) { +// DiffUtil.calculateDiff(new DiffUtil.Callback() { +// @Override +// public int getOldListSize() { +// return prevRowHashCodes.size(); +// } +// +// @Override +// public int getNewListSize() { +// return rowHashCodes.size(); +// } +// +// @Override +// public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { +// return prevRowHashCodes.get(oldItemPosition).equals(rowHashCodes.get(newItemPosition)); +// } +// +// @Override +// public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { +// return true; +// } +// }, false).dispatchUpdatesTo(SearchAdapter.this); +// } else { + this.notifyDataSetChanged(); + // } switchSearchEmptyView(searched && count == 0); } @@ -1933,10 +2051,10 @@ private class Adapter extends RecyclerListView.SelectionAdapter { public boolean isEnabled(RecyclerView.ViewHolder holder) { int viewType = holder.getItemViewType(); return ( - viewType == VIEW_TYPE_IMAGE || - viewType == VIEW_TYPE_REACTION || - viewType == VIEW_TYPE_EMOJI || - viewType == VIEW_TYPE_TOPIC_ICON + viewType == VIEW_TYPE_IMAGE || + viewType == VIEW_TYPE_REACTION || + viewType == VIEW_TYPE_EMOJI || + viewType == VIEW_TYPE_TOPIC_ICON ); } @@ -2007,7 +2125,8 @@ public int getItemViewType(int position) { return VIEW_TYPE_HINT; } else if (positionToSection.indexOfKey(position) >= 0 || position == recentReactionsSectionRow || position == popularSectionRow || position == topicEmojiHeaderRow) { return VIEW_TYPE_HEADER; - } if (position == defaultTopicIconRow) { + } + if (position == defaultTopicIconRow) { return VIEW_TYPE_TOPIC_ICON; } else { if (showStickers) { @@ -2152,7 +2271,10 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi if (pack != null) { button.set(pack.set.title, !pack.free && !UserConfig.getInstance(currentAccount).isPremium(), pack.installed, e -> { if (!pack.free && !UserConfig.getInstance(currentAccount).isPremium()) { - new PremiumFeatureBottomSheet(baseFragment, getContext(), currentAccount, PremiumPreviewFragment.PREMIUM_FEATURE_ANIMATED_EMOJI, false).show(); + BaseFragment fragment = LaunchActivity.getLastFragment(); + if (fragment != null) { + fragment.showDialog(new PremiumFeatureBottomSheet(baseFragment, getContext(), currentAccount, PremiumPreviewFragment.PREMIUM_FEATURE_ANIMATED_EMOJI, false)); + } return; } Integer p = null; @@ -2261,7 +2383,7 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi TLRPC.Document document = pack.documents.get(position - startPosition - 1); if (document != null) { if (showStickers) { - imageView.setSticker(document, emojiSearchGridView); + imageView.setSticker(document, emojiSearchGridView); } else { imageView.isStaticIcon = false; if (imageView.imageReceiver != null) { @@ -2294,6 +2416,11 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi public int getItemCount() { return totalCount; } + + @Override + public long getItemId(int position) { + return Math.abs(rowHashCodes.get(position)); + } } private boolean enterAnimationInProgress() { @@ -2389,7 +2516,23 @@ private class EmojiPackButton extends FrameLayout { public EmojiPackButton(Context context) { super(context); - addButtonTextView = new AnimatedTextView(getContext()); + addButtonTextView = new AnimatedTextView(getContext()) { + @Override + public void invalidate() { + if (HwEmojis.grab(this)) { + return; + } + super.invalidate(); + } + + @Override + public void invalidate(int l, int t, int r, int b) { + if (HwEmojis.grab(this)) { + return; + } + super.invalidate(l, t, r, b); + } + }; addButtonTextView.setAnimationProperties(.3f, 0, 250, CubicBezierInterpolator.EASE_OUT_QUINT); addButtonTextView.setTextSize(AndroidUtilities.dp(14)); addButtonTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); @@ -2565,13 +2708,14 @@ public class ImageViewEmoji extends View { public int skewIndex; public boolean isStaticIcon; private float selectedProgress; + private float animatedScale = 1f; - final AnimatedEmojiSpan.InvalidateHolder invalidateHolder = new AnimatedEmojiSpan.InvalidateHolder() { - @Override - public void invalidate() { - if (getParent() != null) { - ((View)getParent()).invalidate(); - } + final AnimatedEmojiSpan.InvalidateHolder invalidateHolder = () -> { + if (HwEmojis.isHwEnabled()) { + return; + } + if (getParent() != null) { + ((View) getParent()).invalidate(); } }; @@ -2580,6 +2724,17 @@ public ImageViewEmoji(Context context) { preloadEffectImageReceiver.ignoreNotifications = true; } + /** + * {@link View#setScaleX} causes an implicit redraw of the parent. Therefore, we use a custom method. + */ + public void setAnimatedScale(float scale) { + animatedScale = scale; + } + + public float getAnimatedScale() { + return animatedScale; + } + @Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(MeasureSpec.makeMeasureSpec(View.MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(View.MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY)); @@ -2646,11 +2801,11 @@ public void setViewSelected(boolean selected, boolean animated) { public void drawSelected(Canvas canvas, View view) { if ((selected || selectedProgress > 0) && !notDraw) { if (selected && selectedProgress < 1f) { - selectedProgress += 16/ 300f; + selectedProgress += 16 / 300f; view.invalidate(); } if (!selected && selectedProgress > 0) { - selectedProgress -= 16/ 300f; + selectedProgress -= 16 / 300f; view.invalidate(); } selectedProgress = Utilities.clamp(selectedProgress, 1f, 0f); @@ -2708,7 +2863,6 @@ public void setDrawable(Drawable drawable) { ((AnimatedEmojiDrawable) drawable).addView(invalidateHolder); } } - } public void setSticker(TLRPC.Document document, View parent) { @@ -2733,15 +2887,26 @@ public void createImageReceiver(View parent) { @Override public void invalidate() { + if (HwEmojis.isHwEnabled()) { + return; + } if (getParent() != null) { ((View) getParent()).invalidate(); } } + @Override + public void invalidate(int l, int t, int r, int b) { + if (HwEmojis.isHwEnabled()) { + return; + } + super.invalidate(l, t, r, b); + } + public void createPremiumLockView() { if (premiumLockIconView == null) { premiumLockIconView = new PremiumLockIconView(getContext(), PremiumLockIconView.TYPE_STICKERS_PREMIUM_LOCKED); - int measureSpec = MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(12),MeasureSpec.EXACTLY); + int measureSpec = MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(12), MeasureSpec.EXACTLY); premiumLockIconView.measure(measureSpec, measureSpec); premiumLockIconView.layout(0, 0, premiumLockIconView.getMeasuredWidth(), premiumLockIconView.getMeasuredHeight()); } @@ -2778,7 +2943,7 @@ private void incrementHintUse() { if (type == TYPE_SET_DEFAULT_REACTION) { return; } - final String key = "emoji" + (type==TYPE_EMOJI_STATUS ? "status" : "reaction") + "usehint"; + final String key = "emoji" + (type == TYPE_EMOJI_STATUS ? "status" : "reaction") + "usehint"; final int value = MessagesController.getGlobalMainSettings().getInt(key, 0); if (value <= 3) { MessagesController.getGlobalMainSettings().edit().putInt(key, value + 1).apply(); @@ -2802,23 +2967,27 @@ public void preload(int type, int account) { MediaDataController.getInstance(account).checkReactions(); } else if (type == TYPE_EMOJI_STATUS) { MediaDataController.getInstance(account).fetchEmojiStatuses(0, true); + MediaDataController.getInstance(account).getStickerSet(new TLRPC.TL_inputStickerSetEmojiDefaultStatuses(), false); } else if (type == TYPE_TOPIC_ICON) { MediaDataController.getInstance(account).checkDefaultTopicIcons(); } else if (type == TYPE_AVATAR_CONSTRUCTOR) { MediaDataController.getInstance(currentAccount).loadRecents(MediaDataController.TYPE_IMAGE, false, true, false); MediaDataController.getInstance(account).checkStickers(MediaDataController.TYPE_IMAGE); } - MediaDataController.getInstance(account).getStickerSet(new TLRPC.TL_inputStickerSetEmojiDefaultStatuses(), false); } + private static boolean[] preloaded = new boolean[UserConfig.MAX_ACCOUNT_COUNT]; + public static void preload(int account) { - if (MediaDataController.getInstance(account) == null) { + if (preloaded[account] || MediaDataController.getInstance(account) == null) { return; } + preloaded[account] = true; MediaDataController.getInstance(account).checkStickers(MediaDataController.TYPE_EMOJIPACKS); MediaDataController.getInstance(account).fetchEmojiStatuses(0, true); MediaDataController.getInstance(account).checkReactions(); MediaDataController.getInstance(account).getStickerSet(new TLRPC.TL_inputStickerSetEmojiDefaultStatuses(), false); + MediaDataController.getInstance(account).getDefaultEmojiStatuses(); MediaDataController.getInstance(account).checkDefaultTopicIcons(); StickerCategoriesListView.preload(account, StickerCategoriesListView.CategoriesType.STATUS); } @@ -2881,7 +3050,7 @@ private void updateRows(boolean updateEmojipacks, boolean animated, boolean diff totalCount++; } } else { - TLRPC.TL_emojiList emojiList = forUser ? MediaDataController.getInstance(currentAccount). profileAvatarConstructorDefault : MediaDataController.getInstance(currentAccount).groupAvatarConstructorDefault; + TLRPC.TL_emojiList emojiList = forUser ? MediaDataController.getInstance(currentAccount).profileAvatarConstructorDefault : MediaDataController.getInstance(currentAccount).groupAvatarConstructorDefault; if (emojiList != null && emojiList.document_id != null && !emojiList.document_id.isEmpty()) { for (int i = 0; i < emojiList.document_id.size(); ++i) { recent.add(new AnimatedEmojiSpan(emojiList.document_id.get(i), null)); @@ -2975,7 +3144,7 @@ private void updateRows(boolean updateEmojipacks, boolean animated, boolean diff } } else if (type == TYPE_EMOJI_STATUS) { ArrayList recentEmojiStatuses = MediaDataController.getInstance(currentAccount).getRecentEmojiStatuses(); - TLRPC.TL_messages_stickerSet defaultSet = MediaDataController.getInstance(currentAccount).getStickerSet(new TLRPC.TL_inputStickerSetEmojiDefaultStatuses(), false); + TLRPC.TL_messages_stickerSet defaultSet = MediaDataController.getInstance(currentAccount).getStickerSet(new TLRPC.TL_inputStickerSetEmojiDefaultStatuses(), true); if (defaultSet == null) { defaultSetLoading = true; } else { @@ -3357,7 +3526,6 @@ public void dispatchDraw(Canvas canvas) { imageViewEmoji.updatePressedProgress(); int top = smoothScrolling ? (int) child.getY() : child.getTop(); ArrayList arrayList = viewsGroupedByLines.get(top); - canvas.save(); canvas.translate(imageViewEmoji.getX(), imageViewEmoji.getY()); imageViewEmoji.drawSelected(canvas, this); @@ -3461,8 +3629,8 @@ public void dispatchDraw(Canvas canvas) { if (imageViewEmoji.premiumLockIconView != null && imageViewEmoji.premiumLockIconView.getVisibility() == View.VISIBLE) { canvas.save(); canvas.translate( - (int) (imageViewEmoji.getX() + imageViewEmoji.getMeasuredWidth() - imageViewEmoji.premiumLockIconView.getMeasuredWidth()), - (int) (imageViewEmoji.getY() + imageViewEmoji.getMeasuredHeight() - imageViewEmoji.premiumLockIconView.getMeasuredHeight()) + (int) (imageViewEmoji.getX() + imageViewEmoji.getMeasuredWidth() - imageViewEmoji.premiumLockIconView.getMeasuredWidth()), + (int) (imageViewEmoji.getY() + imageViewEmoji.getMeasuredHeight() - imageViewEmoji.premiumLockIconView.getMeasuredHeight()) ); imageViewEmoji.premiumLockIconView.draw(canvas); canvas.restore(); @@ -3476,6 +3644,7 @@ public void dispatchDraw(Canvas canvas) { } canvas.restoreToCount(restoreTo); + HwEmojis.exec(); } public class DrawingInBackgroundLine extends DrawingInBackgroundThreadDrawable { @@ -3487,6 +3656,8 @@ public class DrawingInBackgroundLine extends DrawingInBackgroundThreadDrawable { float skewAlpha = 1f; boolean skewBelow = false; + boolean lite = LiteMode.isEnabled(LiteMode.FLAG_ANIMATED_EMOJI_REACTIONS); + @Override public void draw(Canvas canvas, long time, int w, int h, float alpha) { if (imageViewEmojis == null) { @@ -3501,7 +3672,7 @@ public void draw(Canvas canvas, long time, int w, int h, float alpha) { skewAlpha = .25f + .75f * skewAlpha; } } - boolean drawInUi = skewAlpha < 1 || isAnimating() || imageViewEmojis.size() <= 4 || !LiteMode.isEnabled(LiteMode.FLAG_ANIMATED_EMOJI_REACTIONS) || enterAnimationInProgress() || type == TYPE_AVATAR_CONSTRUCTOR; + boolean drawInUi = skewAlpha < 1 || isAnimating() || imageViewEmojis.size() <= 4 || !lite || enterAnimationInProgress() || type == TYPE_AVATAR_CONSTRUCTOR; if (!drawInUi) { boolean animatedExpandIn = animateExpandStartTime > 0 && (SystemClock.elapsedRealtime() - animateExpandStartTime) < animateExpandDuration(); for (int i = 0; i < imageViewEmojis.size(); i++) { @@ -3512,7 +3683,11 @@ public void draw(Canvas canvas, long time, int w, int h, float alpha) { } } } - if (drawInUi) { + + if (HwEmojis.isHwEnabled()) { + alpha = 1f; + } + if (drawInUi || HwEmojis.isPreparing()) { prepareDraw(System.currentTimeMillis()); drawInUiThread(canvas, alpha); reset(); @@ -3556,7 +3731,7 @@ public void drawBitmap(Canvas canvas, Bitmap bitmap, Paint paint) { // // canvas.drawBitmapMesh(bitmap, 3, 1, verts, 0, null, 0, paint); // } else { - canvas.drawBitmap(bitmap, 0, 0, paint); + canvas.drawBitmap(bitmap, 0, 0, paint); // } } @@ -3659,10 +3834,10 @@ public void prepareDraw(long time) { AndroidUtilities.rectTmp2.set(imageView.getPaddingLeft(), imageView.getPaddingTop(), imageView.getWidth() - imageView.getPaddingRight(), imageView.getHeight() - imageView.getPaddingBottom()); if (imageView.selected && type != TYPE_TOPIC_ICON && type != TYPE_AVATAR_CONSTRUCTOR) { AndroidUtilities.rectTmp2.set( - (int) Math.round(AndroidUtilities.rectTmp2.centerX() - AndroidUtilities.rectTmp2.width() / 2f * 0.86f), - (int) Math.round(AndroidUtilities.rectTmp2.centerY() - AndroidUtilities.rectTmp2.height() / 2f * 0.86f), - (int) Math.round(AndroidUtilities.rectTmp2.centerX() + AndroidUtilities.rectTmp2.width() / 2f * 0.86f), - (int) Math.round(AndroidUtilities.rectTmp2.centerY() + AndroidUtilities.rectTmp2.height() / 2f * 0.86f) + (int) Math.round(AndroidUtilities.rectTmp2.centerX() - AndroidUtilities.rectTmp2.width() / 2f * 0.86f), + (int) Math.round(AndroidUtilities.rectTmp2.centerY() - AndroidUtilities.rectTmp2.height() / 2f * 0.86f), + (int) Math.round(AndroidUtilities.rectTmp2.centerX() + AndroidUtilities.rectTmp2.width() / 2f * 0.86f), + (int) Math.round(AndroidUtilities.rectTmp2.centerY() + AndroidUtilities.rectTmp2.height() / 2f * 0.86f) ); } AndroidUtilities.rectTmp2.offset(imageView.getLeft() + (int) imageView.getTranslationX() - startOffset, topOffset); @@ -3757,7 +3932,10 @@ protected void drawInUiThread(Canvas canvas, float alpha) { imageView.skewIndex = i; if (scale != 1 || skewAlpha < 1) { canvas.save(); - canvas.scale(scale, scale, AndroidUtilities.rectTmp2.centerX(), AndroidUtilities.rectTmp2.centerY()); + if (imageView.selected && type != TYPE_TOPIC_ICON && type != TYPE_AVATAR_CONSTRUCTOR) { + //scale here only selected emoji + canvas.scale(0.85f, 0.85f, AndroidUtilities.rectTmp2.centerX(), AndroidUtilities.rectTmp2.centerY()); + } skew(canvas, i, imageView.getHeight()); drawImage(canvas, drawable, imageView, alpha); canvas.restore(); @@ -3835,14 +4013,33 @@ private void release(ArrayList lineDrawables) { lineDrawables.clear(); } + @Override + public void invalidateViews() { + if (HwEmojis.grab(this)) { + return; + } + super.invalidateViews(); + } + @Override public void invalidate() { + if (HwEmojis.grab(this)) { + return; + } if (invalidated) { return; } invalidated = true; super.invalidate(); } + + @Override + public void invalidate(int l, int t, int r, int b) { + if (HwEmojis.grab(this)) { + return; + } + super.invalidate(l, t, r, b); + } } @Override @@ -3874,38 +4071,43 @@ protected void onDetachedFromWindow() { } } - private final Runnable updateRowsDelayed = () -> NotificationCenter.getInstance(currentAccount).doOnIdle(() -> updateRows(true, true)); + private final Runnable updateRows = () -> updateRows(true, true); + private final Runnable updateRowsDelayed = () -> { + NotificationCenter.getGlobalInstance().removeDelayed(updateRows); + NotificationCenter.getGlobalInstance().doOnIdle(updateRows); + }; + + private void updateRowsDelayed() { + AndroidUtilities.cancelRunOnUIThread(updateRowsDelayed); + AndroidUtilities.runOnUIThread(updateRowsDelayed); + } @Override public void didReceivedNotification(int id, int account, Object... args) { if (id == NotificationCenter.stickersDidLoad) { if (((int) args[0]) == MediaDataController.TYPE_EMOJIPACKS || (((int) args[0]) == MediaDataController.TYPE_IMAGE && showStickers)) { - updateRows(true, true); + updateRowsDelayed(); } } else if (id == NotificationCenter.featuredEmojiDidLoad) { - NotificationCenter.getGlobalInstance().doOnIdle(() -> { - AndroidUtilities.runOnUIThread(() -> { - updateRows(false, true); - }, 120); - }); + updateRowsDelayed(); } else if (id == NotificationCenter.recentEmojiStatusesUpdate) { - NotificationCenter.getGlobalInstance().doOnIdle(() -> { - AndroidUtilities.runOnUIThread(() -> { - updateRows(false, true); - }, 120); - }); + updateRowsDelayed(); } else if (id == NotificationCenter.groupStickersDidLoad) { - AndroidUtilities.cancelRunOnUIThread(updateRowsDelayed); - AndroidUtilities.runOnUIThread(updateRowsDelayed, 100); + updateRowsDelayed(); } } + private static boolean isFirstOpen = true; private Runnable dismiss; final float durationScale = 1f; final long showDuration = (long) (800 * durationScale); private ValueAnimator showAnimator; private ValueAnimator hideAnimator; - private int animationIndex = -1; + private AnimationNotificationsLocker notificationsLocker = new AnimationNotificationsLocker(); + + private boolean isAnimatedShow() { + return type != TYPE_TOPIC_ICON && type != TYPE_AVATAR_CONSTRUCTOR; + } public void onShow(Runnable dismiss) { if (listStateId != null) { @@ -3933,19 +4135,34 @@ public void onShow(Runnable dismiss) { hideAnimator.cancel(); hideAnimator = null; } - boolean animated = type != TYPE_TOPIC_ICON && type != TYPE_AVATAR_CONSTRUCTOR; + boolean animated = isAnimatedShow(); if (animated) { - showAnimator = ValueAnimator.ofFloat(0, 1); + showAnimator = ValueAnimator.ofFloat(0f, 1f); showAnimator.addUpdateListener(anm -> { final float t = (float) anm.getAnimatedValue(); updateShow(t); }); + showAnimator.addListener(new AnimatorListenerAdapter() { + @Override public void onAnimationEnd(Animator animation) { + HwEmojis.disableHw(); + emojiGridView.setLayerType(LAYER_TYPE_NONE, null); + searchBox.setLayerType(LAYER_TYPE_NONE, null); + emojiTabsShadow.setLayerType(LAYER_TYPE_NONE, null); + backgroundView.setLayerType(LAYER_TYPE_NONE, null); + if (bubble2View != null) { + bubble2View.setLayerType(LAYER_TYPE_NONE, null); + } + if (bubble1View != null) { + bubble1View.setLayerType(LAYER_TYPE_NONE, null); + } + searchBox.checkInitialization(); + emojiTabs.showRecentTabStub(false); NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.startAllHeavyOperations, 512); - NotificationCenter.getGlobalInstance().onAnimationFinish(animationIndex); + notificationsLocker.unlock(); AndroidUtilities.runOnUIThread(NotificationCenter.getGlobalInstance()::runDelayedNotifications); checkScroll(); updateShow(1); @@ -3960,22 +4177,48 @@ public void onAnimationEnd(Animator animation) { child.setScaleY(1); } emojiTabs.contentView.invalidate(); + emojiGridViewContainer.invalidate(); + emojiGridView.invalidate(); } }); - updateShow(0); - showAnimator.setDuration(showDuration); + + if (isFirstOpen) { + isFirstOpen = false; + AnimatedEmojiDrawable.getDocumentFetcher(currentAccount).setUiDbCallback(() -> { + HwEmojis.enableHw(); + AndroidUtilities.runOnUIThread(() -> showAnimator.start(), 0); + }); + HwEmojis.prepare(null, true); + } else { + Runnable action = () -> { + HwEmojis.enableHw(); + AndroidUtilities.runOnUIThread(() -> showAnimator.start(), 0); + }; + HwEmojis.prepare(action, true); + } + NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.stopAllHeavyOperations, 512); - animationIndex = NotificationCenter.getGlobalInstance().setAnimationInProgress(animationIndex, null); - showAnimator.start(); + notificationsLocker.lock(); + showAnimator.setDuration(showDuration); + emojiGridView.setLayerType(LAYER_TYPE_HARDWARE, null); + searchBox.setLayerType(LAYER_TYPE_HARDWARE, null); + emojiTabsShadow.setLayerType(LAYER_TYPE_HARDWARE, null); + backgroundView.setLayerType(LAYER_TYPE_HARDWARE, null); + if (bubble2View != null) { + bubble2View.setLayerType(LAYER_TYPE_HARDWARE, null); + } + if (bubble1View != null) { + bubble1View.setLayerType(LAYER_TYPE_HARDWARE, null); + } + emojiTabs.showRecentTabStub(true); + updateShow(0); } else { checkScroll(); updateShow(1); } } - private static boolean firstOpen = true; - - private class SearchBox extends FrameLayout { + public class SearchBox extends FrameLayout { private FrameLayout box; private ImageView search; private ImageView clear; @@ -3984,14 +4227,18 @@ private class SearchBox extends FrameLayout { private SearchStateDrawable searchStateDrawable; private EditTextCaption input; private StickerCategoriesListView categoriesListView; + private float inputBoxGradientAlpha; + private ValueAnimator inputBoxGradientAnimator; - public SearchBox(Context context) { + public SearchBox(Context context, boolean drawBackground) { super(context); setClickable(true); - setBackgroundColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuBackground, resourcesProvider)); box = new FrameLayout(context); + if (drawBackground) { + setBackgroundColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuBackground, resourcesProvider)); + } box.setBackground(Theme.createRoundRectDrawable(dp(18), Theme.getColor(Theme.key_chat_emojiPanelBackground, resourcesProvider))); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { box.setClipToOutline(true); @@ -4026,10 +4273,42 @@ public void getOutline(View view, Outline outline) { }); box.addView(search, LayoutHelper.createFrame(36, 36, Gravity.LEFT | Gravity.TOP)); - inputBox = new FrameLayout(context); + inputBox = new FrameLayout(context) { + Paint fadePaint; + + @Override + protected void dispatchDraw(Canvas canvas) { + if (!drawBackground && inputBoxGradientAlpha > 0) { + if (fadePaint == null) { + fadePaint = new Paint(); + fadePaint.setShader(new LinearGradient(0, 0, AndroidUtilities.dp(18), 0, new int[]{0xffffffff, 0}, new float[]{0f, 1f}, Shader.TileMode.CLAMP)); + fadePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); + } + canvas.saveLayerAlpha(0, 0, getMeasuredWidth(), getMeasuredHeight(), 255, Canvas.ALL_SAVE_FLAG); + super.dispatchDraw(canvas); + fadePaint.setAlpha((int) (inputBoxGradientAlpha * 255)); + canvas.drawRect(0, 0, AndroidUtilities.dp(18), getMeasuredHeight(), fadePaint); + canvas.restore(); + } else { + super.dispatchDraw(canvas); + } + } + }; box.addView(inputBox, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL, 36, 0, 0, 0)); input = new EditTextCaption(context, resourcesProvider) { + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_UP && prevWindowKeyboardVisible()) { + AndroidUtilities.runOnUIThread(() -> { + requestFocus(); + }, 200); + return false; + } + return super.onTouchEvent(event); + } + @Override protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { if (focused) { @@ -4037,16 +4316,25 @@ protected void onFocusChanged(boolean focused, int direction, Rect previouslyFoc AndroidUtilities.runOnUIThread(() -> { AndroidUtilities.showKeyboard(input); }, 200); - } super.onFocusChanged(focused, direction, previouslyFocusedRect); } + + @Override + public void invalidate() { + if (HwEmojis.isHwEnabled()) { + return; + } + super.invalidate(); + } }; input.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + @Override public void onTextChanged(CharSequence s, int start, int before, int count) {} + @Override public void afterTextChanged(Editable s) { final String query = input.getText() == null || AndroidUtilities.trim(input.getText(), null).length() == 0 ? null : input.getText().toString(); @@ -4078,16 +4366,20 @@ public void afterTextChanged(Editable s) { input.setSingleLine(true); input.setLines(1); input.setTranslationY(AndroidUtilities.dp(-1)); - inputBox.addView(input, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL, 0, 0, 32, 0)); - - inputBoxGradient = new View(context); - Drawable gradientDrawable = context.getResources().getDrawable(R.drawable.gradient_right).mutate(); - gradientDrawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_chat_emojiPanelBackground, resourcesProvider), PorterDuff.Mode.MULTIPLY)); - inputBoxGradient.setBackground(gradientDrawable); - inputBoxGradient.setAlpha(0f); - inputBox.addView(inputBoxGradient, LayoutHelper.createFrame(18, LayoutHelper.MATCH_PARENT, Gravity.LEFT)); + inputBox.addView(input, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL, 0, 0, 32, 0)); + if (drawBackground) { + inputBoxGradient = new View(context); + Drawable gradientDrawable = context.getResources().getDrawable(R.drawable.gradient_right).mutate(); + gradientDrawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_chat_emojiPanelBackground, resourcesProvider), PorterDuff.Mode.MULTIPLY)); + inputBoxGradient.setBackground(gradientDrawable); + inputBoxGradient.setAlpha(0f); + inputBox.addView(inputBoxGradient, LayoutHelper.createFrame(18, LayoutHelper.MATCH_PARENT, Gravity.LEFT)); + } setOnClickListener(e -> { + if (prevWindowKeyboardVisible()) { + return; + } onInputFocus(); input.requestFocus(); scrollToPosition(0, 0); @@ -4096,7 +4388,10 @@ public void afterTextChanged(Editable s) { clear = new ImageView(context); clear.setScaleType(ImageView.ScaleType.CENTER); clear.setImageDrawable(new CloseProgressDrawable2(1.25f) { - { setSide(AndroidUtilities.dp(7)); } + { + setSide(AndroidUtilities.dp(7)); + } + @Override protected int getCurrentColor() { return Theme.getColor(Theme.key_chat_emojiSearchIcon, resourcesProvider); @@ -4116,15 +4411,15 @@ protected int getCurrentColor() { showInputBoxGradient(false); }); box.addView(clear, LayoutHelper.createFrame(36, 36, Gravity.RIGHT | Gravity.TOP)); - - if (firstOpen) { - AndroidUtilities.runOnUIThread(this::createCategoriesListView, 450); - firstOpen = false; - } else { + if (!HwEmojis.isFirstOpen()) { createCategoriesListView(); } } + public void checkInitialization() { + createCategoriesListView(); + } + private void createCategoriesListView() { if (categoriesListView != null || getContext() == null) { return; @@ -4160,7 +4455,6 @@ protected boolean isTabIconsAnimationEnabled(boolean loaded) { }; categoriesListView.setShownButtonsAtStart(type == TYPE_AVATAR_CONSTRUCTOR ? 6.5f : 4.5f); categoriesListView.setDontOccupyWidth((int) (input.getPaint().measureText(input.getHint() + ""))); - categoriesListView.setBackgroundColor(Theme.getColor(Theme.key_chat_emojiPanelBackground, resourcesProvider)); categoriesListView.setOnScrollIntoOccupiedWidth(scrolled -> { input.setTranslationX(-Math.max(0, scrolled)); showInputBoxGradient(scrolled > 0); @@ -4179,6 +4473,7 @@ protected boolean isTabIconsAnimationEnabled(boolean loaded) { } private Runnable delayedToggle; + private void toggleClear(boolean enabled) { if (enabled) { if (delayedToggle == null) { @@ -4196,13 +4491,28 @@ private void toggleClear(boolean enabled) { } private boolean inputBoxShown = false; + private void showInputBoxGradient(boolean show) { - if (show == inputBoxShown || inputBoxGradient == null) { + if (show == inputBoxShown) { return; } inputBoxShown = show; - inputBoxGradient.clearAnimation(); - inputBoxGradient.animate().alpha(show ? 1 : 0).setDuration(120).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).start(); + if (inputBoxGradientAnimator != null) { + inputBoxGradientAnimator.cancel(); + } + inputBoxGradientAnimator = ValueAnimator.ofFloat(inputBoxGradientAlpha, show ? 1f : 0); + inputBoxGradientAnimator.addUpdateListener(animation -> { + inputBoxGradientAlpha = (float) animation.getAnimatedValue(); + if (inputBoxGradient != null) { + inputBoxGradient.setAlpha(inputBoxGradientAlpha); + } else if (inputBox != null) { + inputBox.invalidate(); + } + }); + inputBoxGradientAnimator.setDuration(120); + inputBoxGradientAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + inputBoxGradientAnimator.start(); + } public boolean isInProgress() { @@ -4230,7 +4540,15 @@ private void updateButton(boolean force) { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(8+36+8), MeasureSpec.EXACTLY)); + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(8 + 36 + 8), MeasureSpec.EXACTLY)); + } + + @Override + public void invalidate() { + if (HwEmojis.grab(this)) { + return; + } + super.invalidate(); } } @@ -4263,30 +4581,47 @@ private void updateShow(float t) { containery = CubicBezierInterpolator.EASE_OUT_QUINT.getInterpolation(containery); // containeritemst = endslow.getInterpolation(containeritemst); // containeralphat = CubicBezierInterpolator.EASE_OUT.getInterpolation(containeralphat); - contentView.setAlpha(containeralphat); + + backgroundView.setAlpha(containeralphat); + searchBox.setAlpha(containeralphat); + for (int i = 0; i < emojiTabs.contentView.getChildCount(); i++) { + View child = emojiTabs.contentView.getChildAt(i); + child.setAlpha(containeralphat); + } if (scrimDrawable != null) { invalidate(); } - contentView.setTranslationY(AndroidUtilities.dp(-5) * (1f - containeralphat)); + contentView.setTranslationY(dp(-5) * (1f - containeralphat)); if (bubble2View != null) { - bubble2View.setTranslationY(AndroidUtilities.dp(-5) * (1f - containeralphat)); + bubble2View.setTranslationY(dp(-5) * (1f - containeralphat)); } this.scaleX = .15f + .85f * containerx; this.scaleY = .075f + .925f * containery; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + contentView.invalidateOutline(); + } else { + backgroundView.setVisibility(GONE); + contentView.setAlpha(containeralphat); + contentView.invalidate(); + } if (bubble2View != null) { bubble2View.setAlpha(containeralphat); } - contentView.invalidate(); emojiTabsShadow.setAlpha(containeralphat); emojiTabsShadow.setScaleX(Math.min(scaleX, 1)); final float px = emojiTabsShadow.getPivotX(), py = 0; final float fullr = (float) Math.sqrt(Math.max( - px * px + Math.pow(contentView.getHeight(), 2), - Math.pow(contentView.getWidth() - px, 2) + Math.pow(contentView.getHeight(), 2) + px * px + Math.pow(contentView.getHeight(), 2), + Math.pow(contentView.getWidth() - px, 2) + Math.pow(contentView.getHeight(), 2) )); for (int i = 0; i < emojiTabs.contentView.getChildCount(); ++i) { View child = emojiTabs.contentView.getChildAt(i); + if (t == 0) { + child.setLayerType(LAYER_TYPE_HARDWARE, null); + } else if (t == 1) { + child.setLayerType(LAYER_TYPE_NONE, null); + } float ccx = child.getLeft() + child.getWidth() / 2f, ccy = child.getTop() + child.getHeight() / 2f; float distance = (float) Math.sqrt((ccx - px) * (ccx - px) + ccy * ccy * .4f); float scale = AndroidUtilities.cascade(containeritemst, distance, fullr, child.getHeight() * 1.75f); @@ -4296,17 +4631,20 @@ private void updateShow(float t) { child.setScaleX(scale); child.setScaleY(scale); } - emojiTabs.contentView.invalidate(); for (int i = 0; i < emojiGridView.getChildCount(); ++i) { View child = emojiGridView.getChildAt(i); - float cx = child.getLeft() + child.getWidth() / 2f, cy = child.getTop() + child.getHeight() / 2f; - float distance = (float) Math.sqrt((cx - px) * (cx - px) + cy * cy * .2f); - float scale = AndroidUtilities.cascade(containeritemst, distance, fullr, child.getHeight() * 1.75f); - if (Float.isNaN(scale)) - scale = 0; - child.setScaleX(scale); - child.setScaleY(scale); + if (child instanceof ImageViewEmoji) { + ImageViewEmoji imageViewEmoji = (ImageViewEmoji) child; + float cx = child.getLeft() + child.getWidth() / 2f, cy = child.getTop() + child.getHeight() / 2f; + float distance = (float) Math.sqrt((cx - px) * (cx - px) + cy * cy * .2f); + float scale = AndroidUtilities.cascade(containeritemst, distance, fullr, child.getHeight() * 1.75f); + if (Float.isNaN(scale)) { + scale = 0; + } + imageViewEmoji.setAnimatedScale(scale); + } } + emojiGridViewContainer.invalidate(); emojiGridView.invalidate(); } @@ -4354,6 +4692,14 @@ public void onAnimationEnd(Animator animation) { public void setDrawBackground(boolean drawBackground) { this.drawBackground = drawBackground; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + contentView.setClipToOutline(drawBackground); + } + if (!drawBackground) { + backgroundView.setVisibility(GONE); + } else { + backgroundView.setVisibility(VISIBLE); + } } public void setRecentReactions(List reactions) { @@ -4405,7 +4751,7 @@ public void setSelected(Long documentId) { } public void setScrimDrawable(AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable scrimDrawable, View drawableParent) { - this.scrimColor = scrimDrawable == null ? 0 : scrimDrawable.getColor(); + this.scrimColor = scrimDrawable == null || scrimDrawable.getColor() == null ? 0 : scrimDrawable.getColor(); this.scrimDrawable = scrimDrawable; this.scrimDrawableParent = drawableParent; if (isAttached && scrimDrawable != null) { @@ -4515,8 +4861,8 @@ public ContentView(Context context) { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure( - MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY) + MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY) ); } @@ -4545,10 +4891,10 @@ protected void dispatchDraw(Canvas canvas) { scale *= 0.8f + 0.2f * (1f - (imageViewEmoji.selected ? .7f : imageViewEmoji.pressedProgress)); } AndroidUtilities.rectTmp2.set( - (int) (AndroidUtilities.rectTmp.centerX() - AndroidUtilities.rectTmp.width() / 2 * scale), - (int) (AndroidUtilities.rectTmp.centerY() - AndroidUtilities.rectTmp.height() / 2 * scale), - (int) (AndroidUtilities.rectTmp.centerX() + AndroidUtilities.rectTmp.width() / 2 * scale), - (int) (AndroidUtilities.rectTmp.centerY() + AndroidUtilities.rectTmp.height() / 2 * scale) + (int) (AndroidUtilities.rectTmp.centerX() - AndroidUtilities.rectTmp.width() / 2 * scale), + (int) (AndroidUtilities.rectTmp.centerY() - AndroidUtilities.rectTmp.height() / 2 * scale), + (int) (AndroidUtilities.rectTmp.centerX() + AndroidUtilities.rectTmp.width() / 2 * scale), + (int) (AndroidUtilities.rectTmp.centerY() + AndroidUtilities.rectTmp.height() / 2 * scale) ); float skew = 1f - (1f - imageViewEmoji.skewAlpha) * (1f - showT); canvas.save(); @@ -4635,8 +4981,8 @@ public SelectStatusDurationDialog(Context context, Runnable parentDialogDismiss, this.parentDialogView = parentDialogView; setContentView( - this.contentView = new ContentView(context), - new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) + this.contentView = new ContentView(context), + new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) ); linearLayoutView = new LinearLayout(context); @@ -4648,10 +4994,10 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto super.onLayout(changed, left, top, right, bottom); getLocationOnScreen(tempLocation); to.set( - tempLocation[0], - tempLocation[1], - tempLocation[0] + getWidth(), - tempLocation[1] + getHeight() + tempLocation[0], + tempLocation[1], + tempLocation[0] + getWidth(), + tempLocation[1] + getHeight() ); AndroidUtilities.lerp(from, to, showT, current); } @@ -4663,32 +5009,32 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto ActionBarMenuItem.addItem(true, false, menuView, 0, LocaleController.getString("SetEmojiStatusUntil1Hour", R.string.SetEmojiStatusUntil1Hour), false, resourcesProvider) - .setOnClickListener(e -> done((int) (System.currentTimeMillis() / 1000 + 60 * 60))); + .setOnClickListener(e -> done((int) (System.currentTimeMillis() / 1000 + 60 * 60))); ActionBarMenuItem.addItem(false, false, menuView, 0, LocaleController.getString("SetEmojiStatusUntil2Hours", R.string.SetEmojiStatusUntil2Hours), false, resourcesProvider) - .setOnClickListener(e -> done((int) (System.currentTimeMillis() / 1000 + 2 * 60 * 60))); + .setOnClickListener(e -> done((int) (System.currentTimeMillis() / 1000 + 2 * 60 * 60))); ActionBarMenuItem.addItem(false, false, menuView, 0, LocaleController.getString("SetEmojiStatusUntil8Hours", R.string.SetEmojiStatusUntil8Hours), false, resourcesProvider) - .setOnClickListener(e -> done((int) (System.currentTimeMillis() / 1000 + 8 * 60 * 60))); + .setOnClickListener(e -> done((int) (System.currentTimeMillis() / 1000 + 8 * 60 * 60))); ActionBarMenuItem.addItem(false, false, menuView, 0, LocaleController.getString("SetEmojiStatusUntil2Days", R.string.SetEmojiStatusUntil2Days), false, resourcesProvider) - .setOnClickListener(e -> done((int) (System.currentTimeMillis() / 1000 + 2 * 24 * 60 * 60))); + .setOnClickListener(e -> done((int) (System.currentTimeMillis() / 1000 + 2 * 24 * 60 * 60))); ActionBarMenuItem.addItem(false, true, menuView, 0, LocaleController.getString("SetEmojiStatusUntilOther", R.string.SetEmojiStatusUntilOther), false, resourcesProvider) - .setOnClickListener(e -> { - if (dateBottomSheet != null) { - return; - } - boolean[] selected = new boolean[1]; - BottomSheet.Builder builder = AlertsCreator.createStatusUntilDatePickerDialog(context, System.currentTimeMillis() / 1000, date -> { - selected[0] = true; - done(date); - }); - builder.setOnPreDismissListener(di -> { - if (!selected[0]) { - animateMenuShow(true, null); + .setOnClickListener(e -> { + if (dateBottomSheet != null) { + return; } - dateBottomSheet = null; + boolean[] selected = new boolean[1]; + BottomSheet.Builder builder = AlertsCreator.createStatusUntilDatePickerDialog(context, System.currentTimeMillis() / 1000, date -> { + selected[0] = true; + done(date); + }); + builder.setOnPreDismissListener(di -> { + if (!selected[0]) { + animateMenuShow(true, null); + } + dateBottomSheet = null; + }); + dateBottomSheet = builder.show(); + animateMenuShow(false, null); }); - dateBottomSheet = builder.show(); - animateMenuShow(false, null); - }); contentView.addView(linearLayoutView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); @@ -4755,8 +5101,8 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto mediaFilter = filter; } imageReceiver.setImage(mediaLocation, mediaFilter, ImageLocation.getForDocument(thumb, document), filter, null, null, thumbDrawable, document.size, null, document, 1); - if (imageViewEmoji.drawable instanceof AnimatedEmojiDrawable && ((AnimatedEmojiDrawable) imageViewEmoji.drawable).canOverrideColor()) { - imageReceiver.setColorFilter(AnimatedEmojiDrawable.isDefaultStatusEmoji((AnimatedEmojiDrawable) imageViewEmoji.drawable) ? premiumStarColorFilter : Theme.chat_animatedEmojiTextColorFilter); + if (imageViewEmoji.drawable instanceof AnimatedEmojiDrawable && (MessageObject.isTextColorEmoji(document) || ((AnimatedEmojiDrawable) imageViewEmoji.drawable).canOverrideColor())) { + imageReceiver.setColorFilter(MessageObject.isTextColorEmoji(document) || AnimatedEmojiDrawable.isDefaultStatusEmoji((AnimatedEmojiDrawable) imageViewEmoji.drawable) ? premiumStarColorFilter : Theme.getAnimatedEmojiColorFilter(resourcesProvider)); } } @@ -4773,6 +5119,7 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto } private boolean done = false; + private void done(Integer date) { if (done) { return; @@ -4864,6 +5211,7 @@ private void prepareBlurBitmap() { private float showT; private boolean showing; private ValueAnimator showAnimator; + private void animateShow(boolean show, Runnable onDone, Runnable onPartly, boolean showback) { if (imageViewEmoji == null) { if (onDone != null) { @@ -4941,6 +5289,7 @@ public void onAnimationEnd(Animator animation) { private float showMenuT; private boolean showingMenu; private ValueAnimator showMenuAnimator; + private void animateMenuShow(boolean show, Runnable onDone) { if (showMenuAnimator != null) { if (showingMenu == show) { @@ -5007,11 +5356,12 @@ public boolean dispatchTouchEvent(@NonNull MotionEvent ev) { public void show() { super.show(); NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.stopAllHeavyOperations, 4); - animateShow(true, null, null,true); + animateShow(true, null, null, true); animateMenuShow(true, null); } private boolean dismissed = false; + @Override public void dismiss() { if (dismissed) { @@ -5031,7 +5381,7 @@ public void setForumIconDrawable(Drawable drawable) { @Override public void setPressed(boolean pressed) { - return; + return; } public void setAnimationsEnabled(boolean aniationsEnabled) { @@ -5039,6 +5389,32 @@ public void setAnimationsEnabled(boolean aniationsEnabled) { } public void setEnterAnimationInProgress(boolean enterAnimationInProgress) { - this.enterAnimationInProgress = enterAnimationInProgress; + if (this.enterAnimationInProgress != enterAnimationInProgress) { + this.enterAnimationInProgress = enterAnimationInProgress; + if (!enterAnimationInProgress) { + AndroidUtilities.forEachViews(emojiGridView, (child) -> { + child.setScaleX(1); + child.setScaleY(1); + }); + for (int i = 0; i < emojiTabs.contentView.getChildCount(); ++i) { + View child = emojiTabs.contentView.getChildAt(i); + child.setScaleX(1); + child.setScaleY(1); + } + emojiTabs.contentView.invalidate(); + } + } + } + + public void setBackgroundDelegate(BackgroundDelegate backgroundDelegate) { + this.backgroundDelegate = backgroundDelegate; + } + + public interface BackgroundDelegate { + void drawRect(Canvas canvas, int left, int top, int right, int bottom, float x, float y); + } + + public boolean prevWindowKeyboardVisible() { + return false; } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SessionBottomSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/SessionBottomSheet.java index 30b61c338c..351c5de0be 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SessionBottomSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SessionBottomSheet.java @@ -262,7 +262,7 @@ public void onClick(View view) { fragment.showDialog(alertDialog); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } }); @@ -310,7 +310,7 @@ private void setAnimation(TLRPC.TL_authorization session, RLottieImageView image } String deviceModel = session.device_model.toLowerCase(); int iconId; - String colorKey, colorKey2; + int colorKey, colorKey2; boolean animation = true; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SessionsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/SessionsActivity.java index 6ce09eddf5..2a0c390279 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SessionsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SessionsActivity.java @@ -197,7 +197,7 @@ public void onItemClick(int id) { @Override public Integer getSelectorColor(int position) { if (position == terminateAllSessionsRow) { - return Theme.multAlpha(getThemedColor(Theme.key_windowBackgroundWhiteRedText2), .1f); + return Theme.multAlpha(getThemedColor(Theme.key_text_RedRegular), .1f); } return getThemedColor(Theme.key_listSelector); } @@ -341,7 +341,7 @@ public boolean supportsPredictiveItemAnimations() { showDialog(alertDialog); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } else if (position >= otherSessionsStartRow && position < otherSessionsEndRow || position >= passwordSessionsStartRow && position < passwordSessionsEndRow || position == currentSessionRow) { if (getParentActivity() == null) { @@ -461,7 +461,7 @@ public boolean supportsPredictiveItemAnimations() { showDialog(alertDialog); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } }); @@ -831,8 +831,8 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { case VIEW_TYPE_TEXT: TextCell textCell = (TextCell) holder.itemView; if (position == terminateAllSessionsRow) { - textCell.setColors(Theme.key_windowBackgroundWhiteRedText2, Theme.key_windowBackgroundWhiteRedText2); - textCell.setTag(Theme.key_windowBackgroundWhiteRedText2); + textCell.setColors(Theme.key_text_RedRegular, Theme.key_text_RedRegular); + textCell.setTag(Theme.key_text_RedRegular); if (currentType == 0) { textCell.setTextAndIcon(LocaleController.getString("TerminateAllSessions", R.string.TerminateAllSessions), R.drawable.msg_block2, false); } else { @@ -853,7 +853,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } else { privacyCell.setText(LocaleController.getString("ClearOtherWebSessionsHelp", R.string.ClearOtherWebSessionsHelp)); } - privacyCell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + privacyCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); } else if (position == otherSessionsTerminateDetail) { if (currentType == 0) { if (sessions.isEmpty()) { @@ -864,16 +864,16 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } else { privacyCell.setText(LocaleController.getString("TerminateWebSessionInfo", R.string.TerminateWebSessionInfo)); } - privacyCell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + privacyCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); } else if (position == passwordSessionsDetailRow) { privacyCell.setText(LocaleController.getString("LoginAttemptsInfo", R.string.LoginAttemptsInfo)); if (otherSessionsTerminateDetail == -1) { - privacyCell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + privacyCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } else { - privacyCell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + privacyCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); } } else if (position == qrCodeDividerRow || position == ttlDivideRow || position == noOtherSessionsRow) { - privacyCell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + privacyCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); privacyCell.setText(""); privacyCell.setFixedSize(12); } @@ -1248,7 +1248,7 @@ public ArrayList getThemeDescriptions() { themeDescriptions.add(new ThemeDescription(emptyView, ThemeDescription.FLAG_PROGRESSBAR, null, null, null, null, Theme.key_progressCircle)); - themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_TEXTCOLOR | ThemeDescription.FLAG_CHECKTAG, new Class[]{TextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteRedText2)); + themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_TEXTCOLOR | ThemeDescription.FLAG_CHECKTAG, new Class[]{TextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_text_RedRegular)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_TEXTCOLOR | ThemeDescription.FLAG_CHECKTAG, new Class[]{TextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueText4)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{TextInfoPrivacyCell.class}, null, null, null, Theme.key_windowBackgroundGrayShadow)); @@ -1263,8 +1263,8 @@ public ArrayList getThemeDescriptions() { themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{SessionCell.class}, new String[]{"detailExTextView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText3)); themeDescriptions.add(new ThemeDescription(undoView, ThemeDescription.FLAG_BACKGROUNDFILTER, null, null, null, null, Theme.key_undo_background)); - themeDescriptions.add(new ThemeDescription(undoView, 0, new Class[]{UndoView.class}, new String[]{"undoImageView"}, null, null, null, Theme.key_windowBackgroundWhiteRedText2)); - themeDescriptions.add(new ThemeDescription(undoView, 0, new Class[]{UndoView.class}, new String[]{"undoTextView"}, null, null, null, Theme.key_windowBackgroundWhiteRedText2)); + themeDescriptions.add(new ThemeDescription(undoView, 0, new Class[]{UndoView.class}, new String[]{"undoImageView"}, null, null, null, Theme.key_text_RedRegular)); + themeDescriptions.add(new ThemeDescription(undoView, 0, new Class[]{UndoView.class}, new String[]{"undoTextView"}, null, null, null, Theme.key_text_RedRegular)); themeDescriptions.add(new ThemeDescription(undoView, 0, new Class[]{UndoView.class}, new String[]{"infoTextView"}, null, null, null, Theme.key_undo_infoColor)); themeDescriptions.add(new ThemeDescription(undoView, 0, new Class[]{UndoView.class}, new String[]{"textPaint"}, null, null, null, Theme.key_undo_infoColor)); themeDescriptions.add(new ThemeDescription(undoView, 0, new Class[]{UndoView.class}, new String[]{"progressPaint"}, null, null, null, Theme.key_undo_infoColor)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SponsoredMessageInfoView.java b/TMessagesProj/src/main/java/org/telegram/ui/SponsoredMessageInfoView.java index 0accce6fa6..d19adbcb08 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SponsoredMessageInfoView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SponsoredMessageInfoView.java @@ -19,6 +19,7 @@ import org.telegram.messenger.browser.Browser; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.LinkSpanDrawable; public class SponsoredMessageInfoView extends FrameLayout { @@ -36,20 +37,21 @@ public SponsoredMessageInfoView(Activity context, Theme.ResourcesProvider resour textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); - TextView description1 = new TextView(context); - description1.setText(LocaleController.getString("SponsoredMessageInfoDescription1", R.string.SponsoredMessageInfoDescription1)); + LinkSpanDrawable.LinksTextView description1 = new LinkSpanDrawable.LinksTextView(context, resourcesProvider); + description1.setText(AndroidUtilities.replaceLinks(LocaleController.getString("SponsoredMessageInfo2Description1"), resourcesProvider)); + description1.setLinkTextColor(Theme.getColor(Theme.key_chat_messageLinkIn, resourcesProvider)); description1.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); description1.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); description1.setLineSpacing(AndroidUtilities.dp(2), 1f); - TextView description2 = new TextView(context); - description2.setText(LocaleController.getString("SponsoredMessageInfoDescription2", R.string.SponsoredMessageInfoDescription2)); + LinkSpanDrawable.LinksTextView description2 = new LinkSpanDrawable.LinksTextView(context); + description2.setText(AndroidUtilities.replaceLinks(LocaleController.getString("SponsoredMessageInfo2Description2"), resourcesProvider)); description2.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); description2.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); description2.setLineSpacing(AndroidUtilities.dp(2), 1f); - TextView description3 = new TextView(context); - description3.setText(LocaleController.getString("SponsoredMessageInfoDescription3", R.string.SponsoredMessageInfoDescription3)); + LinkSpanDrawable.LinksTextView description3 = new LinkSpanDrawable.LinksTextView(context); + description3.setText(AndroidUtilities.replaceLinks(LocaleController.getString("SponsoredMessageInfo2Description3"), resourcesProvider)); description3.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); description3.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); description3.setLineSpacing(AndroidUtilities.dp(2), 1f); @@ -82,22 +84,32 @@ public void onClick(View view) { button.setGravity(Gravity.CENTER_VERTICAL); - TextView description4 = new TextView(context); - description4.setText(LocaleController.getString("SponsoredMessageInfoDescription4", R.string.SponsoredMessageInfoDescription4)); + LinkSpanDrawable.LinksTextView description4 = new LinkSpanDrawable.LinksTextView(context); + description4.setText(AndroidUtilities.replaceLinks(LocaleController.getString("SponsoredMessageInfo2Description4"), resourcesProvider)); description4.setLineSpacing(AndroidUtilities.dp(2), 1f); description4.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); description4.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + textView.setPadding(AndroidUtilities.dp(22), 0, AndroidUtilities.dp(22), 0); linearLayout.addView(textView); + + description1.setPadding(AndroidUtilities.dp(22), 0, AndroidUtilities.dp(22), 0); linearLayout.addView(description1, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, 18, 0, 0)); + + description2.setPadding(AndroidUtilities.dp(22), 0, AndroidUtilities.dp(22), 0); linearLayout.addView(description2, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, 24, 0, 0)); + + description3.setPadding(AndroidUtilities.dp(22), 0, AndroidUtilities.dp(22), 0); linearLayout.addView(description3, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, 24, 0, 0)); - linearLayout.addView(button, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 34, Gravity.CENTER_HORIZONTAL, 0, 14, 0, 0)); + + linearLayout.addView(button, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 34, Gravity.CENTER_HORIZONTAL, 22, 14, 22, 0)); + + description4.setPadding(AndroidUtilities.dp(22), 0, AndroidUtilities.dp(22), 0); linearLayout.addView(description4, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, 14, 0, 0)); ScrollView scrollView = new ScrollView(getContext()); scrollView.addView(linearLayout); - addView(scrollView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 22, 12, 22, 22)); + addView(scrollView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, 12, 0, 22)); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/StatisticActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/StatisticActivity.java index 9b8cdb3a98..8cea04c7bc 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/StatisticActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/StatisticActivity.java @@ -212,7 +212,7 @@ public boolean onFragmentCreate() { if (recentPostsAll.size() > 0) { int lastPostId = recentPostsAll.get(0).counters.msg_id; int count = recentPostsAll.size(); - getMessagesStorage().getMessages(-chat.id, 0, false, count, lastPostId, 0, 0, classGuid, 0, false, 0, 0, true, false); + getMessagesStorage().getMessages(-chat.id, 0, false, count, lastPostId, 0, 0, classGuid, 0, false, 0, 0, true, false, null); } AndroidUtilities.runOnUIThread(() -> { @@ -1648,7 +1648,7 @@ public void recolor() { if (data != null && data.chartData != null && data.chartData.lines != null && data.chartData.lines.size() > 1) { for (int i = 0; i < data.chartData.lines.size(); i++) { int color; - if (data.chartData.lines.get(i).colorKey != null && Theme.hasThemeKey(data.chartData.lines.get(i).colorKey)) { + if (data.chartData.lines.get(i).colorKey >= 0 && Theme.hasThemeKey(data.chartData.lines.get(i).colorKey)) { color = Theme.getColor(data.chartData.lines.get(i).colorKey); } else { boolean darkBackground = ColorUtils.calculateLuminance(Theme.getColor(Theme.key_windowBackgroundWhite)) < 0.5f; @@ -1916,7 +1916,7 @@ private void recolorRecyclerItem(View child) { if (child instanceof ChartCell) { ((ChartCell) child).recolor(); } else if (child instanceof ShadowSectionCell) { - Drawable shadowDrawable = Theme.getThemedDrawable(ApplicationLoader.applicationContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow); + Drawable shadowDrawable = Theme.getThemedDrawableByKey(ApplicationLoader.applicationContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow); Drawable background = new ColorDrawable(Theme.getColor(Theme.key_windowBackgroundGray)); CombinedDrawable combinedDrawable = new CombinedDrawable(background, shadowDrawable, 0, 0); combinedDrawable.setFullsize(true); @@ -2133,7 +2133,7 @@ public ArrayList getThemeDescriptions() { arrayList.add(new ThemeDescription(null, 0, null, null, null, themeDelegate, Theme.key_windowBackgroundGray)); arrayList.add(new ThemeDescription(null, 0, null, null, null, themeDelegate, Theme.key_windowBackgroundGrayShadow)); arrayList.add(new ThemeDescription(null, 0, null, null, null, themeDelegate, Theme.key_windowBackgroundWhiteGreenText2)); - arrayList.add(new ThemeDescription(null, 0, null, null, null, themeDelegate, Theme.key_windowBackgroundWhiteRedText5)); + arrayList.add(new ThemeDescription(null, 0, null, null, null, themeDelegate, Theme.key_text_RedRegular)); arrayList.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_windowBackgroundWhite)); arrayList.add(new ThemeDescription(avatarContainer != null ? avatarContainer.getTitleTextView() : null, ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_player_actionBarTitle)); @@ -2143,8 +2143,8 @@ public ArrayList getThemeDescriptions() { arrayList.add(new ThemeDescription(recyclerListView, ThemeDescription.FLAG_CHECKTAG, new Class[]{ManageChatTextCell.class}, new String[]{"imageView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayIcon)); arrayList.add(new ThemeDescription(recyclerListView, ThemeDescription.FLAG_CHECKTAG, new Class[]{ManageChatTextCell.class}, new String[]{"imageView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueButton)); arrayList.add(new ThemeDescription(recyclerListView, ThemeDescription.FLAG_CHECKTAG, new Class[]{ManageChatTextCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueIcon)); - arrayList.add(new ThemeDescription(recyclerListView, ThemeDescription.FLAG_CHECKTAG, new Class[]{ManageChatTextCell.class}, new String[]{"imageView"}, null, null, null, Theme.key_windowBackgroundWhiteRedText5)); - arrayList.add(new ThemeDescription(recyclerListView, ThemeDescription.FLAG_CHECKTAG, new Class[]{ManageChatTextCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteRedText5)); + arrayList.add(new ThemeDescription(recyclerListView, ThemeDescription.FLAG_CHECKTAG, new Class[]{ManageChatTextCell.class}, new String[]{"imageView"}, null, null, null, Theme.key_text_RedRegular)); + arrayList.add(new ThemeDescription(recyclerListView, ThemeDescription.FLAG_CHECKTAG, new Class[]{ManageChatTextCell.class}, new String[]{"textView"}, null, null, null, Theme.key_text_RedRegular)); arrayList.add(new ThemeDescription(recyclerListView, ThemeDescription.FLAG_CELLBACKGROUNDCOLOR, new Class[]{ManageChatUserCell.class, ManageChatTextCell.class, HeaderCell.class, TextView.class, PeopleNearbyActivity.HintInnerCell.class}, null, null, null, Theme.key_windowBackgroundWhite)); if (isMegagroup) { @@ -2197,7 +2197,7 @@ public ArrayList getThemeDescriptions() { public static void putColorFromData(ChartViewData chartViewData, ArrayList arrayList, ThemeDescription.ThemeDescriptionDelegate themeDelegate) { if (chartViewData != null && chartViewData.chartData != null) { for (ChartData.Line l : chartViewData.chartData.lines) { - if (l.colorKey != null) { + if (l.colorKey >= 0) { if (!Theme.hasThemeKey(l.colorKey)) { Theme.setColor(l.colorKey, Theme.isCurrentThemeNight() ? l.colorDark : l.color, false); Theme.setDefaultColor(l.colorKey, l.color); @@ -2404,12 +2404,12 @@ public void setData(OverviewChannelData data) { primary[3].setText(data.sharesPrimary); secondary[0].setText(data.followersSecondary); - secondary[0].setTag(data.followersUp ? Theme.key_windowBackgroundWhiteGreenText2 : Theme.key_windowBackgroundWhiteRedText5); + secondary[0].setTag(data.followersUp ? Theme.key_windowBackgroundWhiteGreenText2 : Theme.key_text_RedRegular); secondary[1].setText(""); secondary[2].setText(data.viewsSecondary); - secondary[2].setTag(data.viewsUp ? Theme.key_windowBackgroundWhiteGreenText2 : Theme.key_windowBackgroundWhiteRedText5); + secondary[2].setTag(data.viewsUp ? Theme.key_windowBackgroundWhiteGreenText2 : Theme.key_text_RedRegular); secondary[3].setText(data.sharesSecondary); - secondary[3].setTag(data.sharesUp ? Theme.key_windowBackgroundWhiteGreenText2 : Theme.key_windowBackgroundWhiteRedText5); + secondary[3].setTag(data.sharesUp ? Theme.key_windowBackgroundWhiteGreenText2 : Theme.key_text_RedRegular); title[0].setText(data.followersTitle); title[1].setText(data.notificationsTitle); @@ -2426,15 +2426,15 @@ public void setData(OverviewChatData data) { primary[3].setText(data.postingMembersPrimary); secondary[0].setText(data.membersSecondary); - secondary[0].setTag(data.membersUp ? Theme.key_windowBackgroundWhiteGreenText2 : Theme.key_windowBackgroundWhiteRedText5); + secondary[0].setTag(data.membersUp ? Theme.key_windowBackgroundWhiteGreenText2 : Theme.key_text_RedRegular); secondary[1].setText(data.messagesSecondary); - secondary[1].setTag(data.messagesUp ? Theme.key_windowBackgroundWhiteGreenText2 : Theme.key_windowBackgroundWhiteRedText5); + secondary[1].setTag(data.messagesUp ? Theme.key_windowBackgroundWhiteGreenText2 : Theme.key_text_RedRegular); secondary[2].setText(data.viewingMembersSecondary); - secondary[2].setTag(data.viewingMembersUp ? Theme.key_windowBackgroundWhiteGreenText2 : Theme.key_windowBackgroundWhiteRedText5); + secondary[2].setTag(data.viewingMembersUp ? Theme.key_windowBackgroundWhiteGreenText2 : Theme.key_text_RedRegular); secondary[3].setText(data.postingMembersSecondary); - secondary[3].setTag(data.postingMembersUp ? Theme.key_windowBackgroundWhiteGreenText2 : Theme.key_windowBackgroundWhiteRedText5); + secondary[3].setTag(data.postingMembersUp ? Theme.key_windowBackgroundWhiteGreenText2 : Theme.key_text_RedRegular); title[0].setText(data.membersTitle); title[1].setText(data.messagesTitle); @@ -2449,7 +2449,7 @@ private void updateColors() { primary[i].setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); title[i].setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2)); - String colorKey = (String) secondary[i].getTag(); + Integer colorKey = (Integer) secondary[i].getTag(); if (colorKey != null) { secondary[i].setTextColor(Theme.getColor(colorKey)); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/StickersActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/StickersActivity.java index 3ce00d6633..923027cecc 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/StickersActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/StickersActivity.java @@ -94,7 +94,7 @@ import org.telegram.ui.Components.BulletinFactory; import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.EmojiPacksAlert; -import org.telegram.ui.Components.EmojiView; +import org.telegram.ui.Components.ItemOptions; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.NumberTextView; import org.telegram.ui.Components.Premium.PremiumFeatureBottomSheet; @@ -915,14 +915,7 @@ public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { loopRow = -1; loopInfoRow = -1; - - if (currentType == MediaDataController.TYPE_EMOJIPACKS) { - suggestAnimatedEmojiRow = rowCount++; - suggestAnimatedEmojiInfoRow = rowCount++; - } else { - suggestAnimatedEmojiRow = -1; - suggestAnimatedEmojiInfoRow = -1; - } + archivedRow = -1; if (currentType == MediaDataController.TYPE_IMAGE) { featuredRow = rowCount++; @@ -941,7 +934,7 @@ public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { masksRow = -1; emojiPacksRow = -1; - if (mediaDataController.getArchivedStickersCount(currentType) != 0 && currentType != MediaDataController.TYPE_EMOJIPACKS) { + if (mediaDataController.getArchivedStickersCount(currentType) != 0) { boolean inserted = archivedRow == -1; archivedRow = rowCount++; @@ -963,6 +956,14 @@ public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { } } + if (currentType == MediaDataController.TYPE_EMOJIPACKS) { + suggestAnimatedEmojiRow = rowCount++; + suggestAnimatedEmojiInfoRow = rowCount++; + } else { + suggestAnimatedEmojiRow = -1; + suggestAnimatedEmojiInfoRow = -1; + } + if (currentType == MediaDataController.TYPE_IMAGE) { reactionsDoubleTapRow = rowCount++; } else { @@ -1317,7 +1318,7 @@ public boolean didCopy() { if (which == MENU_DELETE) { TextView button = (TextView) dialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } break; @@ -1560,7 +1561,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } case TYPE_SHADOW: if (position == stickersShadowRow) { - holder.itemView.setBackground(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + holder.itemView.setBackground(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } break; case TYPE_SWITCH: @@ -1705,70 +1706,29 @@ protected void onRemoveButtonClick() { stickerSetCell.setOnOptionsClick(v -> { StickerSetCell cell = (StickerSetCell) v.getParent(); TLRPC.TL_messages_stickerSet stickerSet = cell.getStickersSet(); - AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); - builder.setTitle(stickerSet.set.title); - int[] options; - CharSequence[] items; - int[] icons; + ItemOptions options = ItemOptions.makeOptions(StickersActivity.this, cell); + options.add(R.drawable.msg_archive, LocaleController.getString("StickersHide", R.string.StickersHide), () -> processSelectionOption(MENU_ARCHIVE, stickerSet)); if (stickerSet.set.official) { - options = new int[]{MENU_ARCHIVE, 4}; - items = new CharSequence[]{ - LocaleController.getString("StickersHide", R.string.StickersHide), - LocaleController.getString("StickersReorder", R.string.StickersReorder) - }; - icons = new int[]{R.drawable.msg_archive, R.drawable.msg_reorder}; + options.add(R.drawable.msg_reorder, LocaleController.getString("StickersReorder", R.string.StickersReorder), () -> processSelectionOption(4, stickerSet)); } else { if (NekoConfig.enableStickerPin.Bool() && currentType == MediaDataController.TYPE_IMAGE) { - options = new int[]{MENU_ARCHIVE, 3, 4, 2, MENU_DELETE, MENU_TOGGLE_PIN}; - items = new CharSequence[]{ - LocaleController.getString("StickersHide", R.string.StickersHide), - LocaleController.getString("StickersCopy", R.string.StickersCopy), - LocaleController.getString("StickersReorder", R.string.StickersReorder), - LocaleController.getString("StickersShare", R.string.StickersShare), - LocaleController.getString("StickersRemove", R.string.StickersRemove), - PinnedStickerHelper.getInstance(currentAccount).isPinned(stickerSet.set.id) ? - LocaleController.getString("UnpinSticker", R.string.UnpinSticker) : - LocaleController.getString("PinSticker", R.string.PinSticker) - }; - icons = new int[]{ - R.drawable.msg_archive, - R.drawable.msg_link, - R.drawable.msg_reorder, - R.drawable.msg_share, - R.drawable.msg_delete, - R.drawable.msg_pin - }; - } else { - options = new int[]{MENU_ARCHIVE, 3, 4, 2, MENU_DELETE}; - items = new CharSequence[]{ - LocaleController.getString("StickersHide", R.string.StickersHide), - LocaleController.getString("StickersCopy", R.string.StickersCopy), - LocaleController.getString("StickersReorder", R.string.StickersReorder), - LocaleController.getString("StickersShare", R.string.StickersShare), - LocaleController.getString("StickersRemove", R.string.StickersRemove) - }; - icons = new int[]{ - R.drawable.msg_archive, - R.drawable.msg_link, - R.drawable.msg_reorder, - R.drawable.msg_share, - R.drawable.msg_delete - }; + options.add(R.drawable.msg_pin, PinnedStickerHelper.getInstance(currentAccount).isPinned(stickerSet.set.id) ? + LocaleController.getString("UnpinSticker", R.string.UnpinSticker) : + LocaleController.getString("PinSticker", R.string.PinSticker), () -> processSelectionOption(MENU_TOGGLE_PIN, stickerSet)); } + options.add(R.drawable.msg_archive, LocaleController.getString("StickersHide", R.string.StickersHide), () -> processSelectionOption(MENU_ARCHIVE, stickerSet)); + options.add(R.drawable.msg_link, LocaleController.getString("StickersCopy", R.string.StickersCopy), () -> processSelectionOption(3, stickerSet)); + options.add(R.drawable.msg_reorder, LocaleController.getString("StickersReorder", R.string.StickersReorder), () -> processSelectionOption(4, stickerSet)); + options.add(R.drawable.msg_share, LocaleController.getString("StickersShare", R.string.StickersShare), () -> processSelectionOption(2, stickerSet)); + options.add(R.drawable.msg_delete, LocaleController.getString("StickersRemove", R.string.StickersRemove), true, () -> processSelectionOption(MENU_DELETE, stickerSet)); } - builder.setItems(items, icons, (dialog, which) -> processSelectionOption(options[which], stickerSet)); - - AlertDialog dialog = builder.create(); - showDialog(dialog); - - if (options[options.length - 1] == MENU_DELETE) { - dialog.setItemColor(items.length - 1, Theme.getColor(Theme.key_dialogTextRed), Theme.getColor(Theme.key_dialogRedIcon)); - } + options.setMinWidth(190); + options.show(); }); break; case TYPE_INFO: view = new TextInfoPrivacyCell(mContext); - view.setBackground(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + view.setBackground(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); break; case TYPE_TEXT_AND_VALUE: view = new TextCell(mContext); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Storage/CacheModel.java b/TMessagesProj/src/main/java/org/telegram/ui/Storage/CacheModel.java index 73136c0c92..f851ce71eb 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Storage/CacheModel.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Storage/CacheModel.java @@ -19,6 +19,7 @@ public class CacheModel { public final ArrayList documents = new ArrayList<>(); public final ArrayList music = new ArrayList<>(); public final ArrayList voice = new ArrayList<>(); + public final ArrayList stories = new ArrayList<>(); private final HashSet dialogIdsTmp = new HashSet<>(); @@ -32,41 +33,43 @@ public class CacheModel { public boolean allDocumentsSelected; public boolean allMusicSelected; public boolean allVoiceSelected; + public boolean allStoriesSelected; public long photosSelectedSize; public long videosSelectedSize; public long documentsSelectedSize; public long musicSelectedSize; public long voiceSelectedSize; + public long storiesSelectedSize; public CacheModel(boolean isDialog) { this.isDialog = isDialog; } public void add(int addToType, FileInfo fileInfo) { - if (addToType == CacheControlActivity.TYPE_PHOTOS) { - media.add(fileInfo); - } else if (addToType == CacheControlActivity.TYPE_VIDEOS) { - media.add(fileInfo); - } else if (addToType == CacheControlActivity.TYPE_DOCUMENTS) { - documents.add(fileInfo); - } else if (addToType == CacheControlActivity.TYPE_MUSIC) { - music.add(fileInfo); - } else if (addToType == CacheControlActivity.TYPE_VOICE) { - voice.add(fileInfo); - } + getListByType(addToType).add(fileInfo); } - private void remove(int type, FileInfo fileInfo) { + private ArrayList getListByType(int type) { if (type == CacheControlActivity.TYPE_PHOTOS) { - media.remove(fileInfo); + return media; } else if (type == CacheControlActivity.TYPE_VIDEOS) { - media.remove(fileInfo); + return media; } else if (type == CacheControlActivity.TYPE_DOCUMENTS) { - documents.remove(fileInfo); + return documents; } else if (type == CacheControlActivity.TYPE_MUSIC) { - music.remove(fileInfo); + return music; } else if (type == CacheControlActivity.TYPE_VOICE) { - voice.remove(fileInfo); + return voice; + } else if (type == CacheControlActivity.TYPE_STORIES) { + return stories; + } + return null; + } + + private void remove(int type, FileInfo fileInfo) { + ArrayList list = getListByType(type); + if (list != null) { + list.remove(fileInfo); } } @@ -80,6 +83,7 @@ public void sortBySize() { sort(documents); sort(music); sort(voice); + sort(stories); } private void sort(ArrayList entities) { @@ -123,6 +127,8 @@ private void checkAllFilesSelected(int type, boolean added) { allMusicSelected = false; } else if (type == CacheControlActivity.TYPE_VOICE) { allVoiceSelected = false; + } else if (type == CacheControlActivity.TYPE_STORIES) { + allStoriesSelected = false; } } else { if (type == CacheControlActivity.TYPE_PHOTOS) { @@ -135,6 +141,8 @@ private void checkAllFilesSelected(int type, boolean added) { allMusicSelected = checkAllFilesSelectedInArray(type, music); } else if (type == CacheControlActivity.TYPE_VOICE) { allVoiceSelected = checkAllFilesSelectedInArray(type, voice); + } else if (type == CacheControlActivity.TYPE_STORIES) { + allStoriesSelected = checkAllFilesSelectedInArray(type, stories); } } } @@ -309,6 +317,9 @@ public void allFilesSelcetedByType(int type, boolean selected) { } else if (type == CacheControlActivity.TYPE_VOICE) { files = voice; allVoiceSelected = selected; + } else if (type == CacheControlActivity.TYPE_STORIES) { + files = stories; + allStoriesSelected = selected; } if (files != null) { for (int i = 0; i < files.size(); i++) { @@ -341,6 +352,8 @@ private void incSize(FileInfo fileInfo, boolean inc) { musicSelectedSize += size; } else if (fileInfo.type == CacheControlActivity.TYPE_VOICE) { voiceSelectedSize += size; + } else if (fileInfo.type == CacheControlActivity.TYPE_STORIES) { + storiesSelectedSize += size; } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/CloseFriendsBadge.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/CloseFriendsBadge.java new file mode 100644 index 0000000000..afdaa612f8 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/CloseFriendsBadge.java @@ -0,0 +1,37 @@ +package org.telegram.ui.Stories; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.os.Build; +import android.view.View; +import android.widget.ImageView; + +import androidx.annotation.RequiresApi; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.R; +import org.telegram.ui.Components.ScaleStateListAnimator; + +public class CloseFriendsBadge extends ImageView { + + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + + public CloseFriendsBadge(Context context) { + super(context); + paint.setColor(0xff7BD11F); + setScaleType(ScaleType.CENTER_INSIDE); + //setPadding(AndroidUtilities.dp(12), AndroidUtilities.dp(12), AndroidUtilities.dp(12), AndroidUtilities.dp(12)); + setImageResource(R.drawable.msg_mini_closefriends); + ScaleStateListAnimator.apply(this); + } + + @Override + protected void onDraw(Canvas canvas) { + float cx = getMeasuredWidth() / 2f; + float cy = getMeasuredHeight() / 2f; + float r = AndroidUtilities.dp(11); + canvas.drawCircle(cx, cy, r, paint); + super.onDraw(canvas); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/DarkThemeResourceProvider.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/DarkThemeResourceProvider.java new file mode 100644 index 0000000000..593170db34 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/DarkThemeResourceProvider.java @@ -0,0 +1,199 @@ +package org.telegram.ui.Stories; + +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.drawable.Drawable; +import android.util.SparseIntArray; + +import androidx.core.graphics.ColorUtils; + +import com.google.android.exoplayer2.util.Log; + +import org.checkerframework.checker.units.qual.C; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.ActionBar.ThemeColors; + +import java.util.HashSet; +import java.util.Objects; + +public class DarkThemeResourceProvider implements Theme.ResourcesProvider { + + HashSet debugUnknownKeys = new HashSet<>(); + SparseIntArray sparseIntArray = new SparseIntArray(); + + Paint dividerPaint = new Paint(); + Paint actionPaint; + ColorFilter animatedEmojiColorFilter; + + public DarkThemeResourceProvider() { + + sparseIntArray.put(Theme.key_dialogIcon, Color.WHITE); + sparseIntArray.put(Theme.key_text_RedBold, 0xFFDB4646); + sparseIntArray.put(Theme.key_dialogButton, -10177041); + sparseIntArray.put(Theme.key_chat_gifSaveHintBackground, ColorUtils.blendARGB(Color.BLACK, Color.WHITE, 0.2f)); + sparseIntArray.put(Theme.key_dialogSearchHint, ColorUtils.blendARGB(Color.BLACK, Color.WHITE, 0.5f)); + sparseIntArray.put(Theme.key_dialogSearchIcon, ColorUtils.blendARGB(Color.BLACK, Color.WHITE, 0.5f)); + sparseIntArray.put(Theme.key_dialogSearchBackground, ColorUtils.setAlphaComponent(Color.WHITE, 17)); + sparseIntArray.put(Theme.key_actionBarDefaultSubmenuItem, Color.WHITE); + sparseIntArray.put(Theme.key_actionBarDefaultSubmenuItemIcon, Color.WHITE); + sparseIntArray.put(Theme.key_listSelector, 234881023); + sparseIntArray.put(Theme.key_dialogButtonSelector, 436207615); + sparseIntArray.put(Theme.key_chat_emojiPanelTrendingTitle, Color.WHITE); + sparseIntArray.put(Theme.key_groupcreate_sectionText, 0x99ffffff); + sparseIntArray.put(Theme.key_windowBackgroundWhiteHintText, ColorUtils.blendARGB(Color.BLACK, Color.WHITE, 0.5f)); + sparseIntArray.put(Theme.key_dialogTextHint, ColorUtils.blendARGB(Color.BLACK, Color.WHITE, 0.5f)); + sparseIntArray.put(Theme.key_sheet_scrollUp, ColorUtils.blendARGB(Color.BLACK, Color.WHITE, 0.2f)); + + sparseIntArray.put(Theme.key_dialogTextBlack, -592138); + sparseIntArray.put(Theme.key_windowBackgroundWhiteBlueIcon, Color.WHITE); + sparseIntArray.put(Theme.key_chat_emojiPanelStickerSetName, 0x73ffffff); + sparseIntArray.put(Theme.key_chat_emojiPanelStickerSetNameIcon, 0x73ffffff); + sparseIntArray.put(Theme.key_chat_TextSelectionCursor, Color.WHITE); + sparseIntArray.put(Theme.key_featuredStickers_addedIcon, Color.WHITE); + sparseIntArray.put(Theme.key_actionBarDefault, Color.WHITE); + sparseIntArray.put(Theme.key_chat_gifSaveHintText, Color.WHITE); + sparseIntArray.put(Theme.key_chat_messagePanelSend, Color.WHITE); + + sparseIntArray.put(Theme.key_chat_emojiSearchBackground, ColorUtils.setAlphaComponent(Color.WHITE, 30)); + sparseIntArray.put(Theme.key_chat_emojiPanelBackground, 0xc0000000); + + sparseIntArray.put(Theme.key_dialogSearchHint, ColorUtils.blendARGB(Color.BLACK, Color.WHITE, 0.5f)); + sparseIntArray.put(Theme.key_dialogSearchBackground, ColorUtils.setAlphaComponent(Color.WHITE, 17)); + sparseIntArray.put(Theme.key_windowBackgroundWhiteGrayText, ColorUtils.setAlphaComponent(Color.WHITE, 127)); + sparseIntArray.put(Theme.key_chat_messagePanelVoiceLockBackground, -14606046); + sparseIntArray.put(Theme.key_chat_messagePanelVoiceLock, -1); + sparseIntArray.put(Theme.key_chat_recordedVoiceDot, -1221292); + sparseIntArray.put(Theme.key_chat_messagePanelVoiceDelete, -1); + sparseIntArray.put(Theme.key_chat_recordedVoiceBackground, 0xFF1A9CFF); + sparseIntArray.put(Theme.key_chat_messagePanelVoiceDuration, -1); + sparseIntArray.put(Theme.key_chat_recordTime, 2030043135); + sparseIntArray.put(Theme.key_chat_recordVoiceCancel, -10638868); + sparseIntArray.put(Theme.key_chat_messagePanelCursor, -1); + sparseIntArray.put(Theme.key_chat_messagePanelHint, 1694498815); + sparseIntArray.put(Theme.key_chat_inTextSelectionHighlight, -1515107571); + sparseIntArray.put(Theme.key_chat_messageLinkOut, -5316609); + sparseIntArray.put(Theme.key_chat_messagePanelText, -1); + sparseIntArray.put(Theme.key_chat_messagePanelIcons, Color.WHITE); + sparseIntArray.put(Theme.key_chat_messagePanelIcons, Color.WHITE); + sparseIntArray.put(Theme.key_chat_messagePanelBackground, ColorUtils.setAlphaComponent(Color.BLACK, 122)); + sparseIntArray.put(Theme.key_dialogBackground, 0xFF1F1F1F); + sparseIntArray.put(Theme.key_dialog_inlineProgressBackground, -15393241); + sparseIntArray.put(Theme.key_windowBackgroundWhite, -15198183); + sparseIntArray.put(Theme.key_windowBackgroundWhiteBlackText, Color.WHITE); + sparseIntArray.put(Theme.key_chat_emojiPanelEmptyText, -8553090); + sparseIntArray.put(Theme.key_progressCircle, -10177027); + sparseIntArray.put(Theme.key_chat_emojiPanelStickerPackSelector, 181267199); + sparseIntArray.put(Theme.key_chat_emojiSearchIcon, ColorUtils.setAlphaComponent(Color.WHITE, 125)); + sparseIntArray.put(Theme.key_chat_emojiPanelIcon, 0x80ffffff); + sparseIntArray.put(Theme.key_chat_emojiBottomPanelIcon, ColorUtils.setAlphaComponent(Color.WHITE, 125)); + sparseIntArray.put(Theme.key_chat_emojiPanelIconSelected, 0xffffffff); + sparseIntArray.put(Theme.key_chat_emojiPanelStickerPackSelectorLine, -10177041); + sparseIntArray.put(Theme.key_chat_emojiPanelShadowLine, ColorUtils.setAlphaComponent(Color.BLACK, 30)); + sparseIntArray.put(Theme.key_chat_emojiPanelBackspace, ColorUtils.setAlphaComponent(Color.WHITE, 125)); + sparseIntArray.put(Theme.key_divider, 0xFF000000); + sparseIntArray.put(Theme.key_dialogFloatingButton, -10177041); + sparseIntArray.put(Theme.key_dialogFloatingIcon, 0xffffffff); + sparseIntArray.put(Theme.key_graySection, 0xFF292929); + sparseIntArray.put(Theme.key_graySectionText, -8158332); + sparseIntArray.put(Theme.key_windowBackgroundGray, 0xFF1F1F1F); + sparseIntArray.put(Theme.key_windowBackgroundWhiteBlueHeader, -9652488); + sparseIntArray.put(Theme.key_windowBackgroundWhiteGrayText3, ColorUtils.blendARGB(Color.WHITE, Color.BLACK, 0.3f)); + sparseIntArray.put(Theme.key_undo_background, 0xFF212426); + sparseIntArray.put(Theme.key_undo_cancelColor, 0xFF8BC8F5); + sparseIntArray.put(Theme.key_undo_infoColor, Color.WHITE); + sparseIntArray.put(Theme.key_actionBarDefaultSubmenuSeparator, 0xF2151515); + sparseIntArray.put(Theme.key_chat_emojiPanelStickerSetNameHighlight, Color.WHITE); + + sparseIntArray.put(Theme.key_switchTrack, 0xFF636363); + sparseIntArray.put(Theme.key_switchTrackChecked, 0xFF1A9CFF); + sparseIntArray.put(Theme.key_dialogRoundCheckBox, -10177041); + sparseIntArray.put(Theme.key_dialogTextBlue2, 0xFF1A9CFF); + sparseIntArray.put(Theme.key_color_red, -832444); + sparseIntArray.put(Theme.key_checkboxDisabled, 0xff626262); + sparseIntArray.put(Theme.key_dialogRoundCheckBoxCheck, 0xffffffff); + sparseIntArray.put(Theme.key_dialogButtonSelector, 436207615); + sparseIntArray.put(Theme.key_groupcreate_spanBackground, -13816531); + sparseIntArray.put(Theme.key_groupcreate_spanDelete, 0xffffffff); + sparseIntArray.put(Theme.key_groupcreate_spanText, -657931); + sparseIntArray.put(Theme.key_avatar_text, 0xffffffff); + sparseIntArray.put(Theme.key_groupcreate_hintText, -8553091); + sparseIntArray.put(Theme.key_groupcreate_cursor, -10177041); + sparseIntArray.put(Theme.key_actionBarDefaultSubmenuBackground, 0xF21F1F1F); + sparseIntArray.put(Theme.key_actionBarDefaultSelector, 385875967); + sparseIntArray.put(Theme.key_fastScrollInactive, -12500671); + sparseIntArray.put(Theme.key_fastScrollActive, -13133079); + sparseIntArray.put(Theme.key_fastScrollText, 0xffffffff); + sparseIntArray.put(Theme.key_featuredStickers_addButton, 0xFF1A9CFF); + sparseIntArray.put(Theme.key_dialogTextLink, -10177041); + sparseIntArray.put(Theme.key_dialogSearchText, Color.WHITE); + sparseIntArray.put(Theme.key_chat_messageLinkIn, 0xFF46A3EB); + sparseIntArray.put(Theme.key_dialogTextGray2, -8553091); + + sparseIntArray.put(Theme.key_sheet_other, 1140850687); + + sparseIntArray.put(Theme.key_chat_outBubble, ColorUtils.blendARGB(Color.BLACK, Color.WHITE, 0.4f)); + sparseIntArray.put(Theme.key_chat_outBubbleGradient1, 0); + sparseIntArray.put(Theme.key_chat_outBubbleGradient2, 0); + sparseIntArray.put(Theme.key_chat_outBubbleGradient3, 0); + sparseIntArray.put(Theme.key_chat_textSelectBackground, ColorUtils.blendARGB(Color.BLACK, Color.WHITE, 0.6f)); + + appendColors(); + dividerPaint.setColor(getColor(Theme.key_divider)); + } + + public void appendColors() { + + } + + @Override + public int getColor(int key) { + int index = sparseIntArray.indexOfKey(key); + if (index >= 0) { + return sparseIntArray.valueAt(index); + } + + if (!debugUnknownKeys.contains(key)) { + debugUnknownKeys.add(key); + } + return Theme.getColor(key); + } + + Drawable msgOutMedia; + + @Override + public Drawable getDrawable(String drawableKey) { + if (Objects.equals(drawableKey, Theme.key_drawable_msgOutMedia)) { + if (msgOutMedia == null) { + msgOutMedia = new Theme.MessageDrawable(Theme.MessageDrawable.TYPE_MEDIA, true, false, this); + } + return msgOutMedia; + } + return Theme.ResourcesProvider.super.getDrawable(drawableKey); + } + + @Override + public Paint getPaint(String paintKey) { + if (paintKey.equals(Theme.key_paint_divider)) { + return dividerPaint; + } + if (paintKey.equals(Theme.key_paint_chatActionBackground)) { + if (actionPaint == null) { + actionPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + actionPaint.setColor(ColorUtils.blendARGB(Color.BLACK, Color.WHITE, 0.1f)); + } + return actionPaint; + } + return Theme.getThemePaint(paintKey); + } + + @Override + public ColorFilter getAnimatedEmojiColorFilter() { + if (animatedEmojiColorFilter == null) { + animatedEmojiColorFilter = new PorterDuffColorFilter(getColor(Theme.key_windowBackgroundWhiteBlackText), PorterDuff.Mode.SRC_IN); + } + return animatedEmojiColorFilter; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/DialogStoriesCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/DialogStoriesCell.java new file mode 100644 index 0000000000..b981564879 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/DialogStoriesCell.java @@ -0,0 +1,1649 @@ +package org.telegram.ui.Stories; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.text.Layout; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.TextUtils; +import android.text.style.ClickableSpan; +import android.view.Gravity; +import android.view.HapticFeedbackConstants; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.OvershootInterpolator; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; +import androidx.core.graphics.ColorUtils; +import androidx.recyclerview.widget.DefaultItemAnimator; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.BotWebViewVibrationEffect; +import org.telegram.messenger.Emoji; +import org.telegram.messenger.ImageReceiver; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.NotificationsController; +import org.telegram.messenger.R; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.Utilities; +import org.telegram.tgnet.TLObject; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.ActionBar; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.SimpleTextView; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AnimatedTextView; +import org.telegram.ui.Components.AvatarDrawable; +import org.telegram.ui.Components.ButtonBounce; +import org.telegram.ui.Components.CombinedDrawable; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.EllipsizeSpanAnimator; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.ListView.AdapterWithDiffUtils; +import org.telegram.ui.Components.RadialProgress; +import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.Components.TypefaceSpan; +import org.telegram.ui.PremiumPreviewFragment; +import org.telegram.ui.Stories.recorder.HintView2; +import org.telegram.ui.Stories.recorder.StoryRecorder; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Objects; + +public class DialogStoriesCell extends FrameLayout implements NotificationCenter.NotificationCenterDelegate { + + public final static int TYPE_DIALOGS = 0; + public final static int TYPE_ARCHIVE= 1; + private static final float COLLAPSED_DIS = 18; + + private final int type; + public final static int HEIGHT_IN_DP = 81; + private final Drawable addNewStoryDrawable; + private final DefaultItemAnimator miniItemAnimator; + private int addNewStoryLastColor; + int currentAccount; + public RecyclerListView recyclerListView; + public RadialProgress radialProgress; + + RecyclerListView listViewMini; + + StoriesController storiesController; + ArrayList oldItems = new ArrayList<>(); + ArrayList oldMiniItems = new ArrayList<>(); + ArrayList items = new ArrayList<>(); + ArrayList miniItems = new ArrayList<>(); + Adapter adapter = new Adapter(false); + Adapter miniAdapter = new Adapter(true); + Paint grayPaint = new Paint(); + Paint addCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + private HintView2 premiumHint; + + public static final int COLLAPSED_SIZE = 28; + + boolean updateOnIdleState; + private int clipTop; + + private final static int EXPANDED_STATE = 0; + private final static int TRANSITION_STATE = 1; + private final static int COLLAPSED_STATE = 2; + public int currentCellWidth; + + float collapsedProgress = -1; + + int currentState = -1; + + ArrayList viewsDrawInParent = new ArrayList<>(); + ArrayList animateToDialogIds = new ArrayList<>(); + DefaultItemAnimator itemAnimator; + LinearLayoutManager layoutManager; + AnimatedTextView titleView; + boolean progressWasDrawn; + boolean drawCircleForce; + ArrayList afterNextLayout = new ArrayList<>(); + private float collapsedProgress1 = -1; + private float collapsedProgress2; + BaseFragment fragment; + private CharSequence currentTitle; + private boolean hasOverlayText; + private int overlayTextId; + private SpannableStringBuilder uploadingString; + private ValueAnimator textAnimator; + private Runnable animationRunnable; + public boolean allowGlobalUpdates = true; + private boolean lastUploadingCloseFriends; + private float overscrollPrgoress; + private int overscrollSelectedPosition; + private StoryCell overscrollSelectedView; + private ActionBar actionBar; + + public DialogStoriesCell(@NonNull Context context, BaseFragment fragment, int currentAccount, int type) { + super(context); + this.type = type; + this.currentAccount = currentAccount; + this.fragment = fragment; + storiesController = MessagesController.getInstance(currentAccount).getStoriesController(); + recyclerListView = new RecyclerListView(context) { + @Override + public boolean drawChild(Canvas canvas, View child, long drawingTime) { + if (viewsDrawInParent.contains(child)) { + return true; + } + return super.drawChild(canvas, child, drawingTime); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + for (int i = 0; i < afterNextLayout.size(); i++) { + afterNextLayout.get(i).run(); + } + afterNextLayout.clear(); + } + }; + recyclerListView.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { + super.onScrolled(recyclerView, dx, dy); + invalidate(); + checkLoadMore(); + if (premiumHint != null) { + premiumHint.hide(); + } + } + }); + itemAnimator = new DefaultItemAnimator(); + itemAnimator.setDelayAnimations(false); + itemAnimator.setDurations(150); + itemAnimator.setSupportsChangeAnimations(false); + recyclerListView.setItemAnimator(itemAnimator); + recyclerListView.setLayoutManager(layoutManager = new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)); + RecyclerListView.OnItemClickListener itemClickListener = (view, position) -> { + StoryCell cell = (StoryCell) view; + openStoryForCell(cell); + }; + recyclerListView.setOnItemClickListener(itemClickListener); + recyclerListView.setOnItemLongClickListener(new RecyclerListView.OnItemLongClickListener() { + @Override + public boolean onItemClick(View view, int position) { + if (collapsedProgress == 0 && overscrollPrgoress == 0) { + onUserLongPressed(view, ((StoryCell) view).dialogId); + } + return false; + } + }); + + recyclerListView.setAdapter(adapter); + addView(recyclerListView); + + titleView = new AnimatedTextView(getContext(), true, true, false); + titleView.setGravity(Gravity.LEFT); + titleView.setTextColor(getTextColor()); + titleView.setEllipsizeByGradient(true); + titleView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + titleView.setPadding(0, AndroidUtilities.dp(8), 0, AndroidUtilities.dp(8)); + titleView.setTextSize(AndroidUtilities.dp(!AndroidUtilities.isTablet() && getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE ? 18 : 20)); + addView(titleView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + ellipsizeSpanAnimator.addView(titleView); + + titleView.setAlpha(0f); + + grayPaint.setColor(0xffD5DADE); + grayPaint.setStyle(Paint.Style.STROKE); + grayPaint.setStrokeWidth(AndroidUtilities.dp(1)); + addNewStoryDrawable = ContextCompat.getDrawable(getContext(), R.drawable.msg_mini_addstory); + + listViewMini = new RecyclerListView(getContext()) { + @Override + protected void dispatchDraw(Canvas canvas) { + viewsDrawInParent.clear(); + for (int i = 0; i < getChildCount(); i++) { + StoryCell storyCell = (StoryCell) getChildAt(i); + storyCell.position = getChildAdapterPosition(storyCell); + storyCell.drawInParent = true; + storyCell.isFirst = storyCell.position == 0; + storyCell.isLast = storyCell.position == miniItems.size() - 1; + viewsDrawInParent.add(storyCell); + } + + Collections.sort(viewsDrawInParent, comparator); + for (int i = 0; i < viewsDrawInParent.size(); i++) { + StoryCell cell = viewsDrawInParent.get(i); + int restoreCount = canvas.save(); + canvas.translate(cell.getX(), cell.getY()); + if (cell.getAlpha() != 1f) { + canvas.saveLayerAlpha((float) -AndroidUtilities.dp(4), -AndroidUtilities.dp(4), AndroidUtilities.dp(50), AndroidUtilities.dp(50), (int) (255 * cell.getAlpha()), Canvas.ALL_SAVE_FLAG); + } + canvas.scale(cell.getScaleX(), cell.getScaleY(), AndroidUtilities.dp(14), cell.getCy()); + cell.draw(canvas); + canvas.restoreToCount(restoreCount); + } + } + + @Override + public void onScrolled(int dx, int dy) { + super.onScrolled(dx, dy); + if (premiumHint != null) { + premiumHint.hide(); + } + } + }; + listViewMini.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false)); + listViewMini.addItemDecoration(new RecyclerView.ItemDecoration() { + @Override + public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { + int p = parent.getChildLayoutPosition(view); + outRect.setEmpty(); + if (p == 1) { + outRect.left = -AndroidUtilities.dp(85) + AndroidUtilities.dp(25 + COLLAPSED_DIS - 14); + } else if (p == 2) { + outRect.left = -AndroidUtilities.dp(85) + AndroidUtilities.dp(25 + COLLAPSED_DIS - 14); + } + } + }); + miniItemAnimator = new DefaultItemAnimator() { + @Override + protected float animateByScale(View view) { + return 0.6f; + } + }; + miniItemAnimator.setDelayAnimations(false); + miniItemAnimator.setSupportsChangeAnimations(false); + listViewMini.setItemAnimator(miniItemAnimator); + listViewMini.setAdapter(miniAdapter); + listViewMini.setClipChildren(false); + addView(listViewMini); + setClipChildren(false); + setClipToPadding(false); + + updateItems(false, false); + } + + private void openStoryForCell(StoryCell cell) { + if (cell == null) { + return; + } + if (cell.isSelf && !storiesController.hasSelfStories()) { + if (!MessagesController.getInstance(currentAccount).storiesEnabled()) { + showPremiumHint(); + } else { + openStoryRecorder(); + } + return; + } + int position = cell.position; + long startFromDialogId = cell.dialogId; + ArrayList peerIds = new ArrayList<>(); + boolean allStoriesIsRead = true; + if (!storiesController.hasStories(cell.dialogId)) { + return; + } + for (int i = 0; i < items.size(); i++) { + long dialogId = items.get(i).dialogId; + if (dialogId != UserConfig.getInstance(currentAccount).clientUserId && storiesController.hasUnreadStories(dialogId)) { + allStoriesIsRead = false; + break; + } + } + if (cell.isSelf && (!allStoriesIsRead || items.size() == 1)) { + peerIds.add(cell.dialogId); + } else { + boolean isUnreadStory = !cell.isSelf && storiesController.hasUnreadStories(cell.dialogId); + if (isUnreadStory) { + for (int i = 0; i < items.size(); i++) { + long dialogId = items.get(i).dialogId; + if (!cell.isSelf && storiesController.hasUnreadStories(dialogId)) { + peerIds.add(dialogId); + } + if (dialogId == cell.dialogId) { + position = peerIds.size() - 1; + } + } + } else { + for (int i = 0; i < items.size(); i++) { + long dialogId = items.get(i).dialogId; + if (storiesController.hasStories(dialogId)) { + peerIds.add(items.get(i).dialogId); + } else if (i <= position) { + position--; + } + } + } + } + fragment.getOrCreateStoryViewer().open(getContext(), null, peerIds, position, null, null, StoriesListPlaceProvider.of(recyclerListView), false); + } + + private void checkLoadMore() { + if (layoutManager.findLastVisibleItemPosition() + 10 > items.size()) { + boolean hidden = type == TYPE_ARCHIVE; + storiesController.loadNextStories(hidden); + } + } + + public void updateItems(boolean animated, boolean force) { + if ((currentState == TRANSITION_STATE || overscrollPrgoress != 0) && !force) { + updateOnIdleState = true; + return; + } + oldItems.clear(); + oldItems.addAll(items); + oldMiniItems.clear(); + oldMiniItems.addAll(miniItems); + items.clear(); + if (type != TYPE_ARCHIVE) { + items.add(new Item(UserConfig.getInstance(currentAccount).getClientUserId())); + } + + ArrayList allStories = type == TYPE_ARCHIVE ? storiesController.getHiddenList() : storiesController.getDialogListStories(); + for (int i = 0; i < allStories.size(); i++) { + if (allStories.get(i).user_id != UserConfig.getInstance(currentAccount).getClientUserId()) { + items.add(new Item(allStories.get(i).user_id)); + } + } + int size = items.size(); + if (!storiesController.hasSelfStories()) { + size--; + } + int totalCount; + boolean hidden = type == TYPE_ARCHIVE; + totalCount = Math.max(1, Math.max(storiesController.getTotalStoriesCount(hidden), size)); + + if (storiesController.hasOnlySelfStories()) { + if (!storiesController.getUploadingStories().isEmpty()) { + String str = LocaleController.getString("UploadingStory", R.string.UploadingStory); + int index = str.indexOf("…"); + if (index > 0) { + if (uploadingString == null) { + SpannableStringBuilder spannableStringBuilder = SpannableStringBuilder.valueOf(str); + UploadingDotsSpannable dotsSpannable = new UploadingDotsSpannable(); + spannableStringBuilder.setSpan(dotsSpannable, spannableStringBuilder.length() - 1, spannableStringBuilder.length(), 0); + dotsSpannable.setParent(titleView, true); + uploadingString = spannableStringBuilder; + } + currentTitle = uploadingString; + } else { + currentTitle = str; + } + } else { + currentTitle = LocaleController.getString("MyStory", R.string.MyStory); + } + } else { + currentTitle = LocaleController.formatPluralString("Stories", totalCount); + } + + if (!hasOverlayText) { + titleView.setText(currentTitle, animated); + } + + miniItems.clear(); + for (int i = 0; i < items.size(); i++) { + if (items.get(i).dialogId == UserConfig.getInstance(currentAccount).clientUserId && !shouldDrawSelfInMini()) { + continue; + } else { + miniItems.add(items.get(i)); + if (miniItems.size() >= 3) { + break; + } + } + } + + if (animated) { + if (currentState == COLLAPSED_STATE) { + listViewMini.setItemAnimator(miniItemAnimator); + recyclerListView.setItemAnimator(null); + } else { + recyclerListView.setItemAnimator(itemAnimator); + listViewMini.setItemAnimator(null); + } + } else { + recyclerListView.setItemAnimator(null); + listViewMini.setItemAnimator(null); + } + adapter.setItems(oldItems, items); + miniAdapter.setItems(oldMiniItems, miniItems); + + oldItems.clear(); + invalidate(); + } + + private boolean shouldDrawSelfInMini() { + long dialogId = UserConfig.getInstance(currentAccount).clientUserId; + if (storiesController.hasUnreadStories(dialogId) || (storiesController.hasSelfStories() && storiesController.getDialogListStories().size() <= 3)) { + return true; + } + return false; + } + + Comparator comparator = (o1, o2) -> o2.position - o1.position; + + @Override + protected void dispatchDraw(Canvas canvas) { + canvas.save(); + if (clipTop > 0) { + canvas.clipRect(0, clipTop, getMeasuredWidth(), getMeasuredHeight()); + } + float y = AndroidUtilities.lerp(0, getMeasuredHeight() - ActionBar.getCurrentActionBarHeight() - AndroidUtilities.dp(4), collapsedProgress1); + recyclerListView.setTranslationY(y); + listViewMini.setTranslationY(y); + listViewMini.setTranslationX(AndroidUtilities.dp(68)); + float progressHalf = Utilities.clamp((CubicBezierInterpolator.EASE_OUT.getInterpolation(collapsedProgress) - 0.5f) / 0.5f, 1f, 0); + + for (int i = 0; i < viewsDrawInParent.size(); i++) { + viewsDrawInParent.get(i).drawInParent = false; + } + viewsDrawInParent.clear(); + int animateFromPosition = -1; + boolean crossfade = false; + if (currentState == TRANSITION_STATE && !animateToDialogIds.isEmpty()) { + for (int i = 0; i < recyclerListView.getChildCount(); i++) { + StoryCell cell = (StoryCell) recyclerListView.getChildAt(i); + if (cell.dialogId == animateToDialogIds.get(0)) { + animateFromPosition = recyclerListView.getChildAdapterPosition(cell); + } + } + } else if (currentState == COLLAPSED_STATE) { + animateFromPosition = 0; + } + float lastViewRight = 0; + if (currentState >= 0 && currentState != COLLAPSED_STATE) { + if (animateFromPosition == -1) { + crossfade = true; + animateFromPosition = layoutManager.findFirstCompletelyVisibleItemPosition(); + if (animateFromPosition == -1) { + animateFromPosition = layoutManager.findFirstVisibleItemPosition(); + } + } + + recyclerListView.setAlpha(1f - Utilities.clamp(collapsedProgress / K, 1f, 0)); + overscrollSelectedPosition = -1; + if (overscrollPrgoress != 0) { + int minUnreadPosition = -1; + int minPosition = -1; + for (int i = 0; i < recyclerListView.getChildCount(); i++) { + View child = recyclerListView.getChildAt(i); + if (child.getX() < 0 || child.getX() + child.getMeasuredWidth() > getMeasuredWidth()) { + continue; + } + int position = recyclerListView.getChildAdapterPosition(child); + if (position < 0) { + continue; + } +// if ((minUnreadPosition == -1 || position < minUnreadPosition) && storiesController.hasUnreadStories(items.get(position).dialogId)) { +// minUnreadPosition = position; +// } + + if ((minPosition == -1 || position < minPosition) && items.get(position).dialogId != UserConfig.getInstance(currentAccount).clientUserId) { + minPosition = position; + overscrollSelectedView = (StoryCell) child; + } + } + overscrollSelectedPosition = minPosition; + } + for (int i = 0; i < recyclerListView.getChildCount(); i++) { + StoryCell cell = (StoryCell) recyclerListView.getChildAt(i); + cell.setProgressToCollapsed(collapsedProgress, collapsedProgress2, overscrollPrgoress, overscrollSelectedPosition == cell.position); + float ovescrollSelectProgress = Utilities.clamp((overscrollPrgoress - 0.5f) / 0.5f, 1f, 0f); + float overScrollOffset = AndroidUtilities.dp(16) * ovescrollSelectProgress; + float overscrollAlpha = (float) (0.5 + 0.5f * (1f - ovescrollSelectProgress)); + if (collapsedProgress > 0) { + float toX = 0; + int adapterPosition = recyclerListView.getChildAdapterPosition(cell); + boolean drawInParent = adapterPosition >= animateFromPosition && adapterPosition <= animateFromPosition + 2; + if (crossfade) { + int p = adapterPosition - animateFromPosition; + if (p >= 0 && p < animateToDialogIds.size()) { + long dialogId = animateToDialogIds.get(p); + cell.setCrossfadeTo(dialogId); + } else { + cell.setCrossfadeTo(-1); + } + } else { + cell.setCrossfadeTo(-1); + } + cell.drawInParent = drawInParent; + cell.isFirst = adapterPosition == animateFromPosition; + cell.isLast = adapterPosition >= animateFromPosition + animateToDialogIds.size() - 1; + if (adapterPosition <= animateFromPosition) { + toX = 0; + } else if (adapterPosition == animateFromPosition + 1) { + toX = AndroidUtilities.dp(COLLAPSED_DIS); + } else { + toX = AndroidUtilities.dp(COLLAPSED_DIS) * 2; + } + toX += AndroidUtilities.dp(68); + cell.setTranslationX(AndroidUtilities.lerp(0, toX - cell.getLeft(), CubicBezierInterpolator.EASE_OUT.getInterpolation(collapsedProgress))); + if (drawInParent) { + viewsDrawInParent.add(cell); + } + } else if (recyclerListView.getItemAnimator() == null || !recyclerListView.getItemAnimator().isRunning()) { + if (overscrollPrgoress > 0) { + if (cell.position < overscrollSelectedPosition) { + cell.setTranslationX(-overScrollOffset); + cell.setTranslationY(0); + cell.setAlpha(overscrollAlpha); + } else if (cell.position > overscrollSelectedPosition) { + cell.setTranslationX(overScrollOffset); + cell.setTranslationY(0); + cell.setAlpha(overscrollAlpha); + } else { + cell.setTranslationX(0); + cell.setTranslationY(-overScrollOffset / 2f); + cell.setAlpha(1f); + } + } else { + cell.setTranslationX(0); + cell.setTranslationY(0); + cell.setAlpha(1f); + } + } + if (cell.drawInParent) { + float right = recyclerListView.getX() + cell.getX() + cell.getMeasuredWidth() / 2f + AndroidUtilities.dp(74) / 2f; + if (lastViewRight == 0 || right > lastViewRight) { + lastViewRight = right; + } + } + } + } else { + for (int i = 0; i < listViewMini.getChildCount(); i++) { + StoryCell cell = (StoryCell) listViewMini.getChildAt(i); + float right = listViewMini.getX() + cell.getX() + cell.getMeasuredWidth(); + if (lastViewRight == 0 || right > lastViewRight) { + lastViewRight = right; + } + } + } + + if (premiumHint != null) { + float x = AndroidUtilities.lerp(37 - 8, 68 + 14 - 8, CubicBezierInterpolator.EASE_OUT.getInterpolation(collapsedProgress)); + if (recyclerListView.getChildCount() > 0) { + x += recyclerListView.getChildAt(0).getLeft(); + } + premiumHint.setJoint(0, x); + } + + float progress = Math.min(collapsedProgress, collapsedProgress2); + if (progress != 0) { + float offset = (titleView.getMeasuredHeight() - titleView.getTextHeight()) / 2f; + titleView.setTranslationY(y + AndroidUtilities.dp(14) - offset); + int cellWidth = AndroidUtilities.dp(72); + lastViewRight += -cellWidth + getAvatarRight(cellWidth, collapsedProgress) + AndroidUtilities.dp(12); + // float toX = AndroidUtilities.dp(28) * Math.min(1, animateToCount) + AndroidUtilities.dp(14) * Math.max(0, animateToCount - 1); + titleView.setTranslationX(lastViewRight); + titleView.getDrawable().setRightPadding(lastViewRight + actionBar.menu.getItemsMeasuredWidth() * progress); + titleView.setAlpha(progress); + titleView.setVisibility(View.VISIBLE); + } else { + titleView.setVisibility(View.GONE); + } + + super.dispatchDraw(canvas); + if (currentState >= 0 && currentState != COLLAPSED_STATE) { + Collections.sort(viewsDrawInParent, comparator); + for (int i = 0; i < viewsDrawInParent.size(); i++) { + StoryCell cell = viewsDrawInParent.get(i); + canvas.save(); + canvas.translate(recyclerListView.getX() + cell.getX(), recyclerListView.getY() + cell.getY()); + cell.draw(canvas); + canvas.restore(); + } + } + canvas.restore(); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + updateItems(false, false); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.storiesUpdated); + ellipsizeSpanAnimator.onAttachedToWindow(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.storiesUpdated); + ellipsizeSpanAnimator.onDetachedFromWindow(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + titleView.setTextSize(AndroidUtilities.dp(!AndroidUtilities.isTablet() && getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE ? 18 : 20)); + int fullWidth = items.size() * AndroidUtilities.dp(74); + int lastCellWidth = currentCellWidth; +// if (fullWidth < MeasureSpec.getSize(widthMeasureSpec)) { +// currentCellWidth = MeasureSpec.getSize(widthMeasureSpec) / items.size(); +// } else { + currentCellWidth = AndroidUtilities.dp(74); + // } +// if (currentCellWidth != lastCellWidth) { +// //adapter.notifyItemRangeChanged(0, items.size()); +// AndroidUtilities.forEachViews(recyclerListView, view -> view.forceLayout()); +// } + AndroidUtilities.rectTmp.set(0, 0, getMeasuredWidth(), getMeasuredHeight()); + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(85), MeasureSpec.EXACTLY)); + } + + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.storiesUpdated) { + if (allowGlobalUpdates) { + updateItems(getVisibility() == View.VISIBLE, false); + AndroidUtilities.runOnUIThread(() -> { + checkLoadMore(); + }); + } + } + } + + boolean collapsed; + float K = 0.3f; + ValueAnimator valueAnimator; + + public void setProgressToCollapse(float progress) { + setProgressToCollapse(progress, true); + } + + public void setProgressToCollapse(float progress, boolean animated) { + if (collapsedProgress1 == progress) { + return; + } + + collapsedProgress1 = progress; + checkCollapsedProgres(); + + boolean newCollapsed = progress > K; + if (newCollapsed != collapsed) { + collapsed = newCollapsed; + if (valueAnimator != null) { + valueAnimator.removeAllListeners(); + valueAnimator.cancel(); + valueAnimator = null; + } + if (animated) { + valueAnimator = ValueAnimator.ofFloat(collapsedProgress2, newCollapsed ? 1f : 0); + } else { + collapsedProgress2 = newCollapsed ? 1f : 0; + checkCollapsedProgres(); + } + if (valueAnimator != null) { + valueAnimator.addUpdateListener(animation -> { + collapsedProgress2 = (float) animation.getAnimatedValue(); + checkCollapsedProgres(); + }); + valueAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + collapsedProgress2 = newCollapsed ? 1f : 0; + checkCollapsedProgres(); + } + }); + valueAnimator.setDuration(450); + valueAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + valueAnimator.start(); + } + } + } + + private void checkCollapsedProgres() { + collapsedProgress = 1f - AndroidUtilities.lerp(1f - collapsedProgress1, 1f, 1f - collapsedProgress2); + updateCollapsedProgress(); + + int state = EXPANDED_STATE; + if (collapsedProgress == 1f) { + state = COLLAPSED_STATE; + } else if (collapsedProgress != 0) { + state = TRANSITION_STATE; + } + updateCurrentState(state); + invalidate(); + } + + public float getCollapsedProgress() { + return collapsedProgress; + } + + public void updateCollapsedProgress() { + + } + + public void scrollToFirstCell() { + layoutManager.scrollToPositionWithOffset(0, 0); + } + + public void updateColors() { + int color = getTextColor(); + + titleView.setTextColor(color); + AndroidUtilities.forEachViews(recyclerListView, view -> { + StoryCell cell = (StoryCell) view; + cell.invalidate(); + cell.textView.setTextColor(color); + }); + AndroidUtilities.forEachViews(listViewMini, view -> { + StoryCell cell = (StoryCell) view; + cell.invalidate(); + }); + + } + + private int getTextColor() { + if (type == TYPE_DIALOGS) { + return Theme.getColor(Theme.key_actionBarDefaultTitle); + } else { + return Theme.getColor(Theme.key_actionBarDefaultArchivedTitle); + } + } + + public boolean scrollTo(long currentDialogId) { + int position = -1; + for (int i = 0; i < items.size(); i++) { + if (items.get(i).dialogId == currentDialogId) { + position = i; + break; + } + } + if (position >= 0) { + if (position < layoutManager.findFirstCompletelyVisibleItemPosition()) { + layoutManager.scrollToPositionWithOffset(position, 0); + return true; + } else if (position > layoutManager.findLastCompletelyVisibleItemPosition()) { + layoutManager.scrollToPositionWithOffset(position, 0, true); + return true; + } + } + return false; + } + + public void afterNextLayout(Runnable r) { + afterNextLayout.add(r); + } + + public boolean isExpanded() { + return currentState == EXPANDED_STATE || currentState == TRANSITION_STATE; + } + + public boolean isFullExpanded() { + return currentState == EXPANDED_STATE; + } + + public boolean scrollToFirst() { + if (layoutManager.findFirstVisibleItemPosition() != 0) { + recyclerListView.smoothScrollToPosition(0); + return true; + } + return false; + } + + public void onUserLongPressed(View view, long dialogId) { + + } + + public void openStoryRecorder() { + StoryCell cell = null; + for (int i = 0 ; i < recyclerListView.getChildCount(); i++) { + StoryCell storyCell = (StoryCell) recyclerListView.getChildAt(i); + if (storyCell.isSelf) { + cell = storyCell; + break; + } + } + if (cell == null) { + return; + } + StoryRecorder.getInstance(fragment.getParentActivity(), currentAccount) + .open(StoryRecorder.SourceView.fromStoryCell(cell)); + } + + EllipsizeSpanAnimator ellipsizeSpanAnimator = new EllipsizeSpanAnimator(this); + + public void setTitleOverlayText(String titleOverlayText, int textId) { + if (titleOverlayText != null) { + hasOverlayText = true; + if (overlayTextId != textId) { + overlayTextId = textId; + String title = LocaleController.getString(titleOverlayText, textId); + CharSequence textToSet = title; + if (!TextUtils.isEmpty(title)) { + int index = TextUtils.indexOf(textToSet, "..."); + if (index >= 0) { + SpannableString spannableString = SpannableString.valueOf(textToSet); + ellipsizeSpanAnimator.wrap(spannableString, index); + textToSet = spannableString; + } + } + titleView.setText(textToSet, true); + } + } else { + hasOverlayText = false; + overlayTextId = 0; + titleView.setText(currentTitle, true); + } + } + + public void setClipTop(int clipTop) { + if (clipTop < 0) { + clipTop = 0; + } + if (this.clipTop != clipTop) { + this.clipTop = clipTop; + invalidate(); + } + } + + public void openSelfStories() { + if (!storiesController.hasSelfStories()) { + return; + } + ArrayList peerIds = new ArrayList<>(); + peerIds.add(UserConfig.getInstance(currentAccount).clientUserId); + fragment.getOrCreateStoryViewer().open(getContext(), null, peerIds, 0, null, null, StoriesListPlaceProvider.of(listViewMini), false); + } + + public void onResume() { + storiesController.checkExpiredStories(); + for (int i = 0; i < items.size(); i++) { + TLRPC.TL_userStories stories = storiesController.getStories(items.get(i).dialogId); + if (stories != null) { + storiesController.preloadUserStories(stories); + } + } + } + + public void setOverscoll(float storiesOverscroll) { + overscrollPrgoress = storiesOverscroll / AndroidUtilities.dp(90); + invalidate(); + recyclerListView.invalidate(); + if (overscrollPrgoress != 0) { + recyclerListView.setClipChildren(false); + ((ViewGroup) getParent()).setClipChildren(false); + } else { + recyclerListView.setClipChildren(true); + ((ViewGroup) getParent()).setClipChildren(true); + } + } + + public void openOverscrollSelectedStory() { + openStoryForCell(overscrollSelectedView); + performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + } + + public void setActionBar(ActionBar actionBar) { + this.actionBar = actionBar; + } + + public float overscrollProgress() { + return overscrollPrgoress; + } + + private class Adapter extends AdapterWithDiffUtils { + + boolean mini; + + public Adapter(boolean mini) { + this.mini = mini; + } + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + StoryCell storyCell = new StoryCell(parent.getContext()); + storyCell.mini = mini; + if (mini) { + storyCell.setProgressToCollapsed(1f, 1f, 0f, false); + } + return new RecyclerListView.Holder(storyCell); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + StoryCell cell = (StoryCell) holder.itemView; + cell.position = position; + if (mini) { + cell.setDialogId(miniItems.get(position).dialogId); + } else { + cell.setDialogId(items.get(position).dialogId); + } + } + + @Override + public int getItemCount() { + return mini ? miniItems.size() : items.size(); + } + + + @Override + public boolean isEnabled(RecyclerView.ViewHolder holder) { + return false; + } + } + + private class Item extends AdapterWithDiffUtils.Item { + + final long dialogId; + + public Item(long dialogId) { + super(0, false); + this.dialogId = dialogId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Item)) return false; + Item item = (Item) o; + return dialogId == item.dialogId; + } + + @Override + public int hashCode() { + return Objects.hash(dialogId); + } + } + + public StoryCell findSelfStoryCell() { + RecyclerListView parent = recyclerListView; + if (currentState == COLLAPSED_STATE) { + parent = listViewMini; + } + for (int i = 0; i < parent.getChildCount(); ++i) { + View child = parent.getChildAt(i); + if (child instanceof StoryCell) { + StoryCell storyCell = (StoryCell) child; + if (storyCell.isSelf) { + return storyCell; + } + } + } + return null; + } + + public class StoryCell extends FrameLayout { + public boolean drawInParent; + public int position; + public boolean isLast; + public boolean isFirst; + TLRPC.User user; + TLRPC.Chat chat; + + AvatarDrawable avatarDrawable = new AvatarDrawable(); + public ImageReceiver avatarImage = new ImageReceiver(this); + public ImageReceiver crossfageToAvatarImage = new ImageReceiver(this); + AvatarDrawable crossfadeAvatarDrawable = new AvatarDrawable(); + public boolean drawAvatar = true; + FrameLayout textViewContainer; + SimpleTextView textView; + long dialogId; + boolean isSelf; + boolean crossfadeToDialog; + long crossfadeToDialogId; + + float progressToCollapsed; + float progressToCollapsed2; + private float cx, cy; + private boolean mini; + public final StoriesUtilities.AvatarStoryParams params = new StoriesUtilities.AvatarStoryParams(true); + float textAlpha = 1f; + float textAlphaTransition = 1f; + + public RadialProgress radialProgress; + private Drawable verifiedDrawable; + private float bounceScale = 1f; + private boolean isUploadingState; + private float overscrollProgress; + private boolean selectedForOverscroll; + + public StoryCell(Context context) { + super(context); + params.isArchive = type == TYPE_ARCHIVE; + avatarImage.setInvalidateAll(true); + avatarImage.setAllowLoadingOnAttachedOnly(true); + + textViewContainer = new FrameLayout(getContext()); + textViewContainer.setClipChildren(false); + if (!mini) { + setClipChildren(false); + } + createTextView(); + addView(textViewContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + avatarImage.setRoundRadius(AndroidUtilities.dp(48) / 2); + crossfageToAvatarImage.setRoundRadius(AndroidUtilities.dp(48) / 2); + } + + private void createTextView() { + textView = new SimpleTextView(getContext()); + textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + textView.setGravity(Gravity.CENTER); + textView.setTextSize(11); + textView.setTextColor(getTextColor()); + // textView.setEllipsize(TextUtils.TruncateAt.END); + textView.setMaxLines(1); + //textView.setSingleLine(true); + + textViewContainer.addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 2, 0, 2, 0)); + avatarImage.setRoundRadius(AndroidUtilities.dp(48) / 2); + crossfageToAvatarImage.setRoundRadius(AndroidUtilities.dp(48) / 2); + } + + public void setDialogId(long dialogId) { + boolean animated = this.dialogId == dialogId; + this.dialogId = dialogId; + isSelf = dialogId == UserConfig.getInstance(currentAccount).getClientUserId(); + TLObject object; + if (dialogId > 0) { + object = user = MessagesController.getInstance(currentAccount).getUser(dialogId); + chat = null; + } else { + object = chat = MessagesController.getInstance(currentAccount).getChat(-dialogId); + user = null; + } + + if (object == null) { + textView.setText(""); + avatarImage.clearImage(); + return; + } + avatarDrawable.setInfo(object); + avatarImage.setForUserOrChat(object, avatarDrawable); + if (mini) { + return; + } + if (dialogId == UserConfig.getInstance(currentAccount).getClientUserId()) { + textView.setRightDrawable(null); + if (!storiesController.getUploadingStories().isEmpty()) { + StoriesUtilities.applyUploadingStr(textView, true, false); + isUploadingState = true; + } else if (storiesController.getEditingStory() != null) { + StoriesUtilities.applyUploadingStr(textView, true, false); + isUploadingState = true; + } else { + if (animated && isUploadingState && !mini) { + View oldTextView = textView; + createTextView(); + if (textAnimator != null) { + textAnimator.cancel(); + textAnimator = null; + } + textAnimator = ValueAnimator.ofFloat(0, 1f); + textAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(@NonNull ValueAnimator animation) { + float progress = (float) animation.getAnimatedValue(); + oldTextView.setAlpha(1f - progress); + oldTextView.setTranslationY(-AndroidUtilities.dp(5) * progress); + + textView.setAlpha(progress); + textView.setTranslationY(AndroidUtilities.dp(5) * (1f - progress)); + } + }); + textAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + textAnimator = null; + AndroidUtilities.removeFromParent(oldTextView); + } + }); + textAnimator.setDuration(150); + textView.setAlpha(0); + textView.setTranslationY(AndroidUtilities.dp(5)); + animationRunnable = () -> { + if (textAnimator != null) { + textAnimator.start(); + } + animationRunnable = null; + }; + } + AndroidUtilities.runOnUIThread(animationRunnable, 500); + isUploadingState = false; + textView.setText(LocaleController.getString("MyStory", R.string.MyStory));//, animated); + } + } else if (user != null) { + String name = user.first_name == null ? "" : user.first_name.trim(); + int index = name.indexOf(" "); + if (index > 0) { + name = name.substring(0, index); + } + if (user.verified) { + if (verifiedDrawable == null) { + verifiedDrawable = createVerifiedDrawable(); + } + CharSequence text = name; + text = Emoji.replaceEmoji(text, textView.getPaint().getFontMetricsInt(), false); + textView.setText(text); + textView.setRightDrawable(verifiedDrawable); + } else { + CharSequence text = name; + text = Emoji.replaceEmoji(text, textView.getPaint().getFontMetricsInt(), false); + textView.setText(text); + textView.setRightDrawable(null); + }//, false); + } else { + CharSequence text = chat.title; + text = Emoji.replaceEmoji(text, textView.getPaint().getFontMetricsInt(), false); + textView.setText(text);//, false); + textView.setRightDrawable(null); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(MeasureSpec.makeMeasureSpec(mini ? AndroidUtilities.dp(74) : currentCellWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(HEIGHT_IN_DP), MeasureSpec.EXACTLY)); + } + + float getCy() { + float size = AndroidUtilities.dp(48); + float collapsedSize = AndroidUtilities.dp(COLLAPSED_SIZE); + + float finalSize = AndroidUtilities.lerp(size, collapsedSize, progressToCollapsed); + float radius = finalSize / 2f; + + float y = AndroidUtilities.lerp(AndroidUtilities.dp(5), (ActionBar.getCurrentActionBarHeight() - collapsedSize) / 2f, collapsedProgress1); + return y + radius; + } + + @Override + protected void dispatchDraw(Canvas canvas) { + float size = AndroidUtilities.dp(48); + float collapsedSize = AndroidUtilities.dp(COLLAPSED_SIZE); + float overscrollSize = AndroidUtilities.dp(8) * Utilities.clamp(overscrollPrgoress / 0.5f, 1f, 0); + if (selectedForOverscroll) { + overscrollSize += AndroidUtilities.dp(16) * Utilities.clamp((overscrollPrgoress - 0.5f) / 0.5f, 1f, 0f); + } + + float finalSize = AndroidUtilities.lerp(size + overscrollSize, collapsedSize, progressToCollapsed); + float radius = finalSize / 2f; + + float fromX = getMeasuredWidth() / 2f - radius; + float x = AndroidUtilities.lerp(fromX, 0, progressToCollapsed); + float y = AndroidUtilities.lerp(AndroidUtilities.dp(5), (ActionBar.getCurrentActionBarHeight() - collapsedSize) / 2f, progressToCollapsed); + + float progressHalf = Utilities.clamp(progressToCollapsed / 0.5f, 1f, 0f); + + params.drawSegments = true; + if (!params.forceAnimateProgressToSegments) { + params.progressToSegments = 1f - collapsedProgress2; + } + params.originalAvatarRect.set(x, y, x + finalSize, y + finalSize); + avatarImage.setAlpha(1f); + avatarImage.setRoundRadius((int) radius); + + cx = x + radius; + cy = y + radius; + if (type == TYPE_DIALOGS) { + backgroundPaint.setColor(Theme.getColor(Theme.key_actionBarDefault)); + } else { + backgroundPaint.setColor(Theme.getColor(Theme.key_actionBarDefaultArchived)); + } + if (progressToCollapsed != 0) { + canvas.drawCircle(cx, cy, radius + AndroidUtilities.dp(3), backgroundPaint); + } + if (isSelf) { + canvas.save(); + canvas.scale(bounceScale, bounceScale, cx, cy); + if (radialProgress == null) { + radialProgress = DialogStoriesCell.this.radialProgress; + } + if (!storiesController.getUploadingAndEditingStories().isEmpty() || (progressWasDrawn && radialProgress != null && radialProgress.getAnimatedProgress() < 0.98f)) { + float uploadingProgress = 0; + boolean closeFriends = false; + if (storiesController.getUploadingAndEditingStories().isEmpty()) { + uploadingProgress = 1f; + closeFriends = lastUploadingCloseFriends; + } else { + for (int i = 0; i < storiesController.getUploadingAndEditingStories().size(); i++) { + uploadingProgress += storiesController.getUploadingAndEditingStories().get(i).progress; + } + uploadingProgress = uploadingProgress / storiesController.getUploadingAndEditingStories().size(); + lastUploadingCloseFriends = closeFriends = storiesController.getUploadingAndEditingStories().get(storiesController.getUploadingAndEditingStories().size() - 1).isCloseFriends(); + } + invalidate(); + if (radialProgress == null) { + if (DialogStoriesCell.this.radialProgress != null) { + radialProgress = DialogStoriesCell.this.radialProgress; + } else { + DialogStoriesCell.this.radialProgress = radialProgress = new RadialProgress(this); + radialProgress.setBackground(null, true, false); + } + } + if (drawAvatar) { + canvas.save(); + canvas.scale(params.getScale(), params.getScale(), params.originalAvatarRect.centerX(), params.originalAvatarRect.centerY()); + avatarImage.setImageCoords(params.originalAvatarRect); + avatarImage.draw(canvas); + canvas.restore(); + } + radialProgress.setDiff(0); + Paint paint = closeFriends ? + StoriesUtilities.getCloseFriendsPaint(avatarImage) : + StoriesUtilities.getActiveCirclePaint(avatarImage, true); + paint.setAlpha(255); + radialProgress.setPaint(paint); + radialProgress.setProgressRect( + (int) (avatarImage.getImageX() - AndroidUtilities.dp(3)), (int) (avatarImage.getImageY() - AndroidUtilities.dp(3)), + (int) (avatarImage.getImageX2() + AndroidUtilities.dp(3)), (int) (avatarImage.getImageY2() + AndroidUtilities.dp(3)) + ); + radialProgress.setProgress(Utilities.clamp(uploadingProgress, 1f, 0), progressWasDrawn); + if (avatarImage.getVisible()) { + radialProgress.draw(canvas); + } + progressWasDrawn = true; + drawCircleForce = true; + invalidate(); + } else { + if (drawAvatar) { + if (progressWasDrawn) { + animateBounce(); + params.forceAnimateProgressToSegments = true; + params.progressToSegments = 0f; + ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 1f); + valueAnimator.addUpdateListener(animation -> { + params.progressToSegments = AndroidUtilities.lerp(0, 1f - collapsedProgress2, (float) animation.getAnimatedValue()); + invalidate(); + }); + valueAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + params.forceAnimateProgressToSegments = false; + } + }); + valueAnimator.setDuration(100); + valueAnimator.start(); + } + + params.animate = !progressWasDrawn; + params.progressToArc = getArcProgress(cx, radius); + params.isLast = isLast; + params.isFirst = isFirst; + params.crossfadeToDialog = 0; + StoriesUtilities.drawAvatarWithStory(dialogId, canvas, avatarImage, storiesController.hasSelfStories(), params); + // avatarImage.draw(canvas); + } + progressWasDrawn = false; + if (drawAvatar) { + canvas.save(); + float s = 1f - progressHalf; + canvas.scale(s, s, cx + AndroidUtilities.dp(16), cy + AndroidUtilities.dp(16)); + drawPlus(canvas, cx, cy, 1f); + canvas.restore(); + } + } + canvas.restore(); + } else { + if (drawAvatar) { + params.animate = true; + params.progressToArc = getArcProgress(cx, radius); + params.isLast = isLast; + params.isFirst = isFirst; + if (crossfadeToDialog) { + params.crossfadeToDialog = crossfadeToDialogId; + params.crossfadeToDialogProgress = progressToCollapsed2; + } else { + params.crossfadeToDialog = 0; + } + StoriesUtilities.drawAvatarWithStory(dialogId, canvas, avatarImage, storiesController.hasStories(dialogId), params); +// avatarImage.draw(canvas); + } + } + + if (crossfadeToDialog && progressToCollapsed2 > 0) { + crossfageToAvatarImage.setImageCoords(x, y, finalSize, finalSize); + crossfageToAvatarImage.setAlpha(progressToCollapsed2); + crossfageToAvatarImage.draw(canvas); + } + textViewContainer.setTranslationY(y + finalSize + AndroidUtilities.dp(7) * (1f - progressToCollapsed)); + textViewContainer.setTranslationX(x - fromX); + if (!mini) { + float p; + if (isSelf) { + textAlpha = 1f; + } else { + if (params.progressToSate != 1f) { + p = params.currentState == StoriesUtilities.STATE_READ ? params.progressToSate : (1f - params.progressToSate); + } else { + p = params.currentState == StoriesUtilities.STATE_READ ? 1f : 0f; + } + textAlpha = params.globalState == StoriesUtilities.STATE_READ ? 0.7f : 1f;//AndroidUtilities.lerp(1f, 0.7f, p); + } + textViewContainer.setAlpha(textAlphaTransition * textAlpha); + } + super.dispatchDraw(canvas); + } + + private void animateBounce() { + AnimatorSet animatorSet = new AnimatorSet(); + ValueAnimator inAnimator = ValueAnimator.ofFloat(1, 1.05f); + inAnimator.setDuration(100); + inAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT); + + ValueAnimator outAnimator = ValueAnimator.ofFloat(1.05f, 1f); + outAnimator.setDuration(250); + outAnimator.setInterpolator(new OvershootInterpolator()); + + ValueAnimator.AnimatorUpdateListener updater = animation -> { + bounceScale = (float) animation.getAnimatedValue(); + invalidate(); + }; + setClipInParent(false); + inAnimator.addUpdateListener(updater); + outAnimator.addUpdateListener(updater); + animatorSet.playSequentially(inAnimator, outAnimator); + animatorSet.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + bounceScale = 1f; + invalidate(); + setClipInParent(true); + } + }); + animatorSet.start(); + + if (animationRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(animationRunnable); + animationRunnable.run(); + animationRunnable = null; + } + } + + private void setClipInParent(boolean clip) { + if (getParent() != null) { + ((ViewGroup) getParent()).setClipChildren(clip); + } + if (getParent() != null && getParent().getParent() != null && getParent().getParent().getParent() != null) { + ((ViewGroup) getParent().getParent().getParent()).setClipChildren(clip); + } + } + + private float getArcProgress(float cx, float radius) { + if (isLast || overscrollPrgoress > 0) { + return 0; + } + float p = CubicBezierInterpolator.EASE_OUT.getInterpolation(progressToCollapsed); + float distance = AndroidUtilities.lerp(getMeasuredWidth(), AndroidUtilities.dp(COLLAPSED_DIS), p); + radius += AndroidUtilities.dpf2(3.5f); + if (distance < radius * 2) { + //double cosA = (distance / 2f) / radius; + return (float) Math.toDegrees(Math.acos((distance / 2f) / radius)) * 2; + } else { + return 0; + } + } + + @Override + public void setPressed(boolean pressed) { + super.setPressed(pressed); + if (pressed && params.buttonBounce == null) { + params.buttonBounce = new ButtonBounce(this, 1.5f); + } + if (params.buttonBounce != null) { + params.buttonBounce.setPressed(pressed); + } + } + + @Override + public void invalidate() { + if (mini || drawInParent && getParent() != null) { + if (getParent() == listViewMini) { + listViewMini.invalidate(); + } else { + DialogStoriesCell.this.invalidate(); + } + } + super.invalidate(); + } + + @Override + public void invalidate(int l, int t, int r, int b) { + if (mini || drawInParent && getParent() != null) { + if (getParent() == listViewMini) { + listViewMini.invalidate(); + } + DialogStoriesCell.this.invalidate(); + } + super.invalidate(l, t, r, b); + } + + public void drawPlus(Canvas canvas, float cx, float cy, float alpha) { + if (!isSelf || storiesController.hasStories(dialogId) || !storiesController.getUploadingStories().isEmpty()) { + return; + } + float cx2 = cx + AndroidUtilities.dp(16); + float cy2 = cy + AndroidUtilities.dp(16); + addCirclePaint.setColor(Theme.multAlpha(getTextColor(), alpha)); + if (type == TYPE_DIALOGS) { + backgroundPaint.setColor(Theme.multAlpha(Theme.getColor(Theme.key_actionBarDefault), alpha)); + } else { + backgroundPaint.setColor(Theme.multAlpha(Theme.getColor(Theme.key_actionBarDefaultArchived), alpha)); + } + canvas.drawCircle(cx2, cy2, AndroidUtilities.dp(11), backgroundPaint); + canvas.drawCircle(cx2, cy2, AndroidUtilities.dp(9), addCirclePaint); + + int newDrawableColor = type == TYPE_DIALOGS ? Theme.getColor(Theme.key_actionBarDefault) : Theme.getColor(Theme.key_actionBarDefaultArchived); + if (newDrawableColor != addNewStoryLastColor) { + addNewStoryDrawable.setColorFilter(new PorterDuffColorFilter(addNewStoryLastColor = newDrawableColor, PorterDuff.Mode.MULTIPLY)); + } + addNewStoryDrawable.setAlpha((int) (0xFF * alpha)); + addNewStoryDrawable.setBounds( + (int) (cx2 - addNewStoryDrawable.getIntrinsicWidth() / 2f), + (int) (cy2 - addNewStoryDrawable.getIntrinsicHeight() / 2f), + (int) (cx2 + addNewStoryDrawable.getIntrinsicWidth() / 2f), + (int) (cy2 + addNewStoryDrawable.getIntrinsicHeight() / 2f) + ); + addNewStoryDrawable.draw(canvas); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + avatarImage.onAttachedToWindow(); + crossfageToAvatarImage.onAttachedToWindow(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + avatarImage.onDetachedFromWindow(); + crossfageToAvatarImage.onDetachedFromWindow(); + params.onDetachFromWindow(); + } + + public void setProgressToCollapsed(float progressToCollapsed, float progressToCollapsed2, float overscrollProgress, boolean selectedForOverscroll) { + if (this.progressToCollapsed != progressToCollapsed || this.progressToCollapsed2 != progressToCollapsed2 || this.overscrollProgress != overscrollProgress || this.selectedForOverscroll != selectedForOverscroll) { + this.selectedForOverscroll = selectedForOverscroll; + this.progressToCollapsed = progressToCollapsed; + this.progressToCollapsed2 = progressToCollapsed2; + float progressHalf = Utilities.clamp(progressToCollapsed / 0.5f, 1f, 0f); + float size = AndroidUtilities.dp(48); + float collapsedSize = AndroidUtilities.dp(COLLAPSED_SIZE); + invalidate(); + recyclerListView.invalidate(); + } + textAlphaTransition = mini ? 0 : 1f - Utilities.clamp(collapsedProgress / K, 1f, 0); + textViewContainer.setAlpha(textAlphaTransition * textAlpha); + } + + public void setCrossfadeTo(long dialogId) { + if (crossfadeToDialogId != dialogId) { + this.crossfadeToDialogId = dialogId; + crossfadeToDialog = dialogId != -1; + if (crossfadeToDialog) { + TLObject object; + if (dialogId > 0) { + object = user = MessagesController.getInstance(currentAccount).getUser(dialogId); + chat = null; + } else { + object = chat = MessagesController.getInstance(currentAccount).getChat(-dialogId); + user = null; + } + if (object != null) { + crossfadeAvatarDrawable.setInfo(object); + crossfageToAvatarImage.setForUserOrChat(object, crossfadeAvatarDrawable); + } + } else { + crossfageToAvatarImage.clearImage(); + } + } + } + } + + + private Drawable createVerifiedDrawable() { + Drawable verifyDrawable = ContextCompat.getDrawable(getContext(), R.drawable.verified_area).mutate(); + Drawable checkDrawable = ContextCompat.getDrawable(getContext(), R.drawable.verified_check).mutate(); + CombinedDrawable combinedDrawable = new CombinedDrawable( + verifyDrawable, + checkDrawable + ) { + + int lastColor; + @Override + public void draw(Canvas canvas) { + int color = type == TYPE_DIALOGS ? Theme.getColor(Theme.key_actionBarDefault) : Theme.getColor(Theme.key_actionBarDefaultArchived); + if (lastColor != color) { + lastColor = color; + int textColor = type == TYPE_DIALOGS ? Theme.getColor(Theme.key_actionBarDefaultTitle) : Theme.getColor(Theme.key_actionBarDefaultArchivedTitle);//Theme.getColor(Theme.key_actionBarDefaultTitle); + verifyDrawable.setColorFilter(new PorterDuffColorFilter(ColorUtils.blendARGB(textColor , color, 0.1f), PorterDuff.Mode.MULTIPLY)); + checkDrawable.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY)); + } + super.draw(canvas); + } + }; + combinedDrawable.setFullsize(true); + return combinedDrawable; + } + + private void updateCurrentState(int state) { + if (this.currentState == state) { + return; + } + int prevState = this.currentState; + this.currentState = state; + if (currentState != TRANSITION_STATE && updateOnIdleState) { + AndroidUtilities.runOnUIThread(() -> { + updateItems(true, false); + }); + } + if (currentState == EXPANDED_STATE) { + AndroidUtilities.forEachViews(recyclerListView, view -> { + view.setAlpha(1f); + view.setTranslationX(0); + view.setTranslationY(0); + }); + listViewMini.setVisibility(View.INVISIBLE); + recyclerListView.setVisibility(View.VISIBLE); + checkExpanded(); + } else if (currentState == TRANSITION_STATE) { + animateToDialogIds.clear(); + for (int i = 0; i < items.size(); i++) { + if (items.get(i).dialogId != UserConfig.getInstance(currentAccount).getClientUserId() || shouldDrawSelfInMini()) { + animateToDialogIds.add(items.get(i).dialogId); + if (animateToDialogIds.size() == 3) { + break; + } + } + } + listViewMini.setVisibility(View.INVISIBLE); + recyclerListView.setVisibility(View.VISIBLE); + } else if (currentState == COLLAPSED_STATE) { + listViewMini.setVisibility(View.VISIBLE); + recyclerListView.setVisibility(View.INVISIBLE); + layoutManager.scrollToPositionWithOffset(0, 0); + MessagesController.getInstance(currentAccount).getStoriesController().scheduleSort(); + } + invalidate(); + } + + static float getAvatarRight(int width, float progressToCollapsed) { + float size = AndroidUtilities.dp(48); + float collapsedSize = AndroidUtilities.dp(COLLAPSED_SIZE); + float finalSize = AndroidUtilities.lerp(size, collapsedSize, progressToCollapsed); + float radius = finalSize / 2f; + + float fromX = width / 2f - radius; + float x = AndroidUtilities.lerp(fromX, 0, progressToCollapsed); + return x + radius * 2; + } + + private long checkedStoryNotificationDeletion; + private void checkExpanded() { + if (System.currentTimeMillis() < checkedStoryNotificationDeletion) { + return; + } + NotificationsController.getInstance(currentAccount).processIgnoreStories(); + checkedStoryNotificationDeletion = System.currentTimeMillis() + 1000L * 60; + } + + @Override + public void setTranslationY(float translationY) { + super.setTranslationY(translationY); + if (premiumHint != null) { + premiumHint.setTranslationY(translationY); + } + } + + public HintView2 getPremiumHint() { + return premiumHint; + } + + private HintView2 makePremiumHint() { + if (premiumHint != null) { + return premiumHint; + } + premiumHint = new HintView2(getContext(), HintView2.DIRECTION_TOP) + .setBgColor(Theme.getColor(Theme.key_undo_background)) + .setMultilineText(true) + .setTextAlign(Layout.Alignment.ALIGN_CENTER) + .setJoint(0, 37 - 8); + Spannable text = AndroidUtilities.replaceSingleTag(LocaleController.getString("StoriesPremiumHint").replace('\n', ' '), Theme.key_undo_cancelColor, 0, () -> { + if (premiumHint != null) { + premiumHint.hide(); + } + fragment.presentFragment(new PremiumPreviewFragment("stories")); + }); + ClickableSpan[] spans = text.getSpans(0, text.length(), ClickableSpan.class); + if (spans != null && spans.length >= 1) { + int start = text.getSpanStart(spans[0]); + int end = text.getSpanEnd(spans[0]); + text.setSpan(new TypefaceSpan(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + premiumHint.setMaxWidthPx(HintView2.cutInFancyHalf(text, premiumHint.getTextPaint())); + premiumHint.setText(text); + premiumHint.setPadding(AndroidUtilities.dp(8), AndroidUtilities.dp(24), AndroidUtilities.dp(8), 0); + if (getParent() instanceof FrameLayout) { + ((FrameLayout) getParent()).addView(premiumHint, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 150, Gravity.LEFT | Gravity.TOP)); + } + return premiumHint; + } + + public void showPremiumHint() { + makePremiumHint(); + if (premiumHint != null) { + if (premiumHint.shown()) { + BotWebViewVibrationEffect.APP_ERROR.vibrate(); + } + premiumHint.show(); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/HwLayouts.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/HwLayouts.java new file mode 100644 index 0000000000..32d8b23faf --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/HwLayouts.java @@ -0,0 +1,187 @@ +package org.telegram.ui.Stories; + +import static org.telegram.messenger.SharedConfig.PERFORMANCE_CLASS_HIGH; +import static org.telegram.messenger.SharedConfig.getDevicePerformanceClass; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.view.TextureView; +import android.view.View; +import android.widget.FrameLayout; + +import org.telegram.messenger.ImageReceiver; + +import androidx.annotation.NonNull; +import androidx.annotation.UiThread; + +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AvatarsImageView; + +import java.util.HashSet; +import java.util.Set; + +class HwFrameLayout extends FrameLayout { + static final Set hwViews = new HashSet<>(); + static boolean hwEnabled = false; + + private final boolean isFastDevice; + + public HwFrameLayout(@NonNull Context context) { + super(context); + isFastDevice = getDevicePerformanceClass() == PERFORMANCE_CLASS_HIGH; + } + + private void disableHwAcceleration(boolean withLayer) { + hwEnabled = false; + if (withLayer) { + setLayerType(View.LAYER_TYPE_NONE, null); + } + for (View view : hwViews) { + view.invalidate(); + } + hwViews.clear(); + } + + @UiThread + public void enableHwAcceleration() { + hwEnabled = true; + setLayerType(View.LAYER_TYPE_HARDWARE, null); + } + + @UiThread + public void disableHwAcceleration() { + disableHwAcceleration(true); + } + + /** + * Used for early transition {@link ImageReceiver#checkAlphaAnimation} from blurred preview to normal for high-end devices. + * The flag {@link View#LAYER_TYPE_HARDWARE} still keeping. With the flag it will be faster with redraws than without. + */ + @UiThread + public void checkHwAcceleration(float progress) { + if (progress > 0.6f && hwEnabled && isFastDevice) { + disableHwAcceleration(false); + } + } + + @Override + public void invalidate() { + if (hwEnabled) { + hwViews.add(this); + return; + } + super.invalidate(); + } + + @Override + public void invalidate(int l, int t, int r, int b) { + if (hwEnabled) { + hwViews.add(this); + return; + } + super.invalidate(l, t, r, b); + } +} + +class HwTextureView extends TextureView { + + public HwTextureView(@NonNull Context context) { + super(context); + } + + @Override + public void invalidate() { + if (HwFrameLayout.hwEnabled) { + HwFrameLayout.hwViews.add(this); + return; + } + super.invalidate(); + } + + @Override + public void invalidate(int l, int t, int r, int b) { + if (HwFrameLayout.hwEnabled) { + HwFrameLayout.hwViews.add(this); + return; + } + super.invalidate(l, t, r, b); + } +} + +@SuppressLint("ViewConstructor") +class HwStoriesViewPager extends StoriesViewPager { + + public HwStoriesViewPager(@NonNull Context context, StoryViewer storyViewer, Theme.ResourcesProvider resourcesProvider) { + super(context, storyViewer, resourcesProvider); + } + + @Override + public void invalidate() { + if (HwFrameLayout.hwEnabled) { + HwFrameLayout.hwViews.add(this); + return; + } + super.invalidate(); + } + + @Override + public void invalidate(int l, int t, int r, int b) { + if (HwFrameLayout.hwEnabled) { + HwFrameLayout.hwViews.add(this); + return; + } + super.invalidate(l, t, r, b); + } +} + +@SuppressLint("ViewConstructor") +class HwAvatarsImageView extends AvatarsImageView { + + public HwAvatarsImageView(@NonNull Context context, boolean inCall) { + super(context, inCall); + } + + @Override + public void invalidate() { + if (HwFrameLayout.hwEnabled) { + HwFrameLayout.hwViews.add(this); + return; + } + super.invalidate(); + } + + @Override + public void invalidate(int l, int t, int r, int b) { + if (HwFrameLayout.hwEnabled) { + HwFrameLayout.hwViews.add(this); + return; + } + super.invalidate(l, t, r, b); + } +} + +@SuppressLint("ViewConstructor") +class HwPeerStoriesView extends PeerStoriesView { + + public HwPeerStoriesView(@NonNull Context context, StoryViewer storyViewer, SharedResources sharedResources, Theme.ResourcesProvider resourcesProvider) { + super(context, storyViewer, sharedResources, resourcesProvider); + } + + @Override + public void invalidate() { + if (HwFrameLayout.hwEnabled) { + HwFrameLayout.hwViews.add(this); + return; + } + super.invalidate(); + } + + @Override + public void invalidate(int l, int t, int r, int b) { + if (HwFrameLayout.hwEnabled) { + HwFrameLayout.hwViews.add(this); + return; + } + super.invalidate(l, t, r, b); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/MessageMediaStoryFull.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/MessageMediaStoryFull.java new file mode 100644 index 0000000000..7d508e0e41 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/MessageMediaStoryFull.java @@ -0,0 +1,24 @@ +package org.telegram.ui.Stories; + +import org.telegram.tgnet.AbstractSerializedData; +import org.telegram.tgnet.TLRPC; + +public class MessageMediaStoryFull extends TLRPC.TL_messageMediaStory { + + public static int constructor = 0xc79aee1d; + + public void readParams(AbstractSerializedData stream, boolean exception) { + user_id = stream.readInt64(exception); + id = stream.readInt32(exception); + storyItem = TLRPC.StoryItem.TLdeserialize(stream, stream.readInt32(exception), exception); + via_mention = stream.readBool(exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(user_id); + stream.writeInt32(id); + storyItem.serializeToStream(stream); + stream.writeBool(via_mention); + } +} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/MessageMediaStoryFull_old.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/MessageMediaStoryFull_old.java new file mode 100644 index 0000000000..4921a7762d --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/MessageMediaStoryFull_old.java @@ -0,0 +1,26 @@ +package org.telegram.ui.Stories; + +import org.telegram.tgnet.AbstractSerializedData; +import org.telegram.tgnet.TLRPC; + +public class MessageMediaStoryFull_old extends TLRPC.TL_messageMediaStory { + + public MessageMediaStoryFull_old() { + + } + + public static int constructor = 0xc79aee1f; + + public void readParams(AbstractSerializedData stream, boolean exception) { + user_id = stream.readInt64(exception); + id = stream.readInt32(exception); + storyItem = TLRPC.StoryItem.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(user_id); + stream.writeInt32(id); + storyItem.serializeToStream(stream); + } +} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/PeerStoriesView.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/PeerStoriesView.java new file mode 100644 index 0000000000..c4ac21db8b --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/PeerStoriesView.java @@ -0,0 +1,4637 @@ +package org.telegram.ui.Stories; + +import static org.telegram.messenger.AndroidUtilities.dp; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.app.Activity; +import android.app.Dialog; +import android.content.ClipData; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.text.Layout; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.TextUtils; +import android.text.style.CharacterStyle; +import android.text.style.ClickableSpan; +import android.text.style.URLSpan; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.HapticFeedbackConstants; +import android.view.Menu; +import android.view.MotionEvent; +import android.view.SurfaceView; +import android.view.TextureView; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.collection.LongSparseArray; +import androidx.core.content.ContextCompat; +import androidx.core.graphics.ColorUtils; +import androidx.core.math.MathUtils; +import androidx.recyclerview.widget.ChatListItemAnimator; + +import org.telegram.messenger.AccountInstance; +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimationNotificationsLocker; +import org.telegram.messenger.BotWebViewVibrationEffect; +import org.telegram.messenger.BuildVars; +import org.telegram.messenger.ContactsController; +import org.telegram.messenger.Emoji; +import org.telegram.messenger.FileLoader; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.ImageLoader; +import org.telegram.messenger.ImageLocation; +import org.telegram.messenger.ImageReceiver; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MediaController; +import org.telegram.messenger.MediaDataController; +import org.telegram.messenger.MessageObject; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.NotificationsController; +import org.telegram.messenger.R; +import org.telegram.messenger.SendMessagesHelper; +import org.telegram.messenger.SharedConfig; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.UserObject; +import org.telegram.messenger.Utilities; +import org.telegram.messenger.VideoEditedInfo; +import org.telegram.messenger.browser.Browser; +import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.RequestDelegate; +import org.telegram.tgnet.TLObject; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.ActionBar; +import org.telegram.ui.ActionBar.ActionBarMenuItem; +import org.telegram.ui.ActionBar.ActionBarMenuSubItem; +import org.telegram.ui.ActionBar.ActionBarPopupWindow; +import org.telegram.ui.ActionBar.AdjustPanLayoutHelper; +import org.telegram.ui.ActionBar.AlertDialog; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.BottomSheet; +import org.telegram.ui.ActionBar.SimpleTextView; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Cells.TextSelectionHelper; +import org.telegram.ui.ChatActivity; +import org.telegram.ui.Components.AlertsCreator; +import org.telegram.ui.Components.AnimatedEmojiDrawable; +import org.telegram.ui.Components.AnimatedEmojiSpan; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.AvatarDrawable; +import org.telegram.ui.Components.AvatarsImageView; +import org.telegram.ui.Components.BackupImageView; +import org.telegram.ui.Components.BitmapShaderTools; +import org.telegram.ui.Components.Bulletin; +import org.telegram.ui.Components.BulletinFactory; +import org.telegram.ui.Components.ChatActivityEnterView; +import org.telegram.ui.Components.ChatAttachAlert; +import org.telegram.ui.Components.ChatAttachAlertDocumentLayout; +import org.telegram.ui.Components.CombinedDrawable; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.CustomPopupMenu; +import org.telegram.ui.Components.DotDividerSpan; +import org.telegram.ui.Components.EmojiPacksAlert; +import org.telegram.ui.Components.HintView; +import org.telegram.ui.Components.InstantCameraView; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.LoadingDrawable; +import org.telegram.ui.Components.MediaActivity; +import org.telegram.ui.Components.MentionsContainerView; +import org.telegram.ui.Components.RLottieDrawable; +import org.telegram.ui.Components.RLottieImageView; +import org.telegram.ui.Components.RadialProgress; +import org.telegram.ui.Components.Reactions.ReactionsEffectOverlay; +import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble; +import org.telegram.ui.Components.ReactionsContainerLayout; +import org.telegram.ui.Components.ScaleStateListAnimator; +import org.telegram.ui.Components.ShareAlert; +import org.telegram.ui.Components.SizeNotifierFrameLayout; +import org.telegram.ui.Components.TextStyleSpan; +import org.telegram.ui.Components.URLSpanMono; +import org.telegram.ui.Components.URLSpanNoUnderline; +import org.telegram.ui.Components.URLSpanReplacement; +import org.telegram.ui.Components.URLSpanUserMention; +import org.telegram.ui.Components.voip.CellFlickerDrawable; +import org.telegram.ui.LaunchActivity; +import org.telegram.ui.NotificationsCustomSettingsActivity; +import org.telegram.ui.PinchToZoomHelper; +import org.telegram.ui.ProfileActivity; +import org.telegram.ui.Stories.recorder.CaptionContainerView; +import org.telegram.ui.Stories.recorder.HintView2; +import org.telegram.ui.Stories.recorder.StoryEntry; +import org.telegram.ui.Stories.recorder.StoryPrivacyBottomSheet; +import org.telegram.ui.Stories.recorder.StoryRecorder; +import org.telegram.ui.WrappedResourceProvider; + +import java.io.File; +import java.io.UnsupportedEncodingException; +import java.net.IDN; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Locale; + +public class PeerStoriesView extends SizeNotifierFrameLayout implements NotificationCenter.NotificationCenterDelegate { + + public static final float SHARE_BUTTON_OFFSET = 46; + private final static long IMAGE_LIVE_TIME = 10_000; + private final ImageView optionsIconView; + private final FrameLayout muteIconContainer; + private final RLottieImageView muteIconView; + private final ImageView noSoundIconView; + private final Theme.ResourcesProvider resourcesProvider; +// private final CloseFriendsBadge closeFriendsBadge; + private final StoryPrivacyButton privacyButton; + private HintView2 privacyHint; + private HintView2 soundTooltip; + private int reactionsContainerIndex; + private final StoryViewer storyViewer; + private final StoryCaptionView storyCaptionView; + private CaptionContainerView storyEditCaptionView; + private final ImageView shareButton; + private long currentImageTime; + private long lastDrawTime; + private boolean switchEventSent; + private boolean lastNoThumb; + private boolean attachedToWindow; + private boolean allowDrawSurface = true; + + public FrameLayout storyContainer; + private final ImageReceiver imageReceiver; + private final ImageReceiver leftPreloadImageReceiver; + private final ImageReceiver rightPreloadImageReceiver; + private Runnable onImageReceiverThumbLoaded; + + private ImageReceiver viewsThumbImageReceiver; + private float viewsThumbAlpha; + + private final AvatarDrawable avatarDrawable; + PeerHeaderView headerView; + private final StoryLinesDrawable storyLines; + private StoryPositionView storyPositionView; + + private int shiftDp = -5; + ActionBarMenuSubItem editStoryItem; + CustomPopupMenu popupMenu; + + TLRPC.TL_userStories userStories; + final ArrayList storyItems; + final ArrayList uploadingStories; + final SharedResources sharedResources; + RoundRectOutlineProvider outlineProvider; + + ArrayList day; + + int count; + private long dialogId; + boolean isSelf; + + private float alpha = 1f; + private int previousSelectedPotision = -1; + private int selectedPosition; + boolean isActive; + + private int listPosition; + private int linesPosition, linesCount; + + public final StoryItemHolder currentStory = new StoryItemHolder(); + private final BitmapShaderTools bitmapShaderTools; + + Delegate delegate; + private boolean paused; + StoriesController storiesController; + private boolean isUploading, isEditing; + private FrameLayout selfView; + ChatActivityEnterView chatActivityEnterView; + private ValueAnimator changeBoundAnimator; + ReactionsContainerLayout reactionsContainerLayout; + + Paint inputBackgroundPaint; + + int lastKeyboardHeight; + ValueAnimator keyboardAnimator; + float progressToKeyboard = -1; + float progressToDismiss = -1; + float lastAnimatingKeyboardHeight = -1; + int lastOpenedKeyboardHeight; + boolean animateKeyboardOpening; + public boolean keyboardVisible; + private boolean BIG_SCREEN; + private int realKeyboardHeight; + private int classGuid = ConnectionsManager.generateClassGuid(); + private TextView selfStatusView; + private AvatarsImageView selfAvatarsView; + private int currentAccount; + private int totalStoriesCount; + private boolean deletedPeer; + private View selfAvatarsContainer; + boolean showViewsProgress; + private Runnable cancellableViews; + private boolean isRecording; + private float animatingKeyboardHeight; + private ChatAttachAlert chatAttachAlert; + private InstantCameraView instantCameraView; + private int enterViewBottomOffset; + private boolean isLongPressed; + + final VideoPlayerSharedScope playerSharedScope; + final AnimationNotificationsLocker notificationsLocker; + private AnimatedFloat progressToHideInterface = new AnimatedFloat(this); + private float prevToHideProgress; + private long videoDuration; + private boolean allowShare, allowShareLink; + public boolean forceUpdateOffsets; + private HintView mediaBanTooltip; + + public PinchToZoomHelper pinchToZoomHelper = new PinchToZoomHelper(); + private boolean imageChanged; + public ShareAlert shareAlert; + private FrameLayout unsupportedContainer; + private TextView replyDisabledTextView; + public boolean unsupported; + private boolean isVideoStory; + private MentionsContainerView mentionContainer; + private float muteIconViewAlpha = 1f; + private boolean isVisible; + boolean inBlackoutMode; + boolean checkBlackoutMode; + private boolean messageSent; + private boolean isCaptionPartVisible; + + public PeerStoriesView(@NonNull Context context, StoryViewer storyViewer, SharedResources sharedResources, Theme.ResourcesProvider resourcesProvider) { + super(context); + pinchToZoomHelper.setCallback(new PinchToZoomHelper.Callback() { + @Override + public void onZoomStarted(MessageObject messageObject) { + delegate.setIsInPinchToZoom(true); + } + + @Override + public void onZoomFinished(MessageObject messageObject) { + delegate.setIsInPinchToZoom(false); + } + }); + playerSharedScope = new VideoPlayerSharedScope(); + notificationsLocker = new AnimationNotificationsLocker(); + this.storyItems = new ArrayList<>(); + this.uploadingStories = new ArrayList<>(); + + this.imageReceiver = new ImageReceiver() { + + @Override + protected boolean setImageBitmapByKey(Drawable drawable, String key, int type, boolean memCache, int guid) { + boolean r = super.setImageBitmapByKey(drawable, key, type, memCache, guid); + if (type == TYPE_THUMB && onImageReceiverThumbLoaded != null) { + onImageReceiverThumbLoaded.run(); + onImageReceiverThumbLoaded = null; + } + return r; + } + }; + this.imageReceiver.setCrossfadeWithOldImage(false); + this.imageReceiver.setAllowLoadingOnAttachedOnly(true); + this.imageReceiver.ignoreNotifications = true; + this.imageReceiver.setFileLoadingPriority(FileLoader.PRIORITY_LOW); + + this.leftPreloadImageReceiver = new ImageReceiver(); + this.leftPreloadImageReceiver.setAllowLoadingOnAttachedOnly(true); + this.leftPreloadImageReceiver.ignoreNotifications = true; + this.leftPreloadImageReceiver.setFileLoadingPriority(FileLoader.PRIORITY_LOW); + + this.rightPreloadImageReceiver = new ImageReceiver(); + this.rightPreloadImageReceiver.setAllowLoadingOnAttachedOnly(true); + this.rightPreloadImageReceiver.ignoreNotifications = true; + this.rightPreloadImageReceiver.setFileLoadingPriority(FileLoader.PRIORITY_LOW); + imageReceiver.setPreloadingReceivers(Arrays.asList(leftPreloadImageReceiver, rightPreloadImageReceiver)); + + this.avatarDrawable = new AvatarDrawable(); + this.storyViewer = storyViewer; + this.sharedResources = sharedResources; + this.bitmapShaderTools = sharedResources.bitmapShaderTools; + storiesController = MessagesController.getInstance(UserConfig.selectedAccount).getStoriesController(); + sharedResources.dimPaint.setColor(Color.BLACK); + inputBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + this.resourcesProvider = resourcesProvider; + setClipChildren(false); + + storyContainer = new HwFrameLayout(context) { + + AnimatedFloat progressToAudio = new AnimatedFloat(this, 150, CubicBezierInterpolator.DEFAULT); + AnimatedFloat progressToFullBlackoutA = new AnimatedFloat(this, 150, CubicBezierInterpolator.DEFAULT); + CellFlickerDrawable loadingDrawable = new CellFlickerDrawable(); + AnimatedFloat loadingDrawableAlpha2 = new AnimatedFloat(this); + AnimatedFloat loadingDrawableAlpha = new AnimatedFloat(this); + { + loadingDrawableAlpha2.setDuration(500); + loadingDrawableAlpha.setDuration(100); + } + + boolean splitDrawing; + boolean drawOverlayed; + + @Override + protected void dispatchDraw(Canvas canvas) { + if (!isActive) { + headerView.backupImageView.getImageReceiver().setVisible(true, true); + } + if (!unsupported) { + if (playerSharedScope.renderView != null) { + invalidate(); + } + canvas.save(); + pinchToZoomHelper.applyTransform(canvas); + if (playerSharedScope.renderView != null && playerSharedScope.firstFrameRendered) { + //playerSharedScope.surfaceView.draw(canvas); + if (!imageReceiver.hasBitmapImage()) { + sharedResources.imageBackgroundDrawable.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight() + 1); + sharedResources.imageBackgroundDrawable.draw(canvas); + } + imageReceiver.setImageCoords(0, 0, getMeasuredWidth(), getMeasuredHeight() + 1); + imageReceiver.draw(canvas); + if (isActive) { + if (storyViewer.USE_SURFACE_VIEW && playerSharedScope.player != null && playerSharedScope.player.paused && storyViewer.playerStubBitmap != null && playerSharedScope.player.stubAvailable) { + float sx = getMeasuredWidth() / (float) storyViewer.playerStubBitmap.getWidth(); + float sy = getMeasuredHeight() / (float) storyViewer.playerStubBitmap.getHeight(); + canvas.save(); + canvas.scale(sx, sy); + canvas.drawBitmap(storyViewer.playerStubBitmap, 0, 0, storyViewer.playerStubPaint); + canvas.restore(); + } else { + if (!storyViewer.USE_SURFACE_VIEW || allowDrawSurface) { + playerSharedScope.renderView.draw(canvas); + } + } + } + + } else { + if (playerSharedScope.renderView != null) { + invalidate(); + } + if (currentStory.skipped) { + canvas.drawColor(ColorUtils.blendARGB(Color.BLACK, Color.WHITE, 0.2f)); + } else { + if (!imageReceiver.hasBitmapImage()) { + sharedResources.imageBackgroundDrawable.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight() + 1); + sharedResources.imageBackgroundDrawable.draw(canvas); + } + imageReceiver.setImageCoords(0, 0, getMeasuredWidth(), getMeasuredHeight() + 1); + imageReceiver.draw(canvas); + } + } + canvas.restore(); + if (imageChanged) { + loadingDrawableAlpha2.set(0, true); + loadingDrawableAlpha.set(0, true); + } + loadingDrawableAlpha2.set(!imageReceiver.hasNotThumb() && (playerSharedScope.renderView == null || !playerSharedScope.firstFrameRendered) && currentStory.uploadingStory == null ? 1f : 0f); + loadingDrawableAlpha.set(loadingDrawableAlpha2.get() == 1f ? 1f : 0); + + if (loadingDrawableAlpha.get() > 0) { + AndroidUtilities.rectTmp.set(0, 0, getMeasuredWidth(), getMeasuredHeight()); + loadingDrawable.setAlpha((int) (255 * loadingDrawableAlpha.get())); + loadingDrawable.setParentWidth(getMeasuredWidth() * 2); + loadingDrawable.animationSpeedScale = 1.3f; + loadingDrawable.draw(canvas, AndroidUtilities.rectTmp, dp(10), this); + } + imageChanged = false; + } else { + canvas.drawColor(ColorUtils.blendARGB(Color.BLACK, Color.WHITE, 0.2f)); + } + + if (!lastNoThumb && imageReceiver.hasNotThumb()) { + lastNoThumb = true; + PeerStoriesView.this.invalidate(); + } + float hideInterfaceAlpha = getHideInterfaceAlpha(); + + + sharedResources.topOverlayGradient.setAlpha((int) (255 * hideInterfaceAlpha)); + sharedResources.topOverlayGradient.draw(canvas); + float progressToFullBlackout = 0; + if ((isSelf || !BIG_SCREEN) || storyCaptionView.getVisibility() == View.VISIBLE) { + if (storyCaptionView.getVisibility() == View.VISIBLE) { + int gradientHeight = dp(72); + int gradientTop = (int) (storyCaptionView.getTextTop() - dp(24)) + storyCaptionView.getTop(); + int gradientBottom = gradientTop + gradientHeight; + float startFullBlackoutFrom = getMeasuredHeight() * 0.65f; + boolean hideCaptionWithInterface = hideCaptionWithInterface(); + if ((startFullBlackoutFrom - gradientTop) / dp(60) > 0 && storyCaptionView.isTouched() && storyCaptionView.hasScroll()) { + int maxGradientTop = (int) (storyCaptionView.getMaxTop() - dp(24)) + storyCaptionView.getTop(); + if ((startFullBlackoutFrom - maxGradientTop) / dp(60) > 0) { + inBlackoutMode = true; + } + } else if (checkBlackoutMode) { + checkBlackoutMode = false; + int maxGradientTop = (int) (storyCaptionView.getMaxTop() - dp(24)) + storyCaptionView.getTop(); + if ((startFullBlackoutFrom - maxGradientTop) / dp(60) > 0) { + inBlackoutMode = true; + } + } else if (storyCaptionView.getProgressToBlackout() == 0) { + inBlackoutMode = false; + } + float hideCaptionAlpha = hideCaptionWithInterface ? hideInterfaceAlpha : 1f; + progressToFullBlackout = progressToFullBlackoutA.set(inBlackoutMode ? 1f : 0); + if (progressToFullBlackout > 0) { + splitDrawing = true; + drawOverlayed = false; + super.dispatchDraw(canvas); + splitDrawing = false; + drawLines(canvas); + sharedResources.gradientBackgroundPaint.setColor(ColorUtils.setAlphaComponent(Color.BLACK, (int) (255 * 0.6f * progressToFullBlackout * hideCaptionAlpha))); + canvas.drawPaint(sharedResources.gradientBackgroundPaint); + } + if (progressToFullBlackout < 1f) { + canvas.save(); + sharedResources.gradientBackgroundPaint.setColor(ColorUtils.setAlphaComponent(Color.BLACK, (int) (255 * 0.506f * (1f - progressToFullBlackout) * hideCaptionAlpha))); + sharedResources.bottomOverlayGradient.setAlpha((int) (255 * (1f - progressToFullBlackout) * hideCaptionAlpha)); + sharedResources.bottomOverlayGradient.setBounds(0, gradientTop, getMeasuredWidth(), gradientBottom); + sharedResources.bottomOverlayGradient.draw(canvas); + canvas.drawRect(0, gradientBottom, getMeasuredWidth(), getMeasuredHeight(), sharedResources.gradientBackgroundPaint); + canvas.restore(); + } + if (progressToFullBlackout > 0 && storyCaptionView.getAlpha() > 0) { + storyCaptionView.disableDraw(false); + if (storyCaptionView.getAlpha() != 1f) { + canvas.saveLayerAlpha(0, 0, getMeasuredWidth(), getMeasuredHeight(), (int) (255 * storyCaptionView.getAlpha()), Canvas.ALL_SAVE_FLAG); + } else { + canvas.save(); + } + canvas.translate(storyCaptionView.getX(), storyCaptionView.getY() - storyCaptionView.getScrollY()); + storyCaptionView.draw(canvas); + canvas.restore(); + } + storyCaptionView.disableDraw(progressToFullBlackout > 0); + if (progressToFullBlackout > 0) { + splitDrawing = true; + drawOverlayed = true; + super.dispatchDraw(canvas); + splitDrawing = false; + } + } else { + int bottomGradientHeight = BIG_SCREEN ? dp(56) : dp(110); + if ((isSelf || !BIG_SCREEN) && storyCaptionView.getVisibility() == View.VISIBLE) { + bottomGradientHeight *= 2.5f; + } + sharedResources.bottomOverlayGradient.setBounds(0, storyContainer.getMeasuredHeight() - bottomGradientHeight, getMeasuredWidth(), storyContainer.getMeasuredHeight()); + + sharedResources.bottomOverlayGradient.setAlpha((int) (255 * hideInterfaceAlpha)); + sharedResources.bottomOverlayGradient.draw(canvas); + } + } + if (viewsThumbAlpha != 0 && viewsThumbImageReceiver != null) { + viewsThumbImageReceiver.setImageCoords(0, 0, getMeasuredWidth(), getMeasuredHeight() + 1); + viewsThumbImageReceiver.setAlpha(viewsThumbAlpha); + viewsThumbImageReceiver.draw(canvas); + viewsThumbImageReceiver.setAlpha(1f); + } + + progressToAudio.set(isRecording ? 1f : 0); + + if (isActive) { + boolean isCaption = storyCaptionView.getVisibility() == View.VISIBLE && (inBlackoutMode || storyCaptionView.isTouched()); + isCaptionPartVisible = storyCaptionView.getVisibility() == View.VISIBLE && (storyCaptionView.getProgressToBlackout() > 0); + delegate.setIsCaption(isCaption); + delegate.setIsCaptionPartVisible(isCaptionPartVisible); + } + if (progressToFullBlackout <= 0) { + super.dispatchDraw(canvas); + drawLines(canvas); + } + } + + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + if (splitDrawing) { + if (Bulletin.getVisibleBulletin() != null && child == Bulletin.getVisibleBulletin().getLayout()) { + if (drawOverlayed) { + return super.drawChild(canvas, child, drawingTime); + } else { + return true; + } + } else { + return super.drawChild(canvas, child, drawingTime); + } + } else { + return super.drawChild(canvas, child, drawingTime); + } + } + + private void drawLines(Canvas canvas) { + if (imageReceiver.hasNotThumb() || (currentStory.isVideo && playerSharedScope.firstFrameRendered)) { + currentStory.checkSendView(); + } + + float timeProgress = 0; + float hideInterfaceAlpha = getHideInterfaceAlpha(); + if (currentStory.isVideo()) { + if (playerSharedScope.player != null) { + float p = playerSharedScope.player.getPlaybackProgress(videoDuration); + timeProgress = Utilities.clamp(p, 1f, 0f); + } + invalidate(); + } else if (!paused && isActive && !isUploading && !isEditing && imageReceiver.hasNotThumb()) { + long currentTime = System.currentTimeMillis(); + if (lastDrawTime != 0) { + if (!isCaptionPartVisible) { + currentImageTime += currentTime - lastDrawTime; + } + } + lastDrawTime = currentTime; + timeProgress = Utilities.clamp(currentImageTime / (float) IMAGE_LIVE_TIME, 1f, 0f); + invalidate(); + } else { + timeProgress = Utilities.clamp(currentImageTime / (float) IMAGE_LIVE_TIME, 1f, 0f); + } + + if (!switchEventSent && timeProgress == 1f && !(currentStory.isVideo && isCaptionPartVisible)) { + switchEventSent = true; + post(() -> { + if (delegate != null) { + if (isUploading || isEditing) { + if (currentStory.isVideo()) { + playerSharedScope.player.loopBack(); + } else { + currentImageTime = 0; + } + } else { + delegate.shouldSwitchToNext(); + } + } + }); + } + if (storyViewer.storiesList != null) { + if (storyPositionView == null) { + storyPositionView = new StoryPositionView(); + } + storyPositionView.draw(canvas, hideInterfaceAlpha * alpha * (1f - outT), listPosition, storyViewer.storiesList.getCount(), this, headerView); + } + canvas.save(); + canvas.translate(0, dp(8) - dp(8) * outT); + boolean buffering = currentStory.isVideo() && playerSharedScope.isBuffering(); + storyLines.draw(canvas, getMeasuredWidth(), linesPosition, timeProgress, linesCount, hideInterfaceAlpha, alpha * (1f - outT), buffering); + canvas.restore(); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + Bulletin.addDelegate(this, new Bulletin.Delegate() { + @Override + public int getTopOffset(int tag) { + return dp(58); + } + + public boolean clipWithGradient(int tag) { + return tag == 1 || tag == 2; + } + + @Override + public void onShow(Bulletin bulletin) { + if (bulletin != null && bulletin.tag == 2 && delegate != null) { + delegate.setBulletinIsVisible(true); + } + } + + @Override + public void onHide(Bulletin bulletin) { + if (bulletin != null && bulletin.tag == 2 && delegate != null) { + delegate.setBulletinIsVisible(false); + } + } + + @Override + public int getBottomOffset(int tag) { + return BIG_SCREEN ? 0 : dp(64); + } + }); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + Bulletin.removeDelegate(this); + if (delegate != null) { + delegate.setBulletinIsVisible(false); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + LayoutParams layoutParams = (LayoutParams) muteIconContainer.getLayoutParams(); + if (drawLinesAsCounter()) { + layoutParams.rightMargin = dp(2); + layoutParams.topMargin = dp(15 + 40); + } else { + layoutParams.rightMargin = dp(2 + 40); + layoutParams.topMargin = dp(15); + } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + }; + storyContainer.setClipChildren(false); +// SurfaceView surfaceView = new SurfaceView(context); +// playerSharedScope.surfaceView = surfaceView; + // storyContainer.addView(surfaceView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + // storyContainer.setClipChildren(false); + + storyCaptionView = new StoryCaptionView(getContext(), storyViewer.resourcesProvider) { + @Override + public void onLinkClick(CharacterStyle span, View spoilersTextView) { + if (span instanceof URLSpanUserMention) { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(Utilities.parseLong(((URLSpanUserMention) span).getURL())); + if (user != null) { + MessagesController.openChatOrProfileWith(user, null, storyViewer.fragment, 0, false); + } + } else if (span instanceof URLSpanNoUnderline) { + String str = ((URLSpanNoUnderline) span).getURL(); + String username = Browser.extractUsername(str); + if (username != null) { + username = username.toLowerCase(); + if (str.startsWith("@")) { + MessagesController.getInstance(currentAccount).openByUserName(username, storyViewer.fragment, 0, null); + } else { + processExternalUrl(0, str, span, false); + } + } else { + processExternalUrl(0, str, span, false); + } + } else if (span instanceof URLSpan) { + String url = ((URLSpan) span).getURL(); + processExternalUrl(2, url, span, span instanceof URLSpanReplacement); + } else if (span instanceof URLSpanMono) { + ((URLSpanMono) span).copyToClipboard(); + BulletinFactory.of(storyContainer, resourcesProvider).createCopyBulletin(LocaleController.getString("TextCopied", R.string.TextCopied)).show(); + } else if (span instanceof ClickableSpan) { + ((ClickableSpan) span).onClick(spoilersTextView); + } + } + + private void processExternalUrl(int type, String url, CharacterStyle span, boolean forceAlert) { + if (forceAlert || AndroidUtilities.shouldShowUrlInAlert(url)) { + if (type == 0 || type == 2) { + boolean forceNotInternalForApps = false; + if (span instanceof URLSpanReplacement && (((URLSpanReplacement) span).getTextStyleRun().flags & TextStyleSpan.FLAG_STYLE_TEXT_URL) != 0) { + forceNotInternalForApps = true; + } + AlertsCreator.showOpenUrlAlert(storyViewer.fragment, url, true, true, true, forceNotInternalForApps, null, resourcesProvider); + } else if (type == 1) { + AlertsCreator.showOpenUrlAlert(storyViewer.fragment, url, true, true, false, null, resourcesProvider); + } + } else { + if (type == 0) { + Browser.openUrl(getContext(), Uri.parse(url), true, true, null); + } else if (type == 1) { + Browser.openUrl(getContext(), Uri.parse(url), false, false, null); + } else if (type == 2) { + Browser.openUrl(getContext(), Uri.parse(url), false, true, null); + } + } + } + + @Override + public void onLinkLongPress(URLSpan span, View spoilersTextView, Runnable done) { + final String urlFinal = span.getURL(); + String formattedUrl = span.getURL(); + try { + try { + Uri uri = Uri.parse(formattedUrl); + formattedUrl = Browser.replaceHostname(uri, IDN.toUnicode(uri.getHost(), IDN.ALLOW_UNASSIGNED)); + } catch (Exception e) { + FileLog.e(e, false); + } + formattedUrl = URLDecoder.decode(formattedUrl.replaceAll("\\+", "%2b"), "UTF-8"); + } catch (Exception e) { + FileLog.e(e); + } + try { + performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); + } catch (Exception ignore) {} + BottomSheet.Builder builder = new BottomSheet.Builder(getContext(), false, resourcesProvider); + builder.setTitle(formattedUrl); + builder.setTitleMultipleLines(true); + builder.setItems(currentStory != null && !currentStory.allowScreenshots() ? new CharSequence[] {LocaleController.getString("Open", R.string.Open)} : new CharSequence[]{LocaleController.getString("Open", R.string.Open), LocaleController.getString("Copy", R.string.Copy)}, (dialog, which) -> { + if (which == 0) { + onLinkClick(span, spoilersTextView); + } else if (which == 1) { + AndroidUtilities.addToClipboard(urlFinal); + BulletinFactory.of(storyContainer, resourcesProvider).createCopyLinkBulletin().show(); + } + }); + builder.setOnPreDismissListener(di -> done.run()); + BottomSheet sheet = builder.create(); + sheet.fixNavigationBar(Theme.getColor(Theme.key_dialogBackground, resourcesProvider)); + delegate.showDialog(sheet); + } + + @Override + public void onEmojiClick(AnimatedEmojiSpan span) { + if (span == null || delegate == null) { + return; + } + TLRPC.Document document = span.document; + if (document == null) { + document = AnimatedEmojiDrawable.findDocument(currentAccount, span.documentId); + } + if (document == null) { + return; + } + Bulletin bulletin = BulletinFactory.of(storyContainer, resourcesProvider).createContainsEmojiBulletin(document, BulletinFactory.CONTAINS_EMOJI_IN_STORY, set -> { + ArrayList inputSets = new ArrayList<>(1); + inputSets.add(set); + EmojiPacksAlert alert = new EmojiPacksAlert(storyViewer.fragment, getContext(), resourcesProvider, inputSets); + if (delegate != null) { + delegate.showDialog(alert); + } + }); + if (bulletin == null) { + return; + } + bulletin.tag = 1; + bulletin.show(true); + } + }; + storyCaptionView.captionTextview.setOnClickListener(v -> { + if (storyCaptionView.expanded) { + if (!storyCaptionView.textSelectionHelper.isInSelectionMode()) { + storyCaptionView.collapse(); + } else { + storyCaptionView.checkCancelTextSelection(); + } + } else { + checkBlackoutMode = true; + storyCaptionView.expand(); + } + }); + + shareButton = new ImageView(context); + shareButton.setImageDrawable(sharedResources.shareDrawable); + int padding = dp(8); + shareButton.setPadding(padding, padding, padding, padding); + shareButton.setOnClickListener(v -> { + shareStory(true); + }); + + imageReceiver.setAllowLoadingOnAttachedOnly(true); + imageReceiver.setParentView(storyContainer); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + outlineProvider = new RoundRectOutlineProvider(10); + storyContainer.setOutlineProvider(outlineProvider); + storyContainer.setClipToOutline(true); + } + addView(storyContainer); + headerView = new PeerHeaderView(context, currentStory); + headerView.setOnClickListener(v -> { + if (UserConfig.getInstance(currentAccount).clientUserId == dialogId) { + Bundle args = new Bundle(); + args.putInt("type", MediaActivity.TYPE_STORIES); + args.putLong("dialog_id", dialogId); + MediaActivity mediaActivity = new MediaActivity(args, null); + storyViewer.presentFragment(mediaActivity); + } else { + Bundle args = new Bundle(); + args.putLong("user_id", dialogId); + ProfileActivity profileActivity = new ProfileActivity(args); + + BaseFragment.BottomSheetParams params = new BaseFragment.BottomSheetParams(); + params.transitionFromLeft = true; + params.allowNestedScroll = false; + storyViewer.presentFragment(profileActivity); + } + +// LaunchActivity.getLastFragment().showAsSheet(profileActivity, params); +// profileActivity.showAsActivity(); + }); + storyContainer.addView(headerView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, 17, 0, 0)); + + + addView(shareButton, LayoutHelper.createFrame(40, 40, Gravity.RIGHT, 10, 10, 10, 10)); + + optionsIconView = new ImageView(context); + optionsIconView.setImageDrawable(sharedResources.optionsDrawable); + optionsIconView.setPadding(dp(8), dp(8), dp(8), dp(8)); + optionsIconView.setBackground(Theme.createSelectorDrawable(Color.WHITE)); + storyContainer.addView(optionsIconView, LayoutHelper.createFrame(40, 40, Gravity.RIGHT | Gravity.TOP, 2, 15, 2, 0)); + + optionsIconView.setOnClickListener(v -> { + delegate.setPopupIsVisible(true); + editStoryItem = null; + final boolean[] popupStillVisible = new boolean[] { false }; + popupMenu = new CustomPopupMenu(getContext(), resourcesProvider, isSelf) { + private boolean edit; + @Override + protected void onCreate(ActionBarPopupWindow.ActionBarPopupWindowLayout popupLayout) { + if (isSelf) { + TLRPC.StoryItem storyItem = currentStory.storyItem; + if (currentStory.uploadingStory != null) { + ActionBarMenuSubItem item = ActionBarMenuItem.addItem(popupLayout, R.drawable.msg_cancel, LocaleController.getString("Cancel", R.string.Cancel), false, resourcesProvider); + item.setOnClickListener(v -> { + if (currentStory.uploadingStory != null) { + currentStory.uploadingStory.cancel(); + updateStoryItems(); + } + if (popupMenu != null) { + popupMenu.dismiss(); + } + }); + } + if (storyItem == null) { + return; + } + String str = currentStory.isVideo() ? LocaleController.getString("SaveVideo", R.string.SaveVideo) : LocaleController.getString("SaveImage", R.string.SaveImage); + + final StoryPrivacyBottomSheet.StoryPrivacy storyPrivacy = new StoryPrivacyBottomSheet.StoryPrivacy(currentAccount, storyItem.privacy); + ActionBarMenuSubItem item = ActionBarMenuItem.addItem(popupLayout, R.drawable.msg_view_file, LocaleController.getString("WhoCanSee", R.string.WhoCanSee), false, resourcesProvider); + item.setSubtext(storyPrivacy.toString()); + item.setOnClickListener(v -> { + editPrivacy(storyPrivacy, storyItem); + if (popupMenu != null) { + popupMenu.dismiss(); + } + }); + item.setItemHeight(56); + + ActionBarPopupWindow.GapView gap = new ActionBarPopupWindow.GapView(getContext(), resourcesProvider, Theme.key_actionBarDefaultSubmenuSeparator); + gap.setTag(R.id.fit_width_tag, 1); + popupLayout.addView(gap, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 8)); + + if (!unsupported && MessagesController.getInstance(currentAccount).storiesEnabled()) { + editStoryItem = ActionBarMenuItem.addItem(popupLayout, R.drawable.msg_edit, LocaleController.getString("EditStory", R.string.EditStory), false, resourcesProvider); + editStoryItem.setOnClickListener(v -> { + if (v.getAlpha() < 1) { + AndroidUtilities.shakeViewSpring(v, shiftDp = -shiftDp); + BulletinFactory.of(storyContainer, resourcesProvider).createErrorBulletin("Wait until current upload is complete").show(); + return; + } + Activity activity = AndroidUtilities.findActivity(context); + if (activity == null) { + return; + } + edit = true; + if (popupMenu != null) { + popupMenu.dismiss(); + } + setActive(false); + final Runnable openEdit = () -> { + StoryRecorder editor = StoryRecorder.getInstance(activity, currentAccount); + long time = 0; + if (playerSharedScope != null && playerSharedScope.player != null) { + time = playerSharedScope.player.currentPosition; + } + editor.openEdit(StoryRecorder.SourceView.fromStoryViewer(storyViewer), StoryEntry.fromStoryItem(currentStory.getPath(), currentStory.storyItem), time, true); + editor.setOnPrepareCloseListener((t, close, sent) -> { + final long start = System.currentTimeMillis(); + if (playerSharedScope.player == null) { + delegate.setPopupIsVisible(false); + setActive(true); + onImageReceiverThumbLoaded = () -> { + AndroidUtilities.cancelRunOnUIThread(close); + AndroidUtilities.runOnUIThread(close); + }; + if (sent) { + updatePosition(); + } + AndroidUtilities.runOnUIThread(close, 400); + return; + } + playerSharedScope.firstFrameRendered = playerSharedScope.player.firstFrameRendered = false; + playerSharedScope.player.setOnReadyListener(() -> { + AndroidUtilities.cancelRunOnUIThread(close); + AndroidUtilities.runOnUIThread(close, Math.max(0, 32L - (System.currentTimeMillis() - start))); + }); + delegate.setPopupIsVisible(false); + if (muteIconView != null) { + muteIconView.setAnimation(sharedResources.muteDrawable); + } + if (videoDuration > 0 && t > videoDuration * .4f) { + t = 0L; + } + setActive(t, true); + AndroidUtilities.runOnUIThread(close, 400); + if (sent) { + updatePosition(); + } + }); + }; + if (!delegate.releasePlayer(openEdit)) { + openEdit.run(); + } + }); + if (storiesController.hasUploadingStories() && currentStory.isVideo && !SharedConfig.allowPreparingHevcPlayers()) { + editStoryItem.setAlpha(0.5f); + } + } + + final boolean pin = !storyItem.pinned; + ActionBarMenuItem.addItem(popupLayout, pin ? R.drawable.msg_save_story : R.drawable.msg_archive, pin ? LocaleController.getString("SaveToProfile", R.string.SaveToProfile) : LocaleController.getString("ArchiveStory"), false, resourcesProvider).setOnClickListener(v -> { + ArrayList storyItems = new ArrayList<>(); + storyItems.add(storyItem); + MessagesController.getInstance(currentAccount).getStoriesController().updateStoriesPinned(storyItems, pin, success -> { + if (success) { + storyItem.pinned = pin; + BulletinFactory.of(storyContainer, resourcesProvider).createSimpleBulletin(pin ? R.raw.contact_check : R.raw.chats_archived, pin ? LocaleController.getString("StoryPinnedToProfile", R.string.StoryPinnedToProfile) : LocaleController.getString("StoryArchivedFromProfile", R.string.StoryArchivedFromProfile)).show(); + } else { + BulletinFactory.of(storyContainer, resourcesProvider).createSimpleBulletin(R.raw.error, LocaleController.getString("UnknownError", R.string.UnknownError)).show(); + } + }); + if (popupMenu != null) { + popupMenu.dismiss(); + } + }); + + if (!unsupported) { + ActionBarMenuItem.addItem(popupLayout, R.drawable.msg_gallery, str, false, resourcesProvider).setOnClickListener(v -> { + saveToGallery(); + if (popupMenu != null) { + popupMenu.dismiss(); + } + }); + } + + if (allowShare) { + ActionBarMenuItem.addItem(popupLayout, R.drawable.msg_shareout, LocaleController.getString("BotShare", R.string.BotShare), false, resourcesProvider).setOnClickListener(v -> { + shareStory(false); + if (popupMenu != null) { + popupMenu.dismiss(); + } + }); + } + + ActionBarMenuSubItem deleteItem = ActionBarMenuItem.addItem(popupLayout, R.drawable.msg_delete, LocaleController.getString("Delete", R.string.Delete), false, resourcesProvider); + deleteItem.setSelectorColor(Theme.multAlpha(Theme.getColor(Theme.key_text_RedBold, resourcesProvider), .12f)); + deleteItem.setColors(resourcesProvider.getColor(Theme.key_text_RedBold), resourcesProvider.getColor(Theme.key_text_RedBold)); + deleteItem.setOnClickListener(v -> { + deleteStory(); + if (popupMenu != null) { + popupMenu.dismiss(); + } + }); + } else { +// ActionBarMenuItem.addItem(popupLayout, R.drawable.msg_mute, LocaleController.getString("Mute", R.string.Mute), false, resourcesProvider).setOnClickListener(v -> { +// if (popupMenu != null) { +// popupMenu.dismiss(); +// } +// }); + + + final String key = NotificationsController.getSharedPrefKey(dialogId, 0); + boolean muted = !NotificationsCustomSettingsActivity.areStoriesNotMuted(currentAccount, dialogId); + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); + if (!UserObject.isService(dialogId)) { + if (!muted) { + ActionBarMenuSubItem item = ActionBarMenuItem.addItem(popupLayout, R.drawable.msg_mute, LocaleController.getString("NotificationsStoryMute2", R.string.NotificationsStoryMute2), false, resourcesProvider); + item.setOnClickListener(v -> { + MessagesController.getNotificationsSettings(currentAccount).edit().putBoolean("stories_" + key, false).apply(); + NotificationsController.getInstance(currentAccount).updateServerNotificationsSettings(dialogId, 0); + String name = user == null ? "" : user.first_name.trim(); + int index = name.indexOf(" "); + if (index > 0) { + name = name.substring(0, index); + } + BulletinFactory.of(storyContainer, resourcesProvider).createUsersBulletin(Arrays.asList(user), AndroidUtilities.replaceTags(LocaleController.formatString("NotificationsStoryMutedHint", R.string.NotificationsStoryMutedHint, name))).setTag(2).show(); + if (popupMenu != null) { + popupMenu.dismiss(); + } + }); + item.setMultiline(false); + } else { + ActionBarMenuSubItem item = ActionBarMenuItem.addItem(popupLayout, R.drawable.msg_unmute, LocaleController.getString("NotificationsStoryUnmute2", R.string.NotificationsStoryUnmute2), false, resourcesProvider); + item.setOnClickListener(v -> { + MessagesController.getNotificationsSettings(currentAccount).edit().putBoolean("stories_" + key, true).apply(); + NotificationsController.getInstance(currentAccount).updateServerNotificationsSettings(dialogId, 0); + String name = user == null ? "" : user.first_name.trim(); + int index = name.indexOf(" "); + if (index > 0) { + name = name.substring(0, index); + } + BulletinFactory.of(storyContainer, resourcesProvider).createUsersBulletin(Arrays.asList(user), AndroidUtilities.replaceTags(LocaleController.formatString("NotificationsStoryUnmutedHint", R.string.NotificationsStoryUnmutedHint, name))).setTag(2).show(); + if (popupMenu != null) { + popupMenu.dismiss(); + } + }); + item.setMultiline(false); + } + if (user.contact) { + if (!user.stories_hidden) { + ActionBarMenuItem.addItem(popupLayout, R.drawable.msg_archive, LocaleController.getString("ArchivePeerStories", R.string.ArchivePeerStories), false, resourcesProvider).setOnClickListener(v -> { + toggleArchiveForStory(dialogId); + if (popupMenu != null) { + popupMenu.dismiss(); + } + }); + } else { + ActionBarMenuItem.addItem(popupLayout, R.drawable.msg_unarchive, LocaleController.getString("UnarchiveStories", R.string.UnarchiveStories), false, resourcesProvider).setOnClickListener(v -> { + toggleArchiveForStory(dialogId); + if (popupMenu != null) { + popupMenu.dismiss(); + } + }); + } + } + } + + if (!unsupported) { + if (UserObject.isService(dialogId) || allowShare) { + ActionBarMenuItem.addItem(popupLayout, R.drawable.msg_gallery, LocaleController.getString("SaveToGallery", R.string.SaveToGallery), false, resourcesProvider).setOnClickListener(v -> { + saveToGallery(); + if (popupMenu != null) { + popupMenu.dismiss(); + } + }); + } + } + if (allowShareLink) { + ActionBarMenuItem.addItem(popupLayout, R.drawable.msg_link, LocaleController.getString("CopyLink", R.string.CopyLink), false, resourcesProvider).setOnClickListener(v -> { + AndroidUtilities.addToClipboard(currentStory.createLink()); + onLickCopied(); + if (popupMenu != null) { + popupMenu.dismiss(); + } + }); + } + if (allowShare) { + ActionBarMenuItem.addItem(popupLayout, R.drawable.msg_shareout, LocaleController.getString("BotShare", R.string.BotShare), false, resourcesProvider).setOnClickListener(v -> { + shareStory(false); + if (popupMenu != null) { + popupMenu.dismiss(); + } + }); + } + + if (!unsupported) { + if (!UserObject.isService(dialogId)) { + ActionBarMenuItem.addItem(popupLayout, R.drawable.msg_report, LocaleController.getString("ReportChat", R.string.ReportChat), false, resourcesProvider).setOnClickListener(v -> { + AlertsCreator.createReportAlert(getContext(), dialogId, 0, currentStory.storyItem.id, storyViewer.fragment, resourcesProvider, null); + if (popupMenu != null) { + popupMenu.dismiss(); + } + }); + } + } + } + + final boolean hasStickers = currentStory != null && ( +// currentStory.uploadingStory != null && currentStory.uploadingStory.entry != null && currentStory.uploadingStory.entry.stickers != null && !currentStory.uploadingStory.entry.stickers.isEmpty() || + currentStory.storyItem != null && currentStory.storyItem.media != null && ( + MessageObject.isDocumentHasAttachedStickers(currentStory.storyItem.media.document) || + currentStory.storyItem.media.photo != null && currentStory.storyItem.media.photo.has_stickers + ) + ); + ArrayList setsFromCaption = getAnimatedEmojiSets(currentStory); + final boolean hasEmojis = setsFromCaption != null && !setsFromCaption.isEmpty(); + if (hasStickers || hasEmojis) { + ActionBarPopupWindow.GapView gap = new ActionBarPopupWindow.GapView(context, resourcesProvider, Theme.key_actionBarDefaultSubmenuSeparator); + gap.setTag(R.id.fit_width_tag, 1); + popupLayout.addView(gap, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 8)); + + TLObject obj = currentStory.storyItem.media.document != null ? currentStory.storyItem.media.document : currentStory.storyItem.media.photo; + StoryContainsEmojiButton btn = new StoryContainsEmojiButton(context, currentAccount, obj, currentStory.storyItem, hasStickers, setsFromCaption, resourcesProvider); + btn.setOnClickListener(v -> { + BottomSheet alert = btn.getAlert(); + if (alert != null && delegate != null) { + delegate.showDialog(alert); + popupMenu.dismiss(); + } + }); + btn.setTag(R.id.fit_width_tag, 1); + popupLayout.addView(btn, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + } + } + + @Override + protected void onDismissed() { + if (!edit && !popupStillVisible[0]) { + AndroidUtilities.runOnUIThread(() -> { + delegate.setPopupIsVisible(false); + }); + } + popupMenu = null; + editStoryItem = null; + } + }; + popupMenu.show(optionsIconView, 0, -ActionBar.getCurrentActionBarHeight() + AndroidUtilities.dp(6)); + }); + + muteIconContainer = new FrameLayout(context); + storyContainer.addView(muteIconContainer, LayoutHelper.createFrame(40, 40, Gravity.RIGHT | Gravity.TOP, 2, 15, 2 + 40, 0)); + + muteIconView = new RLottieImageView(context); + muteIconView.setPadding(dp(6), dp(6), dp(6), dp(6)); + muteIconContainer.addView(muteIconView); + + noSoundIconView = new ImageView(context); + noSoundIconView.setPadding(dp(6), dp(6), dp(6), dp(6)); + noSoundIconView.setImageDrawable(sharedResources.noSoundDrawable); + muteIconContainer.addView(noSoundIconView); + noSoundIconView.setVisibility(View.GONE); + + privacyButton = new StoryPrivacyButton(context); + privacyButton.setOnClickListener(v -> { + TLRPC.StoryItem storyItem = currentStory.storyItem; + if (storyItem == null) { + return; + } + if (isSelf) { + StoryPrivacyBottomSheet.StoryPrivacy privacy = new StoryPrivacyBottomSheet.StoryPrivacy(currentAccount, storyItem.privacy); + editPrivacy(privacy, storyItem); + } else { + if (privacyHint == null) { + privacyHint = new HintView2(getContext(), HintView2.DIRECTION_TOP) + .setMultilineText(true) + .setTextAlign(Layout.Alignment.ALIGN_CENTER) + .setOnHiddenListener(() -> delegate.setIsHintVisible(false)); + privacyHint.setPadding(dp(8), 0, dp(8), 0); + storyContainer.addView(privacyHint, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 60, Gravity.FILL_HORIZONTAL | Gravity.TOP, 0, 52, 0, 0)); + } + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); + if (user == null) { + return; + } + String firstName = user.first_name; + int index; + if ((index = firstName.indexOf(' ')) > 0) { + firstName = firstName.substring(0, index); + } + CharSequence text; + boolean twoLines = true; + if (storyItem.close_friends) { + privacyHint.setInnerPadding(15, 8, 15, 8); + text = AndroidUtilities.replaceTags(LocaleController.formatString("StoryCloseFriendsHint", R.string.StoryCloseFriendsHint, firstName)); + } else if (storyItem.contacts) { + privacyHint.setInnerPadding(11, 6, 11, 7); + text = AndroidUtilities.replaceTags(LocaleController.formatString("StoryContactsHint", R.string.StoryContactsHint, firstName)); + twoLines = false; + } else if (storyItem.selected_contacts) { + privacyHint.setInnerPadding(15, 8, 15, 8); + text = AndroidUtilities.replaceTags(LocaleController.formatString("StorySelectedContactsHint", R.string.StorySelectedContactsHint, firstName)); + } else { + return; + } + text = Emoji.replaceEmoji(text, privacyHint.getTextPaint().getFontMetricsInt(), false); + privacyHint.setMaxWidthPx(twoLines ? HintView2.cutInFancyHalf(text, privacyHint.getTextPaint()) : storyContainer.getMeasuredWidth()); + privacyHint.setText(text); + privacyHint.setJoint(1, -(storyContainer.getWidth() - privacyButton.getCenterX()) / AndroidUtilities.density); + delegate.setIsHintVisible(true); + if (privacyHint.shown()) { + BotWebViewVibrationEffect.IMPACT_LIGHT.vibrate(); + } + privacyHint.show(); + } + }); + storyContainer.addView(privacyButton, LayoutHelper.createFrame(60, 40, Gravity.RIGHT | Gravity.TOP, 2, 15, 2 + 40, 0)); + + muteIconContainer.setOnClickListener(v -> { + if (currentStory.hasSound()) { + storyViewer.toggleSilentMode(); + } else { + if (soundTooltip == null) { + soundTooltip = new HintView2(context, HintView2.DIRECTION_TOP).setJoint(1, -56); + soundTooltip.setText(LocaleController.getString(R.string.StoryNoSound)); + soundTooltip.setPadding(AndroidUtilities.dp(8), 0, AndroidUtilities.dp(8), 0); + storyContainer.addView(soundTooltip, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.FILL_HORIZONTAL | Gravity.TOP, 0, 52, 0, 0)); + } + soundTooltip.show(); + } + }); + + storyLines = new StoryLinesDrawable(this, sharedResources); + + storyContainer.addView(storyCaptionView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, 0, 0, 64, 0, 0)); + + muteIconContainer.setBackground(Theme.createSimpleSelectorRoundRectDrawable(dp(20), Color.TRANSPARENT, ColorUtils.setAlphaComponent(Color.WHITE, 100))); + optionsIconView.setBackground(Theme.createSimpleSelectorRoundRectDrawable(dp(20), Color.TRANSPARENT, ColorUtils.setAlphaComponent(Color.WHITE, 100))); + shareButton.setBackground(Theme.createSimpleSelectorRoundRectDrawable(dp(20), Color.TRANSPARENT, ColorUtils.setAlphaComponent(Color.WHITE, 100))); + + View overlay = storyCaptionView.textSelectionHelper.getOverlayView(context); + if (overlay != null) { + AndroidUtilities.removeFromParent(overlay); + addView(overlay); + } + storyCaptionView.textSelectionHelper.setCallback(new TextSelectionHelper.Callback() { + @Override + public void onStateChanged(boolean isSelected) { + delegate.setIsInSelectionMode(storyCaptionView.textSelectionHelper.isInSelectionMode()); + } + }); + storyCaptionView.textSelectionHelper.setParentView(this); + } + + private ArrayList getAnimatedEmojiSets(StoryItemHolder storyHolder) { + if (storyHolder != null) { + if (storyHolder.storyItem != null && storyHolder.storyItem.entities != null && !storyHolder.storyItem.entities.isEmpty()) { + HashSet ids = new HashSet<>(); + ArrayList inputStickerSets = new ArrayList<>(); + for (int i = 0; i < storyHolder.storyItem.entities.size(); ++i) { + TLRPC.MessageEntity messageEntity = storyHolder.storyItem.entities.get(i); + if (!(messageEntity instanceof TLRPC.TL_messageEntityCustomEmoji)) { + continue; + } + TLRPC.Document document = ((TLRPC.TL_messageEntityCustomEmoji) messageEntity).document; + if (document == null) { + document = AnimatedEmojiDrawable.findDocument(currentAccount, ((TLRPC.TL_messageEntityCustomEmoji) messageEntity).document_id); + } + if (document == null) { + continue; + } + TLRPC.InputStickerSet set = MessageObject.getInputStickerSet(document); + if (ids.contains(set.id)) { + continue; + } + ids.add(set.id); + inputStickerSets.add(set); + } + return inputStickerSets; + } else if (storyHolder.uploadingStory != null && storyHolder.uploadingStory.entry != null) { + CharSequence caption = storyHolder.uploadingStory.entry.caption; + if (!(caption instanceof Spanned)) { + return null; + } + AnimatedEmojiSpan[] spans = ((Spanned) caption).getSpans(0, caption.length(), AnimatedEmojiSpan.class); + if (spans == null) { + return null; + } + HashSet ids = new HashSet<>(); + ArrayList inputStickerSets = new ArrayList<>(); + for (int i = 0; i < spans.length; ++i) { + TLRPC.Document document = spans[i].document; + if (document == null) { + document = AnimatedEmojiDrawable.findDocument(currentAccount, spans[i].documentId); + } + if (document == null) { + continue; + } + TLRPC.InputStickerSet set = MessageObject.getInputStickerSet(document); + if (ids.contains(set.id)) { + continue; + } + ids.add(set.id); + inputStickerSets.add(set); + } + return inputStickerSets; + } + } + return null; + } + + private void toggleArchiveForStory(long dialogId) { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); + boolean hide = !user.stories_hidden; + MessagesController messagesController = MessagesController.getInstance(currentAccount); + + AndroidUtilities.runOnUIThread(() -> { + messagesController.getStoriesController().toggleHidden(dialogId, hide, false, true); + BulletinFactory.UndoObject undoObject = new BulletinFactory.UndoObject(); + undoObject.onUndo = () -> { + messagesController.getStoriesController().toggleHidden(dialogId, !hide, false, true); + }; + undoObject.onAction = () -> { + messagesController.getStoriesController().toggleHidden(dialogId, hide, true, true); + }; + CharSequence str; + if (!hide) { + str = AndroidUtilities.replaceTags(LocaleController.formatString("StoriesMovedToDialogs", R.string.StoriesMovedToDialogs, ContactsController.formatName(user.first_name, null, 10))); + } else { + str = AndroidUtilities.replaceTags(LocaleController.formatString("StoriesMovedToContacts", R.string.StoriesMovedToContacts, ContactsController.formatName(user.first_name, null, 10))); + } + BulletinFactory.of(storyContainer, resourcesProvider).createUsersBulletin(Arrays.asList(user), str, null, undoObject).setTag(2).show(); + }, 200); + } + + private boolean drawLinesAsCounter() { + return false;//linesCount > 20; + } + + private void createEnterView() { + Theme.ResourcesProvider emojiResourceProvider = new WrappedResourceProvider(resourcesProvider) { + @Override + public void appendColors() { + sparseIntArray.put(Theme.key_chat_emojiPanelBackground, ColorUtils.setAlphaComponent(Color.WHITE, 30)); + } + }; + chatActivityEnterView = new ChatActivityEnterView(AndroidUtilities.findActivity(getContext()), this, null, true, emojiResourceProvider) { + + private Animator messageEditTextAnimator; + private int chatActivityEnterViewAnimateFromTop; + int lastContentViewHeight; + int messageEditTextPredrawHeigth; + int messageEditTextPredrawScrollY; + + public void checkAnimation() { + int t = getBackgroundTop(); + if (chatActivityEnterViewAnimateFromTop != 0 && t != chatActivityEnterViewAnimateFromTop) { + int dy = animatedTop + chatActivityEnterViewAnimateFromTop - t; + animatedTop = dy; + forceUpdateOffsets = true; + if (changeBoundAnimator != null) { + changeBoundAnimator.removeAllListeners(); + changeBoundAnimator.cancel(); + } + + if (topView != null && topView.getVisibility() == View.VISIBLE) { + topView.setTranslationY(animatedTop + (1f - topViewEnterProgress) * topView.getLayoutParams().height); + if (topLineView != null) { + topLineView.setTranslationY(animatedTop); + } + } + + PeerStoriesView.this.invalidate(); + changeBoundAnimator = ValueAnimator.ofFloat(dy, 0); + changeBoundAnimator.addUpdateListener(a -> { + float top = (float) a.getAnimatedValue(); + animatedTop = (int) top; + forceUpdateOffsets = true; + PeerStoriesView.this.invalidate(); + invalidate(); + }); + changeBoundAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + PeerStoriesView.this.invalidate(); + animatedTop = 0; + forceUpdateOffsets = true; + if (topView != null && topView.getVisibility() == View.VISIBLE) { + topView.setTranslationY(animatedTop + (1f - topViewEnterProgress) * topView.getLayoutParams().height); + if (topLineView != null) { + topLineView.setTranslationY(animatedTop); + } + } + changeBoundAnimator = null; + } + }); + changeBoundAnimator.setDuration(ChatListItemAnimator.DEFAULT_DURATION); + changeBoundAnimator.setInterpolator(ChatListItemAnimator.DEFAULT_INTERPOLATOR); + changeBoundAnimator.start(); + chatActivityEnterViewAnimateFromTop = 0; + } + if (shouldAnimateEditTextWithBounds) { + float dy = (messageEditTextPredrawHeigth - messageEditText.getMeasuredHeight()) + (messageEditTextPredrawScrollY - messageEditText.getScrollY()); + messageEditText.setOffsetY(messageEditText.getOffsetY() - dy); + ValueAnimator a = ValueAnimator.ofFloat(messageEditText.getOffsetY(), 0); + a.addUpdateListener(animation -> messageEditText.setOffsetY((float) animation.getAnimatedValue())); + if (messageEditTextAnimator != null) { + messageEditTextAnimator.cancel(); + } + messageEditTextAnimator = a; + a.setDuration(ChatListItemAnimator.DEFAULT_DURATION); + a.setInterpolator(ChatListItemAnimator.DEFAULT_INTERPOLATOR); + a.start(); + shouldAnimateEditTextWithBounds = false; + } + lastContentViewHeight = getMeasuredHeight(); + } + + @Override + protected void onLineCountChanged(int oldLineCount, int newLineCount) { + if (chatActivityEnterView != null) { + shouldAnimateEditTextWithBounds = true; + messageEditTextPredrawHeigth = messageEditText.getMeasuredHeight(); + messageEditTextPredrawScrollY = messageEditText.getScrollY(); + invalidate(); + PeerStoriesView.this.invalidate(); + chatActivityEnterViewAnimateFromTop = chatActivityEnterView.getBackgroundTop(); + } + } + + @Override + protected void updateRecordInterface(int recordState) { + super.updateRecordInterface(recordState); + checkRecording(); + } + + @Override + protected void isRecordingStateChanged() { + super.isRecordingStateChanged(); + checkRecording(); + } + + private void checkRecording() { + isRecording = chatActivityEnterView.isRecordingAudioVideo() || (recordedAudioPanel != null && recordedAudioPanel.getVisibility() == View.VISIBLE); + if (isActive) { + delegate.setIsRecording(isRecording); + } + invalidate(); + storyContainer.invalidate(); + } + + @Override + public void extendActionMode(Menu menu) { + ChatActivity.fillActionModeMenu(menu, null); + } + }; + chatActivityEnterView.setOverrideKeyboardAnimation(true); + chatActivityEnterView.setOverrideHint(LocaleController.getString("ReplyPrivately", R.string.ReplyPrivately)); + chatActivityEnterView.setClipChildren(false); + chatActivityEnterView.setDelegate(new ChatActivityEnterView.ChatActivityEnterViewDelegate() { + @Override + public void onMessageSend(CharSequence message, boolean notify, int scheduleDate) { + if (isRecording) { + AndroidUtilities.runOnUIThread(() -> { + afterMessageSend(); + }, 200); + } else { + afterMessageSend(); + } + } + + @Override + public void needSendTyping() { + + } + + @Override + public void onTextChanged(CharSequence text, boolean bigChange) { + if (mentionContainer == null) { + createMentionsContainer(); + } + if (mentionContainer.getAdapter() != null) { + mentionContainer.setDialogId(dialogId); + mentionContainer.getAdapter().setUserOrChar(MessagesController.getInstance(currentAccount).getUser(dialogId), null); + mentionContainer.getAdapter().searchUsernameOrHashtag(text, chatActivityEnterView.getCursorPosition(), null, false, false); + } + invalidate(); + } + + @Override + public void onTextSelectionChanged(int start, int end) { + + } + + @Override + public void onTextSpansChanged(CharSequence text) { + + } + + @Override + public void onAttachButtonHidden() { + + } + + @Override + public void onAttachButtonShow() { + + } + + @Override + public void onWindowSizeChanged(int size) { + + } + + @Override + public void onStickersTab(boolean opened) { + + } + + @Override + public void onMessageEditEnd(boolean loading) { + + } + + @Override + public void didPressAttachButton() { + openAttachMenu(); + } + + @Override + public void needStartRecordVideo(int state, boolean notify, int scheduleDate) { + checkInstantCameraView(); + if (instantCameraView != null) { + if (state == 0) { + instantCameraView.showCamera(); + } else if (state == 1 || state == 3 || state == 4) { + instantCameraView.send(state, notify, scheduleDate); + } else if (state == 2 || state == 5) { + instantCameraView.cancel(state == 2); + } + } + } + + @Override + public void needChangeVideoPreviewState(int state, float seekProgress) { + if (instantCameraView != null) { + instantCameraView.changeVideoPreviewState(state, seekProgress); + } + } + + @Override + public void onSwitchRecordMode(boolean video) { + + } + + @Override + public void onPreAudioVideoRecord() { + + } + + @Override + public void needStartRecordAudio(int state) { + + } + + @Override + public void needShowMediaBanHint() { + if (mediaBanTooltip == null) { + mediaBanTooltip = new HintView(getContext(), 9, resourcesProvider); + mediaBanTooltip.setVisibility(View.GONE); + addView(mediaBanTooltip, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 10, 0, 10, 0)); + } + mediaBanTooltip.setText(AndroidUtilities.replaceTags(LocaleController.formatString(chatActivityEnterView.isInVideoMode() ? R.string.VideoMessagesRestrictedByPrivacy : R.string.VoiceMessagesRestrictedByPrivacy, MessagesController.getInstance(currentAccount).getUser(dialogId).first_name))); + mediaBanTooltip.showForView(chatActivityEnterView.getAudioVideoButtonContainer(), true); + } + + @Override + public void onStickersExpandedChange() { + requestLayout(); + } + + @Override + public void onUpdateSlowModeButton(View button, boolean show, CharSequence time) { + + } + + @Override + public void onSendLongClick() { + + } + + @Override + public void onAudioVideoInterfaceUpdated() { + + } + + @Override + public TLRPC.StoryItem getReplyToStory() { + return currentStory.storyItem; + } + + }); + setDelegate(chatActivityEnterView); + chatActivityEnterView.shouldDrawBackground = false; + chatActivityEnterView.shouldDrawRecordedAudioPanelInParent = true; + chatActivityEnterView.setAllowStickersAndGifs(true, true, true); + chatActivityEnterView.updateColors(); + addView(chatActivityEnterView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.BOTTOM, 0, 0, 0, 0)); + + chatActivityEnterView.recordingGuid = classGuid; + playerSharedScope.viewsToInvalidate.add(storyContainer); + playerSharedScope.viewsToInvalidate.add(PeerStoriesView.this); + if (attachedToWindow) { + chatActivityEnterView.onResume(); + } + + reactionsContainerIndex = getChildCount(); + } + + private void createMentionsContainer() { + mentionContainer = new MentionsContainerView(getContext(), dialogId, 0, storyViewer.fragment, PeerStoriesView.this, resourcesProvider) { + @Override + public void drawRoundRect(Canvas canvas, Rect rect, float radius) { + bitmapShaderTools.setBounds(getX(), -getY(), getX() + getMeasuredWidth(), -getY() + getMeasuredHeight()); + AndroidUtilities.rectTmp.set(rect); + AndroidUtilities.rectTmp.offset(0, 0); + canvas.drawRoundRect(AndroidUtilities.rectTmp, radius, radius, bitmapShaderTools.paint); + canvas.drawRoundRect(AndroidUtilities.rectTmp, radius, radius, inputBackgroundPaint); + if (AndroidUtilities.rectTmp.top < getMeasuredHeight() - 1) { + canvas.drawRect(0, getMeasuredHeight(), getMeasuredWidth(), getMeasuredHeight() - 1, resourcesProvider.getPaint(Theme.key_paint_divider)); + } + } + }; + mentionContainer.withDelegate(new MentionsContainerView.Delegate() { + @Override + public void onStickerSelected(TLRPC.TL_document document, String query, Object parent) { + SendMessagesHelper.getInstance(currentAccount).sendSticker(document, query, dialogId, null, null, currentStory.storyItem, null, true, 0, false, parent); + chatActivityEnterView.addStickerToRecent(document); + chatActivityEnterView.setFieldText(""); + afterMessageSend(); + } + + @Override + public void replaceText(int start, int len, CharSequence replacingString, boolean allowShort) { + chatActivityEnterView.replaceWithText(start, len, replacingString, allowShort); + } + + @Override + public Paint.FontMetricsInt getFontMetrics() { + return chatActivityEnterView.getEditField().getPaint().getFontMetricsInt(); + } + + @Override + public void addEmojiToRecent(String code) { + chatActivityEnterView.addEmojiToRecent(code); + } + }); + addView(mentionContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.BOTTOM)); + } + + private void saveToGallery() { + if (currentStory.storyItem == null && currentStory.uploadingStory == null) { + return; + } + if (currentStory.storyItem instanceof TLRPC.TL_storyItemSkipped) { + return; + } + File f = currentStory.getPath(); + boolean isVideo = currentStory.isVideo(); + if (f != null && f.exists()) { + MediaController.saveFile(f.toString(), getContext(), isVideo ? 1 : 0, null, null, uri -> { + BulletinFactory.createSaveToGalleryBulletin(storyContainer, isVideo, resourcesProvider).show(); + }); + } else { + showDownloadAlert(); + } + } + + private void showDownloadAlert() { + AlertDialog.Builder builder = new AlertDialog.Builder(getContext(), resourcesProvider); + builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); + builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), null); + // boolean alreadyDownloading = currentMessageObject != null && currentMessageObject.isVideo() && FileLoader.getInstance(currentMessageObject.currentAccount).isLoadingFile(currentFileNames[0]); +// if (alreadyDownloading) { +// builder.setMessage(LocaleController.getString("PleaseStreamDownload", R.string.PleaseStreamDownload)); +// } else { + builder.setMessage(LocaleController.getString("PleaseDownload", R.string.PleaseDownload)); + // } + delegate.showDialog(builder.create()); + } + + private void openAttachMenu() { + if (chatActivityEnterView == null) { + return; + } + createChatAttachView(); + chatAttachAlert.getPhotoLayout().loadGalleryPhotos(); + if (Build.VERSION.SDK_INT == 21 || Build.VERSION.SDK_INT == 22) { + chatActivityEnterView.closeKeyboard(); + } + chatAttachAlert.setMaxSelectedPhotos(-1, true); + chatAttachAlert.init(); + chatAttachAlert.getCommentTextView().setText(chatActivityEnterView.getFieldText()); + chatAttachAlert.setDialogId(dialogId); + delegate.showDialog(chatAttachAlert); + } + + private void createChatAttachView() { + if (chatAttachAlert == null) { + chatAttachAlert = new ChatAttachAlert(getContext(), null, false, false, true, resourcesProvider) { + @Override + public void dismissInternal() { +// if (chatAttachAlert != null && chatAttachAlert.isShowing()) { +// AndroidUtilities.requestAdjustResize(getParentActivity(), classGuid); +// } + super.dismissInternal(); + } + + @Override + public void onDismissAnimationStart() { + if (chatAttachAlert != null) { + chatAttachAlert.setFocusable(false); + } + if (chatActivityEnterView != null && chatActivityEnterView.getEditField() != null) { + chatActivityEnterView.getEditField().requestFocus(); + } +// if (chatAttachAlert != null && chatAttachAlert.isShowing()) { +// AndroidUtilities.requestAdjustResize(getParentActivity(), classGuid); +// } +// onEditTextDialogClose(false, false); + } + + }; + chatAttachAlert.setDelegate(new ChatAttachAlert.ChatAttachViewDelegate() { + + @Override + public void didPressedButton(int button, boolean arg, boolean notify, int scheduleDate, boolean forceDocument) { + if (!storyViewer.isShowing) { + return; + } + TLRPC.StoryItem storyItem = currentStory.storyItem; + if (storyItem == null || storyItem instanceof TLRPC.TL_storyItemSkipped) { + return; + } + if (button == 8 || button == 7 || button == 4 && !chatAttachAlert.getPhotoLayout().getSelectedPhotos().isEmpty()) { + if (button != 8) { + chatAttachAlert.dismiss(true); + } + HashMap selectedPhotos = chatAttachAlert.getPhotoLayout().getSelectedPhotos(); + ArrayList selectedPhotosOrder = chatAttachAlert.getPhotoLayout().getSelectedPhotosOrder(); + if (!selectedPhotos.isEmpty()) { + for (int i = 0; i < Math.ceil(selectedPhotos.size() / 10f); ++i) { + int count = Math.min(10, selectedPhotos.size() - (i * 10)); + ArrayList photos = new ArrayList<>(); + for (int a = 0; a < count; a++) { + if (i * 10 + a >= selectedPhotosOrder.size()) { + continue; + } + MediaController.PhotoEntry photoEntry = (MediaController.PhotoEntry) selectedPhotos.get(selectedPhotosOrder.get(i * 10 + a)); + + SendMessagesHelper.SendingMediaInfo info = new SendMessagesHelper.SendingMediaInfo(); + if (!photoEntry.isVideo && photoEntry.imagePath != null) { + info.path = photoEntry.imagePath; + } else if (photoEntry.path != null) { + info.path = photoEntry.path; + } + info.thumbPath = photoEntry.thumbPath; + info.isVideo = photoEntry.isVideo; + info.caption = photoEntry.caption != null ? photoEntry.caption.toString() : null; + info.entities = photoEntry.entities; + info.masks = photoEntry.stickers; + info.ttl = photoEntry.ttl; + info.videoEditedInfo = photoEntry.editedInfo; + info.canDeleteAfter = photoEntry.canDeleteAfter; + info.updateStickersOrder = SendMessagesHelper.checkUpdateStickersOrder(photoEntry.caption); + info.hasMediaSpoilers = photoEntry.hasSpoiler; + photos.add(info); + photoEntry.reset(); + } + boolean updateStickersOrder = false; + if (i == 0) { + updateStickersOrder = photos.get(0).updateStickersOrder; + } + SendMessagesHelper.prepareSendingMedia(getAccountInstance(), photos, dialogId, null, null, storyItem, button == 4 || forceDocument, arg, null, notify, scheduleDate, updateStickersOrder, null); + } + afterMessageSend(); + chatActivityEnterView.setFieldText(""); + } +// if (scheduleDate != 0) { +// if (scheduledMessagesCount == -1) { +// scheduledMessagesCount = 0; +// } +// scheduledMessagesCount += selectedPhotos.size(); +// updateScheduledInterface(true); +// } + return; + } else if (chatAttachAlert != null) { + chatAttachAlert.dismissWithButtonClick(button); + } + // processSelectedAttach(button); + } + + @Override + public View getRevealView() { + return chatActivityEnterView.getAttachButton(); + } + + @Override + public void onCameraOpened() { + chatActivityEnterView.closeKeyboard(); + } + + @Override + public void doOnIdle(Runnable runnable) { + NotificationCenter.getInstance(currentAccount).doOnIdle(runnable); + } + + @Override + public void sendAudio(ArrayList audios, CharSequence caption, boolean notify, int scheduleDate) { + TLRPC.StoryItem storyItem = currentStory.storyItem; + if (storyItem == null || storyItem instanceof TLRPC.TL_storyItemSkipped) { + return; + } + SendMessagesHelper.prepareSendingAudioDocuments(getAccountInstance(), audios, caption != null ? caption : null, dialogId, null, null, storyItem, notify, scheduleDate, null); + afterMessageSend(); + } + + @Override + public boolean needEnterComment() { + return needEnterText(); + } + }); + chatAttachAlert.getPhotoLayout().loadGalleryPhotos(); + chatAttachAlert.setAllowEnterCaption(true); + chatAttachAlert.init(); + chatAttachAlert.setDocumentsDelegate(new ChatAttachAlertDocumentLayout.DocumentSelectActivityDelegate() { + @Override + public void didSelectFiles(ArrayList files, String caption, ArrayList fmessages, boolean notify, int scheduleDate) { + TLRPC.StoryItem storyItem = currentStory.storyItem; + if (storyItem == null || storyItem instanceof TLRPC.TL_storyItemSkipped) { + return; + } + SendMessagesHelper.prepareSendingDocuments(getAccountInstance(), files, files, null, caption, null, dialogId, null, null, storyItem, null, notify, scheduleDate, null); + afterMessageSend(); + } + + @Override + public void startDocumentSelectActivity() { + try { + Intent photoPickerIntent = new Intent(Intent.ACTION_GET_CONTENT); + if (Build.VERSION.SDK_INT >= 18) { + photoPickerIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); + } + photoPickerIntent.setType("*/*"); + storyViewer.startActivityForResult(photoPickerIntent, 21); + } catch (Exception e) { + FileLog.e(e); + } + } + }); + chatAttachAlert.getCommentTextView().setText(chatActivityEnterView.getFieldText()); + } + } + + private void shareStory(boolean internal) { + if (currentStory.storyItem != null && storyViewer.fragment != null) { + TLRPC.StoryItem storyItem = currentStory.storyItem; + String link = currentStory.createLink(); + if (internal) { + Theme.ResourcesProvider shareResourceProvider = new WrappedResourceProvider(resourcesProvider) { + @Override + public void appendColors() { + sparseIntArray.put(Theme.key_chat_emojiPanelBackground, ColorUtils.blendARGB(Color.BLACK, Color.WHITE, 0.2f)); + } + }; + shareAlert = new ShareAlert(storyViewer.fragment.getContext(), null, link, false, link, false, shareResourceProvider) { + + @Override + public void dismissInternal() { + super.dismissInternal(); + shareAlert = null; + } + + @Override + protected void onSend(LongSparseArray dids, int count, TLRPC.TL_forumTopic topic) { + super.onSend(dids, count, topic); + BulletinFactory bulletinFactory = BulletinFactory.of(storyContainer, resourcesProvider); + if (bulletinFactory != null) { + if (dids.size() == 1) { + long did = dids.keyAt(0); + if (did == UserConfig.getInstance(currentAccount).clientUserId) { + bulletinFactory.createSimpleBulletin(R.raw.saved_messages, AndroidUtilities.replaceTags(LocaleController.formatString("StorySharedToSavedMessages", R.string.StorySharedToSavedMessages)), Bulletin.DURATION_PROLONG).hideAfterBottomSheet(false).show(); + } else if (did < 0) { + TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-did); + bulletinFactory.createSimpleBulletin(R.raw.forward, AndroidUtilities.replaceTags(LocaleController.formatString("StorySharedTo", R.string.StorySharedTo, topic != null ? topic.title : chat.title)), Bulletin.DURATION_PROLONG).hideAfterBottomSheet(false).show(); + } else { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(did); + bulletinFactory.createSimpleBulletin(R.raw.forward, AndroidUtilities.replaceTags(LocaleController.formatString("StorySharedTo", R.string.StorySharedTo, user.first_name)), Bulletin.DURATION_PROLONG).hideAfterBottomSheet(false).show(); + } + } else { + bulletinFactory.createSimpleBulletin(R.raw.forward, AndroidUtilities.replaceTags(LocaleController.formatPluralString("StorySharedToManyChats", dids.size(), dids.size()))).hideAfterBottomSheet(false).show(); + } + performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + } + } + }; + currentStory.storyItem.dialogId = dialogId; + shareAlert.setStoryToShare(currentStory.storyItem); + shareAlert.setDelegate(new ShareAlert.ShareAlertDelegate() { + + @Override + public boolean didCopy() { + onLickCopied(); + return true; + } + }); + delegate.showDialog(shareAlert); + } else { + Intent intent = new Intent(Intent.ACTION_SEND); + intent.setType("text/plain"); + intent.putExtra(Intent.EXTRA_TEXT, link); + LaunchActivity.instance.startActivityForResult(Intent.createChooser(intent, LocaleController.getString("StickersShare", R.string.StickersShare)), 500); + } + } + } + + private void onLickCopied() { + if (currentStory.storyItem == null) { + return; + } + TLRPC.TL_stories_exportStoryLink exportStoryLink = new TLRPC.TL_stories_exportStoryLink(); + exportStoryLink.id = currentStory.storyItem.id; + exportStoryLink.user_id = MessagesController.getInstance(currentAccount).getInputUser(dialogId); + ConnectionsManager.getInstance(currentAccount).sendRequest(exportStoryLink, new RequestDelegate() { + @Override + public void run(TLObject response, TLRPC.TL_error error) { + + } + }); + } + + public void setDay(long dialogId, ArrayList day) { + this.dialogId = dialogId; + this.day = day; + bindInternal(); + } + + public void setDialogId(long dialogId) { + if (this.dialogId != dialogId) { + currentStory.clear(); + } + this.dialogId = dialogId; + this.day = null; + bindInternal(); + if (storyViewer.overrideUserStories != null) { + storiesController.loadSkippedStories(storyViewer.overrideUserStories, true); + } else { + storiesController.loadSkippedStories(dialogId); + } + } + + private void bindInternal() { + deletedPeer = false; + forceUpdateOffsets = true; + if (dialogId >= 0) { + isSelf = dialogId == UserConfig.getInstance(currentAccount).getClientUserId(); + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); + avatarDrawable.setInfo(user); + headerView.backupImageView.getImageReceiver().setForUserOrChat(user, avatarDrawable); + if (isSelf) { + headerView.titleView.setText(LocaleController.getString("SelfStoryTitle", R.string.SelfStoryTitle)); + headerView.titleView.setRightDrawable(null); + } else { + if (user != null && user.verified) { + Drawable verifyDrawable = ContextCompat.getDrawable(getContext(), R.drawable.verified_profile).mutate(); + verifyDrawable.setAlpha(255); + CombinedDrawable drawable = new CombinedDrawable(verifyDrawable, null); + drawable.setFullsize(true); + drawable.setCustomSize(AndroidUtilities.dp(16), AndroidUtilities.dp(16)); + headerView.titleView.setRightDrawable(drawable); + } else { + headerView.titleView.setRightDrawable(null); + } + if (user != null) { + CharSequence text = ContactsController.formatName(user); + text = Emoji.replaceEmoji(text, headerView.titleView.getPaint().getFontMetricsInt(), false); + headerView.titleView.setText(text); + } else { + headerView.titleView.setText(null); + } + } + if (isActive) { + storiesController.pollViewsForSelfStories(true); + } + } else { + TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-dialogId); + avatarDrawable.setInfo(chat); + headerView.backupImageView.getImageReceiver().setForUserOrChat(chat, avatarDrawable); + headerView.titleView.setText(chat.title); + TLRPC.ChatFull chatFull = MessagesController.getInstance(currentAccount).getChatFull(-dialogId); + } + updateStoryItems(); + selectedPosition = 0; + currentImageTime = 0; + switchEventSent = false; + if (isSelf) { + createSelfPeerView(); + selfView.setVisibility(View.VISIBLE); + if (chatActivityEnterView != null) { + chatActivityEnterView.setVisibility(View.GONE); + } + if (day != null) { + int index = day.indexOf(storyViewer.dayStoryId); + if (index < 0) { + if (!day.isEmpty()) { + if (storyViewer.dayStoryId > day.get(0)) { + index = 0; + } else if (storyViewer.dayStoryId < day.get(day.size() - 1)) { + index = day.size() - 1; + } + } + } + selectedPosition = Math.max(0, index); + } else if (!uploadingStories.isEmpty()) { + selectedPosition = storyItems.size(); + } else { + for (int i = 0; i < storyItems.size(); i++) { + if (storyItems.get(i).justUploaded || storyItems.get(i).id > storiesController.dialogIdToMaxReadId.get(dialogId)) { + selectedPosition = i; + break; + } + } + } + updatePosition(); + storyContainer.invalidate(); + invalidate(); + } else { + if (chatActivityEnterView == null) { + createEnterView(); + } + updateSelectedPosition(); + if (chatActivityEnterView != null) { + chatActivityEnterView.setVisibility(View.VISIBLE); + if (!TextUtils.isEmpty(chatActivityEnterView.getEditField().getText())) { + chatActivityEnterView.getEditField().setText(""); + } + chatActivityEnterView.setDialogId(dialogId, currentAccount); + TLRPC.UserFull userFull = MessagesController.getInstance(currentAccount).getUserFull(dialogId); + if (userFull != null) { + chatActivityEnterView.updateRecordButton(null, userFull); + } else { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); + MessagesController.getInstance(currentAccount).loadFullUser(user, classGuid, false); + } + } + count = getStoriesCount(); + if (selfView != null) { + selfView.setVisibility(View.GONE); + } + updatePosition(); + storyContainer.invalidate(); + invalidate(); + } + } + + private void createUnsupportedContainer() { + if (unsupportedContainer != null) { + return; + } + FrameLayout frameLayout = new FrameLayout(getContext()); + + LinearLayout linearLayout = new LinearLayout(getContext()); + linearLayout.setOrientation(LinearLayout.VERTICAL); + + TextView textView = new TextView(getContext()); + textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + textView.setGravity(Gravity.CENTER_HORIZONTAL); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + textView.setText(LocaleController.getString("StoryUnsupported", R.string.StoryUnsupported)); + textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); + + TextView buttonTextView = new TextView(getContext()); + ScaleStateListAnimator.apply(buttonTextView); + buttonTextView.setText(LocaleController.getString("AppUpdate", R.string.AppUpdate)); + buttonTextView.setTextColor(Theme.getColor(Theme.key_featuredStickers_buttonText, resourcesProvider)); + buttonTextView.setPadding(AndroidUtilities.dp(16), AndroidUtilities.dp(12), AndroidUtilities.dp(16), AndroidUtilities.dp(12)); + buttonTextView.setGravity(Gravity.CENTER); + buttonTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + buttonTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); + buttonTextView.setBackground( + Theme.createSimpleSelectorRoundRectDrawable(dp(8), + Theme.getColor(Theme.key_featuredStickers_addButton, resourcesProvider), + ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_featuredStickers_buttonText, resourcesProvider), 30)) + ); + buttonTextView.setOnClickListener(v -> { + if (LaunchActivity.instance != null) { + LaunchActivity.instance.checkAppUpdate(true); + } + }); + linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + linearLayout.addView(buttonTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 24, 0, 0)); + + frameLayout.addView(linearLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 72, 0, 72, 0)); + storyContainer.addView(frameLayout); + unsupportedContainer = frameLayout; + } + + + public void preloadMainImage(long dialogId) { + if (this.dialogId == dialogId && day == null) { + return; + } + this.dialogId = dialogId; + updateStoryItems(); + updateSelectedPosition(); + updatePosition(true); + if (storyViewer.overrideUserStories != null) { + storiesController.loadSkippedStories(storyViewer.overrideUserStories, true); + } else { + storiesController.loadSkippedStories(dialogId); + } + } + + private void updateSelectedPosition() { + if (day != null) { + int index = day.indexOf(storyViewer.dayStoryId); + if (index < 0) { + if (!day.isEmpty()) { + if (storyViewer.dayStoryId > day.get(0)) { + index = 0; + } else if (storyViewer.dayStoryId < day.get(day.size() - 1)) { + index = day.size() - 1; + } + } + } + selectedPosition = index; + } else { + selectedPosition = storyViewer.savedPositions.get(dialogId, -1); + if (selectedPosition == -1) { + if (!storyViewer.isSingleStory && userStories != null && userStories.max_read_id > 0) { + for (int i = 0; i < storyItems.size(); i++) { + if (storyItems.get(i).id > userStories.max_read_id) { + selectedPosition = i; + break; + } + } + } + } + } + if (selectedPosition == -1) { + selectedPosition = 0; + } + } + + private void updateStoryItems() { + storyItems.clear(); + if (storyViewer.isSingleStory) { + storyItems.add(storyViewer.singleStory); + } else if (day != null && storyViewer.storiesList != null) { + for (int id : day) { + MessageObject messageObject = storyViewer.storiesList.findMessageObject(id); + if (messageObject != null && messageObject.storyItem != null) { + storyItems.add(messageObject.storyItem); + } + } + } else if (storyViewer.storiesList != null) { + // TODO: actually load more stories + for (int i = 0; i < storyViewer.storiesList.messageObjects.size(); ++i) { + storyItems.add(storyViewer.storiesList.messageObjects.get(i).storyItem); + } + } else { + if (storyViewer.overrideUserStories != null && storyViewer.overrideUserStories.user_id == dialogId) { + userStories = storyViewer.overrideUserStories; + } else { + userStories = storiesController.getStories(dialogId); + } + totalStoriesCount = 0; + if (userStories != null) { + totalStoriesCount = userStories.stories.size(); + storyItems.addAll(userStories.stories); + } + uploadingStories.clear(); + if (isSelf) { + uploadingStories.addAll(storiesController.getUploadingStories()); + } + } + count = getStoriesCount(); + } + + private void createSelfPeerView() { + if (selfView != null) { + return; + } + selfView = new FrameLayout(getContext()) { + @Override + protected void dispatchDraw(Canvas canvas) { + if (selfAvatarsContainer.getVisibility() == View.VISIBLE) { + int w = (int) (selfStatusView.getX() + selfStatusView.getMeasuredWidth() - selfAvatarsContainer.getX() + AndroidUtilities.dp(10)); + if (selfAvatarsContainer.getLayoutParams().width != w) { + selfAvatarsContainer.getLayoutParams().width = w; + selfAvatarsContainer.invalidate(); + selfAvatarsContainer.requestLayout(); + } + } + super.dispatchDraw(canvas); + } + }; + selfView.setClickable(true); + addView(selfView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.TOP, 0, 0, 56 + 40, 0)); + + selfAvatarsContainer = new View(getContext()) { + + LoadingDrawable loadingDrawable = new LoadingDrawable(); + AnimatedFloat animatedFloat = new AnimatedFloat(250, CubicBezierInterpolator.DEFAULT); + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + animatedFloat.setParent(this); + animatedFloat.set(showViewsProgress ? 1f : 0, false); + if (animatedFloat.get() != 0) { + + if (animatedFloat.get() != 1f) { + canvas.saveLayerAlpha(0, 0, getLayoutParams().width, getMeasuredHeight(), (int) (animatedFloat.get() * 255), Canvas.ALL_SAVE_FLAG); + } else { + canvas.save(); + } + AndroidUtilities.rectTmp.set(0, 0, getLayoutParams().width, getMeasuredHeight()); + loadingDrawable.setBounds(AndroidUtilities.rectTmp); + loadingDrawable.setRadiiDp(24); + loadingDrawable.setColors(ColorUtils.setAlphaComponent(Color.WHITE, 20), ColorUtils.setAlphaComponent(Color.WHITE, 50), ColorUtils.setAlphaComponent(Color.WHITE, 50), ColorUtils.setAlphaComponent(Color.WHITE, 70)); + loadingDrawable.draw(canvas); + invalidate(); + canvas.restore(); + } + } + }; + selfAvatarsContainer.setOnClickListener(v -> { + showUserViewsDialog(); + }); + selfView.addView(selfAvatarsContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 32, 0, 9, 11, 0, 0)); + + selfAvatarsView = new HwAvatarsImageView(getContext(), false); + selfAvatarsView.setAvatarsTextSize(AndroidUtilities.dp(18)); + selfView.addView(selfAvatarsView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 28, 0, 13, 13, 0, 0)); + + selfStatusView = new TextView(getContext()); + selfStatusView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + selfStatusView.setTextColor(Color.WHITE); + selfView.addView(selfStatusView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, 0, 0, 16, 0, 9)); + + ImageView imageView = new ImageView(getContext()); + imageView.setImageDrawable(sharedResources.deleteDrawable); + +// int padding = AndroidUtilities.dp(12); +// imageView.setPadding(padding, padding, padding, padding); +// selfView.addView(imageView, LayoutHelper.createFrame(48, 48, Gravity.RIGHT | Gravity.CENTER_VERTICAL, 0, 0, 6, 0)); +// imageView.setOnClickListener(v -> { +// deleteStory(); +// }); + + + selfAvatarsContainer.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(15), 0, ColorUtils.setAlphaComponent(Color.WHITE, 120))); + imageView.setBackground(Theme.createCircleSelectorDrawable(ColorUtils.setAlphaComponent(Color.WHITE, 120), -AndroidUtilities.dp(2), -AndroidUtilities.dp(2))); + } + + private void deleteStory() { + AlertDialog.Builder builder = new AlertDialog.Builder(getContext(), resourcesProvider); + builder.setTitle(LocaleController.getString("DeleteStoryTitle", R.string.DeleteStoryTitle)); + builder.setMessage(LocaleController.getString("DeleteStorySubtitle", R.string.DeleteStorySubtitle)); + builder.setPositiveButton(LocaleController.getString("Delete", R.string.Delete), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + currentStory.cancelOrDelete(); + updateStoryItems(); + if (isActive && count == 0) { + delegate.switchToNextAndRemoveCurrentPeer(); + return; + } + if (selectedPosition >= count) { + selectedPosition = count - 1; + } else if (selectedPosition < 0) { + selectedPosition = 0; + } + updatePosition(); + } + }); + builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), (DialogInterface.OnClickListener) (dialog, which) -> { + dialog.dismiss(); + }); + AlertDialog dialog = builder.create(); + delegate.showDialog(dialog); + dialog.redPositive(); + } + + private void showUserViewsDialog() { + if (StoriesUtilities.hasExpiredViews(currentStory.storyItem)) { + performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + BulletinFactory bulletinFactory = BulletinFactory.global(); + if (bulletinFactory != null) { + bulletinFactory.createErrorBulletin(AndroidUtilities.replaceTags(LocaleController.getString("ExpiredViewsStub", R.string.ExpiredViewsStub))).show(); + } + } else { + storyViewer.openViews(); + } + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + sharedResources.topOverlayGradient.setBounds(0, 0, getMeasuredWidth(), AndroidUtilities.dp(72)); + } + + @Override + protected void dispatchDraw(Canvas canvas) { + updateViewOffsets(); + super.dispatchDraw(canvas); + if (chatActivityEnterView != null) { + chatActivityEnterView.drawRecordedPannel(canvas); + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + attachedToWindow = true; + imageReceiver.onAttachedToWindow(); + rightPreloadImageReceiver.onAttachedToWindow(); + leftPreloadImageReceiver.onAttachedToWindow(); + if (chatActivityEnterView != null) { + chatActivityEnterView.onResume(); + } + // sharedResources.muteDrawable.addView(muteIconView); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.storiesUpdated); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.storiesListUpdated); + NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.emojiLoaded); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + attachedToWindow = false; + imageReceiver.onDetachedFromWindow(); + rightPreloadImageReceiver.onDetachedFromWindow(); + leftPreloadImageReceiver.onDetachedFromWindow(); + if (chatActivityEnterView != null) { + chatActivityEnterView.onPause(); + } + //sharedResources.muteDrawable.removeView(muteIconView); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.storiesUpdated); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.storiesListUpdated); + NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.emojiLoaded); + } + + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.storiesUpdated || id == NotificationCenter.storiesListUpdated && storyViewer.storiesList == args[0]) { + if (delegate != null && delegate.isClosed()) { + return; + } + if (isActive) { + updateStoryItems(); + if (count == 0) { + if (!deletedPeer) { + deletedPeer = true; + delegate.switchToNextAndRemoveCurrentPeer(); + } + return; + } + if (selectedPosition >= storyItems.size() + uploadingStories.size()) { + selectedPosition = storyItems.size() + uploadingStories.size() - 1; + } + updatePosition(); + if (isSelf) { + updateUserViews(); + } + } + if (storyViewer.overrideUserStories != null) { + storiesController.loadSkippedStories(storyViewer.overrideUserStories, true); + } else if (dialogId != 0) { + storiesController.loadSkippedStories(dialogId); + } + if (editStoryItem != null) { + editStoryItem.animate().alpha(storiesController.hasUploadingStories() && currentStory.isVideo && !SharedConfig.allowPreparingHevcPlayers() ? .5f : 1f).start(); + } + } else if (id == NotificationCenter.emojiLoaded) { + storyCaptionView.captionTextview.invalidate(); + } + } + + public void updatePosition() { + updatePosition(false); + } + + private void updatePosition(boolean preload) { + if (storyItems.isEmpty() && uploadingStories.isEmpty()) { + return; + } + forceUpdateOffsets = true; + TLRPC.StoryItem oldStoryItem = currentStory.storyItem; + StoriesController.UploadingStory oldUploadingStory = currentStory.uploadingStory; + + String filter = StoriesUtilities.getStoryImageFilter(); + + lastNoThumb = false; + unsupported = false; + int position = selectedPosition; + + final boolean wasUploading = isUploading; + final boolean wasEditing = isEditing; + + currentStory.editingSourceItem = null; + if (!uploadingStories.isEmpty() && position >= storyItems.size()) { + isUploading = true; + isEditing = false; + position -= storyItems.size(); + if (position < 0 || position >= uploadingStories.size()) { + return; + } + StoriesController.UploadingStory uploadingStory = uploadingStories.get(position); + Drawable thumbDrawable = null; + imageReceiver.setCrossfadeWithOldImage(false); + imageReceiver.setCrossfadeDuration(ImageReceiver.DEFAULT_CROSSFADE_DURATION); + if (uploadingStory.entry.thumbBitmap != null) { + Bitmap blurredBitmap = Bitmap.createBitmap(uploadingStory.entry.thumbBitmap); + Utilities.blurBitmap(blurredBitmap, 3, 1, blurredBitmap.getWidth(), blurredBitmap.getHeight(), blurredBitmap.getRowBytes()); + thumbDrawable = new BitmapDrawable(blurredBitmap); + } + if (uploadingStory.isVideo) { + imageReceiver.setImage(null, null, ImageLocation.getForPath(uploadingStory.firstFramePath), filter, null, null, thumbDrawable, 0, null, null, 0); + } else { + imageReceiver.setImage(null, null, ImageLocation.getForPath(uploadingStory.path), filter, null, null, thumbDrawable, 0, null, null, 0); + } + currentStory.set(uploadingStory); + allowShare = allowShareLink = false; + } else { + isUploading = false; + isEditing = false; + if (position < 0 || position > storyItems.size() - 1) { + storyViewer.close(true); + return; + } + TLRPC.StoryItem storyItem = storyItems.get(position); + StoriesController.UploadingStory editingStory = storiesController.findEditingStory(storyItem); + if (editingStory != null) { + isEditing = true; + imageReceiver.setCrossfadeWithOldImage(false); + imageReceiver.setCrossfadeDuration(onImageReceiverThumbLoaded == null ? ImageReceiver.DEFAULT_CROSSFADE_DURATION : 0); + if (editingStory.isVideo) { + imageReceiver.setImage(null, null, ImageLocation.getForPath(editingStory.firstFramePath), filter, /*messageObject.strippedThumb*/null, 0, null, null, 0); + } else { + imageReceiver.setImage(null, null, ImageLocation.getForPath(editingStory.firstFramePath), filter, /*messageObject.strippedThumb*/null, 0, null, null, 0); + } + currentStory.set(editingStory); + currentStory.editingSourceItem = storyItem; + allowShare = allowShareLink = false; + } else { + boolean isVideo = storyItem.media != null && MessageObject.isVideoDocument(storyItem.media.document); + Drawable thumbDrawable = null; + storyItem.dialogId = dialogId; + imageReceiver.setCrossfadeWithOldImage(wasEditing); + imageReceiver.setCrossfadeDuration(ImageReceiver.DEFAULT_CROSSFADE_DURATION); + if (storyItem.media instanceof TLRPC.TL_messageMediaUnsupported) { + unsupported = true; + } else if (storyItem.attachPath != null) { + if (storyItem.media == null) { + isVideo = storyItem.attachPath.toLowerCase().endsWith(".mp4"); + } + if (isVideo) { + if (storyItem.media != null) { + thumbDrawable = ImageLoader.createStripedBitmap(storyItem.media.document.thumbs); + } + if (storyItem.firstFramePath != null && ImageLoader.getInstance().isInMemCache(ImageLocation.getForPath(storyItem.firstFramePath).getKey(null, null, false) + "@" + filter, false)) { + imageReceiver.setImage(null, null, ImageLocation.getForPath(storyItem.firstFramePath), filter, null, null, thumbDrawable, 0, null, null, 0); + } else { + imageReceiver.setImage(null, null, ImageLocation.getForPath(storyItem.attachPath), filter + "_pframe", null, null, thumbDrawable, 0, null, null, 0); + } + } else { + TLRPC.Photo photo = storyItem.media != null ? storyItem.media.photo : null; + if (photo != null) { + thumbDrawable = ImageLoader.createStripedBitmap(photo.sizes); + } + if (wasEditing) { + imageReceiver.setImage(ImageLocation.getForPath(storyItem.attachPath), filter, ImageLocation.getForPath(storyItem.firstFramePath), filter, thumbDrawable, 0, null, null, 0); + } else { + imageReceiver.setImage(ImageLocation.getForPath(storyItem.attachPath), filter, null, null, thumbDrawable, 0, null, null, 0); + } + } + } else { + if (storyViewer.isSingleStory && storyViewer.transitionViewHolder.storyImage != null) { + thumbDrawable = storyViewer.transitionViewHolder.storyImage.getDrawable(); + } + storyItem.dialogId = dialogId; + if (isVideo) { + TLRPC.PhotoSize size = FileLoader.getClosestPhotoSizeWithSize(storyItem.media.document.thumbs, 1000); + if (thumbDrawable == null) { + thumbDrawable = ImageLoader.createStripedBitmap(storyItem.media.document.thumbs); + } + //imageReceiver.setImage(ImageLocation.getForDocument(size, storyItem.media.document), filter, null, null, thumbDrawable, 0, null, storyItem, 0); + imageReceiver.setImage(ImageLocation.getForDocument(storyItem.media.document), filter + "_pframe", ImageLocation.getForDocument(size, storyItem.media.document), filter, null, null, thumbDrawable, 0, null, storyItem, 0); + } else { + TLRPC.Photo photo = storyItem.media != null ? storyItem.media.photo : null; + if (photo != null && photo.sizes != null) { + if (thumbDrawable == null) { + thumbDrawable = ImageLoader.createStripedBitmap(photo.sizes); + } + TLRPC.PhotoSize size = FileLoader.getClosestPhotoSizeWithSize(photo.sizes, Integer.MAX_VALUE); +// TLRPC.PhotoSize thumbSize = FileLoader.getClosestPhotoSizeWithSize(photo.sizes, 800); +// if (thumbSize != size) { +// imageReceiver.setImage(ImageLocation.getForPhoto(size, photo), filter, ImageLocation.getForPhoto(thumbSize, photo), filter, null, null, thumbDrawable, 0, null, storyItem, 0); +// } else { + imageReceiver.setImage(null, null, ImageLocation.getForPhoto(size, photo), filter, null, null, thumbDrawable, 0, null, storyItem, 0); + // } + } else { + imageReceiver.clearImage(); + } + } + } + storyItem.dialogId = dialogId; + currentStory.set(storyItem); + allowShare = allowShareLink = !unsupported && currentStory.storyItem != null && !(currentStory.storyItem instanceof TLRPC.TL_storyItemDeleted) && !(currentStory.storyItem instanceof TLRPC.TL_storyItemSkipped); + if (allowShare) { + allowShare = currentStory.allowScreenshots() && currentStory.storyItem.isPublic; + } + if (allowShareLink) { + final TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); + allowShareLink = user != null && UserObject.getPublicUsername(user) != null && currentStory.storyItem.isPublic; + } + NotificationsController.getInstance(currentAccount).processReadStories(dialogId, storyItem.id); + } + } + + if (currentStory.storyItem != null && !preload) { + storyViewer.dayStoryId = currentStory.storyItem.id; + } + + storyViewer.storiesViewPager.checkAllowScreenshots(); + imageChanged = true; + if (isSelf) { + updateUserViews(); + } + + final boolean sameId = + getStoryId(currentStory.storyItem, currentStory.uploadingStory) == getStoryId(oldStoryItem, oldUploadingStory) || + oldUploadingStory != null && currentStory.storyItem != null && TextUtils.equals(oldUploadingStory.path, currentStory.storyItem.attachPath); + final boolean animateSubtitle = sameId && (isEditing != wasEditing || isUploading != wasUploading); + + boolean storyChanged = false; + if (!( + oldUploadingStory != null && oldUploadingStory.path != null && oldUploadingStory.path.equals(currentStory.getLocalPath()) || + (oldStoryItem != null && currentStory.storyItem != null && oldStoryItem.id == currentStory.storyItem.id) + )) { + storyChanged = true; + if (chatActivityEnterView != null) { + chatActivityEnterView.getEditField().setText(""); + } + currentImageTime = 0; + switchEventSent = false; + + if (currentStory.uploadingStory != null) { + if (headerView.radialProgress != null) { + headerView.radialProgress.setProgress(currentStory.uploadingStory.progress, false); + } + headerView.backupImageView.invalidate(); + } else if (!animateSubtitle) { + headerView.progressToUploading = 0; + } + Bulletin.hideVisible(); + storyCaptionView.reset(); + cancelWaiting(); + } + + if (storyChanged || oldUploadingStory != null && currentStory.uploadingStory == null) { + if (currentStory.uploadingStory != null) { + headerView.setSubtitle(StoriesUtilities.getUploadingStr(headerView.subtitleView[0], false, isEditing), animateSubtitle); + } else if (currentStory.storyItem != null) { + if (currentStory.storyItem.date == -1) { + headerView.setSubtitle(LocaleController.getString("CachedStory", R.string.CachedStory)); + } else { + CharSequence string = LocaleController.formatStoryDate(currentStory.storyItem.date); + if (currentStory.storyItem.edited) { + SpannableStringBuilder spannableStringBuilder = SpannableStringBuilder.valueOf(string); + DotDividerSpan dotDividerSpan = new DotDividerSpan(); + dotDividerSpan.setTopPadding(AndroidUtilities.dp(1.5f)); + dotDividerSpan.setSize(5); + spannableStringBuilder.append(" . ").setSpan(dotDividerSpan, spannableStringBuilder.length() - 2, spannableStringBuilder.length() - 1, 0); + spannableStringBuilder.append(LocaleController.getString("EditedMessage", R.string.EditedMessage)); + string = spannableStringBuilder; + } + headerView.setSubtitle(string, animateSubtitle); + } + } + if (privacyHint != null) { + privacyHint.hide(false); + } + if (soundTooltip != null) { + soundTooltip.hide(false); + } + } + CharSequence caption = null; + if (currentStory.uploadingStory != null) { + caption = currentStory.uploadingStory.entry.caption; + caption = Emoji.replaceEmoji(caption, storyCaptionView.captionTextview.getPaint().getFontMetricsInt(), AndroidUtilities.dp(20), false); + SpannableStringBuilder spannableStringBuilder = SpannableStringBuilder.valueOf(caption); + MessageObject.addLinks(true, spannableStringBuilder); + } else if (currentStory.storyItem != null) { + caption = currentStory.storyItem.caption; + caption = Emoji.replaceEmoji(caption, storyCaptionView.captionTextview.getPaint().getFontMetricsInt(), AndroidUtilities.dp(20), false); + if (caption != null && currentStory.storyItem.entities != null) { + SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(currentStory.storyItem.caption); + spannableStringBuilder = SpannableStringBuilder.valueOf(MessageObject.replaceAnimatedEmoji(spannableStringBuilder, currentStory.storyItem.entities, storyCaptionView.captionTextview.getPaint().getFontMetricsInt(), false)); + SpannableStringBuilder.valueOf(Emoji.replaceEmoji(spannableStringBuilder, storyCaptionView.captionTextview.getPaint().getFontMetricsInt(), false)); + MessageObject.addLinks(true, spannableStringBuilder); + MessageObject.addEntitiesToText(spannableStringBuilder, currentStory.storyItem.entities, false, true, true, false); + caption = spannableStringBuilder; + } + } + + if (unsupported) { + createUnsupportedContainer(); + createReplyDisabledView(); + unsupportedContainer.setVisibility(View.VISIBLE); + replyDisabledTextView.setVisibility(View.VISIBLE); + allowShare = allowShareLink = false; + if (chatActivityEnterView != null) { + chatActivityEnterView.setVisibility(View.GONE); + } + if (selfView != null) { + selfView.setVisibility(View.GONE); + } + } else { + if (UserObject.isService(dialogId) && chatActivityEnterView != null) { + chatActivityEnterView.setVisibility(View.GONE); + } else if (!isSelf && chatActivityEnterView != null) { + chatActivityEnterView.setVisibility(View.VISIBLE); + } + if (isSelf && selfView != null) { + selfView.setVisibility(View.VISIBLE); + } + if (unsupportedContainer != null) { + unsupportedContainer.setVisibility(View.GONE); + } + if (UserObject.isService(dialogId)){ + createReplyDisabledView(); + replyDisabledTextView.setVisibility(View.VISIBLE); + } else if (replyDisabledTextView != null) { + replyDisabledTextView.setVisibility(View.GONE); + } + } + + if (caption != null && !unsupported) { + storyCaptionView.captionTextview.setText(caption); + storyCaptionView.setVisibility(View.VISIBLE); + } else { + if (isActive) { + delegate.setIsCaption(false); + delegate.setIsCaptionPartVisible(isCaptionPartVisible = false); + } + storyCaptionView.setVisibility(View.GONE); + } + storyContainer.invalidate(); + if (delegate != null && isSelectedPeer()) { + delegate.onPeerSelected(dialogId, selectedPosition); + } + shareButton.setVisibility(allowShare ? View.VISIBLE : View.GONE); + + storyViewer.savedPositions.append(dialogId, position); + + + if (isActive) { + requestVideoPlayer(0); + updatePreloadImages(); + imageReceiver.bumpPriority(); + } + + listPosition = 0; + if (storyViewer.storiesList != null && currentStory.storyItem != null) { + int id = currentStory.storyItem.id; + for (int i = 0; i < storyViewer.storiesList.messageObjects.size(); ++i) { + MessageObject obj = storyViewer.storiesList.messageObjects.get(i); + if (obj != null && obj.getId() == id) { + listPosition = i; + break; + } + } + } + linesPosition = selectedPosition; + linesCount = count; + if (storyViewer.reversed) { + linesPosition = linesCount - 1 - linesPosition; + } + + if (currentStory.isVideo()) { + muteIconContainer.setVisibility(View.VISIBLE); + muteIconViewAlpha = currentStory.hasSound() ? 1f : 0.5f; + if (currentStory.hasSound()) { + muteIconView.setVisibility(View.VISIBLE); + noSoundIconView.setVisibility(View.GONE); + } else { + muteIconView.setVisibility(View.GONE); + noSoundIconView.setVisibility(View.VISIBLE); + } + muteIconContainer.setAlpha(muteIconViewAlpha * (1f - outT)); + } else { + muteIconContainer.setVisibility(View.GONE); + } + + if (currentStory.uploadingStory != null) { + privacyButton.set(isSelf, currentStory.uploadingStory, sameId && editedPrivacy); + } else if (currentStory.storyItem != null) { + privacyButton.set(isSelf, currentStory.storyItem, sameId && editedPrivacy); + } else { + privacyButton.set(isSelf, (TLRPC.StoryItem) null, sameId && editedPrivacy); + } + editedPrivacy = false; + privacyButton.setTranslationX(muteIconContainer.getVisibility() == View.VISIBLE ? -AndroidUtilities.dp(44) : 0); + +// final boolean closeFriends = currentStory.forCloseFriends(); +// if (oldStoryItem != null && currentStory.storyItem != null && oldStoryItem.id == currentStory.storyItem.id) { +// if (closeFriends) { +// closeFriendsBadge.setVisibility(View.VISIBLE); +// } +// closeFriendsBadge.clearAnimation(); +// closeFriendsBadge.animate().scaleX(closeFriends ? 1 : 0).scaleY(closeFriends ? 1 : 0).withEndAction(() -> { +// if (!closeFriends) { +// closeFriendsBadge.setVisibility(View.GONE); +// } +// }).setInterpolator(closeFriends ? new OvershootInterpolator(3) : CubicBezierInterpolator.DEFAULT).setDuration(closeFriends ? 240 : 120).start(); +// } else { +// closeFriendsBadge.setScaleX(closeFriends ? 1 : 0); +// closeFriendsBadge.setScaleY(closeFriends ? 1 : 0); +// closeFriendsBadge.setVisibility(currentStory.forCloseFriends() ? View.VISIBLE : View.GONE); +// } +// closeFriendsBadge.setTranslationX(muteIconContainer.getVisibility() == View.VISIBLE ? -AndroidUtilities.dp(44) : 0); + //sharedResources.muteDrawable.setIcon(storyViewer.soundEnabled() ? R.drawable.media_mute : R.drawable.media_unmute, false); + sharedResources.setIconMuted(!storyViewer.soundEnabled(), false); + // sharedResources. + + if (isActive && currentStory.storyItem != null) { + FileLog.d("StoryViewer displayed story dialogId=" + dialogId + " storyId=" + currentStory.storyItem.id); + } + if (isSelf) { + SelfStoryViewsPage.preload(currentAccount, currentStory.storyItem); + } + headerView.titleView.setPadding(0, 0, storyViewer.storiesList != null && storyViewer.storiesList.getCount() != linesCount ? AndroidUtilities.dp(56) : 0, 0); + } + + private void createReplyDisabledView() { + if (replyDisabledTextView != null) { + return; + } + replyDisabledTextView = new TextView(getContext()); + replyDisabledTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + replyDisabledTextView.setTextColor(ColorUtils.blendARGB(Color.BLACK, Color.WHITE, 0.42f)); + replyDisabledTextView.setGravity(Gravity.LEFT); + replyDisabledTextView.setText(LocaleController.getString("StoryReplyDisabled", R.string.StoryReplyDisabled)); + addView(replyDisabledTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL)); + } + + ArrayList uriesToPrepare = new ArrayList<>(); + ArrayList documentsToPrepare = new ArrayList<>(); + + private void updatePreloadImages() { + int maxWidth = Math.max(AndroidUtilities.getRealScreenSize().x, AndroidUtilities.getRealScreenSize().y); + int filterSize = (int) (maxWidth / AndroidUtilities.density); + String filter = filterSize + "_" + filterSize; + + uriesToPrepare.clear(); + documentsToPrepare.clear(); + for (int i = 0; i < 2; i++) { + int position = selectedPosition; + ImageReceiver imageReceiver; + if (i == 0) { + position--; + imageReceiver = leftPreloadImageReceiver; + if (position < 0) { + imageReceiver.clearImage(); + continue; + } + } else { + position++; + imageReceiver = rightPreloadImageReceiver; + if (position >= getStoriesCount()) { + imageReceiver.clearImage(); + continue; + } + } + if (!uploadingStories.isEmpty() && position >= storyItems.size()) { + position -= storyItems.size(); + StoriesController.UploadingStory uploadingStory = uploadingStories.get(position); + setStoryImage(uploadingStory, imageReceiver, filter); + } else { + if (position < 0) { + position = 0; + } + if (position >= storyItems.size()) { + position = storyItems.size() - 1; + } + TLRPC.StoryItem storyItem = storyItems.get(position); + storyItem.dialogId = dialogId; + setStoryImage(storyItem, imageReceiver, filter); + + boolean isVideo = storyItem.media != null && storyItem.media.document != null && MessageObject.isVideoDocument(storyItem.media.document); + if (isVideo) { + TLRPC.Document document = storyItem.media.document; + if (storyItem.fileReference == 0) { + storyItem.fileReference = FileLoader.getInstance(currentAccount).getFileReference(storyItem); + } + String params = null; + try { + params = "?account=" + currentAccount + + "&id=" + document.id + + "&hash=" + document.access_hash + + "&dc=" + document.dc_id + + "&size=" + document.size + + "&mime=" + URLEncoder.encode(document.mime_type, "UTF-8") + + "&rid=" + storyItem.fileReference + + "&name=" + URLEncoder.encode(FileLoader.getDocumentFileName(document), "UTF-8") + + "&reference=" + Utilities.bytesToHex(document.file_reference != null ? document.file_reference : new byte[0]); + uriesToPrepare.add(Uri.parse("tg://" + FileLoader.getAttachFileName(document) + params)); + documentsToPrepare.add(document); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + } + } + } +// if (selfAvatarsContainer != null) { +// selfAvatarsContainer.setEnabled(!StoriesUtilities.hasExpiredViews(currentStory.storyItem)); +// } + delegate.preparePlayer(documentsToPrepare, uriesToPrepare); + } + + private void setStoryImage(TLRPC.StoryItem storyItem, ImageReceiver imageReceiver, String filter) { + StoriesController.UploadingStory editingStory = storiesController.findEditingStory(storyItem); + if (editingStory != null) { + setStoryImage(editingStory, imageReceiver, filter); + return; + } + boolean isVideo = storyItem.media != null && storyItem.media.document != null && MessageObject.isVideoDocument(storyItem.media.document); + + if (storyItem.attachPath != null) { + if (storyItem.media == null) { + isVideo = storyItem.attachPath.toLowerCase().endsWith(".mp4"); + } + if (isVideo) { + imageReceiver.setImage(ImageLocation.getForPath(storyItem.attachPath), filter +"_pframe", ImageLocation.getForPath(storyItem.firstFramePath), filter, null, null, /*messageObject.strippedThumb*/null, 0, null, null, 0); + } else { + imageReceiver.setImage(ImageLocation.getForPath(storyItem.attachPath), filter, null, null, /*messageObject.strippedThumb*/null, 0, null, null, 0); + } + } else { + if (isVideo) { + TLRPC.PhotoSize size = FileLoader.getClosestPhotoSizeWithSize(storyItem.media.document.thumbs, 1000); + imageReceiver.setImage(ImageLocation.getForDocument(storyItem.media.document), filter + "_pframe", ImageLocation.getForDocument(size, storyItem.media.document), filter, null, null, null, 0, null, storyItem, 0); + } else { + TLRPC.Photo photo = storyItem.media != null ? storyItem.media.photo : null; + if (photo != null && photo.sizes != null) { + TLRPC.PhotoSize size = FileLoader.getClosestPhotoSizeWithSize(photo.sizes, Integer.MAX_VALUE); + TLRPC.PhotoSize thumbSize = FileLoader.getClosestPhotoSizeWithSize(photo.sizes, 800); +// if (thumbSize != size) { +// imageReceiver.setImage(ImageLocation.getForPhoto(size, photo), filter, ImageLocation.getForPhoto(thumbSize, photo), filter, null, null, null, 0, null, storyItem, 0); +// } else { + imageReceiver.setImage(null, null, ImageLocation.getForPhoto(size, photo), filter, null, null, null, 0, null, storyItem, 0); + //} + } else { + imageReceiver.clearImage(); + } + } + } + } + + private void setStoryImage(StoriesController.UploadingStory uploadingStory, ImageReceiver imageReceiver, String filter) { + if (uploadingStory.isVideo) { + imageReceiver.setImage(null, null, ImageLocation.getForPath(uploadingStory.firstFramePath), filter, null, null, null, 0, null, null, 0); + } else { + imageReceiver.setImage(ImageLocation.getForPath(uploadingStory.path), filter, null, null, null, 0, null, null, 0); + } + } + + private void cancelWaiting() { + if (cancellableViews != null) { + cancellableViews.run(); + cancellableViews = null; + } + showViewsProgress = false; + if (isActive) { + delegate.setIsWaiting(false); + } + } + + private void updateUserViews() { + TLRPC.StoryItem storyItem = currentStory.storyItem; + if (storyItem == null) { + storyItem = currentStory.editingSourceItem; + } + if (storyItem != null) { + if (storyItem.views != null && storyItem.views.views_count > 0) { + int avatarsCount = 0; + int k = 0; + for (int i = 0; i < storyItem.views.recent_viewers.size(); i++) { + TLObject object = MessagesController.getInstance(currentAccount).getUserOrChat(storyItem.views.recent_viewers.get(i)); + if (object != null) { + selfAvatarsView.setObject(avatarsCount, currentAccount, object); + avatarsCount++; + } + if (avatarsCount >= 3) { + break; + } + } + k = avatarsCount; + while (avatarsCount < 3) { + selfAvatarsView.setObject(avatarsCount, currentAccount, null); + avatarsCount++; + } + selfAvatarsView.commitTransition(false); + selfStatusView.setText(LocaleController.formatPluralStringComma("Views", storyItem.views.views_count)); + if (k == 0) { + selfAvatarsView.setVisibility(View.GONE); + selfStatusView.setTranslationX(AndroidUtilities.dp(16)); + } else { + selfAvatarsView.setVisibility(View.VISIBLE); + selfStatusView.setTranslationX(AndroidUtilities.dp(13) + AndroidUtilities.dp(24) + AndroidUtilities.dp(20) * (k - 1) + AndroidUtilities.dp(10)); + } + selfAvatarsContainer.setVisibility(View.VISIBLE); + } else { + selfStatusView.setText(storyViewer.storiesList == null ? LocaleController.getString("NobodyViews", R.string.NobodyViews) : LocaleController.getString("NobodyViewsArchived", R.string.NobodyViewsArchived)); + selfStatusView.setTranslationX(AndroidUtilities.dp(16)); + selfAvatarsView.setVisibility(View.GONE); + selfAvatarsContainer.setVisibility(View.GONE); + } + } else { + selfStatusView.setText(""); + selfAvatarsContainer.setVisibility(View.GONE); + selfAvatarsView.setVisibility(View.GONE); + } + } + + private void requestVideoPlayer(long t) { + if (isActive) { + Uri uri = null; + if (currentStory.isVideo()) { + TLRPC.Document document = null; + if (currentStory.getLocalPath() != null && new File(currentStory.getLocalPath()).exists()) { + uri = Uri.fromFile(new File(currentStory.getLocalPath())); + videoDuration = 0; + } else if (currentStory.storyItem != null) { + currentStory.storyItem.dialogId = dialogId; + try { + document = currentStory.storyItem.media.document; + if (currentStory.storyItem.fileReference == 0) { + currentStory.storyItem.fileReference = FileLoader.getInstance(currentAccount).getFileReference(currentStory.storyItem); + } + String params = "?account=" + currentAccount + + "&id=" + document.id + + "&hash=" + document.access_hash + + "&dc=" + document.dc_id + + "&size=" + document.size + + "&mime=" + URLEncoder.encode(document.mime_type, "UTF-8") + + "&rid=" + currentStory.storyItem.fileReference + + "&name=" + URLEncoder.encode(FileLoader.getDocumentFileName(document), "UTF-8") + + "&reference=" + Utilities.bytesToHex(document.file_reference != null ? document.file_reference : new byte[0]); + uri = Uri.parse("tg://" + FileLoader.getAttachFileName(document) + params); + videoDuration = (long) (MessageObject.getDocumentDuration(document) * 1000); + } catch (Exception exception) { + uri = null; + } + } + delegate.requestPlayer(document, uri, t, playerSharedScope); + storyContainer.invalidate(); + } else { + delegate.requestPlayer(null, null, 0, playerSharedScope); + playerSharedScope.renderView = null; + playerSharedScope.firstFrameRendered = false; + } + } else { + playerSharedScope.renderView = null; + } +// imageReceiver.setImage(ImageLocation.getForPath(uploadingStory.path), ImageLoader.AUTOPLAY_FILTER, null, null, /*messageObject.strippedThumb*/null, 0, null, null, 0); + } + + public boolean isSelectedPeer() { + return false; + } + + public boolean switchToNext(boolean forward) { + if (storyViewer.reversed) { + forward = !forward; + } + if (forward) { + if (selectedPosition < getStoriesCount() - 1) { + selectedPosition++; + updatePosition(); + return true; + } + } else { + if (selectedPosition > 0) { + selectedPosition--; + updatePosition(); + return true; + } + } + return false; + } + + public void setDelegate(Delegate delegate) { + this.delegate = delegate; + } + + public void createBlurredBitmap(Canvas canvas, Bitmap bitmap) { + if (playerSharedScope.renderView != null && playerSharedScope.surfaceView != null) { + Bitmap surfaceBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { + AndroidUtilities.getBitmapFromSurface(playerSharedScope.surfaceView, surfaceBitmap); + } + if (surfaceBitmap != null) { + canvas.drawBitmap(surfaceBitmap, 0, 0, null); + } + } else if (playerSharedScope.renderView != null && playerSharedScope.textureView != null) { + Bitmap textureBitmap = playerSharedScope.textureView.getBitmap(bitmap.getWidth(), bitmap.getHeight()); + if (textureBitmap != null) { + canvas.drawBitmap(textureBitmap, 0, 0, null); + } + } else { + canvas.save(); + canvas.scale(bitmap.getWidth() / (float) storyContainer.getMeasuredWidth(), bitmap.getHeight() / (float) storyContainer.getMeasuredHeight()); + imageReceiver.draw(canvas); + canvas.restore(); + } + int color = AndroidUtilities.getDominantColor(bitmap); + float brightness = AndroidUtilities.computePerceivedBrightness(color); + if (brightness < 0.15f) { + canvas.drawColor(ColorUtils.setAlphaComponent(Color.WHITE, (int) (255 * 0.4f))); + } + + Utilities.blurBitmap(bitmap, 3, 1, bitmap.getWidth(), bitmap.getHeight(), bitmap.getRowBytes()); + Utilities.blurBitmap(bitmap, 3, 1, bitmap.getWidth(), bitmap.getHeight(), bitmap.getRowBytes()); + } + + public void stopPlaying(boolean stop) { + if (stop) { + imageReceiver.stopAnimation(); + imageReceiver.setAllowStartAnimation(false); + } else { + imageReceiver.startAnimation(); + imageReceiver.setAllowStartAnimation(true); + } + } + + public long getCurrentPeer() { + return dialogId; + } + + public ArrayList getCurrentDay() { + return day; + } + + public void setPaused(boolean paused) { + if (this.paused != paused) { + this.paused = paused; + stopPlaying(paused); + lastDrawTime = 0; + storyContainer.invalidate(); + } + } + + public int getSelectedPosition() { + return selectedPosition; + } + + public boolean closeKeyboardOrEmoji() { + if (storyCaptionView.textSelectionHelper.isInSelectionMode()) { + storyCaptionView.textSelectionHelper.clear(false); + return true; + } + if (privacyHint != null) { + privacyHint.hide(); + } + if (soundTooltip != null) { + soundTooltip.hide(); + } + + if (mediaBanTooltip != null) { + mediaBanTooltip.hide(true); + } + if (storyEditCaptionView != null && storyEditCaptionView.onBackPressed()) { + return true; + } else if (popupMenu != null && popupMenu.isShowing()) { + popupMenu.dismiss(); + return true; + } else if (checkRecordLocked(false)) { + return true; + } else if (reactionsContainerLayout != null && reactionsContainerLayout.getReactionsWindow() != null && reactionsContainerLayout.getReactionsWindow().isShowing()) { + reactionsContainerLayout.getReactionsWindow().dismiss(); + return true; + } else if (chatActivityEnterView != null && chatActivityEnterView.isPopupShowing()) { + if (realKeyboardHeight > 0) { + AndroidUtilities.hideKeyboard(chatActivityEnterView.getEmojiView()); + } else { + chatActivityEnterView.hidePopup(true, false); + } + return true; + } else if (getKeyboardHeight() >= AndroidUtilities.dp(20)) { + AndroidUtilities.hideKeyboard(chatActivityEnterView); + return true; + } else if (storyCaptionView.getVisibility() == View.VISIBLE && storyCaptionView.getProgressToBlackout() > 0) { + storyCaptionView.collapse(); + inBlackoutMode = false; + storyContainer.invalidate(); + return true; + } + return false; + } + + public boolean findClickableView(ViewGroup container, float x, float y, boolean swipeToDissmiss) { + if (container == null) { + return false; + } + if (privacyHint != null && privacyHint.shown()) { + return true; + } + if (soundTooltip != null && soundTooltip.shown()) { + return true; + } + + for (int i = 0; i < container.getChildCount(); i++) { + View child = container.getChildAt(i); + if (child.getVisibility() != View.VISIBLE) { + continue; + } + if (child == storyCaptionView) { + child.getHitRect(AndroidUtilities.rectTmp2); + if (AndroidUtilities.rectTmp2.contains((int) x, (int) y) && storyCaptionView.allowInterceptTouchEvent(x, y - child.getTop())) { + return true; + } + } + child.getHitRect(AndroidUtilities.rectTmp2); + if (keyboardVisible && child == chatActivityEnterView && y > AndroidUtilities.rectTmp2.top) { + return true; + } else if (!swipeToDissmiss && AndroidUtilities.rectTmp2.contains((int) x, (int) y) && (((child.isClickable() || child == reactionsContainerLayout) && child.isEnabled()) || (chatActivityEnterView != null && child == chatActivityEnterView.getRecordCircle()))) { + return true; + } else if (child.isEnabled() && child instanceof ViewGroup && findClickableView((ViewGroup) child, x - child.getX(), y - child.getY(), swipeToDissmiss)) { + return true; + } + } + return false; + } + + public void setAccount(int currentAccount) { + this.currentAccount = currentAccount; + storiesController = MessagesController.getInstance(currentAccount).storiesController; + if (reactionsContainerLayout != null) { + reactionsContainerLayout.setCurrentAccount(currentAccount); + reactionsContainerLayout.setMessage(null, null); + } + } + + public void setActive(boolean active) { + setActive(0, active); + } + + private static int activeCount; + public void setActive(long t, boolean active) { + if (isActive != active) { + activeCount += active ? 1 : -1; + isActive = active; + + if (isActive) { + if (useSurfaceInViewPagerWorkAround()) { + delegate.setIsSwiping(true); + AndroidUtilities.cancelRunOnUIThread(allowDrawSurfaceRunnable); + AndroidUtilities.runOnUIThread(allowDrawSurfaceRunnable, 100); + } + requestVideoPlayer(t); + updatePreloadImages(); + muteIconView.setAnimation(sharedResources.muteDrawable); + isActive = true; + + //storyViewer.allowScreenshots(allowScreenshots); + } else { + cancelTextSelection(); + muteIconView.clearAnimationDrawable(); + viewsThumbImageReceiver = null; + isLongPressed = false; + progressToHideInterface.set(0, true); + storyContainer.invalidate(); + invalidate(); + cancelWaiting(); + delegate.setIsRecording(false); + +// rightPreloadImageReceiver.clearImage(); +// leftPreloadImageReceiver.clearImage(); + } + imageReceiver.setFileLoadingPriority(isActive ? FileLoader.PRIORITY_HIGH : FileLoader.PRIORITY_NORMAL_UP); + leftPreloadImageReceiver.setFileLoadingPriority(isActive ? FileLoader.PRIORITY_NORMAL_UP : FileLoader.PRIORITY_LOW); + rightPreloadImageReceiver.setFileLoadingPriority(isActive ? FileLoader.PRIORITY_NORMAL_UP : FileLoader.PRIORITY_LOW); + if (isSelf) { + storiesController.pollViewsForSelfStories(isActive); + } + } + } + + public void progressToDismissUpdated() { + if (BIG_SCREEN) { + invalidate(); + } + } + + public void reset() { + headerView.backupImageView.getImageReceiver().setVisible(true, true); + if (changeBoundAnimator != null) { + chatActivityEnterView.reset(); + chatActivityEnterView.setAlpha(1f); + } + if (reactionsContainerLayout != null) { + reactionsContainerLayout.reset(); + } + if (instantCameraView != null) { + AndroidUtilities.removeFromParent(instantCameraView); + instantCameraView.hideCamera(true); + instantCameraView = null; + } + setActive(false); + setIsVisible(false); + isLongPressed = false; + progressToHideInterface.set(0, false); + viewsThumbImageReceiver = null; + messageSent = false; + cancelTextSelection(); + } + + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (resultCode == Activity.RESULT_OK) { + if (requestCode == 0 || requestCode == 2) { + createChatAttachView(); + if (chatAttachAlert != null) { + chatAttachAlert.getPhotoLayout().onActivityResultFragment(requestCode, data, null); + } + } else if (requestCode == 21) { + if (data == null) { + showAttachmentError(); + return; + } + if (data.getData() != null) { + sendUriAsDocument(data.getData()); + } else if (data.getClipData() != null) { + ClipData clipData = data.getClipData(); + for (int i = 0; i < clipData.getItemCount(); i++) { + sendUriAsDocument(clipData.getItemAt(i).getUri()); + } + } else { + showAttachmentError(); + } + if (chatAttachAlert != null) { + chatAttachAlert.dismiss(); + } + afterMessageSend(); + } + } + } + + private void sendUriAsDocument(Uri uri) { + if (uri == null) { + return; + } + TLRPC.StoryItem storyItem = currentStory.storyItem; + if (storyItem == null || storyItem instanceof TLRPC.TL_storyItemSkipped) { + return; + } + String extractUriFrom = uri.toString(); + if (extractUriFrom.contains("com.google.android.apps.photos.contentprovider")) { + try { + String firstExtraction = extractUriFrom.split("/1/")[1]; + int index = firstExtraction.indexOf("/ACTUAL"); + if (index != -1) { + firstExtraction = firstExtraction.substring(0, index); + String secondExtraction = URLDecoder.decode(firstExtraction, "UTF-8"); + uri = Uri.parse(secondExtraction); + } + } catch (Exception e) { + FileLog.e(e); + } + } + String tempPath = AndroidUtilities.getPath(uri); + String originalPath = tempPath; + boolean sendAsUri = false; + if (!BuildVars.NO_SCOPED_STORAGE) { + sendAsUri = true; + } else if (tempPath == null) { + originalPath = uri.toString(); + tempPath = MediaController.copyFileToCache(uri, "file"); + + if (tempPath == null) { + showAttachmentError(); + return; + } + } + if (sendAsUri) { + SendMessagesHelper.prepareSendingDocument(getAccountInstance(), null, null, uri, null, null, dialogId, null, null, storyItem, null, true, 0, null); + } else { + SendMessagesHelper.prepareSendingDocument(getAccountInstance(), tempPath, originalPath, null, null, null, dialogId, null, null, storyItem, null, true, 0, null); + } + } + + private void showAttachmentError() { + BulletinFactory.of(storyContainer, resourcesProvider).createErrorBulletin(LocaleController.getString("UnsupportedAttachment", R.string.UnsupportedAttachment), resourcesProvider).show(); + } + + public void setLongpressed(boolean isLongpressed) { + if (isActive) { + this.isLongPressed = isLongpressed; + invalidate(); + } + } + + public void showKeyboard() { + chatActivityEnterView.getEditField().requestFocus(); + AndroidUtilities.showKeyboard(chatActivityEnterView.getEditField()); + } + + public void checkPinchToZoom(MotionEvent ev) { + pinchToZoomHelper.checkPinchToZoom(ev, storyContainer, null, null); + } + + + public void setIsVisible(boolean visible) { + if (this.isVisible == visible) { + return; + } + isVisible = visible; + if (visible) { + imageReceiver.setCurrentAlpha(1f); + } + } + + public ArrayList getStoryItems() { + return storyItems; + } + + public void selectPosition(int position) { + if (selectedPosition != position) { + selectedPosition = position; + updatePosition(); + } + } + + public void cancelTouch() { + storyCaptionView.cancelTouch(); + } + + public void onActionDown(MotionEvent ev) { + if (privacyHint != null && privacyHint.shown() && privacyButton != null && + !privacyHint.containsTouch(ev, getX() + storyContainer.getX() + privacyHint.getX(), getY() + storyContainer.getY() + privacyHint.getY()) && + !hitButton(privacyButton, ev) + ) { + privacyHint.hide(); + } + if (soundTooltip != null && soundTooltip.shown() && muteIconContainer != null && + !soundTooltip.containsTouch(ev, getX() + storyContainer.getX() + soundTooltip.getX(), getY() + storyContainer.getY() + soundTooltip.getY()) && + !hitButton(muteIconContainer, ev) + ) { + soundTooltip.hide(); + } + } + + private boolean hitButton(View v, MotionEvent e) { + float ox = getX() + storyContainer.getX() + v.getX(), oy = getY() + storyContainer.getY() + v.getY(); + return ( + e.getX() >= ox && e.getX() <= ox + v.getWidth() && + e.getY() >= oy && e.getY() <= oy + v.getHeight() + ); + } + + Runnable allowDrawSurfaceRunnable = new Runnable() { + @Override + public void run() { + if (isActive && allowDrawSurface) { + delegate.setIsSwiping(false); + } + } + }; + + public void setOffset(float position) { + boolean allowDrawSurface = position == 0; + if (this.allowDrawSurface != allowDrawSurface) { + this.allowDrawSurface = allowDrawSurface; + storyContainer.invalidate(); + if (isActive) { + if (useSurfaceInViewPagerWorkAround()) { + if (allowDrawSurface) { + AndroidUtilities.cancelRunOnUIThread(allowDrawSurfaceRunnable); + AndroidUtilities.runOnUIThread(allowDrawSurfaceRunnable, 250); + } else { + AndroidUtilities.cancelRunOnUIThread(allowDrawSurfaceRunnable); + delegate.setIsSwiping(true); + } + } + } + } + } + + //surface can missing in view pager + public boolean useSurfaceInViewPagerWorkAround() { + return storyViewer.USE_SURFACE_VIEW && Build.VERSION.SDK_INT < 33; + } + + public void showNoSoundHint() { + muteIconContainer.callOnClick(); + } + + public boolean checkTextSelectionEvent(MotionEvent ev) { + if (storyCaptionView.textSelectionHelper.isInSelectionMode()) { + float xOffset = getX(); + float yOffset = getY() + ((View) getParent()).getY(); + ev.offsetLocation(-xOffset, -yOffset); + if (storyCaptionView.textSelectionHelper.getOverlayView(getContext()).onTouchEvent(ev)) { + return true; + } else { + ev.offsetLocation(xOffset, yOffset); + } + } + return false; + } + + public void cancelTextSelection() { + if (storyCaptionView.textSelectionHelper.isInSelectionMode()) { + storyCaptionView.textSelectionHelper.clear(); + } + } + + public static class PeerHeaderView extends FrameLayout { + + public BackupImageView backupImageView; + public SimpleTextView titleView; + private TextView[] subtitleView = new TextView[2]; + RadialProgress radialProgress; + StoryItemHolder storyItemHolder; + Paint radialProgressPaint; + private float progressToUploading; + + private boolean uploading; + private boolean uploadedTooFast; + + public PeerHeaderView(@NonNull Context context, StoryItemHolder holder) { + super(context); + this.storyItemHolder = holder; + backupImageView = new BackupImageView(context) { + @Override + protected void onDraw(Canvas canvas) { + if (imageReceiver.getVisible()) { + AndroidUtilities.rectTmp.set(0, 0, getMeasuredWidth(), getMeasuredHeight()); + drawUploadingProgress(canvas, AndroidUtilities.rectTmp, true, 1f); + } + super.onDraw(canvas); + } + }; + backupImageView.setRoundRadius(dp(16)); + addView(backupImageView, LayoutHelper.createFrame(32, 32, 0, 12, 2, 0, 0)); + setClipChildren(false); + + titleView = new SimpleTextView(context); + titleView.setTextSize(14); + titleView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + titleView.setMaxLines(1); + titleView.setEllipsizeByGradient(dp(4)); + // titleView.setSingleLine(true); + // titleView.setEllipsize(TextUtils.TruncateAt.END); + NotificationCenter.listenEmojiLoading(titleView); + addView(titleView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, 0, 54, 0, 86, 0)); + + for (int a = 0; a < 2; ++a) { + subtitleView[a] = new TextView(context); + subtitleView[a].setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12); + subtitleView[a].setMaxLines(1); + subtitleView[a].setSingleLine(true); + subtitleView[a].setEllipsize(TextUtils.TruncateAt.END); + subtitleView[a].setTextColor(Color.WHITE); + addView(subtitleView[a], LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, 0, 54, 18, 86, 0)); + } + + titleView.setTextColor(Color.WHITE); + } + + public void setSubtitle(CharSequence text) { + setSubtitle(text, false); + } + + private ValueAnimator subtitleAnimator; + public void setSubtitle(CharSequence text, boolean animated) { + if (subtitleAnimator != null) { + subtitleAnimator.cancel(); + subtitleAnimator = null; + } + if (animated) { + subtitleView[1].setText(subtitleView[0].getText()); + subtitleView[1].setVisibility(View.VISIBLE); + subtitleView[1].setAlpha(1f); + subtitleView[1].setTranslationY(0); + subtitleView[0].setText(text); + subtitleView[0].setVisibility(View.VISIBLE); + subtitleView[0].setAlpha(0f); + subtitleView[0].setTranslationY(-AndroidUtilities.dp(4)); + subtitleAnimator = ValueAnimator.ofFloat(0, 1); + subtitleAnimator.addUpdateListener(anm -> { + float t = (float) anm.getAnimatedValue(); + subtitleView[0].setAlpha(t); + subtitleView[0].setTranslationY((1f - t) * -AndroidUtilities.dp(4)); + subtitleView[1].setAlpha(1f - t); + subtitleView[1].setTranslationY(t * AndroidUtilities.dp(4)); + }); + subtitleAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + subtitleView[1].setVisibility(View.GONE); + subtitleView[0].setAlpha(1f); + subtitleView[0].setTranslationY(0); + } + }); + subtitleAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + subtitleAnimator.setDuration(340); + subtitleAnimator.start(); + } else { + subtitleView[0].setVisibility(View.VISIBLE); + subtitleView[0].setAlpha(1f); + subtitleView[0].setText(text); + subtitleView[1].setVisibility(View.GONE); + subtitleView[1].setAlpha(0f); + } + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + if (!isEnabled()) { + return false; + } + return super.dispatchTouchEvent(ev); + } + + public void drawUploadingProgress(Canvas canvas, RectF rect, boolean allowDraw, float progressToOpen) { + if ((storyItemHolder != null && storyItemHolder.uploadingStory != null) || progressToUploading != 0) { + float progress = 1f; + final boolean disappearing; + if (storyItemHolder != null && storyItemHolder.uploadingStory != null) { + progressToUploading = 1f; + progress = storyItemHolder.uploadingStory.progress; + disappearing = false; + if (!uploading) { + uploading = true; + } + } else { + disappearing = true; + if (uploading) { + uploading = false; + uploadedTooFast = radialProgress.getAnimatedProgress() < .2f; + } + if (!uploadedTooFast) { + progressToUploading = Utilities.clamp(progressToUploading - (1000f / AndroidUtilities.screenRefreshRate) / 300f, 1f, 0); + } + } + if (radialProgress == null) { + radialProgress = new RadialProgress(backupImageView); + radialProgress.setBackground(null, true, false); + } + radialProgress.setDiff(0); + ImageReceiver imageReceiver = backupImageView.getImageReceiver(); + float offset = AndroidUtilities.dp(3) - AndroidUtilities.dp(6) * (1f - progressToUploading); + radialProgress.setProgressRect( + (int) (rect.left - offset), (int) (rect.top - offset), + (int) (rect.right + offset), (int) (rect.bottom + offset) + ); + radialProgress.setProgress(disappearing ? 1 : Utilities.clamp(progress, 1f, 0), true); + if (uploadedTooFast && disappearing && radialProgress.getAnimatedProgress() >= .9f) { + progressToUploading = Utilities.clamp(progressToUploading - (1000f / AndroidUtilities.screenRefreshRate) / 300f, 1f, 0); + } + if (allowDraw) { + if (progressToOpen != 1f) { + Paint paint = StoriesUtilities.getActiveCirclePaint(imageReceiver, false); + paint.setAlpha((int) (255 * progressToUploading)); + radialProgress.setPaint(paint); + radialProgress.draw(canvas); + } + if (radialProgressPaint == null) { + radialProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + radialProgressPaint.setColor(Color.WHITE); + radialProgressPaint.setStrokeWidth(AndroidUtilities.dp(2)); + radialProgressPaint.setStyle(Paint.Style.STROKE); + radialProgressPaint.setStrokeCap(Paint.Cap.ROUND); + } + radialProgressPaint.setAlpha((int) (255 * progressToOpen * progressToUploading)); + radialProgress.setPaint(radialProgressPaint); + radialProgress.draw(canvas); + } + } + } + } + + public int getStoriesCount() { + return uploadingStories.size() + Math.max(totalStoriesCount, storyItems.size()); + } + + public interface Delegate { + void onPeerSelected(long dialogId, int position); + + void onCloseClick(); + + void onCloseLongClick(); + + void onEnterViewClick(); + + void shouldSwitchToNext(); + + void switchToNextAndRemoveCurrentPeer(); + + void setHideEnterViewProgress(float v); + + void showDialog(Dialog dialog); + + void updatePeers(); + + void requestAdjust(boolean b); + + void setKeyboardVisible(boolean keyboardVisible); + + void setAllowTouchesByViewPager(boolean b); + + void requestPlayer(TLRPC.Document document, Uri uri, long t, VideoPlayerSharedScope scope); + + boolean releasePlayer(Runnable whenReleased); + + boolean isClosed(); + + float getProgressToDismiss(); + + void setIsRecording(boolean recordingAudioVideo); + + void setIsWaiting(boolean b); + + int getKeyboardHeight(); + + void setIsCaption(boolean b); + + void setIsCaptionPartVisible(boolean b); + + void setPopupIsVisible(boolean b); + + void setBulletinIsVisible(boolean b); + + void setIsInPinchToZoom(boolean b); + + void preparePlayer(ArrayList documents, ArrayList uries); + + void setIsHintVisible(boolean visible); + + void setIsSwiping(boolean swiping); + + void setIsInSelectionMode(boolean selectionMode); + } + + public class StoryItemHolder { + public TLRPC.StoryItem storyItem = null; + public StoriesController.UploadingStory uploadingStory = null; + public TLRPC.StoryItem editingSourceItem; + boolean skipped; + private boolean isVideo; + + void set(TLRPC.StoryItem storyItem) { + this.storyItem = storyItem; + this.uploadingStory = null; + skipped = storyItem instanceof TLRPC.TL_storyItemSkipped; + isVideo = isVideoInternal(); + } + + private boolean isVideoInternal() { + if (uploadingStory != null) { + return uploadingStory.isVideo; + } + if (storyItem != null && storyItem.media != null && storyItem.media.document != null) { + return storyItem.media.document != null && MessageObject.isVideoDocument(storyItem.media.document); + } + if (storyItem != null && storyItem.media == null && storyItem.attachPath != null) { + return storyItem.attachPath.toLowerCase().endsWith(".mp4"); + } + return false; + } + + void set(StoriesController.UploadingStory uploadingStory) { + this.uploadingStory = uploadingStory; + this.storyItem = null; + skipped = false; + isVideo = isVideoInternal(); + } + + public void clear() { + this.uploadingStory = null; + this.storyItem = null; + } + + void cancelOrDelete() { + if (storyItem != null) { + storiesController.deleteStory(storyItem); + } else if (uploadingStory != null) { + uploadingStory.cancel(); + } + } + + public boolean isEmpty() { + return false; + } + + public void checkSendView() { + TLRPC.TL_userStories userStories = PeerStoriesView.this.userStories; + if (userStories == null) { + userStories = storiesController.getStories(dialogId); + if (userStories == null) { + TLRPC.UserFull userFull = MessagesController.getInstance(currentAccount).getUserFull(dialogId); + if (userFull != null) { + userStories = userFull.stories; + } + } + } + if (isActive && this.storyItem != null && userStories != null && ((!StoriesUtilities.hasExpiredViews(storyItem) && (this.storyItem.id > userStories.max_read_id || this.storyItem.id > storiesController.dialogIdToMaxReadId.get(dialogId, 0))) || isSelf)) { + if (storyViewer.overrideUserStories != null) { + if (storiesController.markStoryAsRead(storyViewer.overrideUserStories, storyItem, true)) { + storyViewer.unreadStateChanged = true; + } + } else { + if (storiesController.markStoryAsRead(dialogId, storyItem)) { + storyViewer.unreadStateChanged = true; + } + } + } + } + + public String getLocalPath() { + if (storyItem != null) { + return storyItem.attachPath; + } else if (uploadingStory != null) { + return null;//uploadingStory.path; + } + return null; + } + + boolean isVideo() { + return isVideo; + } + + boolean hasSound() { + if (!isVideo) { + return false; + } + if (storyItem != null && storyItem.media != null && storyItem.media.document != null) { + for (int i = 0; i < storyItem.media.document.attributes.size(); i++) { + TLRPC.DocumentAttribute attribute = storyItem.media.document.attributes.get(i); + if (attribute instanceof TLRPC.TL_documentAttributeVideo && attribute.nosound) { + return false; + } + } + return true; + } else if (uploadingStory != null) { + return !uploadingStory.entry.muted; + } + return true; + } + + public String createLink() { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); + return String.format(Locale.US, "https://t.me/%s/s/%s", UserObject.getPublicUsername(user), currentStory.storyItem.id); + // return String.format(Locale.US, "tg://resolve?domain=%s&story=%s", user.username, currentStory.storyItem.id); + } + + public File getPath() { + if (getLocalPath() != null) { + return new File(getLocalPath()); + } + if (storyItem != null) { + if (storyItem.media != null && storyItem.media.document != null) { + return FileLoader.getInstance(currentAccount).getPathToAttach(storyItem.media.document); + } else if (storyItem.media != null && storyItem.media.photo != null) { + TLRPC.PhotoSize size = FileLoader.getClosestPhotoSizeWithSize(storyItem.media.photo.sizes, Integer.MAX_VALUE); + File file = FileLoader.getInstance(currentAccount).getPathToAttach(size, true); + if (!file.exists()) { + file = FileLoader.getInstance(currentAccount).getPathToAttach(size, false); + } + return file; + } + } + return null; + } + + public boolean forCloseFriends() { + if (uploadingStory != null) { + if (uploadingStory.entry.privacy != null) { + return uploadingStory.entry.privacy.isCloseFriends(); + } else if (uploadingStory.entry.privacyRules != null) { + for (int i = 0; i < uploadingStory.entry.privacyRules.size(); ++i) { + if (uploadingStory.entry.privacyRules.get(i) instanceof TLRPC.TL_inputPrivacyValueAllowCloseFriends) { + return true; + } + } + return false; + } + } + return storyItem != null && storyItem.close_friends; + } + + public boolean allowScreenshots() { + if (uploadingStory != null) { + return uploadingStory.entry.allowScreenshots; + } + if (storyItem != null) { + return !storyItem.noforwards; + } + return true; + } + } + + public static int getStoryId(TLRPC.StoryItem storyItem, StoriesController.UploadingStory uploadingStory) { + if (storyItem != null) { + return storyItem.id; + } else if (uploadingStory != null && uploadingStory.entry != null) { + return uploadingStory.entry.editStoryId; + } else { + return 0; + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (storyViewer.ATTACH_TO_FRAGMENT) { + FrameLayout.LayoutParams layoutParams = (LayoutParams) getLayoutParams(); + layoutParams.topMargin = AndroidUtilities.statusBarHeight; + } + FrameLayout.LayoutParams layoutParams; + if (isActive && shareAlert == null) { + realKeyboardHeight = delegate.getKeyboardHeight(); + } else { + realKeyboardHeight = 0; + } + int heightWithKeyboard; + if (storyViewer.ATTACH_TO_FRAGMENT) { + heightWithKeyboard = MeasureSpec.getSize(heightMeasureSpec); + } else { + heightWithKeyboard = MeasureSpec.getSize(heightMeasureSpec) + realKeyboardHeight; + } + int width = MeasureSpec.getSize(widthMeasureSpec); + int viewPagerHeight; + if (heightWithKeyboard > (int) (16f * width / 9f)) { + viewPagerHeight = (int) (16f * width / 9f); + if (viewPagerHeight > heightWithKeyboard) { + viewPagerHeight = heightWithKeyboard; + } + } else { + viewPagerHeight = heightWithKeyboard; + } + if (realKeyboardHeight < AndroidUtilities.dp(20)) { + realKeyboardHeight = 0; + } + int keyboardHeight = realKeyboardHeight; + if (chatActivityEnterView != null && (chatActivityEnterView.isPopupShowing() || chatActivityEnterView.isWaitingForKeyboard())) { + if (chatActivityEnterView.getEmojiView().getMeasuredHeight() == 0) { + keyboardHeight = chatActivityEnterView.getEmojiPadding(); + } else if (chatActivityEnterView.isStickersExpanded()) { + chatActivityEnterView.checkStickresExpandHeight(); + keyboardHeight = chatActivityEnterView.getStickersExpandedHeight(); + } else { + keyboardHeight = chatActivityEnterView.getVisibleEmojiPadding(); + } + } + boolean keyboardVisibleOld = keyboardVisible; + if (lastKeyboardHeight != keyboardHeight) { + keyboardVisible = false; + if (keyboardHeight > 0 && isActive) { + keyboardVisible = true; + messageSent = false; + lastOpenedKeyboardHeight = keyboardHeight; + checkReactionsLayout(); + ReactionsEffectOverlay.dismissAll(); + } + if (keyboardVisible && mentionContainer != null) { + mentionContainer.setVisibility(View.VISIBLE); + } + if (!keyboardVisible && reactionsContainerLayout != null) { + reactionsContainerLayout.reset(); + } + headerView.setEnabled(!keyboardVisible); + optionsIconView.setEnabled(!keyboardVisible); + if (chatActivityEnterView != null) { + chatActivityEnterView.checkReactionsButton(!keyboardVisible); + } + if (isActive && keyboardVisible) { + delegate.setKeyboardVisible(true); + } + lastKeyboardHeight = keyboardHeight; + if (keyboardAnimator != null) { + keyboardAnimator.cancel(); + } + notificationsLocker.lock(); + keyboardAnimator = ValueAnimator.ofFloat(animatingKeyboardHeight, keyboardHeight); + keyboardAnimator.addUpdateListener(animation -> { + animatingKeyboardHeight = (float) animation.getAnimatedValue(); + invalidate(); + }); + keyboardAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + notificationsLocker.unlock(); + animatingKeyboardHeight = lastKeyboardHeight; + if (chatActivityEnterView != null) { + chatActivityEnterView.onOverrideAnimationEnd(); + } + if (isActive && !keyboardVisible) { + delegate.setKeyboardVisible(false); + } + if (!keyboardVisible && mentionContainer != null) { + mentionContainer.setVisibility(View.GONE); + } + forceUpdateOffsets = true; + invalidate(); + } + }); + if (keyboardVisible) { + keyboardAnimator.setDuration(AdjustPanLayoutHelper.keyboardDuration); + keyboardAnimator.setInterpolator(AdjustPanLayoutHelper.keyboardInterpolator); + storyViewer.cancelSwipeToReply(); + } else { + keyboardAnimator.setDuration(500); + keyboardAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + } + + keyboardAnimator.start(); + + if (keyboardVisible != keyboardVisibleOld) { + if (keyboardVisible) { + createBlurredBitmap(bitmapShaderTools.getCanvas(), bitmapShaderTools.getBitmap()); + } else { + if (chatActivityEnterView != null) { + chatActivityEnterView.getEditField().clearFocus(); + } + } + animateKeyboardOpening = true; + } else { + animateKeyboardOpening = false; + } + } + if (chatActivityEnterView != null && chatActivityEnterView.getEmojiView() != null) { + layoutParams = (LayoutParams) chatActivityEnterView.getEmojiView().getLayoutParams(); + layoutParams.gravity = Gravity.BOTTOM; + } + layoutParams = (LayoutParams) storyContainer.getLayoutParams(); + layoutParams.height = viewPagerHeight; + BIG_SCREEN = heightWithKeyboard - viewPagerHeight > AndroidUtilities.dp(64); + int top = layoutParams.topMargin = (heightWithKeyboard - (viewPagerHeight + (BIG_SCREEN ? AndroidUtilities.dp(64) : 0))) >> 1; + if (BIG_SCREEN) { + enterViewBottomOffset = -top + heightWithKeyboard - viewPagerHeight - AndroidUtilities.dp(64); + } else { + enterViewBottomOffset = -top + heightWithKeyboard - viewPagerHeight; + } + if (selfView != null) { + layoutParams = (LayoutParams) selfView.getLayoutParams(); + if (BIG_SCREEN) { + layoutParams.topMargin = top + viewPagerHeight + AndroidUtilities.dp(8); + } else { + layoutParams.topMargin = top + viewPagerHeight - AndroidUtilities.dp(48); + } + } + + if (replyDisabledTextView != null) { + layoutParams = (LayoutParams) replyDisabledTextView.getLayoutParams(); + if (BIG_SCREEN) { + layoutParams.topMargin = top + viewPagerHeight + AndroidUtilities.dp(8) + AndroidUtilities.dp(24); + } else { + layoutParams.topMargin = top + viewPagerHeight - AndroidUtilities.dp(48) + AndroidUtilities.dp(24); + } + } + if (instantCameraView != null) { + layoutParams = (LayoutParams) instantCameraView.getLayoutParams(); + if (keyboardHeight == 0) { + layoutParams.bottomMargin = heightWithKeyboard - (top + viewPagerHeight - AndroidUtilities.dp(64)); + } else { + layoutParams.bottomMargin = keyboardHeight + AndroidUtilities.dp(64); + } + } + + if (!BIG_SCREEN) { + ((LayoutParams) shareButton.getLayoutParams()).topMargin = top + viewPagerHeight - AndroidUtilities.dp(12) - AndroidUtilities.dp(40); + int bottomPadding = isSelf ? AndroidUtilities.dp(40) : AndroidUtilities.dp(64); + ((LayoutParams) storyCaptionView.getLayoutParams()).bottomMargin = bottomPadding; + storyCaptionView.blackoutBottomOffset = bottomPadding; + } else { + ((LayoutParams) shareButton.getLayoutParams()).topMargin = top + viewPagerHeight + AndroidUtilities.dp(12); + ((LayoutParams) storyCaptionView.getLayoutParams()).bottomMargin = AndroidUtilities.dp(8); + storyCaptionView.blackoutBottomOffset = AndroidUtilities.dp(8); + } + + forceUpdateOffsets = true; + + float headerRightMargin = AndroidUtilities.dp(8) + AndroidUtilities.dp(40); +// if (closeFriendsBadge.getVisibility() == View.VISIBLE) { +// headerRightMargin += AndroidUtilities.dp(40); +// } + if (privacyButton.getVisibility() == View.VISIBLE) { + headerRightMargin += AndroidUtilities.dp(60); + } + if (muteIconContainer.getVisibility() == View.VISIBLE) { + headerRightMargin += AndroidUtilities.dp(40); + } + layoutParams = (LayoutParams) headerView.titleView.getLayoutParams(); + if (layoutParams.rightMargin != headerRightMargin) { + layoutParams.rightMargin = (int) headerRightMargin; + layoutParams = (LayoutParams) headerView.subtitleView[0].getLayoutParams(); + layoutParams.rightMargin = (int) headerRightMargin; + layoutParams = (LayoutParams) headerView.subtitleView[1].getLayoutParams(); + layoutParams.rightMargin = (int) headerRightMargin; + headerView.forceLayout(); + } + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(heightWithKeyboard, MeasureSpec.EXACTLY)); + } + + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + progressToKeyboard = -1; + forceUpdateOffsets = true; + invalidate(); + } + + AnimatedFloat progressToRecording = new AnimatedFloat(this); + AnimatedFloat progressToTextA = new AnimatedFloat(this); + AnimatedFloat progressToStickerExpanded = new AnimatedFloat(this); + float progressToReply; + + private void updateViewOffsets() { + float progressToDismissLocal = delegate.getProgressToDismiss(); + float progressToKeyboardLocal; + progressToHideInterface.set(isLongPressed ? 1f : 0); + + if (lastOpenedKeyboardHeight != 0 && animateKeyboardOpening) { + progressToKeyboardLocal = MathUtils.clamp(animatingKeyboardHeight / lastOpenedKeyboardHeight, 0, 1f); + } else { + progressToKeyboardLocal = keyboardVisible ? 1f : 0; + } + float progressToRecord = progressToRecording.get(); + float progressToText = progressToTextA.get(); + float progressToStickerExpandedLocal = progressToStickerExpanded.get(); + progressToRecording.set(isRecording ? 1f : 0); + if (!messageSent) { + progressToTextA.set(chatActivityEnterView != null && !TextUtils.isEmpty(chatActivityEnterView.getFieldText()) ? 1f : 0); + } + progressToStickerExpanded.set(chatActivityEnterView != null && chatActivityEnterView.isStickersExpanded() ? 1f : 0); + if (chatActivityEnterView != null) { + chatActivityEnterView.checkAnimation(); + } + boolean popupVisible = chatActivityEnterView != null && chatActivityEnterView.isPopupShowing(); + if (forceUpdateOffsets || progressToReply != storyViewer.swipeToReplyProgress || progressToHideInterface.get() != prevToHideProgress || lastAnimatingKeyboardHeight != animatingKeyboardHeight || progressToKeyboardLocal != progressToKeyboard || progressToDismissLocal != progressToDismiss || progressToRecord != progressToRecording.get() || popupVisible || progressToStickerExpandedLocal != progressToStickerExpanded.get() || progressToText != progressToTextA.get()) { + forceUpdateOffsets = false; + lastAnimatingKeyboardHeight = animatingKeyboardHeight; + if (progressToHideInterface.get() != prevToHideProgress) { + storyContainer.invalidate(); + } + if (progressToDismissLocal != 0) { + //fix jittering caption shadow + storyContainer.setLayerType(LAYER_TYPE_HARDWARE, null); + } else { + storyContainer.setLayerType(LAYER_TYPE_NONE, null); + } + prevToHideProgress = progressToHideInterface.get(); + progressToDismiss = progressToDismissLocal; + progressToKeyboard = progressToKeyboardLocal; + progressToReply = storyViewer.swipeToReplyProgress; + } else { + return; + } + + + if (reactionsContainerLayout != null) { + reactionsContainerLayout.setVisibility(progressToKeyboard > 0 ? View.VISIBLE : View.GONE); + } + float hideInterfaceAlpha = getHideInterfaceAlpha(); + if (BIG_SCREEN) { + inputBackgroundPaint.setColor(ColorUtils.blendARGB( + ColorUtils.blendARGB(Color.BLACK, Color.WHITE, 0.13f), + ColorUtils.setAlphaComponent(Color.BLACK, 170), + progressToKeyboard + )); + inputBackgroundPaint.setAlpha((int) (inputBackgroundPaint.getAlpha() * (1f - progressToDismiss) * hideInterfaceAlpha)); + } else { + inputBackgroundPaint.setColor(ColorUtils.setAlphaComponent(Color.BLACK, (int) (140 * hideInterfaceAlpha))); + } + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + + if (child.getVisibility() != View.VISIBLE || child == selfView || child.getTag(R.id.parent_tag) != null || child == storyCaptionView.textSelectionHelper.getOverlayView(getContext())) { + if (child == selfView) { + if (BIG_SCREEN) { + child.setAlpha((1f - progressToDismiss) * hideInterfaceAlpha * (1f - outT)); + } else { + child.setAlpha(hideInterfaceAlpha * (1f - outT)); + } + } + continue; + } + if (chatActivityEnterView != null && child == chatActivityEnterView.getEmojiView()) { + child.setTranslationY(chatActivityEnterView.getEmojiView().getMeasuredHeight() - animatingKeyboardHeight); + } else if (child instanceof HintView) { + HintView hintView = (HintView) child; + hintView.updatePosition(); + } else if (child != instantCameraView && child != storyContainer && child != shareButton && child != mediaBanTooltip) { + float alpha; + float translationY = -enterViewBottomOffset * (1f - progressToKeyboard) - animatingKeyboardHeight - AndroidUtilities.dp(8) * (1f - progressToKeyboard) - AndroidUtilities.dp(20) * storyViewer.swipeToReplyProgress; + if (BIG_SCREEN) { + alpha = (1f - progressToDismiss) * hideInterfaceAlpha; + } else { + alpha = 1f * hideInterfaceAlpha; + } + if (child == mentionContainer) { + translationY -= chatActivityEnterView.getMeasuredHeight() - chatActivityEnterView.getAnimatedTop(); + alpha = progressToKeyboard; + child.invalidate(); + } + if (child == reactionsContainerLayout) { + float finalProgress = progressToKeyboard * (1f - progressToRecording.get()) * (1f - progressToStickerExpandedLocal) * (1f - progressToTextA.get()); + child.setAlpha(finalProgress * alpha * 1f); + float s = 0.8f + 0.2f * finalProgress; + child.setScaleX(s); + child.setScaleY(s); + } else { + child.setTranslationY(translationY); + child.setAlpha(alpha); + } + } + } + shareButton.setAlpha(hideInterfaceAlpha * (1f - progressToDismissLocal)); + + for (int i = 0; i < storyContainer.getChildCount(); i++) { + View child = storyContainer.getChildAt(i); + if (child == null) { + continue; + } + if (child == headerView || child == optionsIconView || child == muteIconContainer || child == selfView || child == storyCaptionView || child == privacyButton) { + float alpha = 1f; + if (child == muteIconContainer) { + alpha = muteIconViewAlpha; + } + if (child == storyCaptionView) { + boolean hideCaptionWithInterface = hideCaptionWithInterface(); + child.setAlpha(alpha * (hideCaptionWithInterface ? hideInterfaceAlpha : 1f) * (1f - outT)); + } else { + child.setAlpha(alpha * hideInterfaceAlpha * (1f - outT)); + } + } else { + child.setAlpha(hideInterfaceAlpha); + } + } + if (chatActivityEnterView != null) { + chatActivityEnterView.setHorizontalPadding(AndroidUtilities.dp(10), progressToKeyboard, allowShare); + if (chatActivityEnterView.getEmojiView() != null) { + chatActivityEnterView.getEmojiView().setAlpha(progressToKeyboard); + } + } + } + + private boolean hideCaptionWithInterface() { + return true;//storyCaptionView.getProgressToBlackout() == 0;; + } + + private float getHideInterfaceAlpha() { + return (1f - progressToHideInterface.get()) * (1f - storyViewer.getProgressToSelfViews()); + } + + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + if (child == mentionContainer) { + canvas.save(); + float bottom = mentionContainer.getY() + mentionContainer.getMeasuredHeight(); + canvas.clipRect(0, mentionContainer.getY(), getMeasuredWidth(), bottom); + boolean rez = super.drawChild(canvas, child, drawingTime); + canvas.restore(); + return rez; + } else if (child == chatActivityEnterView) { + sharedResources.rect1.set(0, + chatActivityEnterView.getY() + chatActivityEnterView.getAnimatedTop(), + getMeasuredWidth() + AndroidUtilities.dp(20), + getMeasuredHeight() + ); + sharedResources.rect2.set(AndroidUtilities.dp(10), + (chatActivityEnterView.getBottom() - AndroidUtilities.dp(48) + chatActivityEnterView.getTranslationY() + AndroidUtilities.dp(2)), + getMeasuredWidth() - AndroidUtilities.dp(10) - (allowShare ? AndroidUtilities.dp(SHARE_BUTTON_OFFSET) : 0), + (chatActivityEnterView.getY() + chatActivityEnterView.getMeasuredHeight() - AndroidUtilities.dp(2)) + ); + if (chatActivityEnterView.getMeasuredHeight() > AndroidUtilities.dp(50)) { + chatActivityEnterView.getEditField().setTranslationY((1f - progressToKeyboard) * (chatActivityEnterView.getMeasuredHeight() - AndroidUtilities.dp(50))); + } else { + chatActivityEnterView.getEditField().setTranslationY(0); + } + float radius = AndroidUtilities.dp(50) / 2f * (1f - progressToKeyboard); + bitmapShaderTools.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight()); + AndroidUtilities.lerp(sharedResources.rect2, sharedResources.rect1, progressToKeyboard, sharedResources.finalRect); + if (progressToKeyboard > 0) { + bitmapShaderTools.paint.setAlpha((int) (255 * progressToKeyboard)); + canvas.drawRoundRect(sharedResources.finalRect, radius, radius, bitmapShaderTools.paint); + } + canvas.drawRoundRect(sharedResources.finalRect, radius, radius, inputBackgroundPaint); + if (progressToKeyboard < 0.5f) { + canvas.save(); + canvas.clipRect(sharedResources.finalRect); + boolean rez = super.drawChild(canvas, child, drawingTime); + canvas.restore(); + return rez; + } + } else if (chatActivityEnterView != null && chatActivityEnterView.isPopupView(child)) { + canvas.save(); + canvas.clipRect(sharedResources.finalRect); + boolean res = super.drawChild(canvas, child, drawingTime); + canvas.restore(); + return res; + } else if (child == reactionsContainerLayout && chatActivityEnterView != null) { + child.setTranslationY(-reactionsContainerLayout.getMeasuredHeight() + (chatActivityEnterView.getY() + chatActivityEnterView.getAnimatedTop()) - AndroidUtilities.dp(18)); + if (progressToKeyboard > 0) { + sharedResources.dimPaint.setAlpha((int) (125 * progressToKeyboard)); + canvas.drawRect(0, 0, getMeasuredWidth(), chatActivityEnterView.getY() + chatActivityEnterView.getAnimatedTop(), sharedResources.dimPaint); + } + } + return super.drawChild(canvas, child, drawingTime); + } + + private void checkInstantCameraView() { + if (instantCameraView != null) { + return; + } + + instantCameraView = new InstantCameraView(getContext(), new InstantCameraView.Delegate() { + @Override + public View getFragmentView() { + return PeerStoriesView.this; + } + + @Override + public void sendMedia(MediaController.PhotoEntry photoEntry, VideoEditedInfo videoEditedInfo, boolean notify, int scheduleDate, boolean forceDocument) { + if (photoEntry == null) { + return; + } + TLRPC.StoryItem storyItem = currentStory.storyItem; + if (storyItem == null || storyItem instanceof TLRPC.TL_storyItemSkipped) { + return; + } + storyItem.dialogId = dialogId; + if (photoEntry.isVideo) { + if (videoEditedInfo != null) { + SendMessagesHelper.prepareSendingVideo(getAccountInstance(), photoEntry.path, videoEditedInfo, dialogId, null, null, storyItem, photoEntry.entities, photoEntry.ttl, null, notify, scheduleDate, forceDocument, photoEntry.hasSpoiler, photoEntry.caption); + } else { + SendMessagesHelper.prepareSendingVideo(getAccountInstance(), photoEntry.path, null, dialogId, null, null, storyItem, photoEntry.entities, photoEntry.ttl, null, notify, scheduleDate, forceDocument, photoEntry.hasSpoiler, photoEntry.caption); + } + } else { + if (photoEntry.imagePath != null) { + SendMessagesHelper.prepareSendingPhoto(getAccountInstance(), photoEntry.imagePath, photoEntry.thumbPath, null, dialogId, null, null, storyItem, photoEntry.entities, photoEntry.stickers, null, photoEntry.ttl, null, videoEditedInfo, notify, scheduleDate, forceDocument, photoEntry.caption); + } else if (photoEntry.path != null) { + SendMessagesHelper.prepareSendingPhoto(getAccountInstance(), photoEntry.path, photoEntry.thumbPath, null, dialogId, null, null, storyItem, photoEntry.entities, photoEntry.stickers, null, photoEntry.ttl, null, videoEditedInfo, notify, scheduleDate, forceDocument, photoEntry.caption); + } + } + afterMessageSend(); + } + + @Override + public Activity getParentActivity() { + return AndroidUtilities.findActivity(getContext()); + } + + @Override + public int getClassGuid() { + return classGuid; + } + + @Override + public long getDialogId() { + return dialogId; + } + + }, resourcesProvider); + instantCameraView.drawBlur = false; + int i = indexOfChild(chatActivityEnterView.getRecordCircle()); + addView(instantCameraView, i, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP)); + } + + private void afterMessageSend() { + if (instantCameraView != null) { + instantCameraView.resetCameraFile(); + instantCameraView.cancel(false); + } + messageSent = true; + storyViewer.closeKeyboardOrEmoji(); + BulletinFactory bulletinFactory = BulletinFactory.of(storyContainer, resourcesProvider); + if (bulletinFactory != null) { + bulletinFactory.createSimpleBulletin(R.raw.forward, LocaleController.getString("MessageSent", R.string.MessageSent), LocaleController.getString("ViewInChat", R.string.ViewInChat), Bulletin.DURATION_PROLONG, this::openChat).hideAfterBottomSheet(false).show(false); + } + MessagesController.getInstance(currentAccount).ensureMessagesLoaded(dialogId, 0, null); + } + + private void openChat() { + Bundle bundle = new Bundle(); + bundle.putLong("user_id", dialogId); + TLRPC.Dialog dialog = MessagesController.getInstance(currentAccount).getDialog(dialogId); + if (dialog != null) { + bundle.putInt("message_id", dialog.top_message); + } + ChatActivity chatActivity = new ChatActivity(bundle); + storyViewer.presentFragment(chatActivity); + } + + private AccountInstance getAccountInstance() { + return AccountInstance.getInstance(currentAccount); + } + + public static class VideoPlayerSharedScope { + StoryViewer.VideoPlayerHolder player; + SurfaceView surfaceView; + View renderView; + TextureView textureView; + boolean firstFrameRendered; + ArrayList viewsToInvalidate = new ArrayList<>(); + + public void invalidate() { + for (int i = 0; i < viewsToInvalidate.size(); i++) { + viewsToInvalidate.get(i).invalidate(); + } + } + + public boolean isBuffering() { + return player != null && player.isBuffering(); + } + } + + void checkReactionsLayout() { + if (reactionsContainerLayout == null) { + + reactionsContainerLayout = new ReactionsContainerLayout(ReactionsContainerLayout.TYPE_STORY, LaunchActivity.getLastFragment(), getContext(), currentAccount, new WrappedResourceProvider(resourcesProvider) { + @Override + public void appendColors() { + sparseIntArray.put(Theme.key_chat_emojiPanelBackground, ColorUtils.setAlphaComponent(Color.WHITE, 30)); + } + }); + reactionsContainerLayout.skipEnterAnimation = true; + addView(reactionsContainerLayout, reactionsContainerIndex, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 52, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, 0, 0, 64)); + reactionsContainerLayout.setDelegate(new ReactionsContainerLayout.ReactionsContainerDelegate() { + @Override + public void onReactionClicked(View view, ReactionsLayoutInBubble.VisibleReaction visibleReaction, boolean longpress, boolean addToRecent) { + ReactionsEffectOverlay effectOverlay; + if (longpress && visibleReaction.emojicon != null) { + performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + effectOverlay = new ReactionsEffectOverlay( + view.getContext(), null, + reactionsContainerLayout, null, + view, getMeasuredWidth() / 2f, getMeasuredHeight() / 2f, + visibleReaction, currentAccount, + ReactionsEffectOverlay.LONG_ANIMATION, true); + } else { + effectOverlay = new ReactionsEffectOverlay( + view.getContext(), null, + reactionsContainerLayout, null, + view, getMeasuredWidth() / 2f, getMeasuredHeight() / 2f, + visibleReaction, currentAccount, + ReactionsEffectOverlay.ONLY_MOVE_ANIMATION, true); + } + ReactionsEffectOverlay.currentOverlay = effectOverlay; + effectOverlay.windowView.setTag(R.id.parent_tag, 1); + addView(effectOverlay.windowView); + effectOverlay.started = true; + effectOverlay.startTime = System.currentTimeMillis(); + TLRPC.Document document; + if (visibleReaction.emojicon != null) { + document = MediaDataController.getInstance(currentAccount).getEmojiAnimatedSticker(visibleReaction.emojicon); + SendMessagesHelper.SendMessageParams params = SendMessagesHelper.SendMessageParams.of(visibleReaction.emojicon, dialogId); + params.replyToStoryItem = currentStory.storyItem; + SendMessagesHelper.getInstance(currentAccount).sendMessage(params); + } else { + document = AnimatedEmojiDrawable.findDocument(currentAccount, visibleReaction.documentId); + String emoticon = MessageObject.findAnimatedEmojiEmoticon(document, null); + SendMessagesHelper.SendMessageParams params = SendMessagesHelper.SendMessageParams.of(emoticon, dialogId); + params.entities = new ArrayList<>(); + TLRPC.TL_messageEntityCustomEmoji customEmojiEntitiy = new TLRPC.TL_messageEntityCustomEmoji(); + customEmojiEntitiy.document_id = visibleReaction.documentId; + customEmojiEntitiy.offset = 0; + customEmojiEntitiy.length = emoticon.length(); + params.entities.add(customEmojiEntitiy); + params.replyToStoryItem = currentStory.storyItem; + SendMessagesHelper.getInstance(currentAccount).sendMessage(params); + } + + BulletinFactory.of(storyContainer, resourcesProvider).createEmojiBulletin(document, + LocaleController.getString("ReactionSent", R.string.ReactionSent), + LocaleController.getString("ViewInChat", R.string.ViewInChat), () -> { + openChat(); + }).setDuration(Bulletin.DURATION_PROLONG).show(); + if (reactionsContainerLayout.getReactionsWindow() != null) { + reactionsContainerLayout.getReactionsWindow().dismissWithAlpha(); + } + closeKeyboardOrEmoji(); + } + + @Override + public void hideMenu() { + + } + + @Override + public void drawRoundRect(Canvas canvas, RectF rect, float radius, float offsetX, float offsetY) { + bitmapShaderTools.setBounds(-offsetX, -offsetY, + -offsetX + getMeasuredWidth(), + -offsetY + getMeasuredHeight()); + if (radius > 0) { + canvas.drawRoundRect(rect, radius, radius, bitmapShaderTools.paint); + canvas.drawRoundRect(rect, radius, radius, inputBackgroundPaint); + } else { + canvas.drawRect(rect, bitmapShaderTools.paint); + canvas.drawRect(rect, inputBackgroundPaint); + } + } + + @Override + public boolean needEnterText() { + return PeerStoriesView.this.needEnterText(); + } + + @Override + public void onEmojiWindowDismissed() { + delegate.requestAdjust(false); + } + }); + reactionsContainerLayout.setMessage(null, null); + } + reactionsContainerLayout.setFragment(LaunchActivity.getLastFragment()); + } + + public boolean needEnterText() { + boolean keyboardVisible = chatActivityEnterView.isKeyboardVisible(); + if (keyboardVisible) { + chatActivityEnterView.showEmojiView(); + } + AndroidUtilities.runOnUIThread(() -> { + delegate.requestAdjust(true); + }, 300); + + return keyboardVisible; + } + + public void setViewsThumbImageReceiver(float alpha, ImageReceiver viewsThumbImageReceiver) { + this.viewsThumbImageReceiver = viewsThumbImageReceiver; + this.viewsThumbAlpha = alpha; + } + + public static class SharedResources { + + public final Paint barPaint; + public final Paint selectedBarPaint; + private final Paint gradientBackgroundPaint; + private final Drawable topOverlayGradient; + private final Drawable bottomOverlayGradient; + public final Drawable imageBackgroundDrawable; + public final BitmapShaderTools bitmapShaderTools = new BitmapShaderTools(); + + private final RectF rect1 = new RectF(); + private final RectF rect2 = new RectF(); + private final RectF finalRect = new RectF(); + private final Paint dimPaint = new Paint(); + public Drawable shareDrawable; + public Drawable drawMoreDrawable; + public Drawable optionsDrawable; + public Drawable deleteDrawable; + public RLottieDrawable noSoundDrawable; + // public ReplaceableIconDrawable muteDrawable; + public RLottieDrawable muteDrawable; + + SharedResources(Context context) { + // drawMoreDrawable = ContextCompat.getDrawable(context, R.drawable.msg_draw_more); + shareDrawable = ContextCompat.getDrawable(context, R.drawable.media_share); + optionsDrawable = ContextCompat.getDrawable(context, R.drawable.media_more); + deleteDrawable = ContextCompat.getDrawable(context, R.drawable.msg_delete); + muteDrawable = new RLottieDrawable(R.raw.media_mute_unmute, "media_mute_unmute", AndroidUtilities.dp(28), AndroidUtilities.dp(28), true, null); + // muteDrawable = new ReplaceableIconDrawable(context); + noSoundDrawable = new RLottieDrawable(R.raw.media_mute_unmute, "media_mute_unmute", AndroidUtilities.dp(28), AndroidUtilities.dp(28), true, null); + noSoundDrawable.setCurrentFrame(20, false, true); + noSoundDrawable.stop(); + // muteDrawable = new CrossOutDrawable(context, R.drawable.msg_unmute, -1); + //muteDrawable.setOffsets(-AndroidUtilities.dp(1), AndroidUtilities.dp(0), -AndroidUtilities.dp(1)); + barPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + barPaint.setColor(0x55ffffff); + selectedBarPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + selectedBarPaint.setColor(0xffffffff); + + + + int gradientColor = ColorUtils.setAlphaComponent(Color.BLACK, (int) (255 * 0.4f)); + topOverlayGradient = ContextCompat.getDrawable(context, R.drawable.shadow_story_top); + bottomOverlayGradient = ContextCompat.getDrawable(context, R.drawable.shadow_story_bottom);//new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, new int[]{0, gradientColor}); + // bottomOverlayGradient.setShape(GradientDrawable.RECTANGLE); + + gradientBackgroundPaint = new Paint(); + gradientBackgroundPaint.setColor(gradientColor); + + imageBackgroundDrawable = new ColorDrawable(ColorUtils.blendARGB(Color.BLACK, Color.WHITE, 0.1f)); + } + + public void setIconMuted(boolean muted, boolean animated) { + if (!animated) { + muteDrawable.setCurrentFrame(muted ? 20 : 0, false); + muteDrawable.setCustomEndFrame(muted ? 20 : 0); + return; + } + if (muted) { + if (muteDrawable.getCurrentFrame() > 20) { + muteDrawable.setCurrentFrame(0, false); + } + muteDrawable.setCustomEndFrame(20); + muteDrawable.start(); + } else { + if (muteDrawable.getCurrentFrame() == 0 || muteDrawable.getCurrentFrame() >= 43) { + return; + } + muteDrawable.setCustomEndFrame(43); + muteDrawable.start(); + } + } + } + + private boolean editedPrivacy; + private void editPrivacy(StoryPrivacyBottomSheet.StoryPrivacy currentPrivacy, TLRPC.StoryItem storyItem) { + delegate.showDialog(new StoryPrivacyBottomSheet(getContext(), storyItem.pinned ? Integer.MAX_VALUE : storyItem.expire_date - storyItem.date, resourcesProvider) + .setValue(currentPrivacy) + .enableSharing(false) + .allowSmallChats(false) + .isEdit(true) + .whenSelectedRules((privacy, a, b, whenDone) -> { + TLRPC.TL_stories_editStory editStory = new TLRPC.TL_stories_editStory(); + editStory.id = storyItem.id; + editStory.flags |= 4; + editStory.privacy_rules = privacy.rules; + ConnectionsManager.getInstance(currentAccount).sendRequest(editStory, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + if (whenDone != null) { + whenDone.run(); + } + if (error == null || "STORY_NOT_MODIFIED".equals(error.text)) { + + storyItem.privacy = privacy.toValue(); + storyItem.close_friends = privacy.type == StoryPrivacyBottomSheet.TYPE_CLOSE_FRIENDS; + storyItem.contacts = privacy.type == StoryPrivacyBottomSheet.TYPE_CONTACTS; + storyItem.selected_contacts = privacy.type == StoryPrivacyBottomSheet.TYPE_SELECTED_CONTACTS; + MessagesController.getInstance(currentAccount).getStoriesController().updateStoryItem(storyItem.dialogId, storyItem); + editedPrivacy = true; + + if (privacy.type == StoryPrivacyBottomSheet.TYPE_EVERYONE) { + BulletinFactory.of(storyContainer, resourcesProvider).createSimpleBulletin(R.raw.contact_check, LocaleController.getString("StorySharedToEveryone")).show(); + } else if (privacy.type == StoryPrivacyBottomSheet.TYPE_CLOSE_FRIENDS) { + BulletinFactory.of(storyContainer, resourcesProvider).createSimpleBulletin(R.raw.contact_check, LocaleController.getString("StorySharedToCloseFriends")).show(); + } else if (privacy.type == StoryPrivacyBottomSheet.TYPE_CONTACTS) { + if (privacy.selectedUserIds.isEmpty()) { + BulletinFactory.of(storyContainer, resourcesProvider).createSimpleBulletin(R.raw.contact_check, LocaleController.getString("StorySharedToAllContacts")).show(); + } else { + BulletinFactory.of(storyContainer, resourcesProvider).createSimpleBulletin(R.raw.contact_check, LocaleController.formatPluralString("StorySharedToAllContactsExcluded", privacy.selectedUserIds.size())).show(); + } + } else if (privacy.type == StoryPrivacyBottomSheet.TYPE_SELECTED_CONTACTS) { + BulletinFactory.of(storyContainer, resourcesProvider).createSimpleBulletin(R.raw.contact_check, LocaleController.formatPluralString("StorySharedToContacts", privacy.selectedUserIds.size())).show(); + } + } else { + BulletinFactory.of(storyContainer, resourcesProvider).createSimpleBulletin(R.raw.error, LocaleController.getString("UnknownError", R.string.UnknownError)).show(); + } + + updatePosition(); + })); + }, false)); + } + + public boolean checkRecordLocked(boolean forceCloseOnDiscard) { + if (chatActivityEnterView != null && chatActivityEnterView.isRecordLocked()) { + AlertDialog.Builder builder = new AlertDialog.Builder(getContext(), resourcesProvider); + if (chatActivityEnterView.isInVideoMode()) { + builder.setTitle(LocaleController.getString("DiscardVideoMessageTitle", R.string.DiscardVideoMessageTitle)); + builder.setMessage(LocaleController.getString("DiscardVideoMessageDescription", R.string.DiscardVideoMessageDescription)); + } else { + builder.setTitle(LocaleController.getString("DiscardVoiceMessageTitle", R.string.DiscardVoiceMessageTitle)); + builder.setMessage(LocaleController.getString("DiscardVoiceMessageDescription", R.string.DiscardVoiceMessageDescription)); + } + builder.setPositiveButton(LocaleController.getString("DiscardVoiceMessageAction", R.string.DiscardVoiceMessageAction), (dialog, which) -> { + if (chatActivityEnterView != null) { + if (forceCloseOnDiscard) { + storyViewer.close(true); + } else { + chatActivityEnterView.cancelRecordingAudioVideo(); + } + } + }); + builder.setNegativeButton(LocaleController.getString("Continue", R.string.Continue), null); + delegate.showDialog(builder.create()); + return true; + } + return false; + } + + private ValueAnimator outAnimator; + private float outT; + public void animateOut(boolean out) { + if (outAnimator != null) { + outAnimator.cancel(); + } + outAnimator = ValueAnimator.ofFloat(outT, out ? 1 : 0); + outAnimator.addUpdateListener(anm -> { + outT = (float) anm.getAnimatedValue(); + headerView.setTranslationY(-AndroidUtilities.dp(8) * outT); + headerView.setAlpha(1f - outT); + optionsIconView.setTranslationY(-AndroidUtilities.dp(8) * outT); + optionsIconView.setAlpha(1f - outT); + muteIconContainer.setTranslationY(-AndroidUtilities.dp(8) * outT); + muteIconContainer.setAlpha(muteIconViewAlpha * (1f - outT)); + if (selfView != null) { + selfView.setTranslationY(AndroidUtilities.dp(8) * outT); + selfView.setAlpha(1f - outT); + } + if (privacyButton != null) { + privacyButton.setTranslationY(-AndroidUtilities.dp(8) * outT); + privacyButton.setAlpha(1f - outT); + } + storyCaptionView.setAlpha(1f - outT); + storyContainer.invalidate(); + }); + outAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + outT = out ? 1 : 0; + headerView.setTranslationY(-AndroidUtilities.dp(8) * outT); + headerView.setAlpha(1f - outT); + optionsIconView.setTranslationY(-AndroidUtilities.dp(8) * outT); + optionsIconView.setAlpha(1f - outT); + muteIconContainer.setTranslationY(-AndroidUtilities.dp(8) * outT); + muteIconContainer.setAlpha(muteIconViewAlpha * (1f - outT)); + if (selfView != null) { + selfView.setTranslationY(AndroidUtilities.dp(8) * outT); + selfView.setAlpha(1f - outT); + } + if (privacyButton != null) { + privacyButton.setTranslationY(-AndroidUtilities.dp(8) * outT); + privacyButton.setAlpha(1f - outT); + } + storyCaptionView.setAlpha(1f - outT); + storyContainer.invalidate(); + } + }); + outAnimator.setDuration(420); + outAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + outAnimator.start(); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/ProfileStoriesView.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/ProfileStoriesView.java new file mode 100644 index 0000000000..d9a266178d --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/ProfileStoriesView.java @@ -0,0 +1,909 @@ +package org.telegram.ui.Stories; + +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.AndroidUtilities.dpf2; +import static org.telegram.messenger.AndroidUtilities.lerp; +import static org.telegram.messenger.AndroidUtilities.rectTmp; +import static org.telegram.messenger.Utilities.clamp; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.RectF; +import android.graphics.Region; +import android.graphics.drawable.Drawable; +import android.util.Log; +import android.view.HapticFeedbackConstants; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.animation.OvershootInterpolator; + +import androidx.annotation.NonNull; +import androidx.core.graphics.ColorUtils; + +import com.google.zxing.common.detector.MathUtils; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ImageReceiver; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.SharedConfig; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.Utilities; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.AnimatedTextView; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.ProfileActivity; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class ProfileStoriesView extends View implements NotificationCenter.NotificationCenterDelegate { + + private static final int CIRCLES_MAX = 3; + + private final StoriesGradientTools storiesGradientTools; + private int readPaintAlpha; + private final Paint readPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint whitePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + private final int currentAccount; + private final long userId; + private final View avatarContainer; + private final ProfileActivity.AvatarImageView avatarImage; + + private final AnimatedTextView.AnimatedTextDrawable titleDrawable = new AnimatedTextView.AnimatedTextDrawable(false, true, true); + + private final Paint clipOutAvatar = new Paint(Paint.ANTI_ALIAS_FLAG); + + private int unreadCount; + private int count; + private StoryCircle mainCircle; + private final ArrayList circles = new ArrayList<>(); + + private boolean attached; + + private class StoryCircle { + public StoryCircle(TLRPC.StoryItem storyItem) { + this.storyId = storyItem.id; + this.imageReceiver.setRoundRadius(dp(200)); + this.imageReceiver.setParentView(ProfileStoriesView.this); + if (attached) { + this.imageReceiver.onAttachedToWindow(); + } + StoriesUtilities.setThumbImage(this.imageReceiver, storyItem, 25, 25); + } + + int storyId; + ImageReceiver imageReceiver = new ImageReceiver(); + int index = 0; + boolean read = false; + float scale = 1; + final AnimatedFloat readAnimated = new AnimatedFloat(ProfileStoriesView.this, 420, CubicBezierInterpolator.EASE_OUT_QUINT); + final AnimatedFloat indexAnimated = new AnimatedFloat(ProfileStoriesView.this, 420, CubicBezierInterpolator.EASE_OUT_QUINT); + final AnimatedFloat scaleAnimated = new AnimatedFloat(ProfileStoriesView.this, 420, CubicBezierInterpolator.EASE_OUT_QUINT); + + float cachedIndex; + float cachedScale; + float cachedRead; + final RectF cachedRect = new RectF(); + final RectF borderRect = new RectF(); + + public float getIndex() { + return indexAnimated.set(index); + } + + public void destroy() { + imageReceiver.onDetachedFromWindow(); + } + + public void apply() { + readAnimated.set(read, true); + indexAnimated.set(index, true); + scaleAnimated.set(scale, true); + } + } + + public ProfileStoriesView(Context context, int currentAccount, long userId, @NonNull View avatarContainer, ProfileActivity.AvatarImageView avatarImage, Theme.ResourcesProvider resourcesProvider) { + super(context); + + this.currentAccount = currentAccount; + this.userId = userId; + this.avatarContainer = avatarContainer; + this.avatarImage = avatarImage; + + storiesGradientTools = new StoriesGradientTools(); + storiesGradientTools.paint.setStyle(Paint.Style.STROKE); + storiesGradientTools.paint.setStrokeCap(Paint.Cap.ROUND); + + readPaint.setColor(0x5affffff); + readPaintAlpha = readPaint.getAlpha(); + readPaint.setStrokeWidth(dpf2(1.5f)); + readPaint.setStyle(Paint.Style.STROKE); + readPaint.setStrokeCap(Paint.Cap.ROUND); + + whitePaint.setColor(Theme.getColor(Theme.key_windowBackgroundWhite, resourcesProvider)); + + titleDrawable.setTextSize(dp(18)); + titleDrawable.setAnimationProperties(.4f, 0, 320, CubicBezierInterpolator.EASE_OUT_QUINT); + titleDrawable.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + titleDrawable.setTextColor(Theme.getColor(Theme.key_actionBarDefaultTitle, resourcesProvider)); + titleDrawable.setEllipsizeByGradient(true); + titleDrawable.setCallback(this); + + clipOutAvatar.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); + + updateStories(false, false); + } + + @Override + protected boolean verifyDrawable(@NonNull Drawable who) { + return who == titleDrawable || super.verifyDrawable(who); + } + + private TLRPC.UserFull userFull; + public void setUserFull(TLRPC.UserFull userFull) { + this.userFull = userFull; + updateStories(true, false); + } + + private void updateStories(boolean animated, boolean asUpdate) { + final boolean me = userId == UserConfig.getInstance(currentAccount).getClientUserId(); + TLRPC.TL_userStories userFullStories = userFull != null && !me ? userFull.stories : null; + TLRPC.TL_userStories stateStories = MessagesController.getInstance(currentAccount).getStoriesController().getStories(userId); + final TLRPC.TL_userStories userStories; + if (userId == 0) { + userStories = null; +// } else if (stateStories != null) { +// userStories = stateStories; + } else { + userStories = userFullStories; + } + int max_read_id = 0; + if (userFullStories != null) { + max_read_id = Math.max(max_read_id, userFullStories.max_read_id); + } + if (stateStories != null) { + max_read_id = Math.max(max_read_id, stateStories.max_read_id); + } + List stories = userStories == null || userStories.stories == null ? new ArrayList() : userStories.stories; + ArrayList storiesToShow = new ArrayList<>(); + int count = 0; + final int lastUnreadCount = unreadCount; + unreadCount = 0; + if (stories != null) { + for (int i = 0; i < stories.size(); ++i) { + TLRPC.StoryItem storyItem = stories.get(i); + if (storyItem instanceof TLRPC.TL_storyItemDeleted) { + continue; + } + if (storyItem.id > max_read_id) { + unreadCount++; + } + count++; + } + for (int i = 0; i < stories.size(); ++i) { + TLRPC.StoryItem storyItem = stories.get(i); + if (storyItem instanceof TLRPC.TL_storyItemDeleted) { + continue; + } + if (storyItem instanceof TLRPC.TL_storyItemSkipped) { + int id = storyItem.id; + if (stateStories != null) { + for (int j = 0; j < stateStories.stories.size(); ++j) { + if (stateStories.stories.get(j).id == id) { + storyItem = stateStories.stories.get(j); + break; + } + } + } + if (storyItem instanceof TLRPC.TL_storyItemSkipped) { + if (userFullStories != null) { + for (int j = 0; j < userFullStories.stories.size(); ++j) { + if (userFullStories.stories.get(j).id == id) { + storyItem = userFullStories.stories.get(j); + break; + } + } + } + continue; + } + if (storyItem instanceof TLRPC.TL_storyItemSkipped) { + continue; + } + } + if (me || storyItem.id > max_read_id) { + storiesToShow.add(storyItem); + if (storiesToShow.size() >= CIRCLES_MAX) { + break; + } + } + } + } + if (storiesToShow.size() < CIRCLES_MAX) { + for (int i = 0; i < stories.size(); ++i) { + TLRPC.StoryItem storyItem = stories.get(i); + if (storyItem instanceof TLRPC.TL_storyItemSkipped) { + int id = storyItem.id; + if (stateStories != null) { + for (int j = 0; j < stateStories.stories.size(); ++j) { + if (stateStories.stories.get(j).id == id) { + storyItem = stateStories.stories.get(j); + break; + } + } + } + if (storyItem instanceof TLRPC.TL_storyItemSkipped) { + if (userFullStories != null) { + for (int j = 0; j < userFullStories.stories.size(); ++j) { + if (userFullStories.stories.get(j).id == id) { + storyItem = userFullStories.stories.get(j); + break; + } + } + } + continue; + } + if (storyItem instanceof TLRPC.TL_storyItemSkipped) { + continue; + } + } + if (storyItem instanceof TLRPC.TL_storyItemDeleted) { + continue; + } + if (!storiesToShow.contains(storyItem)) { + storiesToShow.add(storyItem); + if (storiesToShow.size() >= CIRCLES_MAX) { + break; + } + } + } + } + + // update all existing circles (update and remove) + for (int i = 0; i < circles.size(); ++i) { + StoryCircle circle = circles.get(i); + + int index = -1; + TLRPC.StoryItem storyItem = null; + for (int j = 0; j < storiesToShow.size(); ++j) { + TLRPC.StoryItem storyItem2 = storiesToShow.get(j); + if (storyItem2.id == circle.storyId) { + index = j; + storyItem = storyItem2; + break; + } + } + + if (index == -1) { + // delete circle + circle.scale = 0f; + } else { + circle.index = index; + circle.read = me || userStories != null && storyItem != null && storyItem.id <= userStories.max_read_id; + } + if (!animated) { + circle.apply(); + } + } + + // add new + for (int i = 0; i < storiesToShow.size(); ++i) { + TLRPC.StoryItem storyItem = storiesToShow.get(i); + + int index = -1; + for (int j = 0; j < circles.size(); ++j) { + StoryCircle circle = circles.get(j); + if (circle.storyId == storyItem.id) { + index = j; + break; + } + } + + if (index == -1) { + storyItem.dialogId = userId; + StoryCircle circle = new StoryCircle(storyItem); + circle.index = i; + circle.scale = 1f; + circle.scaleAnimated.set(0f, true); + circle.read = me || userStories != null && storyItem.id <= userStories.max_read_id; + if (!animated) { + circle.apply(); + } + circles.add(circle); + } + } + + mainCircle = null; + for (int i = 0; i < circles.size(); ++i) { + StoryCircle circle = circles.get(i); + if (circle.scale > 0) { + mainCircle = circle; + break; + } + } + + final int newCount = Math.max(storiesToShow.size(), count); + if (asUpdate && animated && newCount == this.count + 1 && unreadCount == lastUnreadCount + 1) { + animateNewStory(); + } + this.count = newCount; + titleDrawable.setText(this.count > 0 ? LocaleController.formatPluralString("Stories", this.count) : "", animated && !LocaleController.isRTL); + + invalidate(); + } + + public void updateColors() { + + } + + private float expandProgress; + public void setExpandProgress(float progress) { + this.expandProgress = progress; + invalidate(); + } + + private float actionBarProgress; + public void setActionBarActionMode(float progress) { + if (Theme.isCurrentThemeDark()) { + return; + } + actionBarProgress = progress; + invalidate(); + } + + private final RectF rect1 = new RectF(); + private final RectF rect2 = new RectF(); + private final RectF rect3 = new RectF(); + + private final Path clipPath = new Path(); + + private final AnimatedFloat segmentsCountAnimated = new AnimatedFloat(this, 0, 240 * 2, CubicBezierInterpolator.EASE_OUT_QUINT); + private final AnimatedFloat segmentsUnreadCountAnimated = new AnimatedFloat(this, 0, 240, CubicBezierInterpolator.EASE_OUT_QUINT); + + private float newStoryBounceT = 1; + private ValueAnimator newStoryBounce; + + private void vibrateNewStory() { + if (SharedConfig.getDevicePerformanceClass() <= SharedConfig.PERFORMANCE_CLASS_LOW) { + return; + } + try { + performHapticFeedback(HapticFeedbackConstants.TEXT_HANDLE_MOVE, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); + } catch (Exception ignore) {} + AndroidUtilities.runOnUIThread(() -> { + try { + performHapticFeedback(HapticFeedbackConstants.TEXT_HANDLE_MOVE, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); + } catch (Exception ignore2) {} + }, 180); + } + + public void animateNewStory() { + if (newStoryBounce != null) { + newStoryBounce.cancel(); + } + + final boolean[] vibrated = new boolean[] { false }; + + newStoryBounce = ValueAnimator.ofFloat(0, 1); + newStoryBounce.addUpdateListener(anm -> { + float t = (float) anm.getAnimatedValue(); + if (!vibrated[0] && t > .2f) { + vibrated[0] = true; + vibrateNewStory(); + } + newStoryBounceT = Math.max(1, t); + invalidate(); + }); + newStoryBounce.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (!vibrated[0]) { + vibrated[0] = true; + vibrateNewStory(); + } + newStoryBounceT = 1; + invalidate(); + } + }); + newStoryBounce.setInterpolator(new OvershootInterpolator(3.0f)); + newStoryBounce.setDuration(400); + newStoryBounce.setStartDelay(120); + newStoryBounce.start(); + } + + float w; + + @Override + protected void dispatchDraw(Canvas canvas) { + float rright = rightAnimated.set(this.right); + float ax = avatarContainer.getX(); + float ay = avatarContainer.getY(); + float aw = avatarContainer.getWidth() * avatarContainer.getScaleX(); + float ah = avatarContainer.getHeight() * avatarContainer.getScaleY(); + rect1.set(ax, ay, ax + aw, ay + ah); + + float maxX = this.left; + boolean needsSort = false; + for (int i = 0; i < circles.size(); ++i) { + StoryCircle circle = circles.get(i); + circle.cachedScale = circle.scaleAnimated.set(circle.scale); + if (circle.cachedScale <= 0 && circle.scale <= 0) { + circle.destroy(); + circles.remove(i); + i--; + continue; + } + circle.cachedIndex = circle.indexAnimated.set(circle.index); + circle.cachedRead = circle.readAnimated.set(circle.read); + if (i > 0 && circles.get(i - 1).cachedIndex > circle.cachedIndex) { + needsSort = true; + break; + } + } + if (needsSort) { + Collections.sort(circles, (a, b) -> (int) (b.cachedIndex - a.cachedIndex)); + } + + final float segmentsAlpha = clamp(1f - expandProgress / 0.2f, 1, 0); + final float segmentsCount = segmentsCountAnimated.set(count); + final float segmentsUnreadCount = segmentsUnreadCountAnimated.set(unreadCount); + +// float cy = lerp(rect1.centerY(), this.cy, expandProgress); + float cy = lerp(rect1.centerY(), this.expandY, expandProgress); + storiesGradientTools.setBounds(this.left, cy - dp(24), this.right, cy + dp(24)); + if (expandProgress > 0 && segmentsAlpha < 1) { + float ix = 0; + w = 0; + for (int i = 0; i < circles.size(); ++i) { + StoryCircle circle = circles.get(i); + float scale = circle.cachedScale; + w += dp(14) * scale; + } + for (int i = 0; i < circles.size(); ++i) { + StoryCircle circle = circles.get(i); + + float scale = circle.cachedScale; + float read = circle.cachedRead; + + float r = dp(28) / 2f * scale; +// float cx = left + r + ix; + float cx = expandRight - w + r + ix; + ix += dp(18) * scale; + + maxX = Math.max(maxX, cx + r); + + rect2.set(cx - r, cy - r, cx + r, cy + r); + lerpCentered(rect1, rect2, expandProgress, rect3); + + circle.cachedRect.set(rect3); + circle.borderRect.set(rect3); + final float inset = lerp(dpf2(2.66f), lerp(dpf2(1.33f), dpf2(2.33f), expandProgress), read * expandProgress); + circle.borderRect.inset(-inset * scale, -inset * scale); + } + readPaint.setColor(ColorUtils.blendARGB(0x5affffff, 0x80BBC4CC, expandProgress)); + readPaintAlpha = readPaint.getAlpha(); + storiesGradientTools.paint.setStrokeWidth(lerp(dpf2(2.33f), dpf2(1.5f), expandProgress)); + readPaint.setStrokeWidth(lerp(dpf2(1.125f), dpf2(1.5f), expandProgress)); + if (expandProgress > 0) { + for (int i = 0; i < circles.size(); ++i) { + StoryCircle circle = circles.get(i); + int wasAlpha = whitePaint.getAlpha(); + whitePaint.setAlpha((int) (wasAlpha * expandProgress)); + canvas.drawCircle( + circle.cachedRect.centerX(), + circle.cachedRect.centerY(), + Math.min(circle.cachedRect.width(), circle.cachedRect.height()) / 2f + + lerp( + dpf2(2.66f) + storiesGradientTools.paint.getStrokeWidth() / 2f, + dpf2(2.33f) - readPaint.getStrokeWidth() / 2f, + circle.cachedRead + ) * expandProgress, + whitePaint + ); + whitePaint.setAlpha(wasAlpha); + } + } + for (int i = 0; i < circles.size(); ++i) { + StoryCircle B = circles.get(i); + StoryCircle A = nearest(i - 2 >= 0 ? circles.get(i - 2) : null, i - 1 >= 0 ? circles.get(i - 1) : null, B); + StoryCircle C = nearest(i + 1 < circles.size() ? circles.get(i + 1) : null, i + 2 < circles.size() ? circles.get(i + 2) : null, B); + + if (A != null && ( + Math.abs(A.borderRect.centerX() - B.borderRect.centerX()) < Math.abs(B.borderRect.width() / 2f - A.borderRect.width() / 2f) || + Math.abs(A.borderRect.centerX() - B.borderRect.centerX()) > A.borderRect.width() / 2f + B.borderRect.width() / 2f + )) { + A = null; + } + if (C != null && ( + Math.abs(C.borderRect.centerX() - B.borderRect.centerX()) < Math.abs(B.borderRect.width() / 2f - C.borderRect.width() / 2f) || + Math.abs(C.borderRect.centerX() - B.borderRect.centerX()) > C.borderRect.width() / 2f + B.borderRect.width() / 2f + )) { + C = null; + } + + if (B.cachedRead < 1) { + storiesGradientTools.paint.setAlpha((int) (0xFF * B.cachedScale * (1f - B.cachedRead) * (1f - segmentsAlpha))); + drawArcs(canvas, A, B, C, storiesGradientTools.paint); + } + if (B.cachedRead > 0) { + readPaint.setAlpha((int) (readPaintAlpha * B.cachedScale * B.cachedRead * (1f - segmentsAlpha))); + drawArcs(canvas, A, B, C, readPaint); + } + } + canvas.saveLayerAlpha(0, 0, getWidth(), getHeight(), (int) (0xFF * expandProgress * (1f - segmentsAlpha)), Canvas.ALL_SAVE_FLAG); + for (int i = circles.size() - 1; i >= 0; i--) { + StoryCircle circle = circles.get(i); + if (!circle.imageReceiver.getVisible()) { + continue; + } + int r = canvas.getSaveCount(); + final StoryCircle nextCircle = nearest(i - 1 >= 0 ? circles.get(i - 1) : null, i - 2 >= 0 ? circles.get(i - 2) : null, circle); + clipCircle(canvas, circle, nextCircle); + circle.imageReceiver.setImageCoords(circle.cachedRect); + circle.imageReceiver.draw(canvas); + canvas.restoreToCount(r); + } + canvas.restore(); + } + if (mainCircle != null && segmentsAlpha > 0) { + rect2.set(rect1); + rect2.inset(-dpf2(2.66f + 2.23f / 2), -dpf2(2.66f + 2.23f / 2)); + rect3.set(rect1); + rect3.inset(-dpf2(2.66f + 1.5f / 2), -dpf2(2.66f + 1.5f / 2)); + + final float separatorAngle = lerp(0, (float) (dpf2(2 + 2.23f) / (rect1.width() * Math.PI) * 360f), clamp(segmentsCount - 1, 1, 0) * segmentsAlpha); + final float maxCount = 50; // (float) (AndroidUtilities.dp(60) * Math.PI / dpf2(4)); + + final int mcount = Math.min(count, (int) maxCount); + final float animcount = Math.min(segmentsCount, maxCount); + + final float widthAngle = (360 - Math.max(0, animcount) * separatorAngle) / Math.max(1, animcount); + readPaint.setColor(ColorUtils.blendARGB(0x5affffff, 0x3a000000, actionBarProgress)); + readPaintAlpha = readPaint.getAlpha(); + float a = -90 - separatorAngle / 2f; + for (int i = 0; i < mcount; ++i) { + final float read = 1f - clamp(segmentsUnreadCount - i, 1, 0); + final float appear = 1f - clamp(mcount - animcount - i, 1, 0); + if (appear < 0) { + continue; + } + + float bounceScale = i == 0 ? 1 + (newStoryBounceT - 1) / 2.5f : 1f; + + if (bounceScale != 1) { + canvas.save(); + canvas.scale(bounceScale, bounceScale, rect2.centerX(), rect2.centerY()); + } + + if (read < 1) { + storiesGradientTools.paint.setAlpha((int) (0xFF * (1f - read) * segmentsAlpha)); + storiesGradientTools.paint.setStrokeWidth(dpf2(2.33f)); + canvas.drawArc(rect2, a, -widthAngle * appear, false, storiesGradientTools.paint); + } + + if (read > 0) { + readPaint.setAlpha((int) (readPaintAlpha * read * segmentsAlpha)); + readPaint.setStrokeWidth(dpf2(1.5f)); + canvas.drawArc(rect3, a, -widthAngle * appear, false, readPaint); + } + + if (bounceScale != 1) { + canvas.restore(); + } + + a -= widthAngle * appear + separatorAngle * appear; + } + } + +// float titleAlpha = Math.max(0, (expandProgress - .5f) * 2f); +// if (titleAlpha > 0) { +// float left = lerp(rect1.right + dp(16), maxX + dp(12), expandProgress); +// float right = lerp(getWidth(), rright, expandProgress); +// float y = lerp(rect1.centerY(), this.cy, expandProgress); +// titleDrawable.setBounds((int) (left), (int) (y - dp(18)), (int) (right), (int) (y + dp(18))); +// titleDrawable.setAlpha((int) (0xFF * titleAlpha)); +// titleDrawable.draw(canvas); +// } + } + + private void clipCircle(Canvas canvas, StoryCircle circle, StoryCircle nextCircle) { + if (nextCircle == null) { + return; + } + + rectTmp.set(nextCircle.cachedRect); + final float inset = dpf2(1.66f) * nextCircle.cachedScale; + rectTmp.inset(-inset, -inset); + float xA = nextCircle.cachedRect.centerX(), rA = nextCircle.cachedRect.width() / 2f; + float xB = circle.cachedRect.centerX(), rB = circle.cachedRect.width() / 2f; + + clipPath.rewind(); + float mx, d; + if (xA > xB) { + mx = ((xA - rA) + (xB + rB)) / 2f; + d = Math.abs(mx - xB); + float angle = (float) Math.toDegrees(Math.acos(d / rB)); + clipPath.arcTo(rectTmp, 180 + angle, -angle * 2); + clipPath.arcTo(circle.cachedRect, angle, 360 - angle * 2); + } else { + mx = ((xA + rA) + (xB - rB)) / 2f; + d = Math.abs(mx - xB); + float angle = (float) Math.toDegrees(Math.acos(d / rB)); + clipPath.arcTo(rectTmp, -angle, angle * 2); + clipPath.arcTo(circle.cachedRect, 180 - angle, -(360 - angle * 2)); + } + clipPath.close(); + canvas.save(); + canvas.clipPath(clipPath); + } + + private StoryCircle nearest(StoryCircle a, StoryCircle b, StoryCircle c) { + if (c == null || a == null && b == null) { + return null; + } else if (a == null || b == null) { + if (a != null) { + return a; + } + return b; + } + float ad = Math.min(Math.abs(a.borderRect.left - c.borderRect.right), Math.abs(a.borderRect.right - c.borderRect.left)); + float bd = Math.min(Math.abs(b.borderRect.left - c.borderRect.right), Math.abs(b.borderRect.right - c.borderRect.left)); + if (ad > bd) { + return a; + } + return b; + } + + private void drawArcs(Canvas canvas, StoryCircle A, StoryCircle B, StoryCircle C, Paint paint) { + if (A == null && C == null) { + canvas.drawArc(B.borderRect, 0, 360, false, paint); + } else if (A != null && C != null) { + float xA = A.borderRect.centerX(), rA = A.borderRect.width() / 2f; + float xB = B.borderRect.centerX(), rB = B.borderRect.width() / 2f; + float xC = C.borderRect.centerX(), rC = C.borderRect.width() / 2f; + + boolean d1, d2; + float mx, d, angle, angle1, angle2; + if (d1 = xA > xB) { + mx = ((xA - rA) + (xB + rB)) / 2f; + d = Math.abs(mx - xB); + angle1 = (float) Math.toDegrees(Math.acos(d / rB)); + } else { + mx = ((xA + rA) + (xB - rB)) / 2f; + d = Math.abs(mx - xB); + angle1 = (float) Math.toDegrees(Math.acos(d / rB)); + } + + if (d2 = xC > xB) { + mx = ((xC - rC) + (xB + rB)) / 2f; + d = Math.abs(mx - xB); + angle2 = (float) Math.toDegrees(Math.acos(d / rB)); + } else { + mx = ((xC + rC) + (xB - rB)) / 2f; + d = Math.abs(mx - xB); + angle2 = (float) Math.toDegrees(Math.acos(d / rB)); + } + + if (d1 && d2) { + angle = Math.max(angle1, angle2); + canvas.drawArc(B.borderRect, angle, 360 - angle * 2, false, paint); + } else if (d1) { // d1 && !d2 + canvas.drawArc(B.borderRect, 180 + angle2, 180 - (angle1 + angle2), false, paint); + canvas.drawArc(B.borderRect, angle1, 180 - angle2 - angle1, false, paint); + } else if (d2) { // !d1 && d2 + canvas.drawArc(B.borderRect, 180 + angle1, 180 - (angle2 + angle1), false, paint); + canvas.drawArc(B.borderRect, angle2, 180 - angle2 - angle1, false, paint); + } else { // !d1 && !d2 + angle = Math.max(angle1, angle2); + canvas.drawArc(B.borderRect, 180 + angle, 360 - angle * 2, false, paint); + } + + } else if (A != null || C != null) { + if (A == null) { + A = C; + } + float xA = A.borderRect.centerX(), rA = A.borderRect.width() / 2f; + float xB = B.borderRect.centerX(), rB = B.borderRect.width() / 2f; + + if (Math.abs(xA - xB) > rA + rB) { + canvas.drawArc(B.borderRect, 0, 360, false, paint); + } else { + float mx, d; + if (xA > xB) { + mx = ((xA - rA) + (xB + rB)) / 2f; + d = Math.abs(mx - xB); + float angle = (float) Math.toDegrees(Math.acos(d / rB)); + canvas.drawArc(B.borderRect, angle, 360 - angle * 2, false, paint); + } else { + mx = ((xA + rA) + (xB - rB)) / 2f; + d = Math.abs(mx - xB); + float angle = (float) Math.toDegrees(Math.acos(d / rB)); + canvas.drawArc(B.borderRect, 180 + angle, 360 - angle * 2, false, paint); + } + } + } + } + + private void lerpCentered(RectF a, RectF b, float t, RectF c) { + float cx = lerp(a.centerX(), b.centerX(), t); + float cy = lerp(a.centerY(), b.centerY(), t); + float r = lerp( + Math.min(a.width(), a.height()), + Math.min(b.width(), b.height()), + t + ) / 2f; + c.set(cx - r, cy - r, cx + r, cy + r); + } + + private float left, right, cy; + private float expandRight, expandY; + private final AnimatedFloat rightAnimated = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + + public void setBounds(float left, float right, float cy, boolean animated) { + boolean changed = Math.abs(left - this.left) > 0.1f || Math.abs(right - this.right) > 0.1f || Math.abs(cy - this.cy) > 0.1f; + this.left = left; + this.right = right; + if (!animated) { + this.rightAnimated.set(this.right, true); + } + this.cy = cy; + if (changed) { + invalidate(); + } + } + + public void setExpandCoords(float right, float y) { + this.expandRight = right; + this.expandY = y; + invalidate(); + } + + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.storiesUpdated) { + updateStories(true, true); + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + attached = true; + for (int i = 0; i < circles.size(); ++i) { + circles.get(i).imageReceiver.onAttachedToWindow(); + } + + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.storiesUpdated); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + + attached = false; + for (int i = 0; i < circles.size(); ++i) { + circles.get(i).imageReceiver.onDetachedFromWindow(); + } + + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.storiesUpdated); + } + + private final StoryViewer.PlaceProvider provider = new StoryViewer.PlaceProvider() { + @Override + public boolean findView(long dialogId, int messageId, int storyId, int type, StoryViewer.TransitionViewHolder holder) { + holder.avatarImage = null; + holder.storyImage = null; + if (expandProgress < .2f) { + holder.avatarImage = avatarImage.getImageReceiver(); + holder.storyImage = null; + holder.view = avatarImage; + holder.clipTop = 0; + holder.clipBottom = AndroidUtilities.displaySize.y; + holder.clipParent = (View) getParent(); + return true; + } + + StoryCircle a = null, b = null; + ImageReceiver imageReceiver = null; + for (int i = 0; i < circles.size(); ++i) { + StoryCircle circle = circles.get(i); + if (circle.scale >= 1 && circle.storyId == storyId) { + a = circle; + b = nearest(i - 1 >= 0 ? circles.get(i - 1) : null, i - 2 >= 0 ? circles.get(i - 2) : null, circle); + imageReceiver = circle.imageReceiver; + break; + } + } + if (imageReceiver == null) { + return false; + } + + holder.storyImage = imageReceiver; + holder.avatarImage = null; + holder.view = ProfileStoriesView.this; + holder.clipTop = 0; + holder.clipBottom = AndroidUtilities.displaySize.y; + holder.clipParent = (View) getParent(); + if (a != null && b != null) { + final RectF aRect = new RectF(a.cachedRect), bRect = new RectF(b.cachedRect); + final StoryCircle circle = a, nextCircle = b; + holder.drawClip = (canvas, bounds, alpha) -> { + aRect.set(circle.cachedRect); + bRect.set(nextCircle.cachedRect); + circle.cachedRect.set(bounds); + + try { + float scale = bounds.width() / aRect.width(); + float bcx = bounds.centerX() - (aRect.centerX() - bRect.centerX()) * (scale + 2f * (1f - alpha)); + float bcy = bounds.centerY(); // bounds.centerX() - (aRect.centerY() - bRect.centerY()) * scale; + float w2 = bRect.width() / 2f * scale, h2 = bRect.height() / 2f * scale; + nextCircle.cachedRect.set(bcx - w2, bcy - h2, bcx + w2, bcy + h2); + } catch (Exception ignore) {} + + clipCircle(canvas, circle, nextCircle); + + circle.cachedRect.set(aRect); + nextCircle.cachedRect.set(bRect); + }; + } else { + holder.drawClip = null; + } + return true; + } + + @Override + public void preLayout(long currentDialogId, int messageId, Runnable o) { + updateStories(true, false); + o.run(); + } + }; + + public boolean isEmpty() { + return circles.isEmpty(); + } + + protected void onTap(StoryViewer.PlaceProvider provider) { + + } + + private long tapTime; + private float tapX, tapY; + + @Override + public boolean onTouchEvent(MotionEvent event) { + boolean hit; + if (expandProgress < .9f) { + hit = rect2.contains(event.getX(), event.getY()); + } else { +// hit = event.getX() >= left && event.getX() <= right && Math.abs(event.getY() - cy) < dp(32); + hit = event.getX() >= expandRight - w - dp(32) && event.getX() <= expandRight + dp(32) && Math.abs(event.getY() - expandY) < dp(32); + } + if (hit && event.getAction() == MotionEvent.ACTION_DOWN) { + tapTime = System.currentTimeMillis(); + tapX = event.getX(); + tapY = event.getY(); + return true; + } else if (event.getAction() == MotionEvent.ACTION_UP) { + if (hit && System.currentTimeMillis() - tapTime <= ViewConfiguration.getTapTimeout() && MathUtils.distance(tapX, tapY, event.getX(), event.getY()) <= AndroidUtilities.dp(12) && !circles.isEmpty()) { + onTap(provider); + return true; + } + } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { + tapTime = -1; + } + return super.onTouchEvent(event); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/RoundRectOutlineProvider.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/RoundRectOutlineProvider.java new file mode 100644 index 0000000000..b6010c1c9b --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/RoundRectOutlineProvider.java @@ -0,0 +1,25 @@ +package org.telegram.ui.Stories; + +import android.graphics.Outline; +import android.os.Build; +import android.view.View; +import android.view.ViewOutlineProvider; + +import androidx.annotation.RequiresApi; + +import org.telegram.messenger.AndroidUtilities; + +@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) +public class RoundRectOutlineProvider extends ViewOutlineProvider { + + public float radiusInDp; + + public RoundRectOutlineProvider(int radiusInDp) { + this.radiusInDp = radiusInDp; + } + + @Override + public void getOutline(View view, Outline outline) { + outline.setRoundRect(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight(), AndroidUtilities.dpf2(radiusInDp)); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/SelfStoriesPreviewView.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/SelfStoriesPreviewView.java new file mode 100644 index 0000000000..9d6a54c7aa --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/SelfStoriesPreviewView.java @@ -0,0 +1,409 @@ +package org.telegram.ui.Stories; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Canvas; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.View; +import android.view.animation.OvershootInterpolator; +import android.widget.Scroller; + +import androidx.annotation.NonNull; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ImageReceiver; +import org.telegram.ui.Components.CubicBezierInterpolator; + +import java.util.ArrayList; + +public abstract class SelfStoriesPreviewView extends View { + + public int imagesFromY; + public int imagesFromW; + public int imagesFromH; + Scroller scroller; + float scrollX; + float minScroll; + float maxScroll; + + int childPadding; + private int viewH; + private int viewW; + private boolean isAttachedToWindow; + private int scrollToPositionInLayout = -1; + float topPadding; + float progressToOpen; + + ArrayList storyItems = new ArrayList<>(); + + ArrayList imageReceiversTmp = new ArrayList<>(); + ArrayList lastDrawnImageReceivers = new ArrayList<>(); + + GestureDetector gestureDetector = new GestureDetector(new GestureDetector.OnGestureListener() { + @Override + public boolean onDown(@NonNull MotionEvent e) { + scroller.abortAnimation(); + if (scrollAnimator != null) { + scrollAnimator.removeAllListeners(); + scrollAnimator.cancel(); + scrollAnimator = null; + } + checkScroll = false; + onDragging(); + return true; + } + + @Override + public void onShowPress(@NonNull MotionEvent e) { + + } + + @Override + public boolean onSingleTapUp(@NonNull MotionEvent e) { + for (int i = 0; i < lastDrawnImageReceivers.size(); i++) { + ImageHolder holder = lastDrawnImageReceivers.get(i); + if (lastDrawnImageReceivers.get(i).receiver.getDrawRegion().contains(e.getX(), e.getY())) { + if (lastClosestPosition != holder.position) { + scrollToPosition(holder.position, true, false); + } else { + onCenteredImageTap(); + } + } + } + return false; + } + + @Override + public boolean onScroll(@NonNull MotionEvent e1, @NonNull MotionEvent e2, float distanceX, float distanceY) { + scrollX += distanceX; + if (scrollX < minScroll) { + scrollX = minScroll; + } + if (scrollX > maxScroll) { + scrollX = maxScroll; + } + invalidate(); + return false; + } + + @Override + public void onLongPress(@NonNull MotionEvent e) { + + } + + @Override + public boolean onFling(@NonNull MotionEvent e1, @NonNull MotionEvent e2, float velocityX, float velocityY) { + scroller.fling((int) scrollX, 0, (int) -velocityX, 0, (int) minScroll, (int) maxScroll, 0, 0); + invalidate(); + return false; + } + }); + + public void onCenteredImageTap() { + + } + + private int lastClosestPosition; + + public SelfStoriesPreviewView(Context context) { + super(context); + scroller = new Scroller(context, new OvershootInterpolator()); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + childPadding = AndroidUtilities.dp(8); + viewH = (int) (AndroidUtilities.dp(180) / 1.2f); + viewW = (int) ((viewH / 16f) * 9f); + topPadding = ((AndroidUtilities.dp(180) - viewH) / 2f) + AndroidUtilities.dp(20); + updateScrollParams(); + if (scrollToPositionInLayout >= 0 && getMeasuredWidth() > 0) { + lastClosestPosition = -1; + scrollToPosition(scrollToPositionInLayout, false, false); + scrollToPositionInLayout = -1; + } + } + + private void updateScrollParams() { + minScroll = -(getMeasuredWidth() - viewW) / 2f; + maxScroll = (viewW + childPadding) * storyItems.size() - childPadding - getMeasuredWidth() + (getMeasuredWidth() - viewW) / 2f; + } + + boolean checkScroll; + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + if (scroller.computeScrollOffset()) { +// if (Math.abs(scrollX - scroller.getCurrX()) < AndroidUtilities.dp(1)) { +// scroller.abortAnimation(); +// } else { + scrollX = scroller.getCurrX(); + // } + invalidate(); + checkScroll = true; + } else if (checkScroll) { + scrollToClosest(); + } + float cx = getMeasuredWidth() / 2f; + imageReceiversTmp.clear(); + imageReceiversTmp.addAll(lastDrawnImageReceivers); + lastDrawnImageReceivers.clear(); + int closestPositon = -1; + float minDistance = Integer.MAX_VALUE; + for (int i = 0; i < storyItems.size(); i++) { + float x = -scrollX + (viewW + childPadding) * i; + float viewCx = x + viewW / 2f; + float distance = Math.abs(viewCx - cx); + float s = 1f; + float k = 0; + if (distance < viewW) { + k = 1f - Math.abs(viewCx - cx) / viewW; + s = 1f + 0.2f * k; + } + if (closestPositon == -1 || distance < minDistance) { + closestPositon = i; + minDistance = distance; + } + if (viewCx - cx < 0) { + x -= viewW * 0.1f * (1f - k); + } else { + x += viewW * 0.1f * (1f - k); + } + if (x > getMeasuredWidth() || x + viewW < 0) { + continue; + } + ImageHolder holder = findOrCreateImageReceiver(i, imageReceiversTmp); + float w = viewW * s; + float h = viewH * s; + float imageX = x - (w - viewW) / 2f; + float imageY = topPadding - (h - viewH) / 2f; + if (progressToOpen == 0 || i == lastClosestPosition) { + holder.receiver.setImageCoords(imageX, imageY, w, h); + } else { + float fromX = (i - lastClosestPosition) * getMeasuredWidth(); + float fromY = imagesFromY; + float fromH = imagesFromH; + float fromW = imagesFromW; + holder.receiver.setImageCoords( + AndroidUtilities.lerp(fromX, imageX, progressToOpen), + AndroidUtilities.lerp(fromY, imageY, progressToOpen), + AndroidUtilities.lerp(fromW, w, progressToOpen), + AndroidUtilities.lerp(fromH, h, progressToOpen) + ); + } + if (!(progressToOpen != 1f && i == lastClosestPosition)) { + holder.receiver.draw(canvas); + } + lastDrawnImageReceivers.add(holder); + } + if (scrollAnimator == null && lastClosestPosition != closestPositon) { + lastClosestPosition = closestPositon; + onClosestPositionChanged(lastClosestPosition); + } + for (int i = 0; i < imageReceiversTmp.size(); i++) { + imageReceiversTmp.get(i).onDetach(); + } + imageReceiversTmp.clear(); + // canvas.drawLine(getMeasuredWidth() / 2f, 0, getMeasuredWidth() / 2f, getMeasuredHeight(), new Paint()); + } + + abstract void onDragging(); + + public void onClosestPositionChanged(int lastClosestPosition) { + + } + + private void scrollToClosest() { + if (lastClosestPosition >= 0) { + scrollToPosition(lastClosestPosition, true, true); + } + } + + private ImageHolder findOrCreateImageReceiver(int position, ArrayList imageReceivers) { + for (int i = 0; i < imageReceivers.size(); i++) { + //TODO change to id + if (imageReceivers.get(i).position == position) { + return imageReceivers.remove(i); + } + } + ImageHolder imageHolder = new ImageHolder(); + imageHolder.onBind(position); + imageHolder.position = position; + return imageHolder; + } + + ValueAnimator scrollAnimator; + public void scrollToPosition(int p, boolean animated, boolean force) { + if ((lastClosestPosition == p && !force) || getMeasuredHeight() <= 0) { + return; + } + if (lastClosestPosition != p) { + lastClosestPosition = p; + onClosestPositionChanged(lastClosestPosition); + } + scroller.abortAnimation(); + checkScroll = false; + if (scrollAnimator != null) { + scrollAnimator.removeAllListeners(); + scrollAnimator.cancel(); + scrollAnimator = null; + } + if (!animated) { + scrollX = -getMeasuredWidth() / 2f + viewW / 2f + (viewW + childPadding) * p; + invalidate(); + } else { + float newScroll = -getMeasuredWidth() / 2f + viewW / 2f + (viewW + childPadding) * p; + if (newScroll == scrollX) { + return; + } + scrollAnimator = ValueAnimator.ofFloat(scrollX, newScroll); + scrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(@NonNull ValueAnimator animation) { + scrollX = (float) animation.getAnimatedValue(); + invalidate(); + } + }); + scrollAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + scrollAnimator = null; + } + }); + scrollAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); + scrollAnimator.setDuration(200); + scrollAnimator.start(); + } + } + + + @Override + public boolean onTouchEvent(MotionEvent event) { + gestureDetector.onTouchEvent(event); + if ((event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL) && scroller.isFinished()) { + scrollToClosest(); + } + return true; + } + + public void setItems(ArrayList storyItems, int position) { + this.storyItems.clear(); + this.storyItems.addAll(storyItems); + updateScrollParams(); + if (getMeasuredHeight() > 0) { + scrollToPosition(position, false, false); + } else { + scrollToPositionInLayout = position; + } + } + + public int getClosestPosition() { + return lastClosestPosition; + } + + public ImageReceiver getCenteredImageReciever() { + for (int i = 0; i < lastDrawnImageReceivers.size(); i++) { + if (lastDrawnImageReceivers.get(i).position == lastClosestPosition) { + return lastDrawnImageReceivers.get(i).receiver; + } + } + return null; + } + + public void abortScroll() { + scroller.abortAnimation(); + if (scrollAnimator != null) { + scrollAnimator.cancel(); + scrollAnimator = null; + } + scrollToPosition(lastClosestPosition, false, true); + } + + public float getFinalHeight() { + return AndroidUtilities.dp(180); + } + + public void setProgressToOpen(float progressToOpen) { + if (this.progressToOpen == progressToOpen) { + return; + } + this.progressToOpen = progressToOpen; + invalidate(); + } + + public void scrollToPositionWithOffset(int position, float positionOffset) { + scroller.abortAnimation(); + if (Math.abs(positionOffset) > 1) { + return; + } + if (scrollAnimator != null) { + scrollAnimator.cancel(); + scrollAnimator = null; + } + float fromScrollX = -getMeasuredWidth() / 2f + viewW / 2f + (viewW + childPadding) * position; + float progress = positionOffset; + float toScrollX; + if (positionOffset > 0) { + toScrollX = -getMeasuredWidth() / 2f + viewW / 2f + (viewW + childPadding) * (position + 1); + } else { + toScrollX = -getMeasuredWidth() / 2f + viewW / 2f + (viewW + childPadding) * (position - 1); + progress = -positionOffset; + } + if (progress == 0) { + scrollX = fromScrollX; + } else { + scrollX = AndroidUtilities.lerp(fromScrollX, toScrollX, progress); + } + checkScroll = false; + invalidate(); + } + + private class ImageHolder { + ImageReceiver receiver = new ImageReceiver(SelfStoriesPreviewView.this); + int position; + + public ImageHolder() { + receiver.setAllowLoadingOnAttachedOnly(true); + receiver.setRoundRadius(AndroidUtilities.dp(6)); + } + + void onBind(int position) { + SelfStoryViewsView.StoryItemInternal storyItem = storyItems.get(position); + if (isAttachedToWindow) { + receiver.onAttachedToWindow(); + } + if (storyItem.storyItem != null) { + StoriesUtilities.setImage(receiver, storyItem.storyItem); + } else { + StoriesUtilities.setImage(receiver, storyItem.uploadingStory); + } + + } + + void onDetach() { + receiver.onDetachedFromWindow(); + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + isAttachedToWindow = true; + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + isAttachedToWindow = false; + for (int i = 0; i < lastDrawnImageReceivers.size(); i++) { + lastDrawnImageReceivers.get(i).onDetach(); + } + lastDrawnImageReceivers.clear(); + } +} + diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/SelfStoryViewsPage.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/SelfStoryViewsPage.java new file mode 100644 index 0000000000..a5d8290d59 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/SelfStoryViewsPage.java @@ -0,0 +1,462 @@ +package org.telegram.ui.Stories; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.drawable.GradientDrawable; +import android.util.TypedValue; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.R; +import org.telegram.messenger.UserConfig; +import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Cells.FixedHeightEmptyCell; +import org.telegram.ui.Cells.ReactedUserHolderView; +import org.telegram.ui.Components.FlickerLoadingView; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.RecyclerItemsEnterAnimator; +import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.Components.StickerEmptyView; +import org.telegram.ui.LaunchActivity; +import org.telegram.ui.ProfileActivity; + +import java.util.ArrayList; +import java.util.HashSet; + +public class SelfStoryViewsPage extends FrameLayout implements NotificationCenter.NotificationCenterDelegate { + + private final static int TOP_PADDING = 46; + private static final int FIRST_PADDING_ITEM = 0; + private static final int USER_ITEM = 1; + private static final int LAST_ITEM = 2; + private static final int BUTTON_PADDING = 3; + private static final int FLICKER_LOADING_ITEM = 4; + private static final int EMPTY_VIEW = 5; + + private final TextView titleView; + private int measuerdHeight; + + RecyclerListView recyclerListView; + Theme.ResourcesProvider resourcesProvider; + int currentAccount; + ListAdapter listAdapter; + + public LinearLayoutManager layoutManager; + SelfStoryViewsView.StoryItemInternal storyItem; + ViewsModel model; + private boolean isAttachedToWindow; + RecyclerItemsEnterAnimator recyclerItemsEnterAnimator; + + public SelfStoryViewsPage(StoryViewer storyViewer, @NonNull Context context) { + super(context); + this.resourcesProvider = storyViewer.resourcesProvider; + currentAccount = storyViewer.currentAccount; + + titleView = new TextView(context); + titleView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider)); + titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); + titleView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + titleView.setPadding(AndroidUtilities.dp(21), AndroidUtilities.dp(6), AndroidUtilities.dp(21), AndroidUtilities.dp(8)); + + recyclerListView = new RecyclerListView(context) { + @Override + protected void onMeasure(int widthSpec, int heightSpec) { + measuerdHeight = MeasureSpec.getSize(heightSpec); + super.onMeasure(widthSpec, heightSpec); + } + }; + recyclerListView.setClipToPadding(false); + recyclerItemsEnterAnimator = new RecyclerItemsEnterAnimator(recyclerListView, true); + recyclerListView.setLayoutManager(layoutManager = new LinearLayoutManager(context)); + recyclerListView.setNestedScrollingEnabled(true); + recyclerListView.setAdapter(listAdapter = new ListAdapter()); + + addView(recyclerListView); + + + recyclerListView.setOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { + checkLoadMore(); + } + }); + recyclerListView.setOnItemClickListener((view, position) -> { + TLRPC.TL_storyView user = listAdapter.items.get(position).user; + if (user != null) { + storyViewer.presentFragment(ProfileActivity.of(user.user_id)); + } + }); + + listAdapter.updateRows(); + + View shadowView = new View(getContext()); + shadowView.setBackground(new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, new int[]{ Theme.getColor(Theme.key_dialogBackground, resourcesProvider), Color.TRANSPARENT })); + addView(shadowView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 8, 0, 0, TOP_PADDING - 8, 0, 0)); + + View shadowView2 = new View(getContext()); + shadowView2.setBackgroundColor(Theme.getColor(Theme.key_dialogBackground, resourcesProvider)); + addView(shadowView2, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 10, 0, 0, TOP_PADDING - 17, 0, 0)); + + addView(titleView); + } + + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + if (child == recyclerListView) { + canvas.save(); + canvas.clipRect(0, AndroidUtilities.dp(TOP_PADDING), getMeasuredWidth(), getMeasuredHeight()); + super.drawChild(canvas, child, drawingTime); + canvas.restore(); + return true; + } + return super.drawChild(canvas, child, drawingTime); + } + + private void checkLoadMore() { + if (model != null && layoutManager.findLastVisibleItemPosition() > listAdapter.getItemCount() - 10) { + model.loadNext(); + } + } + + public void setStoryItem(SelfStoryViewsView.StoryItemInternal storyItem) { + this.storyItem = storyItem; + if (storyItem.storyItem != null) { + TLRPC.StoryItem serverItem = storyItem.storyItem; + model = MessagesController.getInstance(currentAccount).storiesController.selfViewsModel.get(serverItem.id); + int totalCount = serverItem.views == null ? 0 : serverItem.views.views_count; + if (model == null || model.totalCount != totalCount) { + if (model != null) { + model.release(); + } + model = new ViewsModel(currentAccount, serverItem); + model.loadNext(); + MessagesController.getInstance(currentAccount).storiesController.selfViewsModel.put(serverItem.id, model); + } + if (serverItem.views == null || serverItem.views.views_count == 0) { + titleView.setText(LocaleController.getString("NobodyViewsTitle", R.string.NobodyViewsTitle)); + } else { + titleView.setText(LocaleController.formatPluralStringComma("Views", serverItem.views.views_count)); + } + } else { + titleView.setText(LocaleController.getString("UploadingStory", R.string.UploadingStory)); + } + } + + public static void preload(int currentAccount, TLRPC.StoryItem storyItem) { + if (storyItem == null) { + return; + } + ViewsModel model = MessagesController.getInstance(currentAccount).storiesController.selfViewsModel.get(storyItem.id); + int totalCount = storyItem.views == null ? 0 : storyItem.views.views_count; + if (model == null || model.totalCount != totalCount) { + if (model != null) { + model.release(); + } + model = new ViewsModel(currentAccount, storyItem); + model.loadNext(); + MessagesController.getInstance(currentAccount).storiesController.selfViewsModel.put(storyItem.id, model); + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (model != null) { + model.addListener(this); + model.animateDateForUsers.clear(); + } + listAdapter.updateRows(); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.storiesUpdated); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (model != null) { + model.removeListener(this); + } + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.storiesUpdated); + } + + public void onDataRecieved() { + NotificationCenter.getInstance(currentAccount).doOnIdle(() -> { + int oldCount = listAdapter.getItemCount(); + listAdapter.updateRows(); + recyclerItemsEnterAnimator.showItemsAnimated(oldCount); + checkLoadMore(); + }); + } + + public void setListBottomPadding(float bottomPadding) { + if (bottomPadding != recyclerListView.getPaddingBottom()) { + recyclerListView.setPadding(0, 0, 0, (int) bottomPadding); + recyclerListView.requestLayout(); + } + } + + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.storiesUpdated) { + if (storyItem.uploadingStory != null) { + TLRPC.TL_userStories stories = MessagesController.getInstance(currentAccount).storiesController.getStories(UserConfig.getInstance(currentAccount).clientUserId); + if (stories != null) { + for (int i = 0; i < stories.stories.size(); i++) { + TLRPC.StoryItem storyItem = stories.stories.get(i); + if (storyItem.attachPath != null && storyItem.attachPath.equals(this.storyItem.uploadingStory.path)) { + this.storyItem.uploadingStory = null; + this.storyItem.storyItem = storyItem; + setStoryItem(this.storyItem); + break; + } + } + } + } + } + } + + private class ListAdapter extends RecyclerListView.SelectionAdapter { + + ArrayList items = new ArrayList<>(); + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view; + switch (viewType) { + case FIRST_PADDING_ITEM: + view = new View(getContext()) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(TOP_PADDING), MeasureSpec.EXACTLY)); + } + }; + break; + case BUTTON_PADDING: + view = new FixedHeightEmptyCell(getContext(), 70); + break; + case USER_ITEM: + view = new ReactedUserHolderView(ReactedUserHolderView.STYLE_STORY, currentAccount, getContext(), resourcesProvider) { + @Override + public void openStory(long dialogId, Runnable onDone) { + LaunchActivity.getLastFragment().getOrCreateOverlayStoryViewer().doOnAnimationReady(onDone); + LaunchActivity.getLastFragment().getOrCreateOverlayStoryViewer().open(getContext(), dialogId, StoriesListPlaceProvider.of(recyclerListView)); + } + }; + break; + case FLICKER_LOADING_ITEM: + FlickerLoadingView loadingView = new FlickerLoadingView(getContext(), resourcesProvider); + loadingView.setIsSingleCell(true); + loadingView.setViewType(FlickerLoadingView.SOTRY_VIEWS_USER_TYPE); + loadingView.showDate(false); + view = loadingView; + break; + default: + case LAST_ITEM: + view = new View(getContext()) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + }; + break; + case EMPTY_VIEW: + StickerEmptyView emptyView = new StickerEmptyView(getContext(), null, model.isExpiredViews ? StickerEmptyView.STICKER_TYPE_PRIVACY : StickerEmptyView.STICKER_TYPE_NO_CONTACTS, resourcesProvider) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(measuerdHeight - recyclerListView.getPaddingBottom(), MeasureSpec.EXACTLY)); + } + }; + emptyView.title.setVisibility(View.GONE); + if (model.isExpiredViews) { + emptyView.subtitle.setText(AndroidUtilities.replaceTags(LocaleController.getString("ExpiredViewsStub", R.string.ExpiredViewsStub))); + } else { + emptyView.subtitle.setText(LocaleController.getString("NoViewsStub", R.string.NoViewsStub)); + } + emptyView.showProgress(false, false); + view = emptyView; + break; + } + return new RecyclerListView.Holder(view); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + if (holder.getItemViewType() == USER_ITEM) { + ReactedUserHolderView view = (ReactedUserHolderView) holder.itemView; + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(items.get(position).user.user_id); + + boolean animated = model.animateDateForUsers.remove(items.get(position).user.user_id); + view.setUserReaction(user, null, null, items.get(position).user.date, true, animated); + // items.get(position + 1).viewType == USER_ITEM + } + } + + @Override + public int getItemCount() { + return items.size(); + } + + @Override + public boolean isEnabled(RecyclerView.ViewHolder holder) { + return holder.getItemViewType() == USER_ITEM; + } + + public void updateRows() { + items.clear(); + if (model != null && model.views.isEmpty() && (model.isExpiredViews || (!model.loading && !model.hasNext))) { + items.add(new Item(EMPTY_VIEW)); + } else { + items.add(new Item(FIRST_PADDING_ITEM)); + if (model != null) { + for (int i = 0; i < model.views.size(); i++) { + items.add(new Item(USER_ITEM, model.views.get(i))); + } + } + if (model != null && (model.loading || model.hasNext)) { + items.add(new Item(FLICKER_LOADING_ITEM)); + } + } + notifyDataSetChanged(); + } + + @Override + public int getItemViewType(int position) { + return items.get(position).viewType; + } + } + + private class Item { + final int viewType; + TLRPC.TL_storyView user; + + private Item(int viewType) { + this.viewType = viewType; + } + + private Item(int viewType, TLRPC.TL_storyView user) { + this.viewType = viewType; + this.user = user; + } + } + + public static class ViewsModel { + + public int totalCount; + TLRPC.StoryItem storyItem; + int currentAccount; + boolean loading; + ArrayList views = new ArrayList<>(); + boolean isExpiredViews; + boolean initial; + boolean hasNext = true; + long offsetId; + int offsetDate; + int reqId = -1; + HashSet animateDateForUsers = new HashSet<>(); + + ArrayList listeners = new ArrayList<>(); + + public ViewsModel(int currentAccount, TLRPC.StoryItem storyItem) { + this.currentAccount = currentAccount; + this.storyItem = storyItem; + this.totalCount = storyItem.views == null ? 0 : storyItem.views.views_count; + isExpiredViews = StoriesUtilities.hasExpiredViews(storyItem); + if (!isExpiredViews) { + initial = true; + if (storyItem.views != null) { + for (int i = 0; i < storyItem.views.recent_viewers.size(); i++) { + TLRPC.TL_storyView storyView = new TLRPC.TL_storyView(); + storyView.user_id = storyItem.views.recent_viewers.get(i); + storyView.date = 0; + views.add(storyView); + } + } + } + } + + public void loadNext() { + if (loading || !hasNext || isExpiredViews) { + return; + } + TLRPC.TL_stories_getStoryViewsList req = new TLRPC.TL_stories_getStoryViewsList(); + req.id = storyItem.id; + req.limit = initial ? 20 : 100; + req.offset_id = offsetId; + req.offset_date = offsetDate; + + loading = true; + reqId = ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + loading = false; + reqId = -1; + if (response != null) { + TLRPC.TL_stories_storyViewsList res = (TLRPC.TL_stories_storyViewsList) response; + MessagesController.getInstance(currentAccount).putUsers(res.users, false); + if (initial) { + initial = false; + for (int i = 0; i < views.size(); i++) { + animateDateForUsers.add(views.get(i).user_id); + } + views.clear(); + } + views.addAll(res.views); + + if (!res.views.isEmpty()) { + TLRPC.TL_storyView last = res.views.get(res.views.size() - 1); + offsetDate = last.date; + offsetId = last.user_id; + hasNext = res.views.size() == req.limit; + } else { + hasNext = false; + } + + if (storyItem.views == null) { + storyItem.views = new TLRPC.TL_storyViews(); + } + if (res.count > storyItem.views.views_count) { + storyItem.views.recent_viewers.clear(); + for (int i = 0; i < (Math.min(3, res.users.size())); i++) { + storyItem.views.recent_viewers.add(res.users.get(i).id); + } + storyItem.views.views_count = res.count; + } + } else { + hasNext = false; + } + for (int i = 0; i < listeners.size(); i++) { + listeners.get(i).onDataRecieved(); + } + })); + + } + + public void addListener(SelfStoryViewsPage listener) { + if (!listeners.contains(listener)) { + listeners.add(listener); + } + } + + public void removeListener(SelfStoryViewsPage listener) { + listeners.remove(listener); + } + + public void release() { + if (reqId >= 0) { + ConnectionsManager.getInstance(currentAccount).cancelRequest(reqId, false); + } + reqId = -1; + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/SelfStoryViewsView.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/SelfStoryViewsView.java new file mode 100644 index 0000000000..e2d250eee8 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/SelfStoryViewsView.java @@ -0,0 +1,372 @@ +package org.telegram.ui.Stories; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.drawable.Drawable; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.core.view.NestedScrollingParent3; +import androidx.core.view.NestedScrollingParentHelper; +import androidx.core.view.ViewCompat; +import androidx.viewpager.widget.PagerAdapter; +import androidx.viewpager.widget.ViewPager; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.ImageReceiver; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.R; +import org.telegram.messenger.Utilities; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.LayoutHelper; + +import java.util.ArrayList; + +public class SelfStoryViewsView extends FrameLayout { + + private final PagerAdapter pagerAdapter; + public float toY; + public float maxSelfStoriesViewsOffset; + public float bottomPadding; + ContainerView viewPagerContainer; + float progressToOpen; + SelfStoriesPreviewView selfStoriesPreviewView; + float toHeight; + StoryViewer storyViewer; + private Drawable shadowDrawable; + Theme.ResourcesProvider resourcesProvider; + float selfStoriesViewsOffset; + boolean listenPager; + + ViewPagerInner viewPager; + ArrayList storyItems = new ArrayList<>(); + ArrayList itemViews = new ArrayList<>(); + private int currentState; + + public SelfStoryViewsView(@NonNull Context context, StoryViewer storyViewer) { + super(context); + this.resourcesProvider = storyViewer.resourcesProvider; + this.storyViewer = storyViewer; + selfStoriesPreviewView = new SelfStoriesPreviewView(getContext()) { + + @Override + void onDragging() { + listenPager = false; + } + + @Override + public void onClosestPositionChanged(int lastClosestPosition) { + super.onClosestPositionChanged(lastClosestPosition); + if (listenPager) { + return; + } + if (viewPager.getCurrentItem() != lastClosestPosition) { + try { + viewPager.setCurrentItem(lastClosestPosition, false); + } catch (Throwable e) { + FileLog.e(e); + viewPager.getAdapter().notifyDataSetChanged(); + viewPager.setCurrentItem(lastClosestPosition, false); + } + } + } + + @Override + public void onCenteredImageTap() { + storyViewer.cancelSwipeToViews(false); + } + }; + //selfStoriesPreviewView.setAlpha(0.2f); + + shadowDrawable = context.getResources().getDrawable(R.drawable.sheet_shadow_round).mutate(); + shadowDrawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_dialogBackground, resourcesProvider), PorterDuff.Mode.MULTIPLY)); + viewPagerContainer = new ContainerView(context); + + viewPager = new ViewPagerInner(context); + viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + if (listenPager) { + selfStoriesPreviewView.scrollToPositionWithOffset(position, positionOffset); + } + } + + @Override + public void onPageSelected(int position) { +// if (currentState == ViewPager.SCROLL_STATE_DRAGGING) { +// selfStoriesPreviewView.scrollToPosition(position, true, false); +// } + } + + @Override + public void onPageScrollStateChanged(int state) { + currentState = state; + if (currentState == ViewPager.SCROLL_STATE_DRAGGING) { + listenPager = true; + } +// if (currentState != ViewPager.SCROLL_STATE_DRAGGING) { +// selfStoriesPreviewView.scrollToPosition(viewPager.getCurrentItem(), true, true); +// } + } + }); + viewPager.setAdapter(pagerAdapter = new PagerAdapter() { + @Override + public int getCount() { + return storyItems.size(); + } + + @NonNull + @Override + public Object instantiateItem(@NonNull ViewGroup container, int position) { + SelfStoryViewsPage item = new SelfStoryViewsPage(storyViewer, context) { + @Override + protected void dispatchDraw(Canvas canvas) { + shadowDrawable.setBounds(-AndroidUtilities.dp(6), 0, getMeasuredWidth() + AndroidUtilities.dp(6), getMeasuredHeight()); + shadowDrawable.draw(canvas); + super.dispatchDraw(canvas); + } + }; + item.setPadding(0, AndroidUtilities.dp(16), 0 , 0); + item.setStoryItem(storyItems.get(position)); + // bottomPadding = (selfStoriesPreviewView.getTop() + toHeight + AndroidUtilities.dp(24)); + item.setListBottomPadding(bottomPadding); + + container.addView(item); + + + itemViews.add(item); + return item; + } + + @Override + public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) { + container.removeView((View) object); + itemViews.remove(object); + } + + @Override + public boolean isViewFromObject(@NonNull View view, @NonNull Object object) { + return view == object; + } + }); + viewPagerContainer.addView(viewPager, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT,0, 0, 0, 0, 0)); + + +// buttonContainer = new FrameLayout(getContext()); +// buttonContainer.setBackgroundColor(Theme.getColor(Theme.key_dialogBackground, resourcesProvider)); +// ButtonWithCounterView button = new ButtonWithCounterView(getContext(), resourcesProvider); +// +// buttonContainer.addView(button, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.FILL_HORIZONTAL, 10, 10, 10, 10)); +// viewPagerContainer.addView(buttonContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM)); +// button.setText(LocaleController.getString("Close", R.string.Close), false); +// button.setOnClickListener(new View.OnClickListener() { +// @Override +// public void onClick(View v) { +// storyViewer.cancelSwipeToViews(false); +// } +// }); + +// ImageView closeImageView = new ImageView(context); +// closeImageView.setScaleType(ImageView.ScaleType.CENTER); +// closeImageView.setImageResource(R.drawable.msg_close); +// closeImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider), PorterDuff.Mode.MULTIPLY)); +// closeImageView.setOnClickListener(e -> storyViewer.cancelSwipeToViews(false)); +// viewPagerContainer.addView(closeImageView, LayoutHelper.createFrame(48, 48, Gravity.RIGHT | Gravity.TOP, 0, 0, 2, 0)); + + addView(selfStoriesPreviewView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + addView(viewPagerContainer); + setVisibility(View.INVISIBLE); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int topMargin = 0;//AndroidUtilities.dp(20); + if (storyViewer.ATTACH_TO_FRAGMENT) { + topMargin += AndroidUtilities.statusBarHeight; + } + int height = MeasureSpec.getSize(heightMeasureSpec); + FrameLayout.LayoutParams layoutParams = (LayoutParams) selfStoriesPreviewView.getLayoutParams(); + layoutParams.topMargin = topMargin; + toHeight = selfStoriesPreviewView.getFinalHeight(); + toY = topMargin + AndroidUtilities.dp(20); + bottomPadding = (topMargin + AndroidUtilities.dp(20) + toHeight + AndroidUtilities.dp(24)); + maxSelfStoriesViewsOffset = height - bottomPadding; + for (int i = 0; i < itemViews.size(); i++) { + itemViews.get(i).setListBottomPadding(bottomPadding); + } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + + public void setOffset(float selfStoriesViewsOffset) { + if (this.selfStoriesViewsOffset == selfStoriesViewsOffset) { + return; + } + this.selfStoriesViewsOffset = selfStoriesViewsOffset; + viewPagerContainer.setTranslationY(getMeasuredHeight() - selfStoriesViewsOffset); + float oldProgressToOpen = progressToOpen; + progressToOpen = Utilities.clamp(selfStoriesViewsOffset / maxSelfStoriesViewsOffset, 1f, 0); + float alpha = Utilities.clamp(progressToOpen / 0.5f, 1f, 0); + // selfStoriesPreviewView.setAlpha(alpha); + + final PeerStoriesView currentView = storyViewer.getCurrentPeerView(); + if (oldProgressToOpen == 1f && progressToOpen != 1f) { + if (currentView != null) { + currentView.selectPosition(selfStoriesPreviewView.getClosestPosition()); + } + selfStoriesPreviewView.abortScroll(); + } + if (currentView != null) { + selfStoriesPreviewView.imagesFromY = currentView.storyContainer.getTop(); + selfStoriesPreviewView.imagesFromW = currentView.storyContainer.getMeasuredWidth(); + selfStoriesPreviewView.imagesFromH = currentView.storyContainer.getMeasuredHeight(); + } + selfStoriesPreviewView.setProgressToOpen(progressToOpen); + if (viewPager.gesturesEnabled && progressToOpen != 1f) { + viewPager.onTouchEvent(AndroidUtilities.emptyMotionEvent()); + } + setVisibility(progressToOpen == 0 ? View.INVISIBLE : View.VISIBLE); + if (progressToOpen != 1f) { + viewPager.gesturesEnabled = false; + } + } + + public void setItems(ArrayList storyItems, int selectedPosition) { + this.storyItems.clear(); + for (int i = 0; i < storyItems.size(); i++) { + this.storyItems.add(new StoryItemInternal(storyItems.get(i))); + } + ArrayList uploadingStories = MessagesController.getInstance(storyViewer.currentAccount).storiesController.getUploadingStories(); + for (int i = 0; i < uploadingStories.size(); i++) { + this.storyItems.add(new StoryItemInternal(uploadingStories.get(i))); + } + selfStoriesPreviewView.setItems(this.storyItems, selectedPosition); + viewPager.setAdapter(null); + viewPager.setAdapter(pagerAdapter); + pagerAdapter.notifyDataSetChanged(); + viewPager.setCurrentItem(selectedPosition); + } + + public ImageReceiver getCrossfadeToImage() { + return selfStoriesPreviewView.getCenteredImageReciever(); + } + + private class ContainerView extends FrameLayout implements NestedScrollingParent3{ + + private final NestedScrollingParentHelper nestedScrollingParentHelper; + + public ContainerView(@NonNull Context context) { + super(context); + nestedScrollingParentHelper = new NestedScrollingParentHelper(this); + } + + @Override + public boolean onStartNestedScroll(@NonNull View child, @NonNull View target, int axes, int type) { + if (axes == ViewCompat.SCROLL_AXIS_VERTICAL) { + return true; + } + return false; + } + + @Override + public void onNestedScrollAccepted(@NonNull View child, @NonNull View target, int axes, int type) { + nestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes); + } + + @Override + public void onStopNestedScroll(@NonNull View target, int type) { + nestedScrollingParentHelper.onStopNestedScroll(target); + } + + @Override + public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) { + // onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type, null); + } + + @Override + public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type, @NonNull int[] consumed) { + if (dyUnconsumed != 0 && dyConsumed == 0) { + float currentTranslation = storyViewer.selfStoriesViewsOffset; + currentTranslation += dyUnconsumed; + if (currentTranslation > storyViewer.selfStoriesViewsOffset) { + currentTranslation = storyViewer.selfStoriesViewsOffset; + } + setOffset(currentTranslation); + storyViewer.setSelfStoriesViewsOffset(currentTranslation); + } + } + + + @Override + public void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) { + + float currentTranslation = storyViewer.selfStoriesViewsOffset; + if (currentTranslation < maxSelfStoriesViewsOffset && dy > 0) { + currentTranslation += dy; + consumed[1] = dy; + if (currentTranslation > maxSelfStoriesViewsOffset) { + currentTranslation = maxSelfStoriesViewsOffset; + } + setOffset(currentTranslation); + storyViewer.setSelfStoriesViewsOffset(currentTranslation); + } + } + } + + private class ViewPagerInner extends ViewPager { + + boolean gesturesEnabled; + + public ViewPagerInner(@NonNull Context context) { + super(context); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + gesturesEnabled = true; + } + if (!gesturesEnabled) { + return false; + } + try { + return super.onInterceptTouchEvent(ev); + } catch (Exception e) { + return false; + } + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + gesturesEnabled = true; + } + if (!gesturesEnabled) { + return false; + } + return super.onTouchEvent(ev); + } + } + + public class StoryItemInternal { + public TLRPC.StoryItem storyItem; + public StoriesController.UploadingStory uploadingStory; + + public StoryItemInternal(TLRPC.StoryItem storyItem) { + this.storyItem = storyItem; + } + + public StoryItemInternal(StoriesController.UploadingStory uploadingStory) { + this.uploadingStory = uploadingStory; + } + } + + +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesController.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesController.java new file mode 100644 index 0000000000..ae830706e0 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesController.java @@ -0,0 +1,2285 @@ +package org.telegram.ui.Stories; + +import android.content.Intent; +import android.content.SharedPreferences; +import android.text.TextUtils; +import android.util.SparseArray; +import android.webkit.MimeTypeMap; + +import androidx.annotation.NonNull; +import androidx.collection.LongSparseArray; + +import com.google.android.exoplayer2.util.Consumer; + +import org.telegram.SQLite.SQLiteCursor; +import org.telegram.SQLite.SQLiteDatabase; +import org.telegram.SQLite.SQLitePreparedStatement; +import org.telegram.messenger.AccountInstance; +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.BuildVars; +import org.telegram.messenger.DownloadController; +import org.telegram.messenger.FileLoader; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.ImageLocation; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MediaController; +import org.telegram.messenger.MediaDataController; +import org.telegram.messenger.MessageObject; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.MessagesStorage; +import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.NotificationsController; +import org.telegram.messenger.R; +import org.telegram.messenger.SendMessagesHelper; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.UserObject; +import org.telegram.messenger.Utilities; +import org.telegram.messenger.VideoEditedInfo; +import org.telegram.messenger.support.LongSparseIntArray; +import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.NativeByteBuffer; +import org.telegram.tgnet.RequestDelegate; +import org.telegram.tgnet.TLObject; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.Components.Bulletin; +import org.telegram.ui.Stories.recorder.DraftsController; +import org.telegram.ui.Stories.recorder.StoryEntry; +import org.telegram.ui.Stories.recorder.StoryPrivacyBottomSheet; +import org.telegram.ui.Stories.recorder.StoryUploadingService; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.SortedSet; +import java.util.TreeSet; + +public class StoriesController { + + public final static int STATE_READ = 0; + public final static int STATE_UNREAD = 1; + public final static int STATE_UNREAD_CLOSE_FRIEND = 2; + + private final int currentAccount; + private final ArrayList uploadingStories = new ArrayList<>(); + private final ArrayList uploadingAndEditingStories = new ArrayList<>(); + private final HashMap editingStories = new HashMap<>(); + public LongSparseIntArray dialogIdToMaxReadId = new LongSparseIntArray(); + + TLRPC.TL_userStories currentUserStories; + + private ArrayList dialogListStories = new ArrayList<>(); + private ArrayList hiddenListStories = new ArrayList<>(); + private LongSparseArray allStoriesMap = new LongSparseArray(); + private LongSparseIntArray loadingDialogsStories = new LongSparseIntArray(); + StoriesStorage storiesStorage; + SharedPreferences mainSettings; + final ViewsForSelfStoriesRequester pollingViewsForSelfStoriesRequester; + + public final static Comparator storiesComparator = Comparator.comparingInt(o -> o.date); + + //load all stories once and manage they by updates + //reload only if user get diffToLong + boolean allStoriesLoaded; + boolean allHiddenStoriesLoaded; + boolean loadingFromDatabase; + String state = ""; + boolean hasMore; + private boolean loadingFromServer; + private boolean loadingFromServerHidden; + private boolean storiesReadLoaded; + private int totalStoriesCount; + private int totalStoriesCountHidden; + + private final DraftsController draftsController; + + + public SparseArray selfViewsModel = new SparseArray<>(); + private String stateHidden; + private boolean hasMoreHidden = true; + private boolean firstLoad = true; + + public StoriesController(int currentAccount) { + this.currentAccount = currentAccount; + storiesStorage = new StoriesStorage(currentAccount); + mainSettings = MessagesController.getInstance(currentAccount).getMainSettings(); + state = mainSettings.getString("last_stories_state", ""); + stateHidden = mainSettings.getString("last_stories_state_hidden", ""); + totalStoriesCountHidden = mainSettings.getInt("total_stores_hidden", 0); + totalStoriesCount = mainSettings.getInt("total_stores", 0); + storiesReadLoaded = mainSettings.getBoolean("read_loaded", false); + pollingViewsForSelfStoriesRequester = new ViewsForSelfStoriesRequester(this, currentAccount); + storiesStorage.getMaxReadIds(longSparseIntArray -> dialogIdToMaxReadId = longSparseIntArray); + + sortStoriesRunnable = () -> { + sortDialogStories(dialogListStories); + sortDialogStories(hiddenListStories); + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.storiesUpdated); + }; + + draftsController = new DraftsController(currentAccount); + } + + public void loadAllStories() { + if (!firstLoad) { + loadStories(); + loadStoriesRead(); + } + } + + private void loadStoriesRead() { + if (storiesReadLoaded) { + return; + } + TLRPC.TL_stories_getAllReadUserStories allReadUserStories = new TLRPC.TL_stories_getAllReadUserStories(); + ConnectionsManager.getInstance(currentAccount).sendRequest(allReadUserStories, (response, error) -> { + TLRPC.Updates updates = (TLRPC.Updates) response; + if (updates == null) { + return; + } + MessagesController.getInstance(currentAccount).processUpdateArray(updates.updates, updates.users, updates.chats, false, updates.date); + AndroidUtilities.runOnUIThread(() -> { + storiesReadLoaded = true; + mainSettings.edit().putBoolean("read_loaded", true).apply(); + }); + }); + } + + private void sortDialogStories(ArrayList list) { + fixDeletedAndNonContactsStories(list); + Collections.sort(list, userStoriesComparator); + } + + private void fixDeletedAndNonContactsStories(ArrayList list) { + for (int k = 0; k < list.size(); k++) { + TLRPC.TL_userStories userStories = list.get(k); + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(userStories.user_id); + if (user != null && !user.contact) { + list.remove(k); + k--; + } + for (int i = 0; i < userStories.stories.size(); i++) { + if (userStories.stories.get(i) instanceof TLRPC.TL_storyItemDeleted) { + userStories.stories.remove(i); + i--; + } + } + if (userStories.stories.isEmpty()) { + list.remove(k); + k--; + } + } + } + + @NonNull + public DraftsController getDraftsController() { + return draftsController; + } + + public boolean hasStories(long dialogId) { + TLRPC.TL_userStories stories = allStoriesMap.get(dialogId); + return stories != null && !stories.stories.isEmpty(); + } + + public boolean hasStories() { + return (dialogListStories != null && dialogListStories.size() > 0) || hasSelfStories(); + } + + public void loadStories() { + if (firstLoad) { + loadingFromDatabase = true; + storiesStorage.getAllStories(allStories -> { + loadingFromDatabase = false; + if (allStories != null) { + processAllStoriesResponse(allStories, false, true, false); + loadFromServer(false); + loadFromServer(true); + } else { + cleanup(); + loadStories(); + } + }); + } else { + loadFromServer(false); + loadFromServer(true); + } + firstLoad = false; + } + + public void loadHiddenStories() { + if (hasMoreHidden) { + loadFromServer(true); + } + } + + public void toggleHidden(long dialogId, boolean hide, boolean request, boolean notify) { + ArrayList removeFrom; + ArrayList insertTo; + boolean remove = true; + if (hide) { + // remove = true; + removeFrom = dialogListStories; + insertTo = hiddenListStories; + } else { + removeFrom = hiddenListStories; + insertTo = dialogListStories; + } + + TLRPC.TL_userStories removed = null; + for (int i = 0; i < removeFrom.size(); i++) { + if (removeFrom.get(i).user_id == dialogId) { + if (remove) { + removed = removeFrom.remove(i); + } else { + removed = removeFrom.get(i); + } + break; + } + } + if (removed != null) { + boolean found = false; + for (int i = 0; i < insertTo.size(); i++) { + if (insertTo.get(i).user_id == dialogId) { + found = true; + break; + } + } + if (!found) { + insertTo.add(0, removed); + AndroidUtilities.cancelRunOnUIThread(sortStoriesRunnable); + sortStoriesRunnable.run(); + } + } + if (notify) { + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.storiesUpdated); + } + MessagesController.getInstance(currentAccount).checkArchiveFolder(); + if (request) { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); + user.stories_hidden = hide; + MessagesStorage.getInstance(currentAccount).putUsersAndChats(Collections.singletonList(user), null, false, true); + MessagesController.getInstance(currentAccount).putUser(user, false); + TLRPC.TL_contacts_toggleStoriesHidden req = new TLRPC.TL_contacts_toggleStoriesHidden(); + req.id = MessagesController.getInstance(currentAccount).getInputUser(dialogId); + req.hidden = hide; + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> { + }); + } + } + + private void loadFromServer(boolean hidden) { + if ((hidden && loadingFromServerHidden) || (!hidden && loadingFromServer) || loadingFromDatabase) { + return; + } + if (hidden) { + loadingFromServerHidden = true; + } else { + loadingFromServer = true; + } + TLRPC.TL_stories_getAllStories req = new TLRPC.TL_stories_getAllStories(); + String state = hidden ? stateHidden : this.state; + boolean hasMore = hidden ? hasMoreHidden : this.hasMore; + if (!TextUtils.isEmpty(state)) { + req.state = state; + req.flags |= 1; + } + boolean isNext = false; + if (hasMore && !TextUtils.isEmpty(state)) { + isNext = req.next = true; + } + req.include_hidden = hidden; + boolean finalIsNext = isNext; + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + if (hidden) { + loadingFromServerHidden = false; + } else { + loadingFromServer = false; + } + FileLog.d("StoriesController loaded stories from server state=" + req.state + " more=" + req.next + " " + response); + if (response instanceof TLRPC.TL_stories_allStories) { + TLRPC.TL_stories_allStories storiesResponse = (TLRPC.TL_stories_allStories) response; + if (!hidden) { + this.totalStoriesCount = ((TLRPC.TL_stories_allStories) response).count; + this.hasMore = ((TLRPC.TL_stories_allStories) response).has_more; + this.state = storiesResponse.state; + mainSettings.edit().putString("last_stories_state", this.state) + .putBoolean("last_stories_has_more", this.hasMore) + .putInt("total_stores", this.totalStoriesCount) + .apply(); + } else { + this.totalStoriesCountHidden = ((TLRPC.TL_stories_allStories) response).count; + this.hasMoreHidden = ((TLRPC.TL_stories_allStories) response).has_more; + this.stateHidden = storiesResponse.state; + mainSettings.edit().putString("last_stories_state_hidden", this.stateHidden) + .putBoolean("last_stories_has_more_hidden", this.hasMoreHidden) + .putInt("total_stores_hidden", this.totalStoriesCountHidden) + .apply(); + } + processAllStoriesResponse(storiesResponse, hidden, false, finalIsNext); + } else if (response instanceof TLRPC.TL_stories_allStoriesNotModified) { + if (!hidden) { + this.hasMore = mainSettings.getBoolean("last_stories_has_more", false); + this.state = ((TLRPC.TL_stories_allStoriesNotModified) response).state; + mainSettings.edit().putString("last_stories_state", this.state).apply(); + } else { + this.hasMoreHidden = mainSettings.getBoolean("last_stories_has_more_hidden", false); + this.stateHidden = ((TLRPC.TL_stories_allStoriesNotModified) response).state; + mainSettings.edit().putString("last_stories_state_hidden", this.stateHidden).apply(); + } + boolean hasMoreLocal = hidden ? hasMoreHidden : this.hasMore; + if (hasMoreLocal) { + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.storiesUpdated); + } + } + })); + } + + private void processAllStoriesResponse(TLRPC.TL_stories_allStories storiesResponse, boolean hidden, boolean fromCache, boolean isNext) { + if (!isNext) { + if (!hidden) { + //allStoriesMap.clear(); + dialogListStories.clear(); + } else { + hiddenListStories.clear(); + } + } + FileLog.d("StoriesController processAllStoriesResponse " + storiesResponse.user_stories.size() + " " + fromCache + " " + hidden); + + MessagesController.getInstance(currentAccount).putUsers(storiesResponse.users, false); + + for (int i = 0; i < storiesResponse.user_stories.size(); i++) { + TLRPC.TL_userStories userStories = storiesResponse.user_stories.get(i); + for (int j = 0; j < userStories.stories.size(); j++) { + if (userStories.stories.get(j) instanceof TLRPC.TL_storyItemDeleted) { + NotificationsController.getInstance(currentAccount).processDeleteStory(userStories.user_id, userStories.stories.get(j).id); + userStories.stories.remove(j); + j--; + } + } + if (!userStories.stories.isEmpty()) { + allStoriesMap.put(userStories.user_id, userStories); + for (int k = 0; k < 2; k++) { + ArrayList storiesList = k == 0 ? hiddenListStories : dialogListStories; + // if (isNext) { + for (int j = 0; j < storiesList.size(); j++) { + if (storiesList.get(j).user_id == userStories.user_id) { + storiesList.remove(j); + break; + } + } + // } + } + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(userStories.user_id); + if (user == null) { + continue; + } + if (user.stories_hidden) { + addUserToHiddenList(userStories); + } else { + dialogListStories.add(userStories); + preloadUserStories(userStories); + } + } else { + allStoriesMap.remove(userStories.user_id); + } + } + if (!fromCache) { + storiesStorage.saveAllStories(storiesResponse.user_stories, isNext, hidden, () -> { +// if (!hidden) { +// FileLog.d("StoriesController all stories loaded"); +// allStoriesLoaded = true; +// mainSettings.edit().putBoolean("stories_loaded", true).apply(); +// } else { +// FileLog.d("StoriesController all hidden stories loaded"); +// allHiddenStoriesLoaded = true; +// mainSettings.edit().putBoolean("stories_loaded_hidden", true).apply(); +// } + }); + } + sortUserStories(); + } + + private void addUserToHiddenList(TLRPC.TL_userStories userStories) { + boolean found = false; + if (userStories.user_id == UserConfig.getInstance(currentAccount).getClientUserId()) { + return; + } + for (int i = 0; i < hiddenListStories.size(); i++) { + if (hiddenListStories.get(i).user_id == userStories.user_id) { + found = true; + } + } + if (!found) { + hiddenListStories.add(userStories); + } + MessagesController.getInstance(currentAccount).checkArchiveFolder(); + } + + private void sortUserStories() { + AndroidUtilities.cancelRunOnUIThread(sortStoriesRunnable); + sortStoriesRunnable.run(); + } + + public void preloadUserStories(TLRPC.TL_userStories userStories) { + int preloadPosition = 0; + for (int i = 0; i < userStories.stories.size(); i++) { + if (userStories.stories.get(i).id > userStories.max_read_id) { + preloadPosition = i; + break; + } + } + if (userStories.stories.isEmpty()) { + return; + } + preloadStory(userStories.user_id, userStories.stories.get(preloadPosition)); + if (preloadPosition > 0) { + preloadStory(userStories.user_id, userStories.stories.get(preloadPosition - 1)); + } + if (preloadPosition < userStories.stories.size() - 1) { + preloadStory(userStories.user_id, userStories.stories.get(preloadPosition + 1)); + } + } + + private void preloadStory(long dialogId, TLRPC.StoryItem storyItem) { + if (storyItem.attachPath != null) { + return; + } + boolean canPreloadStories = DownloadController.getInstance(currentAccount).canPreloadStories(); + if (!canPreloadStories) { + return; + } + boolean isVideo = storyItem.media != null && MessageObject.isVideoDocument(storyItem.media.document); + storyItem.dialogId = dialogId; + if (isVideo) { + TLRPC.PhotoSize size = FileLoader.getClosestPhotoSizeWithSize(storyItem.media.document.thumbs, 1000); + FileLoader.getInstance(currentAccount).loadFile(storyItem.media.document, storyItem, FileLoader.PRIORITY_LOW, 1); + FileLoader.getInstance(currentAccount).loadFile(ImageLocation.getForDocument(size, storyItem.media.document), storyItem, "jpg", FileLoader.PRIORITY_LOW, 1); + } else { + TLRPC.Photo photo = storyItem.media == null ? null : storyItem.media.photo; + if (photo != null && photo.sizes != null) { + TLRPC.PhotoSize size = FileLoader.getClosestPhotoSizeWithSize(photo.sizes, Integer.MAX_VALUE); + FileLoader.getInstance(currentAccount).loadFile(ImageLocation.getForPhoto(size, photo), storyItem, "jpg", FileLoader.PRIORITY_LOW, 1); + } + } + } + + public void uploadStory(StoryEntry entry, boolean count) { + UploadingStory uploadingStory = new UploadingStory(entry); + if (count) { + if (entry.isEdit) { + editingStories.put(entry.editStoryId, uploadingStory); + } else { + uploadingStories.add(uploadingStory); + } + uploadingAndEditingStories.add(uploadingStory); + } + uploadingStory.start(); + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.storiesUpdated); + } + + public ArrayList getDialogListStories() { + return dialogListStories; + } + + public TLRPC.TL_userStories getStories(long peerId) { + return allStoriesMap.get(peerId); + } + + public ArrayList getUploadingStories() { + return uploadingStories; + } + + public ArrayList getUploadingAndEditingStories() { + return uploadingAndEditingStories; + } + + public int getMyStoriesCount() { + int count = uploadingAndEditingStories.size(); + TLRPC.TL_userStories userStories = getStories(getSelfUserId()); + if (userStories != null && userStories.stories != null) { + count += userStories.stories.size(); + } + return count; + } + + public UploadingStory findEditingStory(TLRPC.StoryItem storyItem) { + if (storyItem == null || storyItem.dialogId != getSelfUserId()) { + return null; + } + return editingStories.get(storyItem.id); + } + + public UploadingStory getEditingStory() { + if (editingStories.isEmpty()) { + return null; + } + Collection values = editingStories.values(); + if (values.isEmpty()) { + return null; + } + return values.iterator().next(); + } + + private void applyNewStories(TLRPC.TL_userStories stories) { + allStoriesMap.put(stories.user_id, stories); + if (stories.user_id != UserConfig.getInstance(UserConfig.selectedAccount).clientUserId) { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(stories.user_id); + applyToList(stories); + if (user != null && !user.stories_hidden) { + preloadUserStories(stories); + } + } + updateStoriesInLists(stories.user_id, stories.stories); + } + + public static TLRPC.StoryItem applyStoryUpdate(TLRPC.StoryItem oldStoryItem, TLRPC.StoryItem newStoryItem) { + if (newStoryItem == null) { + return oldStoryItem; + } + if (oldStoryItem == null) { + return newStoryItem; + } + if (newStoryItem.min) { + oldStoryItem.pinned = newStoryItem.pinned; + oldStoryItem.isPublic = newStoryItem.isPublic; + oldStoryItem.close_friends = newStoryItem.close_friends; + if (newStoryItem.date != 0) { + oldStoryItem.date = newStoryItem.date; + } + if (newStoryItem.expire_date != 0) { + oldStoryItem.expire_date = newStoryItem.expire_date; + } + oldStoryItem.caption = newStoryItem.caption; + oldStoryItem.entities = newStoryItem.entities; + if (newStoryItem.media != null) { + oldStoryItem.media = newStoryItem.media; + } + // privacy and views shouldn't be copied when min=true + return oldStoryItem; + } + return newStoryItem; + } + + public void processUpdate(TLRPC.TL_updateStory updateStory) { + //stage queue + if (updateStory.story == null) { + return; + } + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(updateStory.user_id); + if (user != null && (user.contact || user.self)) { + storiesStorage.processUpdate(updateStory); + } + AndroidUtilities.runOnUIThread(() -> { + TLRPC.TL_userStories currentUserStory = allStoriesMap.get(updateStory.user_id); + FileLog.d("StoriesController update stories for user " + updateStory.user_id); + updateStoriesInLists(updateStory.user_id, Collections.singletonList(updateStory.story)); + + ArrayList newStoryItems = new ArrayList<>(); + int oldStoriesCount = totalStoriesCount; + long dialogId = updateStory.user_id; + boolean notify = false; + if (currentUserStory != null) { + boolean changed = false; + TLRPC.StoryItem newStory = updateStory.story; + if (newStory instanceof TLRPC.TL_storyItemDeleted) { + NotificationsController.getInstance(currentAccount).processDeleteStory(dialogId, newStory.id); + } + boolean found = false; + for (int i = 0; i < currentUserStory.stories.size(); i++) { + if (currentUserStory.stories.get(i).id == newStory.id) { + found = true; + if (newStory instanceof TLRPC.TL_storyItemDeleted) { + currentUserStory.stories.remove(i); + FileLog.d("StoriesController remove story id=" + newStory.id); + changed = true; + } else { + TLRPC.StoryItem oldStory = currentUserStory.stories.get(i); + newStory = applyStoryUpdate(oldStory, newStory); + newStoryItems.add(newStory); + currentUserStory.stories.set(i, newStory); + if (newStory.attachPath == null) { + newStory.attachPath = oldStory.attachPath; + } + if (newStory.firstFramePath == null) { + newStory.firstFramePath = oldStory.firstFramePath; + } + FileLog.d("StoriesController update story id=" + newStory.id); + } + break; + } + } + if (!found) { + if (newStory instanceof TLRPC.TL_storyItemDeleted) { + FileLog.d("StoriesController can't add new story DELETED"); + return; + } + if (StoriesUtilities.isExpired(currentAccount, newStory)) { + FileLog.d("StoriesController can't add new story isExpired"); + return; + } + if (user == null || (!user.self && !user.contact)) { + FileLog.d("StoriesController can't add new story user is not contact"); + return; + } + newStoryItems.add(newStory); + changed = true; + currentUserStory.stories.add(newStory); + FileLog.d("StoriesController add new story id=" + newStory.id + " total stories count " + currentUserStory.stories.size()); + preloadStory(dialogId, newStory); + notify = true; + applyToList(currentUserStory); + } + if (changed) { + if (currentUserStory.stories.isEmpty()) { + dialogListStories.remove(currentUserStory); + hiddenListStories.remove(currentUserStory); + allStoriesMap.remove(currentUserStory.user_id); + totalStoriesCount--; + } else { + Collections.sort(currentUserStory.stories, storiesComparator); + } + notify = true; + } + } else { + if (updateStory.story instanceof TLRPC.TL_storyItemDeleted) { + FileLog.d("StoriesController can't add user " + updateStory.user_id + " with new story DELETED"); + return; + } + if (StoriesUtilities.isExpired(currentAccount, updateStory.story)) { + FileLog.d("StoriesController can't add user " + updateStory.user_id + " with new story isExpired"); + return; + } + if (user == null || (!user.self && !user.contact)) { + FileLog.d("StoriesController can't add user cause is not contact"); + return; + } + currentUserStory = new TLRPC.TL_userStories(); + currentUserStory.user_id = updateStory.user_id; + currentUserStory.stories.add(updateStory.story); + FileLog.d("StoriesController add new user with story id=" + updateStory.story.id); + applyNewStories(currentUserStory); + notify = true; + totalStoriesCount++; + loadAllStoriesForDialog(updateStory.user_id); + } + if (oldStoriesCount != totalStoriesCount) { + mainSettings.edit().putInt("total_stores", totalStoriesCount).apply(); + } + fixDeletedAndNonContactsStories(dialogListStories); + fixDeletedAndNonContactsStories(hiddenListStories); + if (notify) { + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.storiesUpdated); + } + MessagesController.getInstance(currentAccount).checkArchiveFolder(); + }); + } + + private void applyToList(TLRPC.TL_userStories currentUserStory) { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(currentUserStory.user_id); + if (user == null) { + FileLog.d("StoriesController can't apply story user == null"); + return; + } + boolean found = false; + for (int i = 0; i < dialogListStories.size(); i++) { + if (dialogListStories.get(i).user_id == currentUserStory.user_id) { + dialogListStories.remove(i); + found = true; + break; + } + } + for (int i = 0; i < hiddenListStories.size(); i++) { + if (hiddenListStories.get(i).user_id == currentUserStory.user_id) { + hiddenListStories.remove(i); + found = true; + break; + } + } + if (BuildVars.LOGS_ENABLED) { + FileLog.d("StoriesController move user stories to first " + "hidden=" + user.stories_hidden + " did=" + currentUserStory.user_id); + } + if (user.stories_hidden) { + hiddenListStories.add(0, currentUserStory); + } else { + dialogListStories.add(0, currentUserStory); + } + + if (!found) { + loadAllStoriesForDialog(currentUserStory.user_id); + } + MessagesController.getInstance(currentAccount).checkArchiveFolder(); + } + + HashSet allStoriesLoading = new HashSet<>(); + + private void loadAllStoriesForDialog(long user_id) { + if (allStoriesLoading.contains(user_id)) { + return; + } + allStoriesLoading.add(user_id); + FileLog.d("StoriesController loadAllStoriesForDialog " + user_id); + TLRPC.TL_stories_getUserStories userStories = new TLRPC.TL_stories_getUserStories(); + userStories.user_id = MessagesController.getInstance(currentAccount).getInputUser(user_id); + ConnectionsManager.getInstance(currentAccount).sendRequest(userStories, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + allStoriesLoading.remove(user_id); + if (response == null) { + return; + } + TLRPC.TL_stories_userStories stories_userStories = (TLRPC.TL_stories_userStories) response; + MessagesController.getInstance(currentAccount).putUsers(stories_userStories.users, false); + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(user_id); + TLRPC.TL_userStories stories = stories_userStories.stories; + allStoriesMap.put(stories.user_id, stories); + if (user != null && (user.contact || user.self)) { + applyToList(stories); + storiesStorage.putUserStories(stories); + } + + FileLog.d("StoriesController processAllStoriesResponse dialogId=" + user_id + " overwrite stories " + stories_userStories.stories.stories.size()); + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.storiesUpdated); + })); + } + + public boolean hasSelfStories() { + TLRPC.TL_userStories storyItem = allStoriesMap.get(UserConfig.getInstance(currentAccount).clientUserId); + if (storyItem != null && !storyItem.stories.isEmpty()) { + return true; + } + if (!uploadingStories.isEmpty()) { + return true; + } + return false; + } + + public int getSelfStoriesCount() { + int count = 0; + TLRPC.TL_userStories storyItem = allStoriesMap.get(UserConfig.getInstance(currentAccount).clientUserId); + if (storyItem != null) { + count += storyItem.stories.size(); + } + count += uploadingStories.size(); + return count; + } + + public void deleteStory(TLRPC.StoryItem storyItem) { + if (storyItem == null || storyItem instanceof TLRPC.TL_storyItemDeleted) { + return; + } + TLRPC.TL_userStories stories = allStoriesMap.get(getSelfUserId()); + if (stories != null) { + for (int i = 0; i < stories.stories.size(); i++) { + if (stories.stories.get(i).id == storyItem.id) { + stories.stories.remove(i); + if (stories.stories.size() == 0) { + allStoriesMap.remove(getSelfUserId()); + dialogListStories.remove(stories); + hiddenListStories.remove(stories); + } + break; + } + } + } + TLRPC.TL_stories_deleteStories req = new TLRPC.TL_stories_deleteStories(); + req.id.add(storyItem.id); + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> { + if (error == null) { + + } + }); + storiesStorage.deleteStory(getSelfUserId(), storyItem.id); + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.storiesUpdated); + MessagesController.getInstance(currentAccount).checkArchiveFolder(); + updateDeletedStoriesInLists(getSelfUserId(), Arrays.asList(storyItem)); + } + + public void deleteStories(ArrayList storyItems) { + if (storyItems == null) { + return; + } + TLRPC.TL_stories_deleteStories req = new TLRPC.TL_stories_deleteStories(); + TLRPC.TL_userStories stories = allStoriesMap.get(getSelfUserId()); + for (int i = 0; i < storyItems.size(); ++i) { + TLRPC.StoryItem storyItem = storyItems.get(i); + if (storyItem instanceof TLRPC.TL_storyItemDeleted) { + continue; + } + if (stories != null) { + for (int j = 0; j < stories.stories.size(); j++) { + if (stories.stories.get(j).id == storyItem.id) { + stories.stories.remove(j); + if (stories.stories.isEmpty()) { + allStoriesMap.remove(getSelfUserId()); + } + break; + } + } + } + req.id.add(storyItem.id); + } + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> { + + }); + updateDeletedStoriesInLists(getSelfUserId(), storyItems); + storiesStorage.deleteStories(getSelfUserId(), req.id); + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.storiesUpdated); + } + + public void updateStoriesPinned(ArrayList storyItems, boolean pinned, Utilities.Callback whenDone) { + TLRPC.TL_stories_togglePinned req = new TLRPC.TL_stories_togglePinned(); + for (int i = 0; i < storyItems.size(); ++i) { + TLRPC.StoryItem storyItem = storyItems.get(i); + if (storyItem instanceof TLRPC.TL_storyItemDeleted) { + continue; + } + storyItem.pinned = pinned; + // todo: do update stories in one go in database + req.id.add(storyItem.id); + } + updateStoriesInLists(getSelfUserId(), storyItems); + req.pinned = pinned; + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + if (whenDone != null) { + whenDone.run(error == null); + } + })); + } + + private long getSelfUserId() { + return UserConfig.getInstance(currentAccount).getClientUserId(); + } + + public void updateStoryItem(long dialogId, TLRPC.StoryItem storyItem) { + storiesStorage.updateStoryItem(dialogId, storyItem); + updateStoriesInLists(dialogId, Collections.singletonList(storyItem)); + } + + public boolean markStoryAsRead(long dialogId, TLRPC.StoryItem storyItem) { + TLRPC.TL_userStories userStories = getStories(dialogId); + if (userStories == null) { + TLRPC.UserFull userFull = MessagesController.getInstance(currentAccount).getUserFull(dialogId); + userStories = userFull.stories; + } + return markStoryAsRead(userStories, storyItem, false); + } + + public boolean markStoryAsRead(TLRPC.TL_userStories userStories, TLRPC.StoryItem storyItem, boolean profile) { + if (storyItem == null || userStories == null) { + return false; + } + final long dialogId = userStories.user_id; + if (dialogId == UserConfig.getInstance(currentAccount).getClientUserId()) { + storyItem.justUploaded = false; + } + int currentReadId = dialogIdToMaxReadId.get(dialogId); + int newReadId = Math.max(userStories.max_read_id, Math.max(currentReadId, storyItem.id)); + NotificationsController.getInstance(currentAccount).processReadStories(dialogId, newReadId); + userStories.max_read_id = newReadId; + dialogIdToMaxReadId.put(dialogId, newReadId); + if (newReadId > currentReadId) { + if (!profile) { + storiesStorage.updateMaxReadId(dialogId, newReadId); + } + TLRPC.TL_stories_readStories req = new TLRPC.TL_stories_readStories(); + req.user_id = MessagesController.getInstance(currentAccount).getInputUser(dialogId); + req.max_id = storyItem.id; + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> {}); + return true; + } + return false; + } + + public void markStoriesAsReadFromServer(long dialogId, int maxStoryId) { + //stage queue + AndroidUtilities.runOnUIThread(() -> { + int maxStoryReadId = Math.max(dialogIdToMaxReadId.get(dialogId, 0), maxStoryId); + dialogIdToMaxReadId.put(dialogId, maxStoryReadId); + storiesStorage.updateMaxReadId(dialogId, maxStoryReadId); + TLRPC.TL_userStories userStories = getStories(dialogId); + if (userStories == null) { + return; + } + if (maxStoryId > userStories.max_read_id) { + userStories.max_read_id = maxStoryId; + Collections.sort(dialogListStories, userStoriesComparator); + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.storiesUpdated); + } + }); + } + + public boolean hasUnreadStories(long dialogId) { + TLRPC.TL_userStories userStories = allStoriesMap.get(dialogId); + if (userStories == null) { + return false; + } + if (dialogId == UserConfig.getInstance(currentAccount).getClientUserId()) { + if (!uploadingStories.isEmpty()) { + return true; + } + } + for (int i = 0; i < userStories.stories.size(); i++) { +// if (userStories.stories.get(i).justUploaded) { +// return true; +// } + if (userStories.stories.get(i).id > userStories.max_read_id) { + return true; + } + } + return false; + } + + public int getUnreadState(long dialogId) { + return getUnreadState(dialogId, 0); + } + + public int getUnreadState(long dialogId, int storyId) { + TLRPC.TL_userStories userStories = allStoriesMap.get(dialogId); + if (userStories == null) { + TLRPC.UserFull user = MessagesController.getInstance(currentAccount).getUserFull(dialogId); + if (user != null) { + userStories = user.stories; + } + } + if (userStories == null) { + return STATE_READ; + } + if (dialogId == UserConfig.getInstance(currentAccount).getClientUserId()) { + if (!uploadingStories.isEmpty()) { + return STATE_UNREAD; + } + } + boolean hasUnread = false; + int maxReadId = Math.max(userStories.max_read_id, dialogIdToMaxReadId.get(dialogId, 0)); + for (int i = 0; i < userStories.stories.size(); i++) { + if ((storyId == 0 || userStories.stories.get(i).id == storyId) && userStories.stories.get(i).id > maxReadId) { + hasUnread = true; + if (userStories.stories.get(i).close_friends) { + return STATE_UNREAD_CLOSE_FRIEND; + } + } + } + if (hasUnread) { + return STATE_UNREAD; + } + + return STATE_READ; + } + + public boolean hasUploadingStories() { + return !uploadingStories.isEmpty() || !editingStories.isEmpty(); + } + + public void cleanup() { + allStoriesLoaded = false; + allHiddenStoriesLoaded = false; + storiesReadLoaded = false; + stateHidden = ""; + state = ""; + mainSettings.edit() + .putBoolean("stories_loaded", false) + .remove("last_stories_state") + .putBoolean("stories_loaded_hidden", false) + .remove("last_stories_state_hidden") + .putBoolean("read_loaded", false) + .apply(); + AndroidUtilities.runOnUIThread(draftsController::cleanup); + + loadStories(); + loadStoriesRead(); + } + + public void pollViewsForSelfStories(boolean start) { + pollingViewsForSelfStoriesRequester.start(start); + } + + HashSet loadingAllStories = new HashSet<>(); + + void loadSkippedStories(long dialogId) { + loadSkippedStories(getStories(dialogId), false); + } + + void loadSkippedStories(TLRPC.TL_userStories userStories, boolean profile) { + if (userStories == null) { + return; + } + final long dialogId = userStories.user_id; + final long key = dialogId * (profile ? -1 : 1); + if (loadingAllStories.contains(key)) { + return; + } + ArrayList storyIdsToLoad = null; + if (userStories != null) { + for (int i = 0; i < userStories.stories.size(); i++) { + if (userStories.stories.get(i) instanceof TLRPC.TL_storyItemSkipped) { + if (storyIdsToLoad == null) { + storyIdsToLoad = new ArrayList<>(); + } + storyIdsToLoad.add(userStories.stories.get(i).id); + } + } + if (storyIdsToLoad != null) { + loadingAllStories.add(key); + TLRPC.TL_stories_getStoriesByID stories = new TLRPC.TL_stories_getStoriesByID(); + stories.id = storyIdsToLoad; + stories.user_id = MessagesController.getInstance(currentAccount).getInputUser(dialogId); + ConnectionsManager.getInstance(currentAccount).sendRequest(stories, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + loadingAllStories.remove(key); + TLRPC.TL_userStories userStories2 = profile ? userStories : getStories(dialogId); + if (userStories2 == null) { + return; + } + if (response instanceof TLRPC.TL_stories_stories) { + TLRPC.TL_stories_stories res = (TLRPC.TL_stories_stories) response; + for (int i = 0; i < res.stories.size(); i++) { + for (int j = 0; j < userStories2.stories.size(); j++) { + if (userStories2.stories.get(j).id == res.stories.get(i).id) { + userStories2.stories.set(j, res.stories.get(i)); + preloadStory(dialogId, res.stories.get(i)); + } + } + } + if (!profile) { + storiesStorage.updateStories(userStories2); + } + } + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.storiesUpdated); + })); + } + } + } + + public void loadNextStories(boolean hidden) { + if (hasMore) { + loadFromServer(hidden); + } + } + + public void fillMessagesWithStories(LongSparseArray> messagesWithUnknownStories, Runnable callback) { + storiesStorage.fillMessagesWithStories(messagesWithUnknownStories, callback); + } + + LongSparseArray resolvedStories = new LongSparseArray<>(); + + public void resolveStoryLink(long peerId, int storyId, Consumer consumer) { + TLRPC.TL_userStories userStoriesLocal = getStories(peerId); + if (userStoriesLocal != null) { + for (int i = 0; i < userStoriesLocal.stories.size(); i++) { + if (userStoriesLocal.stories.get(i).id == storyId && !(userStoriesLocal.stories.get(i) instanceof TLRPC.TL_storyItemSkipped)) { + consumer.accept(userStoriesLocal.stories.get(i)); + return; + } + } + } + long hash = peerId + storyId << 12; + TLRPC.StoryItem storyItem = resolvedStories.get(hash); + if (storyItem != null) { + consumer.accept(storyItem); + return; + } + TLRPC.TL_stories_getStoriesByID stories = new TLRPC.TL_stories_getStoriesByID(); + stories.id.add(storyId); + stories.user_id = MessagesController.getInstance(currentAccount).getInputUser(peerId); + ConnectionsManager.getInstance(currentAccount).sendRequest(stories, new RequestDelegate() { + @Override + public void run(TLObject res, TLRPC.TL_error error) { + AndroidUtilities.runOnUIThread(() -> { + TLRPC.StoryItem storyItem = null; + if (res != null) { + TLRPC.TL_stories_stories response = (TLRPC.TL_stories_stories) res; + if (response.stories.size() > 0) { + storyItem = response.stories.get(0); + resolvedStories.put(hash, storyItem); + } + } + consumer.accept(storyItem); + }); + } + }); + } + + public ArrayList getHiddenList() { + return hiddenListStories; + } + + public int getUnreadStoriesCount(long dialogId) { + TLRPC.TL_userStories userStories = allStoriesMap.get(dialogId); + for (int i = 0; i < userStories.stories.size(); i++) { + if (userStories.max_read_id < userStories.stories.get(i).id) { + return userStories.stories.size() - i; + } + } + return 0; + } + + public int getTotalStoriesCount(boolean hidden) { + if (hidden) { + return hasMoreHidden ? Math.max(1, totalStoriesCountHidden) : hiddenListStories.size(); + } else { + return hasMore ? Math.max(1, totalStoriesCount) : dialogListStories.size(); + } + } + + public void putStories(long dialogId, TLRPC.TL_userStories stories) { + allStoriesMap.put(dialogId, stories); + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); + if (user.contact || user.self) { + storiesStorage.putUserStories(stories); + applyToList(stories); + } + } + + public void setLoading(long dialogId, boolean loading) { + if (loading) { + loadingDialogsStories.put(dialogId, 1); + } else { + loadingDialogsStories.delete(dialogId); + } + } + + public boolean isLoading(long dialogId) { + return loadingDialogsStories.get(dialogId, 0) == 1; + } + + public void removeContact(long dialogId) { + for (int i = 0; i < dialogListStories.size(); i++) { + if (dialogListStories.get(i).user_id == dialogId) { + dialogListStories.remove(i); + break; + } + } + for (int i = 0; i < hiddenListStories.size(); i++) { + if (hiddenListStories.get(i).user_id == dialogId) { + hiddenListStories.remove(i); + break; + } + } + storiesStorage.deleteAllUserStories(dialogId); + MessagesController.getInstance(currentAccount).checkArchiveFolder(); + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.storiesUpdated); + } + + public StoriesStorage getStoriesStorage() { + return storiesStorage; + } + + public boolean hasHiddenStories() { + return !hiddenListStories.isEmpty(); + } + + public void checkExpiredStories() { + checkExpireStories(dialogListStories); + checkExpireStories(hiddenListStories); + } + + private void checkExpireStories(ArrayList dialogListStories) { + boolean notify = false; + for (int k = 0; k < dialogListStories.size(); k++) { + TLRPC.TL_userStories stories = dialogListStories.get(k); + for (int i = 0; i < stories.stories.size(); i++) { + if (StoriesUtilities.isExpired(currentAccount, stories.stories.get(i))) { + stories.stories.remove(i); + i--; + } + } + if (stories.stories.isEmpty()) { + allStoriesMap.remove(stories.user_id); + dialogListStories.remove(stories); + notify = true; + } + } + if (notify) { + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.storiesUpdated); + } + } + + public void checkExpiredStories(long dialogId) { + TLRPC.TL_userStories userStories = getStories(dialogId); + for (int i = 0; i < userStories.stories.size(); i++) { + if (StoriesUtilities.isExpired(currentAccount, userStories.stories.get(i))) { + userStories.stories.remove(i); + i--; + } + } + if (userStories.stories.isEmpty()) { + dialogListStories.remove(userStories); + hiddenListStories.remove(userStories); + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.storiesUpdated); + } + } + + public boolean hasLoadingStories() { + return loadingDialogsStories.size() > 0; + } + + public class UploadingStory implements NotificationCenter.NotificationCenterDelegate { + + public final long random_id; + public final boolean edit; + + final StoryEntry entry; + String path; + String firstFramePath; + float progress; + float convertingProgress, uploadProgress; + boolean ready; + boolean isVideo; + + boolean canceled; + private int currentRequest; + private long firstSecondSize = -1; + private long duration; + + private MessageObject messageObject; + private VideoEditedInfo info; + private boolean putMessages; + private boolean isCloseFriends; + + public UploadingStory(StoryEntry entry) { + random_id = Utilities.random.nextLong(); + edit = entry.isEdit; + this.entry = entry; + if (entry.uploadThumbFile != null) { + this.firstFramePath = entry.uploadThumbFile.getAbsolutePath(); + } + } + + private void startForeground() { + Intent intent = new Intent(ApplicationLoader.applicationContext, StoryUploadingService.class); + intent.putExtra("path", path); + intent.putExtra("currentAccount", currentAccount); + try { + ApplicationLoader.applicationContext.startService(intent); + } catch (Throwable e) { + FileLog.e(e); + } + } + + public void start() { + if (entry.isEdit && !entry.editedMedia) { + sendUploadedRequest(null); + return; + } + isCloseFriends = entry.privacy != null && entry.privacy.isCloseFriends(); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.fileUploaded); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.fileUploadFailed); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.fileUploadProgressChanged); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.filePreparingFailed); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.filePreparingStarted); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.fileNewChunkAvailable); + if (isVideo = entry.wouldBeVideo()) { + TLRPC.TL_message message = new TLRPC.TL_message(); + message.id = 1; + path = message.attachPath = StoryEntry.makeCacheFile(currentAccount, true).getAbsolutePath(); + messageObject = new MessageObject(currentAccount, message, (MessageObject) null, false, false); + entry.getVideoEditedInfo(info -> { + this.info = info; + messageObject.videoEditedInfo = info; + duration = info.estimatedDuration / 1000L; + if (messageObject.videoEditedInfo.needConvert()) { + MediaController.getInstance().scheduleVideoConvert(messageObject, false, false); + } else { + boolean rename = new File(messageObject.videoEditedInfo.originalPath).renameTo(new File(path)); + if (rename) { + FileLoader.getInstance(currentAccount).uploadFile(path, false, false, ConnectionsManager.FileTypeVideo); + } + } + }); + } else { + final File destFile = StoryEntry.makeCacheFile(currentAccount, false); + path = destFile.getAbsolutePath(); + Utilities.themeQueue.postRunnable(() -> { + entry.buildPhoto(destFile); + AndroidUtilities.runOnUIThread(() -> { + ready = true; + upload(); + }); + }); + } + startForeground(); + } + + private void upload() { + if (entry.shareUserIds != null) { + putMessages(); + } else { + FileLoader.getInstance(currentAccount).uploadFile(path, false, !entry.isVideo, isVideo ? Math.max(1, (int) (info != null ? info.estimatedSize : 0)) : 0, entry.isVideo ? ConnectionsManager.FileTypeVideo : ConnectionsManager.FileTypePhoto, true); + } + } + + public void cleanup() { + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.fileUploaded); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.fileUploadFailed); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.fileUploadProgressChanged); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.filePreparingFailed); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.filePreparingStarted); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.fileNewChunkAvailable); + uploadingStories.remove(UploadingStory.this); + uploadingAndEditingStories.remove(UploadingStory.this); + if (edit) { + editingStories.remove(entry.editStoryId); + } + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.storiesUpdated); + if (entry != null) { + entry.destroy(false); + } + NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.uploadStoryEnd, path); + } + + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.filePreparingStarted) { + if (args[0] == messageObject) { + this.path = (String) args[1]; + upload(); + } + } else if (id == NotificationCenter.fileNewChunkAvailable) { + if (args[0] == messageObject) { + String finalPath = (String) args[1]; + long availableSize = (Long) args[2]; + long finalSize = (Long) args[3]; + convertingProgress = (float) args[4]; + progress = convertingProgress * .3f + uploadProgress * .7f; + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.uploadStoryProgress, path, progress); + + if (firstSecondSize < 0 && convertingProgress * duration >= 1000) { + firstSecondSize = availableSize; + } + + FileLoader.getInstance(currentAccount).checkUploadNewDataAvailable(finalPath, false, Math.max(1, availableSize), finalSize, convertingProgress); + + if (finalSize > 0) { + if (firstSecondSize < 0) { + firstSecondSize = finalSize; + } + ready = true; + } + } + } else if (id == NotificationCenter.filePreparingFailed) { + if (args[0] == messageObject) { + // TODO + cleanup(); + } + } else if (id == NotificationCenter.fileUploaded) { + String location = (String) args[0]; + if (path != null && location.equals(path)) { + TLRPC.InputFile uploadedFile = (TLRPC.InputFile) args[1]; + sendUploadedRequest(uploadedFile); + } + } else if (id == NotificationCenter.fileUploadFailed) { + String location = (String) args[0]; + if (path != null && location.equals(path)) { + NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.showBulletin, Bulletin.TYPE_ERROR, LocaleController.getString("StoryUploadError", R.string.StoryUploadError)); + cleanup(); + } + } else if (id == NotificationCenter.fileUploadProgressChanged) { + String location = (String) args[0]; + if (location.equals(path)) { + Long loadedSize = (Long) args[1]; + Long totalSize = (Long) args[2]; + uploadProgress = Math.min(1f, loadedSize / (float) totalSize); + progress = convertingProgress * .3f + uploadProgress * .7f; + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.uploadStoryProgress, path, progress); + } + } + } + + private void sendUploadedRequest(TLRPC.InputFile uploadedFile) { + if (canceled) { + return; + } + if (entry.shareUserIds != null) { + return; + } + + TLRPC.InputMedia media = null; + if (uploadedFile != null) { + if (entry.wouldBeVideo()) { + TLRPC.TL_inputMediaUploadedDocument inputMediaVideo = new TLRPC.TL_inputMediaUploadedDocument(); + inputMediaVideo.file = uploadedFile; + TLRPC.TL_documentAttributeVideo attributeVideo = new TLRPC.TL_documentAttributeVideo(); + SendMessagesHelper.fillVideoAttribute(path, attributeVideo, null); + attributeVideo.supports_streaming = true; + attributeVideo.flags |= 4; + attributeVideo.preload_prefix_size = (int) firstSecondSize; + inputMediaVideo.attributes.add(attributeVideo); + if (entry.stickers != null && (!entry.stickers.isEmpty() || entry.editStickers != null && !entry.editStickers.isEmpty())) { + inputMediaVideo.flags |= 1; + inputMediaVideo.stickers = new ArrayList<>(entry.stickers); + if (entry.editStickers != null) { + inputMediaVideo.stickers.addAll(entry.editStickers); + } + inputMediaVideo.attributes.add(new TLRPC.TL_documentAttributeHasStickers()); + } + media = inputMediaVideo; + media.nosound_video = entry.muted || !entry.isVideo; + media.mime_type = "video/mp4"; + } else { + TLRPC.TL_inputMediaUploadedPhoto inputMediaPhoto = new TLRPC.TL_inputMediaUploadedPhoto(); + inputMediaPhoto.file = uploadedFile; + media = inputMediaPhoto; + MimeTypeMap myMime = MimeTypeMap.getSingleton(); + String ext = "txt"; + int idx = path.lastIndexOf('.'); + if (idx != -1) { + ext = path.substring(idx + 1).toLowerCase(); + } + String mimeType = myMime.getMimeTypeFromExtension(ext); + media.mime_type = mimeType; + if (entry.stickers != null && (!entry.stickers.isEmpty() || entry.editStickers != null && !entry.editStickers.isEmpty())) { + inputMediaPhoto.flags |= 1; + if (entry.editStickers != null) { + inputMediaPhoto.stickers.addAll(entry.editStickers); + } + inputMediaPhoto.stickers = new ArrayList<>(entry.stickers); + } + } + } + TLObject req; + + final int captionLimit = MessagesController.getInstance(currentAccount).storyCaptionLengthLimit; + if (edit) { + TLRPC.TL_stories_editStory editStory = new TLRPC.TL_stories_editStory(); + editStory.id = entry.editStoryId; + + if (media != null && entry.editedMedia) { + editStory.flags |= 1; + editStory.media = media; + } + + if (entry.editedCaption && entry.caption != null) { + editStory.flags |= 2; + CharSequence[] caption = new CharSequence[]{ entry.caption }; + if (caption[0].length() > captionLimit) { + caption[0] = caption[0].subSequence(0, captionLimit); + } + editStory.entities = MediaDataController.getInstance(currentAccount).getEntities(caption, true); + if (caption[0].length() > captionLimit) { + caption[0] = caption[0].subSequence(0, captionLimit); + } + editStory.caption = caption[0].toString(); + } + + if (entry.editedPrivacy) { + editStory.flags |= 4; + editStory.privacy_rules.addAll(entry.privacyRules); + } + + req = editStory; + } else { + TLRPC.TL_stories_sendStory sendStory = new TLRPC.TL_stories_sendStory(); + sendStory.random_id = random_id; + sendStory.media = media; + sendStory.privacy_rules.addAll(entry.privacyRules); + sendStory.pinned = entry.pinned; + sendStory.noforwards = !entry.allowScreenshots; + + if (entry.caption != null) { + sendStory.flags |= 3; + CharSequence[] caption = new CharSequence[]{ entry.caption }; + if (caption[0].length() > captionLimit) { + caption[0] = caption[0].subSequence(0, captionLimit); + } + sendStory.entities = MediaDataController.getInstance(currentAccount).getEntities(caption, true); + if (caption[0].length() > captionLimit) { + caption[0] = caption[0].subSequence(0, captionLimit); + } + sendStory.caption = caption[0].toString(); + } + + if (entry.period == Integer.MAX_VALUE) { + sendStory.pinned = true; + } else { + sendStory.flags |= 8; + sendStory.period = entry.period; + } + + req = sendStory; + } + + currentRequest = ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> { + if (response != null) { + TLRPC.Updates updates = (TLRPC.Updates) response; + int storyId = 0; + TLRPC.StoryItem storyItem = null; + for (int i = 0; i < updates.updates.size(); i++) { + if (updates.updates.get(i) instanceof TLRPC.TL_updateStory) { + TLRPC.TL_updateStory updateStory = (TLRPC.TL_updateStory) updates.updates.get(i); + updateStory.story.attachPath = path; + updateStory.story.firstFramePath = firstFramePath; + updateStory.story.justUploaded = !edit; + storyId = updateStory.story.id; + if (storyItem == null) { + storyItem = updateStory.story; + } + } + if (updates.updates.get(i) instanceof TLRPC.TL_updateStoryID) { + TLRPC.TL_updateStoryID updateStory = (TLRPC.TL_updateStoryID) updates.updates.get(i); + if (storyItem == null) { + storyItem = new TLRPC.TL_storyItem(); + storyItem.date = ConnectionsManager.getInstance(currentAccount).getCurrentTime(); + storyItem.expire_date = storyItem.date + (entry.period == Integer.MAX_VALUE ? 86400 : entry.period); + storyItem.privacy = StoryPrivacyBottomSheet.StoryPrivacy.toOutput(entry.privacyRules); + storyItem.pinned = entry.period == Integer.MAX_VALUE; + storyItem.dialogId = UserConfig.getInstance(currentAccount).clientUserId; + storyItem.attachPath = path; + storyItem.firstFramePath = firstFramePath; + storyItem.id = updateStory.id; + storyItem.justUploaded = !edit; + } + } + } + if (canceled) { + TLRPC.TL_stories_deleteStories stories_deleteStory = new TLRPC.TL_stories_deleteStories(); + stories_deleteStory.id.add(storyId); + ConnectionsManager.getInstance(currentAccount).sendRequest(stories_deleteStory, (response1, error1) -> { + + }); + } else { + if ((storyId == 0 || edit) && storyItem != null) { + TLRPC.TL_updateStory tl_updateStory = new TLRPC.TL_updateStory(); + tl_updateStory.user_id = UserConfig.getInstance(currentAccount).clientUserId; + tl_updateStory.story = storyItem; + AndroidUtilities.runOnUIThread(() -> { + MessagesController.getInstance(currentAccount).getStoriesController().processUpdate(tl_updateStory); + }); + } + MessagesController.getInstance(currentAccount).processUpdateArray(updates.updates, updates.users, updates.chats, false, updates.date); + } + } + + AndroidUtilities.runOnUIThread(this::cleanup); + }); + } + + private void putMessages() { + if (entry.shareUserIds == null || putMessages) { + return; + } + final int count = entry.shareUserIds.size(); + String caption = entry.caption == null ? null : entry.caption.toString(); + ArrayList captionEntities = entry.caption == null ? null : MediaDataController.getInstance(currentAccount).getEntities(new CharSequence[]{entry.caption}, true); + for (int i = 0; i < count; ++i) { + long userId = entry.shareUserIds.get(i); + if (entry.wouldBeVideo()) { + SendMessagesHelper.prepareSendingVideo(AccountInstance.getInstance(currentAccount), path, null, userId, null, null, null, captionEntities, 0, null, !entry.silent, entry.scheduleDate, false, false, caption); + } else { + SendMessagesHelper.prepareSendingPhoto(AccountInstance.getInstance(currentAccount), path, null, null, userId, null, null, null, captionEntities, null, null, 0, null, null, !entry.silent, entry.scheduleDate, false, caption /* TODO: */); + } + } + putMessages = true; + } + + public void cancel() { + canceled = true; + if (entry.wouldBeVideo()) { + MediaController.getInstance().cancelVideoConvert(messageObject); + } + FileLoader.getInstance(currentAccount).cancelFileUpload(path, false); + if (currentRequest >= 0) { + ConnectionsManager.getInstance(currentAccount).cancelRequest(currentRequest, true); + } + cleanup(); + } + + public boolean isCloseFriends() { + return isCloseFriends; + } + } + + private final HashMap[] storiesLists = new HashMap[2]; + + @NonNull + public StoriesList getStoriesList(long userId, int type) { + return getStoriesList(userId, type, true); + } + + private StoriesList getStoriesList(long userId, int type, boolean createIfNotExist) { + if (storiesLists[type] == null) { + storiesLists[type] = new HashMap<>(); + } + StoriesList list = storiesLists[type].get(userId); + if (list == null && createIfNotExist) { + storiesLists[type].put(userId, list = new StoriesList(currentAccount, userId, type, this::destroyStoryList)); + } + return list; + } + + public void updateStoriesInLists(long userId, List storyItems) { + StoriesList pinned = getStoriesList(userId, StoriesList.TYPE_PINNED, false); + StoriesList archived = getStoriesList(userId, StoriesList.TYPE_ARCHIVE, false); + if (pinned != null) { + pinned.updateStories(storyItems); + } + if (archived != null) { + archived.updateStories(storyItems); + } + } + + public void updateDeletedStoriesInLists(long userId, List storyItems) { + StoriesList pinned = getStoriesList(userId, StoriesList.TYPE_PINNED, false); + StoriesList archived = getStoriesList(userId, StoriesList.TYPE_ARCHIVE, false); + if (pinned != null) { + pinned.updateDeletedStories(storyItems); + } + if (archived != null) { + archived.updateDeletedStories(storyItems); + } + } + + public void destroyStoryList(StoriesList list) { + if (storiesLists[list.type] != null) { + storiesLists[list.type].remove(list.userId); + } + } + + public static class StoriesList { + + private static HashMap lastLoadTime; + + private int maxLinkId = 0; + private final ArrayList links = new ArrayList<>(); + public int link() { + final int id = maxLinkId++; + links.add(id); + AndroidUtilities.cancelRunOnUIThread(destroyRunnable); + return id; + } + public void unlink(int id) { + links.remove((Integer) id); + if (links.isEmpty()) { + AndroidUtilities.cancelRunOnUIThread(destroyRunnable); + AndroidUtilities.runOnUIThread(destroyRunnable, 1000 * 60 * 5); + } + } + + public static final int TYPE_PINNED = 0; + public static final int TYPE_ARCHIVE = 1; + + public final int currentAccount; + public final long userId; + public final int type; + + public final HashMap> groupedByDay = new HashMap<>(); + + public final ArrayList messageObjects = new ArrayList<>(); + private final HashMap messageObjectsMap = new HashMap<>(); + + private final SortedSet cachedObjects = new TreeSet<>(Comparator.reverseOrder()); + private final SortedSet loadedObjects = new TreeSet<>(Comparator.reverseOrder()); + + private boolean showPhotos = true; + private boolean showVideos = true; + + public void updateFilters(boolean photos, boolean videos) { + this.showPhotos = photos; + this.showVideos = videos; + this.fill(true); + } + + public boolean isOnlyCache() { + return loadedObjects.isEmpty() && canLoad(); + } + + public boolean showPhotos() { + return showPhotos; + } + + public boolean showVideos() { + return showVideos; + } + + private final ArrayList tempArr = new ArrayList<>(); + + private final Runnable notify = () -> { + NotificationCenter.getInstance(StoriesList.this.currentAccount).postNotificationName(NotificationCenter.storiesListUpdated, StoriesList.this); + }; + + public void fill(boolean notify) { + fill(this.messageObjects, showPhotos, showVideos); + String s = ""; + for (int i = 0; i < this.messageObjects.size(); ++i) { + long id = this.messageObjects.get(i).getId(); + if (i > 0) s += ", "; + s += id; + } + if (notify) { + AndroidUtilities.cancelRunOnUIThread(this.notify); + AndroidUtilities.runOnUIThread(this.notify); + } + } + + private void fill(ArrayList arrayList, boolean showPhotos, boolean showVideos) { + tempArr.clear(); + int minId = Integer.MAX_VALUE; + for (int id : loadedObjects) { + MessageObject msg = messageObjectsMap.get(id); + if (filter(msg, showPhotos, showVideos)) { + tempArr.add(msg); + } + if (id < minId) { + minId = id; + } + } + if (!done) { + Iterator i = cachedObjects.iterator(); + while (i.hasNext() && (totalCount == -1 || tempArr.size() < totalCount)) { + int id = i.next(); + if (minId == Integer.MAX_VALUE || id < minId) { + MessageObject msg = messageObjectsMap.get(id); + if (filter(msg, showPhotos, showVideos)) { + tempArr.add(msg); + } + } + } + } + arrayList.clear(); + arrayList.addAll(tempArr); + } + + private boolean filter(MessageObject msg, boolean photos, boolean videos) { + return msg != null && msg.isStory() && (photos && msg.isPhoto() || videos && msg.isVideo() || msg.storyItem.media instanceof TLRPC.TL_messageMediaUnsupported); + } + + private boolean done; + private int totalCount = -1; + private boolean preloading, loading; + private boolean invalidateAfterPreload; + private boolean error; + private Runnable destroyRunnable; + + private Utilities.CallbackReturn toLoad; + + private StoriesList(int currentAccount, long userId, int type, Utilities.Callback destroy) { + this.currentAccount = currentAccount; + this.userId = userId; + this.type = type; + this.destroyRunnable = () -> destroy.run(this); + + preloadCache(); + } + + private void preloadCache() { + if (preloading || loading || error) { + return; + } + + preloading = true; + final MessagesStorage storage = MessagesStorage.getInstance(currentAccount); + storage.getStorageQueue().postRunnable(() -> { + SQLiteCursor cursor = null; + ArrayList cacheResult = new ArrayList<>(); + try { + SQLiteDatabase database = storage.getDatabase(); + if (type == TYPE_PINNED) { + cursor = database.queryFinalized(String.format(Locale.US, "SELECT data FROM profile_stories WHERE dialog_id = %d ORDER BY story_id DESC", userId)); + } else { + cursor = database.queryFinalized("SELECT data FROM archived_stories ORDER BY story_id DESC"); + } + while (cursor.next()) { + NativeByteBuffer data = cursor.byteBufferValue(0); + if (data != null) { + TLRPC.StoryItem storyItem = TLRPC.StoryItem.TLdeserialize(data, data.readInt32(true), true); + storyItem.dialogId = userId; + storyItem.messageId = storyItem.id; + MessageObject msg = new MessageObject(currentAccount, storyItem); + msg.generateThumbs(false); + cacheResult.add(msg); + data.reuse(); + } + } + cursor.dispose(); + } catch (Throwable e) { + storage.checkSQLException(e); + } finally { + if (cursor != null) { + cursor.dispose(); + cursor = null; + } + } + + AndroidUtilities.runOnUIThread(() -> { + preloading = false; + if (invalidateAfterPreload) { + invalidateAfterPreload = false; + toLoad = null; + invalidateCache(); + return; + } + + cachedObjects.clear(); + for (int i = 0; i < cacheResult.size(); ++i) { + pushObject(cacheResult.get(i), true); + } + fill(false); + + if (toLoad != null) { + toLoad.run(0); + toLoad = null; + } + + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.storiesListUpdated, StoriesList.this); + }); + }); + } + + private void pushObject(MessageObject messageObject, boolean cachedOrLoaded) { + if (messageObject == null) { + return; + } + messageObjectsMap.put(messageObject.getId(), messageObject); + (cachedOrLoaded ? cachedObjects : loadedObjects).add(messageObject.getId()); + + long id = day(messageObject); + TreeSet group = groupedByDay.get(id); + if (group == null) { + groupedByDay.put(id, group = new TreeSet<>(Comparator.reverseOrder())); + } + group.add(messageObject.getId()); + } + + private boolean removeObject(int storyId, boolean removeFromCache) { + MessageObject messageObject = messageObjectsMap.remove(storyId); + if (removeFromCache) { + cachedObjects.remove(storyId); + } + loadedObjects.remove(storyId); + if (messageObject != null) { + long id = day(messageObject); + SortedSet group = groupedByDay.get(id); + if (group != null) { + group.remove(storyId); + if (group.isEmpty()) { + groupedByDay.remove(id); + } + } + return true; + } + return false; + } + + public static long day(MessageObject messageObject) { + if (messageObject == null) { + return 0; + } + final long date = messageObject.messageOwner.date; + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(date * 1000L); + final int year = calendar.get(Calendar.YEAR); + final int month = calendar.get(Calendar.MONTH); + final int day = calendar.get(Calendar.DAY_OF_MONTH); + return year * 10000L + month * 100L + day; + } + + public SortedSet getStoryDayGroup(MessageObject messageObject) { + return groupedByDay.get(day(messageObject)); + } + + public ArrayList> getDays() { + final ArrayList keys = new ArrayList<>(groupedByDay.keySet()); + Collections.sort(keys, (a, b) -> (int) (b - a)); + final ArrayList> days = new ArrayList<>(); + for (Long key : keys) { + TreeSet storyIds = groupedByDay.get(key); + if (storyIds != null) { + days.add(new ArrayList<>(storyIds)); + } + } + return days; + } + + public void invalidateCache() { + if (preloading) { + invalidateAfterPreload = true; + return; + } + + resetCanLoad(); + + final MessagesStorage storage = MessagesStorage.getInstance(currentAccount); + storage.getStorageQueue().postRunnable(() -> { + try { + SQLiteDatabase database = storage.getDatabase(); + if (type == TYPE_PINNED) { + database.executeFast(String.format(Locale.US, "DELETE FROM profile_stories WHERE dialog_id = %d", userId)).stepThis().dispose(); + } else if (type == TYPE_ARCHIVE) { + database.executeFast("DELETE FROM archived_stories").stepThis().dispose(); + } + } catch (Throwable e) { + storage.checkSQLException(e); + } + + AndroidUtilities.runOnUIThread(() -> { + cachedObjects.clear(); + fill(true); + }); + }); + } + + private boolean saving; + + private void saveCache() { + if (saving) { + return; + } + saving = true; + final MessagesStorage storage = MessagesStorage.getInstance(currentAccount); + storage.getStorageQueue().postRunnable(() -> { + SQLitePreparedStatement state = null; + ArrayList toSave = new ArrayList<>(); + fill(toSave, true, true); + try { + SQLiteDatabase database = storage.getDatabase(); + if (type == TYPE_PINNED) { + database.executeFast(String.format(Locale.US, "DELETE FROM profile_stories WHERE dialog_id = %d", userId)).stepThis().dispose(); + state = database.executeFast("REPLACE INTO profile_stories VALUES(?, ?, ?)"); + } else { + database.executeFast("DELETE FROM archived_stories").stepThis().dispose(); + state = database.executeFast("REPLACE INTO archived_stories VALUES(?, ?)"); + } + + for (int i = 0; i < toSave.size(); ++i) { + MessageObject messageObject = toSave.get(i); + TLRPC.StoryItem storyItem = messageObject.storyItem; + if (storyItem == null) { + continue; + } + + NativeByteBuffer data = new NativeByteBuffer(storyItem.getObjectSize()); + storyItem.serializeToStream(data); + + state.requery(); + if (type == TYPE_PINNED) { + state.bindLong(1, userId); + state.bindInteger(2, storyItem.id); + state.bindByteBuffer(3, data); + } else { + state.bindInteger(1, storyItem.id); + state.bindByteBuffer(2, data); + } + state.step(); + data.reuse(); + } + } catch (Throwable e) { + storage.checkSQLException(e); + } finally { + if (state != null) { + state.dispose(); + state = null; + } + } + + AndroidUtilities.runOnUIThread(() -> { + saving = false; + }); + }); + } + + private boolean canLoad() { + if (lastLoadTime == null) { + return true; + } + final int key = Objects.hash(currentAccount, type, userId); + Long time = lastLoadTime.get(key); + if (time == null) { + return true; + } + return System.currentTimeMillis() - time > 1000L * 60 * 2; + } + + private void resetCanLoad() { + if (lastLoadTime != null) { + lastLoadTime.remove(Objects.hash(currentAccount, type, userId)); + } + } + + public boolean load(boolean force, final int count) { + if (loading || (done || error || !canLoad()) && !force) { + return false; + } + if (preloading) { + toLoad = i -> load(force, count); + return false; + } + + final int offset_id; + TLObject request; + if (type == TYPE_PINNED) { + TLRPC.TL_stories_getPinnedStories req = new TLRPC.TL_stories_getPinnedStories(); + req.user_id = MessagesController.getInstance(currentAccount).getInputUser(userId); + if (!loadedObjects.isEmpty()) { + req.offset_id = offset_id = loadedObjects.last(); + } else { + offset_id = -1; + } + req.limit = count; + request = req; + } else { + TLRPC.TL_stories_getStoriesArchive req = new TLRPC.TL_stories_getStoriesArchive(); + if (!loadedObjects.isEmpty()) { + req.offset_id = offset_id = loadedObjects.last(); + } else { + offset_id = -1; + } + req.limit = count; + request = req; + } + + loading = true; + ConnectionsManager.getInstance(currentAccount).sendRequest(request, (response, err) -> { + if (response instanceof TLRPC.TL_stories_stories) { + ArrayList newMessageObjects = new ArrayList<>(); + TLRPC.TL_stories_stories stories = (TLRPC.TL_stories_stories) response; + for (int i = 0; i < stories.stories.size(); ++i) { + TLRPC.StoryItem storyItem = stories.stories.get(i); + newMessageObjects.add(toMessageObject(storyItem)); + } + AndroidUtilities.runOnUIThread(() -> { + loading = false; + + totalCount = stories.count; + for (int i = 0; i < newMessageObjects.size(); ++i) { + pushObject(newMessageObjects.get(i), false); + } + done = loadedObjects.size() >= totalCount; + if (!done) { + final int loadedFromId = offset_id == -1 ? loadedObjects.first() : offset_id; + final int loadedToId = !loadedObjects.isEmpty() ? loadedObjects.last() : 0; + Iterator i = cachedObjects.iterator(); + while (i.hasNext()) { + int cachedId = i.next(); + if (!loadedObjects.contains(cachedId) && cachedId >= loadedFromId && cachedId <= loadedToId) { + i.remove(); + removeObject(cachedId, false); + } + } + } else { + Iterator i = cachedObjects.iterator(); + while (i.hasNext()) { + int cachedId = i.next(); + if (!loadedObjects.contains(cachedId)) { + i.remove(); + removeObject(cachedId, false); + } + } + } + fill(true); + + if (done) { + if (lastLoadTime == null) { + lastLoadTime = new HashMap<>(); + } + lastLoadTime.put(Objects.hash(currentAccount, type, userId), System.currentTimeMillis()); + } else { + resetCanLoad(); + } + + saveCache(); + }); + } else { + AndroidUtilities.runOnUIThread(() -> { + loading = false; + error = true; + + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.storiesListUpdated, StoriesList.this, false); + }); + } + }); + return true; + } + +// public void invalidate() { +// resetCanLoad(); +// final int wasCount = messageObjects.size(); +// messageObjectsMap.clear(); +// loadedObjects.clear(); +// cachedObjects.clear(); +// invalidateCache(); +// done = false; +// error = false; +// load(true, Utilities.clamp(wasCount, 50, 10)); +// } + + public void updateDeletedStories(List storyItems) { + if (storyItems == null) { + return; + } + boolean changed = false; + for (int i = 0; i < storyItems.size(); ++i) { + TLRPC.StoryItem storyItem = storyItems.get(i); + if (storyItem == null) { + continue; + } + boolean contains = loadedObjects.contains(storyItem.id) || cachedObjects.contains(storyItem.id); + if (contains) { + changed = true; + loadedObjects.remove(storyItem.id); + cachedObjects.remove(storyItem.id); + if (totalCount != -1) { + totalCount--; + } + } + removeObject(storyItem.id, true); + } + if (changed) { + fill(true); + saveCache(); + } + } + + public void updateStories(List storyItems) { + if (storyItems == null) { + return; + } + boolean changed = false; + for (int i = 0; i < storyItems.size(); ++i) { + TLRPC.StoryItem storyItem = storyItems.get(i); + if (storyItem == null) { + continue; + } + boolean contains = loadedObjects.contains(storyItem.id) || cachedObjects.contains(storyItem.id); + boolean shouldContain = type == TYPE_ARCHIVE ? true : storyItem.pinned; + if (storyItem instanceof TLRPC.TL_storyItemDeleted) { + shouldContain = false; + } + if (contains != shouldContain) { + changed = true; + if (!shouldContain) { + removeObject(storyItem.id, true); + if (totalCount != -1) { + totalCount--; + } + } else { + pushObject(toMessageObject(storyItem), false); + if (totalCount != -1) { + totalCount++; + } + } + } else if (contains && shouldContain) { + MessageObject messageObject = messageObjectsMap.get(storyItem.id); + if (messageObject == null || !equal(messageObject.storyItem, storyItem)) { + messageObjectsMap.put(storyItem.id, toMessageObject(storyItem)); + changed = true; + } + } + } + if (changed) { + fill(true); + saveCache(); + } + } + + public MessageObject findMessageObject(int storyId) { + return messageObjectsMap.get(storyId); + } + + public boolean equal(TLRPC.StoryItem a, TLRPC.StoryItem b) { + if (a == null && b == null) { + return true; + } + if ((a == null) != (b == null)) { + return false; + } + return ( + a == b || + a.id == b.id && + a.media == b.media && + TextUtils.equals(a.caption, b.caption) + ); + } + + private MessageObject toMessageObject(TLRPC.StoryItem storyItem) { + storyItem.dialogId = userId; + storyItem.messageId = storyItem.id; + MessageObject msg = new MessageObject(currentAccount, storyItem); + msg.generateThumbs(false); + return msg; + } + + public boolean isLoading() { + return preloading || loading; + } + + public boolean isFull() { + return done; + } + + public boolean isError() { + return error; + } + + public int getLoadedCount() { + return loadedObjects.size(); + } + + public int getCount() { + if (showVideos && showPhotos) { + if (totalCount < 0) { + return messageObjects.size(); + } + return Math.max(messageObjects.size(), totalCount); + } else { + return messageObjects.size(); + } + } + } + + private final Comparator userStoriesComparator = (o1, o2) -> { + boolean hasUnread1 = hasUnreadStories(o1.user_id); + boolean hasUnread2 = hasUnreadStories(o2.user_id); + if (hasUnread1 == hasUnread2) { + int service1 = UserObject.isService(o1.user_id) ? 1 : 0; + int service2 = UserObject.isService(o2.user_id) ? 1 : 0; + if (service1 == service2) { + int i1 = isPremium(o1.user_id) ? 1 : 0; + int i2 = isPremium(o2.user_id) ? 1 : 0; + if (i1 == i2) { + int date1 = o1.stories.isEmpty() ? 0 : o1.stories.get(o1.stories.size() - 1).date; + int date2 = o2.stories.isEmpty() ? 0 : o2.stories.get(o2.stories.size() - 1).date; + return date2 - date1; + } else { + return i2 - i1; + } + } else { + return service2 - service1; + } + } else { + int i1 = hasUnread1 ? 1 : 0; + int i2 = hasUnread2 ? 1 : 0; + return i2 - i1; + } + }; + + private boolean isPremium(long uid) { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(uid); + if (user == null) { + return false; + } + return user.premium; + } + + final Runnable sortStoriesRunnable; + + public void scheduleSort() { + AndroidUtilities.cancelRunOnUIThread(sortStoriesRunnable); + sortStoriesRunnable.run(); + // AndroidUtilities.runOnUIThread(sortStoriesRunnable, 2000); + } + + public boolean hasOnlySelfStories() { + return hasSelfStories() && (getDialogListStories().isEmpty() || (getDialogListStories().size() == 1 && getDialogListStories().get(0).user_id == UserConfig.getInstance(currentAccount).clientUserId)); + } + + public void sortHiddenStories() { + sortDialogStories(hiddenListStories); + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.storiesUpdated); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesGradientTools.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesGradientTools.java new file mode 100644 index 0000000000..21dffa87e3 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesGradientTools.java @@ -0,0 +1,20 @@ +package org.telegram.ui.Stories; + +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.GradientTools; + +public class StoriesGradientTools extends GradientTools { + + int colorKey1 = Theme.key_voipgroup_overlayGreen1; + int colorKey2 = Theme.key_voipgroup_overlayBlue1; + public StoriesGradientTools() { + isDiagonal = true; + setColors(Theme.getColor(colorKey1), Theme.getColor(colorKey2)); + } + + @Override + protected void updateBounds() { + setColors(Theme.getColor(colorKey1), Theme.getColor(colorKey2)); + super.updateBounds(); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesListPlaceProvider.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesListPlaceProvider.java new file mode 100644 index 0000000000..a18df5b671 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesListPlaceProvider.java @@ -0,0 +1,221 @@ +package org.telegram.ui.Stories; + +import android.graphics.Canvas; +import android.view.View; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.MessageObject; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.SharedConfig; +import org.telegram.messenger.UserConfig; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Cells.ChatActionCell; +import org.telegram.ui.Cells.ChatMessageCell; +import org.telegram.ui.Cells.DialogCell; +import org.telegram.ui.Cells.ProfileSearchCell; +import org.telegram.ui.Cells.ReactedUserHolderView; +import org.telegram.ui.Cells.SharedPhotoVideoCell2; +import org.telegram.ui.Cells.UserCell; +import org.telegram.ui.Components.BlurredRecyclerView; +import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.Components.SharedMediaLayout; + +public class StoriesListPlaceProvider implements StoryViewer.PlaceProvider { + + private final RecyclerListView recyclerListView; + int[] clipPoint = new int[2]; + private boolean isHiddenArchive; + + public static StoriesListPlaceProvider of(RecyclerListView recyclerListView) { + return of(recyclerListView, false); + } + + public static StoriesListPlaceProvider of(RecyclerListView recyclerListView, boolean hiddenArchive) { + return new StoriesListPlaceProvider(recyclerListView, hiddenArchive); + } + + public StoriesListPlaceProvider(RecyclerListView recyclerListView, boolean hiddenArchive) { + this.recyclerListView = recyclerListView; + this.isHiddenArchive = hiddenArchive; + } + + @Override + public void preLayout(long currentDialogId, int messageId, Runnable r) { + if (recyclerListView.getParent() instanceof DialogStoriesCell) { + DialogStoriesCell dilogsCell = (DialogStoriesCell) recyclerListView.getParent(); + if (dilogsCell.scrollTo(currentDialogId)) { + dilogsCell.afterNextLayout(r); + } else { + r.run(); + } + } else { + if (isHiddenArchive) { + MessagesController.getInstance(UserConfig.selectedAccount).getStoriesController().sortHiddenStories(); + } + r.run(); + } + } + + public boolean findView(long dialogId, int messageId, int storyId, int type, StoryViewer.TransitionViewHolder holder) { + holder.view = null; + holder.avatarImage = null; + holder.storyImage = null; + holder.drawAbove = null; + + if (recyclerListView == null) { + return false; + } + + DialogStoriesCell dialogStoriesCell = null; + if (recyclerListView.getParent() instanceof DialogStoriesCell) { + dialogStoriesCell = (DialogStoriesCell) recyclerListView.getParent(); + } + RecyclerListView listView = recyclerListView; + if (dialogStoriesCell != null && !dialogStoriesCell.isExpanded()) { + listView = dialogStoriesCell.listViewMini; + } + for (int i = 0; i < listView.getChildCount(); i++) { + View child = listView.getChildAt(i); + + if (child instanceof DialogStoriesCell.StoryCell) { + DialogStoriesCell.StoryCell cell = (DialogStoriesCell.StoryCell) child; + + if (cell.dialogId == dialogId) { + holder.view = child; + holder.avatarImage = cell.avatarImage; + holder.params = cell.params; + holder.radialProgressUpload = cell.radialProgress; + DialogStoriesCell storiesCell = (DialogStoriesCell) cell.getParent().getParent(); + holder.clipParent = storiesCell; + holder.clipTop = holder.clipBottom = 0; + // updateClip(holder); + return true; + } + } else if (child instanceof DialogCell) { + DialogCell cell = (DialogCell) child; + if (cell.getDialogId() == dialogId || (isHiddenArchive && cell.isDialogFolder())) { + holder.view = child; + holder.params = cell.storyParams; + holder.avatarImage = cell.avatarImage; + holder.clipParent = (View) cell.getParent(); + if (isHiddenArchive) { + holder.crossfadeToAvatarImage = cell.avatarImage; + } + updateClip(holder); + return true; + } + } else if (child instanceof ChatMessageCell) { + ChatMessageCell cell = (ChatMessageCell) child; + if (cell.getMessageObject().getId() == messageId) { + holder.view = child; + if (type == 1 || type == 2) { + holder.storyImage = cell.getPhotoImage(); + } else { + holder.storyImage = cell.replyImageReceiver; + } + holder.clipParent = (View) cell.getParent(); + updateClip(holder); + return true; + } + } else if (child instanceof ChatActionCell) { + ChatActionCell cell = (ChatActionCell) child; + if (cell.getMessageObject().getId() == messageId) { + holder.view = child; + TLRPC.StoryItem storyItem = cell.getMessageObject().messageOwner.media.storyItem; + if (storyItem.noforwards) { + holder.avatarImage = cell.getPhotoImage(); + } else { + holder.storyImage = cell.getPhotoImage(); + } + holder.clipParent = (View) cell.getParent(); + updateClip(holder); + return true; + } + } else if (child instanceof SharedPhotoVideoCell2) { + SharedPhotoVideoCell2 cell = (SharedPhotoVideoCell2) child; + MessageObject msg = cell.getMessageObject(); + if ( + cell.getStyle() == SharedPhotoVideoCell2.STYLE_CACHE && cell.storyId == storyId || + msg != null && msg.isStory() && msg.getId() == storyId && msg.storyItem.dialogId == dialogId + ) { + final RecyclerListView.FastScroll fastScroll = listView.getFastScroll(); + final int[] loc = new int[2]; + if (fastScroll != null) { + fastScroll.getLocationInWindow(loc); + } + holder.view = child; + holder.storyImage = cell.imageReceiver; + holder.drawAbove = (canvas, bounds, alpha) -> { + cell.drawDuration(canvas, bounds, alpha); + if (fastScroll != null && fastScroll.isVisible && fastScroll.getVisibility() == View.VISIBLE) { + canvas.saveLayerAlpha(0, 0, canvas.getWidth(), canvas.getHeight(), (int) (0xFF * alpha), Canvas.ALL_SAVE_FLAG); + canvas.translate(loc[0], loc[1]); + fastScroll.draw(canvas); + canvas.restore(); + } + }; + holder.clipParent = (View) cell.getParent(); + updateClip(holder); + return true; + } + } else if (child instanceof UserCell) { + UserCell cell = (UserCell) child; + if (cell.getDialogId() == dialogId) { + holder.view = cell.avatarImageView; + holder.params = cell.storyParams; + holder.avatarImage = cell.avatarImageView.getImageReceiver(); + holder.clipParent = (View) cell.getParent(); + updateClip(holder); + return true; + } + } else if (child instanceof ReactedUserHolderView) { + ReactedUserHolderView cell = (ReactedUserHolderView) child; + if (cell.dialogId == dialogId) { + holder.view = cell.avatarView; + holder.params = cell.params; + holder.avatarImage = cell.avatarView.getImageReceiver(); + holder.clipParent = (View) cell.getParent(); + updateClip(holder); + return true; + } + } else if (child instanceof ProfileSearchCell) { + ProfileSearchCell cell = (ProfileSearchCell) child; + if (cell.getDialogId() == dialogId) { + holder.view = cell; + holder.params = cell.avatarStoryParams; + holder.avatarImage = cell.avatarImage; + holder.clipParent = (View) cell.getParent(); + updateClip(holder); + return true; + } + } + } + return false; + } + + private void updateClip(StoryViewer.TransitionViewHolder holder) { + if (holder.clipParent == null) { + return; + } + if (holder.clipParent instanceof ClippedView) { + ((ClippedView) holder.clipParent).updateClip(clipPoint); + holder.clipTop = clipPoint[0]; + holder.clipBottom = clipPoint[1]; + } else if (holder.clipParent instanceof BlurredRecyclerView) { + holder.clipTop = ((BlurredRecyclerView) holder.clipParent).blurTopPadding; + holder.clipBottom = holder.clipParent.getMeasuredHeight() - holder.clipParent.getPaddingBottom(); + } else { + holder.clipTop = holder.clipParent.getPaddingTop(); + holder.clipBottom = holder.clipParent.getMeasuredHeight() - holder.clipParent.getPaddingBottom(); + } + } + + public interface ClippedView { + void updateClip(int[] clip); + } + + public interface AvatarOverlaysView { + boolean drawAvatarOverlays(Canvas canvas); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesStorage.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesStorage.java new file mode 100644 index 0000000000..ec44cd2557 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesStorage.java @@ -0,0 +1,768 @@ +package org.telegram.ui.Stories; + +import android.os.Build; +import android.text.TextUtils; +import android.util.Log; + +import androidx.collection.LongSparseArray; + +import com.google.android.exoplayer2.util.Consumer; + +import org.telegram.SQLite.SQLiteCursor; +import org.telegram.SQLite.SQLiteDatabase; +import org.telegram.SQLite.SQLiteException; +import org.telegram.SQLite.SQLitePreparedStatement; +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.BuildVars; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.MessageObject; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.MessagesStorage; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.Utilities; +import org.telegram.messenger.support.LongSparseIntArray; +import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.NativeByteBuffer; +import org.telegram.tgnet.RequestDelegate; +import org.telegram.tgnet.TLObject; +import org.telegram.tgnet.TLRPC; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; + +//TODO stories +//support deleting story files +public class StoriesStorage { + + private static final int EXPIRE_AFTER = 60 * 60 * 24;//one day + int currentAccount; + MessagesStorage storage; + + public StoriesStorage(int currentAccount) { + this.currentAccount = currentAccount; + storage = MessagesStorage.getInstance(currentAccount); + + } + + public void getAllStories(Consumer consumer) { + storage.getStorageQueue().postRunnable(() -> { + SQLiteDatabase database = storage.getDatabase(); + SQLiteCursor cursor = null; + ArrayList userStoriesArray = new ArrayList<>(); + ArrayList usersToLoad = new ArrayList<>(); + ArrayList chatsToLoad = new ArrayList<>(); + try { + cursor = database.queryFinalized("SELECT dialog_id, max_read FROM stories_counter"); + LongSparseIntArray dialogsCounter = new LongSparseIntArray(); + + while (cursor.next()) { + long dialogId = cursor.longValue(0); + int maxReadId = cursor.intValue(1); + dialogsCounter.put(dialogId, maxReadId); + if (dialogId > 0) { + usersToLoad.add(dialogId); + } else { + chatsToLoad.add(dialogId); + } + } + cursor.dispose(); + cursor = null; + + + for (int i = 0; i < dialogsCounter.size(); i++) { + long dialogId = dialogsCounter.keyAt(i); + int maxReadId = dialogsCounter.valueAt(i); + cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, local_path, local_thumb_path FROM stories WHERE dialog_id = %d", dialogId)); + ArrayList storyItems = new ArrayList<>(); + while (cursor.next()) { + NativeByteBuffer data = cursor.byteBufferValue(0); + String path = cursor.stringValue(1); + String firstFramePath = cursor.stringValue(2); + if (data != null) { + TLRPC.StoryItem storyItem = TLRPC.StoryItem.TLdeserialize(data, data.readInt32(true), true); + storyItem.attachPath = path; + storyItem.firstFramePath = firstFramePath; + storyItems.add(storyItem); + data.reuse(); + } + } + cursor.dispose(); + cursor = null; + TLRPC.TL_userStories userStories; + userStories = new TLRPC.TL_userStories(); + userStories.stories = storyItems; + userStories.max_read_id = maxReadId; + userStories.user_id = dialogId; + userStoriesArray.add(userStories); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (cursor != null) { + cursor.dispose(); + } + } + TLRPC.TL_stories_allStories storiesResponse = new TLRPC.TL_stories_allStories(); + storiesResponse.user_stories = userStoriesArray; + storiesResponse.users = storage.getUsers(usersToLoad); + for (int i = 0; i < storiesResponse.user_stories.size(); i++) { + TLRPC.TL_userStories userStories = storiesResponse.user_stories.get(i); + checkExpiredStories(userStories.user_id, userStories.stories); + if (userStories.stories.isEmpty()) { + storiesResponse.user_stories.remove(i); + i--; + } + Collections.sort(userStories.stories, StoriesController.storiesComparator); + } + Collections.sort(storiesResponse.user_stories, Comparator.comparingInt(o -> -o.stories.get(o.stories.size() - 1).date)); + + AndroidUtilities.runOnUIThread(() -> consumer.accept(storiesResponse)); + }); + } + + private void checkExpiredStories(long dialogId, ArrayList stories) { + int currentTime = ConnectionsManager.getInstance(currentAccount).getCurrentTime(); + SQLiteDatabase database = storage.getDatabase(); + ArrayList storiesToDeleteIds = null; + ArrayList storiesToDelete = null; + for (int i = 0; i < stories.size(); i++) { + TLRPC.StoryItem storyItem = stories.get(i); + if (currentTime - stories.get(i).date > EXPIRE_AFTER) { + if (storiesToDeleteIds == null) { + storiesToDeleteIds = new ArrayList<>(); + storiesToDelete = new ArrayList<>(); + } + storiesToDeleteIds.add(storyItem.id); + storiesToDelete.add(storyItem); + stories.remove(i); + i--; + } + } + + if (storiesToDelete != null) { + String ids = TextUtils.join(", ", storiesToDeleteIds); + try { + database.executeFast(String.format(Locale.US, "DELETE FROM stories WHERE dialog_id = %d AND story_id IN (%s)", dialogId, ids)).stepThis().dispose(); + } catch (SQLiteException e) { + e.printStackTrace(); + } + ArrayList finalStoriesToDelete = storiesToDelete; + } + + } + + public void putStoriesInternal(long dialogId, TLRPC.TL_userStories userStories) { + SQLiteDatabase database = storage.getDatabase(); + try { + if (userStories != null) { + ArrayList storyItems = userStories.stories; + SQLitePreparedStatement state = database.executeFast("REPLACE INTO stories VALUES(?, ?, ?, ?, ?)"); + for (int i = 0; i < storyItems.size(); i++) { + state.requery(); + TLRPC.StoryItem storyItem = storyItems.get(i); + if (dialogId == UserConfig.getInstance(currentAccount).getClientUserId()) { + SQLiteCursor cursor = database.queryFinalized(String.format(Locale.US, "SELECT local_path, local_thumb_path FROM stories WHERE dialog_id = %d AND story_id = %d", dialogId, storyItem.id)); + if (cursor.next()) { + storyItem.attachPath = cursor.stringValue(1); + storyItem.firstFramePath = cursor.stringValue(2); + } + cursor.dispose(); + } + if (storyItem instanceof TLRPC.TL_storyItemDeleted) { + FileLog.e("try write deleted story"); + continue; + } + state.bindLong(1, dialogId); + state.bindLong(2, storyItem.id); + + NativeByteBuffer data = new NativeByteBuffer(storyItem.getObjectSize()); + storyItem.serializeToStream(data); + state.bindByteBuffer(3, data); + if (storyItem.attachPath == null) { + state.bindNull(4); + } else { + state.bindString(4, storyItem.attachPath); + } + if (storyItem.firstFramePath == null) { + state.bindNull(5); + } else { + state.bindString(5, storyItem.firstFramePath); + } + state.step(); + data.reuse(); + } + state.dispose(); + database.executeFast(String.format(Locale.US, "REPLACE INTO stories_counter VALUES(%d, %d, %d)", dialogId, 0, userStories.max_read_id)).stepThis().dispose(); + } + } catch (Exception e) { + FileLog.e(e); + } + } + + public void putStoryInternal(long dialogId, TLRPC.StoryItem storyItem) { + SQLiteDatabase database = storage.getDatabase(); + try { + SQLitePreparedStatement state = database.executeFast("REPLACE INTO stories VALUES(?, ?, ?, ?, ?)"); + if (dialogId == UserConfig.getInstance(currentAccount).getClientUserId()) { + SQLiteCursor cursor = database.queryFinalized(String.format(Locale.US, "SELECT local_path, local_thumb_path FROM stories WHERE dialog_id = %d AND story_id = %d", dialogId, storyItem.id)); + if (cursor.next()) { + storyItem.attachPath = cursor.stringValue(1); + storyItem.firstFramePath = cursor.stringValue(2); + } + cursor.dispose(); + } + if (storyItem instanceof TLRPC.TL_storyItemDeleted) { + FileLog.e("putStoryInternal: try write deleted story"); + return; + } + state.bindLong(1, dialogId); + state.bindLong(2, storyItem.id); + + NativeByteBuffer data = new NativeByteBuffer(storyItem.getObjectSize()); + storyItem.serializeToStream(data); + state.bindByteBuffer(3, data); + if (storyItem.attachPath == null) { + state.bindNull(4); + } else { + state.bindString(4, storyItem.attachPath); + } + if (storyItem.firstFramePath == null) { + state.bindNull(5); + } else { + state.bindString(5, storyItem.firstFramePath); + } + state.step(); + data.reuse(); + state.dispose(); + } catch (Exception e) { + FileLog.e(e); + } + } + + public void saveAllStories(ArrayList user_stories, boolean isNext, boolean hidden, Runnable callback) { + storage.getStorageQueue().postRunnable(() -> { + SQLiteDatabase database = storage.getDatabase(); + for (int i = 0; i < user_stories.size(); i++) { + TLRPC.TL_userStories stories = user_stories.get(i); + fillSkippedStories(stories.user_id, stories); + } + if (!isNext) { + try { + SQLiteCursor cursor = database.queryFinalized("SELECT dialog_id FROM stories"); + + ArrayList dialogsToDelete = new ArrayList<>(); + while (cursor.next()) { + long dialogId = cursor.longValue(1); + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); + if (user == null) { + user = MessagesStorage.getInstance(currentAccount).getUser(dialogId); + } + if (user == null || user.stories_hidden == hidden && !dialogsToDelete.contains(dialogId)) { + dialogsToDelete.add(dialogId); + } + } + if (BuildVars.LOGS_ENABLED) { + FileLog.d("StoriesStorage delete dialogs " + TextUtils.join(",", dialogsToDelete)); + } + database.executeFast(String.format(Locale.US, "DELETE FROM stories WHERE dialog_id IN(%s)", TextUtils.join(",", dialogsToDelete))).stepThis().dispose(); + } catch (Throwable e) { + storage.checkSQLException(e); + } + } + for (int i = 0; i < user_stories.size(); i++) { + TLRPC.TL_userStories stories = user_stories.get(i); + putStoriesInternal(stories.user_id, stories); + } + if (callback != null) { + AndroidUtilities.runOnUIThread(callback); + } + }); + } + + private void fillSkippedStories(long user_id, TLRPC.TL_userStories userStories) { + try { + if (userStories != null) { + ArrayList storyItems = userStories.stories; + for (int i = 0; i < storyItems.size(); i++) { + TLRPC.StoryItem storyItem = storyItems.get(i); + if (storyItem instanceof TLRPC.TL_storyItemSkipped) { + TLRPC.StoryItem storyInDatabase = getStoryInternal(user_id, storyItems.get(i).id); + if (storyInDatabase instanceof TLRPC.TL_storyItem) { + storyItems.set(i, storyInDatabase); + } + } + } + } + } catch (Exception e) { + FileLog.e(e); + } + } + + private TLRPC.StoryItem getStoryInternal(long user_id, int storyId) { + SQLiteDatabase database = storage.getDatabase(); + SQLiteCursor cursor = null; + TLRPC.StoryItem storyItem = null; + try { + cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, local_path, local_thumb_path FROM stories WHERE dialog_id = %d AND story_id = %d", user_id, storyId)); + + if (cursor.next()) { + NativeByteBuffer data = cursor.byteBufferValue(0); + String path = cursor.stringValue(1); + String thumbPath = cursor.stringValue(2); + if (data != null) { + storyItem = TLRPC.StoryItem.TLdeserialize(data, data.readInt32(true), true); + storyItem.dialogId = user_id; + storyItem.attachPath = path; + storyItem.firstFramePath = thumbPath; + data.reuse(); + } + } + cursor.dispose(); + } catch (SQLiteException e) { + e.printStackTrace(); + } + return storyItem; + } + + + public void getStories(long dialogId, Consumer consumer) { + storage.getStorageQueue().postRunnable(() -> { + TLRPC.TL_userStories finalUserStories = getStoriesInternal(dialogId); + AndroidUtilities.runOnUIThread(() -> consumer.accept(finalUserStories)); + }); + } + + private TLRPC.TL_userStories getStoriesInternal(long dialogId) { + SQLiteDatabase database = storage.getDatabase(); + SQLiteCursor cursor = null; + TLRPC.TL_userStories userStories = null; + try { + cursor = database.queryFinalized(String.format(Locale.US, "SELECT count, max_read FROM stories_counter WHERE dialog_id = %d", dialogId)); + int count = 0; + int maxReadId = 0; + while (cursor.next()) { + count = cursor.intValue(1); + maxReadId = cursor.intValue(2); + } + cursor.dispose(); + cursor = null; + + cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, local_path, local_thumb_path FROM stories WHERE dialog_id = %d", dialogId)); + ArrayList storyItems = new ArrayList<>(); + while (cursor.next()) { + NativeByteBuffer data = cursor.byteBufferValue(0); + String path = cursor.stringValue(1); + String thumbPath = cursor.stringValue(2); + if (data != null) { + TLRPC.StoryItem storyItem = TLRPC.StoryItem.TLdeserialize(data, data.readInt32(true), true); + storyItem.attachPath = path; + storyItem.firstFramePath = thumbPath; + storyItems.add(storyItem); + } + data.reuse(); + } + cursor.dispose(); + cursor = null; + userStories = new TLRPC.TL_userStories(); + userStories.max_read_id = maxReadId; + userStories.stories = storyItems; + userStories.user_id = dialogId; + + } catch (Exception e) { + FileLog.e(e); + } finally { + if (cursor != null) { + cursor.dispose(); + } + } + return userStories; + } + + public void updateStoryItem(long dialogId, TLRPC.StoryItem storyItem) { + storage.getStorageQueue().postRunnable(() -> { + updateStoryItemInternal(dialogId, storyItem); + }); + } + + private void updateStoryItemInternal(long dialogId, TLRPC.StoryItem storyItem) { + if (storyItem instanceof TLRPC.TL_storyItemDeleted) { + FileLog.e("StoriesStorage: try write deleted story"); + } + if (StoriesUtilities.isExpired(currentAccount, storyItem)) { + FileLog.e("StoriesStorage: try write expired story"); + } + SQLiteDatabase database = storage.getDatabase(); + SQLitePreparedStatement state; + try { + String attachPath = storyItem.attachPath; + String thumbPath = storyItem.firstFramePath; + if (dialogId == UserConfig.getInstance(currentAccount).getClientUserId()) { + SQLiteCursor cursor = database.queryFinalized(String.format(Locale.US, "SELECT local_path, local_thumb_path FROM stories WHERE dialog_id = %d AND story_id = %d", dialogId, storyItem.id)); + if (cursor.next()) { + attachPath = cursor.stringValue(1); + thumbPath = cursor.stringValue(2); + } + cursor.dispose(); + } + state = database.executeFast("REPLACE INTO stories VALUES(?, ?, ?, ?, ?)"); + state.requery(); + + state.bindLong(1, dialogId); + state.bindLong(2, storyItem.id); + + NativeByteBuffer data = new NativeByteBuffer(storyItem.getObjectSize()); + storyItem.serializeToStream(data); + state.bindByteBuffer(3, data); + if (attachPath == null) { + state.bindNull(4); + } else { + state.bindString(4, attachPath); + } + if (thumbPath == null) { + state.bindNull(5); + } else { + state.bindString(5, thumbPath); + } + state.step(); + data.reuse(); + } catch (Exception e) { + FileLog.e(e); + } + } + + public void updateMaxReadId(long dialogId, int max_read_id) { + TLRPC.UserFull userFull = MessagesController.getInstance(currentAccount).getUserFull(dialogId); + if (userFull != null && userFull.stories != null) { + userFull.stories.max_read_id = max_read_id; + storage.updateUserInfo(userFull, false); + } + storage.getStorageQueue().postRunnable(() -> { + SQLiteDatabase database = storage.getDatabase(); + try { + database.executeFast(String.format(Locale.US, "REPLACE INTO stories_counter VALUES(%d, 0, %d)", dialogId, max_read_id)).stepThis().dispose(); + } catch (Throwable e) { + storage.checkSQLException(e); + } + }); + } + + public void processUpdate(TLRPC.TL_updateStory updateStory) { + storage.getStorageQueue().postRunnable(() -> { + SQLiteDatabase database = storage.getDatabase(); + SQLiteCursor cursor = null; + try { + long dialogId = updateStory.user_id; + int count = 0; + int storyId = updateStory.story.id; + boolean storyExist = false; + if (updateStory.story instanceof TLRPC.TL_storyItemDeleted) { + cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, local_path, local_thumb_path FROM stories WHERE dialog_id = %d AND story_id = %d", dialogId, storyId)); + if (cursor.next()) { + NativeByteBuffer data = cursor.byteBufferValue(0); + String path = cursor.stringValue(1); + String thumbPath = cursor.stringValue(2); + if (data != null) { + TLRPC.StoryItem storyItem = TLRPC.StoryItem.TLdeserialize(data, data.readInt32(true), true); + storyItem.attachPath = path; + storyItem.firstFramePath = thumbPath; + data.reuse(); + } + storyExist = true; + } + cursor.dispose(); + database.executeFast(String.format(Locale.US, "DELETE FROM stories WHERE dialog_id = %d AND story_id = %d", dialogId, storyId)).stepThis().dispose(); + if (storyExist) { + count--; + } + } else if (updateStory.story instanceof TLRPC.TL_storyItem) { + updateStoryItemInternal(dialogId, updateStory.story); + cursor = database.queryFinalized(String.format(Locale.US, "SELECT story_id FROM stories WHERE dialog_id = %d AND story_id = %d", dialogId, storyId)); + if (cursor.next()) { + storyExist = true; + } + cursor.dispose(); + if (!storyExist) { + count++; + } + } + cursor = database.queryFinalized("SELECT count, max_read FROM stories_counter WHERE dialog_id = " + dialogId); + int totalCount = 0; + if (cursor.next()) { + totalCount = cursor.intValue(1); + } + cursor.dispose(); + cursor = null; + database.executeFast(String.format(Locale.US, "UPDATE stories_counter SET count = %d WHERE dialog_id = %d", + totalCount + count, + dialogId) + ).stepThis().dispose(); + } catch (Throwable e) { + storage.checkSQLException(e); + } + }); + } + + public void updateStories(TLRPC.TL_userStories currentStories) { + storage.getStorageQueue().postRunnable(() -> { + for (int i = 0; i < currentStories.stories.size(); i++) { + updateStoryItemInternal(currentStories.user_id, currentStories.stories.get(i)); + } + }); + } + + public void deleteStory(long dialogId, int storyId) { + storage.getStorageQueue().postRunnable(() -> { + SQLiteDatabase database = storage.getDatabase(); + try { + database.executeFast(String.format(Locale.US, "DELETE FROM stories WHERE dialog_id = %d AND story_id = %d", dialogId, storyId)).stepThis().dispose(); + } catch (Throwable e) { + storage.checkSQLException(e); + } + }); + } + + public void deleteStories(long dialogId, ArrayList storyIds) { + storage.getStorageQueue().postRunnable(() -> { + SQLiteDatabase database = storage.getDatabase(); + try { + String ids = TextUtils.join(", ", storyIds); + database.executeFast(String.format(Locale.US, "DELETE FROM stories WHERE dialog_id = %d AND story_id IN (%s)", dialogId, ids)).stepThis().dispose(); + } catch (Throwable e) { + storage.checkSQLException(e); + } + }); + } + + //storage queue + public void fillMessagesWithStories(LongSparseArray> messagesWithUnknownStories, Runnable runnable) { + if (runnable == null) { + return; + } + if (messagesWithUnknownStories == null) { + runnable.run(); + return; + } + ArrayList updatedMessages = new ArrayList<>(); + for (int i = 0; i < messagesWithUnknownStories.size(); i++) { + long dialogId = messagesWithUnknownStories.keyAt(i); + ArrayList messageObjects = messagesWithUnknownStories.valueAt(i); + for (int j = 0; j < messageObjects.size(); j++) { + MessageObject messageObject = messageObjects.get(j); + int storyId = getStoryId(messageObject); + TLRPC.StoryItem storyItem = getStoryInternal(dialogId, storyId); + if (storyItem != null && !(storyItem instanceof TLRPC.TL_storyItemSkipped)) { + applyStory(currentAccount, dialogId, messageObject, storyItem); + + updatedMessages.add(messageObject); + messageObjects.remove(j); + j--; + if (messageObjects.isEmpty()) { + messagesWithUnknownStories.removeAt(i); + i--; + } + } + } + } + + updateMessagesWithStories(updatedMessages); + + if (!messagesWithUnknownStories.isEmpty()) { + int requestsCount[] = new int[]{messagesWithUnknownStories.size()}; + for (int i = 0; i < messagesWithUnknownStories.size(); i++) { + long dialogId = messagesWithUnknownStories.keyAt(i); + ArrayList messageObjects = messagesWithUnknownStories.valueAt(i); + TLRPC.TL_stories_getStoriesByID request = new TLRPC.TL_stories_getStoriesByID(); + request.user_id = MessagesController.getInstance(currentAccount).getInputUser(dialogId); + for (int j = 0; j < messageObjects.size(); j++) { + request.id.add(getStoryId(messageObjects.get(j))); + } + ConnectionsManager.getInstance(currentAccount).sendRequest(request, (response, error) -> { + if (response != null) { + TLRPC.TL_stories_stories stories = (TLRPC.TL_stories_stories) response; + for (int j = 0; j < messageObjects.size(); j++) { + MessageObject messageObject = messageObjects.get(j); + boolean found = false; + for (int k = 0; k < stories.stories.size(); k++) { + if (stories.stories.get(k).id == getStoryId(messageObject)) { + applyStory(currentAccount, dialogId, messageObject, stories.stories.get(k)); + found = true; + break; + } + } + if (!found) { + TLRPC.TL_storyItemDeleted storyItemDeleted = new TLRPC.TL_storyItemDeleted(); + storyItemDeleted.id = getStoryId(messageObject); + applyStory(currentAccount, dialogId, messageObject, storyItemDeleted); + } + storage.getStorageQueue().postRunnable(() -> { + updateMessagesWithStories(messageObjects); + }); + } + } + requestsCount[0]--; + if (requestsCount[0] == 0) { + runnable.run(); + } + }); + } + } else { + runnable.run(); + } + } + + public static void applyStory(int currentAccount, long dialogId, MessageObject messageObject, TLRPC.StoryItem storyItem) { + if (messageObject.messageOwner.reply_to instanceof TLRPC.TL_messageReplyStoryHeader && messageObject.messageOwner.reply_to.story_id == storyItem.id) { + messageObject.messageOwner.replyStory = checkExpiredStateLocal(currentAccount, dialogId, storyItem); + } + if (messageObject.type == MessageObject.TYPE_STORY || messageObject.type == MessageObject.TYPE_STORY_MENTION) { + MessageMediaStoryFull mediaStoryFull = new MessageMediaStoryFull(); + mediaStoryFull.user_id = messageObject.messageOwner.media.user_id; + mediaStoryFull.id = messageObject.messageOwner.media.id; + mediaStoryFull.storyItem = checkExpiredStateLocal(currentAccount, dialogId, storyItem); + mediaStoryFull.via_mention = messageObject.messageOwner.media.via_mention; + messageObject.messageOwner.media = mediaStoryFull; + } + if ( + messageObject.messageOwner.media != null && + messageObject.messageOwner.media.webpage != null && + messageObject.messageOwner.media.webpage.attributes != null + ) { + for (int i = 0; i < messageObject.messageOwner.media.webpage.attributes.size(); ++i) { + TLRPC.WebPageAttribute attr = messageObject.messageOwner.media.webpage.attributes.get(i); + if (attr instanceof TLRPC.TL_webPageAttributeStory && ((TLRPC.TL_webPageAttributeStory) attr).id == storyItem.id) { + attr.flags |= 1; + ((TLRPC.TL_webPageAttributeStory) attr).storyItem = checkExpiredStateLocal(currentAccount, dialogId, storyItem); + } + } + } + } + + private static int getStoryId(MessageObject messageObject) { + if (messageObject.type == MessageObject.TYPE_STORY || messageObject.type == MessageObject.TYPE_STORY_MENTION) { + return messageObject.messageOwner.media.id; + } else if ( + messageObject.messageOwner.media != null && + messageObject.messageOwner.media.webpage != null && + messageObject.messageOwner.media.webpage.attributes != null + ) { + for (int i = 0; i < messageObject.messageOwner.media.webpage.attributes.size(); ++i) { + TLRPC.WebPageAttribute attr = messageObject.messageOwner.media.webpage.attributes.get(i); + if (attr instanceof TLRPC.TL_webPageAttributeStory) { + return ((TLRPC.TL_webPageAttributeStory) attr).id; + } + } + } + return messageObject.messageOwner.reply_to.story_id; + } + + public void updateMessagesWithStories(List updatedMessages) { + try { + SQLiteDatabase database = storage.getDatabase(); + if (!updatedMessages.isEmpty()) { + SQLitePreparedStatement state = database.executeFast("UPDATE messages_v2 SET replydata = ? WHERE mid = ? AND uid = ?"); + SQLitePreparedStatement topicState = database.executeFast("UPDATE messages_topics SET replydata = ? WHERE mid = ? AND uid = ?"); + + SQLitePreparedStatement state2 = database.executeFast("UPDATE messages_v2 SET data = ? WHERE mid = ? AND uid = ?"); + SQLitePreparedStatement topicState2 = database.executeFast("UPDATE messages_topics SET data = ? WHERE mid = ? AND uid = ?"); + for (int i = 0; i < updatedMessages.size(); i++) { + MessageObject messageObject = updatedMessages.get(i); + for (int k = 0; k < 2; k++) { + if (messageObject.messageOwner.replyStory != null) { + SQLitePreparedStatement localState = k == 0 ? state : topicState; + if (localState == null) { + continue; + } + NativeByteBuffer data = new NativeByteBuffer(messageObject.messageOwner.replyStory.getObjectSize()); + messageObject.messageOwner.replyStory.serializeToStream(data); + localState.requery(); + localState.bindByteBuffer(1, data); + localState.bindInteger(2, messageObject.getId()); + localState.bindLong(3, messageObject.getDialogId()); + localState.step(); + } else { + SQLitePreparedStatement localState = k == 0 ? state2 : topicState2; + if (localState == null) { + continue; + } + NativeByteBuffer data = new NativeByteBuffer(messageObject.messageOwner.getObjectSize()); + messageObject.messageOwner.serializeToStream(data); + localState.requery(); + localState.bindByteBuffer(1, data); + localState.bindInteger(2, messageObject.getId()); + localState.bindLong(3, messageObject.getDialogId()); + localState.step(); + } + } + } + + state.dispose(); + topicState.dispose(); + state2.dispose(); + topicState2.dispose(); + } + } catch (Throwable e) { + storage.checkSQLException(e); + } + } + + public static TLRPC.StoryItem checkExpiredStateLocal(int currentAccount, long dialogId, TLRPC.StoryItem storyItem) { + if (storyItem instanceof TLRPC.TL_storyItemDeleted) { + return storyItem; + } + int currentTime = ConnectionsManager.getInstance(currentAccount).getCurrentTime(); + boolean expired = false; + if (storyItem.expire_date > 0) { + expired = currentTime > storyItem.expire_date; + } else { + expired = currentTime - storyItem.date > EXPIRE_AFTER; + } + if (!storyItem.pinned && expired && dialogId != 0 && dialogId != UserConfig.getInstance(currentAccount).clientUserId) { + TLRPC.TL_storyItemDeleted storyItemDeleted = new TLRPC.TL_storyItemDeleted(); + storyItemDeleted.id = storyItem.id; + return storyItemDeleted; + } + return storyItem; + } + + public void getMaxReadIds(Consumer consumer) { + storage.getStorageQueue().postRunnable(() -> { + SQLiteDatabase database = storage.getDatabase(); + SQLiteCursor cursor = null; + LongSparseIntArray longSparseIntArray = new LongSparseIntArray(); + try { + cursor = database.queryFinalized("SELECT dialog_id, max_read FROM stories_counter"); + while (cursor.next()) { + long dialogId = cursor.longValue(0); + int maxReadId = cursor.intValue(1); + longSparseIntArray.put(dialogId, maxReadId); + } + } catch (Exception e) { + storage.checkSQLException(e); + } + AndroidUtilities.runOnUIThread(() -> { + consumer.accept(longSparseIntArray); + }); + }); + } + + public void putUserStories(TLRPC.TL_userStories userStories) { + storage.getStorageQueue().postRunnable(() -> { + putStoriesInternal(userStories.user_id, userStories); + }); + } + + public void deleteAllUserStories(long dialogId) { + storage.getStorageQueue().postRunnable(() -> { + SQLiteDatabase database = storage.getDatabase(); + try { + database.executeFast(String.format(Locale.US, "DELETE FROM stories WHERE dialog_id = %d", dialogId)).stepThis().dispose(); + } catch (Throwable e) { + storage.checkSQLException(e); + } + }); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesUtilities.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesUtilities.java new file mode 100644 index 0000000000..dc39fb335f --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesUtilities.java @@ -0,0 +1,1064 @@ +package org.telegram.ui.Stories; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.RectF; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.text.SpannableStringBuilder; +import android.text.TextPaint; +import android.view.HapticFeedbackConstants; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.view.ViewParent; +import android.widget.TextView; + +import androidx.core.graphics.ColorUtils; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.FileLoader; +import org.telegram.messenger.ImageLoader; +import org.telegram.messenger.ImageLocation; +import org.telegram.messenger.ImageReceiver; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.MessagesStorage; +import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.R; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.Utilities; +import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.SimpleTextView; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AnimatedTextView; +import org.telegram.ui.Components.ButtonBounce; +import org.telegram.ui.Components.ColoredImageSpan; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.GradientTools; +import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.LaunchActivity; + +import java.util.Collections; + +public class StoriesUtilities { + + private final static int ANIMATION_SEGMENT_COUNT = 16; + public static final int STATE_EMPTY = 0; + public static final int STATE_HAS_UNREAD = 1; + public static final int STATE_READ = 2; + public static final int STATE_PROGRESS = 3; + public static GradientTools[] storiesGradientTools = new GradientTools[2]; + public static GradientTools closeFriendsGradientTools; + public static Paint grayPaint; + + public static Paint closeFriendsLastColor; + public static int grayLastColor; + public static Paint[] storyCellGreyPaint = new Paint[2]; + public static int storyCellGrayLastColor; + + public static Drawable expiredStoryDrawable; + + private final static RectF rectTmp = new RectF(); + + public static void drawAvatarWithStory(long dialogId, Canvas canvas, ImageReceiver avatarImage, AvatarStoryParams params) { + StoriesController storiesController = MessagesController.getInstance(UserConfig.selectedAccount).getStoriesController(); + boolean hasStories = storiesController.hasStories(dialogId); + drawAvatarWithStory(dialogId, canvas, avatarImage, UserConfig.getInstance(UserConfig.selectedAccount).getClientUserId() != dialogId && hasStories, params); + } + + static boolean scheduled = false; + static int debugState = 0; + static Runnable debugRunnable = new Runnable() { + @Override + public void run() { + debugState = Math.abs(Utilities.random.nextInt() % 3); + if (debugState == STATE_READ) { + debugState = STATE_HAS_UNREAD; + } else { + debugState = STATE_READ; + } + NotificationCenter.getInstance(UserConfig.selectedAccount).postNotificationName(NotificationCenter.updateInterfaces, 0); + AndroidUtilities.runOnUIThread(debugRunnable, 1000); + LaunchActivity.getLastFragment().getFragmentView(); + } + }; + + public static void drawAvatarWithStory(long dialogId, Canvas canvas, ImageReceiver avatarImage, boolean hasStories, AvatarStoryParams params) { + StoriesController storiesController = MessagesController.getInstance(UserConfig.selectedAccount).getStoriesController(); + boolean animated = params.animate; + if (params.dialogId != dialogId) { + params.dialogId = dialogId; + params.reset(); + animated = false; + } +// if (!scheduled) { +// scheduled = true; +// AndroidUtilities.runOnUIThread(debugRunnable); +// } +// int state = debugState; + int state; + int unreadState = 0; + boolean showProgress = storiesController.isLoading(dialogId); + + if (params.drawHiddenStoriesAsSegments) { + hasStories = storiesController.hasHiddenStories(); + } + + if (params.storyItem != null) { + unreadState = storiesController.getUnreadState(dialogId, params.storyId); + state = unreadState == StoriesController.STATE_READ ? STATE_READ : STATE_HAS_UNREAD; + showProgress = false; + } + if (showProgress) { + state = STATE_PROGRESS; + animated = false; + if (storiesController.hasStories(dialogId)) { + unreadState = STATE_READ;//storiesController.getUnreadState(dialogId); + } else { + unreadState = getPredictiveUnreadState(storiesController, dialogId); + } + } else if (hasStories) { + if (params.drawSegments) { + // unreadState = storiesController.getUnreadState(dialogId, params.storyId); + unreadState = state = STATE_READ; + } else { + unreadState = storiesController.getUnreadState(dialogId, params.storyId); + state = unreadState == StoriesController.STATE_READ ? STATE_READ : STATE_HAS_UNREAD; + } + } else { + unreadState = state = getPredictiveUnreadState(storiesController, dialogId); + } + + if (params.currentState != state) { + if (params.currentState == STATE_PROGRESS) { + animated = true; + } + if (state == STATE_PROGRESS) { + params.animateFromUnreadState = unreadState; + } + if (animated) { + params.prevState = params.currentState; + params.prevUnreadState = params.unreadState; + params.currentState = state; + params.progressToSate = 0; + } else { + params.currentState = state; + params.progressToSate = 1f; + } + } + + params.unreadState = unreadState; + + float scale = params.buttonBounce != null ? params.buttonBounce.getScale(0.08f) : 1f; + if (params.showProgress != showProgress && showProgress) { + params.sweepAngle = 1f; + params.inc = false; + } + params.showProgress = showProgress; + if (params.currentState == STATE_EMPTY && params.progressToSate == 1f) { + avatarImage.setImageCoords(params.originalAvatarRect); + avatarImage.draw(canvas); + return; + } + int restoreCount = 0; + if (scale != 1f) { + restoreCount = canvas.save(); + canvas.scale(scale, scale, params.originalAvatarRect.centerX(), params.originalAvatarRect.centerY()); + } + + float progressToSate = params.progressToSate; + if (progressToSate != 1f) { + progressToSate = CubicBezierInterpolator.DEFAULT.getInterpolation(progressToSate); + } + float insetTo = params.isStoryCell ? 0 : AndroidUtilities.lerp( + getInset(params.prevState, params.animateFromUnreadState), + getInset(params.currentState, params.animateFromUnreadState), + params.progressToSate + ); + if (insetTo == 0) { + avatarImage.setImageCoords(params.originalAvatarRect); + } else { + rectTmp.set(params.originalAvatarRect); + rectTmp.inset(insetTo, insetTo); + avatarImage.setImageCoords(rectTmp); + } + if ((params.prevState == STATE_HAS_UNREAD && params.progressToSate != 1f) || params.currentState == STATE_HAS_UNREAD) { + GradientTools gradientTools; + if (unreadState == StoriesController.STATE_UNREAD_CLOSE_FRIEND) { + getCloseFriendsPaint(avatarImage); + gradientTools = closeFriendsGradientTools; + } else { + getActiveCirclePaint(avatarImage, params.isStoryCell); + gradientTools = storiesGradientTools[params.isStoryCell ? 1 : 0]; + } + boolean animateOut = params.prevState == STATE_HAS_UNREAD && params.progressToSate != 1f; + + float inset = params.isStoryCell ? -AndroidUtilities.dp(4) : 0;//AndroidUtilities.lerp(AndroidUtilities.dp(2), 0, imageScale); + if (animateOut) { + inset += AndroidUtilities.dp(5) * progressToSate; + gradientTools.paint.setAlpha((int) (255 * (1f - progressToSate))); + } else { + gradientTools.paint.setAlpha((int) (255 * progressToSate)); + inset += AndroidUtilities.dp(5) * (1f - progressToSate); + } + rectTmp.set(params.originalAvatarRect); + rectTmp.inset(inset, inset); + + drawCircleInternal(canvas, avatarImage.getParentView(), params, gradientTools.paint); + } + if ((params.prevState == STATE_READ && params.progressToSate != 1f) || params.currentState == STATE_READ) { + boolean animateOut = params.prevState == STATE_READ && params.progressToSate != 1f; + Paint paint; + if (params.isStoryCell) { + checkStoryCellGrayPaint(params.isArchive); + paint = storyCellGreyPaint[params.isArchive ? 1 : 0]; + } else { + checkGrayPaint(); + paint = grayPaint; + } + Paint unreadPaint = null; + Paint closeFriendsPaint = null; + if (params.drawSegments) { + unreadPaint = getActiveCirclePaint(avatarImage, params.isStoryCell); + unreadPaint.setAlpha(255); + closeFriendsPaint = getCloseFriendsPaint(avatarImage); + closeFriendsPaint.setAlpha(255); + checkGrayPaint(); + } + float inset; + if (params.drawSegments) { + inset = params.isStoryCell ? -AndroidUtilities.dpf2(3.5f) : 0; + } else { + inset = params.isStoryCell ? -AndroidUtilities.dpf2(2.7f) : 0; + } + if (animateOut) { + inset += AndroidUtilities.dp(5) * progressToSate; + paint.setAlpha((int) (255 * (1f - progressToSate))); + } else { + paint.setAlpha((int) (255 * progressToSate)); + inset += AndroidUtilities.dp(5) * (1f - progressToSate); + } + rectTmp.set(params.originalAvatarRect); + rectTmp.inset(inset, inset); + if (params.drawSegments) { + checkGrayPaint(); + checkStoryCellGrayPaint(params.isArchive); + int globalState; + if (params.crossfadeToDialog != 0) { + globalState = storiesController.getUnreadState(params.crossfadeToDialog); + } else { + globalState = storiesController.getUnreadState(dialogId); + } + + params.globalState = globalState == StoriesController.STATE_READ ? STATE_READ : STATE_HAS_UNREAD; + TLRPC.TL_userStories userStories = storiesController.getStories(params.dialogId); + int storiesCount; + if (params.drawHiddenStoriesAsSegments) { + storiesCount = storiesController.getHiddenList().size(); + } else { + storiesCount = userStories == null || userStories.stories.size() == 1 ? 1 : userStories.stories.size(); + } + Paint globalPaint; + if (globalState == StoriesController.STATE_UNREAD_CLOSE_FRIEND) { + getCloseFriendsPaint(avatarImage); + globalPaint = closeFriendsGradientTools.paint; + } else if (globalState == StoriesController.STATE_UNREAD) { + getActiveCirclePaint(avatarImage, params.isStoryCell); + globalPaint = storiesGradientTools[params.isStoryCell ? 1 : 0].paint; + } else { + globalPaint = params.isStoryCell ? storyCellGreyPaint[params.isArchive ? 1 : 0] : grayPaint; + } + if (storiesCount == 1) { + Paint localPaint = paint; + if (storiesController.hasUnreadStories(dialogId)) { + localPaint = unreadPaint; + } + float startAngle = -90; + float endAngle = 90; + drawSegment(canvas, rectTmp, localPaint, startAngle, endAngle, params); + startAngle = 90; + endAngle = 270; + drawSegment(canvas, rectTmp, localPaint, startAngle, endAngle, params); + + if (params.progressToSegments != 1 && localPaint != globalPaint) { + globalPaint.setAlpha((int) (255 * (1f - params.progressToSegments))); + startAngle = -90; + endAngle = 90; + drawSegment(canvas, rectTmp, globalPaint, startAngle, endAngle, params); + startAngle = 90; + endAngle = 270; + drawSegment(canvas, rectTmp, globalPaint, startAngle, endAngle, params); + globalPaint.setAlpha(255); + } + // canvas.drawCircle(rectTmp.centerX(), rectTmp.centerY(), rectTmp.width() / 2f, localPaint); + } else { + float step = 360 / (float) storiesCount; + int gap = storiesCount > 20 ? 3 : 5; + float gapLen = gap * params.progressToSegments; + if (gapLen > step) { + gapLen = 0;//step * 0.4f; + } + + + int maxUnread = params.drawHiddenStoriesAsSegments ? 0 : Math.max(userStories.max_read_id, storiesController.dialogIdToMaxReadId.get(dialogId, 0)); + for (int i = 0; i < storiesCount; i++) { + Paint segmentPaint = params.isStoryCell ? storyCellGreyPaint[params.isArchive ? 1 : 0] : grayPaint; + if (params.drawHiddenStoriesAsSegments) { + int userUnreadState = storiesController.getUnreadState(storiesController.getHiddenList().get(storiesCount - 1 - i).user_id); + if (userUnreadState == StoriesController.STATE_UNREAD_CLOSE_FRIEND) { + segmentPaint = closeFriendsPaint; + } else if (userUnreadState == StoriesController.STATE_UNREAD) { + segmentPaint = unreadPaint; + } + } else { + if (userStories.stories.get(i).justUploaded || userStories.stories.get(i).id > maxUnread) { + if (userStories.stories.get(i).close_friends) { + segmentPaint = closeFriendsPaint; + } else { + segmentPaint = unreadPaint; + } + } + } + float startAngle = step * i - 90; + float endAngle = startAngle + step; + startAngle += gapLen; + endAngle -= gapLen; + + drawSegment(canvas, rectTmp, segmentPaint, startAngle, endAngle, params); + if (params.progressToSegments != 1 && segmentPaint != globalPaint) { + float strokeWidth = globalPaint.getStrokeWidth(); + //globalPaint.setStrokeWidth(AndroidUtilities.lerp(segmentPaint.getStrokeWidth(), strokeWidth, 1f - params.progressToSegments)); + globalPaint.setAlpha((int) (255 * (1f - params.progressToSegments))); + drawSegment(canvas, rectTmp, globalPaint, startAngle, endAngle, params); + // globalPaint.setStrokeWidth(strokeWidth); + globalPaint.setAlpha(255); + } + } + } + } else { + drawCircleInternal(canvas, avatarImage.getParentView(), params, paint); + } + } + if ((params.prevState == STATE_PROGRESS && params.progressToSate != 1f) || params.currentState == STATE_PROGRESS) { + Paint paint; + if (params.animateFromUnreadState == STATE_HAS_UNREAD) { + getActiveCirclePaint(avatarImage, params.isStoryCell); + paint = storiesGradientTools[params.isStoryCell ? 1 : 0].paint; + } else { + checkGrayPaint(); + paint = grayPaint; + } + paint.setAlpha((int) (255 * progressToSate)); + float inset = 0; + boolean animateOut = params.prevState == STATE_PROGRESS && params.progressToSate != 1f; + if (animateOut) { + inset += AndroidUtilities.dp(7) * progressToSate; + paint.setAlpha((int) (255 * (1f - progressToSate))); + } else { + paint.setAlpha((int) (255 * progressToSate)); + inset += AndroidUtilities.dp(5) * (1f - progressToSate); + } + rectTmp.set(params.originalAvatarRect); + rectTmp.inset(inset, inset); + drawProgress(canvas, params, avatarImage.getParentView(), paint); + } + + avatarImage.draw(canvas); + + if (params.progressToSate != 1f) { + params.progressToSate += AndroidUtilities.screenRefreshTime / 250; + if (params.progressToSate > 1f) { + params.progressToSate = 1f; + } + if (avatarImage.getParentView() != null) { + avatarImage.invalidate(); + avatarImage.getParentView().invalidate(); + } + } + if (restoreCount != 0) { + canvas.restoreToCount(restoreCount); + } + } + + private static int getPredictiveUnreadState(StoriesController storiesController, long dialogId) { + TLRPC.User user = MessagesController.getInstance(UserConfig.selectedAccount).getUser(dialogId); + if (dialogId != UserConfig.getInstance(UserConfig.selectedAccount).clientUserId && user != null && user.stories_max_id > 0 && !user.stories_unavailable) { + int maxReadId = storiesController.dialogIdToMaxReadId.get(dialogId, 0); + if (user.stories_max_id > maxReadId) { + return STATE_HAS_UNREAD; + } else { + return STATE_READ; + } + } else { + return STATE_EMPTY; + } + } + + private static void drawProgress(Canvas canvas, AvatarStoryParams params, View view, Paint paint) { + float len = 360 / (float) ANIMATION_SEGMENT_COUNT; + params.updateProgressParams(); + view.invalidate(); + + if (params.inc) { + canvas.drawArc(rectTmp, params.globalAngle, 360 * params.sweepAngle, false, paint); + } else { + canvas.drawArc(rectTmp, params.globalAngle + 360, -360 * (params.sweepAngle), false, paint); + } + + for (int i = 0; i < ANIMATION_SEGMENT_COUNT; i++) { + float startAngle = i * len + 10; + float endAngle = startAngle + len - 10; + float segmentLen = endAngle - startAngle; + canvas.drawArc(rectTmp, params.globalAngle + startAngle, segmentLen, false, paint); + } + } + + private static void checkStoryCellGrayPaint(boolean isArchive) { + int index = isArchive ? 1 : 0; + if (storyCellGreyPaint[index] == null) { + storyCellGreyPaint[index] = new Paint(Paint.ANTI_ALIAS_FLAG); + storyCellGreyPaint[index].setStyle(Paint.Style.STROKE); + storyCellGreyPaint[index].setStrokeWidth(AndroidUtilities.dpf2(1.3f)); + storyCellGreyPaint[index].setStrokeCap(Paint.Cap.ROUND); + } + int color = !isArchive ? Theme.getColor(Theme.key_actionBarDefault) : Theme.getColor(Theme.key_actionBarDefaultArchived); + if (storyCellGrayLastColor != color) { + storyCellGrayLastColor = color; + float brightness = AndroidUtilities.computePerceivedBrightness(color); + final boolean isDark = brightness < 0.721f; + if (isDark) { + if (brightness < 0.25f) { + storyCellGreyPaint[index].setColor(ColorUtils.blendARGB(color, Color.WHITE, 0.2f)); + } else { + storyCellGreyPaint[index].setColor(ColorUtils.blendARGB(color, Color.WHITE, 0.44f)); + } + } else { + storyCellGreyPaint[index].setColor(ColorUtils.blendARGB(color, Color.BLACK, 0.2f)); + } + } + } + + private static void checkGrayPaint() { + if (grayPaint == null) { + grayPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + grayPaint.setStyle(Paint.Style.STROKE); + grayPaint.setStrokeWidth(AndroidUtilities.dpf2(1.3f)); + grayPaint.setStrokeCap(Paint.Cap.ROUND); + } + int color = Theme.getColor(Theme.key_windowBackgroundWhite); + if (grayLastColor != color) { + grayLastColor = color; + float brightness = AndroidUtilities.computePerceivedBrightness(color); + final boolean isDark = brightness < 0.721f; + if (isDark) { + if (brightness < 0.25f) { + grayPaint.setColor(ColorUtils.blendARGB(color, Color.WHITE, 0.2f)); + } else { + grayPaint.setColor(ColorUtils.blendARGB(color, Color.WHITE, 0.44f)); + } + } else { + grayPaint.setColor(ColorUtils.blendARGB(color, Color.BLACK, 0.2f)); + } + } + } + + private static void drawCircleInternal(Canvas canvas, View view, AvatarStoryParams params, Paint paint) { + if (params.progressToArc == 0) { + canvas.drawCircle(rectTmp.centerX(), rectTmp.centerY(), rectTmp.width() / 2f, paint); + } else { + canvas.drawArc(rectTmp, 360 + params.progressToArc / 2f, 360 - params.progressToArc, false, paint); + } + } + + private static void drawSegment(Canvas canvas, RectF rectTmp, Paint paint, float startAngle, float endAngle, AvatarStoryParams params) { + if (!params.isFirst && !params.isLast) { + if (startAngle < 90) { + drawArcExcludeArc(canvas, rectTmp, paint, startAngle, endAngle, -params.progressToArc / 2, params.progressToArc / 2); + } else { + drawArcExcludeArc(canvas, rectTmp, paint, startAngle, endAngle, -params.progressToArc / 2 + 180, params.progressToArc / 2 + 180); + } + } else if (params.isLast) { + drawArcExcludeArc(canvas, rectTmp, paint, startAngle, endAngle, -params.progressToArc / 2 + 180, params.progressToArc / 2 + 180); + } else if (params.isFirst) { + // canvas.drawArc(rectTmp, startAngle, endAngle - startAngle, false, paint); + drawArcExcludeArc(canvas, rectTmp, paint, startAngle, endAngle, -params.progressToArc / 2, params.progressToArc / 2); + } else { + canvas.drawArc(rectTmp, startAngle, endAngle - startAngle, false, paint); + } + } + + private static int getInset(int currentState, int unreadState) { + int stateToCheck = currentState; + if (currentState == STATE_PROGRESS) { + stateToCheck = unreadState; + } + if (stateToCheck == STATE_READ) { + return AndroidUtilities.dp(3); + } else if (stateToCheck == STATE_HAS_UNREAD) { + return AndroidUtilities.dp(4); + } + return 0; + } + + public static Paint getActiveCirclePaint(ImageReceiver avatarImage, boolean isDialogCell) { + int i = isDialogCell ? 1 : 0; + if (storiesGradientTools[i] == null) { + storiesGradientTools[i] = new GradientTools(); + storiesGradientTools[i].isDiagonal = true; + storiesGradientTools[i].isRotate = true; + if (isDialogCell) { + storiesGradientTools[i].setColors(0xFF4AED55, 0xFF4DC3FF); + } else { + storiesGradientTools[i].setColors(0xFF39DF3C, 0xFF4DBBFF); + } + storiesGradientTools[i].paint.setStrokeWidth(AndroidUtilities.dpf2(2.3f)); + storiesGradientTools[i].paint.setStyle(Paint.Style.STROKE); + storiesGradientTools[i].paint.setStrokeCap(Paint.Cap.ROUND); + } + storiesGradientTools[i].setBounds(avatarImage.getImageX(), avatarImage.getImageY(), avatarImage.getImageX2(), avatarImage.getImageY2()); + return storiesGradientTools[i].paint; + } + + public static Paint getCloseFriendsPaint(ImageReceiver avatarImage) { + if (closeFriendsGradientTools == null) { + closeFriendsGradientTools = new GradientTools(); + closeFriendsGradientTools.isDiagonal = true; + closeFriendsGradientTools.isRotate = true; + closeFriendsGradientTools.setColors(0xFFC9EB38, 0xFF09C167); + closeFriendsGradientTools.paint.setStrokeWidth(AndroidUtilities.dp(2.3f)); + closeFriendsGradientTools.paint.setStyle(Paint.Style.STROKE); + closeFriendsGradientTools.paint.setStrokeCap(Paint.Cap.ROUND); + } + closeFriendsGradientTools.setBounds(avatarImage.getImageX(), avatarImage.getImageY(), avatarImage.getImageX2(), avatarImage.getImageY2()); + return closeFriendsGradientTools.paint; + } + + public static void setStoryMiniImage(ImageReceiver imageReceiver, TLRPC.StoryItem storyItem) { + if (storyItem == null) { + return; + } + if (storyItem.media.document != null) { + TLRPC.PhotoSize size = FileLoader.getClosestPhotoSizeWithSize(storyItem.media.document.thumbs, 1000); + imageReceiver.setImage(ImageLocation.getForDocument(size, storyItem.media.document), "100_100", null, null, ImageLoader.createStripedBitmap(storyItem.media.document.thumbs), 0, null, storyItem, 0); + } else { + TLRPC.Photo photo = storyItem.media != null ? storyItem.media.photo : null; + if (photo != null && photo.sizes != null) { + TLRPC.PhotoSize size = FileLoader.getClosestPhotoSizeWithSize(photo.sizes, 1000); + imageReceiver.setImage(null, null, ImageLocation.getForPhoto(size, photo), "100_100", null, null, ImageLoader.createStripedBitmap(photo.sizes), 0, null, storyItem, 0); + } else { + imageReceiver.clearImage(); + } + } + } + + public static void setImage(ImageReceiver imageReceiver, TLRPC.StoryItem storyItem) { + setImage(imageReceiver, storyItem, "320_320"); + } + + public static void setImage(ImageReceiver imageReceiver, TLRPC.StoryItem storyItem, String filter) { + if (storyItem == null) { + return; + } + if (storyItem.media != null && storyItem.media.document != null) { + TLRPC.PhotoSize size = FileLoader.getClosestPhotoSizeWithSize(storyItem.media.document.thumbs, Integer.MAX_VALUE); + imageReceiver.setImage(ImageLocation.getForDocument(size, storyItem.media.document), filter, null, null, ImageLoader.createStripedBitmap(storyItem.media.document.thumbs), 0, null, storyItem, 0); + } else { + TLRPC.Photo photo = storyItem.media != null ? storyItem.media.photo : null; + if (storyItem.media instanceof TLRPC.TL_messageMediaUnsupported) { + Bitmap bitmap = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888); + bitmap.eraseColor(ColorUtils.blendARGB(Color.BLACK, Color.WHITE, 0.2f)); + imageReceiver.setImageBitmap(bitmap); + } else if (photo != null && photo.sizes != null) { + TLRPC.PhotoSize size = FileLoader.getClosestPhotoSizeWithSize(photo.sizes, Integer.MAX_VALUE); + imageReceiver.setImage(null, null, ImageLocation.getForPhoto(size, photo), filter, null, null, ImageLoader.createStripedBitmap(photo.sizes), 0, null, storyItem, 0); + } else { + imageReceiver.clearImage(); + } + } + } + + public static void setImage(ImageReceiver imageReceiver, StoriesController.UploadingStory uploadingStory) { + if (uploadingStory.entry.isVideo) { + imageReceiver.setImage(ImageLocation.getForPath(uploadingStory.firstFramePath), "320_180", null, null, null, 0, null, null, 0); + } else { + imageReceiver.setImage(ImageLocation.getForPath(uploadingStory.path), "320_180", null, null, null, 0, null, null, 0); + } + } + + public static void setThumbImage(ImageReceiver imageReceiver, TLRPC.StoryItem storyItem, int w, int h) { + if (storyItem.media != null && storyItem.media.document != null) { + TLRPC.PhotoSize size = FileLoader.getClosestPhotoSizeWithSize(storyItem.media.document.thumbs, AndroidUtilities.dp(Math.max(w, h)), false, null, true); + imageReceiver.setImage(ImageLocation.getForDocument(size, storyItem.media.document), w + "_" + h, null, null, ImageLoader.createStripedBitmap(storyItem.media.document.thumbs), 0, null, storyItem, 0); + } else { + TLRPC.Photo photo = storyItem.media != null ? storyItem.media.photo : null; + if (photo != null && photo.sizes != null) { + TLRPC.PhotoSize size = FileLoader.getClosestPhotoSizeWithSize(photo.sizes, AndroidUtilities.dp(Math.max(w, h)), false, null, true); + imageReceiver.setImage(null, null, ImageLocation.getForPhoto(size, photo), w + "_" + h, null, null, ImageLoader.createStripedBitmap(photo.sizes), 0, null, storyItem, 0); + } else { + imageReceiver.clearImage(); + } + } + } + + public static Drawable getExpiredStoryDrawable() { + if (expiredStoryDrawable == null) { + Bitmap bitmap = Bitmap.createBitmap(360, 180, Bitmap.Config.ARGB_8888); + bitmap.eraseColor(Color.GRAY); + Canvas canvas = new Canvas(bitmap); + TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + textPaint.setTextSize(15); + textPaint.setTextAlign(Paint.Align.CENTER); + textPaint.setColor(ColorUtils.setAlphaComponent(Color.BLACK, 100)); + canvas.drawText("expired", 360 / 2f, 180 / 2f - 4, textPaint); + canvas.drawText("story", 360 / 2f, 180 / 2f + 16, textPaint); + expiredStoryDrawable = new BitmapDrawable(bitmap); + } + return expiredStoryDrawable; + } + + public static CharSequence getUploadingStr(TextView textView, boolean medium, boolean edit) { + String str; + if (edit) { + str = LocaleController.getString("StoryEditing", R.string.StoryEditing); + } else { + str = LocaleController.getString("UploadingStory", R.string.UploadingStory); + } + int index = str.indexOf("…"); + if (index > 0) { + SpannableStringBuilder spannableStringBuilder = SpannableStringBuilder.valueOf(str); + UploadingDotsSpannable dotsSpannable = new UploadingDotsSpannable(); + spannableStringBuilder.setSpan(dotsSpannable, spannableStringBuilder.length() - 1, spannableStringBuilder.length(), 0); + dotsSpannable.setParent(textView, medium); + return spannableStringBuilder; + } else { + return str; + } + } + + public static void applyUploadingStr(SimpleTextView textView, boolean medium, boolean edit) { + String str; + if (edit) { + str = LocaleController.getString("StoryEditing", R.string.StoryEditing); + } else { + str = LocaleController.getString("UploadingStory", R.string.UploadingStory); + } + int index = str.indexOf("…"); + if (index > 0) { + SpannableStringBuilder spannableStringBuilder = SpannableStringBuilder.valueOf(str); + UploadingDotsSpannable dotsSpannable = new UploadingDotsSpannable(); + spannableStringBuilder.setSpan(dotsSpannable, spannableStringBuilder.length() - 1, spannableStringBuilder.length(), 0); + dotsSpannable.setParent(textView, medium); + textView.setText(spannableStringBuilder);//, animated); + } else { + textView.setText(str); + } + } + + public static void applyUploadingStr(AnimatedTextView textView, boolean medium, boolean animated) { + String str = LocaleController.getString("UploadingStory", R.string.UploadingStory); + int index = str.indexOf("…"); + if (index > 0) { + SpannableStringBuilder spannableStringBuilder = SpannableStringBuilder.valueOf(str); + UploadingDotsSpannable dotsSpannable = new UploadingDotsSpannable(); + spannableStringBuilder.setSpan(dotsSpannable, spannableStringBuilder.length() - 1, spannableStringBuilder.length(), 0); + dotsSpannable.setParent(textView, medium); + textView.setText(str, animated); + } else { + textView.setText(str); + } + } + + public static CharSequence createExpiredStoryString() { + return createExpiredStoryString(false, "ExpiredStory", R.string.ExpiredStory); + } + + public static CharSequence createExpiredStoryString(boolean useScale, String strKey, int strRes, Object... args) { + SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(); + spannableStringBuilder.append("d ").append(LocaleController.formatString(strKey, strRes, args)); + ColoredImageSpan coloredImageSpan = new ColoredImageSpan(R.drawable.msg_mini_bomb); + if (useScale) { + coloredImageSpan.setScale(0.8f); + } else { + coloredImageSpan.setTopOffset(-1); + } + spannableStringBuilder.setSpan(coloredImageSpan, 0, 1, 0); + return spannableStringBuilder; + } + + public static CharSequence createReplyStoryString() { + SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(); + spannableStringBuilder.append("d ").append(LocaleController.getString("Story", R.string.Story)); + ColoredImageSpan coloredImageSpan = new ColoredImageSpan(R.drawable.msg_mini_replystory2); + spannableStringBuilder.setSpan(coloredImageSpan, 0, 1, 0); + return spannableStringBuilder; + } + + public static boolean hasExpiredViews(TLRPC.StoryItem storyItem) { + if (storyItem == null) { + return false; + } + return ConnectionsManager.getInstance(UserConfig.selectedAccount).getCurrentTime() > storyItem.expire_date + 86400; + } + + public static void applyViewedUser(TLRPC.StoryItem storyItem, TLRPC.User currentUser) { + if (currentUser == null) { + return; + } + if (storyItem.dialogId == UserConfig.getInstance(UserConfig.selectedAccount).clientUserId && !hasExpiredViews(storyItem)) { + if (storyItem.views == null) { + storyItem.views = new TLRPC.TL_storyViews(); + } + if (storyItem.views.views_count == 0) { + storyItem.views.views_count = 1; + storyItem.views.recent_viewers.add(currentUser.id); + } + } + } + + public static void drawArcExcludeArc(Canvas canvas, RectF rect, Paint paint, float startAngle, float endAngle, float excludeStartAngle, float excludeAndAngle) { + float len = endAngle - startAngle; + float originalStart = startAngle; + float originalEnd = endAngle; + boolean drawn = false; + if (startAngle < excludeStartAngle && endAngle < excludeStartAngle + len) { + float endAngle2 = Math.min(endAngle, excludeStartAngle); + drawn = true; + canvas.drawArc(rect, startAngle, endAngle2 - startAngle, false, paint); + } + + startAngle = Math.max(startAngle, excludeAndAngle); + endAngle = Math.min(endAngle, 360 + excludeStartAngle); + if (endAngle < startAngle) { + if (!drawn && !(originalStart > excludeStartAngle && originalEnd < excludeAndAngle)) { + canvas.drawArc(rect, originalStart, originalEnd - originalStart, false, paint); + } + return; + } + + canvas.drawArc(rect, startAngle, endAngle - startAngle, false, paint); + } + + public static boolean isExpired(int currentAccount, TLRPC.StoryItem newStory) { + return ConnectionsManager.getInstance(currentAccount).getCurrentTime() > newStory.expire_date; + } + + public static String getStoryImageFilter() { + int maxWidth = Math.max(AndroidUtilities.getRealScreenSize().x, AndroidUtilities.getRealScreenSize().y); + int filterSize = (int) (maxWidth / AndroidUtilities.density); + return filterSize + "_" + filterSize; + } + + + public static class AvatarStoryParams { + public boolean drawSegments = true; + public boolean animate = true; + public int storyId; + public TLRPC.StoryItem storyItem; + public float progressToSegments = 1f; + public float progressToArc = 0; + public boolean isLast; + public boolean isFirst; + public int globalState; + public boolean isArchive; + public boolean forceAnimateProgressToSegments; + public int prevUnreadState; + public int unreadState; + public int animateFromUnreadState; + public boolean drawHiddenStoriesAsSegments; + public long crossfadeToDialog; + public float crossfadeToDialogProgress; + + private long dialogId; + public int currentState; + public int prevState; + public float progressToSate = 1f; + public boolean showProgress = false; + + private final boolean isStoryCell; + public RectF originalAvatarRect = new RectF(); + + ButtonBounce buttonBounce; + public boolean allowLongress = false; + + public AvatarStoryParams(boolean isStoryCell) { + this.isStoryCell = isStoryCell; + } + float sweepAngle; + boolean inc; + float globalAngle; + boolean pressed; + UserStoriesLoadOperation operation; + + private void updateProgressParams() { + if (inc) { + sweepAngle += 16 / 1000f; + if (sweepAngle >= 1f) { + sweepAngle = 1f; + inc = false; + } + } else { + sweepAngle -= 16 / 1000f; + if (sweepAngle < 0) { + sweepAngle = 0; + inc = true; + } + } + globalAngle += 16 / 5000f * 360; + } + + float startX, startY; + Runnable longPressRunnable; + public View child; + + public boolean checkOnTouchEvent(MotionEvent event, View view) { + child = view; + StoriesController storiesController = MessagesController.getInstance(UserConfig.selectedAccount).getStoriesController(); + if (event.getAction() == MotionEvent.ACTION_DOWN && originalAvatarRect.contains(event.getX(), event.getY())) { + TLRPC.User user = MessagesController.getInstance(UserConfig.selectedAccount).getUser(dialogId); + boolean hasStories; + if (drawHiddenStoriesAsSegments) { + hasStories = storiesController.hasHiddenStories(); + } else { + hasStories = (MessagesController.getInstance(UserConfig.selectedAccount).getStoriesController().hasStories(dialogId) || user != null && !user.stories_unavailable && user.stories_max_id > 0); + } + if (dialogId != UserConfig.getInstance(UserConfig.selectedAccount).clientUserId && hasStories) { + if (buttonBounce == null) { + buttonBounce = new ButtonBounce(view, 1.5f); + } else { + buttonBounce.setView(view); + } + view.getParent().requestDisallowInterceptTouchEvent(true); + buttonBounce.setPressed(true); + pressed = true; + startX = event.getX(); + startY = event.getY(); + if (allowLongress) { + if (longPressRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(longPressRunnable); + } + AndroidUtilities.runOnUIThread(longPressRunnable = () -> { + view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + if (buttonBounce != null) { + buttonBounce.setPressed(false); + } + ViewParent parent = view.getParent(); + if (parent instanceof ViewGroup) { + ((ViewGroup) parent).requestDisallowInterceptTouchEvent(false); + } + pressed = false; + onLongPress(); + }, ViewConfiguration.getLongPressTimeout()); + } + } + } else if (event.getAction() == MotionEvent.ACTION_MOVE && pressed) { + if (Math.abs(startX - event.getX()) > AndroidUtilities.touchSlop || Math.abs(startY - event.getY()) > AndroidUtilities.touchSlop) { + if (buttonBounce != null) { + buttonBounce.setView(view); + buttonBounce.setPressed(false); + } + if (longPressRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(longPressRunnable); + } + view.getParent().requestDisallowInterceptTouchEvent(false); + pressed = false; + } + } else if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL) { + if (buttonBounce != null) { + buttonBounce.setView(view); + buttonBounce.setPressed(false); + } + if (pressed && event.getAction() == MotionEvent.ACTION_UP) { + processOpenStory(view); + } + ViewParent parent = view.getParent(); + if (parent instanceof ViewGroup) { + ((ViewGroup) parent).requestDisallowInterceptTouchEvent(false); + } + pressed = false; + if (longPressRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(longPressRunnable); + } + } + return pressed; + } + + public void onLongPress() { + + } + + private void processOpenStory(View view) { + int currentAccount = UserConfig.selectedAccount; + MessagesController messagesController = MessagesController.getInstance(UserConfig.selectedAccount); + StoriesController storiesController = messagesController.getStoriesController(); + if (drawHiddenStoriesAsSegments) { + openStory(0, null); + return; + } + if (dialogId != UserConfig.getInstance(UserConfig.selectedAccount).getClientUserId()) { + if (storiesController.hasStories(dialogId)) { + openStory(dialogId, null); + return; + } + TLRPC.User user = messagesController.getUser(dialogId); + if (user != null && !user.stories_unavailable && user.stories_max_id > 0) { + UserStoriesLoadOperation operation = new UserStoriesLoadOperation(); + operation.load(dialogId, view, this); + return; + } + } + } + + public void openStory(long dialogId, Runnable onDone) { + BaseFragment fragment = LaunchActivity.getLastFragment(); + if (fragment != null && child != null) { + fragment.getOrCreateStoryViewer().doOnAnimationReady(onDone); + fragment.getOrCreateStoryViewer().open(fragment.getContext(), dialogId, StoriesListPlaceProvider.of((RecyclerListView) child.getParent())); + } + } + + public float getScale() { + return buttonBounce == null ? 1f : buttonBounce.getScale(0.08f); + } + + public void reset() { + if (operation != null) { + operation.cancel(); + operation = null; + } + buttonBounce = null; + pressed = false; + } + + public void onDetachFromWindow() { + reset(); + } + } + + public static class UserStoriesLoadOperation { + + int guid = ConnectionsManager.generateClassGuid(); + long dialogId; + private int currentAccount; + AvatarStoryParams params; + View view; + + boolean canceled; + int reqId; + + void load(long dialogId, View view, AvatarStoryParams params) { + currentAccount = UserConfig.selectedAccount; + this.dialogId = dialogId; + this.params = params; + this.view = view; + MessagesController messagesController = MessagesController.getInstance(UserConfig.selectedAccount); + StoriesController storiesController = messagesController.getStoriesController(); + + storiesController.setLoading(dialogId, true); + view.invalidate(); + + TLRPC.User user = messagesController.getUser(dialogId); + + TLRPC.TL_stories_getUserStories req = new TLRPC.TL_stories_getUserStories(); + req.user_id = MessagesController.getInstance(currentAccount).getInputUser(dialogId); + reqId = ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + boolean openned = false; + boolean finished = true; + if (response != null) { + TLRPC.TL_stories_userStories stories_userStories = (TLRPC.TL_stories_userStories) response; + MessagesController.getInstance(currentAccount).putUsers(stories_userStories.users, false); + TLRPC.TL_userStories stories = stories_userStories.stories; + if (!stories.stories.isEmpty()) { + MessagesController.getInstance(currentAccount).getStoriesController().putStories(dialogId, stories); + finished = false; + ensureStoryFileLoaded(stories, () -> { + AndroidUtilities.runOnUIThread(() -> { + view.invalidate(); + MessagesController.getInstance(currentAccount).getStoriesController().setLoading(dialogId, false); + }, 500); + params.openStory(dialogId, null); + }); + } + } + if (!openned) { + TLRPC.User user2 = messagesController.getUser(dialogId); + user2.stories_unavailable = true; + MessagesStorage.getInstance(currentAccount).putUsersAndChats(Collections.singletonList(user2), null, false, true); + messagesController.putUser(user2, false); + } + + if (finished) { + view.invalidate(); + MessagesController.getInstance(currentAccount).getStoriesController().setLoading(dialogId, false); + } + })); + } + + private void ensureStoryFileLoaded(TLRPC.TL_userStories stories, Runnable onDoneOrTimeout) { + if (stories == null || stories.stories.isEmpty()) { + onDoneOrTimeout.run(); + return; + } + TLRPC.StoryItem storyItem = null; + int maxReadId = MessagesController.getInstance(currentAccount).storiesController.dialogIdToMaxReadId.get(stories.user_id); + + for (int i = 0; i < stories.stories.size(); i++) { + if (stories.stories.get(i).id > maxReadId) { + storyItem = stories.stories.get(i); + break; + } + } + if (storyItem == null) { + storyItem = stories.stories.get(0); + } + Runnable[] runnableRef = new Runnable[1]; + runnableRef[0] = () -> { + runnableRef[0] = null; + onDoneOrTimeout.run(); + }; + + AndroidUtilities.runOnUIThread(runnableRef[0], 1000); + + ImageReceiver imageReceiver = new ImageReceiver() { + @Override + protected boolean setImageBitmapByKey(Drawable drawable, String key, int type, boolean memCache, int guid) { + boolean res = super.setImageBitmapByKey(drawable, key, type, memCache, guid); + if (runnableRef[0] != null) { + AndroidUtilities.cancelRunOnUIThread(runnableRef[0]); + onDoneOrTimeout.run(); + } + AndroidUtilities.runOnUIThread(this::onDetachedFromWindow); + return res; + } + }; + imageReceiver.setAllowLoadingOnAttachedOnly(true); + imageReceiver.onAttachedToWindow(); + + String filter = getStoryImageFilter(); + + if (storyItem.media != null && storyItem.media.document != null) { + imageReceiver.setImage(ImageLocation.getForDocument(storyItem.media.document), filter + "_pframe", null, null, null, 0, null, storyItem, 0); + } else { + TLRPC.Photo photo = storyItem.media != null ? storyItem.media.photo : null; + if (photo != null && photo.sizes != null) { + TLRPC.PhotoSize size = FileLoader.getClosestPhotoSizeWithSize(photo.sizes, Integer.MAX_VALUE); + imageReceiver.setImage(null, null, ImageLocation.getForPhoto(size, photo), filter, null, null, null, 0, null, storyItem, 0); + } else { + onDoneOrTimeout.run(); + return; + } + } + } + + void cancel() { + ConnectionsManager.getInstance(currentAccount).cancelRequest(reqId, false); + canceled = true; + params = null; + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesViewPager.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesViewPager.java new file mode 100644 index 0000000000..b66ac70ac4 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesViewPager.java @@ -0,0 +1,415 @@ +package org.telegram.ui.Stories; + +import android.content.Context; +import android.graphics.Canvas; +import android.os.Build; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.viewpager.widget.PagerAdapter; +import androidx.viewpager.widget.ViewPager; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.UserConfig; +import org.telegram.ui.ActionBar.Theme; + +import java.util.ArrayList; +import java.util.TreeSet; + +public class StoriesViewPager extends ViewPager { + + long daysDialogId; + ArrayList> days; + + int currentAccount = UserConfig.selectedAccount; + PagerAdapter pagerAdapter; + ArrayList dialogs = new ArrayList<>(); + PeerStoriesView.Delegate delegate; + + boolean updateDelegate; + boolean touchEnabled = true; + int keyboardHeight; + public int currentState; + public boolean dissallowInterceptCalled; + + Runnable doOnNextIdle; + PeerStoriesView.SharedResources resources; + + int selectedPosition; + int toPosition; + float progress; + private boolean touchLocked; + Runnable lockTouchRunnable = new Runnable() { + @Override + public void run() { + touchLocked = false; + } + }; + + StoryViewer storyViewer; + + public StoriesViewPager(@NonNull Context context, StoryViewer storyViewer, Theme.ResourcesProvider resourcesProvider) { + super(context); + resources = new PeerStoriesView.SharedResources(context); + this.storyViewer = storyViewer; + setAdapter(pagerAdapter = new PagerAdapter() { + @Override + public int getCount() { + if (days != null) { + return days.size(); + } + return dialogs.size(); + } + + private final ArrayList cachedViews = new ArrayList<>(); + + @NonNull + @Override + public Object instantiateItem(@NonNull ViewGroup container, int position) { + PageLayout pageLayout = new PageLayout(context); + PeerStoriesView view; + if (!cachedViews.isEmpty()) { + view = cachedViews.remove(0); + view.reset(); + } else { + view = new HwPeerStoriesView(context, storyViewer, resources, resourcesProvider) { + @Override + public boolean isSelectedPeer() { + if (getParent() == null) { + return false; + } + return ((Integer) ((View) getParent()).getTag()) == getCurrentItem(); + } + }; + } + pageLayout.peerStoryView = view; + view.setAccount(currentAccount); + view.setDelegate(delegate); + view.setLongpressed(storyViewer.isLongpressed); + pageLayout.setTag(position); + if (days != null) { + pageLayout.day = days.get(storyViewer.reversed ? days.size() - 1 - position : position); + pageLayout.dialogId = daysDialogId; + } else { + pageLayout.day = null; + pageLayout.dialogId = dialogs.get(position); + } + pageLayout.addView(view); + view.requestLayout(); + container.addView(pageLayout); + return pageLayout; + } + + @Override + public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) { + FrameLayout child = (FrameLayout) object; + container.removeView(child); + PeerStoriesView peerStoriesView = (PeerStoriesView) child.getChildAt(0); + AndroidUtilities.removeFromParent(peerStoriesView); + cachedViews.add(peerStoriesView); + } + + @Override + public boolean isViewFromObject(@NonNull View view, @NonNull Object object) { + return view == object; + } + }); + setPageTransformer(false, (page, position) -> { + PageLayout pageLayout = (PageLayout) page; + if (Math.abs(position) >= 1f) { + pageLayout.setVisible(false); + AndroidUtilities.runOnUIThread(() -> { + if (pageLayout.day != null) { + pageLayout.peerStoryView.day = pageLayout.day; + } + pageLayout.peerStoryView.preloadMainImage(pageLayout.dialogId); + }, 16); + return; + } + if (!pageLayout.isVisible) { + pageLayout.setVisible(true); + if (days != null) { + pageLayout.peerStoryView.setDay(pageLayout.dialogId, pageLayout.day); + } else { + pageLayout.peerStoryView.setDialogId(pageLayout.dialogId); + } + } + pageLayout.peerStoryView.setOffset(position); + page.setCameraDistance(page.getWidth() * 15); + page.setPivotX(position < 0f ? page.getWidth() : 0f); + page.setPivotY(page.getHeight() * 0.5f); + page.setRotationY(90f * position); + }); + setOffscreenPageLimit(0); + + addOnPageChangeListener(new OnPageChangeListener() { + + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + selectedPosition = position; + toPosition = positionOffsetPixels > 0 ? selectedPosition + 1 : selectedPosition - 1; + progress = positionOffset; + + final long me = UserConfig.getInstance(currentAccount).clientUserId; + + if (selectedPosition >= 0 && (days == null ? selectedPosition < dialogs.size() && dialogs.get(selectedPosition) == me : daysDialogId == me)) { + delegate.setHideEnterViewProgress(1f - progress); + } else if (toPosition >= 0 && (days == null ? toPosition < dialogs.size() && dialogs.get(toPosition) == me : daysDialogId == me)) { + delegate.setHideEnterViewProgress(progress); + } else { + delegate.setHideEnterViewProgress(0); + } + } + + @Override + public void onPageSelected(int position) { + final PeerStoriesView peerStoriesView = getCurrentPeerView(); + if (peerStoriesView == null) { + return; + } + delegate.onPeerSelected(peerStoriesView.getCurrentPeer(), peerStoriesView.getSelectedPosition()); + updateActiveStory(); + } + + @Override + public void onPageScrollStateChanged(int state) { + delegate.setAllowTouchesByViewPager(state != SCROLL_STATE_IDLE); + if (doOnNextIdle != null && state == SCROLL_STATE_IDLE) { + doOnNextIdle.run(); + doOnNextIdle = null; + } + currentState = state; + onStateChanged(); + } + }); + setOverScrollMode(OVER_SCROLL_NEVER); + } + + private void updateActiveStory() { + for (int i = 0; i < getChildCount(); i++) { + ((PeerStoriesView) ((FrameLayout) getChildAt(i)).getChildAt(0)).setActive((Integer) getChildAt(i).getTag() == getCurrentItem()); + } + } + + public void checkAllowScreenshots() { + boolean allowScreenshots = true; + for (int i = 0; i < getChildCount(); i++) { + PageLayout layout = (PageLayout) getChildAt(i); + if (layout.isVisible && !layout.peerStoryView.currentStory.allowScreenshots()) { + allowScreenshots = false; + break; + } + } + storyViewer.allowScreenshots(allowScreenshots); + } + + public void onStateChanged() { + + } + + public boolean canScroll(float dx) { + if (selectedPosition == 0 && progress == 0 && dx < 0) { + return false; + } + if (selectedPosition == getAdapter().getCount() - 1 && progress == 0 && dx > 0) { + return false; + } + return true; + } + + @Nullable + public PeerStoriesView getCurrentPeerView() { + for (int i = 0; i < getChildCount(); i++) { + if ((Integer) getChildAt(i).getTag() == getCurrentItem()) { + return (PeerStoriesView) ((FrameLayout) getChildAt(i)).getChildAt(0); + } + } + return null; + } + + public void setPeerIds(ArrayList peerIds, int currentAccount, int position) { + this.dialogs = peerIds; + this.currentAccount = currentAccount; + setAdapter(null); + setAdapter(pagerAdapter); + setCurrentItem(position); + updateDelegate = true; + } + + public void setDays(long dialogId, ArrayList> days, int currentAccount) { + this.daysDialogId = dialogId; + this.days = days; + this.currentAccount = currentAccount; + setAdapter(null); + setAdapter(pagerAdapter); + int position; + for (position = 0; position < days.size(); ++position) { + if (days.get(position).contains(storyViewer.dayStoryId)) { + break; + } + } + if (storyViewer.reversed) { + position = days.size() - 1 - position; + } + setCurrentItem(position); + updateDelegate = true; + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + if (updateDelegate) { + updateDelegate = false; + final PeerStoriesView peerStoriesView = getCurrentPeerView(); + if (peerStoriesView != null) { + delegate.onPeerSelected(peerStoriesView.getCurrentPeer(), peerStoriesView.getSelectedPosition()); + } + } + updateActiveStory(); + } + + public void setDelegate(PeerStoriesView.Delegate delegate) { + this.delegate = delegate; + } + + public boolean useSurfaceInViewPagerWorkAround() { + return storyViewer.USE_SURFACE_VIEW && Build.VERSION.SDK_INT < 33; + } + + public boolean switchToNext(boolean forward) { + if (forward && getCurrentItem() < (days != null ? days : dialogs).size() - 1) { + setCurrentItem(getCurrentItem() + 1, !useSurfaceInViewPagerWorkAround()); + return true; + } + if (!forward && getCurrentItem() > 0) { + setCurrentItem(getCurrentItem() - 1, !useSurfaceInViewPagerWorkAround()); + return true; + } + return false; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + if (!touchEnabled || touchLocked) { + return false; + } + try { + return super.onInterceptTouchEvent(ev); + } catch (Exception e) { + FileLog.e(e); + } + return false; + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + if (!touchEnabled || touchLocked) { + if (touchLocked && (ev.getAction() == MotionEvent.ACTION_DOWN || ev.getAction() == MotionEvent.ACTION_MOVE)) { + return true; + } + return false; + } + return super.onTouchEvent(ev); + } + + public void enableTouch(boolean enable) { + touchEnabled = enable; + } + + public void setPaused(boolean paused) { + for (int i = 0; i < getChildCount(); i++) { + ((PeerStoriesView) ((FrameLayout) getChildAt(i)).getChildAt(0)).setPaused(paused); + } + } + + public long getCurrentDialogId() { + if (days != null) { + return daysDialogId; + } + if (getCurrentItem() < dialogs.size()) { + return dialogs.get(getCurrentItem()); + } + return 0L; + } + + public void onNextIdle(Runnable runnable) { + this.doOnNextIdle = runnable; + } + + public void setKeyboardHeight(int realKeyboardHeight) { + if (keyboardHeight != realKeyboardHeight) { + keyboardHeight = realKeyboardHeight; + final View view = getCurrentPeerView(); + if (view != null) { + view.requestLayout(); + } + } + } + + float lastProgressToDismiss; + + public void setHorizontalProgressToDismiss(float position) { + if (Math.abs(position) > 1 || lastProgressToDismiss == position) { + return; + } + lastProgressToDismiss = position; + setCameraDistance(getWidth() * 15); + setPivotX(position < 0f ? getWidth() : 0f); + setPivotY(getHeight() * 0.5f); + setRotationY(90f * position); + } + + @Override + public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { + if (disallowIntercept) { + dissallowInterceptCalled = true; + } + super.requestDisallowInterceptTouchEvent(disallowIntercept); + } + + public void lockTouchEvent(long duration) { + touchLocked = true; + onTouchEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0, 0, 0)); + AndroidUtilities.cancelRunOnUIThread(lockTouchRunnable); + AndroidUtilities.runOnUIThread(lockTouchRunnable, duration); + } + + private class PageLayout extends FrameLayout { + + public PeerStoriesView peerStoryView; + + long dialogId; + ArrayList day; + + boolean isVisible; + + public PageLayout(@NonNull Context context) { + super(context); + } + + @Override + protected void dispatchDraw(Canvas canvas) { + if (!isVisible) { + return; + } + super.dispatchDraw(canvas); + } + + public void setVisible(boolean visible) { + if (isVisible != visible) { + isVisible = visible; + invalidate(); + peerStoryView.setIsVisible(visible); + + checkAllowScreenshots(); + } + } + } + +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesVolumeContorl.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesVolumeContorl.java new file mode 100644 index 0000000000..f7c11ef775 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesVolumeContorl.java @@ -0,0 +1,94 @@ +package org.telegram.ui.Stories; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.media.AudioManager; +import android.view.KeyEvent; +import android.view.View; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.ui.Components.AnimatedFloat; + +public class StoriesVolumeContorl extends View { + + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + boolean isVisible; + public StoriesVolumeContorl(Context context) { + super(context); + paint.setColor(Color.WHITE); + } + + Runnable hideRunnuble = new Runnable() { + @Override + public void run() { + isVisible = false; + invalidate(); + } + }; + AnimatedFloat progressToVisible = new AnimatedFloat(this); + AnimatedFloat volumeProgress = new AnimatedFloat(this); + + float currentProgress; + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) { + adjustVolume(true); + return true; + } else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { + adjustVolume(false); + return true; + } + + return super.onKeyDown(keyCode, event); + } + + private void adjustVolume(boolean increase) { + AudioManager audioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); + int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); + int currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC); + + if (increase) { + currentVolume++; + if (currentVolume > maxVolume) { + currentVolume = maxVolume; + } + } else { + currentVolume--; + if (currentVolume < 0) { + currentVolume = 0; + } + } + + + audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, currentVolume, 0); + currentProgress = currentVolume / (float) maxVolume; + if (!isVisible) { + volumeProgress.set(currentProgress, true); + } + invalidate(); + isVisible = true; + AndroidUtilities.cancelRunOnUIThread(hideRunnuble); + AndroidUtilities.runOnUIThread(hideRunnuble, 2000); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + volumeProgress.set(currentProgress); + progressToVisible.set(isVisible ? 1 : 0); + if (progressToVisible.get() != 0) { + float rad = getMeasuredHeight() / 2f; + paint.setAlpha((int) (255 * progressToVisible.get())); + AndroidUtilities.rectTmp.set(0, 0, getMeasuredWidth() * volumeProgress.get(), getMeasuredHeight()); + canvas.drawRoundRect(AndroidUtilities.rectTmp, rad, rad, paint); + } + } + + public void hide() { + AndroidUtilities.cancelRunOnUIThread(hideRunnuble); + hideRunnuble.run(); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryCaptionView.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryCaptionView.java new file mode 100644 index 0000000000..67392a8428 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryCaptionView.java @@ -0,0 +1,983 @@ +package org.telegram.ui.Stories; + +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.LinearGradient; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Point; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.PorterDuffXfermode; +import android.graphics.Shader; +import android.graphics.drawable.GradientDrawable; +import android.os.Build; +import android.text.Layout; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.text.style.CharacterStyle; +import android.text.style.ClickableSpan; +import android.text.style.URLSpan; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.OverScroller; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.graphics.ColorUtils; +import androidx.core.view.ViewCompat; +import androidx.core.widget.NestedScrollView; +import androidx.dynamicanimation.animation.DynamicAnimation; +import androidx.dynamicanimation.animation.SpringAnimation; +import androidx.dynamicanimation.animation.SpringForce; + +import org.checkerframework.checker.units.qual.A; +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.R; +import org.telegram.messenger.Utilities; +import org.telegram.ui.ActionBar.ActionBarPopupWindow; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Cells.TextSelectionHelper; +import org.telegram.ui.Components.AnimatedEmojiDrawable; +import org.telegram.ui.Components.AnimatedEmojiSpan; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.LinkPath; +import org.telegram.ui.Components.LinkSpanDrawable; +import org.telegram.ui.Components.StaticLayoutEx; +import org.telegram.ui.Components.URLSpanMono; +import org.telegram.ui.Components.spoilers.SpoilerEffect; +import org.telegram.ui.Components.spoilers.SpoilersClickDetector; +import org.telegram.ui.Components.spoilers.SpoilersTextView; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; +import java.util.concurrent.atomic.AtomicReference; + +public class StoryCaptionView extends NestedScrollView { + + private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + + TextSelectionHelper.SimpleTextSelectionHelper textSelectionHelper; + + private final SpringAnimation springAnimation; + public StoryCaptionTextView captionTextview; + + private boolean nestedScrollStarted; + private float overScrollY; + private float velocitySign; + private float velocityY; + + private float lastMotionX; + private float lastMotionY; + + private Method abortAnimatedScrollMethod; + private OverScroller scroller; + + private boolean isLandscape; + private int textHash; + private int prevHeight; + + private float backgroundAlpha = 1f; + private boolean dontChangeTopMargin; + private int pendingTopMargin = -1; + FrameLayout captionContainer; + + public boolean disableTouches; + private boolean disableDraw; + + public int blackoutBottomOffset; + + int gradientColor = ColorUtils.setAlphaComponent(Color.BLACK, (int) (255 * 0.2f)); + GradientDrawable topOverlayGradient = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, new int[]{0, gradientColor}); + + public StoryCaptionView(@NonNull Context context, Theme.ResourcesProvider resourcesProvider) { + super(context); + this.captionContainer = new FrameLayout(context); + setClipChildren(false); + setOverScrollMode(View.OVER_SCROLL_NEVER); + + NotificationCenter.listenEmojiLoading(this); + + captionTextview = new StoryCaptionTextView(getContext(), resourcesProvider); + textSelectionHelper = new TextSelectionHelper.SimpleTextSelectionHelper(captionTextview, resourcesProvider); + textSelectionHelper.useMovingOffset = false; + captionContainer.addView(captionTextview, LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT); + addView(captionContainer, new ViewGroup.LayoutParams(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + + paint.setColor(Color.BLACK); + setFadingEdgeLength(AndroidUtilities.dp(12)); + setVerticalFadingEdgeEnabled(true); + setWillNotDraw(false); + + springAnimation = new SpringAnimation(captionTextview, DynamicAnimation.TRANSLATION_Y, 0); + springAnimation.getSpring().setStiffness(100f); + springAnimation.setMinimumVisibleChange(DynamicAnimation.MIN_VISIBLE_CHANGE_PIXELS); + springAnimation.addUpdateListener((animation, value, velocity) -> { + overScrollY = value; + velocityY = velocity; + }); + springAnimation.getSpring().setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY); + + try { + abortAnimatedScrollMethod = NestedScrollView.class.getDeclaredMethod("abortAnimatedScroll"); + abortAnimatedScrollMethod.setAccessible(true); + } catch (Exception e) { + abortAnimatedScrollMethod = null; + FileLog.e(e); + } + + try { + final Field scrollerField = NestedScrollView.class.getDeclaredField("mScroller"); + scrollerField.setAccessible(true); + scroller = (OverScroller) scrollerField.get(this); + } catch (Exception e) { + scroller = null; + FileLog.e(e); + } + } + + public void onLinkLongPress(URLSpan span, View spoilersTextView, Runnable done) { + + } + + public void onLinkClick(CharacterStyle span, View spoilersTextView) { + + } + + public void onEmojiClick(AnimatedEmojiSpan span) { + + } + + @Override + public boolean onInterceptTouchEvent(@NonNull MotionEvent ev) { + if (captionTextview.progressToExpand != 1f || disableTouches || ev.getAction() == MotionEvent.ACTION_DOWN && ev.getY() < captionContainer.getTop() - getScrollY() + captionTextview.getTranslationY()) { + if (touched) { + touched = false; + invalidate(); + } + return false; + } + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + touched = true; + invalidate(); + } else if (touched && (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL)) { + touched = false; + invalidate(); + } + return super.onInterceptTouchEvent(ev); + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + if (captionTextview.progressToExpand != 1f || disableTouches || ev.getAction() == MotionEvent.ACTION_DOWN && ev.getY() < captionContainer.getTop() - getScrollY() + captionTextview.getTranslationY()) { + if (touched) { + touched = false; + invalidate(); + } + return false; + } + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + touched = true; + invalidate(); + } else if (touched && (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL)) { + touched = false; + invalidate(); + } + return super.onTouchEvent(ev); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + updateTopMargin(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec)); + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + public void applyPendingTopMargin() { + dontChangeTopMargin = false; + if (pendingTopMargin >= 0) { + ((MarginLayoutParams) captionContainer.getLayoutParams()).topMargin = pendingTopMargin; + pendingTopMargin = -1; + requestLayout(); + } + } + + public int getPendingMarginTopDiff() { + if (pendingTopMargin >= 0) { + return pendingTopMargin - ((MarginLayoutParams) captionContainer.getLayoutParams()).topMargin; + } else { + return 0; + } + } + + public void updateTopMargin() { + updateTopMargin(getWidth(), getHeight()); + } + + private void updateTopMargin(int width, int height) { + final int marginTop = calculateNewContainerMarginTop(width, height); + if (marginTop >= 0) { + if (dontChangeTopMargin) { + pendingTopMargin = marginTop; + } else { + ((MarginLayoutParams) captionContainer.getLayoutParams()).topMargin = marginTop; + pendingTopMargin = -1; + } + } + } + + public int calculateNewContainerMarginTop(int width, int height) { + if (width == 0 || height == 0) { + return -1; + } + + final StoryCaptionTextView textView = captionTextview; + final CharSequence text = textView.text; + + final int textHash = text.hashCode(); + final boolean isLandscape = AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y; + + if (this.textHash == textHash && this.isLandscape == isLandscape && this.prevHeight == height) { + return -1; + } + + this.textHash = textHash; + this.isLandscape = isLandscape; + this.prevHeight = height; + + textView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST)); + + final Layout layout = textView.fullLayout; + final int lineCount = layout.getLineCount(); + + if (lineCount <= 3) { + return height - textView.getMeasuredHeight(); + } + + int i = Math.min(3, lineCount); + + final int lineHeight = textView.textPaint.getFontMetricsInt(null); + return height - lineHeight * (i + 1);// - AndroidUtilities.dp(8); + } + + public void reset() { + scrollTo(0, 0); + expanded = false; + captionTextview.progressToExpand = 0f; + captionTextview.invalidate(); + } + + public void stopScrolling() { + if (abortAnimatedScrollMethod != null) { + try { + abortAnimatedScrollMethod.invoke(this); + } catch (Exception e) { + FileLog.e(e); + } + } + } + + @Override + public void fling(int velocityY) { + super.fling(velocityY); + this.velocitySign = Math.signum(velocityY); + this.velocityY = 0f; + } + + @Override + public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow, int type) { + consumed[1] = 0; + + if (nestedScrollStarted && (overScrollY > 0 && dy > 0 || overScrollY < 0 && dy < 0)) { + final float delta = overScrollY - dy; + + if (overScrollY > 0) { + if (delta < 0) { + overScrollY = 0; + consumed[1] += dy + delta; + } else { + overScrollY = delta; + consumed[1] += dy; + } + } else { + if (delta > 0) { + overScrollY = 0; + consumed[1] += dy + delta; + } else { + overScrollY = delta; + consumed[1] += dy; + } + } + + captionTextview.setTranslationY(overScrollY); + textSelectionHelper.invalidate(); + return true; + } + + return false; + } + + @Override + public void dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow, int type, @NonNull int[] consumed) { + if (dyUnconsumed != 0) { + final int topMargin = 0;//(isStatusBarVisible() ? AndroidUtilities.statusBarHeight : 0) + ActionBar.getCurrentActionBarHeight(); + final int dy = Math.round(dyUnconsumed * (1f - Math.abs((-overScrollY / (captionContainer.getTop() - topMargin))))); + + if (dy != 0) { + if (!nestedScrollStarted) { + if (!springAnimation.isRunning()) { + int consumedY; + float velocity = scroller != null ? scroller.getCurrVelocity() : Float.NaN; + if (!Float.isNaN(velocity)) { + final float clampedVelocity = Math.min(AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y ? 3000 : 5000, velocity); + consumedY = (int) (dy * clampedVelocity / velocity); + velocity = clampedVelocity * -velocitySign; + } else { + consumedY = dy; + velocity = 0; + } + if (consumedY != 0) { + overScrollY -= consumedY; + captionTextview.setTranslationY(overScrollY); + } + startSpringAnimationIfNotRunning(velocity); + } + } else { + overScrollY -= dy; + captionTextview.setTranslationY(overScrollY); + } + } + } + textSelectionHelper.invalidate(); + } + + private void startSpringAnimationIfNotRunning(float velocityY) { + if (!springAnimation.isRunning()) { + springAnimation.setStartVelocity(velocityY); + springAnimation.start(); + } + if (getScrollY() < AndroidUtilities.dp(2)) { + collapse(); + } + } + + @Override + public boolean startNestedScroll(int axes, int type) { + if (type == ViewCompat.TYPE_TOUCH) { + springAnimation.cancel(); + nestedScrollStarted = true; + overScrollY = captionTextview.getTranslationY(); + } + return true; + } + + @Override + public void computeScroll() { + super.computeScroll(); + if (!nestedScrollStarted && overScrollY != 0 && scroller != null && scroller.isFinished()) { + startSpringAnimationIfNotRunning(0); + } + } + + @Override + public void stopNestedScroll(int type) { + if (nestedScrollStarted && type == ViewCompat.TYPE_TOUCH) { + nestedScrollStarted = false; + if (overScrollY != 0 && scroller != null && scroller.isFinished()) { + startSpringAnimationIfNotRunning(velocityY); + } + } + } + + @Override + protected float getTopFadingEdgeStrength() { + return 1f; + } + + @Override + protected float getBottomFadingEdgeStrength() { + return 1f; + } + + + @Override + public void draw(Canvas canvas) { + if (disableDraw) { + return; + } + + // captionTextview.allowClickSpoilers = !canScrollVertically(1); + + final int width = getWidth(); + final int height = getHeight(); + final int scrollY = getScrollY(); + + final int saveCount = canvas.save(); + canvas.clipRect(0, scrollY, width, height + scrollY + blackoutBottomOffset); + +// int gradientHeight = AndroidUtilities.dp(24); +// int gradientTop = (int) (captionContainer.getTop() + captionTextview.getTranslationY() - AndroidUtilities.dp(4)); +// int gradientBottom = gradientTop + gradientHeight; +// paint.setColor(gradientColor); +// topOverlayGradient.setBounds(0, gradientTop, getMeasuredWidth(), gradientBottom); +// topOverlayGradient.draw(canvas); +// canvas.drawRect(0, gradientBottom, width, height + scrollY + blackoutBottomOffset, paint); + + canvas.clipRect(0, scrollY, width, height + scrollY); + super.draw(canvas); + canvas.restoreToCount(saveCount); + } + + public float getTextTop() { + return captionContainer.getTop() + captionTextview.getTranslationY() - getScrollY(); + } + + public float getMaxTop() { + return captionContainer.getTop() - (captionContainer.getBottom() - getMeasuredHeight()); + } + + public boolean allowInterceptTouchEvent(float x, float y) { + if (captionTextview.progressToExpand == 1f && !disableTouches && y > captionContainer.getTop() - getScrollY() + captionTextview.getTranslationY()) { + return true; + } + return false; + } + + @Override + public void scrollBy(int x, int y) { + super.scrollBy(x, y); + invalidate(); + } + + + @Override + public void invalidate() { + super.invalidate(); + if (getParent() != null) { + ((View) getParent()).invalidate(); + } + textSelectionHelper.invalidate(); + } + + public float getProgressToBlackout() { + int maxHeight = Math.min(prevHeight, AndroidUtilities.dp(40)); + return Utilities.clamp((getScrollY() - captionTextview.getTranslationY()) / maxHeight, 1f, 0); + } + + boolean expanded; + public void expand() { + if (expanded) { + return; + } + expanded = true; + float fromScrollY = getScrollY(); + float toScrollY = (captionContainer.getBottom() - getMeasuredHeight()); + float fromP = captionTextview.progressToExpand; + float toP = 1f; + ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1f); + valueAnimator.addUpdateListener(animation -> { + float value = (float) animation.getAnimatedValue(); + setScrollY((int) AndroidUtilities.lerp(fromScrollY, toScrollY, value)); + captionTextview.progressToExpand = AndroidUtilities.lerp(fromP, toP, value); + captionTextview.invalidate(); + }); + valueAnimator.setDuration(250); + valueAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); + valueAnimator.start(); + //fullScroll(View.FOCUS_DOWN); + } + + public void collapse() { + if (!expanded) { + return; + } + expanded = false; + float fromScrollY = getScrollY(); + float toScrollY = 0; + float fromP = captionTextview.progressToExpand; + float toP = 0; + ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1f); + valueAnimator.addUpdateListener(animation -> { + float value = (float) animation.getAnimatedValue(); + setScrollY((int) AndroidUtilities.lerp(fromScrollY, toScrollY, value)); + captionTextview.progressToExpand = AndroidUtilities.lerp(fromP, toP, value); + captionTextview.invalidate(); + }); + valueAnimator.setDuration(250); + valueAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); + valueAnimator.start(); + } + + public void disableDraw(boolean disableDraw) { + if (this.disableDraw != disableDraw) { + this.disableDraw = disableDraw; + invalidate(); + } + } + + public boolean isTouched() { + return touched; + } + + boolean touched; + + public void cancelTouch() { + //captionTextview.clearPressedLinks(); + touched = false; + } + + public boolean hasScroll() { + return captionContainer.getBottom() - getMeasuredHeight() > 0; + } + + public void checkCancelTextSelection() { + if (textSelectionHelper.isInSelectionMode()) { + textSelectionHelper.getOverlayView(getContext()).checkCancel(lastMotionX, lastMotionY, false); + } + } + + public class StoryCaptionTextView extends View implements TextSelectionHelper.SelectableView, TextSelectionHelper.SimpleSelectabeleView { + + private final PorterDuffColorFilter emojiColorFilter; + private LinkSpanDrawable pressedLink; + private AnimatedEmojiSpan pressedEmoji; + private LinkSpanDrawable.LinkCollector links = new LinkSpanDrawable.LinkCollector(this); + + TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + TextPaint showMorePaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + private final Paint xRefPaint = new Paint(); + private final Paint xRefGradinetPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + int sizeCached = 0; + StaticLayout showMore; + StaticLayout fullLayout; + StaticLayout firstLayout; + LineInfo[] nextLinesLayouts; + + CharSequence text = ""; + int textHeight; + float textX; + float textY; + float progressToExpand; + float showMoreY; + float showMoreX; + + //spoilers + private SpoilersClickDetector clickDetector; + protected List spoilers = new ArrayList<>(); + private Stack spoilersPool = new Stack<>(); + private boolean isSpoilersRevealed; + private Path path = new Path(); + public boolean allowClickSpoilers = true; + + int horizontalPadding; + int verticalPadding; + private AnimatedEmojiSpan.EmojiGroupedSpans fullLayoutEmoji; + private AnimatedEmojiSpan.EmojiGroupedSpans firstLayoutEmoji; + + + public StoryCaptionTextView(Context context, Theme.ResourcesProvider resourcesProvider) { + super(context); + + textPaint.setColor(Color.WHITE); + textPaint.linkColor = Theme.getColor(Theme.key_chat_messageLinkIn, resourcesProvider); + textPaint.setTextSize(AndroidUtilities.dp(15)); + + showMorePaint.setColor(Color.WHITE); + showMorePaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + showMorePaint.setTextSize(AndroidUtilities.dp(16)); + + xRefPaint.setColor(0xff000000); + xRefPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); + + xRefGradinetPaint.setShader(new LinearGradient(0, 0, AndroidUtilities.dp(16), 0, new int[]{0, 0xffffffff}, new float[]{0f, 1f}, Shader.TileMode.CLAMP)); + xRefGradinetPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); + + clickDetector = new SpoilersClickDetector(this, spoilers, (eff, x, y) -> { + if (isSpoilersRevealed) return; + + eff.setOnRippleEndCallback(() -> post(() -> { + isSpoilersRevealed = true; + // invalidateSpoilers(); + })); + + float rad = (float) Math.sqrt(Math.pow(getWidth(), 2) + Math.pow(getHeight(), 2)); + for (SpoilerEffect ef : spoilers) + ef.startRipple(x, y, rad); + }); + + emojiColorFilter = new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN); + } + + public void setText(CharSequence text) { + if (text == null) { + text = ""; + } + isSpoilersRevealed = false; + // invalidateSpoilers(); + this.text = text; + sizeCached = 0; + if (getMeasuredWidth() > 0) { + createLayout(getMeasuredWidth()); + } + requestLayout(); + invalidate(); + } + + @SuppressLint("DrawAllocation") + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int size = widthMeasureSpec + heightMeasureSpec << 16; + horizontalPadding = AndroidUtilities.dp(16); + verticalPadding = AndroidUtilities.dp(8); + if (sizeCached != size) { + sizeCached = size; + createLayout(MeasureSpec.getSize(widthMeasureSpec)); + } + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(verticalPadding * 2 + textHeight, MeasureSpec.EXACTLY)); + } + + private void createLayout(int measuredWidth) { + int width = measuredWidth - horizontalPadding * 2; + fullLayout = makeTextLayout(textPaint, text, width); + textHeight = fullLayout.getHeight(); + textX = horizontalPadding; + textY = verticalPadding; + float space = textPaint.measureText(" "); + if (fullLayout.getLineCount() > 3) { + String showMoreText = LocaleController.getString("ShowMore", R.string.ShowMore); + showMore = makeTextLayout(showMorePaint, showMoreText, width); + + float collapsedY = fullLayout.getLineTop(2) + fullLayout.getTopPadding(); + showMoreY = textY + collapsedY - AndroidUtilities.dpf2(0.3f); + showMoreX = width - horizontalPadding - showMorePaint.measureText(showMoreText); + firstLayout = makeTextLayout(textPaint, text.subSequence(0, fullLayout.getLineEnd(2)), width); + spoilersPool.addAll(spoilers); + spoilers.clear(); + SpoilerEffect.addSpoilers(this, fullLayout, spoilersPool, spoilers); + + float x = fullLayout.getLineRight(2) + space; + if (nextLinesLayouts != null) { + for (int i = 0; i < nextLinesLayouts.length; i++) { + if (nextLinesLayouts[i] == null) { + continue; + } + AnimatedEmojiSpan.release(this, nextLinesLayouts[i].layoutEmoji); + } + } + nextLinesLayouts = new LineInfo[fullLayout.getLineCount() - 3]; + + if (spoilers.isEmpty()) { + for (int line = 3; line < fullLayout.getLineCount(); ++line) { + int s = fullLayout.getLineStart(line), e = fullLayout.getLineEnd(line); + final StaticLayout layout = makeTextLayout(textPaint, text.subSequence(Math.min(s, e), Math.max(s, e)), width); + LineInfo lineInfo = new LineInfo(); + nextLinesLayouts[line - 3] = lineInfo; + lineInfo.staticLayout = layout; + lineInfo.finalX = fullLayout.getLineLeft(line); + lineInfo.finalY = fullLayout.getLineTop(line) + fullLayout.getTopPadding(); + if (x < showMoreX - AndroidUtilities.dp(16)) { + lineInfo.collapsedY = collapsedY; + lineInfo.collapsedX = x; + x += layout.getLineRight(0) + space; + } else { + lineInfo.collapsedY = lineInfo.finalY; + lineInfo.collapsedX = lineInfo.finalX; + } + } + } + } else { + showMore = null; + firstLayout = null; + spoilersPool.addAll(spoilers); + spoilers.clear(); + SpoilerEffect.addSpoilers(this, fullLayout, spoilersPool, spoilers); + } + } + + @Override + protected void onDraw(Canvas canvas) { + if (showMore != null) { + canvas.saveLayerAlpha(textX - horizontalPadding, textY, getMeasuredWidth(), getMeasuredHeight() - verticalPadding, 255, Canvas.ALL_SAVE_FLAG); + } else { + canvas.save(); + } + + canvas.save(); + canvas.translate(textX, textY); + if (links.draw(canvas)) { + invalidate(); + } + canvas.restore(); + + if (!spoilers.isEmpty() || firstLayout == null) { + if (fullLayout != null) { + canvas.save(); + canvas.translate(textX, textY); + if (textSelectionHelper.isInSelectionMode()) { + textSelectionHelper.draw(canvas); + } + drawLayout(fullLayout, canvas); + fullLayoutEmoji = AnimatedEmojiSpan.update(AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES, this, fullLayoutEmoji, fullLayout); + AnimatedEmojiSpan.drawAnimatedEmojis(canvas, fullLayout, fullLayoutEmoji, 0, spoilers, 0, 0, 0, 1f, emojiColorFilter); + canvas.restore(); + } + } else { + if (textSelectionHelper.isInSelectionMode()) { + canvas.save(); + canvas.translate(textX, textY); + textSelectionHelper.draw(canvas); + canvas.restore(); + } + if (firstLayout != null) { + canvas.save(); + canvas.translate(textX, textY); + drawLayout(firstLayout, canvas); + firstLayoutEmoji = AnimatedEmojiSpan.update(AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES, this, firstLayoutEmoji, firstLayout); + AnimatedEmojiSpan.drawAnimatedEmojis(canvas, firstLayout, firstLayoutEmoji, 0, spoilers, 0, 0, 0, 1f, emojiColorFilter); + canvas.restore(); + } + + if (nextLinesLayouts != null) { + for (int i = 0; i < nextLinesLayouts.length; i++) { + LineInfo lineInfo = nextLinesLayouts[i]; + canvas.save(); + if (lineInfo.collapsedX == lineInfo.finalX) { + textPaint.setAlpha((int) (255 * progressToExpand)); + canvas.translate(textX + lineInfo.finalX, textY + lineInfo.finalY); + drawLayout(lineInfo.staticLayout, canvas); + lineInfo.staticLayout.draw(canvas); + lineInfo.layoutEmoji = AnimatedEmojiSpan.update(AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES, this, lineInfo.layoutEmoji, lineInfo.staticLayout); + AnimatedEmojiSpan.drawAnimatedEmojis(canvas, lineInfo.staticLayout, lineInfo.layoutEmoji, 0, spoilers, 0, 0, 0, progressToExpand, emojiColorFilter); + textPaint.setAlpha(255); + } else { + float offsetX = AndroidUtilities.lerp(lineInfo.collapsedX, lineInfo.finalX, progressToExpand); + float offsetY = AndroidUtilities.lerp(lineInfo.collapsedY, lineInfo.finalY, CubicBezierInterpolator.EASE_OUT.getInterpolation(progressToExpand)); + canvas.translate(textX + offsetX, textY + offsetY); + //drawLayout(lineInfo.staticLayout, canvas, -offsetX, -offsetY); + lineInfo.staticLayout.draw(canvas); + lineInfo.layoutEmoji = AnimatedEmojiSpan.update(AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES, this, lineInfo.layoutEmoji, lineInfo.staticLayout); + AnimatedEmojiSpan.drawAnimatedEmojis(canvas, lineInfo.staticLayout, lineInfo.layoutEmoji, 0, spoilers, 0, 0, 0, 1f, emojiColorFilter); + } + canvas.restore(); + } + } + } + + if (showMore != null) { + float showMoreY = this.showMoreY + StoryCaptionView.this.getScrollY(); + float alpha = 1f - Utilities.clamp(progressToExpand / 0.5f, 1f, 0); + xRefGradinetPaint.setAlpha((int) (255 * alpha)); + xRefPaint.setAlpha((int) (255 * alpha)); + showMorePaint.setAlpha((int) (255 * alpha)); + canvas.save(); + canvas.translate(showMoreX - AndroidUtilities.dp(32), showMoreY); + canvas.drawRect(0, 0, AndroidUtilities.dp(32), showMore.getHeight() + verticalPadding, xRefGradinetPaint); + canvas.restore(); + + canvas.drawRect(showMoreX - AndroidUtilities.dp(16), showMoreY, getMeasuredWidth(), showMoreY + showMore.getHeight() + verticalPadding, xRefPaint); + canvas.save(); + canvas.translate(showMoreX, showMoreY); + showMore.draw(canvas); + canvas.restore(); + } + canvas.restore(); + } + + AtomicReference patchedLayout = new AtomicReference<>(); + + private void drawLayout(StaticLayout staticLayout, Canvas canvas) { + if (!spoilers.isEmpty()) { + SpoilerEffect.renderWithRipple(this, false, Color.WHITE, 0, patchedLayout, staticLayout, spoilers, canvas, false); + } else { + staticLayout.draw(canvas); + } + } + + private StaticLayout makeTextLayout(TextPaint textPaint, CharSequence string, int width) { + if (Build.VERSION.SDK_INT >= 24) { + return StaticLayout.Builder.obtain(string, 0, string.length(), textPaint, width).setBreakStrategy(StaticLayout.BREAK_STRATEGY_SIMPLE).setHyphenationFrequency(StaticLayout.HYPHENATION_FREQUENCY_NONE).setAlignment(LocaleController.isRTL ? StaticLayoutEx.ALIGN_RIGHT() : StaticLayoutEx.ALIGN_LEFT()).build(); + } else { + return new StaticLayout(string, textPaint, width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + } + } + + public Paint getPaint() { + return textPaint; + } + + @Override + public CharSequence getText() { + return text; + } + + @Override + public StaticLayout getStaticTextLayout() { + return fullLayout; + } + + public class LineInfo { + private AnimatedEmojiSpan.EmojiGroupedSpans layoutEmoji; + StaticLayout staticLayout; + float collapsedX, collapsedY; + float finalX, finalY; + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (fullLayout == null || disableTouches) { + return false; + } + boolean allowIntercept = true; + if (showMore != null) { + AndroidUtilities.rectTmp.set(showMoreX , showMoreY, showMoreX + showMore.getWidth(), showMoreY + showMore.getHeight()); + if (AndroidUtilities.rectTmp.contains(event.getX(), event.getY())) { + allowIntercept = false; + } + } + boolean linkResult = false; + if (allowIntercept && event.getAction() == MotionEvent.ACTION_DOWN || (pressedLink != null || pressedEmoji != null) && event.getAction() == MotionEvent.ACTION_UP) { + int x = (int) (event.getX() - textX); + int y = (int) (event.getY() - textY); + final int line = fullLayout.getLineForVertical(y); + final int off = fullLayout.getOffsetForHorizontal(line, x); + final float left = fullLayout.getLineLeft(line); + + CharacterStyle touchLink = null; + AnimatedEmojiSpan touchEmoji = null; + if (left <= x && left + fullLayout.getLineWidth(line) >= x && y >= 0 && y <= fullLayout.getHeight()) { + Spannable buffer = new SpannableString(text); + CharacterStyle[] link = buffer.getSpans(off, off, ClickableSpan.class); + if (link == null || link.length == 0) { + link = buffer.getSpans(off, off, URLSpanMono.class); + } + if (link != null && link.length != 0) { + touchLink = link[0]; + if (event.getAction() == MotionEvent.ACTION_DOWN) { + linkResult = true; + links.clear(); + pressedEmoji = null; + pressedLink = new LinkSpanDrawable<>(link[0], null, event.getX(), event.getY()); + pressedLink.setColor(0x6662a9e3); + links.addLink(pressedLink); + int start = buffer.getSpanStart(pressedLink.getSpan()); + int end = buffer.getSpanEnd(pressedLink.getSpan()); + LinkPath path = pressedLink.obtainNewPath(); + path.setCurrentLayout(fullLayout, start, getPaddingTop()); + fullLayout.getSelectionPath(start, end, path); + + final LinkSpanDrawable savedPressedLink = pressedLink; + postDelayed(() -> { + if (savedPressedLink == pressedLink && pressedLink != null && pressedLink.getSpan() instanceof URLSpan) { + onLinkLongPress((URLSpan) pressedLink.getSpan(), this, () -> links.clear()); + pressedLink = null; + } + }, ViewConfiguration.getLongPressTimeout()); + } + } + if (pressedLink == null && !linkResult) { + AnimatedEmojiSpan[] emoji = buffer.getSpans(off, off, AnimatedEmojiSpan.class); + if (emoji != null && emoji.length != 0) { + touchEmoji = emoji[0]; + if (event.getAction() == MotionEvent.ACTION_DOWN) { + linkResult = true; // links.clear(); + pressedLink = null; + pressedEmoji = emoji[0]; + } + } + } + } + if (event.getAction() == MotionEvent.ACTION_UP) { + links.clear(); + if (pressedLink != null && pressedLink.getSpan() == touchLink) { + onLinkClick(pressedLink.getSpan(), this); + } else if (pressedEmoji != null && pressedEmoji == touchEmoji) { + onEmojiClick(pressedEmoji); + } + pressedLink = null; + pressedEmoji = null; + linkResult = true; + } + } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { + clearPressedLinks(); + pressedEmoji = null; + linkResult = true; + } + + boolean b = linkResult || super.onTouchEvent(event); + return b; + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + AnimatedEmojiSpan.release(this, fullLayoutEmoji); + AnimatedEmojiSpan.release(this, firstLayoutEmoji); + if (nextLinesLayouts != null) { + for (int i = 0; i < nextLinesLayouts.length; i++) { + if (nextLinesLayouts[i] == null) { + continue; + } + AnimatedEmojiSpan.release(this, nextLinesLayouts[i].layoutEmoji); + } + } + } + + private void clearPressedLinks() { + links.clear(); + pressedLink = null; + invalidate(); + } + + @Override + public void setPressed(boolean pressed) { + final boolean needsRefresh = pressed != isPressed(); + super.setPressed(pressed); + if (needsRefresh) { + invalidate(); + } + } + + @Override + public void setTranslationY(float translationY) { + if (getTranslationY() != translationY) { + super.setTranslationY(translationY); + StoryCaptionView.this.invalidate(); + } + } + + @Override + public boolean dispatchTouchEvent(MotionEvent event) { + boolean allowIntercept = true; + lastMotionX = event.getX(); + lastMotionY = event.getY(); + if (showMore != null) { + AndroidUtilities.rectTmp.set(showMoreX , showMoreY, showMoreX + showMore.getWidth(), showMoreY + showMore.getHeight()); + if (AndroidUtilities.rectTmp.contains(event.getX(), event.getY())) { + allowIntercept = false; + } + } + if (allowIntercept && allowClickSpoilers && clickDetector.onTouchEvent(event)) return true; +// if (allowIntercept && (expanded || firstLayout == null)) { +// textSelectionHelper.update(textX, textY); +// textSelectionHelper.onTouchEvent(event); +// } + return super.dispatchTouchEvent(event); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryContainsEmojiButton.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryContainsEmojiButton.java new file mode 100644 index 0000000000..8ada537bfe --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryContainsEmojiButton.java @@ -0,0 +1,399 @@ +package org.telegram.ui.Stories; + +import static org.telegram.messenger.AndroidUtilities.dp; + +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.drawable.Drawable; +import android.text.Layout; +import android.text.SpannableString; +import android.text.Spanned; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.text.style.ForegroundColorSpan; +import android.view.View; + +import androidx.annotation.NonNull; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.BotWebViewVibrationEffect; +import org.telegram.messenger.FileRefController; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MediaDataController; +import org.telegram.messenger.R; +import org.telegram.messenger.Utilities; +import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.RequestDelegate; +import org.telegram.tgnet.TLObject; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AnimatedEmojiDrawable; +import org.telegram.ui.Components.AnimatedEmojiSpan; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.EmojiPacksAlert; +import org.telegram.ui.Components.LoadingDrawable; +import org.telegram.ui.Components.StickersAlert; +import org.telegram.ui.Components.TypefaceSpan; + +import java.util.ArrayList; + +public class StoryContainsEmojiButton extends View { + + private final Theme.ResourcesProvider resourcesProvider; + + private final TextPaint textPaint; + private final ColorFilter colorFilter; + private StaticLayout layout; + private float layoutLeft, layoutWidth; + private AnimatedEmojiSpan.EmojiGroupedSpans stack; + + private final LoadingDrawable loadingDrawable; + private final Path loadingPath; + + private ArrayList sets; + private ArrayList inputSets; + private TLRPC.Vector vector; + private boolean emoji, stickers; + private Object parentObject; + private float loadT; + + private int lastContentWidth; + private CharSequence toSetText; + + private int shiftDp = -12; + + public StoryContainsEmojiButton(Context context, int account, TLObject object, Object parentObject, boolean requestStickers, ArrayList captionEmojiSets, Theme.ResourcesProvider resourcesProvider) { + super(context); + this.resourcesProvider = resourcesProvider; + + setMinimumWidth(AndroidUtilities.dp(196)); + setPadding(dp(13), dp(8), dp(13), dp(8)); + setBackground(Theme.createRadSelectorDrawable(Theme.getColor(Theme.key_listSelector, resourcesProvider), 0, 8)); + setClickable(true); + + textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + textPaint.setTextSize(dp(13)); + textPaint.setColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuItem, resourcesProvider)); + colorFilter = new PorterDuffColorFilter(Theme.getColor(Theme.key_actionBarDefaultSubmenuItem, resourcesProvider), PorterDuff.Mode.SRC_IN); + + loadingDrawable = new LoadingDrawable(resourcesProvider); + loadingDrawable.setCallback(this); + loadingDrawable.setColors(Theme.multAlpha(0xffffffff, .2f), Theme.multAlpha(0xffffffff, .05f)); + loadingDrawable.usePath(loadingPath = new Path()); + loadingDrawable.setRadiiDp(4); + + load(account, requestStickers, object, captionEmojiSets, parentObject); + } + + public EmojiPacksAlert getAlert() { + if (inputSets == null) { + AndroidUtilities.shakeViewSpring(this, shiftDp = -shiftDp); + BotWebViewVibrationEffect.APP_ERROR.vibrate(); + return null; + } + return new EmojiPacksAlert(null, getContext(), resourcesProvider, inputSets); + } + + @Override + protected boolean verifyDrawable(@NonNull Drawable who) { + return who == loadingDrawable || super.verifyDrawable(who); + } + + public void setText(CharSequence text) { + if (getMeasuredWidth() <= 0) { + toSetText = text; + return; + } + + final int contentWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight(); + if (contentWidth <= 0) { + toSetText = text; + return; + } + layout = new StaticLayout(text, textPaint, contentWidth, Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false); + layoutLeft = layout.getLineCount() > 0 ? layout.getLineLeft(0) : 0; + layoutWidth = layout.getLineCount() > 0 ? layout.getLineWidth(0) : 0; + stack = AnimatedEmojiSpan.update(AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES, this, stack, layout); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final boolean exactly = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY; + + final int height = getPaddingTop() + AndroidUtilities.lerp(dp(29), layout == null ? dp(29) : layout.getHeight(), loadT) + getPaddingBottom(); + setMeasuredDimension(exactly ? MeasureSpec.getSize(widthMeasureSpec) : getMinimumWidth(), height); + + final int contentWidth = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight(); + if (exactly && (toSetText != null || layout != null && lastContentWidth != contentWidth)) { + setText(toSetText != null ? toSetText : layout.getText()); + toSetText = null; + lastContentWidth = contentWidth; + } + } + + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + + if (loadT < 1) { + loadingDrawable.setAlpha((int) (0xFF * (1f - loadT))); + loadingPath.rewind(); + loadingPath.addRect(getPaddingLeft(), getPaddingTop(), getMeasuredWidth() - getPaddingRight(), getPaddingTop() + dp(12), Path.Direction.CW); + loadingPath.addRect(getPaddingLeft(), getPaddingTop() + dp(12 + 4), getPaddingLeft() + (getMeasuredWidth() - getPaddingRight() - getPaddingLeft()) * .46f, getPaddingTop() + dp(12 + 4 + 12), Path.Direction.CW); +// loadingDrawable.setBounds(getPaddingLeft(), (getMeasuredHeight() - dp(16)) / 2, getMeasuredWidth() - getPaddingRight(), (getMeasuredHeight() + dp(16)) / 2); + loadingDrawable.draw(canvas); + invalidate(); + } + + if (layout != null && loadT > 0) { + canvas.save(); + canvas.translate(getPaddingLeft() - (LocaleController.isRTL ? 0 : layoutLeft), getPaddingTop()); + textPaint.setAlpha((int) (0xFF * loadT)); + layout.draw(canvas); + AnimatedEmojiSpan.drawAnimatedEmojis(canvas, layout, stack, 0, null, 0, 0, 0, loadT, colorFilter); + canvas.restore(); + } + } + + private static Object lastRequestParentObject; + private static TLRPC.Vector lastResponse; + + public void load(int currentAccount, boolean requestStickers, TLObject obj, ArrayList additionalEmojiSets, Object parentObject) { + final boolean animate[] = new boolean[] { true }; + this.parentObject = parentObject; +// final RequestDelegate requestDelegate = (response, error) -> AndroidUtilities.runOnUIThread(() -> { +// if (response != null) { +// if (sets.size() == 1 && sets.get(0).set != null) { +// set(sets.get(0)); +// } else { +// set(sets.size()); +// } +// animateLoad(animate[0]); +// } +// }); + if (requestStickers) { + this.sets = new ArrayList<>(); + this.inputSets = new ArrayList<>(); + emoji = false; + stickers = false; + + final TLRPC.TL_messages_getAttachedStickers req = new TLRPC.TL_messages_getAttachedStickers(); + if (obj instanceof TLRPC.Photo) { + TLRPC.Photo photo = (TLRPC.Photo) obj; + TLRPC.TL_inputStickeredMediaPhoto inputStickeredMediaPhoto = new TLRPC.TL_inputStickeredMediaPhoto(); + inputStickeredMediaPhoto.id = new TLRPC.TL_inputPhoto(); + inputStickeredMediaPhoto.id.id = photo.id; + inputStickeredMediaPhoto.id.access_hash = photo.access_hash; + inputStickeredMediaPhoto.id.file_reference = photo.file_reference; + if (inputStickeredMediaPhoto.id.file_reference == null) { + inputStickeredMediaPhoto.id.file_reference = new byte[0]; + } + req.media = inputStickeredMediaPhoto; + } else if (obj instanceof TLRPC.Document) { + TLRPC.Document document = (TLRPC.Document) obj; + TLRPC.TL_inputStickeredMediaDocument inputStickeredMediaDocument = new TLRPC.TL_inputStickeredMediaDocument(); + inputStickeredMediaDocument.id = new TLRPC.TL_inputDocument(); + inputStickeredMediaDocument.id.id = document.id; + inputStickeredMediaDocument.id.access_hash = document.access_hash; + inputStickeredMediaDocument.id.file_reference = document.file_reference; + if (inputStickeredMediaDocument.id.file_reference == null) { + inputStickeredMediaDocument.id.file_reference = new byte[0]; + } + req.media = inputStickeredMediaDocument; + } + final RequestDelegate requestDelegate = (response, error) -> AndroidUtilities.runOnUIThread(() -> { + if (response == null) { + return; + } + TLRPC.Vector vector = this.vector = (TLRPC.Vector) response; + lastRequestParentObject = parentObject; + lastResponse = vector; + for (int i = 0; i < vector.objects.size(); ++i) { + TLRPC.StickerSetCovered setCovered = (TLRPC.StickerSetCovered) vector.objects.get(i); + sets.add(setCovered); + if (setCovered.set != null) { + inputSets.add(MediaDataController.getInputStickerSet(setCovered.set)); + if (setCovered.set.emojis) { + emoji = true; + } else if (!setCovered.set.masks) { + stickers = true; + } + } + } + final int count = (additionalEmojiSets != null ? additionalEmojiSets.size() : 0) + (sets == null ? 0 : sets.size()); + if (inputSets != null && additionalEmojiSets != null && !additionalEmojiSets.isEmpty()) { + for (int i = 0; i < additionalEmojiSets.size(); ++i) { + TLRPC.InputStickerSet inputStickerSet = additionalEmojiSets.get(i); + long id = inputStickerSet.id; + boolean found = false; + for (int j = 0; j < inputSets.size(); ++j) { + if (inputSets.get(j).id == id) { + found = true; + break; + } + } + if (!found) { + inputSets.add(inputStickerSet); + } + } + emoji = true; + this.vector = null; + } + if (count == 1) { + if (sets.size() >= 1) { + set(sets.get(0)); + } else if (additionalEmojiSets != null && additionalEmojiSets.size() >= 1) { + animate[0] = false; + TLRPC.InputStickerSet inputSet = additionalEmojiSets.get(0); + MediaDataController.getInstance(currentAccount).getStickerSet(inputSet, 0, false, set -> { + set(set); + animateLoad(false); + }); + return; + } else { + set(0); + } + } else { + set(count); + } + animateLoad(animate[0]); + }); + if (lastRequestParentObject == parentObject && lastResponse != null) { + animate[0] = false; + requestDelegate.run(lastResponse, null); + return; + } + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> { + if (error != null && FileRefController.isFileRefError(error.text) && parentObject != null) { + FileRefController.getInstance(currentAccount).requestReference(parentObject, req, requestDelegate); + return; + } + requestDelegate.run(response, error); + }); + } else { + emoji = true; + stickers = false; + this.inputSets = new ArrayList<>(); + inputSets.addAll(additionalEmojiSets); + if (inputSets.size() == 1) { + MediaDataController.getInstance(currentAccount).getStickerSet(inputSets.get(0), 0, false, set -> { + set(set); + animateLoad(true); + }); + } else { + set(inputSets.size()); + animateLoad(false); + } + } + } + + private void set(TLRPC.TL_messages_stickerSet set) { + if (set == null) { + return; + } + SpannableString pack = new SpannableString("x " + set.set.title); + pack.setSpan(new ForegroundColorSpan(Theme.getColor(Theme.key_chat_messageLinkIn, loadingDrawable.resourcesProvider)), 0, pack.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + pack.setSpan(new TypefaceSpan(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)), 0, pack.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + TLRPC.Document document = null; + ArrayList documents = set.documents; + for (int i = 0; i < documents.size(); ++i) { + if (documents.get(i).id == set.set.thumb_document_id) { + document = documents.get(i); + break; + } + } + if (document == null && !documents.isEmpty()) { + document = documents.get(0); + } + CharSequence packString = pack; + if (document != null) { + pack.setSpan(new AnimatedEmojiSpan(document, textPaint.getFontMetricsInt()), 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } else { + packString = pack.subSequence(2, pack.length()); + } + String string; + if (emoji && stickers) { + string = LocaleController.getString(R.string.StoryContainsStickersEmojiFrom); + } else if (emoji) { + string = LocaleController.getString(R.string.StoryContainsEmojiFrom); + } else { + string = LocaleController.getString(R.string.StoryContainsStickersFrom); + } + setText(AndroidUtilities.replaceCharSequence("%s", string, packString)); + } + + private void set(TLRPC.StickerSetCovered set) { + SpannableString pack = new SpannableString("x " + set.set.title); + pack.setSpan(new ForegroundColorSpan(Theme.getColor(Theme.key_chat_messageLinkIn, loadingDrawable.resourcesProvider)), 0, pack.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + pack.setSpan(new TypefaceSpan(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)), 0, pack.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + TLRPC.Document document = set.cover; + if (document == null && set instanceof TLRPC.TL_stickerSetFullCovered) { + ArrayList documents = ((TLRPC.TL_stickerSetFullCovered) set).documents; + for (int i = 0; i < documents.size(); ++i) { + if (documents.get(i).id == set.set.thumb_document_id) { + document = documents.get(i); + } + } + if (document == null && !documents.isEmpty()) { + document = documents.get(0); + } + } + CharSequence packString = pack; + if (document != null) { + pack.setSpan(new AnimatedEmojiSpan(document, textPaint.getFontMetricsInt()), 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } else { + packString = pack.subSequence(2, pack.length()); + } + String string; + if (emoji && stickers) { + string = LocaleController.getString(R.string.StoryContainsStickersEmojiFrom); + } else if (emoji) { + string = LocaleController.getString(R.string.StoryContainsEmojiFrom); + } else { + string = LocaleController.getString(R.string.StoryContainsStickersFrom); + } + setText(AndroidUtilities.replaceCharSequence("%s", string, packString)); + } + + private void set(int setsCount) { + if (emoji && stickers) { + setText(AndroidUtilities.replaceTags(LocaleController.formatPluralString("StoryContainsStickersEmoji", setsCount))); + } else if (emoji) { + setText(AndroidUtilities.replaceTags(LocaleController.formatPluralString("StoryContainsEmoji", setsCount))); + } else { + setText(AndroidUtilities.replaceTags(LocaleController.formatPluralString("StoryContainsStickers", setsCount))); + } + } + + private ValueAnimator loadAnimator; + private void animateLoad(boolean animated) { + if (loadAnimator != null) { + loadAnimator.cancel(); + } + if (animated) { + loadAnimator = ValueAnimator.ofFloat(loadT, 1f); + final boolean remeasure = layout == null || Math.abs(getMeasuredHeight() - (getPaddingTop() + layout.getHeight() + getPaddingBottom())) > dp(3); + loadAnimator.addUpdateListener(anm -> { + loadT = (float) anm.getAnimatedValue(); + invalidate(); + if (remeasure) { + requestLayout(); + } + }); + loadAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + loadAnimator.setStartDelay(150); + loadAnimator.setDuration(400); + loadAnimator.start(); + } else { + loadT = 1f; + invalidate(); + post(this::requestLayout); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryLinesDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryLinesDrawable.java new file mode 100644 index 0000000000..84101258ab --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryLinesDrawable.java @@ -0,0 +1,116 @@ +package org.telegram.ui.Stories; + +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.AndroidUtilities.rectTmp; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.RectF; +import android.view.View; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.Utilities; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.CubicBezierInterpolator; + +public class StoryLinesDrawable { + + private final View view; + private final PeerStoriesView.SharedResources sharedResources; + + private final AnimatedFloat scrollT; + + public StoryLinesDrawable(View view, PeerStoriesView.SharedResources sharedResources) { + this.view = view; + this.sharedResources = sharedResources; + scrollT = new AnimatedFloat(view, 0, 230, CubicBezierInterpolator.EASE_OUT_QUINT); + } + + + float bufferingProgress; + boolean incrementBuffering; + int lastPosition; + + + public void draw( + Canvas canvas, + int fullWidth, + int index, + float progress, + int count, + float hideInterfaceAlpha, + float alpha, + boolean buffering + ) { + if (count <= 0) { + return; + } + if (lastPosition != index) { + bufferingProgress = 0; + incrementBuffering = true; + } + lastPosition = index; + + Paint barPaint = sharedResources.barPaint; + Paint selectedBarPaint = sharedResources.selectedBarPaint; + + //final int minStoryWidth = AndroidUtilities.dp(4); + int gapWidth; + if (count > 100) { + gapWidth = 1; + } else if (count >= 50) { + gapWidth = dp(1); + } else { + gapWidth = dp(2); + } + final float sectionWidth = (fullWidth - dp(5 * 2) - gapWidth * (count - 1)) / (float) count; + // final int scrollWidth = sectionWidth * count + dp(2) * (count - 1); + final float indexX = dp(5) + gapWidth * index + sectionWidth * (index + .5f); + final float scrollX = 0;//scrollT.set(Utilities.clamp(indexX - fullWidth / 2f, scrollWidth - (fullWidth - dp(10)), 0)); + + float roundRadius = Math.min(sectionWidth / 2f, dp(1)); + selectedBarPaint.setAlpha((int) (255 * alpha * hideInterfaceAlpha)); + for (int a = 0; a < count; a++) { + float x = -scrollX + dp(5) + gapWidth * a + sectionWidth * a; + if (x > fullWidth || x + sectionWidth < 0) { + continue; + } + float currentProgress; + int baseAlpha = 0x55; + if (a <= index) { + if (a == index) { + currentProgress = progress; + rectTmp.set(x, 0, x + sectionWidth, dp(2)); + int bufferingAlpha = 0; + if (buffering) { + if (incrementBuffering) { + bufferingProgress += 16 / 600f; + if (bufferingProgress > 0.5f) { + incrementBuffering = false; + } + } else { + bufferingProgress -= 16 / 600f; + if (bufferingProgress < -0.5f) { + incrementBuffering = true; + } + } + bufferingAlpha = (int) ((0x33) * alpha * hideInterfaceAlpha * bufferingProgress); + } + barPaint.setAlpha((int) ((0x55) * alpha * hideInterfaceAlpha) + bufferingAlpha); + canvas.drawRoundRect(rectTmp, roundRadius, roundRadius, barPaint); + // baseAlpha = 0x50; + } else { + currentProgress = 1.0f; + } + } else { + currentProgress = 1.0f; + } + rectTmp.set(x, 0, x + sectionWidth * currentProgress, dp(2)); + + if (a > index) { + barPaint.setAlpha((int) (baseAlpha * alpha * hideInterfaceAlpha)); + } + canvas.drawRoundRect(rectTmp, roundRadius, roundRadius, a <= index ? selectedBarPaint : barPaint); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryPositionView.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryPositionView.java new file mode 100644 index 0000000000..8c058ddbc8 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryPositionView.java @@ -0,0 +1,70 @@ +package org.telegram.ui.Stories; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.text.SpannableStringBuilder; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.widget.FrameLayout; + +import androidx.core.graphics.ColorUtils; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.Utilities; +import org.telegram.ui.Cells.DialogCell; +import org.telegram.ui.Components.AnimatedTextView; + +public class StoryPositionView { + + private final SpannableStringBuilder leftSpace; + private final SpannableStringBuilder rightSpace; + AnimatedTextView.AnimatedTextDrawable textDrawable = new AnimatedTextView.AnimatedTextDrawable(true, true, true); + int lastHash; + Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + public StoryPositionView() { + textDrawable.setTextSize(AndroidUtilities.dp(13)); + textDrawable.setTextColor(Color.WHITE); + textDrawable.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + + backgroundPaint.setColor(ColorUtils.setAlphaComponent(Color.BLACK, (int) (255 * 0.23f))); + + leftSpace = new SpannableStringBuilder(); + leftSpace.append(" ").setSpan(new DialogCell.FixedWidthSpan(AndroidUtilities.dp(1)), 0, 1, 0); + + rightSpace = new SpannableStringBuilder(); + rightSpace.append(" ").setSpan(new DialogCell.FixedWidthSpan(AndroidUtilities.dp(1)), 0, 1, 0); + } + + public void draw(Canvas canvas, float alpha, int linesPosition, int linesCount, FrameLayout container, PeerStoriesView.PeerHeaderView headerView) { + int hash = (linesCount << 12) + linesPosition; + if (lastHash != hash) { + lastHash = hash; + SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(); + spannableStringBuilder.append(String.valueOf(linesPosition + 1)).append(leftSpace).append("/").append(rightSpace).append(String.valueOf(linesCount)); + textDrawable.setText(spannableStringBuilder, false); + } + canvas.save(); + float top = headerView.getY() + headerView.titleView.getTop() + textDrawable.getHeight() / 2f - 1;// + //(headerView.titleView.getMeasuredHeight() - textDrawable.getHeight()) / 2f + AndroidUtilities.dp(1); + int rightPadding = (int) textDrawable.getCurrentWidth(); + headerView.titleView.setRightPadding(rightPadding); + float left = AndroidUtilities.dp(4) + headerView.getLeft() + headerView.titleView.getLeft() + headerView.titleView.getTextWidth(); + left -= Utilities.clamp(headerView.titleView.getTextWidth() + rightPadding - headerView.titleView.getWidth(), rightPadding, 0); + canvas.translate(left, top); + + float horizontalPadding = AndroidUtilities.dp(8); + float verticalPadding = AndroidUtilities.dp(2); + + AndroidUtilities.rectTmp.set(-horizontalPadding, -verticalPadding, textDrawable.getCurrentWidth() + horizontalPadding, textDrawable.getHeight() + verticalPadding); + // float r = AndroidUtilities.rectTmp.height() / 2f; + // canvas.drawRoundRect(AndroidUtilities.rectTmp, r, r, backgroundPaint); + + // canvas.clipRect(AndroidUtilities.rectTmp); + //canvas.translate(0, textDrawable.getHeight() / 2f - AndroidUtilities.dpf2(0.5f)); + textDrawable.setAlpha((int) (160 * alpha)); + textDrawable.draw(canvas); + + canvas.restore(); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryPrivacyButton.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryPrivacyButton.java new file mode 100644 index 0000000000..e401c71fd6 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryPrivacyButton.java @@ -0,0 +1,214 @@ +package org.telegram.ui.Stories; + +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.AndroidUtilities.dpf2; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.LinearGradient; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.Shader; +import android.graphics.drawable.Drawable; +import android.util.Log; +import android.view.View; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.R; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.ButtonBounce; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Stories.recorder.StoryPrivacyBottomSheet; + +public class StoryPrivacyButton extends View { + + private int topColor, bottomColor; + private final Matrix gradientMatrix = new Matrix(); + private final Paint[] backgroundPaint = new Paint[2]; + private final AnimatedFloat crossfadeT = new AnimatedFloat(this, 0, 260, CubicBezierInterpolator.EASE_OUT_QUINT); + + public boolean draw; + private int iconResId; + private final Drawable[] icon = new Drawable[2]; + private final float[] iconSize = new float[2]; + + private boolean drawArrow; + private final Paint arrowPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Path arrowPath = new Path(); + + private final ButtonBounce bounce = new ButtonBounce(this, .6f); + + public StoryPrivacyButton(Context context) { + super(context); + backgroundPaint[0] = new Paint(Paint.ANTI_ALIAS_FLAG); + backgroundPaint[1] = new Paint(Paint.ANTI_ALIAS_FLAG); + arrowPaint.setStyle(Paint.Style.STROKE); + arrowPaint.setStrokeCap(Paint.Cap.ROUND); + arrowPaint.setStrokeJoin(Paint.Join.ROUND); + arrowPaint.setColor(Color.WHITE); + } + + public boolean set(boolean mine, TLRPC.StoryItem storyItem, boolean animated) { + drawArrow = mine; + draw = true; + if (storyItem == null) { + draw = false; + } else if (storyItem.close_friends) { + setIcon(R.drawable.msg_stories_closefriends, 15); + setupGradient(0xFF88D93A, 0xFF2DB63B); + crossfadeT.set(animated, true); + } else if (storyItem.contacts) { + setIcon(R.drawable.msg_folders_private, 17.33f); + setupGradient(0xFFC468F2, 0xFF965CFA); + crossfadeT.set(animated, true); + } else if (storyItem.selected_contacts) { + setIcon(R.drawable.msg_folders_groups, 17.33f); + setupGradient(0xFFFFB743, 0xFFF68E34); + crossfadeT.set(animated, true); + } else if (mine) { + setIcon(R.drawable.msg_folders_channels, 17.33f); + setupGradient(0xFF16A5F2, 0xFF1180F7); + crossfadeT.set(animated, true); + } else { + draw = false; + } + setVisibility(draw ? View.VISIBLE : View.GONE); + invalidate(); + return draw; + } + + public boolean set(boolean mine, StoriesController.UploadingStory uploadingStory, boolean animated) { + drawArrow = mine; + draw = true; + if (uploadingStory == null || uploadingStory.entry.privacy == null) { + draw = false; + } else if (uploadingStory.entry.privacy.type == StoryPrivacyBottomSheet.TYPE_CLOSE_FRIENDS) { + setIcon(R.drawable.msg_stories_closefriends, 15); + setupGradient(0xFF88D93A, 0xFF2DB63B); + crossfadeT.set(animated, !animated); + } else if (uploadingStory.entry.privacy.type == StoryPrivacyBottomSheet.TYPE_CONTACTS) { + setIcon(R.drawable.msg_folders_private, 17.33f); + setupGradient(0xFFC468F2, 0xFF965CFA); + crossfadeT.set(animated, !animated); + } else if (uploadingStory.entry.privacy.type == StoryPrivacyBottomSheet.TYPE_SELECTED_CONTACTS) { + setIcon(R.drawable.msg_folders_groups, 17.33f); + setupGradient(0xFFFFB743, 0xFFF68E34); + crossfadeT.set(animated, !animated); + } else if (mine) { + setIcon(R.drawable.msg_folders_channels, 17.33f); + setupGradient(0xFF16A5F2, 0xFF1180F7); + crossfadeT.set(animated, !animated); + } else { + draw = false; + } + setVisibility(draw ? View.VISIBLE : View.GONE); + invalidate(); + return draw; + } + + private void setIcon(int resId, float sz) { + icon[1] = icon[0]; + iconSize[1] = iconSize[0]; + if (icon[0] == null || resId != iconResId) { + icon[0] = getContext().getResources().getDrawable(iconResId = resId).mutate(); + icon[0].setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)); + iconSize[0] = dpf2(sz); + invalidate(); + } + } + + private void setupGradient(int top, int bottom) { + backgroundPaint[1].setShader(backgroundPaint[0].getShader()); + if (topColor != top || bottomColor != bottom) { + LinearGradient gradient = new LinearGradient(0, 0, 0, dp(23), new int[] { topColor = top, bottomColor = bottom }, new float[] { 0, 1 }, Shader.TileMode.CLAMP); + gradientMatrix.reset(); + gradientMatrix.postTranslate(0, dp(8)); + gradient.setLocalMatrix(gradientMatrix); + backgroundPaint[0].setShader(gradient); + invalidate(); + } + } + + @Override + protected void onDraw(Canvas canvas) { + if (!draw) { + return; + } + + final float tx = drawArrow ? 0 : dpf2(7); + final float w = drawArrow ? dpf2(43) : dpf2(23.66f); + final float h = dpf2(23.66f); + AndroidUtilities.rectTmp.set( + tx + (getWidth() - w) / 2f, + (getHeight() - h) / 2f, + tx + (getWidth() + w) / 2f, + (getHeight() + h) / 2f + ); + + final float scale = bounce.getScale(0.075f); + canvas.save(); + canvas.scale(scale, scale, AndroidUtilities.rectTmp.centerX(), AndroidUtilities.rectTmp.centerY()); + + final float crossfade = crossfadeT.set(0); + if (crossfade > 0) { + backgroundPaint[1].setAlpha(0xFF); + canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(12), dp(12), backgroundPaint[1]); + } + if (crossfade < 1) { + backgroundPaint[0].setAlpha((int) (0xFF * (1f - crossfade))); + canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(12), dp(12), backgroundPaint[0]); + } + + final float iconScale = 0.5f + Math.abs(crossfade - 0.5f); + if (icon[1] != null && crossfade > .5f) { + float cx = drawArrow ? AndroidUtilities.rectTmp.left + dpf2(14.66f) : AndroidUtilities.rectTmp.centerX(); + icon[1].setBounds( + (int) (cx - iconSize[1] / 2 * iconScale), + (int) (AndroidUtilities.rectTmp.centerY() - iconSize[1] / 2f * iconScale), + (int) (cx + iconSize[1] / 2 * iconScale), + (int) (AndroidUtilities.rectTmp.centerY() + iconSize[1] / 2f * iconScale) + ); + icon[1].draw(canvas); + } + if (icon[0] != null && crossfade <= .5f) { + float cx = drawArrow ? AndroidUtilities.rectTmp.left + dpf2(14.66f) : AndroidUtilities.rectTmp.centerX(); + icon[0].setBounds( + (int) (cx - iconSize[0] / 2 * iconScale), + (int) (AndroidUtilities.rectTmp.centerY() - iconSize[0] / 2f * iconScale), + (int) (cx + iconSize[0] / 2 * iconScale), + (int) (AndroidUtilities.rectTmp.centerY() + iconSize[0] / 2f * iconScale) + ); + icon[0].draw(canvas); + } + if (drawArrow) { + arrowPath.rewind(); + arrowPath.moveTo(AndroidUtilities.rectTmp.right - dpf2(15.66f), AndroidUtilities.rectTmp.centerY() - dpf2(1.33f)); + arrowPath.lineTo(AndroidUtilities.rectTmp.right - dpf2(12), AndroidUtilities.rectTmp.centerY() + dpf2(2.33f)); + arrowPath.lineTo(AndroidUtilities.rectTmp.right - dpf2(8.16f), AndroidUtilities.rectTmp.centerY() - dpf2(1.33f)); + arrowPaint.setStrokeWidth(dpf2(1.33f)); + canvas.drawPath(arrowPath, arrowPaint); + } + + canvas.restore(); + } + + @Override + public void setPressed(boolean pressed) { + super.setPressed(pressed); + bounce.setPressed(pressed); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(MeasureSpec.makeMeasureSpec(dp(60), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(dp(40), MeasureSpec.EXACTLY)); + } + + public float getCenterX() { + return getX() + getWidth() / 2f + (drawArrow ? 0 : dp(14)); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryViewer.java new file mode 100644 index 0000000000..62fe0795f5 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryViewer.java @@ -0,0 +1,2692 @@ +package org.telegram.ui.Stories; + +import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PixelFormat; +import android.graphics.RectF; +import android.graphics.SurfaceTexture; +import android.media.AudioManager; +import android.net.Uri; +import android.os.Build; +import android.util.SparseArray; +import android.view.GestureDetector; +import android.view.Gravity; +import android.view.HapticFeedbackConstants; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.SurfaceView; +import android.view.TextureView; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowInsets; +import android.view.WindowManager; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.graphics.ColorUtils; +import androidx.core.math.MathUtils; +import androidx.viewpager.widget.ViewPager; + +import com.google.android.exoplayer2.ExoPlayer; +import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimationNotificationsLocker; +import org.telegram.messenger.BuildVars; +import org.telegram.messenger.DispatchQueue; +import org.telegram.messenger.FileLoader; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.FileStreamLoadOperation; +import org.telegram.messenger.ImageReceiver; +import org.telegram.messenger.MessageObject; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.SharedConfig; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.Utilities; +import org.telegram.messenger.support.LongSparseIntArray; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.AdjustPanLayoutHelper; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Cells.ChatActionCell; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.RadialProgress; +import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.Components.SizeNotifierFrameLayout; +import org.telegram.ui.Components.VideoPlayer; +import org.telegram.ui.LaunchActivity; + +import java.util.ArrayList; +import java.util.Objects; + +public class StoryViewer { + + public static boolean animationInProgress; + + public boolean USE_SURFACE_VIEW = SharedConfig.useSurfaceInStories; + public boolean ATTACH_TO_FRAGMENT = true; + public boolean foundViewToClose = false; + + public int allowScreenshotsCounter; + public boolean allowScreenshots = true; + public static ArrayList globalInstances = new ArrayList<>(); + + BaseFragment fragment; + public int currentAccount; + WindowManager windowManager; + WindowManager.LayoutParams windowLayoutParams; + public SizeNotifierFrameLayout windowView; + HwFrameLayout containerView; + SelfStoryViewsView selfStoryViewsView; + + Paint inputBackgroundPaint; + boolean keyboardVisible; + private static TLRPC.StoryItem lastStoryItem; + + Theme.ResourcesProvider resourcesProvider = new DarkThemeResourceProvider(); + ValueAnimator openCloseAnimator; + ValueAnimator swipeToDissmissBackAnimator; + ValueAnimator swipeToReplyBackAnimator; + + long lastDialogId; + int lastPosition; + + float fromXCell; + float fromYCell; + StoriesListPlaceProvider.AvatarOverlaysView animateFromCell; + float fromX; + float fromY; + + float clipTop; + float clipBottom; + + float fromWidth; + float fromHeight; + + RectF avatarRectTmp = new RectF(); + float progressToOpen; + float progressToDismiss; + float swipeToDismissOffset; + float swipeToDismissHorizontalOffset; + float swipeToDismissHorizontalDirection; + float swipeToReplyOffset; + boolean swipeToReplyWaitingKeyboard; + + float fromDismissOffset; + + boolean allowSelfStoriesView; + float swipeToReplyProgress; + float progressToSelfStoryViewsViews; + float selfStoriesViewsOffset; + + + boolean allowIntercept; + boolean allowSwipeToDissmiss; + GestureDetector gestureDetector; + boolean inSwipeToDissmissMode; + boolean allowSwipeToReply; + boolean isShowing; + public StoriesViewPager storiesViewPager; + float pointPosition[] = new float[2]; + + private int realKeyboardHeight; + private boolean isInTouchMode; + private float hideEnterViewProgress; + public final TransitionViewHolder transitionViewHolder = new TransitionViewHolder(); + private PlaceProvider placeProvider; + Dialog currentDialog; + private boolean allowTouchesByViewpager = false; + boolean openedFromLightNavigationBar; + Runnable doOnAnimationReady; + + // to prevent attach/detach textureView in view pager and + // ensure that player is singleton + // create and attach texture view in contentView + // draw it in page + AspectRatioFrameLayout aspectRatioFrameLayout; + VideoPlayerHolder playerHolder; + private TextureView textureView; + private SurfaceView surfaceView; + Uri lastUri; + PeerStoriesView.VideoPlayerSharedScope currentPlayerScope; + private boolean isClosed; + private boolean isRecording; + AnimationNotificationsLocker locker = new AnimationNotificationsLocker(); + private boolean isWaiting; + private boolean fullyVisible; + private boolean isCaption; + LaunchActivity parentActivity; + ArrayList preparedPlayers = new ArrayList<>(); + + boolean isSingleStory; + StoriesController.StoriesList storiesList; + public int dayStoryId; + TLRPC.TL_userStories overrideUserStories; + boolean reversed; + + TLRPC.StoryItem singleStory; + private int messageId; + private boolean animateAvatar; + private int fromRadius; + private static boolean runOpenAnimationAfterLayout; + private boolean isPopupVisible; + private boolean isBulletinVisible; + + public boolean isLongpressed; + + Runnable longPressRunnable = () -> setLongPressed(true); + + public boolean unreadStateChanged; + private StoriesVolumeContorl volumeControl; + private static boolean checkSilentMode = true; + private static boolean isInSilentMode; + + public LongSparseIntArray savedPositions = new LongSparseIntArray(); + private boolean isInPinchToZoom; + private boolean flingCalled; + private boolean invalidateOutRect; + private boolean isHintVisible; + private boolean isInTextSelectionMode; + private boolean isOverlayVisible; + Bitmap playerStubBitmap; + public Paint playerStubPaint; + private boolean isSwiping; + private boolean isCaptionPartVisible; + private Runnable delayedTapRunnable; + private Runnable onCloseListener; + + public static boolean isShowingImage(MessageObject messageObject) { + if (lastStoryItem == null || messageObject.type != MessageObject.TYPE_STORY && !messageObject.isWebpage() || runOpenAnimationAfterLayout) { + return false; + } + return lastStoryItem.messageId == messageObject.getId() && lastStoryItem.messageType != 3; + } + + public static void closeGlobalInstances() { + for (int i = 0; i < globalInstances.size(); i++) { + globalInstances.get(i).close(false); + } + globalInstances.clear(); + } + + private void setLongPressed(boolean b) { + if (isLongpressed != b) { + isLongpressed = b; + updatePlayingMode(); + if (storiesViewPager != null) { + PeerStoriesView peerStoriesView = storiesViewPager.getCurrentPeerView(); + if (peerStoriesView != null) { + peerStoriesView.setLongpressed(isLongpressed); + } + } + } + } + + public StoryViewer(BaseFragment fragment) { + inputBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + this.fragment = fragment; + } + + public void open(Context context, TLRPC.StoryItem storyItem, PlaceProvider placeProvider) { + if (storyItem == null) { + return; + } + currentAccount = UserConfig.selectedAccount; + ArrayList peerIds = new ArrayList<>(); + if (storyItem != null) { + peerIds.add(storyItem.dialogId); + } + open(context, storyItem, peerIds, 0, null, null, placeProvider, false); + } + + public void open(Context context, long dialogId, PlaceProvider placeProvider) { + currentAccount = UserConfig.selectedAccount; + int position = 0; + ArrayList peerIds = new ArrayList<>(); + peerIds.add(dialogId); + MessagesController.getInstance(currentAccount).getStoriesController().checkExpiredStories(dialogId); + open(context, null, peerIds, position, null, null, placeProvider, false); + } + + public void open(Context context, int startStoryId, StoriesController.StoriesList storiesList, PlaceProvider placeProvider) { + currentAccount = UserConfig.selectedAccount; + ArrayList peerIds = new ArrayList<>(); + peerIds.add(storiesList.userId); + dayStoryId = startStoryId; + open(context, null, peerIds, 0, storiesList, null, placeProvider, false); + } + + public void open(Context context, TLRPC.TL_userStories userStories, PlaceProvider placeProvider) { + if (userStories == null || userStories.stories == null || userStories.stories.isEmpty()) { + return; + } + currentAccount = UserConfig.selectedAccount; + ArrayList peerIds = new ArrayList<>(); + peerIds.add(userStories.user_id); + open(context, userStories.stories.get(0), peerIds, 0, null, userStories, placeProvider, false); + } + + public void open(Context context, TLRPC.StoryItem storyItem, int startStoryId, StoriesController.StoriesList storiesList, boolean reversed, PlaceProvider placeProvider) { + currentAccount = UserConfig.selectedAccount; + ArrayList peerIds = new ArrayList<>(); + peerIds.add(storiesList.userId); + dayStoryId = startStoryId; + open(context, storyItem, peerIds, 0, storiesList, null, placeProvider, reversed); + } + + @SuppressLint("WrongConstant") + public void open(Context context, TLRPC.StoryItem storyItem, ArrayList peerIds, int position, StoriesController.StoriesList storiesList, TLRPC.TL_userStories userStories, PlaceProvider placeProvider, boolean reversed) { + if (context == null) { + return; + } + if (openCloseAnimator != null) { + openCloseAnimator.cancel(); + openCloseAnimator = null; + } + if (isShowing) { + return; + } + ATTACH_TO_FRAGMENT = !AndroidUtilities.isTablet(); + USE_SURFACE_VIEW = SharedConfig.useSurfaceInStories && ATTACH_TO_FRAGMENT; + messageId = storyItem == null ? 0 : storyItem.messageId; + isSingleStory = storyItem != null && storiesList == null && userStories == null; + if (storyItem != null) { + singleStory = storyItem; + lastStoryItem = storyItem; + } + this.storiesList = storiesList; + overrideUserStories = userStories; + this.placeProvider = placeProvider; + this.reversed = reversed; + currentAccount = UserConfig.selectedAccount; + swipeToDismissOffset = 0; + swipeToDismissHorizontalOffset = 0; + if (storiesViewPager != null) { + storiesViewPager.setHorizontalProgressToDismiss(0); + } + swipeToReplyProgress = 0; + swipeToReplyOffset = 0; + allowSwipeToReply = false; + progressToDismiss = 0; + isShowing = true; + isLongpressed = false; + savedPositions.clear(); + AndroidUtilities.cancelRunOnUIThread(longPressRunnable); + windowLayoutParams = new WindowManager.LayoutParams(); + windowLayoutParams.height = WindowManager.LayoutParams.MATCH_PARENT; + windowLayoutParams.format = PixelFormat.TRANSLUCENT; + windowLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT; + windowLayoutParams.gravity = Gravity.TOP | Gravity.LEFT; + windowLayoutParams.type = WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; + + windowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; + + if (Build.VERSION.SDK_INT >= 28) { + windowLayoutParams.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; + } + if (Build.VERSION.SDK_INT >= 21) { + windowLayoutParams.flags = + WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | + WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR | + WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS | + WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; + } + isClosed = false; + unreadStateChanged = false; + + BaseFragment fragment = LaunchActivity.getLastFragment(); + if (windowView == null) { + gestureDetector = new GestureDetector(new GestureDetector.OnGestureListener() { + @Override + public boolean onDown(@NonNull MotionEvent e) { + flingCalled = false; + if (findClickableView(windowView, e.getX(), e.getY(), false)) { + return false; + } + return true; + } + + @Override + public void onShowPress(@NonNull MotionEvent e) { + + } + + @Override + public boolean onSingleTapUp(@NonNull MotionEvent e) { + PeerStoriesView peerView = storiesViewPager.getCurrentPeerView(); + if (selfStoriesViewsOffset != 0) { + return false; + } + if (allowIntercept && peerView != null) { + if (keyboardVisible || isCaption || isCaptionPartVisible || isHintVisible || isInTextSelectionMode) { + closeKeyboardOrEmoji(); + } else { + boolean forward = e.getX() > containerView.getMeasuredWidth() * 0.33f; + if (!peerView.switchToNext(forward)) { + boolean switchToNext = storiesViewPager.switchToNext(forward); + if (!switchToNext && forward) { + close(true); + } else if (switchToNext) { + storiesViewPager.lockTouchEvent(150); + } + } + } + } + return false; + } + + @Override + public boolean onScroll(@NonNull MotionEvent e1, @NonNull MotionEvent e2, float distanceX, float distanceY) { + if (inSwipeToDissmissMode) { + if (allowSwipeToReply) { + swipeToReplyOffset += distanceY; + int maxOffset = AndroidUtilities.dp(200); + if (swipeToReplyOffset > maxOffset && !swipeToReplyWaitingKeyboard) { + swipeToReplyWaitingKeyboard = true; + showKeyboard(); + windowView.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + } + swipeToReplyProgress = Utilities.clamp(swipeToReplyOffset / maxOffset, 1f, 0); + storiesViewPager.getCurrentPeerView().invalidate(); + if (swipeToReplyOffset < 0) { + swipeToReplyOffset = 0; + allowSwipeToReply = false; + } else { + return true; + } + } + if (allowSelfStoriesView) { + if (selfStoriesViewsOffset > selfStoryViewsView.maxSelfStoriesViewsOffset && distanceY > 0) { + selfStoriesViewsOffset += distanceY * 0.05f; + } else { + selfStoriesViewsOffset += distanceY; + } + storiesViewPager.getCurrentPeerView().invalidate(); + containerView.invalidate(); + if (selfStoriesViewsOffset < 0) { + selfStoriesViewsOffset = 0; + allowSelfStoriesView = false; + } else { + return true; + } + } + float k = 0.6f; + if (progressToDismiss > 0.8f && ((-distanceY > 0 && swipeToDismissOffset > 0) || (-distanceY < 0 && swipeToDismissOffset < 0))) { + k = 0.3f; + } + swipeToDismissOffset -= distanceY * k; + updateProgressToDismiss(); + return true; + } + return false; + } + + @Override + public void onLongPress(@NonNull MotionEvent e) { + + } + + @Override + public boolean onFling(@NonNull MotionEvent e1, @NonNull MotionEvent e2, float velocityX, float velocityY) { + if (swipeToReplyOffset != 0) { + if (velocityY < -1000 && !swipeToReplyWaitingKeyboard) { + swipeToReplyWaitingKeyboard = true; + windowView.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + showKeyboard(); + } + } + if (selfStoriesViewsOffset != 0) { + if (velocityY < -1000) { + cancelSwipeToViews(true); + } else { + if (velocityY > 1000) { + cancelSwipeToViews(false); + } else { + cancelSwipeToViews(selfStoryViewsView.progressToOpen > 0.5f); + } + } + } + flingCalled = true; + return false; + } + }); + windowView = new SizeNotifierFrameLayout(context) { + + float startY; + float startX; + + final Path path = new Path(); + final RectF rect1 = new RectF(); + final RectF rect2 = new RectF(); + final RectF rect3 = new RectF(); + final RectF outFromRectAvatar = new RectF(); + final RectF outFromRectContainer = new RectF(); + + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + if (child == aspectRatioFrameLayout) { + return false; + } + return super.drawChild(canvas, child, drawingTime); + } + + + @Override + protected void dispatchDraw(Canvas canvas) { + float blackoutAlpha = getBlackoutAlpha(); + canvas.drawColor(ColorUtils.setAlphaComponent(Color.BLACK, (int) (255 * blackoutAlpha))); + + if (ATTACH_TO_FRAGMENT) { + boolean localFullyVisible = progressToOpen * (1f - progressToDismiss) == 1f; + if (fullyVisible != localFullyVisible) { + fullyVisible = localFullyVisible; + if (fragment.getLayoutContainer() != null) { + fragment.getLayoutContainer().invalidate(); + } + } + } + + PeerStoriesView currentView = storiesViewPager.getCurrentPeerView(); + PeerStoriesView.PeerHeaderView headerView = null; + if (currentView != null) { + headerView = currentView.headerView; + if (animateAvatar) { + headerView.backupImageView.getImageReceiver().setVisible(progressToOpen == 1f, true); + } else { + headerView.backupImageView.getImageReceiver().setVisible(true, false); + } + + if (invalidateOutRect) { + invalidateOutRect = false; + View child = headerView.backupImageView; + float toX = 0, toY = 0; + while (child != this) { + if (child.getParent() == this) { + toX += child.getLeft(); + toY += child.getTop(); + } else if (child.getParent() != storiesViewPager) { + toX += child.getX(); + toY += child.getY(); + } + child = (View) child.getParent(); + } + outFromRectAvatar.set(toX, toY, toX + headerView.backupImageView.getMeasuredWidth(), toY + headerView.backupImageView.getMeasuredHeight()); + outFromRectContainer.set(0, currentView.getTop() + currentView.storyContainer.getTop(), containerView.getMeasuredWidth(), containerView.getMeasuredHeight()); + containerView.getMatrix().mapRect(outFromRectAvatar); + containerView.getMatrix().mapRect(outFromRectContainer); + //outFromRectContainer.offset(containerView.getX(), containerView.getY()); + // outFromRect.offset(-containerView.getTranslationX(), -containerView.getTranslationY()); + } + } + + volumeControl.setAlpha(1f - progressToDismiss); + + float dismissScaleProgress = 1f; + if (swipeToDismissHorizontalOffset == 0) { + dismissScaleProgress = 1f - Utilities.clamp(Math.abs(swipeToDismissOffset / getMeasuredHeight()), 1f, 0); + } + storiesViewPager.setHorizontalProgressToDismiss((swipeToDismissHorizontalOffset / (float) containerView.getMeasuredWidth()) * progressToOpen); + if ((fromX == 0 && fromY == 0) || progressToOpen == 1f) { + containerView.setAlpha(progressToOpen); + float scale = 0.75f + 0.1f * progressToOpen + 0.15f * dismissScaleProgress; + containerView.setScaleX(scale); + containerView.setScaleY(scale); + containerView.setTranslationY(swipeToDismissOffset); + containerView.setTranslationX(swipeToDismissHorizontalOffset); + super.dispatchDraw(canvas); + } else { + float progress2 = progressToOpen; + float progressToCircle = progressToOpen; + if (isClosed && animateAvatar) { + // progress2 = 1f - Utilities.clamp((1f - progressToOpen) / 0.8f, 1f, 0); + progress2 = progressToOpen; + progressToCircle = progressToOpen;//(float) Math.pow(progressToOpen, 1.4f);//progressToOpen;//1f - Utilities.clamp((1f - progressToOpen) / 0.85f, 1f, 0); + float startAlpha = 0.8f; + float endAlpha = startAlpha + 0.1f; + float alpha = 1f - Utilities.clamp(((1f - progressToOpen) - startAlpha) / (endAlpha - startAlpha), 1f, 0f); + progressToCircle = Utilities.clamp(progressToCircle - 0.05f * (1f - alpha), 1f, 0); + containerView.setAlpha(alpha); + } else { + containerView.setAlpha(1f); + } + if (isClosed && transitionViewHolder != null && transitionViewHolder.storyImage != null) { + containerView.setAlpha(containerView.getAlpha() * (float) Math.pow(progress2, .2f)); + } else if (isClosed) { + // containerView.setAlpha(Utilities.clamp(progressToOpen / 0.7f, 1f, 0)); + } + containerView.setTranslationX((fromX - containerView.getLeft() - containerView.getMeasuredWidth() / 2f) * (1f - progressToOpen) + swipeToDismissHorizontalOffset * progressToOpen); + containerView.setTranslationY((fromY - containerView.getTop() - containerView.getMeasuredHeight() / 2f) * (1f - progressToOpen) + swipeToDismissOffset * progressToOpen); + float s1 = 0.85f + 0.15f * dismissScaleProgress; + float scale = AndroidUtilities.lerp(fromWidth / (float) containerView.getMeasuredWidth(), s1, progressToCircle); + containerView.setScaleX(scale); + containerView.setScaleY(scale); + + path.rewind(); + rect1.set( + fromX - fromWidth / 2f, fromY - fromHeight / 2f, + fromX + fromWidth / 2f, fromY + fromHeight / 2f + ); + if (isClosed && animateAvatar) { + rect2.set(outFromRectContainer); + } else if (currentView != null) { + rect2.set(0, currentView.storyContainer.getTop() + fromDismissOffset, getMeasuredWidth(), getMeasuredHeight() + fromDismissOffset); + } else { + rect2.set(0, 0, getMeasuredWidth(), getMeasuredHeight()); + } + + if (isClosed && animateAvatar) { + rect1.inset(AndroidUtilities.dp(12), AndroidUtilities.dp(12)); + } + float cx = AndroidUtilities.lerp(rect1.centerX(), rect2.centerX(), progressToOpen); + float cy = AndroidUtilities.lerp(rect1.centerY(), rect2.centerY(), progressToOpen); + float rectHeight = AndroidUtilities.lerp(rect1.height(), rect2.height(), progressToCircle); + float rectWidth = AndroidUtilities.lerp(rect1.width(), rect2.width(), progressToCircle); + if (isClosed && animateAvatar) { + rect1.inset(-AndroidUtilities.dp(12), -AndroidUtilities.dp(12)); + } + + AndroidUtilities.rectTmp.set(cx - rectWidth / 2f, cy - rectHeight / 2f, cx + rectWidth / 2f, cy + rectHeight / 2f); + float rad; + if (animateAvatar) { + rad = AndroidUtilities.lerp(fromWidth / 2f, 0, progressToCircle); + } else { + rad = AndroidUtilities.lerp((float) fromRadius, 0, progress2); + } + path.addRoundRect( + AndroidUtilities.rectTmp, + rad, rad, + Path.Direction.CCW + ); + canvas.save(); + if (clipTop != 0 && clipBottom != 0) { + canvas.clipRect( + 0, + AndroidUtilities.lerp(0, clipTop, (float) Math.pow(1f - progressToOpen, .4f)), + getMeasuredWidth(), + AndroidUtilities.lerp(getMeasuredHeight(), clipBottom, (1f - progressToOpen)) + ); + } + + canvas.save(); + canvas.clipPath(path); + // canvas.drawColor(Color.RED); + super.dispatchDraw(canvas); + if (transitionViewHolder != null && transitionViewHolder.storyImage != null) { + PeerStoriesView page = storiesViewPager.getCurrentPeerView(); + if (page != null && page.storyContainer != null) { + boolean wasVisible = transitionViewHolder.storyImage.getVisible(); + rect2.set( + swipeToDismissHorizontalOffset + containerView.getLeft() + page.getX() + page.storyContainer.getX(), + swipeToDismissOffset + containerView.getTop() + page.getY() + page.storyContainer.getY(), + swipeToDismissHorizontalOffset + containerView.getRight() - (containerView.getWidth() - page.getRight()) - (page.getWidth() - page.storyContainer.getRight()), + swipeToDismissOffset + containerView.getBottom() - (containerView.getHeight() - page.getBottom()) - (page.getHeight() - page.storyContainer.getBottom()) + ); + AndroidUtilities.lerp(rect1, rect2, progress2, rect3); + float x = transitionViewHolder.storyImage.getImageX(); + float y = transitionViewHolder.storyImage.getImageY(); + float w = transitionViewHolder.storyImage.getImageWidth(); + float h = transitionViewHolder.storyImage.getImageHeight(); + transitionViewHolder.storyImage.setImageCoords(rect3); + transitionViewHolder.storyImage.setAlpha(1f - progress2); + transitionViewHolder.storyImage.setVisible(true, false); + int r = canvas.getSaveCount(); + if (transitionViewHolder.drawClip != null) { + transitionViewHolder.drawClip.clip(canvas, rect3, 1f - progress2); + } + transitionViewHolder.storyImage.draw(canvas); + if (transitionViewHolder.drawAbove != null) { + transitionViewHolder.drawAbove.draw(canvas, rect3, 1f - progress2); + } + transitionViewHolder.storyImage.setVisible(wasVisible, false); + transitionViewHolder.storyImage.setImageCoords(x, y, w, h); + canvas.restoreToCount(r); + } + } + canvas.restore(); + + if (headerView != null) { + float toX = swipeToDismissHorizontalOffset, toY = swipeToDismissOffset; + View child = headerView.backupImageView; + if (isClosed && animateAvatar) { + rect2.set(outFromRectAvatar); + } else { + while (child != this) { + if (child.getParent() == this) { + toX += child.getLeft(); + toY += child.getTop(); + } else if (child.getParent() != storiesViewPager) { + toX += child.getX(); + toY += child.getY(); + } + child = (View) child.getParent(); + } + rect2.set(toX, toY, toX + headerView.backupImageView.getMeasuredWidth(), toY + headerView.backupImageView.getMeasuredHeight()); + } + + AndroidUtilities.lerp(rect1, rect2, progressToOpen, AndroidUtilities.rectTmp); + + if (animateAvatar) { + boolean crossfade = transitionViewHolder != null && transitionViewHolder.crossfadeToAvatarImage != null; + if (!crossfade || progressToOpen != 0) { + headerView.backupImageView.getImageReceiver().setImageCoords(AndroidUtilities.rectTmp); + headerView.backupImageView.getImageReceiver().setRoundRadius((int) (AndroidUtilities.rectTmp.width() / 2f)); + headerView.backupImageView.getImageReceiver().setVisible(true, false); + headerView.backupImageView.getImageReceiver().setAlpha(crossfade ? progressToOpen : 1f); + headerView.drawUploadingProgress(canvas, AndroidUtilities.rectTmp, !runOpenAnimationAfterLayout, progressToOpen); + headerView.backupImageView.getImageReceiver().draw(canvas); + headerView.backupImageView.getImageReceiver().setVisible(false, false); + } + if (progressToOpen != 1f && crossfade) { + avatarRectTmp.set( + transitionViewHolder.crossfadeToAvatarImage.getImageX(), + transitionViewHolder.crossfadeToAvatarImage.getImageY(), + transitionViewHolder.crossfadeToAvatarImage.getImageX2(), + transitionViewHolder.crossfadeToAvatarImage.getImageY2() + ); + int oldRadius = transitionViewHolder.crossfadeToAvatarImage.getRoundRadius()[0]; + boolean isVisible = transitionViewHolder.crossfadeToAvatarImage.getVisible(); + transitionViewHolder.crossfadeToAvatarImage.setImageCoords(AndroidUtilities.rectTmp); + transitionViewHolder.crossfadeToAvatarImage.setRoundRadius((int) (AndroidUtilities.rectTmp.width() / 2f)); + transitionViewHolder.crossfadeToAvatarImage.setVisible(true, false); + canvas.saveLayerAlpha(AndroidUtilities.rectTmp, (int) (255 * (1f - progressToOpen)), Canvas.ALL_SAVE_FLAG); + transitionViewHolder.crossfadeToAvatarImage.draw(canvas); + canvas.restore(); + transitionViewHolder.crossfadeToAvatarImage.setVisible(isVisible, false); + transitionViewHolder.crossfadeToAvatarImage.setImageCoords(avatarRectTmp); + transitionViewHolder.crossfadeToAvatarImage.setRoundRadius(oldRadius); + // transitionViewHolder.crossfadeToAvatarImage.setVisible(false, false); + } + } + } + + + if (animateFromCell != null) { + float progressHalf = Utilities.clamp(progressToOpen / 0.4f, 1f, 0); + if (progressHalf != 1) { + AndroidUtilities.rectTmp.set(fromX, fromY, fromX + fromWidth, fromY + fromHeight); + AndroidUtilities.rectTmp.inset(-AndroidUtilities.dp(16), -AndroidUtilities.dp(16)); + if (progressHalf != 0) { + canvas.saveLayerAlpha(AndroidUtilities.rectTmp, (int) (255 * (1f - progressHalf)), Canvas.ALL_SAVE_FLAG); + } else { + canvas.save(); + } + canvas.translate(fromXCell, fromYCell); + animateFromCell.drawAvatarOverlays(canvas); + canvas.restore(); + } + } + canvas.restore(); +// +// if (transitionViewHolder.avatarImage != null) { +// transitionViewHolder.avatarImage.setVisible(false, true); +// } +// if (transitionViewHolder.storyImage != null) { +// transitionViewHolder.storyImage.setVisible(false, true); +// } + } + + if (runOpenAnimationAfterLayout) { + startOpenAnimation(); + runOpenAnimationAfterLayout = false; + } + } + + @Override + public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { + super.requestDisallowInterceptTouchEvent(disallowIntercept); + allowIntercept = false; + } + + SparseArray lastX = new SparseArray<>(); + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + boolean swipeToReplyCancelled = false; + PeerStoriesView peerStoriesView = storiesViewPager.getCurrentPeerView(); + if (peerStoriesView != null && peerStoriesView.checkTextSelectionEvent(ev)) { + return true; + } + if (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL) { + inSwipeToDissmissMode = false; + AndroidUtilities.cancelRunOnUIThread(longPressRunnable); + if (swipeToDismissHorizontalOffset != 0) { + swipeToDissmissBackAnimator = ValueAnimator.ofFloat(swipeToDismissHorizontalOffset, 0); + swipeToDissmissBackAnimator.addUpdateListener(animation -> { + swipeToDismissHorizontalOffset = (float) animation.getAnimatedValue(); + updateProgressToDismiss(); + }); + swipeToDissmissBackAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + swipeToDismissHorizontalOffset = 0; + updateProgressToDismiss(); + } + }); + swipeToDissmissBackAnimator.setDuration(250); + swipeToDissmissBackAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); + swipeToDissmissBackAnimator.start(); + } + swipeToReplyCancelled = true; + if (progressToDismiss >= 0.3f) { + close(true); + } + setInTouchMode(false); + setLongPressed(false); + } + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + swipeToReplyWaitingKeyboard = false; + if (peerStoriesView != null) { + peerStoriesView.onActionDown(ev); + } + storiesViewPager.onTouchEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0, 0, 0)); + } + boolean override = false; + boolean enableTouch = !keyboardVisible && !isClosed && !isRecording; + if (selfStoriesViewsOffset == 0 && !inSwipeToDissmissMode && storiesViewPager.currentState == ViewPager.SCROLL_STATE_DRAGGING && ev.getAction() == MotionEvent.ACTION_MOVE && enableTouch) { + float dx = lastX.get(ev.getPointerId(0), 0f) - ev.getX(0); + if (dx != 0 && !storiesViewPager.canScroll(dx) || swipeToDismissHorizontalOffset != 0) { + if (swipeToDismissHorizontalOffset == 0) { + swipeToDismissHorizontalDirection = -dx; + } + if (dx < 0 && swipeToDismissHorizontalDirection > 0 || dx > 0 && swipeToDismissHorizontalDirection < 0) { + dx *= 0.2f; + } + swipeToDismissHorizontalOffset -= dx; + updateProgressToDismiss(); + if (swipeToDismissHorizontalOffset > 0 && swipeToDismissHorizontalDirection < 0 || swipeToDismissHorizontalOffset < 0 && swipeToDismissHorizontalDirection > 0) { + swipeToDismissHorizontalOffset = 0; + } + override = true; + } + } + if (peerStoriesView != null && selfStoriesViewsOffset == 0 && !inSwipeToDissmissMode && !isCaption && storiesViewPager.currentState != ViewPager.SCROLL_STATE_DRAGGING) { + AndroidUtilities.getViewPositionInParent(peerStoriesView.storyContainer, this, pointPosition); + ev.offsetLocation(-pointPosition[0], -pointPosition[1]); + storiesViewPager.getCurrentPeerView().checkPinchToZoom(ev); + ev.offsetLocation(pointPosition[0], pointPosition[1]); + } + if (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL) { + lastX.clear(); + } else { + for (int i = 0; i < ev.getPointerCount(); i++) { + lastX.put(ev.getPointerId(i), ev.getX(i)); + } + } + if (override) { + return true; + } + boolean rezult = super.dispatchTouchEvent(ev); + if (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL) { + if (selfStoriesViewsOffset != 0 && !flingCalled) { + cancelSwipeToViews(selfStoryViewsView.progressToOpen > 0.5f); + } + PeerStoriesView peerView = getCurrentPeerView(); + if (peerView != null) { + peerView.cancelTouch(); + } + } + if (swipeToReplyCancelled && !swipeToReplyWaitingKeyboard) { + cancelSwipeToReply(); + } + return rezult || animationInProgress && isInTouchMode; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN && progressToOpen == 1f) { + startX = ev.getX(); + startY = ev.getY(); + allowIntercept = !findClickableView(windowView, ev.getX(), ev.getY(), false); + allowSwipeToDissmiss = !findClickableView(windowView, ev.getX(), ev.getY(), true); + setInTouchMode(allowIntercept && !isCaptionPartVisible); + if (allowIntercept && isCaptionPartVisible) { + delayedTapRunnable = () -> setInTouchMode(true); + AndroidUtilities.runOnUIThread(delayedTapRunnable, 150); + } + if (allowIntercept && !keyboardVisible && !isInTextSelectionMode) { + AndroidUtilities.runOnUIThread(longPressRunnable, 400); + } + } else if (ev.getAction() == MotionEvent.ACTION_MOVE) { + if (!inSwipeToDissmissMode && !keyboardVisible && allowSwipeToDissmiss) { + float dy = Math.abs(startY - ev.getY()); + float dx = Math.abs(startX - ev.getX()); + if (dy > dx && dy > AndroidUtilities.touchSlop * 2) { + inSwipeToDissmissMode = true; + PeerStoriesView peerView = storiesViewPager.getCurrentPeerView(); + if (peerView != null) { + peerView.cancelTextSelection(); + } + allowSwipeToReply = !peerView.isSelf; + allowSelfStoriesView = peerView.isSelf && !peerView.unsupported && peerView.currentStory.storyItem != null; + if (allowSelfStoriesView) { + if (StoriesUtilities.hasExpiredViews(peerView.currentStory.storyItem)) { + allowSelfStoriesView = false; + } + } + if (allowSelfStoriesView) { + checkSelfStoriesView(); + } + swipeToReplyOffset = 0; + if (delayedTapRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(delayedTapRunnable); + delayedTapRunnable.run(); + delayedTapRunnable = null; + } + AndroidUtilities.cancelRunOnUIThread(longPressRunnable); + } + layoutAndFindView(); + } + } else if (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL) { + AndroidUtilities.cancelRunOnUIThread(longPressRunnable); + if (delayedTapRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(delayedTapRunnable); + delayedTapRunnable = null; + } + setInTouchMode(false); + } + boolean selfViewsViewVisible = selfStoryViewsView != null && selfStoryViewsView.progressToOpen == 1f; + if (!inSwipeToDissmissMode && !selfViewsViewVisible) { + gestureDetector.onTouchEvent(ev); + } + return inSwipeToDissmissMode || super.onInterceptTouchEvent(ev); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL) { + inSwipeToDissmissMode = false; + setInTouchMode(false); + if (progressToDismiss >= 1f) { + close(true); + } else if (!isClosed) { + swipeToDissmissBackAnimator = ValueAnimator.ofFloat(swipeToDismissOffset, 0); + swipeToDissmissBackAnimator.addUpdateListener(animation -> { + swipeToDismissOffset = (float) animation.getAnimatedValue(); + updateProgressToDismiss(); + }); + swipeToDissmissBackAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + swipeToDismissOffset = 0; + swipeToReplyOffset = 0; + updateProgressToDismiss(); + } + }); + swipeToDissmissBackAnimator.setDuration(150); + swipeToDissmissBackAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); + swipeToDissmissBackAnimator.start(); + } + } + if (inSwipeToDissmissMode || keyboardVisible || swipeToReplyOffset != 0 || (selfStoriesViewsOffset != 0 && allowIntercept)) { + gestureDetector.onTouchEvent(event); + return true; + } else { + return false; + } + } + + @Override + public boolean dispatchKeyEventPreIme(KeyEvent event) { + if (event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP || event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_DOWN) { + dispatchVolumeEvent(event); + return true; + } + + if (event != null && event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) { + onBackPressed(); + return true; + } + return super.dispatchKeyEventPreIme(event); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (ATTACH_TO_FRAGMENT) { + AndroidUtilities.requestAdjustResize(fragment.getParentActivity(), fragment.getClassGuid()); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + ((LayoutParams) volumeControl.getLayoutParams()).topMargin = AndroidUtilities.statusBarHeight - AndroidUtilities.dp(2); + volumeControl.getLayoutParams().height = AndroidUtilities.dp(2); + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + }; + } + + if (containerView == null) { + containerView = new HwFrameLayout(context) { + + public int measureKeyboardHeight() { + View rootView = getRootView(); + getWindowVisibleDisplayFrame(AndroidUtilities.rectTmp2); + if (AndroidUtilities.rectTmp2.bottom == 0 && AndroidUtilities.rectTmp2.top == 0) { + return 0; + } + int usableViewHeight = rootView.getHeight() - (AndroidUtilities.rectTmp2.top != 0 ? AndroidUtilities.statusBarHeight : 0) - AndroidUtilities.getViewInset(rootView); + return Math.max(0, usableViewHeight - (AndroidUtilities.rectTmp2.bottom - AndroidUtilities.rectTmp2.top)); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int heightWithKeyboard = MeasureSpec.getSize(heightMeasureSpec); + if (!ATTACH_TO_FRAGMENT) { + setKeyboardHeightFromParent(measureKeyboardHeight()); + heightWithKeyboard += realKeyboardHeight; + } + int width = MeasureSpec.getSize(widthMeasureSpec); + int viewPagerHeight; + if (heightWithKeyboard > (int) (16f * width / 9f)) { + viewPagerHeight = (int) (16f * width / 9f); + storiesViewPager.getLayoutParams().width = LayoutParams.MATCH_PARENT; + } else { + viewPagerHeight = heightWithKeyboard; + width = storiesViewPager.getLayoutParams().width = (int) (heightWithKeyboard / 16f * 9f); + } + aspectRatioFrameLayout.getLayoutParams().height = viewPagerHeight + 1; + aspectRatioFrameLayout.getLayoutParams().width = width; + FrameLayout.LayoutParams layoutParams = (LayoutParams) aspectRatioFrameLayout.getLayoutParams(); + layoutParams.topMargin = AndroidUtilities.statusBarHeight; + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + @Override + protected void dispatchDraw(Canvas canvas) { + PeerStoriesView peerStoriesView = storiesViewPager.getCurrentPeerView(); + float pivotY = 0; + if (selfStoryViewsView != null && peerStoriesView != null) { + selfStoryViewsView.setOffset(selfStoriesViewsOffset); + if (selfStoryViewsView.progressToOpen == 1f) { + storiesViewPager.setVisibility(View.GONE); + } else { + storiesViewPager.setVisibility(View.VISIBLE); + } + + pivotY = peerStoriesView.getTop() + peerStoriesView.storyContainer.getTop(); + + float progressHalf = selfStoryViewsView.progressToOpen;//Utilities.clamp((selfStoryViewsView.progressToOpen - 0.8f) / 0.2f, 1f, 0); + float fromScale = (getMeasuredHeight() - selfStoriesViewsOffset) / getMeasuredHeight(); + float toScale = selfStoryViewsView.toHeight / peerStoriesView.storyContainer.getMeasuredHeight(); + float s = AndroidUtilities.lerp(1f, toScale, progressHalf); + storiesViewPager.setPivotY(pivotY); + storiesViewPager.setPivotX(getMeasuredWidth() / 2f); + storiesViewPager.setScaleX(s); + storiesViewPager.setScaleY(s); + peerStoriesView.forceUpdateOffsets = true; + + if (selfStoriesViewsOffset == 0) { + peerStoriesView.setViewsThumbImageReceiver(0, null); + } else { + peerStoriesView.setViewsThumbImageReceiver(progressHalf, selfStoryViewsView.getCrossfadeToImage()); + } + peerStoriesView.invalidate(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + peerStoriesView.outlineProvider.radiusInDp = (int) AndroidUtilities.lerp(10f, 6f / toScale, selfStoryViewsView.progressToOpen); + peerStoriesView.storyContainer.invalidateOutline(); + } + storiesViewPager.setTranslationY((selfStoryViewsView.toY - pivotY) * progressHalf); + + } + if (peerStoriesView != null) { + volumeControl.setTranslationY(peerStoriesView.storyContainer.getY() - AndroidUtilities.dp(4)); + } + super.dispatchDraw(canvas); + // canvas.drawRect(0, pivotY, getMeasuredWidth(), pivotY + 1, Theme.DEBUG_RED); + } + }; + storiesViewPager = new HwStoriesViewPager(context, this, resourcesProvider) { + @Override + public void onStateChanged() { + if (storiesViewPager.currentState == ViewPager.SCROLL_STATE_DRAGGING) { + AndroidUtilities.cancelRunOnUIThread(longPressRunnable); + } + } + }; + storiesViewPager.setDelegate(new PeerStoriesView.Delegate() { + + @Override + public void onPeerSelected(long dialogId, int position) { + if (lastPosition != position || lastDialogId != dialogId) { + lastDialogId = dialogId; + lastPosition = position; + } + } + + @Override + public void onCloseClick() { + close(true); + } + + @Override + public void onCloseLongClick() { + + } + + + @Override + public void onEnterViewClick() { + + } + + @Override + public void shouldSwitchToNext() { + PeerStoriesView peerView = storiesViewPager.getCurrentPeerView(); + if (!peerView.switchToNext(true)) { + if (!storiesViewPager.switchToNext(true)) { + close(true); + } + } + } + + @Override + public void switchToNextAndRemoveCurrentPeer() { + if (storiesList != null) { + if (storiesViewPager.days == null) { + return; + } + ArrayList> newDays = new ArrayList<>(storiesViewPager.days); + int index = newDays.indexOf(storiesViewPager.getCurrentPeerView().getCurrentDay()); + if (index >= 0) { + newDays.remove(index); + } else { + close(false); + return; + } + if (!storiesViewPager.switchToNext(true)) { + close(false); + } else { + storiesViewPager.onNextIdle(() -> { + storiesViewPager.setDays(storiesList.userId, newDays, currentAccount); + }); + } + } else { + ArrayList newPeers = new ArrayList<>(peerIds); + int index = newPeers.indexOf(storiesViewPager.getCurrentPeerView().getCurrentPeer()); + if (index >= 0) { + newPeers.remove(index); + } else { + close(false); + return; + } + if (!storiesViewPager.switchToNext(true)) { + close(false); + } else { + storiesViewPager.onNextIdle(() -> { + storiesViewPager.setPeerIds(newPeers, currentAccount, index); + }); + } + } + } + + @Override + public void setHideEnterViewProgress(float v) { + if (hideEnterViewProgress != v) { + hideEnterViewProgress = v; + containerView.invalidate(); + } + } + + @Override + public void showDialog(Dialog dialog) { + StoryViewer.this.showDialog(dialog); + } + + @Override + public void updatePeers() { + //storiesViewPager.setPeerIds(); + } + + @Override + public boolean releasePlayer(Runnable whenReleased) { + if (playerHolder != null) { + final boolean r = playerHolder.release(whenReleased); + playerHolder = null; + return r; + } else { + return false; + } + } + + @Override + public void requestAdjust(boolean b) { + StoryViewer.this.requestAdjust(b); + } + + @Override + public void setKeyboardVisible(boolean keyboardVisible) { + if (StoryViewer.this.keyboardVisible != keyboardVisible) { + StoryViewer.this.keyboardVisible = keyboardVisible; + updatePlayingMode(); + } + } + + @Override + public void setAllowTouchesByViewPager(boolean b) { + StoryViewer.this.allowTouchesByViewpager = allowTouchesByViewpager; + updatePlayingMode(); + } + + @Override + public void requestPlayer(TLRPC.Document document, Uri uri, long t, PeerStoriesView.VideoPlayerSharedScope scope) { + if (isClosed || progressToOpen != 1f) { + scope.firstFrameRendered = false; + scope.player = null; + return; + } + String lastAutority = lastUri == null ? null : lastUri.getAuthority(); + String autority = uri == null ? null : uri.getAuthority(); + boolean sameUri = Objects.equals(lastAutority, autority); + if (!sameUri || playerHolder == null) { + lastUri = uri; + if (playerHolder != null) { + playerHolder.release(null); + playerHolder = null; + } + if (currentPlayerScope != null) { + currentPlayerScope.player = null; + currentPlayerScope.firstFrameRendered = false; + currentPlayerScope.renderView = null; + currentPlayerScope.textureView = null; + currentPlayerScope.surfaceView = null; + currentPlayerScope.invalidate(); + currentPlayerScope = null; + } + if (uri != null) { + currentPlayerScope = scope; + for (int i = 0; i < preparedPlayers.size(); i++) { + if (preparedPlayers.get(i).uri.equals(uri)) { + playerHolder = preparedPlayers.remove(i); + break; + } + } + if (playerHolder == null) { + playerHolder = new VideoPlayerHolder(); + playerHolder.document = document; + } +// if (surfaceView != null) { +// AndroidUtilities.removeFromParent(surfaceView); +// aspectRatioFrameLayout.addView(surfaceView = new SurfaceView(context)); +// } + playerHolder.uri = uri; + currentPlayerScope.player = playerHolder; + currentPlayerScope.firstFrameRendered = false; + currentPlayerScope.renderView = aspectRatioFrameLayout; + currentPlayerScope.textureView = textureView; + currentPlayerScope.surfaceView = surfaceView; + FileStreamLoadOperation.setPriorityForDocument(playerHolder.document, FileLoader.PRIORITY_HIGH); + FileLoader.getInstance(currentAccount).changePriority(FileLoader.PRIORITY_HIGH, playerHolder.document, null, null, null, null, null); + currentPlayerScope.player.start(isPaused(), uri, t, isInSilentMode); + currentPlayerScope.invalidate(); + } + } else if (sameUri) { + currentPlayerScope = scope; + currentPlayerScope.player = playerHolder; + currentPlayerScope.firstFrameRendered = playerHolder.firstFrameRendered; + currentPlayerScope.renderView = aspectRatioFrameLayout; + currentPlayerScope.textureView = textureView; + currentPlayerScope.surfaceView = surfaceView; + } + if (USE_SURFACE_VIEW) { + if (uri == null) { + surfaceView.setVisibility(View.INVISIBLE); + } else { + surfaceView.setVisibility(View.VISIBLE); + } + } + } + + @Override + public boolean isClosed() { + return isClosed; + } + + @Override + public float getProgressToDismiss() { + return progressToDismiss; + } + + @Override + public void setIsRecording(boolean isRecording) { + StoryViewer.this.isRecording = isRecording; + updatePlayingMode(); + } + + @Override + public void setIsWaiting(boolean b) { + StoryViewer.this.isWaiting = b; + updatePlayingMode(); + } + + @Override + public void setIsCaption(boolean b) { + StoryViewer.this.isCaption = b; + updatePlayingMode(); + } + + @Override + public void setIsCaptionPartVisible(boolean b) { + StoryViewer.this.isCaptionPartVisible = b; + } + + @Override + public void setPopupIsVisible(boolean b) { + StoryViewer.this.isPopupVisible = b; + updatePlayingMode(); + } + + @Override + public void setBulletinIsVisible(boolean b) { + StoryViewer.this.isBulletinVisible = b; + updatePlayingMode(); + } + + @Override + public void setIsInPinchToZoom(boolean b) { + StoryViewer.this.isInPinchToZoom = b; + updatePlayingMode(); + } + + @Override + public void setIsHintVisible(boolean visible) { + StoryViewer.this.isHintVisible = visible; + updatePlayingMode(); + } + + @Override + public void setIsSwiping(boolean swiping) { + StoryViewer.this.isSwiping = swiping; + updatePlayingMode(); + } + + @Override + public void setIsInSelectionMode(boolean selectionMode) { + StoryViewer.this.isInTextSelectionMode = selectionMode; + updatePlayingMode(); + } + + @Override + public int getKeyboardHeight() { + return realKeyboardHeight; + } + + @Override + public void preparePlayer(ArrayList documents, ArrayList uries) { + if (!SharedConfig.deviceIsHigh() || !SharedConfig.allowPreparingHevcPlayers()) { + return; + } + if (isClosed) { + return; + } + for (int i = 0; i < preparedPlayers.size(); i++) { + boolean found = false; + for (int j = 0; j < uries.size(); j++) { + if (uries.get(j).equals(preparedPlayers.get(i).uri)) { + found = true; + uries.remove(j); + } + } + } + for (int i = 0; i < uries.size(); i++) { + Uri uri = uries.get(i); + VideoPlayerHolder playerHolder = new VideoPlayerHolder(); + playerHolder.uri = uri; + playerHolder.document = documents.get(i); + FileStreamLoadOperation.setPriorityForDocument(playerHolder.document, FileLoader.PRIORITY_LOW); + playerHolder.preparePlayer(uri, isInSilentMode); + preparedPlayers.add(playerHolder); + if (preparedPlayers.size() > 2) { + VideoPlayerHolder player = preparedPlayers.remove(0); + player.release(null); + } + } + } + }); + containerView.addView(storiesViewPager, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.CENTER_HORIZONTAL)); + aspectRatioFrameLayout = new AspectRatioFrameLayout(context); + if (USE_SURFACE_VIEW) { + surfaceView = new SurfaceView(context); + surfaceView.setZOrderMediaOverlay(false); + surfaceView.setZOrderOnTop(false); + //surfaceView.setZOrderMediaOverlay(true); + aspectRatioFrameLayout.addView(surfaceView); + } else { + textureView = new HwTextureView(context) { + @Override + public void invalidate() { + super.invalidate(); + if (currentPlayerScope != null) { + currentPlayerScope.invalidate(); + } + } + }; + aspectRatioFrameLayout.addView(textureView); + } + + volumeControl = new StoriesVolumeContorl(context); + containerView.addView(volumeControl, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, 0, 4, 0, 4, 0)); + } + AndroidUtilities.removeFromParent(aspectRatioFrameLayout); + windowView.addView(aspectRatioFrameLayout); + + AndroidUtilities.removeFromParent(containerView); + windowView.addView(containerView); + windowView.setClipChildren(false); + + if (ATTACH_TO_FRAGMENT) { + if (fragment.getParentActivity() instanceof LaunchActivity) { + LaunchActivity activity = (LaunchActivity) fragment.getParentActivity(); + activity.requestCustomNavigationBar(); + } + } + if (isSingleStory) { + updateTransitionParams(); + } + if (storiesList != null) { + storiesViewPager.setDays(storiesList.userId, storiesList.getDays(), currentAccount); + } else { + storiesViewPager.setPeerIds(peerIds, currentAccount, position); + } + + windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + if (ATTACH_TO_FRAGMENT) { + windowView.setFitsSystemWindows(true); + fragment.getLayoutContainer().addView(windowView); + AndroidUtilities.requestAdjustResize(fragment.getParentActivity(), fragment.getClassGuid()); + } else { + windowView.setFocusable(false); + containerView.setFocusable(false); + if (Build.VERSION.SDK_INT >= 21) { + windowView.setFitsSystemWindows(true); + + containerView.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() { + + @NonNull + @Override + public WindowInsets onApplyWindowInsets(@NonNull View v, @NonNull WindowInsets insets) { + ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) containerView.getLayoutParams(); + + layoutParams.topMargin = insets.getSystemWindowInsetTop(); + layoutParams.bottomMargin = insets.getSystemWindowInsetBottom(); + layoutParams.leftMargin = insets.getSystemWindowInsetLeft(); + layoutParams.rightMargin = insets.getSystemWindowInsetRight(); + + windowView.requestLayout(); + containerView.requestLayout(); + if (Build.VERSION.SDK_INT >= 30) { + return WindowInsets.CONSUMED; + } else { + return insets.consumeSystemWindowInsets(); + } + } + }); + containerView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); + } + windowManager.addView(windowView, windowLayoutParams); + } + windowView.requestLayout(); + runOpenAnimationAfterLayout = true; + + updateTransitionParams(); + progressToOpen = 0f; + checkNavBarColor(); + animationInProgress = true; + + checkInSilentMode(); + if (ATTACH_TO_FRAGMENT) { + lockOrientation(true); + } + + if (!ATTACH_TO_FRAGMENT) { + globalInstances.add(this); + } + AndroidUtilities.hideKeyboard(fragment.getFragmentView()); + } + + private void showKeyboard() { + PeerStoriesView currentPeerView = storiesViewPager.getCurrentPeerView(); + if (currentPeerView != null) { + currentPeerView.showKeyboard(); + } + AndroidUtilities.runOnUIThread(this::cancelSwipeToReply, 200); + } + + ValueAnimator swipeToViewsAnimator; + + public void cancelSwipeToViews(boolean open) { + if (swipeToViewsAnimator != null) { + return; + } + if (allowSelfStoriesView || selfStoriesViewsOffset != 0) { + locker.lock(); + swipeToViewsAnimator = ValueAnimator.ofFloat(selfStoriesViewsOffset, open ? selfStoryViewsView.maxSelfStoriesViewsOffset : 0); + swipeToViewsAnimator.addUpdateListener(animation -> { + selfStoriesViewsOffset = (float) animation.getAnimatedValue(); + final PeerStoriesView peerStoriesView = storiesViewPager.getCurrentPeerView(); + if (peerStoriesView != null) { + peerStoriesView.invalidate(); + } + containerView.invalidate(); + }); + swipeToViewsAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + locker.unlock(); + selfStoriesViewsOffset = open ? selfStoryViewsView.maxSelfStoriesViewsOffset : 0; + final PeerStoriesView peerStoriesView = storiesViewPager.getCurrentPeerView(); + if (peerStoriesView != null) { + peerStoriesView.invalidate(); + } + containerView.invalidate(); + swipeToViewsAnimator = null; + } + }); + if (open) { + swipeToViewsAnimator.setDuration(350); + swipeToViewsAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + } else { + swipeToViewsAnimator.setDuration(350); + swipeToViewsAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); + } + swipeToViewsAnimator.start(); + } + } + + private void checkSelfStoriesView() { + if (selfStoryViewsView == null) { + selfStoryViewsView = new SelfStoryViewsView(containerView.getContext(), this); + containerView.addView(selfStoryViewsView, 0); + } + PeerStoriesView peerStoriesView = storiesViewPager.getCurrentPeerView(); + if (peerStoriesView != null) { + selfStoryViewsView.setItems(peerStoriesView.getStoryItems(), peerStoriesView.getSelectedPosition()); + } + } + + public void showDialog(Dialog dialog) { + try { + currentDialog = dialog; + dialog.setOnDismissListener(dialog1 -> { + if (dialog1 == currentDialog) { + currentDialog = null; + updatePlayingMode(); + } + }); + dialog.show(); + updatePlayingMode(); + } catch (Throwable e) { + FileLog.e(e); + currentDialog = null; + } + } + + public void cancelSwipeToReply() { + if (swipeToReplyBackAnimator == null) { + inSwipeToDissmissMode = false; + allowSwipeToReply = false; + swipeToReplyBackAnimator = ValueAnimator.ofFloat(swipeToReplyOffset, 0); + swipeToReplyBackAnimator.addUpdateListener(animation -> { + swipeToReplyOffset = (float) animation.getAnimatedValue(); + int maxOffset = AndroidUtilities.dp(200); + swipeToReplyProgress = Utilities.clamp(swipeToReplyOffset / maxOffset, 1f, 0); + PeerStoriesView peerView = storiesViewPager == null ? null : storiesViewPager.getCurrentPeerView(); + if (peerView != null) { + peerView.invalidate(); + } + }); + swipeToReplyBackAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + swipeToReplyBackAnimator = null; + swipeToReplyOffset = 0; + swipeToReplyProgress = 0; + PeerStoriesView peerView = storiesViewPager == null ? null : storiesViewPager.getCurrentPeerView(); + if (peerView != null) { + peerView.invalidate(); + } + } + }); + swipeToReplyBackAnimator.setDuration(AdjustPanLayoutHelper.keyboardDuration); + swipeToReplyBackAnimator.setInterpolator(AdjustPanLayoutHelper.keyboardInterpolator); + swipeToReplyBackAnimator.start(); + } + } + + public boolean getStoryRect(RectF rectF) { + if (storiesViewPager == null) { + return false; + } + PeerStoriesView page = storiesViewPager.getCurrentPeerView(); + if (page == null || page.storyContainer == null) { + return false; + } + float x = windowView == null ? 0 : windowView.getX(); + float y = windowView == null ? 0 : windowView.getY(); + rectF.set( + x + swipeToDismissHorizontalOffset + containerView.getLeft() + page.getX() + page.storyContainer.getX(), + y + swipeToDismissOffset + containerView.getTop() + page.getY() + page.storyContainer.getY(), + x + swipeToDismissHorizontalOffset + containerView.getRight() - (containerView.getWidth() - page.getRight()) - (page.getWidth() - page.storyContainer.getRight()), + y + swipeToDismissOffset + containerView.getBottom() - (containerView.getHeight() - page.getBottom()) - (page.getHeight() - page.storyContainer.getBottom()) + ); + return true; + } + + @Nullable + public PeerStoriesView getCurrentPeerView() { + if (storiesViewPager == null) { + return null; + } + return storiesViewPager.getCurrentPeerView(); + } + + private void lockOrientation(boolean lock) { + Activity activity = AndroidUtilities.findActivity(fragment.getContext()); + if (activity != null) { + activity.setRequestedOrientation(lock ? ActivityInfo.SCREEN_ORIENTATION_PORTRAIT : ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); + if (lock) { + activity.getWindow().addFlags(FLAG_KEEP_SCREEN_ON); + } else { + activity.getWindow().clearFlags(FLAG_KEEP_SCREEN_ON); + } + } + } + + private void dispatchVolumeEvent(KeyEvent event) { + if (isInSilentMode) { + toggleSilentMode(); + return; + } + PeerStoriesView peerStoriesView = storiesViewPager.getCurrentPeerView(); + if (peerStoriesView != null && !peerStoriesView.currentStory.hasSound() && peerStoriesView.currentStory.isVideo()) { + peerStoriesView.showNoSoundHint(); + return; + } + volumeControl.onKeyDown(event.getKeyCode(), event); + } + + public void toggleSilentMode() { + isInSilentMode = !isInSilentMode; + if (playerHolder != null) { + playerHolder.setAudioEnabled(!isInSilentMode, false); + } + for (int i = 0; i < preparedPlayers.size(); i++) { + preparedPlayers.get(i).setAudioEnabled(!isInSilentMode, true); + } + final PeerStoriesView peerStoriesView = storiesViewPager.getCurrentPeerView(); + if (peerStoriesView != null) { + peerStoriesView.sharedResources.setIconMuted(!soundEnabled(), true); + } + } + + private void checkInSilentMode() { + if (checkSilentMode) { + checkSilentMode = false; + AudioManager am = (AudioManager) windowView.getContext().getSystemService(Context.AUDIO_SERVICE); + isInSilentMode = am.getRingerMode() != AudioManager.RINGER_MODE_NORMAL; + } + } + + private void layoutAndFindView() { + foundViewToClose = true; + if (transitionViewHolder.avatarImage != null) { + transitionViewHolder.avatarImage.setVisible(true, true); + } + if (transitionViewHolder.storyImage != null) { + transitionViewHolder.storyImage.setAlpha(1f); + transitionViewHolder.storyImage.setVisible(true, true); + } + if (storiesList != null) { + final PeerStoriesView peerView = storiesViewPager.getCurrentPeerView(); + if (peerView != null) { + int position = peerView.getSelectedPosition(); + if (position >= 0 && position < storiesList.messageObjects.size()) { + messageId = storiesList.messageObjects.get(position).getId(); + } + } + } + if (placeProvider != null) { + placeProvider.preLayout(storiesViewPager.getCurrentDialogId(), messageId, () -> { + updateTransitionParams(); + if (transitionViewHolder.avatarImage != null) { + transitionViewHolder.avatarImage.setVisible(false, true); + } + if (transitionViewHolder.storyImage != null) { + transitionViewHolder.storyImage.setVisible(false, true); + } + }); + } + } + + private void updateTransitionParams() { + if (placeProvider != null) { + if (transitionViewHolder.avatarImage != null) { + transitionViewHolder.avatarImage.setVisible(true, true); + } + if (transitionViewHolder.storyImage != null) { + transitionViewHolder.storyImage.setAlpha(1f); + transitionViewHolder.storyImage.setVisible(true, true); + } + final PeerStoriesView peerView = storiesViewPager.getCurrentPeerView(); + int position = peerView == null ? 0 : peerView.getSelectedPosition(); + int storyId = peerView == null || position < 0 || position >= peerView.storyItems.size() ? 0 : peerView.storyItems.get(position).id; + TLRPC.StoryItem storyItem = peerView == null || position < 0 || position >= peerView.storyItems.size() ? null : peerView.storyItems.get(position); + if (storyItem == null && isSingleStory) { + storyItem = singleStory; + } + transitionViewHolder.clear(); + if (placeProvider.findView(storiesViewPager.getCurrentDialogId(), messageId, storiesList != null ? dayStoryId : storyId, storyItem == null ? -1 : storyItem.messageType, transitionViewHolder)) { + if (transitionViewHolder.view != null) { + int[] loc = new int[2]; + transitionViewHolder.view.getLocationOnScreen(loc); + fromXCell = loc[0]; + fromYCell = loc[1]; + if (transitionViewHolder.view instanceof StoriesListPlaceProvider.AvatarOverlaysView) { + animateFromCell = (StoriesListPlaceProvider.AvatarOverlaysView) transitionViewHolder.view; + } else { + animateFromCell = null; + } + animateAvatar = false; + if (transitionViewHolder.avatarImage != null) { + fromX = loc[0] + transitionViewHolder.avatarImage.getCenterX(); + fromY = loc[1] + transitionViewHolder.avatarImage.getCenterY(); + fromWidth = transitionViewHolder.avatarImage.getImageWidth(); + fromHeight = transitionViewHolder.avatarImage.getImageHeight(); + if (transitionViewHolder.params != null) { + fromWidth *= transitionViewHolder.params.getScale(); + fromHeight *= transitionViewHolder.params.getScale(); + } + if (transitionViewHolder.view.getParent() instanceof View) { + View parent = (View) transitionViewHolder.view.getParent(); + fromX -= fromWidth / 2f; + fromY -= fromHeight / 2f; + fromWidth *= parent.getScaleX(); + fromHeight *= parent.getScaleY(); + fromX += fromWidth / 2f; + fromY += fromHeight / 2f; + } + animateAvatar = true; + } else if (transitionViewHolder.storyImage != null) { + fromX = loc[0] + transitionViewHolder.storyImage.getCenterX(); + fromY = loc[1] + transitionViewHolder.storyImage.getCenterY(); + fromWidth = transitionViewHolder.storyImage.getImageWidth(); + fromHeight = transitionViewHolder.storyImage.getImageHeight(); + fromRadius = transitionViewHolder.storyImage.getRoundRadius()[0]; + } + + transitionViewHolder.clipParent.getLocationOnScreen(loc); + if (transitionViewHolder.clipTop == 0 && transitionViewHolder.clipBottom == 0) { + clipBottom = clipTop = 0; + } else { + clipTop = loc[1] + transitionViewHolder.clipTop; + clipBottom = loc[1] + transitionViewHolder.clipBottom; + } + } else { + animateAvatar = false; + fromX = fromY = 0; + } + } else { + animateAvatar = false; + fromX = fromY = 0; + } + } else { + animateAvatar = false; + fromX = fromY = 0; + } + } + + private void requestAdjust(boolean nothing) { + if (ATTACH_TO_FRAGMENT) { + if (nothing) { + AndroidUtilities.requestAdjustNothing(fragment.getParentActivity(), fragment.getClassGuid()); + } else { + AndroidUtilities.requestAdjustResize(fragment.getParentActivity(), fragment.getClassGuid()); + } + } else { + windowLayoutParams.softInputMode = (nothing ? WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING : WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); + try { + windowManager.updateViewLayout(windowView, windowLayoutParams); + } catch (Exception e) { + FileLog.e(e); + } + } + } + + private void setInTouchMode(boolean b) { + isInTouchMode = b; + if (isInTouchMode) { + volumeControl.hide(); + } + updatePlayingMode(); + } + + public void setOverlayVisible(boolean visible) { + isOverlayVisible = visible; + updatePlayingMode(); + } + + public void setOnCloseListener(Runnable listener) { + onCloseListener = listener; + } + + public boolean isPaused() { + return isPopupVisible || isBulletinVisible || isCaption || isWaiting || isInTouchMode || keyboardVisible || currentDialog != null || allowTouchesByViewpager || isClosed || isRecording || progressToOpen != 1f || selfStoriesViewsOffset != 0 || isHintVisible || (isSwiping && USE_SURFACE_VIEW) || isOverlayVisible || isInTextSelectionMode; + } + + public void updatePlayingMode() { + if (storiesViewPager == null) { + return; + } + boolean pause = isPaused(); + if (ATTACH_TO_FRAGMENT && (fragment.isPaused() || !fragment.isLastFragment())) { + pause = true; + } + + storiesViewPager.setPaused(pause); + if (playerHolder != null) { + if (pause) { + playerHolder.pause(); + } else { + playerHolder.play(); + } + } + storiesViewPager.enableTouch(!keyboardVisible && !isClosed && !isRecording && !isLongpressed && !isInPinchToZoom && selfStoriesViewsOffset == 0); + } + + private boolean findClickableView(FrameLayout windowView, float x, float y, boolean swipeToDissmiss) { + if (windowView == null) { + return false; + } + if (isPopupVisible) { + return true; + } + if (selfStoryViewsView != null && selfStoriesViewsOffset != 0) { + return true; + } + final PeerStoriesView currentPeerView = storiesViewPager.getCurrentPeerView(); + if (currentPeerView != null) { + //fix view pager strange coordinates + //just skip page.getX() + float x1 = x - containerView.getX() - storiesViewPager.getX() - currentPeerView.getX(); + float y1 = y - containerView.getY() - storiesViewPager.getY() - currentPeerView.getY(); + if (currentPeerView.findClickableView(currentPeerView, x1, y1, swipeToDissmiss)) { + return true; + } + if (currentPeerView.keyboardVisible) { + return false; + } + } + if (swipeToDissmiss) { + return false; + } + if (currentPeerView != null && currentPeerView.chatActivityEnterView != null && currentPeerView.chatActivityEnterView.getVisibility() == View.VISIBLE && y > containerView.getY() + storiesViewPager.getY() + currentPeerView.getY() + currentPeerView.chatActivityEnterView.getY()) { + return true; + } + if (currentPeerView != null && currentPeerView.chatActivityEnterView != null && currentPeerView.chatActivityEnterView.isRecordingAudioVideo()) { + return true; + } + return AndroidUtilities.findClickableView(windowView, x, y); + } + + public boolean closeKeyboardOrEmoji() { + final PeerStoriesView peerStoriesView = storiesViewPager.getCurrentPeerView(); + if (peerStoriesView != null) { + return peerStoriesView.closeKeyboardOrEmoji(); + } + return false; + } + + private void updateProgressToDismiss() { + float newProgress; + if (swipeToDismissHorizontalOffset != 0) { + newProgress = MathUtils.clamp(Math.abs(swipeToDismissHorizontalOffset / AndroidUtilities.dp(80)), 0f, 1f); + } else { + newProgress = MathUtils.clamp(Math.abs(swipeToDismissOffset / AndroidUtilities.dp(80)), 0f, 1f); + } + if (progressToDismiss != newProgress) { + progressToDismiss = newProgress; + checkNavBarColor(); + final PeerStoriesView peerStoriesView = storiesViewPager.getCurrentPeerView(); + if (peerStoriesView != null) { + peerStoriesView.progressToDismissUpdated(); + } + } + + if (windowView != null) { + windowView.invalidate(); + } + } + + private void startOpenAnimation() { + updateTransitionParams(); + progressToOpen = 0f; + setNavigationButtonsColor(true); + foundViewToClose = false; + animationInProgress = true; + fromDismissOffset = swipeToDismissOffset; + if (transitionViewHolder.radialProgressUpload != null) { + final PeerStoriesView peerStoriesView = getCurrentPeerView(); + if (peerStoriesView != null && peerStoriesView.headerView.radialProgress != null) { + peerStoriesView.headerView.radialProgress.copyParams(transitionViewHolder.radialProgressUpload); + } + } + openCloseAnimator = ValueAnimator.ofFloat(0, 1f); + openCloseAnimator.addUpdateListener(animation -> { + progressToOpen = (float) animation.getAnimatedValue(); + containerView.checkHwAcceleration(progressToOpen); + checkNavBarColor(); + if (windowView != null) { + windowView.invalidate(); + } + }); + locker.lock(); + containerView.enableHwAcceleration(); + openCloseAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + progressToOpen = 1f; + checkNavBarColor(); + animationInProgress = false; + containerView.disableHwAcceleration(); + if (windowView != null) { + windowView.invalidate(); + } + if (transitionViewHolder.avatarImage != null && !foundViewToClose) { + transitionViewHolder.avatarImage.setVisible(true, true); + transitionViewHolder.avatarImage = null; + } + if (transitionViewHolder.storyImage != null && !foundViewToClose) { + transitionViewHolder.storyImage.setAlpha(1f); + transitionViewHolder.storyImage.setVisible(true, true); + } + final PeerStoriesView peerStoriesView = getCurrentPeerView(); + if (peerStoriesView != null) { + peerStoriesView.updatePosition(); + } + updatePlayingMode(); + locker.unlock(); + } + }); + + openCloseAnimator.setStartDelay(40); + openCloseAnimator.setDuration(250); + openCloseAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); + openCloseAnimator.start(); + + if (doOnAnimationReady != null) { + doOnAnimationReady.run(); + doOnAnimationReady = null; + } + } + + public void instantClose() { + if (!isShowing) { + return; + } + AndroidUtilities.hideKeyboard(windowView); + isClosed = true; + updatePlayingMode(); + fromX = fromY = 0; + if (transitionViewHolder.avatarImage != null) { + transitionViewHolder.avatarImage.setVisible(true, true); + } + if (transitionViewHolder.storyImage != null) { + transitionViewHolder.storyImage.setVisible(true, true); + } + transitionViewHolder.storyImage = null; + transitionViewHolder.avatarImage = null; + containerView.disableHwAcceleration(); + checkNavBarColor(); + locker.unlock(); + if (currentPlayerScope != null) { + currentPlayerScope.invalidate(); + } + release(); + if (ATTACH_TO_FRAGMENT) { + AndroidUtilities.removeFromParent(windowView); + } else { + windowManager.removeView(windowView); + } + windowView = null; + isShowing = false; + foundViewToClose = false; + if (onCloseListener != null) { + onCloseListener.run(); + onCloseListener = null; + } + } + + private void startCloseAnimation(boolean backAnimation) { + setNavigationButtonsColor(false); + updateTransitionParams(); + locker.lock(); + fromDismissOffset = swipeToDismissOffset; + openCloseAnimator = ValueAnimator.ofFloat(progressToOpen, 0); + openCloseAnimator.addUpdateListener(animation -> { + progressToOpen = (float) animation.getAnimatedValue(); + checkNavBarColor(); + if (windowView != null) { + windowView.invalidate(); + } + }); + if (!backAnimation) { + fromX = fromY = 0; + if (transitionViewHolder.avatarImage != null) { + transitionViewHolder.avatarImage.setVisible(true, true); + } + if (transitionViewHolder.storyImage != null) { + transitionViewHolder.storyImage.setVisible(true, true); + } + transitionViewHolder.storyImage = null; + transitionViewHolder.avatarImage = null; + } else { + layoutAndFindView(); + } + AndroidUtilities.runOnUIThread(() -> { + containerView.enableHwAcceleration(); + openCloseAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + containerView.disableHwAcceleration(); + checkNavBarColor(); + locker.unlock(); + if (transitionViewHolder.avatarImage != null) { + transitionViewHolder.avatarImage.setVisible(true, true); + transitionViewHolder.avatarImage = null; + } + if (transitionViewHolder.storyImage != null) { + transitionViewHolder.storyImage.setAlpha(1f); + transitionViewHolder.storyImage.setVisible(true, true); + } + if (transitionViewHolder.radialProgressUpload != null) { + final PeerStoriesView peerStoriesView = getCurrentPeerView(); + if (peerStoriesView != null && peerStoriesView.headerView.radialProgress != null) { + transitionViewHolder.radialProgressUpload.copyParams(peerStoriesView.headerView.radialProgress); + } + } + if (currentPlayerScope != null) { + currentPlayerScope.invalidate(); + } + release(); + try { + AndroidUtilities.runOnUIThread(() -> { + if (windowView == null) { + return; + } + if (ATTACH_TO_FRAGMENT) { + AndroidUtilities.removeFromParent(windowView); + } else { + windowManager.removeView(windowView); + } + windowView = null; + }); + } catch (Exception e) { + + } + isShowing = false; + foundViewToClose = false; + if (onCloseListener != null) { + onCloseListener.run(); + onCloseListener = null; + } + } + }); + openCloseAnimator.setDuration(400); + openCloseAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); +// openCloseAnimator.setDuration(2000); +// openCloseAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); + openCloseAnimator.start(); + }, 16); + } + + public void release() { + lastUri = null; + setInTouchMode(false); + allowScreenshots(true); + if (playerHolder != null) { + playerHolder.release(null); + playerHolder = null; + } + for (int i = 0; i < preparedPlayers.size(); i++) { + preparedPlayers.get(i).release(null); + } + preparedPlayers.clear(); + MessagesController.getInstance(currentAccount).getStoriesController().pollViewsForSelfStories(false); + if (ATTACH_TO_FRAGMENT) { + lockOrientation(false); + } + + globalInstances.remove(this); + selfStoriesViewsOffset = 0; + lastStoryItem = null; + } + + public void close(boolean backAnimation) { + AndroidUtilities.hideKeyboard(windowView); + isClosed = true; + invalidateOutRect = true; + updatePlayingMode(); + startCloseAnimation(backAnimation); + if (unreadStateChanged) { + unreadStateChanged = false; + //MessagesController.getInstance(currentAccount).getStoriesController().scheduleSort(); + } + } + + public int getNavigationBarColor(int currentColor) { + return ColorUtils.blendARGB(currentColor, Color.BLACK, getBlackoutAlpha()); + } + + private float getBlackoutAlpha() { + return progressToOpen * (0.5f + 0.5f * (1f - progressToDismiss)); + } + + public boolean onBackPressed() { + if (selfStoriesViewsOffset != 0) { + cancelSwipeToViews(false); + return true; + } else if (closeKeyboardOrEmoji()) { + return true; + } else { + close(true); + } + return true; + } + + public boolean isShown() { + return !isClosed; + } + + public void checkNavBarColor() { + if (ATTACH_TO_FRAGMENT && LaunchActivity.instance != null) { + LaunchActivity.instance.checkSystemBarColors(true, false, true, false); + //LaunchActivity.instance.setNavigationBarColor(fragment.getNavigationBarColor(), false); + } + } + + /** + * Changing the color of buttons in the navigation bar is a difficult task for weak devices. + * It's better to change the color before the animation starts. + */ + private void setNavigationButtonsColor(boolean isOpening) { + LaunchActivity activity = LaunchActivity.instance; + if (ATTACH_TO_FRAGMENT && activity != null) { + if (isOpening) { + openedFromLightNavigationBar = activity.isLightNavigationBar(); + } + if (openedFromLightNavigationBar) { + activity.setLightNavigationBar(!isOpening); + } + } + } + + public boolean attachedToParent() { + if (ATTACH_TO_FRAGMENT) { + return windowView != null; + } + return false; + } + + public void setKeyboardHeightFromParent(int keyboardHeight) { + if (realKeyboardHeight != keyboardHeight) { + realKeyboardHeight = keyboardHeight; + storiesViewPager.setKeyboardHeight(keyboardHeight); + storiesViewPager.requestLayout(); + } + } + + public boolean isFullyVisible() { + return fullyVisible; + } + + public void presentFragment(BaseFragment fragment) { + if (ATTACH_TO_FRAGMENT) { + LaunchActivity.getLastFragment().presentFragment(fragment); + } else { + LaunchActivity.getLastFragment().presentFragment(fragment); + close(false); + } + } + + public Theme.ResourcesProvider getResourceProvider() { + return resourcesProvider; + } + + public FrameLayout getContainerView() { + return containerView; + } + + @Nullable + public FrameLayout getContainerForBulletin() { + final PeerStoriesView peerStoriesView = storiesViewPager.getCurrentPeerView(); + if (peerStoriesView != null) { + return peerStoriesView.storyContainer; + } + return null; + } + + public void startActivityForResult(Intent photoPickerIntent, int code) { + if (fragment.getParentActivity() == null) { + return; + } + fragment.getParentActivity().startActivityForResult(photoPickerIntent, code); + } + + public void onActivityResult(int requestCode, int resultCode, Intent data) { + PeerStoriesView currentPeerView = storiesViewPager.getCurrentPeerView(); + if (currentPeerView != null) { + currentPeerView.onActivityResult(requestCode, resultCode, data); + } + } + + public void dispatchKeyEvent(KeyEvent event) { + if (event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP || event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_DOWN) { + dispatchVolumeEvent(event); + } + } + + public void dismissVisibleDialogs() { + if (currentDialog != null) { + currentDialog.dismiss(); + } + PeerStoriesView peerView = getCurrentPeerView(); + if (peerView != null) { + if (peerView.reactionsContainerLayout != null && peerView.reactionsContainerLayout.getReactionsWindow() != null) { + peerView.reactionsContainerLayout.getReactionsWindow().dismiss(); + } + if (peerView.shareAlert != null) { + peerView.shareAlert.dismiss(); + } + peerView.needEnterText(); + } + } + + public float getProgressToSelfViews() { + if (selfStoryViewsView == null) { + return 0; + } + return selfStoryViewsView.progressToOpen; + } + + public void setSelfStoriesViewsOffset(float currentTranslation) { + selfStoriesViewsOffset = currentTranslation; + final PeerStoriesView peerStoriesView = storiesViewPager.getCurrentPeerView(); + if (peerStoriesView != null) { + peerStoriesView.invalidate(); + } + containerView.invalidate(); + } + + public void openViews() { + checkSelfStoriesView(); + AndroidUtilities.runOnUIThread(() -> { + allowSelfStoriesView = true; + cancelSwipeToViews(true); + }, 30); + + } + + public boolean soundEnabled() { + return !isInSilentMode; + } + + public void allowScreenshots(boolean allowScreenshots) { + if (BuildVars.DEBUG_PRIVATE_VERSION) { + return; + } + if (this.allowScreenshots != allowScreenshots) { + this.allowScreenshots = allowScreenshots; + + if (surfaceView != null) { + surfaceView.setSecure(!allowScreenshots); + } + if (ATTACH_TO_FRAGMENT) { + if (fragment.getParentActivity() != null) { + if (allowScreenshots) { + fragment.getParentActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE); + } else { + fragment.getParentActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); + } + } + } else { + if (allowScreenshots) { + windowLayoutParams.flags &= ~WindowManager.LayoutParams.FLAG_SECURE; + } else { + windowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_SECURE; + } + try { + windowManager.updateViewLayout(windowView, windowLayoutParams); + } catch (Exception e) { + FileLog.e(e); + } + } + } + } + + public void openFor(BaseFragment fragment, RecyclerListView recyclerListView, ChatActionCell cell) { + MessageObject messageObject = cell.getMessageObject(); + if (fragment == null || fragment.getContext() == null) { + return; + } + if (messageObject.type == MessageObject.TYPE_STORY_MENTION) { + TLRPC.StoryItem storyItem = messageObject.messageOwner.media.storyItem; + storyItem.dialogId = messageObject.messageOwner.media.user_id; + storyItem.messageId = messageObject.getId(); + open(fragment.getContext(), messageObject.messageOwner.media.storyItem, StoriesListPlaceProvider.of(recyclerListView)); + } + } + + public void doOnAnimationReady(Runnable runnable) { + doOnAnimationReady = runnable; + } + + public interface PlaceProvider { + boolean findView(long dialogId, int messageId, int storyId, int type, TransitionViewHolder holder); + + void preLayout(long currentDialogId, int messageId, Runnable o); + } + + public interface HolderDrawAbove { + void draw(Canvas canvas, RectF bounds, float alpha); + } + + public interface HolderClip { + void clip(Canvas canvas, RectF bounds, float alpha); + } + + public static class TransitionViewHolder { + public View view; + public ImageReceiver avatarImage; + public ImageReceiver storyImage; + public RadialProgress radialProgressUpload; + public HolderDrawAbove drawAbove; + public HolderClip drawClip; + public View clipParent; + public float clipTop; + public float clipBottom; + public ImageReceiver crossfadeToAvatarImage; + StoriesUtilities.AvatarStoryParams params; + + public void clear() { + view = null; + params = null; + avatarImage = null; + storyImage = null; + drawAbove = null; + drawClip = null; + clipParent = null; + radialProgressUpload = null; + crossfadeToAvatarImage = null; + clipTop = 0; + clipBottom = 0; + } + } + + static int queuePointer = 0; + + public class VideoPlayerHolder { + + public boolean paused; + public TLRPC.Document document; + VideoPlayer videoPlayer; + Runnable initRunnable; + volatile boolean released; + boolean firstFrameRendered; + + float progress; + int lastState; + public long currentPosition; + long playerDuration; + boolean audioDisabled; + boolean stubAvailable; + boolean logBuffering; + + final DispatchQueue dispatchQueue = Utilities.getOrCreatePlayerQueue(); + Uri uri; + + Runnable progressRunnable = new Runnable() { + @Override + public void run() { + if (videoPlayer != null) { + if (lastState == ExoPlayer.STATE_ENDED) { + progress = 1f; + } else { + currentPosition = videoPlayer.getCurrentPosition(); + playerDuration = videoPlayer.getDuration(); + } + if (lastState == ExoPlayer.STATE_READY) { + dispatchQueue.cancelRunnable(progressRunnable); + dispatchQueue.postRunnable(progressRunnable, 16); + } + } + } + }; + + long startTime; + + void preparePlayer(Uri uri, boolean audioDisabled) { + this.audioDisabled = audioDisabled; + paused = true; + if (initRunnable != null) { + dispatchQueue.cancelRunnable(initRunnable); + } + dispatchQueue.postRunnable(initRunnable = () -> { + if (released) { + return; + } + ensurePlayerCreated(audioDisabled); + videoPlayer.preparePlayer(uri, "other", FileLoader.PRIORITY_LOW); + videoPlayer.setPlayWhenReady(false); + videoPlayer.setWorkerQueue(dispatchQueue); + }); + } + + void start(boolean paused, Uri uri, long t, boolean audioDisabled) { + startTime = System.currentTimeMillis(); + this.audioDisabled = audioDisabled; + this.paused = paused; + dispatchQueue.postRunnable(initRunnable = () -> { + if (released) { + return; + } + if (videoPlayer == null) { + ensurePlayerCreated(audioDisabled); + videoPlayer.preparePlayer(uri, "other"); + videoPlayer.setWorkerQueue(dispatchQueue); + if (!paused) { + if (USE_SURFACE_VIEW) { + videoPlayer.setSurfaceView(surfaceView); + } else { + videoPlayer.setTextureView(textureView); + } + videoPlayer.setPlayWhenReady(true); + } + } else { + if (!paused) { + if (USE_SURFACE_VIEW) { + videoPlayer.setSurfaceView(surfaceView); + } else { + videoPlayer.setTextureView(textureView); + } + videoPlayer.play(); + } + } + if (t > 0) { + videoPlayer.seekTo(t); + } + + videoPlayer.setVolume(isInSilentMode ? 0 : 1f); + AndroidUtilities.runOnUIThread(() -> initRunnable = null); + }); + } + + private void ensurePlayerCreated(boolean audioDisabled) { + if (videoPlayer != null) { + videoPlayer.releasePlayer(true); + } + videoPlayer = new VideoPlayer(false, audioDisabled); + videoPlayer.setDelegate(new VideoPlayer.VideoPlayerDelegate() { + @Override + public void onStateChanged(boolean playWhenReady, int playbackState) { + lastState = playbackState; + if (playbackState == ExoPlayer.STATE_READY || playbackState == ExoPlayer.STATE_BUFFERING) { + dispatchQueue.cancelRunnable(progressRunnable); + dispatchQueue.postRunnable(progressRunnable); + if (firstFrameRendered && playbackState == ExoPlayer.STATE_BUFFERING) { + logBuffering = true; + AndroidUtilities.runOnUIThread(() -> { + final PeerStoriesView storiesView = getCurrentPeerView(); + if (storiesView != null && storiesView.currentStory.storyItem != null) { + FileLog.d("StoryViewer displayed story buffering dialogId=" + storiesView.getCurrentPeer() + " storyId=" + storiesView.currentStory.storyItem.id); + } + }); + } + if (logBuffering && playbackState == ExoPlayer.STATE_READY) { + logBuffering = false; + AndroidUtilities.runOnUIThread(() -> { + final PeerStoriesView storiesView = getCurrentPeerView(); + if (storiesView != null && storiesView.currentStory.storyItem != null) { + FileLog.d("StoryViewer displayed story playing dialogId=" + storiesView.getCurrentPeer() + " storyId=" + storiesView.currentStory.storyItem.id); + } + }); + } + + + } else if (playbackState == ExoPlayer.STATE_ENDED) { + if (isCaptionPartVisible) { + progress = 0; + videoPlayer.seekTo(0); + videoPlayer.play(); + } else { + progress = 1f; + } + } + } + + @Override + public void onError(VideoPlayer player, Exception e) { + FileLog.e(e); + } + + @Override + public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) { + + } + + @Override + public void onRenderedFirstFrame() { + AndroidUtilities.runOnUIThread(() -> { + if (released || currentPlayerScope == null) { + return; + } + firstFrameRendered = currentPlayerScope.firstFrameRendered = true; + currentPlayerScope.invalidate(); + + if (onReadyListener != null) { + onReadyListener.run(); + onReadyListener = null; + } + }, 16); + } + + @Override + public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { + + } + + @Override + public boolean onSurfaceDestroyed(SurfaceTexture surfaceTexture) { + return false; + } + }); + videoPlayer.setIsStory(); + } + + private Runnable onReadyListener; + public void setOnReadyListener(Runnable listener) { + onReadyListener = listener; + } + + boolean release(Runnable whenReleased) { + TLRPC.Document document = this.document; + if (document != null) { + int priority = FileStreamLoadOperation.getStreamPrioriy(document); + if (priority != FileLoader.PRIORITY_LOW) { + FileStreamLoadOperation.setPriorityForDocument(document, FileLoader.PRIORITY_LOW); + FileLoader.getInstance(currentAccount).changePriority(FileLoader.PRIORITY_LOW, document, null, null, null, null, null); + // FileLoader.getInstance(currentAccount).cancelLoadFile(document); + } + // FileLoader.getInstance(currentAccount).changePriority(FileLoader.PRIORITY_LOW, document, null, null, null, null, null); + } + released = true; + dispatchQueue.cancelRunnable(initRunnable); + initRunnable = null; + dispatchQueue.postRunnable(() -> { + if (videoPlayer != null) { + videoPlayer.setTextureView(null); + videoPlayer.setSurfaceView(null); + videoPlayer.releasePlayer(false); + } + if (document != null) { + FileLoader.getInstance(currentAccount).cancelLoadFile(document); + // FileLoader.getInstance(currentAccount).changePriority(FileLoader.PRIORITY_LOW, document, null, null, null, null, null); + } + if (whenReleased != null) { + AndroidUtilities.runOnUIThread(whenReleased); + } + videoPlayer = null; + }); + if (playerStubBitmap != null) { + AndroidUtilities.recycleBitmap(playerStubBitmap); + playerStubBitmap = null; + } + return true; + } + + public void pause() { + if (released) { + return; + } + if (paused) { + return; + } + paused = true; + if (USE_SURFACE_VIEW && surfaceView != null && firstFrameRendered && surfaceView.getHolder().getSurface().isValid()) { + stubAvailable = true; + if (playerStubBitmap == null) { + playerStubBitmap = Bitmap.createBitmap(720, 1280, Bitmap.Config.ARGB_8888); + playerStubPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + AndroidUtilities.getBitmapFromSurface(surfaceView, playerStubBitmap); + } + } + dispatchQueue.postRunnable(() -> { + if (videoPlayer != null) { + videoPlayer.pause(); + } + }); + } + + public void play() { + if (released) { + return; + } + if (!paused) { + return; + } + paused = false; + dispatchQueue.postRunnable(() -> { + if (videoPlayer != null) { + if (USE_SURFACE_VIEW) { + videoPlayer.setSurfaceView(surfaceView); + } else { + videoPlayer.setTextureView(textureView); + } + videoPlayer.setPlayWhenReady(true); + } + }); + } + + public void setAudioEnabled(boolean enabled, boolean prepared) { + boolean disabled = !enabled; + if (audioDisabled == disabled) { + return; + } + audioDisabled = disabled; + dispatchQueue.postRunnable(() -> { + if (videoPlayer == null) { + return; + } + boolean playing = videoPlayer.isPlaying(); + if (enabled && !videoPlayer.createdWithAudioTrack()) { + //release and create new with audio track + videoPlayer.pause(); + long position = videoPlayer.getCurrentPosition(); + videoPlayer.releasePlayer(false); + videoPlayer = null; + ensurePlayerCreated(audioDisabled); + videoPlayer.preparePlayer(uri, "other"); + videoPlayer.setWorkerQueue(dispatchQueue); + if (!prepared) { + if (USE_SURFACE_VIEW) { + videoPlayer.setSurfaceView(surfaceView); + } else { + videoPlayer.setTextureView(textureView); + } + } + // videoPlayer.setTextureView(textureView); + videoPlayer.seekTo(position + 50); + if (playing && !prepared) { + videoPlayer.setPlayWhenReady(true); + videoPlayer.play(); + } else { + videoPlayer.setPlayWhenReady(false); + videoPlayer.pause(); + } + } else { + videoPlayer.setVolume(enabled ? 1f : 0); + } + }); + } + + public float getPlaybackProgress(long totalDuration) { + if (lastState == ExoPlayer.STATE_ENDED) { + progress = 1f; + } else { + float localProgress; + if (totalDuration != 0) { + localProgress = currentPosition / (float) totalDuration; + } else { + localProgress = currentPosition / (float) playerDuration; + } + if (localProgress < progress) { + return progress; + } + progress = localProgress; + } + return progress; + } + + public void loopBack() { + progress = 0; + lastState = ExoPlayer.STATE_IDLE; + dispatchQueue.postRunnable(() -> { + if (videoPlayer != null) { + videoPlayer.seekTo(0); + } + progress = 0; + currentPosition = 0; + }); + } + + public void setVolume(float v) { + dispatchQueue.postRunnable(() -> { + if (videoPlayer != null) { + videoPlayer.setVolume(v); + } + }); + } + + public boolean isBuffering() { + return !released && lastState == ExoPlayer.STATE_BUFFERING; + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryViewsUsersAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryViewsUsersAlert.java new file mode 100644 index 0000000000..d8faa683bd --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryViewsUsersAlert.java @@ -0,0 +1,266 @@ +package org.telegram.ui.Stories; + +import android.content.Context; +import android.os.Bundle; +import android.text.TextUtils; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.core.util.Consumer; +import androidx.recyclerview.widget.RecyclerView; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ContactsController; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.R; +import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Cells.FixedHeightEmptyCell; +import org.telegram.ui.Cells.GroupCreateUserCell; +import org.telegram.ui.Cells.ReactedUserHolderView; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.Components.UsersAlertBase; +import org.telegram.ui.LaunchActivity; +import org.telegram.ui.ProfileActivity; +import org.telegram.ui.Stories.recorder.ButtonWithCounterView; + +import java.util.ArrayList; + +//TODO stories +//add pagination +public class StoryViewsUsersAlert extends UsersAlertBase { + + private static final int FIRST_PADDING_ITEM = 0; + private static final int USER_ITEM = 1; + private static final int LAST_ITEM = 2; + private static final int BUTTON_PADDING = 3; + + + ListAdapter listAdapter; + boolean showSearch = false; + ArrayList users = new ArrayList<>(); + + StoryViewer storyViewer; + + public StoryViewsUsersAlert(Context context, StoryViewer storyViewer, Theme.ResourcesProvider resourcesProvider) { + super(context, true, storyViewer.currentAccount, resourcesProvider); + showSearch(showSearch = false); + searchListViewAdapter = searchListViewAdapter = new SearchAdapter(); + listView.setAdapter(listViewAdapter = listAdapter = new ListAdapter()); + FrameLayout buttonContainer = new FrameLayout(getContext()); + buttonContainer.setBackgroundColor(Theme.getColor(Theme.key_dialogBackground, resourcesProvider)); + ButtonWithCounterView button = new ButtonWithCounterView(getContext(), resourcesProvider); + + buttonContainer.addView(button, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.FILL_HORIZONTAL, 10, 10, 10, 10)); + containerView.addView(buttonContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM)); + button.setText(LocaleController.getString("Close", R.string.Close), false); + button.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + dismiss(); + } + }); + listView.setOnItemClickListener((view, position) -> { + if (position < 0 || position > listAdapter.items.size() - 1) { + return; + } + TLRPC.TL_storyView user = listAdapter.items.get(position).user; + + if (user != null) { + dismiss(); + + Bundle args = new Bundle(); + args.putLong("user_id", user.user_id); + ProfileActivity profileActivity = new ProfileActivity(args); + + storyViewer.presentFragment(profileActivity); + } + }); + } + + @Override + protected int measurePadding(int availableHeight) { + int padding = super.measurePadding(availableHeight); + if (!showSearch) { + int h = availableHeight - AndroidUtilities.dp(ReactedUserHolderView.STORY_ITEM_HEIGHT_DP) * users.size() - AndroidUtilities.dp(36) - AndroidUtilities.dp(80); + if (h > padding) { + return h; + } + } + return padding; + } + + public void setViews(TLRPC.StoryItem storyItem) { + int viewsCount = storyItem.views != null ? storyItem.views.views_count : 0; + ArrayList recentViewers = storyItem.views != null ? storyItem.views.recent_viewers : new ArrayList<>(); + setTitle(LocaleController.formatPluralStringComma("Views", viewsCount)); + showSearch(viewsCount > 20); + users.clear(); + for (int i = 0; i < recentViewers.size(); i++) { + TLRPC.TL_storyView storyView = new TLRPC.TL_storyView(); + storyView.user_id = recentViewers.get(i); + users.add(storyView); + } + + listAdapter.updateRows(); + } + + private class SearchAdapter extends RecyclerListView.SelectionAdapter { + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return null; + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + + } + + @Override + public int getItemCount() { + return 0; + } + + @Override + public boolean isEnabled(RecyclerView.ViewHolder holder) { + return false; + } + } + + private class ListAdapter extends RecyclerListView.SelectionAdapter { + + ArrayList items = new ArrayList<>(); + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view; + switch (viewType) { + case FIRST_PADDING_ITEM: + view = new View(getContext()) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(36 + (showSearch ? 58 : 0)), MeasureSpec.EXACTLY)); + } + }; + break; + case BUTTON_PADDING: + view = new FixedHeightEmptyCell(getContext(), 64); + break; + case USER_ITEM: + view = new ReactedUserHolderView(ReactedUserHolderView.STYLE_STORY, currentAccount, getContext(), resourcesProvider); + break; + default: + case LAST_ITEM: + view = new View(getContext()); + break; + } + return new RecyclerListView.Holder(view); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + if (holder.getItemViewType() == USER_ITEM) { + ReactedUserHolderView view = (ReactedUserHolderView) holder.itemView; + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(items.get(position).user.user_id); + + view.setUserReaction(user, null, null, items.get(position).user.date, true, false); + // items.get(position + 1).viewType == USER_ITEM + } + } + + @Override + public int getItemCount() { + return items.size(); + } + + @Override + public boolean isEnabled(RecyclerView.ViewHolder holder) { + return holder.getItemViewType() == USER_ITEM; + } + + public void updateRows() { + items.clear(); + items.add(new Item(FIRST_PADDING_ITEM)); + for (int i = 0; i < users.size(); i++) { + items.add(new Item(USER_ITEM, users.get(i))); + } + items.add(new Item(BUTTON_PADDING)); + if (showSearch) { + items.add(new Item(LAST_ITEM)); + } + notifyDataSetChanged(); + } + + @Override + public int getItemViewType(int position) { + return items.get(position).viewType; + } + } + + public static Runnable preload(int currentAccount, Context context, Theme.ResourcesProvider resourcesProvider, TLRPC.StoryItem storyItem, StoryViewer storyViewer, Consumer alertConsumer) { + TLRPC.TL_stories_getStoryViewsList req = new TLRPC.TL_stories_getStoryViewsList(); + req.id = storyItem.id; + req.limit = 100; + req.offset_id = 0; + req.offset_date = 0; + boolean[] canceled = new boolean[]{false}; + int id = ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + if (canceled[0]) { + return; + } + if (response != null) { + TLRPC.TL_stories_storyViewsList res = (TLRPC.TL_stories_storyViewsList) response; + MessagesController.getInstance(currentAccount).putUsers(res.users, false); + StoryViewsUsersAlert alert = new StoryViewsUsersAlert(context, storyViewer, resourcesProvider); + alert.setTitle(LocaleController.formatPluralStringComma("Views", res.count)); + alert.showSearch(res.count > 20); + alert.users.clear(); + for (int i = 0; i < res.views.size(); i++) { + alert.users.add(res.views.get(i)); + } + alert.listAdapter.updateRows(); + if (res.count > storyItem.views.views_count) { + storyItem.views.recent_viewers.clear(); + for (int i = 0; i < (Math.min(3, res.users.size())); i++) { + storyItem.views.recent_viewers.add(res.users.get(i).id); + } + storyItem.views.views_count = res.count; + } + alertConsumer.accept(alert); + } else { + alertConsumer.accept(null); + } + })); + return new Runnable() { + @Override + public void run() { + canceled[0] = true; + ConnectionsManager.getInstance(currentAccount).cancelRequest(id, false); + } + }; + } + + + private class Item { + final int viewType; + TLRPC.TL_storyView user; + + private Item(int viewType) { + this.viewType = viewType; + } + + private Item(int viewType, TLRPC.TL_storyView user) { + this.viewType = viewType; + this.user = user; + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryWaveEffectView.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryWaveEffectView.java new file mode 100644 index 0000000000..ed3e085df8 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryWaveEffectView.java @@ -0,0 +1,461 @@ +package org.telegram.ui.Stories; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Interpolator; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.SurfaceTexture; +import android.opengl.EGL14; +import android.opengl.GLES20; +import android.opengl.GLUtils; +import android.os.Message; +import android.util.Log; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.TextureView; +import android.view.View; +import android.view.WindowManager; +import android.view.animation.DecelerateInterpolator; +import android.view.inspector.WindowInspector; + +import androidx.annotation.NonNull; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.DispatchQueue; +import org.telegram.messenger.LiteMode; +import org.telegram.messenger.SharedConfig; +import org.telegram.messenger.Utilities; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Stories.recorder.StoryRecorder; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.util.List; + +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.egl.EGLContext; +import javax.microedition.khronos.egl.EGLDisplay; +import javax.microedition.khronos.egl.EGLSurface; +import javax.microedition.khronos.opengles.GL10; + +public class StoryWaveEffectView extends TextureView implements TextureView.SurfaceTextureListener { + + public static StoryWaveEffectView launch(Context context, float cx, float cy, float r) { + if (SharedConfig.getDevicePerformanceClass() <= SharedConfig.PERFORMANCE_CLASS_AVERAGE || !LiteMode.isEnabled(LiteMode.FLAGS_CHAT)) { + return null; + } + return new StoryWaveEffectView(context, cx, cy, r); + } + + private final static String VERTEX_SHADER = + "attribute vec4 vPosition;\n" + + "attribute vec2 aTexCoord;\n" + + "varying vec2 vTexCoord;\n" + + "void main() {\n" + + " gl_Position = vPosition;\n" + + " vTexCoord = aTexCoord;\n" + + "}\n"; + private final static String FRAGMENT_SHADER = + "precision lowp float;\n" + + "varying vec2 vTexCoord;\n" + + "uniform sampler2D sTexture;\n" + + "uniform vec2 iResolution;\n" + + "uniform vec2 c;\n" + + "uniform float r;\n" + + "uniform float t;\n" + + "void main() {\n" + + " vec2 U = vTexCoord * iResolution.xy;" + + " float maxd = .35 * max(\n" + + " max(length(c - vec2(0., 0.)), length(c - vec2(iResolution.x, 0.))),\n" + + " max(length(c - vec2(0., iResolution.y)), length(c - iResolution))\n" + + " );" + + " float len = 250.;\n" + + " float amplitude = len / 2. * (1. - t);" + + " float R = mix(r - len, maxd + len, t);\n" + + " float d = (length(U - c) - R) / len;\n" + + " if (d > -1. && d < 1. && length(U - c) > r) {\n" + + " vec2 dir = normalize(c - U);\n" + + " vec2 uv = vTexCoord + dir * d * pow(1. - abs(d), 1.5) * amplitude / iResolution.xy;\n" + + " if (length(uv * iResolution - c) > r) {\n" + + " gl_FragColor = texture2D(sTexture, uv);\n" + + " } else {\n" + + " gl_FragColor = vec4(0.);\n" + + " }\n" + + " gl_FragColor.a *= min(1., (1. - abs(d)) * 2.);\n" + + " } else {\n" + + " gl_FragColor = vec4(0.);\n" + + " }\n" + + "}\n"; + + private Bitmap screenshot; + + private final WindowManager.LayoutParams layoutParams; + private final WindowManager windowManager; + private RenderingThread renderThread; + + private final float[] vertices = { + -1.0f, -1.0f, + 1.0f, -1.0f, + -1.0f, 1.0f, + 1.0f, 1.0f + }; + private final FloatBuffer vertexBuffer; + + private final float[] uv = { + 0f, 1f, + 1f, 1f, + 0f, 0f, + 1f, 0f + }; + private final FloatBuffer uvBuffer; + + private float cx, cy, r; + + public StoryWaveEffectView(Context context, float cx, float cy, float r) { + super(context); + + this.cx = cx; + this.cy = cy; + this.r = r; + + setSurfaceTextureListener(this); + + vertexBuffer = ByteBuffer.allocateDirect(vertices.length * 4) + .order(ByteOrder.nativeOrder()) + .asFloatBuffer() + .put(vertices); + vertexBuffer.position(0); + + uvBuffer = ByteBuffer.allocateDirect(uv.length * 4) + .order(ByteOrder.nativeOrder()) + .asFloatBuffer() + .put(uv); + uvBuffer.position(0); + + layoutParams = new WindowManager.LayoutParams( + WindowManager.LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.LAST_APPLICATION_WINDOW, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, + PixelFormat.TRANSLUCENT + ); + + layoutParams.flags |= + WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | + WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR | + WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS | + WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION | + WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; + + layoutParams.gravity = Gravity.FILL; + layoutParams.x = 0; + layoutParams.y = 0; + + windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + } + + public StoryWaveEffectView prepare() { + screenshot = makeScreenshot(); + return this; + } + + public StoryWaveEffectView start() { + windowManager.addView(this, layoutParams); + return this; + } + + private boolean madeTouchable; + @Override + public boolean onTouchEvent(MotionEvent event) { + if (!madeTouchable) { + layoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; + windowManager.updateViewLayout(this, layoutParams); + madeTouchable = true; + + animate().alpha(0).setDuration(180).withEndAction(() -> { + if (renderThread != null) { + renderThread.finish(); + } + }).start(); + } + return super.onTouchEvent(event); + } + + private Bitmap makeScreenshot() { + List views = AndroidUtilities.allGlobalViews(); + if (views == null) { + return null; + } + Bitmap bitmap = Bitmap.createBitmap(AndroidUtilities.displaySize.x, AndroidUtilities.statusBarHeight + AndroidUtilities.displaySize.y + AndroidUtilities.navigationBarHeight, Bitmap.Config.ARGB_4444); + Canvas canvas = new Canvas(bitmap); + Paint navBarPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + navBarPaint.setColor(Theme.getColor(Theme.key_windowBackgroundGray)); + canvas.drawRect(0, bitmap.getHeight() - AndroidUtilities.navigationBarHeight, bitmap.getWidth(), bitmap.getHeight(), navBarPaint); + for (int i = 0; i < views.size(); ++i) { + View view = views.get(i); + if (view != null && !(view instanceof StoryWaveEffectView) && !(view instanceof StoryRecorder.WindowView)) { + canvas.save(); + canvas.translate(view.getX(), view.getY()); + view.draw(canvas); + canvas.restore(); + } + } + return bitmap; + } + + private EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) { + int[] attrib_list = { EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; + return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list); + } + + @Override + public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) { + if (renderThread == null) { + renderThread = new RenderingThread(surface, screenshot); + } + } + + @Override + public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) { + + } + + @Override + public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) { + if (renderThread != null) { + renderThread.finish(); + renderThread = null; + } + return false; + } + + @Override + public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) { + + } + + + private class RenderingThread extends Thread { + + private volatile boolean running; + + private SurfaceTexture surfaceTexture; + private EGL10 egl; + private EGLDisplay eglDisplay; + private EGLContext eglContext; + private EGLSurface eglSurface; + + private boolean inited; + private int program; + private int sTexture; + private int vPosition, aTexCoord; + private int iC, iTime, iR, iResolution; + + private Bitmap bitmap; + private int[] textureId = new int[1]; + + public RenderingThread(SurfaceTexture surfaceTexture, Bitmap bitmap) { + super("StoryWaveEffectView.RenderingThread"); + this.surfaceTexture = surfaceTexture; + this.bitmap = bitmap; + start(); + } + + @Override + public void run() { + running = true; + eglSetup(surfaceTexture); + final long maxdt = (long) (1000L / Math.max(30, AndroidUtilities.screenRefreshRate)); + start = System.currentTimeMillis(); + while (running) { + long start = System.currentTimeMillis(); + drawFrame(); + long dt = System.currentTimeMillis() - start; + if (dt < maxdt - 1) { + try { + Thread.sleep(maxdt - 1 - dt); + } catch (Exception ignore) {} + } + } + cleanup(); + } + + private void eglSetup(SurfaceTexture surface) { + egl = (EGL10) EGLContext.getEGL(); + eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); + + int[] version = new int[2]; + egl.eglInitialize(eglDisplay, version); + + EGLConfig eglConfig = chooseEglConfig(); + eglContext = createContext(egl, eglDisplay, eglConfig); + + eglSurface = egl.eglCreateWindowSurface(eglDisplay, eglConfig, surface, null); + + if (eglSurface == null || eglSurface == EGL10.EGL_NO_SURFACE) { + throw new RuntimeException("GL Error: " + egl.eglGetError()); + } + + if (!egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) { + throw new RuntimeException("GL Make current error: " + egl.eglGetError()); + } + + int[] compiled = new int[1]; + int vertexShader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER); + GLES20.glShaderSource(vertexShader, VERTEX_SHADER); + GLES20.glCompileShader(vertexShader); + GLES20.glGetShaderiv(vertexShader, GLES20.GL_COMPILE_STATUS, compiled, 0); + if (compiled[0] == 0) { + final String log = GLES20.glGetShaderInfoLog(vertexShader); + GLES20.glDeleteShader(vertexShader); + throw new RuntimeException("Shader Compile Error: " + log); + } + + int fragmentShader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER); + GLES20.glShaderSource(fragmentShader, FRAGMENT_SHADER); + GLES20.glCompileShader(fragmentShader); + GLES20.glGetShaderiv(fragmentShader, GLES20.GL_COMPILE_STATUS, compiled, 0); + if (compiled[0] == 0) { + final String log = GLES20.glGetShaderInfoLog(fragmentShader); + GLES20.glDeleteShader(fragmentShader); + throw new RuntimeException("Shader Compile Error: " + log); + } + + program = GLES20.glCreateProgram(); + GLES20.glAttachShader(program, vertexShader); + GLES20.glAttachShader(program, fragmentShader); + GLES20.glLinkProgram(program); + + GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, compiled, 0); + if (compiled[0] == 0) { + final String log = GLES20.glGetProgramInfoLog(program); + GLES20.glDeleteProgram(program); + throw new RuntimeException("Program Link Error: " + log); + } + + vPosition = GLES20.glGetAttribLocation(program, "vPosition"); + aTexCoord = GLES20.glGetAttribLocation(program, "aTexCoord"); + iC = GLES20.glGetUniformLocation(program, "c"); + iR = GLES20.glGetUniformLocation(program, "r"); + iTime = GLES20.glGetUniformLocation(program, "t"); + iResolution = GLES20.glGetUniformLocation(program, "iResolution"); + sTexture = GLES20.glGetUniformLocation(program, "sTexture"); + + GLES20.glDeleteShader(vertexShader); + GLES20.glDeleteShader(fragmentShader); + + if (bitmap != null) { + GLES20.glGenTextures(1, textureId, 0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId[0]); + GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE); + GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE); + GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR); + GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_NEAREST); + GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); + bitmap.recycle(); + bitmap = null; + } + + GLES20.glEnable(GLES20.GL_BLEND); + GLES20.glDisable(GLES20.GL_DITHER); + GLES20.glDisable(GLES20.GL_STENCIL_TEST); + GLES20.glDisable(GLES20.GL_DEPTH_TEST); + + inited = true; + } + + private EGLConfig chooseEglConfig() { + int[] configsCount = new int[]{0}; + EGLConfig[] configs = new EGLConfig[1]; + int[] configSpec = { + EGL10.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT, + EGL10.EGL_RED_SIZE, 8, + EGL10.EGL_GREEN_SIZE, 8, + EGL10.EGL_BLUE_SIZE, 8, + EGL10.EGL_ALPHA_SIZE, 8, + EGL10.EGL_DEPTH_SIZE, 0, + EGL10.EGL_STENCIL_SIZE, 0, + EGL10.EGL_NONE + }; + + egl.eglChooseConfig(eglDisplay, configSpec, configs, 1, configsCount); + return configs[0]; + } + + final int[] array = new int[1]; + + private long start; + private final long duration = 800; + private final CubicBezierInterpolator interpolator = CubicBezierInterpolator.EASE_OUT; + + private void drawFrame() { + if (!inited) { + return; + } + + egl.eglQuerySurface(eglDisplay, eglSurface, EGL10.EGL_WIDTH, array); + int drawnWidth = array[0]; + egl.eglQuerySurface(eglDisplay, eglSurface, EGL10.EGL_HEIGHT, array); + int drawnHeight = array[0]; + + GLES20.glViewport(0, 0, drawnWidth, drawnHeight); + + GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); + + GLES20.glBlendFuncSeparate(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA, GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA); + + GLES20.glUseProgram(program); + + GLES20.glUniform2f(iResolution, drawnWidth, drawnHeight); + GLES20.glUniform2f(iC, cx, cy); + GLES20.glUniform1f(iR, r); + float tValue = (System.currentTimeMillis() - start) / (float) duration; + float tInterpolated = Math.min(1, interpolator.getInterpolation(Math.min(1, tValue))); + GLES20.glUniform1f(iTime, tInterpolated); + + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId[0]); + GLES20.glUniform1i(sTexture, 0); + + GLES20.glEnableVertexAttribArray(vPosition); + GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 8, vertexBuffer); + GLES20.glEnableVertexAttribArray(aTexCoord); + GLES20.glVertexAttribPointer(aTexCoord, 2, GLES20.GL_FLOAT, false, 8, uvBuffer); + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + GLES20.glDisableVertexAttribArray(vPosition); + + egl.eglSwapBuffers(eglDisplay, eglSurface); + + if (tValue >= 1 && running) { + running = false; + } + }; + + public void finish() { + running = false; + } + + private void cleanup() { + running = false; + + egl.eglMakeCurrent(eglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); + egl.eglDestroySurface(eglDisplay, eglSurface); + egl.eglDestroyContext(eglDisplay, eglContext); + egl.eglTerminate(eglDisplay); + + GLES20.glDeleteProgram(program); + + AndroidUtilities.runOnUIThread(() -> { + WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); + windowManager.removeView(StoryWaveEffectView.this); + }); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/UploadingDotsSpannable.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/UploadingDotsSpannable.java new file mode 100644 index 0000000000..64520608bd --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/UploadingDotsSpannable.java @@ -0,0 +1,84 @@ +package org.telegram.ui.Stories; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.text.TextPaint; +import android.text.style.ReplacementSpan; +import android.view.View; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.ui.Components.CubicBezierInterpolator; + +public class UploadingDotsSpannable extends ReplacementSpan { + private String text = "…"; + + private View parent; + int swapPosition1 = 1; + int swapPosition2 = 2; + float swapProgress; + boolean waitForNextAnimation; + long lastTime; + + CubicBezierInterpolator circle = new CubicBezierInterpolator(0, 0.5f, 0.5f, 1f); + private boolean isMediumTypeface; + + @Override + public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) { + return (int) paint.measureText(this.text); + } + + @Override + public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) { + + TextPaint textPaint = (TextPaint) paint; + float characterWidth = paint.measureText(this.text) / 3; + float baseline = -textPaint.getFontMetrics().top; + + float textThickness = (float) ((textPaint.getFontMetrics().bottom - textPaint.getFontMetrics().top) * (isMediumTypeface ? 0.05f : 0.0365f)); + baseline -= textThickness; + + if (waitForNextAnimation) { + if (System.currentTimeMillis() - lastTime > 1000) { + waitForNextAnimation = false; + } + } else { + swapProgress += 16 / 300f; + if (swapProgress > 1) { + swapProgress = 0; + swapPosition1--; + swapPosition2--; + if (swapPosition1 < 0) { + swapPosition1 = 1; + swapPosition2 = 2; + waitForNextAnimation = true; + lastTime = System.currentTimeMillis(); + } + } + } + for (int i = 0; i < 3; i++) { + float cx = characterWidth * i + x + characterWidth / 2f; + float cy = baseline; + if (i == swapPosition1) { + float fromX = cx; + float toX = characterWidth * (i + 1) + x + characterWidth / 2f; + cx = AndroidUtilities.lerp(fromX, toX, swapProgress); + float swapProgressHalf = swapProgress < 0.5f ? swapProgress / 0.5f : 1f - (swapProgress - 0.5f) / 0.5f; + cy = AndroidUtilities.lerp(cy, cy - characterWidth / 2f, circle.getInterpolation(swapProgressHalf)); + } else if (i == swapPosition2) { + float fromX = cx; + float toX = characterWidth * (i - 1) + x + characterWidth / 2f; + cx = AndroidUtilities.lerp(fromX, toX, swapProgress); + } + canvas.drawCircle(cx, cy, textThickness, paint); + } + if (parent != null) { + parent.invalidate(); + } + } + + public void setParent(View parent, boolean isMediumTypeface) { + this.parent = parent; + this.isMediumTypeface = isMediumTypeface; + } +} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/UserListPoller.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/UserListPoller.java new file mode 100644 index 0000000000..8dbac0efa6 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/UserListPoller.java @@ -0,0 +1,109 @@ +package org.telegram.ui.Stories; + +import android.view.View; + +import com.google.android.exoplayer2.util.Log; + +import org.checkerframework.checker.units.qual.A; +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.MessagesStorage; +import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.support.LongSparseLongArray; +import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.Cells.DialogCell; +import org.telegram.ui.Cells.UserCell; +import org.telegram.ui.Components.RecyclerListView; + +import java.util.ArrayList; + +public class UserListPoller { + + private static UserListPoller[] istances = new UserListPoller[UserConfig.MAX_ACCOUNT_COUNT]; + + final int currentAccount; + + private UserListPoller(int currentAccount) { + this.currentAccount = currentAccount; + } + + public static UserListPoller getInstance(int account) { + if (istances[account] == null) { + istances[account] = new UserListPoller(account); + } + return istances[account]; + } + + LongSparseLongArray userPollLastTime = new LongSparseLongArray(); + ArrayList dialogIds = new ArrayList<>(); + ArrayList collectedDialogIds = new ArrayList<>(); + + ArrayList runningRequests = new ArrayList<>(); + + Runnable requestCollectedRunnables = new Runnable() { + @Override + public void run() { + if (!collectedDialogIds.isEmpty()) { + ArrayList dialogsFinal = new ArrayList<>(collectedDialogIds); + collectedDialogIds.clear(); + TLRPC.TL_users_getStoriesMaxIDs request = new TLRPC.TL_users_getStoriesMaxIDs(); + for (int i = 0; i < dialogsFinal.size(); i++) { + request.id.add(MessagesController.getInstance(currentAccount).getInputUser(dialogsFinal.get(i))); + } + ConnectionsManager.getInstance(currentAccount).sendRequest(request, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + if (response != null) { + TLRPC.Vector vector = (TLRPC.Vector) response; + ArrayList usersToUpdate = new ArrayList<>(); + for (int i = 0; i < vector.objects.size(); i++) { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogsFinal.get(i)); + if (user == null) { + continue; + } + user.stories_max_id = (int) vector.objects.get(i); + if (user.stories_max_id != 0) { + user.flags2 |= 32; + } else { + user.flags2 &= ~32; + } + usersToUpdate.add(user); + } + MessagesStorage.getInstance(currentAccount).putUsersAndChats(usersToUpdate, null, true, true); + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.updateInterfaces, 0); + } + })); + } + } + }; + + public void checkList(RecyclerListView recyclerListView) { + long currentTime = System.currentTimeMillis(); + dialogIds.clear(); + for (int i = 0; i < recyclerListView.getChildCount(); i++) { + View child = recyclerListView.getChildAt(i); + long dialogId = 0; + if (child instanceof DialogCell) { + dialogId = ((DialogCell) child).getDialogId(); + } else if (child instanceof UserCell) { + dialogId = ((UserCell) child).getDialogId(); + } + + if (dialogId > 0) { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); + if (user != null && !user.bot && !user.self && !user.contact && user.status != null && !(user.status instanceof TLRPC.TL_userStatusEmpty)) { + long lastPollTime = userPollLastTime.get(dialogId, 0); + if (currentTime - lastPollTime > 60 * 60 * 1000) { + userPollLastTime.put(dialogId, currentTime); + dialogIds.add(dialogId); + } + } + } + } + if (!dialogIds.isEmpty()) { + collectedDialogIds.addAll(dialogIds); + AndroidUtilities.cancelRunOnUIThread(requestCollectedRunnables); + AndroidUtilities.runOnUIThread(requestCollectedRunnables, 300); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/ViewsForSelfStoriesRequester.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/ViewsForSelfStoriesRequester.java new file mode 100644 index 0000000000..d501ed4c72 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/ViewsForSelfStoriesRequester.java @@ -0,0 +1,82 @@ +package org.telegram.ui.Stories; + +import com.google.android.exoplayer2.util.Log; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.UserConfig; +import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.TLRPC; + +//TODO stories +public class ViewsForSelfStoriesRequester { + + StoriesController storiesController; + int currentAccount; + + int currentReqId; + boolean isRunning; + long time; + + final Runnable scheduleRequestRunnuble = () -> requestInternal(); + + public ViewsForSelfStoriesRequester(StoriesController storiesController, int currentAccount) { + this.currentAccount = currentAccount; + this.storiesController = storiesController; + } + + public void start(boolean start) { + if (isRunning != start) { + if (requestInternal()) { + isRunning = start; + } + } else { + isRunning = false; + AndroidUtilities.cancelRunOnUIThread(scheduleRequestRunnuble); + ConnectionsManager.getInstance(currentAccount).cancelRequest(currentReqId, false); + currentReqId = 0; + } + } + + private boolean requestInternal() { + TLRPC.TL_userStories stories = storiesController.getStories(UserConfig.getInstance(currentAccount).getClientUserId()); + if (stories == null || stories.stories.isEmpty() || currentReqId != 0) { + return false; + } + TLRPC.TL_stories_getStoriesViews req = new TLRPC.TL_stories_getStoriesViews(); + for (int i = 0; i < stories.stories.size(); i++) { + req.id.add(stories.stories.get(i).id); + } + + + currentReqId = ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + if (response != null) { + TLRPC.TL_userStories currentStories = storiesController.getStories(UserConfig.getInstance(currentAccount).getClientUserId()); + if (currentStories == null || currentStories.stories.isEmpty()) { + currentReqId = 0; + isRunning = false; + return; + } + TLRPC.TL_stories_storyViews storyViews = (TLRPC.TL_stories_storyViews) response; + MessagesController.getInstance(currentAccount).putUsers(storyViews.users, false); + + for (int i = 0; i < storyViews.views.size(); i++) { + for (int j = 0; j < currentStories.stories.size(); j++) { + if (currentStories.stories.get(j).id == req.id.get(i)) { + currentStories.stories.get(j).views = storyViews.views.get(i); + } + } + } + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.storiesUpdated); + storiesController.storiesStorage.updateStories(currentStories); + } + currentReqId = 0; + if (isRunning) { + AndroidUtilities.cancelRunOnUIThread(scheduleRequestRunnuble); + AndroidUtilities.runOnUIThread(scheduleRequestRunnuble, 10_000); + } + })); + return true; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/AlbumButton.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/AlbumButton.java new file mode 100644 index 0000000000..cacaa7bc6f --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/AlbumButton.java @@ -0,0 +1,137 @@ +package org.telegram.ui.Stories.recorder; + +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.AndroidUtilities.dpf2; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.drawable.Drawable; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.text.TextUtils; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; + +import org.telegram.messenger.ImageLocation; +import org.telegram.messenger.ImageReceiver; +import org.telegram.messenger.MediaController; +import org.telegram.messenger.R; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.CombinedDrawable; + +public class AlbumButton extends View { + private final ImageReceiver imageReceiver = new ImageReceiver(this); + private final CharSequence title, subtitle; + + private final TextPaint namePaintLayout = new TextPaint(Paint.ANTI_ALIAS_FLAG); + private StaticLayout nameLayout; + private float nameLayoutWidth, nameLayoutLeft; + + private final TextPaint countPaintLayout = new TextPaint(Paint.ANTI_ALIAS_FLAG); + private StaticLayout countLayout; + private float countLayoutWidth, countLayoutLeft; + + public AlbumButton(Context context, MediaController.PhotoEntry cover, CharSequence name, int count, Theme.ResourcesProvider resourcesProvider) { + super(context); + + setPadding(dp(16), 0, dp(16), 0); + setBackground(Theme.getSelectorDrawable(false)); + setMinimumWidth(dp(196)); + setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 48)); + + namePaintLayout.setColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuItem, resourcesProvider)); + namePaintLayout.setTextSize(dp(16)); + countPaintLayout.setColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuItem, resourcesProvider)); + countPaintLayout.setAlpha((int) (.4f * 0xFF)); + countPaintLayout.setTextSize(dp(13)); + + title = "" + name; + subtitle = "" + count; + + imageReceiver.setRoundRadius(dp(4)); + final Drawable noPhotosIcon = context.getResources().getDrawable(R.drawable.msg_media_gallery).mutate(); + noPhotosIcon.setColorFilter(new PorterDuffColorFilter(0x4dFFFFFF, PorterDuff.Mode.MULTIPLY)); + final CombinedDrawable noGalleryDrawable = new CombinedDrawable(Theme.createRoundRectDrawable(dp(6), 0xFF2E2E2F), noPhotosIcon); + noGalleryDrawable.setFullsize(false); + noGalleryDrawable.setIconSize(dp(18), dp(18)); + final String filter = imageSize + "_" + imageSize; + if (cover != null && cover.thumbPath != null) { + imageReceiver.setImage(ImageLocation.getForPath(cover.thumbPath), filter, null, null, noGalleryDrawable, null, 0); + } else if (cover != null && cover.path != null) { + if (cover.isVideo) { + imageReceiver.setImage(ImageLocation.getForPath("vthumb://" + cover.imageId + ":" + cover.path), filter, null, null, noGalleryDrawable, null, 0); + } else { + imageReceiver.setImage(ImageLocation.getForPath("thumb://" + cover.imageId + ":" + cover.path), filter, null, null, noGalleryDrawable, null, 0); + } + } else { + imageReceiver.setImageBitmap(noGalleryDrawable); + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + imageReceiver.onAttachedToWindow(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + imageReceiver.onDetachedFromWindow(); + } + final float imageSize = 30; + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + updateLayouts(MeasureSpec.getSize(widthMeasureSpec) - dp(imageSize) - dp(12) - getPaddingLeft() - getPaddingRight()); + if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST) { + float cwidth = getPaddingLeft() + dp(imageSize) + dp(12) + nameLayoutWidth + dp(8) + countLayoutWidth + getPaddingRight(); + setMeasuredDimension((int) Math.min(cwidth, MeasureSpec.getSize(widthMeasureSpec)), dp(48)); + } else if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY) { + setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), dp(48)); + } + } + + private void updateLayouts(int widthAvailable) { + if (nameLayout == null || nameLayout.getWidth() != widthAvailable) { + CharSequence title = TextUtils.ellipsize(this.title, namePaintLayout, widthAvailable, TextUtils.TruncateAt.END); + nameLayout = new StaticLayout(title, namePaintLayout, Math.max(0, widthAvailable), Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false); + nameLayoutLeft = nameLayout.getLineCount() > 0 ? nameLayout.getLineLeft(0) : 0; + nameLayoutWidth = nameLayout.getLineCount() > 0 ? nameLayout.getLineWidth(0) : 0; + + widthAvailable -= (int) (nameLayoutWidth + dp(8)); + CharSequence subtitle = TextUtils.ellipsize(this.subtitle, countPaintLayout, widthAvailable, TextUtils.TruncateAt.END); + countLayout = new StaticLayout(subtitle, countPaintLayout, Math.max(0, widthAvailable), Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false); + countLayoutLeft = countLayout.getLineCount() > 0 ? countLayout.getLineLeft(0) : 0; + countLayoutWidth = countLayout.getLineCount() > 0 ? countLayout.getLineWidth(0) : 0; + } + } + + @Override + protected void dispatchDraw(Canvas canvas) { + float x = getPaddingLeft(); + imageReceiver.setImageCoords(x, (getMeasuredHeight() - dp(imageSize)) / 2f, dp(imageSize), dp(imageSize)); + imageReceiver.draw(canvas); + x += dp(imageSize); + x += dp(12); + if (nameLayout != null) { + canvas.save(); + canvas.translate(x - nameLayoutLeft, (getMeasuredHeight() - nameLayout.getHeight()) / 2f); + nameLayout.draw(canvas); + x += nameLayoutWidth; + x += dp(6); + canvas.restore(); + } + if (countLayout != null) { + canvas.save(); + canvas.translate(x - countLayoutLeft, (getMeasuredHeight() - countLayout.getHeight()) / 2f + dpf2(1.6f)); + countLayout.draw(canvas); + canvas.restore(); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/ButtonWithCounterView.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/ButtonWithCounterView.java new file mode 100644 index 0000000000..60bfb97e3e --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/ButtonWithCounterView.java @@ -0,0 +1,289 @@ +package org.telegram.ui.Stories.recorder; + +import static org.telegram.messenger.AndroidUtilities.dp; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.StateListAnimator; +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.ShapeDrawable; +import android.text.TextUtils; +import android.view.Gravity; +import android.view.View; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.animation.OvershootInterpolator; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.LocaleController; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.AnimatedTextView; +import org.telegram.ui.Components.CircularProgressDrawable; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.ScaleStateListAnimator; + +public class ButtonWithCounterView extends FrameLayout { + + private Theme.ResourcesProvider resourcesProvider; + + private final Paint paint; + private final AnimatedTextView.AnimatedTextDrawable text; + private final AnimatedTextView.AnimatedTextDrawable countText; + private float countAlpha; + private final AnimatedFloat countAlphaAnimated = new AnimatedFloat(350, CubicBezierInterpolator.EASE_OUT_QUINT); + private final View rippleView; + + public ButtonWithCounterView(Context context, Theme.ResourcesProvider resourcesProvider) { + this(context, true, resourcesProvider); + } + + public ButtonWithCounterView(Context context, boolean filled, Theme.ResourcesProvider resourcesProvider) { + super(context); + + this.resourcesProvider = resourcesProvider; + + ScaleStateListAnimator.apply(this, .02f, 1.2f); + + rippleView = new View(context); + rippleView.setBackground(Theme.createRadSelectorDrawable(Theme.getColor(Theme.key_listSelector, resourcesProvider), 8, 8)); + addView(rippleView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + + if (filled) { + setBackground(Theme.createRoundRectDrawable(dp(8), Theme.getColor(Theme.key_featuredStickers_addButton, resourcesProvider))); + } + + paint = new Paint(Paint.ANTI_ALIAS_FLAG); + paint.setColor(Theme.getColor(Theme.key_featuredStickers_buttonText, resourcesProvider)); + + text = new AnimatedTextView.AnimatedTextDrawable(true, true, false); + text.setAnimationProperties(.3f, 0, 250, CubicBezierInterpolator.EASE_OUT_QUINT); + text.setCallback(this); + text.setTextSize(dp(14)); + if (filled) { + text.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + } + text.setTextColor(Theme.getColor(filled ? Theme.key_featuredStickers_buttonText : Theme.key_featuredStickers_addButton, resourcesProvider)); + text.setGravity(Gravity.CENTER_HORIZONTAL); + + countText = new AnimatedTextView.AnimatedTextDrawable(false, false, true); + countText.setAnimationProperties(.3f, 0, 250, CubicBezierInterpolator.EASE_OUT_QUINT); + countText.setCallback(this); + countText.setTextSize(dp(12)); + countText.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + countText.setTextColor(Theme.getColor(Theme.key_featuredStickers_addButton, resourcesProvider)); + countText.setText(""); + countText.setGravity(Gravity.CENTER_HORIZONTAL); + + setWillNotDraw(false); + } + + public void setText(String newText, boolean animated) { + if (animated) { + text.cancelAnimation(); + } + text.setText(newText, animated); + setContentDescription(newText); + invalidate(); + } + + private float loadingT = 0; + private boolean loading; + private ValueAnimator loadingAnimator; + public void setLoading(boolean loading) { + if (this.loading != loading) { + if (loadingAnimator != null) { + loadingAnimator.cancel(); + loadingAnimator = null; + } + + loadingAnimator = ValueAnimator.ofFloat(loadingT, (this.loading = loading) ? 1 : 0); + loadingAnimator.addUpdateListener(anm -> { + loadingT = (float) anm.getAnimatedValue(); + invalidate(); + }); + loadingAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + loadingT = loading ? 1 : 0; + invalidate(); + } + }); + loadingAnimator.setDuration(320); + loadingAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + loadingAnimator.start(); + } + } + + public boolean isLoading() { + return loading; + } + + private float countScale = 1; + private ValueAnimator countAnimator; + private void animateCount() { + if (countAnimator != null) { + countAnimator.cancel(); + countAnimator = null; + } + + countAnimator = ValueAnimator.ofFloat(0, 1); + countAnimator.addUpdateListener(anm -> { + countScale = Math.max(1, (float) anm.getAnimatedValue()); + invalidate(); + }); + countAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + countScale = 1; + invalidate(); + } + }); + countAnimator.setInterpolator(new OvershootInterpolator(2.0f)); + countAnimator.setDuration(200); + countAnimator.start(); + } + + private int lastCount; + private boolean showZero; + + public void setShowZero(boolean showZero) { + this.showZero = showZero; + } + + public void setCount(int count, boolean animated) { + if (animated) { + countText.cancelAnimation(); + } + if (animated && count != lastCount && count > 0 && lastCount > 0) { + animateCount(); + } + lastCount = count; + countAlpha = count != 0 || showZero ? 1f : 0f; + countText.setText("" + count, animated); + invalidate(); + } + + public void setCount(String count, boolean animated) { + if (animated) { + countText.cancelAnimation(); + animateCount(); + } + lastCount = -1; + countAlpha = !TextUtils.isEmpty(count) || showZero ? 1f : 0f; + countText.setText(count, animated); + invalidate(); + } + + private float enabledT = 1; + private boolean enabled = true; + private ValueAnimator enabledAnimator; + + @Override + public void setEnabled(boolean enabled) { + if (this.enabled != enabled) { + if (enabledAnimator != null) { + enabledAnimator.cancel(); + enabledAnimator = null; + } + + enabledAnimator = ValueAnimator.ofFloat(enabledT, (this.enabled = enabled) ? 1 : 0); + enabledAnimator.addUpdateListener(anm -> { + enabledT = (float) anm.getAnimatedValue(); + invalidate(); + }); + enabledAnimator.start(); + } + } + + @Override + protected boolean verifyDrawable(@NonNull Drawable who) { + return text == who || countText == who || super.verifyDrawable(who); + } + + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + return false; + } + + private CircularProgressDrawable loadingDrawable; + + @Override + protected void onDraw(Canvas canvas) { + rippleView.draw(canvas); + + if (loadingT > 0) { + if (loadingDrawable == null) { + loadingDrawable = new CircularProgressDrawable(text.getTextColor()); + } + int y = (int) ((1f - loadingT) * dp(24)); + loadingDrawable.setBounds(0, y, getWidth(), y + getHeight()); + loadingDrawable.setAlpha((int) (0xFF * loadingT)); + loadingDrawable.draw(canvas); + invalidate(); + } + + if (loadingT < 1) { + boolean restore = false; + if (loadingT != 0) { + canvas.save(); + canvas.translate(0, (int) (loadingT * dp(-24))); + canvas.scale(1, 1f - .4f * loadingT); + restore = true; + } + float textWidth = text.getCurrentWidth(); + float countAlpha = countAlphaAnimated.set(this.countAlpha); + + float width = textWidth + (dp(5.66f + 5 + 5) + countText.getCurrentWidth()) * countAlpha; + AndroidUtilities.rectTmp2.set( + (int) ((getMeasuredWidth() - width - getWidth()) / 2f), + (int) ((getMeasuredHeight() - text.getHeight()) / 2f - dp(1)), + (int) ((getMeasuredWidth() - width + getWidth()) / 2f + textWidth), + (int) ((getMeasuredHeight() + text.getHeight()) / 2f - dp(1)) + ); + text.setAlpha((int) (0xFF * (1f - loadingT) * AndroidUtilities.lerp(.5f, 1f, enabledT))); + text.setBounds(AndroidUtilities.rectTmp2); + text.draw(canvas); + + AndroidUtilities.rectTmp2.set( + (int) ((getMeasuredWidth() - width) / 2f + textWidth + dp(5f)), + (int) ((getMeasuredHeight() - dp(18)) / 2f), + (int) ((getMeasuredWidth() - width) / 2f + textWidth + dp(5f + 4 + 4) + Math.max(dp(9), countText.getCurrentWidth())), + (int) ((getMeasuredHeight() + dp(18)) / 2f) + ); + AndroidUtilities.rectTmp.set(AndroidUtilities.rectTmp2); + + if (countScale != 1) { + canvas.save(); + canvas.scale(countScale, countScale, AndroidUtilities.rectTmp2.centerX(), AndroidUtilities.rectTmp2.centerY()); + } + paint.setAlpha((int) (0xFF * (1f - loadingT) * countAlpha * countAlpha * AndroidUtilities.lerp(.5f, 1f, enabledT))); + canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(10), dp(10), paint); + + AndroidUtilities.rectTmp2.offset(-dp(.3f), -dp(.4f)); + countText.setAlpha((int) (0xFF * (1f - loadingT) * countAlpha)); + countText.setBounds(AndroidUtilities.rectTmp2); + countText.draw(canvas); + if (countScale != 1) { + canvas.restore(); + } + if (restore) { + canvas.restore(); + } + } + } + + @Override + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(info); + info.setClassName("android.widget.Button"); +// info.setContentDescription(text.getText() + (lastCount > 0 ? ", " + LocaleController.formatPluralString("Chats", lastCount) : "")); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/CaptionContainerView.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/CaptionContainerView.java new file mode 100644 index 0000000000..1d431a7f5a --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/CaptionContainerView.java @@ -0,0 +1,759 @@ +package org.telegram.ui.Stories.recorder; + +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.ui.ActionBar.Theme.RIPPLE_MASK_CIRCLE_20DP; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.CornerPathEffect; +import android.graphics.LinearGradient; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PixelFormat; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Shader; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.text.Editable; +import android.text.Layout; +import android.text.SpannableString; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.text.TextWatcher; +import android.text.style.DynamicDrawableSpan; +import android.text.style.ImageSpan; +import android.util.Log; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewParent; +import android.view.ViewPropertyAnimator; +import android.view.ViewTreeObserver; +import android.view.WindowInsets; +import android.widget.EditText; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.graphics.ColorUtils; +import androidx.interpolator.view.animation.FastOutSlowInInterpolator; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.Emoji; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.LiteMode; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.R; +import org.telegram.messenger.SendMessagesHelper; +import org.telegram.messenger.SharedConfig; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.Utilities; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.AdjustPanLayoutHelper; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.AnimatedTextView; +import org.telegram.ui.Components.ButtonBounce; +import org.telegram.ui.Components.CombinedDrawable; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.EditTextCaption; +import org.telegram.ui.Components.EditTextEmoji; +import org.telegram.ui.Components.EmojiView; +import org.telegram.ui.Components.ItemOptions; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.MentionsContainerView; +import org.telegram.ui.Components.SizeNotifierFrameLayout; +import org.telegram.ui.LaunchActivity; +import org.telegram.ui.Stories.PeerStoriesView; +import org.telegram.ui.WrappedResourceProvider; + +public class CaptionContainerView extends FrameLayout { + + private final Theme.ResourcesProvider resourcesProvider; + private final FrameLayout containerView; + private int currentAccount; + + private final Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + public final EditTextEmoji editText; + public ImageView applyButton; + public AnimatedTextView limitTextView; + + public ImageView periodButton; + private ItemOptions periodPopup; + private boolean periodVisible = true; + + public static final int[] periods = new int[] { 6 * 3600, 12 * 3600, 86400, 2 * 86400/*, Integer.MAX_VALUE*/ }; + public static final int[] periodDrawables = new int[] { R.drawable.msg_story_6h, R.drawable.msg_story_12h, R.drawable.msg_story_24h, R.drawable.msg_story_48h/*, R.drawable.msg_story_infinite*/ }; + private int periodIndex = 0; + + private final Paint fadePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final LinearGradient fadeGradient = new LinearGradient(0, 0, 0, AndroidUtilities.dp(10), new int[] { 0xffff0000, 0x00000000 }, new float[] { 0.05f, 1 }, Shader.TileMode.CLAMP); + private final Matrix matrix = new Matrix(); + + private final StoryRecorder.WindowView rootView; + + public final KeyboardNotifier keyboardNotifier; + public MentionsContainerView mentionContainer; + + public CaptionContainerView(Context context, int currentAccount, StoryRecorder.WindowView rootView, FrameLayout containerView, Theme.ResourcesProvider resourcesProvider) { + super(context); + + this.resourcesProvider = resourcesProvider; + this.currentAccount = currentAccount; + this.rootView = rootView; + this.containerView = containerView; + + backgroundPaint.setColor(0x80000000); + + keyboardNotifier = new KeyboardNotifier(rootView, this::updateKeyboard); + + editText = new EditTextEmoji(context, rootView, null, EditTextEmoji.STYLE_STORY, true, resourcesProvider) { + @Override + protected void onEmojiKeyboardUpdate() { + keyboardNotifier.fire(); + } + + @Override + protected void onWaitingForKeyboard() { + keyboardNotifier.awaitKeyboard(); + } + + @Override + protected void createEmojiView() { + super.createEmojiView(); + EmojiView emojiView = getEmojiView(); + if (emojiView != null) { + emojiView.shouldLightenBackground = false; + emojiView.fixBottomTabContainerTranslation = false; + emojiView.setShouldDrawBackground(false); + } + } + + @Override + protected void drawEmojiBackground(Canvas canvas, View view) { + AndroidUtilities.rectTmp.set(0, 0, view.getWidth(), view.getHeight()); + drawBackground(canvas, AndroidUtilities.rectTmp, 0, .95f, view); + } + }; + editText.setHint(LocaleController.getString("StoryAddCaption", R.string.StoryAddCaption)); + editText.getEditText().setTranslationX(AndroidUtilities.dp(-40 + 18)); + editText.getEmojiButton().setAlpha(0f); + editText.getEditText().addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + + } + + @Override + public void onTextChanged(CharSequence text, int start, int before, int count) { + if (editText.getEditText().suppressOnTextChanged) { + return; + } + if (mentionContainer == null) { + createMentionsContainer(); + } + if (mentionContainer.getAdapter() != null) { + mentionContainer.getAdapter().setUserOrChar(UserConfig.getInstance(currentAccount).getCurrentUser(), null); + mentionContainer.getAdapter().searchUsernameOrHashtag(text, editText.getEditText().getSelectionStart(), null, false, false); + } + } + + @Override + public void afterTextChanged(Editable s) { + EditTextCaption editText2 = editText.getEditText(); + if (editText2 != null && editText2.getLayout() != null) { + editText2.ignoreClipTop = ( + editText2.getLayout().getHeight() > (dp(180) - editText2.getPaddingTop() - editText2.getPaddingBottom()) + ); + } + int length = 0; + try { + length = editText.getEditText().getText().length(); + } catch (Exception ignore) { + } + String limitText = null; + final int limit = MessagesController.getInstance(currentAccount).storyCaptionLengthLimit; + if (length + 25 > limit) { + limitText = "" + (limit - length); + } + limitTextView.cancelAnimation(); + limitTextView.setText(limitText); + limitTextView.setTextColor(length >= limit ? 0xffEC7777 : 0xffffffff); + } + }); + addView(editText, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM | Gravity.FILL_HORIZONTAL, 12, 12, 12, 12)); + + applyButton = new BounceableImageView(context); + CombinedDrawable drawable = new CombinedDrawable(Theme.createCircleDrawable(AndroidUtilities.dp(16), 0xff66bffa), context.getResources().getDrawable(R.drawable.input_done).mutate(), 0, AndroidUtilities.dp(1)); + drawable.setCustomSize(AndroidUtilities.dp(32), AndroidUtilities.dp(32)); + applyButton.setImageDrawable(drawable); + applyButton.setScaleType(ImageView.ScaleType.CENTER); + applyButton.setAlpha(0f); + applyButton.setVisibility(View.GONE); + applyButton.setOnClickListener(e -> { + closeKeyboard(); + }); + applyButton.setTranslationY(-AndroidUtilities.dp(1)); + addView(applyButton, LayoutHelper.createFrame(44, 44, Gravity.RIGHT | Gravity.BOTTOM)); + + limitTextView = new AnimatedTextView(context, false, true, true); + limitTextView.setGravity(Gravity.CENTER); + limitTextView.setTextSize(dp(15)); + limitTextView.setTextColor(0xffffffff); + limitTextView.setAnimationProperties(.4f, 0, 320, CubicBezierInterpolator.EASE_OUT_QUINT); + limitTextView.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + limitTextView.setTranslationX(dp(2)); + addView(limitTextView, LayoutHelper.createFrame(52, 16, Gravity.RIGHT | Gravity.BOTTOM, 0, 0, 0, 44)); + + fadePaint.setShader(fadeGradient); + fadePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); + + periodButton = new ImageView(context); + periodButton.setBackground(Theme.createSelectorDrawable(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR, RIPPLE_MASK_CIRCLE_20DP, AndroidUtilities.dp(18))); + periodButton.setScaleType(ImageView.ScaleType.CENTER); + periodButton.setOnClickListener(e -> { + if (periodPopup != null && periodPopup.isShown()) { + return; + } + + Utilities.Callback onPeriodSelected = period -> { + setPeriod(period); + if (onPeriodUpdate != null) { + onPeriodUpdate.run(period); + } + }; + + final boolean isPremium = UserConfig.getInstance(currentAccount).isPremium(); + + Utilities.Callback showPremiumHint = isPremium ? null : period -> { + if (onPremiumHintShow != null) { + onPremiumHintShow.run(period); + } + }; + + periodPopup = ItemOptions.makeOptions(rootView, resourcesProvider, periodButton); + for (int i = 0; i < periods.length; ++i) { + final int period = periods[i]; + periodPopup.add( + 0, + period == Integer.MAX_VALUE ? + LocaleController.getString("StoryPeriodKeep") : + LocaleController.formatPluralString("Hours", period / 3600), + periodIndex == i ? Theme.key_dialogTextBlue2 : Theme.key_actionBarDefaultSubmenuItem, + () -> onPeriodSelected.run(period) + ).putPremiumLock( + isPremium || period == 86400 || period == Integer.MAX_VALUE ? null : () -> showPremiumHint.run(period) + ); + } +// periodPopup.addGap(); + // Select ’Keep Always’ to show the story on your page. +// periodPopup.addText(LocaleController.getString("StoryPeriodKeepHint"), 13); + periodPopup.addGap(); + periodPopup.addText(LocaleController.getString("StoryPeriodHint"), 13); + periodPopup.setDimAlpha(0).show(); + }); + setPeriod(86400, false); + addView(periodButton, LayoutHelper.createFrame(44, 44, Gravity.RIGHT | Gravity.BOTTOM, 0, 0, 11, 11)); + +// setOnClickListener(e -> { +// if (!editText.isKeyboardVisible() && !editText.isPopupVisible()) { +// editText.openKeyboard(); +// } +// }); + } + + public void closeKeyboard() { + editText.closeKeyboard(); + editText.hidePopup(true); + } + + public boolean ignoreTouches; + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + if (ignoreTouches) { + return false; + } + return super.dispatchTouchEvent(ev); + } + + private void createMentionsContainer() { + mentionContainer = new MentionsContainerView(getContext(), UserConfig.getInstance(currentAccount).getClientUserId(), 0, LaunchActivity.getLastFragment(), null, resourcesProvider) { + @Override + public void drawRoundRect(Canvas canvas, Rect rectTmp, float radius) { + AndroidUtilities.rectTmp.set(rectTmp); + drawBackground(canvas, AndroidUtilities.rectTmp, radius, .9f, mentionContainer); + } + }; + mentionContainer.getAdapter().setAllowStickers(false); + mentionContainer.getAdapter().setAllowBots(false); + mentionContainer.getAdapter().setAllowChats(false); + mentionContainer.getAdapter().setSearchInDailogs(true); + mentionContainer.withDelegate(new MentionsContainerView.Delegate() { + + @Override + public void replaceText(int start, int len, CharSequence replacingString, boolean allowShort) { + replaceWithText(start, len, replacingString, allowShort); + } + + @Override + public Paint.FontMetricsInt getFontMetrics() { + return editText.getEditText().getPaint().getFontMetricsInt(); + } + }); + containerView.addView(mentionContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.BOTTOM)); + } + + private void replaceWithText(int start, int len, CharSequence text, boolean parseEmoji) { + if (editText == null) { + return; + } + try { + SpannableStringBuilder builder = new SpannableStringBuilder(editText.getText()); + builder.replace(start, start + len, text); + if (parseEmoji) { + Emoji.replaceEmoji(builder, editText.getEditText().getPaint().getFontMetricsInt(), AndroidUtilities.dp(20), false); + } + editText.setText(builder); + editText.setSelection(start + text.length()); + } catch (Exception e) { + FileLog.e(e); + } + } + + public void setPeriod(int period) { + setPeriod(period, true); + } + + public void setPeriodVisible(boolean visible) { + periodVisible = visible; + periodButton.setVisibility(periodVisible && !keyboardShown ? View.VISIBLE : View.GONE); + } + + public void setPeriod(int period, boolean animated) { + int index = 2; + for (int i = 0; i < periods.length; ++i) { + if (periods[i] == period) { + index = i; + break; + } + } + if (periodIndex == index) { + return; + } + Drawable drawable = getResources().getDrawable(periodDrawables[periodIndex = index]).mutate(); + drawable.setColorFilter(new PorterDuffColorFilter(0xffffffff, PorterDuff.Mode.SRC_IN)); + if (animated) { + AndroidUtilities.updateImageViewImageAnimated(periodButton, drawable); + } else { + periodButton.setImageDrawable(drawable); + } + } + + public void hidePeriodPopup() { + if (periodPopup != null) { + periodPopup.dismiss(); + periodPopup = null; + } + } + + public void onResume() { + editText.onResume(); + } + public void onPause() { + editText.onPause(); + } + + private Utilities.Callback onHeightUpdate; + public void setOnHeightUpdate(Utilities.Callback onHeightUpdate) { + this.onHeightUpdate = onHeightUpdate; + } + + private Utilities.Callback onPeriodUpdate; + public void setOnPeriodUpdate(Utilities.Callback listener) { + this.onPeriodUpdate = listener; + } + + private Utilities.Callback onPremiumHintShow; + public void setOnPremiumHint(Utilities.Callback listener) { + this.onPremiumHintShow = listener; + } + + public void heightUpdate() { + if (onHeightUpdate != null) { + int height = editText.getHeight(); + if (keyboardShown) { + height = Math.max(dp(46), height); + } else { + height = Math.min(dp(150), height); + } + onHeightUpdate.run(height); + } + } + + public int getEditTextHeight() { + int height = editText.getHeight(); + if (keyboardShown) { + height = Math.max(dp(46), height); + } else { + height = Math.min(dp(150), height); + } + return height; + } + + private Utilities.Callback onKeyboardOpen; + public void setOnKeyboardOpen(Utilities.Callback onKeyboardOpen) { + this.onKeyboardOpen = onKeyboardOpen; + } + + ObjectAnimator parentKeyboardAnimator; + + private void updateKeyboard(int keyboardHeight) { + rootView.notifyHeightChanged(); + if (editText.isPopupShowing() || editText.isWaitingForKeyboardOpen()) { + keyboardHeight = Math.max(0, AndroidUtilities.navigationBarHeight + editText.getKeyboardHeight()); + } + keyboardHeight = Math.max(0, keyboardHeight - rootView.getBottomPadding(true)); + View parent = (View) getParent(); + parent.clearAnimation(); + + if (parentKeyboardAnimator != null) { + parentKeyboardAnimator.removeAllListeners(); + parentKeyboardAnimator.cancel(); + parentKeyboardAnimator = null; + } + + parentKeyboardAnimator = ObjectAnimator.ofFloat(parent, TRANSLATION_Y, parent.getTranslationY(), -keyboardHeight); + if (keyboardHeight > AndroidUtilities.dp(20)) { + parentKeyboardAnimator.setInterpolator(AdjustPanLayoutHelper.keyboardInterpolator); + parentKeyboardAnimator.setDuration(AdjustPanLayoutHelper.keyboardDuration); + } else { + parentKeyboardAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + parentKeyboardAnimator.setDuration(640); + } + parentKeyboardAnimator.start(); + + toKeyboardShow = keyboardHeight > AndroidUtilities.dp(20); + AndroidUtilities.cancelRunOnUIThread(updateShowKeyboard); + AndroidUtilities.runOnUIThread(updateShowKeyboard); + if (keyboardHeight < AndroidUtilities.dp(20)) { + editText.getEditText().clearFocus(); + editText.hidePopup(true); + } + } + + private boolean toKeyboardShow; + private Runnable updateShowKeyboard = () -> { + updateShowKeyboard(toKeyboardShow, true); + }; + + public float keyboardT; + public boolean keyboardShown; + private ValueAnimator keyboardAnimator; + private void updateShowKeyboard(boolean show, boolean animated) { + if (keyboardShown == show) { + return; + } + keyboardShown = show; + if (keyboardAnimator != null) { + keyboardAnimator.cancel(); + keyboardAnimator = null; + } + if (onKeyboardOpen != null) { + onKeyboardOpen.run(show); + } + if (animated) { + if (show) { + if (mentionContainer != null) { + mentionContainer.setVisibility(View.VISIBLE); + } + applyButton.setVisibility(View.VISIBLE); + } else { + editText.getEditText().scrollBy(0, -editText.getEditText().getScrollY()); + periodButton.setVisibility(periodVisible ? View.VISIBLE : View.GONE); + } + keyboardAnimator = ValueAnimator.ofFloat(keyboardT, show ? 1 : 0); + keyboardAnimator.addUpdateListener(anm -> { + keyboardT = (float) anm.getAnimatedValue(); + editText.getEditText().setTranslationX(AndroidUtilities.lerp(AndroidUtilities.dp(-40 + 18), AndroidUtilities.dp(2), keyboardT)); + editText.setTranslationX(AndroidUtilities.lerp(0, AndroidUtilities.dp(-8), keyboardT)); + editText.setTranslationY(AndroidUtilities.lerp(0, AndroidUtilities.dp(12 - 2), keyboardT)); + limitTextView.setAlpha(AndroidUtilities.lerp(0, 1, keyboardT)); + editText.getEmojiButton().setAlpha(keyboardT); + applyButton.setAlpha((float) Math.pow(keyboardT, 16)); + periodButton.setAlpha(1f - keyboardT); + if (mentionContainer != null) { + mentionContainer.setAlpha((float) Math.pow(keyboardT, 4)); + } + invalidate(); + }); + keyboardAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (!show) { + applyButton.setVisibility(View.GONE); + if (mentionContainer != null) { + mentionContainer.setVisibility(View.GONE); + } + } else { + periodButton.setVisibility(View.GONE); + } + } + }); + if (show) { + keyboardAnimator.setInterpolator(AdjustPanLayoutHelper.keyboardInterpolator); + keyboardAnimator.setDuration(AdjustPanLayoutHelper.keyboardDuration); + } else { + keyboardAnimator.setInterpolator(new FastOutSlowInInterpolator()); + keyboardAnimator.setDuration(420); + } + keyboardAnimator.start(); + } else { + keyboardT = show ? 1 : 0; + editText.getEditText().setTranslationX(AndroidUtilities.lerp(AndroidUtilities.dp(-40 + 18), AndroidUtilities.dp(2), keyboardT)); + editText.setTranslationX(AndroidUtilities.lerp(0, AndroidUtilities.dp(-8), keyboardT)); + editText.setTranslationY(AndroidUtilities.lerp(0, AndroidUtilities.dp(12 - 2), keyboardT)); + limitTextView.setAlpha(AndroidUtilities.lerp(0, 1, keyboardT)); + editText.getEmojiButton().setAlpha(keyboardT); + applyButton.setVisibility(show ? View.VISIBLE : View.GONE); + applyButton.setAlpha(show ? 1f : 0f); + periodButton.setVisibility(!show && periodVisible ? View.VISIBLE : View.GONE); + periodButton.setAlpha(!show ? 1f : 0f); + invalidate(); + } + editText.setSuggestionsEnabled(show); + if (!show) { + editText.getEditText().setSpoilersRevealed(false, true); + } + + if (show && SharedConfig.getDevicePerformanceClass() >= SharedConfig.PERFORMANCE_CLASS_AVERAGE && !LiteMode.isPowerSaverApplied()) { + if (blurBitmap == null) { + blurBitmap = Bitmap.createBitmap((int) (rootView.getWidth() / 12f), (int) (rootView.getHeight() / 12f), Bitmap.Config.ARGB_8888); + } + ignoreDraw = true; + drawBlurBitmap(blurBitmap, 12); + ignoreDraw = false; + blurBitmapShader = new BitmapShader(blurBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); + if (blurBitmapMatrix == null) { + blurBitmapMatrix = new Matrix(); + } else { + blurBitmapMatrix.reset(); + } + blurBitmapShader.setLocalMatrix(blurBitmapMatrix); + if (blurPaint == null) { + blurPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + blurPaint.setColor(0xffffffff); + } + blurPaint.setShader(blurBitmapShader); + } + } + + protected void drawBlurBitmap(Bitmap bitmap, float amount) { + // do draw + Utilities.stackBlurBitmap(bitmap, (int) amount); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (blurBitmap != null) { + blurBitmap.recycle(); + } + blurBitmapShader = null; + blurPaint = null; + } + + private Bitmap blurBitmap; + private BitmapShader blurBitmapShader; + private Matrix blurBitmapMatrix; + private Paint blurPaint; + + public boolean onBackPressed() { + if (editText.isPopupShowing()) { + editText.hidePopup(true); + return true; + } + + if (editText.isKeyboardVisible() && !keyboardNotifier.ignoring) { + closeKeyboard(); + return true; + } + + return false; + } + + private final AnimatedFloat heightAnimated = new AnimatedFloat(this, 0, 300, CubicBezierInterpolator.EASE_OUT_QUINT); + private int lastHeight; + private float lastHeightTranslation; + + private boolean ignoreDraw = false; + + @Override + protected void dispatchDraw(Canvas canvas) { + if (ignoreDraw) { + return; + } + int height = editText.getHeight(); + if (keyboardShown) { + height = Math.max(dp(46), height); + } else { + height = Math.min(dp(150), height); + } + if (height != lastHeight) { + if (onHeightUpdate != null) { + onHeightUpdate.run(height); + } + lastHeight = height; + } + final int heightAnimated = (int) this.heightAnimated.set(height); + updateMentionsLayoutPosition(); + final float heightTranslation = height - heightAnimated; + if (Math.abs(lastHeightTranslation - heightTranslation) >= 1) { + editText.getEditText().setTranslationY(heightTranslation); + } + lastHeightTranslation = heightTranslation; + + final float pad = AndroidUtilities.lerp(AndroidUtilities.dp(12), 0, keyboardT); + AndroidUtilities.rectTmp.set( + pad, + getHeight() - pad - heightAnimated, + getWidth() - pad, + getHeight() - pad + ); + + final float r = AndroidUtilities.lerp(AndroidUtilities.dp(21), 0, keyboardT); + drawBackground(canvas, AndroidUtilities.rectTmp, r, 1f, this); + + canvas.save(); + canvas.clipRect(AndroidUtilities.rectTmp); + super.dispatchDraw(canvas); + canvas.restore(); + } + + private void drawBackground(Canvas canvas, RectF rectF, float r, float alpha, View view) { + if (keyboardT > 0 && blurPaint != null && blurBitmapShader != null && blurBitmap != null && !blurBitmap.isRecycled()) { + blurBitmapMatrix.reset(); + blurBitmapMatrix.postScale((float) rootView.getWidth() / blurBitmap.getWidth(), (float) rootView.getHeight() / blurBitmap.getHeight()); + float x = 0, y = 0; + for (int i = 0; i < 8 && view != null; ++i) { + x += view.getX(); + y += view.getY(); + ViewParent parent = view.getParent(); + view = parent instanceof View ? (View) parent : null; + } + blurBitmapMatrix.postTranslate(-x, -y); + blurBitmapShader.setLocalMatrix(blurBitmapMatrix); + blurPaint.setAlpha((int) (0xFF * keyboardT * alpha)); + canvas.drawRoundRect(rectF, r, r, blurPaint); + } + backgroundPaint.setAlpha((int) (blurPaint == null ? 0x80 : AndroidUtilities.lerp(0x80, 0x99, keyboardT) * alpha)); + canvas.drawRoundRect(rectF, r, r, backgroundPaint); + } + + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + if (child == editText) { + final float pad = AndroidUtilities.lerp(dp(12), 0, keyboardT); + AndroidUtilities.rectTmp.set(pad, getHeight() - pad - heightAnimated.get(), getWidth() - pad, getHeight() - pad); + + float ty = Math.max(0, editText.getHeight() - dp(150 - 7)) * (1f - keyboardT); + canvas.saveLayerAlpha(0, 0, getWidth(), getHeight(), 0xFF, Canvas.ALL_SAVE_FLAG); + + canvas.save(); + canvas.translate(0, ty); + final boolean result = super.drawChild(canvas, child, drawingTime); + canvas.restore(); + + canvas.save(); + matrix.reset(); + matrix.postTranslate(0, AndroidUtilities.rectTmp.top - 1); + fadeGradient.setLocalMatrix(matrix); + canvas.drawRect(AndroidUtilities.rectTmp.left, AndroidUtilities.rectTmp.top, AndroidUtilities.rectTmp.right, AndroidUtilities.rectTmp.top + AndroidUtilities.dp(10), fadePaint); + + matrix.reset(); + matrix.postRotate(180); + matrix.postTranslate(0, AndroidUtilities.rectTmp.bottom); + fadeGradient.setLocalMatrix(matrix); + canvas.drawRect(AndroidUtilities.rectTmp.left, AndroidUtilities.rectTmp.bottom - AndroidUtilities.dp(10), AndroidUtilities.rectTmp.right, AndroidUtilities.rectTmp.bottom, fadePaint); + canvas.restore(); + canvas.restore(); + + return result; + } + return super.drawChild(canvas, child, drawingTime); + } + + public void clearFocus() { + editText.clearFocus(); + } + + public void clear() { + editText.setText(""); + } + + public void setText(CharSequence text) { + editText.setText(text); + } + + public CharSequence getText() { + return editText.getText(); + } + + public void updateMentionsLayoutPosition() { + if (mentionContainer != null) { + float y = ((View) getParent()).getTranslationY() - heightAnimated.get(); + if (mentionContainer.getY() != y) { + mentionContainer.setTranslationY(y); + mentionContainer.invalidate(); + } + } + } + + public static class BounceableImageView extends ImageView { + private final float scale; + public BounceableImageView(Context context) { + this(context, .2f); + } + public BounceableImageView(Context context, float scale) { + super(context); + this.scale = scale; + } + + private final ButtonBounce bounce = new ButtonBounce(this); + + @Override + public void setPressed(boolean pressed) { + super.setPressed(pressed); + bounce.setPressed(pressed); + } + + @Override + public void draw(Canvas canvas) { + canvas.save(); + final float scale = bounce.getScale(this.scale); + canvas.scale(scale, scale, getWidth() / 2f, getHeight() / 2f); + super.draw(canvas); + canvas.restore(); + } + } + + +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/DominantColors.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/DominantColors.java new file mode 100644 index 0000000000..becd4dfcd4 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/DominantColors.java @@ -0,0 +1,99 @@ +package org.telegram.ui.Stories.recorder; + +import android.graphics.Bitmap; +import android.graphics.Color; + +import androidx.core.graphics.ColorUtils; +import androidx.palette.graphics.Palette; +import androidx.palette.graphics.Target; + +import org.telegram.messenger.Utilities; + +public class DominantColors { + + public static void getColors(boolean smart, Bitmap bitmap, boolean themeIsDark, Utilities.Callback callback) { + if (callback == null) { + return; + } + if (bitmap == null) { + callback.run(new int[] {0, 0}); + return; + } + + smart = false; + + if (smart) { + final Integer[] colors = new Integer[2]; + new Palette.Builder(bitmap).setRegion(0, 0, bitmap.getWidth(), (int) (bitmap.getHeight() * .5f)).generate(palette -> { + colors[0] = adapt(getColorFromPalette(palette), themeIsDark); + if (colors[1] != null) { + callback.run(new int[]{colors[0], colors[1]}); + } + }); + new Palette.Builder(bitmap).setRegion(0, (int) (bitmap.getHeight() * (1f - .5f)), bitmap.getWidth(), bitmap.getHeight()).generate(palette -> { + colors[1] = adapt(getColorFromPalette(palette), themeIsDark); + if (colors[0] != null) { + callback.run(new int[]{colors[0], colors[1]}); + } + }); + } else { + callback.run(getColorsSync(smart, bitmap, themeIsDark)); + } + } + + private static float[] tempHsv; + private static int adapt(int color, boolean themeIsDark) { + if (tempHsv == null) { + tempHsv = new float[3]; + } + Color.colorToHSV(color, tempHsv); + tempHsv[2] = Utilities.clamp(tempHsv[2] + (themeIsDark ? -.05f : +.07f), .85f, .15f); + if (tempHsv[1] > 0.1f && tempHsv[1] <= 0.95f) { + if (tempHsv[1] <= 0.5f) { + tempHsv[1] = Utilities.clamp(tempHsv[1] + 0.2f, 1f, 0f); + } else if (tempHsv[1] > 0.8f) { + tempHsv[1] = Utilities.clamp(tempHsv[1] - 0.4f, 1f, 0f); + } + } + return Color.HSVToColor(tempHsv); + } + + public static int[] getColorsSync(boolean smart, Bitmap bitmap, boolean themeIsDark) { + smart = false; + if (smart) { + Palette top = new Palette.Builder(bitmap).setRegion(0, 0, bitmap.getWidth(), (int) (bitmap.getHeight() * .5f)).generate(); + Palette bottom = new Palette.Builder(bitmap).setRegion(0, (int) (bitmap.getHeight() * (1f - .5f)), bitmap.getWidth(), bitmap.getHeight()).generate(); + return new int[]{adapt(getColorFromPalette(top), themeIsDark), adapt(getColorFromPalette(bottom), themeIsDark)}; + } else { + return new int[]{ + adapt(bitmap.getPixel(bitmap.getWidth() / 2, (int) (bitmap.getHeight() * .1f)), themeIsDark), + adapt(bitmap.getPixel(bitmap.getWidth() / 2, (int) (bitmap.getHeight() * .9f)), themeIsDark) + }; + } + } + + private static int getColorFromPalette(Palette palette) { + if (palette == null) + return 0x00000000; + int color; +// color = palette.getColorForTarget(new Target.Builder().setTargetSaturation(.7f).setMaximumSaturation(.9f).build(), 0); +// if (Color.alpha(color) > 200) +// return color; + color = palette.getMutedColor(0); + if (Color.alpha(color) > 200) + return color; + color = palette.getVibrantColor(0); + if (Color.alpha(color) > 200) + return color; + color = palette.getColorForTarget(new Target.Builder().setMaximumLightness(.8f).setMinimumLightness(.1f).setSaturationWeight(.4f).build(), 0); + if (Color.alpha(color) > 200) + return color; + color = palette.getDarkVibrantColor(0); + if (Color.alpha(color) > 200) + return color; + color = palette.getDarkMutedColor(0); + if (Color.alpha(color) > 200) + return color; + return 0; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/DownloadButton.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/DownloadButton.java new file mode 100644 index 0000000000..64d528b2c3 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/DownloadButton.java @@ -0,0 +1,615 @@ +package org.telegram.ui.Stories.recorder; + +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.AndroidUtilities.dpf2; +import static org.telegram.messenger.AndroidUtilities.lerp; + +import android.Manifest; +import android.app.Activity; +import android.content.Context; +import android.content.pm.PackageManager; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Build; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.util.Log; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.BuildVars; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MediaController; +import org.telegram.messenger.MessageObject; +import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.R; +import org.telegram.messenger.Utilities; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.BulletinFactory; +import org.telegram.ui.Components.ButtonBounce; +import org.telegram.ui.Components.CircularProgressDrawable; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.RLottieDrawable; + +import java.io.File; + +public class DownloadButton extends ImageView { + + private int currentAccount; + private FrameLayout container; + private Theme.ResourcesProvider resourcesProvider; + + private boolean downloading; + private boolean downloadingVideo; + private boolean preparing; + private CircularProgressDrawable progressDrawable; + private Utilities.Callback prepare; + + private PreparingVideoToast toast; + + public DownloadButton(Context context, Utilities.Callback prepare, int currentAccount, FrameLayout container, Theme.ResourcesProvider resourcesProvider) { + super(context); + + this.prepare = prepare; + this.currentAccount = currentAccount; + this.container = container; + this.resourcesProvider = resourcesProvider; + + setScaleType(ImageView.ScaleType.CENTER); + setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.MULTIPLY)); + setBackground(Theme.createSelectorDrawable(0x20ffffff)); + setVisibility(View.GONE); + setAlpha(0f); + + setOnClickListener(e -> onClick()); + + progressDrawable = new CircularProgressDrawable(dp(18), dp(2), 0xffffffff); + + updateImage(); + } + + private StoryEntry currentEntry; + private BuildingVideo buildingVideo; + private Uri savedToGalleryUri; + + public void setEntry(StoryEntry entry) { + savedToGalleryUri = null; + currentEntry = entry; + if (buildingVideo != null) { + buildingVideo.stop(true); + buildingVideo = null; + } + if (toast != null) { + toast.hide(); + toast = null; + } + if (entry == null) { + downloading = false; + updateImage(); + return; + } + } + + private void onClick() { + if (Build.VERSION.SDK_INT >= 23 && (Build.VERSION.SDK_INT <= 28 || BuildVars.NO_SCOPED_STORAGE) && getContext().checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + final Activity activity = AndroidUtilities.findActivity(getContext()); + if (activity != null) { + activity.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 113); + } + return; + } + if (downloading || currentEntry == null) { + return; + } + if (savedToGalleryUri != null) { + if (Build.VERSION.SDK_INT >= 30) { + getContext().getContentResolver().delete(savedToGalleryUri, null); + savedToGalleryUri = null; + } else if (Build.VERSION.SDK_INT < 29) { + try { + new File(savedToGalleryUri.toString()).delete(); + } catch (Exception e) { + FileLog.e(e); + } + savedToGalleryUri = null; + } + } + + downloading = true; + if (toast != null) { + toast.hide(); + toast = null; + } + if (buildingVideo != null) { + buildingVideo.stop(true); + buildingVideo = null; + } + if (prepare != null) { + preparing = true; + prepare.run(this::onClickInternal); + } + if (currentEntry.wouldBeVideo()) { + downloadingVideo = true; + toast = new PreparingVideoToast(getContext()); + toast.setOnCancelListener(() -> { + preparing = false; + if (buildingVideo != null) { + buildingVideo.stop(true); + buildingVideo = null; + } + if (toast != null) { + toast.hide(); + } + downloading = false; + updateImage(); + }); + container.addView(toast); + } else { + downloadingVideo = false; + } + updateImage(); + if (prepare == null) { + onClickInternal(); + } + } + + private void onClickInternal() { + if (!preparing) { + return; + } + preparing = false; + if (currentEntry.wouldBeVideo()) { + final File file = AndroidUtilities.generateVideoPath(); + buildingVideo = new BuildingVideo(currentAccount, currentEntry, file, () -> { + if (!downloading || currentEntry == null) { + return; + } + MediaController.saveFile(file.getAbsolutePath(), getContext(), 1, null, null, uri -> { + if (!downloading || currentEntry == null) { + return; + } + toast.setDone(R.raw.ic_save_to_gallery, LocaleController.getString("VideoSavedHint"), 3500); + downloading = false; + updateImage(); + + savedToGalleryUri = uri; + }, false); + }, progress -> { + if (toast != null) { + toast.setProgress(progress); + } + }, () -> { + if (!downloading || currentEntry == null) { + return; + } + toast.setDone(R.raw.error, LocaleController.getString("VideoConvertFail"), 3500); + downloading = false; + updateImage(); + }); + } else { + final File file = AndroidUtilities.generatePicturePath(false, "png"); + if (file == null) { + toast.setDone(R.raw.error, LocaleController.getString("UnknownError"), 3500); + downloading = false; + updateImage(); + return; + } + Utilities.themeQueue.postRunnable(() -> { + currentEntry.buildPhoto(file); + if (!downloading || currentEntry == null) { + return; + } + AndroidUtilities.runOnUIThread(() -> { + MediaController.saveFile(file.getAbsolutePath(), getContext(), 0, null, null, uri -> { + downloading = false; + updateImage(); + if (toast != null) { + toast.hide(); + toast = null; + } + toast = new PreparingVideoToast(getContext()); + toast.setDone(R.raw.ic_save_to_gallery, LocaleController.getString("PhotoSavedHint"), 2500); + container.addView(toast); + + savedToGalleryUri = uri; + }, false); + }); + }); + } + updateImage(); + } + + private boolean wasImageDownloading = true; + private boolean wasVideoDownloading = true; + + private void updateImage() { + if (wasImageDownloading != (downloading && !downloadingVideo)) { + if (wasImageDownloading = (downloading && !downloadingVideo)) { + AndroidUtilities.updateImageViewImageAnimated(this, progressDrawable); + } else { + AndroidUtilities.updateImageViewImageAnimated(this, R.drawable.media_download); + } + } + if (wasVideoDownloading != (downloading && downloadingVideo)) { + clearAnimation(); + animate().alpha((wasVideoDownloading = (downloading && downloadingVideo)) ? .4f : 1f).start(); + } + } + + public void showToast(int resId, CharSequence text) { + if (toast != null) { + toast.hide(); + toast = null; + } + toast = new PreparingVideoToast(getContext()); + toast.setDone(resId, text, 3500); + container.addView(toast); + } + + public void showFailedVideo() { + showToast(R.raw.error, LocaleController.getString("VideoConvertFail")); + } + + private static class BuildingVideo implements NotificationCenter.NotificationCenterDelegate { + + final int currentAccount; + final StoryEntry entry; + final File file; + + private MessageObject messageObject; + private final Runnable onDone; + private final Utilities.Callback onProgress; + private final Runnable onCancel; + + public BuildingVideo(int account, StoryEntry entry, File file, @NonNull Runnable onDone, @Nullable Utilities.Callback onProgress, @NonNull Runnable onCancel) { + this.currentAccount = account; + this.entry = entry; + this.file = file; + this.onDone = onDone; + this.onProgress = onProgress; + this.onCancel = onCancel; + + start(); + } + + public void start() { + if (messageObject != null) { + return; + } + + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.filePreparingStarted); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.fileNewChunkAvailable); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.filePreparingFailed); + + TLRPC.TL_message message = new TLRPC.TL_message(); + message.id = 1; + message.attachPath = file.getAbsolutePath(); + messageObject = new MessageObject(currentAccount, message, (MessageObject) null, false, false); + entry.getVideoEditedInfo(info -> { + if (messageObject == null) { + return; + } + messageObject.videoEditedInfo = info; + MediaController.getInstance().scheduleVideoConvert(messageObject); + }); + } + + public void stop(boolean cancel) { + if (messageObject == null) { + return; + } + + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.filePreparingStarted); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.fileNewChunkAvailable); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.filePreparingFailed); + + if (cancel) { + MediaController.getInstance().cancelVideoConvert(messageObject); + } + messageObject = null; + } + + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.filePreparingStarted) { + if ((MessageObject) args[0] == messageObject) { + + } + } else if (id == NotificationCenter.fileNewChunkAvailable) { + if ((MessageObject) args[0] == messageObject) { + String finalPath = (String) args[1]; + long availableSize = (Long) args[2]; + long finalSize = (Long) args[3]; + float progress = (float) args[4]; + + if (onProgress != null) { + onProgress.run(progress); + } + + if (finalSize > 0) { + onDone.run(); + stop(false); + } + } + } else if (id == NotificationCenter.filePreparingFailed) { + if ((MessageObject) args[0] == messageObject) { + stop(false); + try { + if (file != null) { + file.delete(); + } + } catch (Exception ignore) {} + onCancel.run(); + } + } + } + } + + public static class PreparingVideoToast extends View { + + private final Paint dimPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + private final TextPaint textPaint2 = new TextPaint(Paint.ANTI_ALIAS_FLAG); + private final Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint whitePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint greyPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + private final ButtonBounce cancelButton = new ButtonBounce(this); + + private RLottieDrawable lottieDrawable; + + private final StaticLayout preparingLayout; + private final float preparingLayoutWidth, preparingLayoutLeft; + + private StaticLayout doneLayout; + private float doneLayoutWidth, doneLayoutLeft; + + public PreparingVideoToast(Context context) { + super(context); + + dimPaint.setColor(0x5a000000); + textPaint.setColor(0xffffffff); + textPaint2.setColor(0xffffffff); + backgroundPaint.setColor(0xcc282828); + whitePaint.setColor(0xffffffff); + greyPaint.setColor(0x33ffffff); + + whitePaint.setStyle(Paint.Style.STROKE); + whitePaint.setStrokeCap(Paint.Cap.ROUND); + whitePaint.setStrokeWidth(dp(4)); + greyPaint.setStyle(Paint.Style.STROKE); + greyPaint.setStrokeCap(Paint.Cap.ROUND); + greyPaint.setStrokeWidth(dp(4)); + + textPaint.setTextSize(dp(14)); + textPaint2.setTextSize(dpf2(14.66f)); + + preparingLayout = new StaticLayout(LocaleController.getString("PreparingVideo"), textPaint, AndroidUtilities.displaySize.x, Layout.Alignment.ALIGN_NORMAL, 1f, 0, false); + preparingLayoutWidth = preparingLayout.getLineCount() > 0 ? preparingLayout.getLineWidth(0) : 0; + preparingLayoutLeft = preparingLayout.getLineCount() > 0 ? preparingLayout.getLineLeft(0) : 0; + + show(); + } + + @Override + protected boolean verifyDrawable(@NonNull Drawable who) { + return who == lottieDrawable || super.verifyDrawable(who); + } + + private boolean shown = false; + private final AnimatedFloat showT = new AnimatedFloat(0, this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + + private boolean preparing = true; + private float progress = 0; + private final AnimatedFloat t = new AnimatedFloat(this); + private final AnimatedFloat progressT = new AnimatedFloat(this); + + private final RectF prepareRect = new RectF(); + private final RectF toastRect = new RectF(); + private final RectF currentRect = new RectF(); + private final RectF hiddenRect = new RectF(); + + private boolean deleted; + + @Override + protected void onDraw(Canvas canvas) { + final int restore = canvas.getSaveCount(); + final float showT = this.showT.set(shown ? 1 : 0); + final float t = this.t.set(preparing ? 0 : 1); + + dimPaint.setAlpha((int) (0x5a * (1f - t) * showT)); + canvas.drawRect(0, 0, getWidth(), getHeight(), dimPaint); + + final float prepareWidth = Math.max(preparingLayoutWidth, dp(54)) + dp(21 + 21); + final float prepareHeight = dp(21 + 54 + 18 + 18) + preparingLayout.getHeight(); + prepareRect.set( + (getWidth() - prepareWidth) / 2f, + (getHeight() - prepareHeight) / 2f, + (getWidth() + prepareWidth) / 2f, + (getHeight() + prepareHeight) / 2f + ); + + final float toastWidth = dp(9 + 36 + 7 + 22) + doneLayoutWidth; + final float toastHeight = dp(6 + 36 + 6); + toastRect.set( + (getWidth() - toastWidth) / 2f, + (getHeight() - toastHeight) / 2f, + (getWidth() + toastWidth) / 2f, + (getHeight() + toastHeight) / 2f + ); + + AndroidUtilities.lerp(prepareRect, toastRect, t, currentRect); + if (showT < 1 && preparing) { + hiddenRect.set(getWidth() / 2f, getHeight() / 2f, getWidth() / 2f, getHeight() / 2f); + AndroidUtilities.lerp(hiddenRect, currentRect, showT, currentRect); + } + if (showT < 1 && !preparing) { + canvas.scale(lerp(.8f, 1f, showT), lerp(.8f, 1f, showT), currentRect.centerX(), currentRect.centerY()); + } + backgroundPaint.setAlpha((int) (0xcc * showT)); + canvas.drawRoundRect(currentRect, dp(10), dp(10), backgroundPaint); + canvas.save(); + canvas.clipRect(currentRect); + if (t < 1) { + drawPreparing(canvas, showT * (1f - t)); + } + if (t > 0) { + drawToast(canvas, showT * t); + } + canvas.restoreToCount(restore); + + if (showT <= 0 && !shown && !deleted) { + deleted = true; + post(() -> { + if (getParent() instanceof ViewGroup) { + ((ViewGroup) getParent()).removeView(this); + } + }); + } + } + + private void drawPreparing(Canvas canvas, float alpha) { + final float progress = this.progressT.set(this.progress); + + final float cx = prepareRect.centerX(); + final float cy = prepareRect.top + dp(21 + 27); + final float r = dp(25); + + greyPaint.setAlpha((int) (0x33 * alpha)); + canvas.drawCircle(cx, cy, r, greyPaint); + AndroidUtilities.rectTmp.set(cx - r, cy - r, cx + r, cy + r); + whitePaint.setAlpha((int) (0xFF * alpha)); + whitePaint.setStrokeWidth(dp(4)); + canvas.drawArc(AndroidUtilities.rectTmp, -90, progress * 360, false, whitePaint); + + final float cancelButtonScale = cancelButton.getScale(.15f); + canvas.save(); + canvas.scale(cancelButtonScale, cancelButtonScale, cx, cy); + whitePaint.setStrokeWidth(dp(3.4f)); + canvas.drawLine(cx - dp(7), cy - dp(7), cx + dp(7), cy + dp(7), whitePaint); + canvas.drawLine(cx - dp(7), cy + dp(7), cx + dp(7), cy - dp(7), whitePaint); + canvas.restore(); + + canvas.save(); + canvas.translate( + prepareRect.left + dp(21) - preparingLayoutLeft, + prepareRect.bottom - dp(18) - preparingLayout.getHeight() + ); + textPaint.setAlpha((int) (0xFF * alpha)); + preparingLayout.draw(canvas); + canvas.restore(); + } + + private void drawToast(Canvas canvas, float alpha) { + if (lottieDrawable != null) { + lottieDrawable.setAlpha((int) (0xFF * alpha)); + lottieDrawable.setBounds( + (int) (toastRect.left + dp(9)), + (int) (toastRect.top + dp(6)), + (int) (toastRect.left + dp(9 + 36)), + (int) (toastRect.top + dp(6 + 36)) + ); + lottieDrawable.draw(canvas); + } + + if (doneLayout != null) { + canvas.save(); + canvas.translate(toastRect.left + dp(9 + 36 + 7) - doneLayoutLeft, toastRect.centerY() - doneLayout.getHeight() / 2f); + textPaint2.setAlpha((int) (0xFF * alpha)); + doneLayout.draw(canvas); + canvas.restore(); + } + } + + public void setProgress(float progress) { + this.progress = progress; + invalidate(); + } + + public void setDone(int resId, CharSequence text, int delay) { + if (lottieDrawable != null) { + lottieDrawable.setCallback(null); + lottieDrawable.recycle(true); + } + + lottieDrawable = new RLottieDrawable(resId, "" + resId, dp(36), dp(36)); + lottieDrawable.setCallback(this); + lottieDrawable.start(); + + doneLayout = new StaticLayout(text, textPaint2, AndroidUtilities.displaySize.x, Layout.Alignment.ALIGN_NORMAL, 1f, 0, false); + doneLayoutWidth = doneLayout.getLineCount() > 0 ? doneLayout.getLineWidth(0) : 0; + doneLayoutLeft = doneLayout.getLineCount() > 0 ? doneLayout.getLineLeft(0) : 0; + + preparing = false; + invalidate(); + if (hideRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(hideRunnable); + } + AndroidUtilities.runOnUIThread(hideRunnable = this::hide, delay); + } + + private Runnable hideRunnable; + public void hide() { + if (hideRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(hideRunnable); + hideRunnable = null; + } + this.shown = false; + invalidate(); + } + + public void show() { + this.shown = true; + invalidate(); + } + + private Runnable onCancel; + public void setOnCancelListener(Runnable onCancel) { + this.onCancel = onCancel; + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + boolean hit = currentRect.contains(event.getX(), event.getY()); + if (event.getAction() == MotionEvent.ACTION_DOWN && (preparing || hit)) { + cancelButton.setPressed(hit); + return true; + } else if (event.getAction() == MotionEvent.ACTION_UP) { + if (cancelButton.isPressed()) { + if (hit) { + if (preparing) { + if (onCancel != null) { + onCancel.run(); + } + } else { + hide(); + } + } + cancelButton.setPressed(false); + return true; + } + } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { + cancelButton.setPressed(false); + return true; + } + return super.onTouchEvent(event); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/DraftSavedHint.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/DraftSavedHint.java new file mode 100644 index 0000000000..55b9569236 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/DraftSavedHint.java @@ -0,0 +1,133 @@ +package org.telegram.ui.Stories.recorder; + +import static org.telegram.messenger.AndroidUtilities.dp; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.CornerPathEffect; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.drawable.Drawable; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.text.TextUtils; +import android.view.Gravity; +import android.view.View; + +import androidx.annotation.NonNull; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.R; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.AnimatedTextView; +import org.telegram.ui.Components.CubicBezierInterpolator; + +public class DraftSavedHint extends View { + + private final Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + private final TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + private final StaticLayout layout; + private final float layoutWidth, layoutLeft; + + private final Path path = new Path(); + + public DraftSavedHint(Context context) { + super(context); + + backgroundPaint.setColor(0xcc282828); + backgroundPaint.setPathEffect(new CornerPathEffect(dp(6))); + + textPaint.setTextSize(dp(14)); + textPaint.setColor(0xffffffff); + + CharSequence text = LocaleController.getString("StoryDraftSaved"); + text = TextUtils.ellipsize(text, textPaint, AndroidUtilities.displaySize.x, TextUtils.TruncateAt.END); + layout = new StaticLayout(text, textPaint, AndroidUtilities.displaySize.x, Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false); + layoutWidth = layout.getLineCount() > 0 ? layout.getLineWidth(0) : 0; + layoutLeft = layout.getLineCount() > 0 ? layout.getLineLeft(0) : 0; + + showT.set(0, true); + } + + private Runnable hideRunnable; + + public void hide(boolean animated) { + show(false, animated); + } + + public void show(boolean show, boolean animated) { + if (!show && hideRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(hideRunnable); + hideRunnable = null; + } + shown = show; + if (!animated) { + showT.set(show, true); + } + invalidate(); + } + + public void show() { + showT.set(0, true); + show(true, true); + if (hideRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(hideRunnable); + } + AndroidUtilities.runOnUIThread(hideRunnable = () -> hide(true), 3500); + } + + private boolean shown; + private final AnimatedFloat showT = new AnimatedFloat(this); + + @Override + protected void dispatchDraw(Canvas canvas) { + float showT = this.showT.set(shown); + + if (showT <= 0) { + return; + } + + canvas.save(); + canvas.translate(0, (shown ? CubicBezierInterpolator.EASE_OUT_BACK.getInterpolation(showT) : 1) * dp(12)); + + showT = CubicBezierInterpolator.EASE_OUT_QUINT.getInterpolation(showT); + + final float W = getMeasuredWidth(), H = getMeasuredHeight(); + final float width = dp(11 + 11) + layoutWidth; + + final float dist = Math.min(dp(135), W * .35f); + final float cx = W / 2f - dist; + + final float x = Math.max(dp(8), cx - width / 2f); + + path.rewind(); + path.moveTo(x, 0); + path.lineTo(x + width, 0); + path.lineTo(x + width, H - dp(6 + 12)); + path.lineTo(cx + dp(7), H - dp(6 + 12)); + path.lineTo(cx + dp(1), H - dp(12)); + path.lineTo(cx - dp(1), H - dp(12)); + path.lineTo(cx - dp(7), H - dp(6 + 12)); + path.lineTo(x, H - dp(6 + 12)); + path.close(); + + backgroundPaint.setAlpha((int) (0xcc * showT)); + canvas.drawPath(path, backgroundPaint); + + canvas.save(); + canvas.translate(x + dp(11) - layoutLeft, (H - dp(6 + 12) - layout.getHeight()) / 2f); + textPaint.setAlpha((int) (0xFF * showT)); + layout.draw(canvas); + canvas.restore(); + + canvas.restore(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), dp(38 + 12)); + } +} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/DraftsController.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/DraftsController.java new file mode 100644 index 0000000000..90deeb0fcc --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/DraftsController.java @@ -0,0 +1,624 @@ +package org.telegram.ui.Stories.recorder; + +import android.net.wifi.WifiManager; +import android.text.SpannableString; +import android.text.TextUtils; +import android.util.Log; + +import androidx.annotation.NonNull; + +import org.telegram.SQLite.SQLiteCursor; +import org.telegram.SQLite.SQLiteDatabase; +import org.telegram.SQLite.SQLitePreparedStatement; +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.Emoji; +import org.telegram.messenger.FileLoader; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.ImageLoader; +import org.telegram.messenger.MediaController; +import org.telegram.messenger.MediaDataController; +import org.telegram.messenger.MessageObject; +import org.telegram.messenger.MessagesStorage; +import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.Utilities; +import org.telegram.messenger.VideoEditedInfo; +import org.telegram.tgnet.AbstractSerializedData; +import org.telegram.tgnet.NativeByteBuffer; +import org.telegram.tgnet.TLObject; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.Theme; + +import java.io.File; +import java.io.IOException; +import java.nio.file.CopyOption; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +public class DraftsController { + + public static final int EXPIRATION_PERIOD = 1000 * 60 * 60 * 24 * 7; // 7 days + + public final int currentAccount; + + public DraftsController(int currentAccount) { + this.currentAccount = currentAccount; + } + + private boolean loaded, loading; + public final ArrayList drafts = new ArrayList<>(); + + public void load() { + if (loaded || loading) { + return; + } + + loading = true; + final MessagesStorage storage = MessagesStorage.getInstance(currentAccount); + storage.getStorageQueue().postRunnable(() -> { + SQLiteCursor cursor = null; + final ArrayList savedDrafts = new ArrayList<>(); + try { + SQLiteDatabase database = storage.getDatabase(); + if (database == null) { + return; + } + cursor = database.queryFinalized("SELECT id, data FROM story_drafts ORDER BY date DESC"); + while (cursor.next()) { + long id = cursor.longValue(0); + NativeByteBuffer buffer = cursor.byteBufferValue(1); + if (buffer != null) { + try { + StoryDraft draft = new StoryDraft(buffer, true); + draft.id = id; + savedDrafts.add(draft); + } catch (Exception e) { + FileLog.e(e); + } + buffer.reuse(); + } + } + } catch (Exception e) { + FileLog.e(e); + } finally { + if (cursor != null) { + cursor.dispose(); + } + } + + AndroidUtilities.runOnUIThread(() -> { + final long now = System.currentTimeMillis(); + ArrayList ids = new ArrayList<>(); + ArrayList deleteEntries = new ArrayList<>(); + for (int i = 0; i < savedDrafts.size(); ++i) { + StoryEntry entry = savedDrafts.get(i).toEntry(); + if (entry == null || entry.file == null || !entry.file.exists() || now - entry.draftDate > EXPIRATION_PERIOD) { + deleteEntries.add(entry); + } else { + drafts.add(entry); + ids.add(entry.draftId); + } + } + delete(deleteEntries); + + loading = false; + loaded = true; + + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.storiesDraftsUpdated); + }); + }); + } + + public void edit(StoryEntry entry) { + if (entry == null) { + return; + } + prepare(entry); + drafts.remove(entry); + drafts.add(0, entry); + final StoryDraft draft = new StoryDraft(entry); + final MessagesStorage storage = MessagesStorage.getInstance(currentAccount); + storage.getStorageQueue().postRunnable(() -> { + SQLitePreparedStatement state = null; + try { + SQLiteDatabase database = storage.getDatabase(); + if (database == null) { + return; + } + + state = database.executeFast("REPLACE INTO story_drafts VALUES (?, ?, ?)"); + state.requery(); + NativeByteBuffer data = new NativeByteBuffer(draft.getObjectSize()); + draft.toStream(data); + state.bindLong(1, draft.id); + state.bindLong(2, draft.date); + state.bindByteBuffer(3, data); + state.step(); + data.reuse(); + state.dispose(); + + } catch (Exception e) { + FileLog.e(e); + } finally { + if (state != null) { + state.dispose(); + } + } + }); + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.storiesDraftsUpdated); + } + + private void prepare(StoryEntry entry) { + if (entry == null) { + return; + } + + entry.draftDate = System.currentTimeMillis(); + entry.isDraft = true; + + if (entry.fileDeletable) { + entry.file = prepareFile(entry.file); + } else if (entry.file != null) { + File newFile = StoryEntry.makeCacheFile(currentAccount, entry.isVideo); + try { + AndroidUtilities.copyFile(entry.file, newFile); + entry.file = prepareFile(newFile); + entry.fileDeletable = true; + } catch (IOException e) { + FileLog.e(e); + } + } + entry.filterFile = prepareFile(entry.filterFile); + entry.paintFile = prepareFile(entry.paintFile); + entry.draftThumbFile = prepareFile(entry.draftThumbFile); + } + + private File draftsFolder; + private File prepareFile(File file) { + if (file == null) { + return null; + } + if (draftsFolder == null) { + draftsFolder = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), "drafts"); + if (!draftsFolder.exists()) { + draftsFolder.mkdir(); + } + } + if (!file.getAbsolutePath().startsWith(draftsFolder.getAbsolutePath())) { + File newFile = new File(draftsFolder, file.getName()); + if (file.renameTo(newFile)) { + return newFile; + } + } + return file; + } + + public void append(StoryEntry entry) { + if (entry == null) { + return; + } + prepare(entry); + final long id = Utilities.random.nextLong(); + entry.draftId = id; + final StoryDraft draft = new StoryDraft(entry); + drafts.remove(entry); + drafts.add(0, entry); + final MessagesStorage storage = MessagesStorage.getInstance(currentAccount); + storage.getStorageQueue().postRunnable(() -> { + SQLitePreparedStatement state = null; + try { + SQLiteDatabase database = storage.getDatabase(); + if (database == null) { + return; + } + + state = database.executeFast("INSERT INTO story_drafts VALUES (?, ?, ?)"); + state.requery(); + NativeByteBuffer data = new NativeByteBuffer(draft.getObjectSize()); + draft.toStream(data); + state.bindLong(1, id); + state.bindLong(2, draft.date); + state.bindByteBuffer(3, data); + state.step(); + data.reuse(); + state.dispose(); + + } catch (Exception e) { + FileLog.e(e); + } finally { + if (state != null) { + state.dispose(); + } + } + }); + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.storiesDraftsUpdated); + } + + public void delete(StoryEntry entry) { + ArrayList list = new ArrayList<>(1); + list.add(entry); + delete(list); + } + + public void deleteExpired() { + final long now = System.currentTimeMillis(); + ArrayList list = new ArrayList<>(); + for (int i = 0; i < drafts.size(); ++i) { + StoryEntry entry = drafts.get(i); + if (entry != null && now - entry.draftDate > EXPIRATION_PERIOD) { + list.add(entry); + } + } + delete(list); + } + + public void delete(ArrayList entries) { + if (entries == null) { + return; + } + ArrayList ids = new ArrayList<>(); + for (int i = 0; i < entries.size(); ++i) { + StoryEntry entry = entries.get(i); + if (entry != null) { + ids.add(entry.draftId); + entry.destroy(true); + } + } + drafts.removeAll(entries); + final MessagesStorage storage = MessagesStorage.getInstance(currentAccount); + storage.getStorageQueue().postRunnable(() -> { + try { + SQLiteDatabase database = storage.getDatabase(); + if (database == null) { + return; + } + database.executeFast("DELETE FROM story_drafts WHERE id IN (" + TextUtils.join(", ", ids) + ")").stepThis().dispose(); + } catch (Exception e) { + FileLog.e(e); + } + }); + + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.storiesDraftsUpdated); + } + + public void cleanup() { + delete(drafts); + loaded = false; + } + + public static class StoryDraft { + + public long id; + public long date; + public String thumb; + + public boolean isVideo; + public String file; + public boolean fileDeletable; + + public boolean muted; + public long left, right; + + public int orientation, invert; + public int width, height; + public int resultWidth, resultHeight; + public long duration; + + public final float[] matrixValues = new float[9]; + public int gradientTopColor, gradientBottomColor; + + public String caption; + public ArrayList captionEntities; + public final ArrayList privacyRules = new ArrayList<>(); + + public String paintFilePath; + public long averageDuration; + public ArrayList mediaEntities; + public List stickers; + + private String filterFilePath; + private MediaController.SavedFilterState filterState; + + private int period; + + private final ArrayList parts = new ArrayList<>(); + + public StoryDraft(@NonNull StoryEntry entry) { + this.id = entry.draftId; + this.date = entry.draftDate; + this.thumb = entry.draftThumbFile == null ? "" : entry.draftThumbFile.toString(); + this.isVideo = entry.isVideo; + this.file = entry.file == null ? "" : entry.file.toString(); + this.fileDeletable = entry.fileDeletable; + this.muted = entry.muted; + this.left = (long) (entry.left * entry.duration); + this.right = (long) (entry.right * entry.duration); + this.orientation = entry.orientation; + this.invert = entry.invert; + this.width = entry.width; + this.height = entry.height; + this.resultWidth = entry.resultWidth; + this.resultHeight = entry.resultHeight; + this.duration = entry.duration; + entry.matrix.getValues(this.matrixValues); + this.gradientTopColor = entry.gradientTopColor; + this.gradientBottomColor = entry.gradientBottomColor; + CharSequence caption = entry.caption; + this.captionEntities = MediaDataController.getInstance(entry.currentAccount).getEntities(new CharSequence[]{caption}, true); + this.caption = caption == null ? "" : caption.toString(); + this.privacyRules.addAll(entry.privacyRules); + this.paintFilePath = entry.paintFile == null ? "" : entry.paintFile.toString(); + this.averageDuration = entry.averageDuration; + this.mediaEntities = entry.mediaEntities; + this.stickers = entry.stickers; + this.filterFilePath = entry.filterFile == null ? "" : entry.filterFile.toString(); + this.filterState = entry.filterState; + this.period = entry.period; + this.parts.clear(); + this.parts.addAll(entry.parts); + } + + public StoryEntry toEntry() { + StoryEntry entry = new StoryEntry(); + entry.draftId = id; + entry.isDraft = true; + entry.draftDate = date; + if (thumb != null) { + entry.draftThumbFile = new File(thumb); + } + entry.isVideo = isVideo; + if (file != null) { + entry.file = new File(file); + } + entry.fileDeletable = fileDeletable; + entry.muted = muted; + entry.duration = duration; + if (duration > 0) { + entry.left = (float) left / duration; + entry.right = (float) right / duration; + } else { + entry.left = 0; + entry.right = 1; + } + entry.orientation = orientation; + entry.invert = invert; + entry.width = width; + entry.height = height; + entry.resultWidth = resultWidth; + entry.resultHeight = resultHeight; + entry.matrix.setValues(matrixValues); + entry.gradientTopColor = gradientTopColor; + entry.gradientBottomColor = gradientBottomColor; + if (caption != null) { + CharSequence caption = new SpannableString(this.caption); + caption = Emoji.replaceEmoji(caption, Theme.chat_msgTextPaint.getFontMetricsInt(), true); + MessageObject.addEntitiesToText(caption, captionEntities, true, false, true, false); + entry.caption = caption; + } else { + entry.caption = ""; + } + entry.privacyRules.clear(); + entry.privacyRules.addAll(privacyRules); + if (paintFilePath != null) { + entry.paintFile = new File(paintFilePath); + } + entry.averageDuration = averageDuration; + entry.mediaEntities = mediaEntities; + entry.stickers = stickers; + if (filterFilePath != null) { + entry.filterFile = new File(filterFilePath); + } + entry.filterState = filterState; + entry.period = period; + entry.parts.clear(); + entry.parts.addAll(parts); + entry.partsMaxId = 0; + for (int i = 0; i < parts.size(); ++i) { + entry.partsMaxId = Math.max(entry.partsMaxId, parts.get(i).id); + } + return entry; + } + + public void toStream(AbstractSerializedData stream) { + stream.writeInt32(0xB16B00B5); + stream.writeInt64(date); + stream.writeString(thumb); + stream.writeBool(isVideo); + stream.writeString(file); + stream.writeBool(fileDeletable); + stream.writeBool(muted); + stream.writeInt64(left); + stream.writeInt64(right); + stream.writeInt32(orientation); + stream.writeInt32(invert); + stream.writeInt32(width); + stream.writeInt32(height); + stream.writeInt32(resultWidth); + stream.writeInt32(resultHeight); + stream.writeInt64(duration); + for (int i = 0; i < matrixValues.length; ++i) { + stream.writeFloat(matrixValues[i]); + } + stream.writeInt32(gradientTopColor); + stream.writeInt32(gradientBottomColor); + stream.writeString(caption); + stream.writeInt32(0x1cb5c415); + stream.writeInt32(captionEntities == null ? 0 : captionEntities.size()); + if (captionEntities != null) { + for (int i = 0; i < captionEntities.size(); ++i) { + captionEntities.get(i).serializeToStream(stream); + } + } + stream.writeInt32(0x1cb5c415); + stream.writeInt32(privacyRules == null ? 0 : privacyRules.size()); + if (privacyRules != null) { + for (int i = 0; i < privacyRules.size(); ++i) { + privacyRules.get(i).serializeToStream(stream); + } + } + stream.writeBool(false); + stream.writeString(paintFilePath); + stream.writeInt64(averageDuration); + stream.writeInt32(0x1cb5c415); + stream.writeInt32(mediaEntities == null ? 0 : mediaEntities.size()); + if (mediaEntities != null) { + for (int i = 0; i < mediaEntities.size(); ++i) { + mediaEntities.get(i).serializeTo(stream, true); + } + } + stream.writeInt32(0x1cb5c415); + stream.writeInt32(stickers == null ? 0 : stickers.size()); + if (stickers != null) { + for (int i = 0; i < stickers.size(); ++i) { + stickers.get(i).serializeToStream(stream); + } + } + stream.writeString(filterFilePath == null ? "" : filterFilePath); + if (filterState == null) { + stream.writeInt32(0x56730bcc); + } else { + stream.writeInt32(0xB16B00B6); + filterState.serializeToStream(stream); + } + stream.writeInt32(period); + stream.writeInt32(0x1cb5c415); + stream.writeInt32(parts.size()); + for (int i = 0; i < parts.size(); ++i) { + parts.get(i).serializeToStream(stream); + } + } + + public int getObjectSize() { + NativeByteBuffer byteBuffer = new NativeByteBuffer(true); + toStream(byteBuffer); + return byteBuffer.length(); + } + + public StoryDraft(@NonNull AbstractSerializedData stream, boolean exception) { + if (stream.readInt32(exception) != 0xB16B00B5) { + if (exception) { + throw new RuntimeException("StoryDraft parse error"); + } else { + return; + } + } + date = stream.readInt64(exception); + thumb = stream.readString(exception); + if (thumb != null && thumb.length() == 0) { + thumb = null; + } + isVideo = stream.readBool(exception); + file = stream.readString(exception); + if (file != null && file.length() == 0) { + file = null; + } + fileDeletable = stream.readBool(exception); + muted = stream.readBool(exception); + left = stream.readInt64(exception); + right = stream.readInt64(exception); + orientation = stream.readInt32(exception); + invert = stream.readInt32(exception); + width = stream.readInt32(exception); + height = stream.readInt32(exception); + resultWidth = stream.readInt32(exception); + resultHeight = stream.readInt32(exception); + duration = stream.readInt64(exception); + for (int i = 0; i < matrixValues.length; ++i) { + matrixValues[i] = stream.readFloat(exception); + } + gradientTopColor = stream.readInt32(exception); + gradientBottomColor = stream.readInt32(exception); + caption = stream.readString(exception); + if (caption != null && caption.length() == 0) { + caption = null; + } + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) + throw new RuntimeException("Vector magic in StoryDraft parse error (1)"); + return; + } + int count = stream.readInt32(exception); + for (int i = 0; i < count; ++i) { + if (captionEntities == null) { + captionEntities = new ArrayList<>(); + } + captionEntities.add(TLRPC.MessageEntity.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) + throw new RuntimeException("Vector magic in StoryDraft parse error (2)"); + return; + } + count = stream.readInt32(exception); + privacyRules.clear(); + for (int i = 0; i < count; ++i) { + privacyRules.add(TLRPC.InputPrivacyRule.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + stream.readBool(exception); + paintFilePath = stream.readString(exception); + if (paintFilePath != null && paintFilePath.length() == 0) { + paintFilePath = null; + } + averageDuration = stream.readInt64(exception); + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) + throw new RuntimeException("Vector magic in StoryDraft parse error (3)"); + return; + } + count = stream.readInt32(exception); + for (int i = 0; i < count; ++i) { + if (mediaEntities == null) { + mediaEntities = new ArrayList<>(); + } + mediaEntities.add(new VideoEditedInfo.MediaEntity(stream, true)); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) + throw new RuntimeException("Vector magic in StoryDraft parse error (4)"); + return; + } + count = stream.readInt32(exception); + for (int i = 0; i < count; ++i) { + if (stickers == null) { + stickers = new ArrayList<>(); + } + stickers.add(TLRPC.InputDocument.TLdeserialize(stream, stream.readInt32(exception), exception)); + } + filterFilePath = stream.readString(exception); + if (filterFilePath != null && filterFilePath.length() == 0) { + filterFilePath = null; + } + magic = stream.readInt32(exception); + if (magic == 0x56730bcc) { + filterState = null; + } else if (magic == 0xB16B00B6) { + filterState = new MediaController.SavedFilterState(); + filterState.readParams(stream, exception); + } + if (stream.remaining() >= 4) { + period = stream.readInt32(exception); + } + if (stream.remaining() > 0) { + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) + throw new RuntimeException("Vector magic in StoryDraft parse error (5)"); + return; + } + count = stream.readInt32(exception); + parts.clear(); + for (int i = 0; i < count; ++i) { + StoryEntry.Part part = new StoryEntry.Part(); + part.readParams(stream, exception); + parts.add(part); + } + } + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/DualCameraView.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/DualCameraView.java new file mode 100644 index 0000000000..ed79e7031e --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/DualCameraView.java @@ -0,0 +1,627 @@ +package org.telegram.ui.Stories.recorder; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.graphics.Matrix; +import android.graphics.PointF; +import android.graphics.SurfaceTexture; +import android.hardware.Camera; +import android.hardware.camera2.CameraAccessException; +import android.os.Build; +import android.text.TextUtils; +import android.util.Log; +import android.view.HapticFeedbackConstants; +import android.view.MotionEvent; +import android.view.ViewConfiguration; + +import com.google.common.primitives.Floats; +import com.google.zxing.common.detector.MathUtils; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.R; +import org.telegram.messenger.SharedConfig; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.camera.CameraController; +import org.telegram.messenger.camera.CameraSession; +import org.telegram.messenger.camera.CameraView; +import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.AlertDialog; + +import java.util.Arrays; +import java.util.Locale; + +public class DualCameraView extends CameraView implements CameraController.ErrorCallback { + + private boolean dualAvailable; + + public DualCameraView(Context context, boolean frontface, boolean lazy) { + super(context, frontface, lazy); + CameraController.getInstance().addOnErrorListener(this); + dualAvailable = dualAvailableStatic(context); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + boolean r = touchEvent(event); + return super.onTouchEvent(event) || r; + } + + @Override + public void destroy(boolean async, Runnable beforeDestroyRunnable) { + saveDual(); + super.destroy(async, beforeDestroyRunnable); + CameraController.getInstance().removeOnErrorListener(this); + } + + private final PointF lastTouch = new PointF(); + private final PointF touch = new PointF(); + private float lastTouchDistance; + private double lastTouchRotation; + private boolean multitouch; + private boolean allowRotation; + private final Matrix touchMatrix = new Matrix(), finalMatrix = new Matrix(); + private boolean down; + private float rotationDiff; + private boolean snappedRotation; + private boolean doNotSpanRotation; + private float[] tempPoint = new float[4]; + + private Matrix toScreen = new Matrix(); + private Matrix toGL = new Matrix(); + + private boolean firstMeasure = true; + private boolean atTop, atBottom; + + private boolean enabledSavedDual; + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + toScreen.reset(); + toScreen.postTranslate(1f, -1f); + toScreen.postScale(getMeasuredWidth() / 2f, -getMeasuredHeight() / 2f); + toScreen.invert(toGL); + } + + @Override + public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { + if (firstMeasure) { + if (isSavedDual()) { + enabledSavedDual = true; + setupDualMatrix(); + super.dual = true; + } + firstMeasure = false; + } + super.onSurfaceTextureAvailable(surface, width, height); + } + + @Override + protected void onDualCameraSuccess() { + saveDual(); + if (enabledSavedDual) { + onSavedDualCameraSuccess(); + } + log(true); + } + + private void log(boolean success) { + final boolean vendor = DualCameraView.dualAvailableDefault(ApplicationLoader.applicationContext, false); + if (MessagesController.getInstance(UserConfig.selectedAccount).collectDeviceStats) { + try { + TLRPC.TL_help_saveAppLog req = new TLRPC.TL_help_saveAppLog(); + TLRPC.TL_inputAppEvent event = new TLRPC.TL_inputAppEvent(); + event.time = ConnectionsManager.getInstance(UserConfig.selectedAccount).getCurrentTime(); + event.type = "android_dual_camera"; + TLRPC.TL_jsonObject obj = new TLRPC.TL_jsonObject(); + TLRPC.TL_jsonObjectValue kv = new TLRPC.TL_jsonObjectValue(); + kv.key = "device"; + TLRPC.TL_jsonString str = new TLRPC.TL_jsonString(); + str.value = "" + Build.MANUFACTURER + Build.MODEL; + kv.value = str; + obj.value.add(kv); + event.data = obj; + event.peer = (success ? 1 : 0) | (vendor ? 2 : 0); + req.events.add(event); + ConnectionsManager.getInstance(UserConfig.selectedAccount).sendRequest(req, (response, error) -> { + }); + } catch (Exception ignore) { + } + } + ApplicationLoader.logDualCamera(success, vendor); + } + + protected void onSavedDualCameraSuccess() { + + } + + public void resetSaved() { + resetSavedDual(); + } + + @Override + public void toggleDual() { + if (!isDual() && !dualAvailable()) { + return; + } + if (!isDual()) { + setupDualMatrix(); + } else { + resetSaved(); + } + super.toggleDual(); + } + + private void setupDualMatrix() { + Matrix matrix = getDualPosition(); + matrix.reset(); + boolean setDefault = true; + Matrix savedMatrix = getSavedDualMatrix(); + if (savedMatrix != null) { + matrix.set(savedMatrix); + setDefault = false; + } + + if (setDefault) { + matrix.postConcat(toScreen); + + float w = getMeasuredWidth() * .43f; + float h = getMeasuredHeight() * .43f; + float px = Math.min(getMeasuredWidth(), getMeasuredWidth()) * .025f; + float py = px * 2; + + matrix.postScale(w / getMeasuredWidth(), h / getMeasuredHeight()); + matrix.postTranslate(getMeasuredWidth() - px - w, px); + matrix.postConcat(toGL); + } + updateDualPosition(); + } + + public boolean isAtDual(float x, float y) { + if (!isDual()) { + return false; + } + vertex[0] = x; + vertex[1] = y; + toGL.mapPoints(vertex); + getDualPosition().invert(invMatrix); + invMatrix.mapPoints(vertex); + int shape = getDualShape() % 3; + boolean square = shape == 0 || shape == 1 || shape == 3; + float H = square ? 9 / 16f : 1f; + return vertex[0] >= -1 && vertex[0] <= 1 && vertex[1] >= -H && vertex[1] <= H; + } + + private float tapX, tapY; + private long tapTime; + private Matrix invMatrix = new Matrix(); + private Runnable longpressRunnable; + private boolean checkTap(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + tapTime = System.currentTimeMillis(); + tapX = ev.getX(); + tapY = ev.getY(); + lastFocusToPoint = null; + if (longpressRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(longpressRunnable); + longpressRunnable = null; + } + if (isAtDual(tapX, tapY)) { + AndroidUtilities.runOnUIThread(longpressRunnable = () -> { + if (tapTime > 0) { + this.dualToggleShape(); + performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); + } + }, ViewConfiguration.getLongPressTimeout()); + } + return true; + } else if (ev.getAction() == MotionEvent.ACTION_UP) { + if (System.currentTimeMillis() - tapTime <= ViewConfiguration.getTapTimeout() && MathUtils.distance(tapX, tapY, ev.getX(), ev.getY()) < AndroidUtilities.dp(10)) { + if (isAtDual(tapX, tapY)) { + switchCamera(); + lastFocusToPoint = null; + } else { + lastFocusToPoint = () -> focusToPoint((int) tapX, (int) tapY); + } + } + tapTime = -1; + if (longpressRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(longpressRunnable); + longpressRunnable = null; + } + } else if (ev.getAction() == MotionEvent.ACTION_CANCEL) { + tapTime = -1; + lastFocusToPoint = null; + if (longpressRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(longpressRunnable); + longpressRunnable = null; + } + } + return false; + } + + private Runnable lastFocusToPoint; + public void allowToTapFocus() { + if (lastFocusToPoint != null) { + lastFocusToPoint.run(); + lastFocusToPoint = null; + } + } + + public void clearTapFocus() { + lastFocusToPoint = null; + tapTime = -1; + } + + private boolean touchEvent(MotionEvent ev) { + boolean r = false; + r = checkTap(ev) || r; + if (isDual()) { + Matrix matrix = getDualPosition(); + + final boolean currentMultitouch = ev.getPointerCount() > 1; + float distance = 0; + double rotation = 0; + if (currentMultitouch) { + touch.x = (ev.getX(0) + ev.getX(1)) / 2f; + touch.y = (ev.getY(0) + ev.getY(1)) / 2f; + distance = MathUtils.distance(ev.getX(0), ev.getY(0), ev.getX(1), ev.getY(1)); + rotation = Math.atan2(ev.getY(1) - ev.getY(0), ev.getX(1) - ev.getX(0)); + } else { + touch.x = ev.getX(0); + touch.y = ev.getY(0); + } + if (multitouch != currentMultitouch) { + lastTouch.x = touch.x; + lastTouch.y = touch.y; + lastTouchDistance = distance; + lastTouchRotation = rotation; + multitouch = currentMultitouch; + } + + float tx = touch.x, ty = touch.y; + float ltx = lastTouch.x, lty = lastTouch.y; + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + touchMatrix.set(matrix); + touchMatrix.postConcat(toScreen); + rotationDiff = 0; + snappedRotation = false; + doNotSpanRotation = false; + down = isPointInsideDual(touchMatrix, touch.x, touch.y); + } + if (ev.getAction() == MotionEvent.ACTION_MOVE && down) { + if (MathUtils.distance(tx, ty, ltx, lty) > AndroidUtilities.dp(2)) { + if (longpressRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(longpressRunnable); + longpressRunnable = null; + } + } + if (ev.getPointerCount() > 1) { + if (lastTouchDistance != 0) { + extractPointsData(touchMatrix); + float scaleFactor = distance / lastTouchDistance; + if (w * scaleFactor > getWidth() * .7f) { + scaleFactor = getWidth() * .7f / w; + } else if (w * scaleFactor < getWidth() * .2f) { + scaleFactor = getWidth() * .2f / w; + } + touchMatrix.postScale(scaleFactor, scaleFactor, tx, ty); + } + float rotate = (float) Math.toDegrees(rotation - lastTouchRotation); + rotationDiff += rotate; + if (!allowRotation) { + allowRotation = Math.abs(rotationDiff) > 20f; + if (!allowRotation) { + extractPointsData(touchMatrix); + allowRotation = Math.round(angle / 90f) * 90f - angle > 20f; + } + if (!snappedRotation) { + try { + performHapticFeedback(HapticFeedbackConstants.TEXT_HANDLE_MOVE, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); + } catch (Exception ignore) { + } + snappedRotation = true; + } + } + if (allowRotation) { + touchMatrix.postRotate(rotate, tx, ty); + } + } + touchMatrix.postTranslate(tx - ltx, ty - lty); + finalMatrix.set(touchMatrix); + extractPointsData(finalMatrix); + float rotDiff = Math.round(angle / 90f) * 90f - angle; + if (allowRotation && !doNotSpanRotation) { + if (Math.abs(rotDiff) < 5f) { + finalMatrix.postRotate(rotDiff, cx, cy); + if (!snappedRotation) { + try { + performHapticFeedback(HapticFeedbackConstants.TEXT_HANDLE_MOVE, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); + } catch (Exception ignore) { + } + snappedRotation = true; + } + } else { + snappedRotation = false; + } + } + if (cx < 0) { + finalMatrix.postTranslate(-cx, 0); + } else if (cx > getWidth()) { + finalMatrix.postTranslate(getWidth() - cx, 0); + } + if (cy < 0) { + finalMatrix.postTranslate(0, -cy); + } else if (cy > getHeight() - AndroidUtilities.dp(150)) { + finalMatrix.postTranslate(0, getHeight() - AndroidUtilities.dp(150) - cy); + } + finalMatrix.postConcat(toGL); + matrix.set(finalMatrix); + updateDualPosition(); + + boolean atTop = Math.min(cy, cy - h / 2f) < AndroidUtilities.dp(66); + boolean atBottom = Math.max(cy, cy + h / 2f) > getHeight() - AndroidUtilities.dp(66); + if (this.atTop != atTop) { + onEntityDraggedTop(this.atTop = atTop); + } + if (this.atBottom != atBottom) { + onEntityDraggedBottom(this.atBottom = atBottom); + } + } + if (ev.getAction() == MotionEvent.ACTION_UP) { + allowRotation = false; + rotationDiff = 0; + snappedRotation = false; + invalidate(); + down = false; + + if (this.atTop) { + onEntityDraggedTop(this.atTop = false); + } + if (this.atBottom) { + onEntityDraggedBottom(this.atBottom = false); + } + } else if (ev.getAction() == MotionEvent.ACTION_CANCEL) { + down = false; + + if (this.atTop) { + onEntityDraggedTop(this.atTop = false); + } + if (this.atBottom) { + onEntityDraggedBottom(this.atBottom = false); + } + } + lastTouch.x = touch.x; + lastTouch.y = touch.y; + lastTouchDistance = distance; + lastTouchRotation = rotation; + r = down || r; + } + return r; + } + + protected void onEntityDraggedTop(boolean value) {} + protected void onEntityDraggedBottom(boolean value) {} + + public boolean isDualTouch() { + return down; + } + + private final float[] vertices = new float[2]; + private float cx, cy, angle, w, h; + + private void extractPointsData(Matrix matrix) { + vertices[0] = 0; + vertices[1] = 0; + matrix.mapPoints(vertices); + cx = vertices[0]; + cy = vertices[1]; + + vertices[0] = 1; + vertices[1] = 0; + matrix.mapPoints(vertices); + angle = (float) Math.toDegrees(Math.atan2(vertices[1] - cy, vertices[0] - cx)); + w = 2 * MathUtils.distance(cx, cy, vertices[0], vertices[1]); + + vertices[0] = 0; + vertices[1] = 1; + matrix.mapPoints(vertices); + h = 2 * MathUtils.distance(cx, cy, vertices[0], vertices[1]); + } + + private Matrix tempMatrix = new Matrix(); + private float[] vertex = new float[2]; + private float[] verticesSrc, verticesDst; + public boolean isPointInsideDual(Matrix matrix, float x, float y) { +// vertex[0] = x; +// vertex[1] = y; +// toGL.mapPoints(vertex); +// matrix.invert(tempMatrix); +// tempMatrix.mapPoints(vertex); +// return vertex[0] >= -1f && vertex[0] <= 1f && vertex[1] >= -1f && vertex[1] <= 1f; + + if (verticesSrc == null) { + verticesSrc = new float[8]; + } + if (verticesDst == null) { + verticesDst = new float[8]; + } + int shape = getDualShape() % 3; + boolean square = shape == 0 || shape == 1 || shape == 3; + float H = square ? 9 / 16f : 1f; + verticesSrc[0] = -1; + verticesSrc[1] = -H; + verticesSrc[2] = 1; + verticesSrc[3] = -H; + verticesSrc[4] = 1; + verticesSrc[5] = H; + verticesSrc[6] = -1; + verticesSrc[7] = H; + matrix.mapPoints(verticesDst, verticesSrc); + + double a1 = Math.sqrt((verticesDst[0] - verticesDst[2]) * (verticesDst[0] - verticesDst[2]) + (verticesDst[1] - verticesDst[3]) * (verticesDst[1] - verticesDst[3])); + double a2 = Math.sqrt((verticesDst[2] - verticesDst[4]) * (verticesDst[2] - verticesDst[4]) + (verticesDst[3] - verticesDst[5]) * (verticesDst[3] - verticesDst[5])); + double a3 = Math.sqrt((verticesDst[4] - verticesDst[6]) * (verticesDst[4] - verticesDst[6]) + (verticesDst[5] - verticesDst[7]) * (verticesDst[5] - verticesDst[7])); + double a4 = Math.sqrt((verticesDst[6] - verticesDst[0]) * (verticesDst[6] - verticesDst[0]) + (verticesDst[7] - verticesDst[1]) * (verticesDst[7] - verticesDst[1])); + + double b1 = Math.sqrt((verticesDst[0] - x) * (verticesDst[0] - x) + (verticesDst[1] - y) * (verticesDst[1] - y)); + double b2 = Math.sqrt((verticesDst[2] - x) * (verticesDst[2] - x) + (verticesDst[3] - y) * (verticesDst[3] - y)); + double b3 = Math.sqrt((verticesDst[4] - x) * (verticesDst[4] - x) + (verticesDst[5] - y) * (verticesDst[5] - y)); + double b4 = Math.sqrt((verticesDst[6] - x) * (verticesDst[6] - x) + (verticesDst[7] - y) * (verticesDst[7] - y)); + + double u1 = (a1 + b1 + b2) / 2; + double u2 = (a2 + b2 + b3) / 2; + double u3 = (a3 + b3 + b4) / 2; + double u4 = (a4 + b4 + b1) / 2; + + return (Math.sqrt(u1 * (u1 - a1) * (u1 - b1) * (u1 - b2)) + Math.sqrt(u2 * (u2 - a2) * (u2 - b2) * (u2 - b3)) + Math.sqrt(u3 * (u3 - a3) * (u3 - b3) * (u3 - b4)) + Math.sqrt(u4 * (u4 - a4) * (u4 - b4) * (u4 - b1)) - a1 * a2) < 1; + } + + @Override + public void onError(int error, Camera camera, CameraSession session) { + if (isDual()) { + if (!dualAvailableDefault(getContext(), false)) { + MessagesController.getGlobalMainSettings().edit().putBoolean("dual_available", dualAvailable = false).apply(); + new AlertDialog.Builder(getContext()) + .setTitle(LocaleController.getString(R.string.DualErrorTitle)) + .setMessage(LocaleController.getString(R.string.DualErrorMessage)) + .setPositiveButton(LocaleController.getString(R.string.OK), null) + .show(); + } + log(false); + toggleDual(); + } + if (getCameraSession(0) == session) { + resetCamera(); + } + onCameraError(); + } + + protected void onCameraError() { + resetSaved(); + } + + public boolean dualAvailable() { + return dualAvailable; + } + + private static final int[] dualWhitelistByDevice = new int[] { + 1893745684, // XIAOMI CUPID + -215458996, // XIAOMI VAYU + -862041025, // XIAOMI WILLOW + -1258375037, // XIAOMI INGRES + -1320049076, // XIAOMI GINKGO + -215749424, // XIAOMI LISA + 1901578030, // XIAOMI LEMON + -215451421, // XIAOMI VIVA + 1908491424, // XIAOMI STONE + -1321491332, // XIAOMI RAPHAEL + -1155551678, // XIAOMI MARBLE + 1908524435, // XIAOMI SURYA + 976847578, // XIAOMI LAUREL_SPROUT + -713271737, // OPPO OP4F2F + -2010722764, // SAMSUNG A52SXQ (A52s 5G) + 1407170066, // SAMSUNG D2Q (Note10+) + -1394190055, // SAMSUNG B4Q + 1407170066, // HUAWEI HWNAM + 1407159934, // HUAWEI HWCOR + 1407172057, // HUAWEI HWPCT + 1231389747, // FAIRPHONE FP3 + -2076538925, // MOTOROLA RSTAR + 41497626, // MOTOROLA RHODEC + 846150482, // MOTOROLA CHANNEL + -1198092731, // MOTOROLA CYPRUS64 + -251277614, // MOTOROLA HANOIP + -2078385967, // MOTOROLA PSTAR +// -1426053134 // REALME REE2ADL1 + }; + + private static final int[] dualWhitelistByModel = new int[] { + + }; + + public static boolean dualAvailableDefault(Context context, boolean withWhitelist) { + boolean def = ( + SharedConfig.getDevicePerformanceClass() >= SharedConfig.PERFORMANCE_CLASS_AVERAGE && + Camera.getNumberOfCameras() > 1 && + SharedConfig.allowPreparingHevcPlayers() + ); + if (def) { + def = context != null && context.getPackageManager().hasSystemFeature("android.hardware.camera.concurrent"); + if (!def && withWhitelist) { + int hash = (Build.MANUFACTURER + " " + Build.DEVICE).toUpperCase().hashCode(); + for (int i = 0; i < dualWhitelistByDevice.length; ++i) { + if (dualWhitelistByDevice[i] == hash) { + def = true; + break; + } + } + if (!def) { + hash = (Build.MANUFACTURER + Build.MODEL).toUpperCase().hashCode(); + for (int i = 0; i < dualWhitelistByModel.length; ++i) { + if (dualWhitelistByModel[i] == hash) { + def = true; + break; + } + } + } + } + } + return def; + } + + public static boolean dualAvailableStatic(Context context) { + return MessagesController.getGlobalMainSettings().getBoolean("dual_available", dualAvailableDefault(context, true)); + } + + + private Matrix getSavedDualMatrix() { + String str = MessagesController.getGlobalMainSettings().getString("dualmatrix", null); + if (str == null) { + return null; + } + String[] parts = str.split(";"); + if (parts.length != 9) { + return null; + } + float[] values = new float[9]; + for (int i = 0; i < parts.length; ++i) { + try { + values[i] = Float.parseFloat(parts[i]); + } catch (Exception e) { + FileLog.e(e); + return null; + } + } + Matrix matrix = new Matrix(); + matrix.setValues(values); + return matrix; + } + + public boolean isSavedDual() { + return dualAvailableStatic(getContext()) && MessagesController.getGlobalMainSettings().getBoolean("dualcam", dualAvailableDefault(ApplicationLoader.applicationContext, false)); + } + + private void resetSavedDual() { + MessagesController.getGlobalMainSettings().edit().putBoolean("dualcam", false).remove("dualmatrix").apply(); + } + + private void saveDual() { + SharedPreferences.Editor edit = MessagesController.getGlobalMainSettings().edit(); + edit.putBoolean("dualcam", isDual()); + if (isDual()) { + float[] values = new float[9]; + getDualPosition().getValues(values); + edit.putString("dualmatrix", Floats.join(";", values)); + } else { + edit.remove("dualmatrix"); + } + edit.apply(); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/EmojiBottomSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/EmojiBottomSheet.java new file mode 100644 index 0000000000..02e432140c --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/EmojiBottomSheet.java @@ -0,0 +1,1997 @@ +package org.telegram.ui.Stories.recorder; + +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.AndroidUtilities.lerp; +import static org.telegram.messenger.AndroidUtilities.translitSafe; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.LinearGradient; +import android.graphics.Matrix; +import android.graphics.Outline; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Shader; +import android.graphics.drawable.Drawable; +import android.media.Image; +import android.os.Build; +import android.os.SystemClock; +import android.text.Editable; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.util.Log; +import android.util.SparseArray; +import android.util.SparseIntArray; +import android.util.TypedValue; +import android.view.GestureDetector; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewOutlineProvider; +import android.view.animation.OvershootInterpolator; +import android.view.inputmethod.EditorInfo; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.OverScroller; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.graphics.ColorUtils; +import androidx.core.math.MathUtils; +import androidx.recyclerview.widget.DefaultItemAnimator; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.LinearSmoothScrollerCustom; +import androidx.recyclerview.widget.RecyclerView; + +import org.checkerframework.checker.units.qual.A; +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.DocumentObject; +import org.telegram.messenger.Emoji; +import org.telegram.messenger.FileLoader; +import org.telegram.messenger.ImageLoader; +import org.telegram.messenger.ImageLocation; +import org.telegram.messenger.ImageReceiver; +import org.telegram.messenger.LiteMode; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MediaController; +import org.telegram.messenger.MediaDataController; +import org.telegram.messenger.MessageObject; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.R; +import org.telegram.messenger.SharedConfig; +import org.telegram.messenger.SvgHelper; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.Utilities; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.AdjustPanLayoutHelper; +import org.telegram.ui.ActionBar.BottomSheet; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Cells.StickerSetNameCell; +import org.telegram.ui.Components.AnimatedEmojiDrawable; +import org.telegram.ui.Components.AnimatedEmojiSpan; +import org.telegram.ui.Components.AnimatedFileDrawable; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.BackupImageView; +import org.telegram.ui.Components.ButtonBounce; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.DrawingInBackgroundThreadDrawable; +import org.telegram.ui.Components.EditTextBoldCursor; +import org.telegram.ui.Components.EmojiTabsStrip; +import org.telegram.ui.Components.EmojiView; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.RLottieDrawable; +import org.telegram.ui.Components.RecyclerAnimationScrollHelper; +import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.Components.ScaleStateListAnimator; +import org.telegram.ui.Components.SearchStateDrawable; +import org.telegram.ui.Components.StickerCategoriesListView; +import org.telegram.ui.Components.ViewPagerFixed; +import org.telegram.ui.SelectAnimatedEmojiDialog; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + +public class EmojiBottomSheet extends BottomSheet implements NotificationCenter.NotificationCenterDelegate { + + private static final int PAGE_TYPE_EMOJI = 0; + private static final int PAGE_TYPE_STICKERS = 1; + + private String query = null; + private int categoryIndex = -1; + + private class Page extends FrameLayout { + public EmojiListView listView; + public Adapter adapter; + public GridLayoutManager layoutManager; + public EmojiTabsStrip tabsStrip; + public SearchField searchField; + + public int spanCount = 8; + + public int currentType; + + public Page(Context context) { + super(context); + + listView = new EmojiListView(context); + listView.setAdapter(adapter = new Adapter()); + listView.setLayoutManager(layoutManager = new GridLayoutManager(context, spanCount)); + listView.setClipToPadding(true); + listView.setVerticalScrollBarEnabled(false); + layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { + @Override + public int getSpanSize(int position) { + if (adapter.getItemViewType(position) != Adapter.VIEW_TYPE_EMOJI) { + return spanCount; + } + return 1; + } + }); + listView.setOnItemClickListener((view, position) -> { + if (position < 0) { + return; + } + TLRPC.Document document = position >= adapter.documents.size() ? null : adapter.documents.get(position); + long documentId = position >= adapter.documentIds.size() ? 0L : adapter.documentIds.get(position); + if (document == null && view instanceof EmojiListView.EmojiImageView && ((EmojiListView.EmojiImageView) view).drawable != null) { + document = ((EmojiListView.EmojiImageView) view).drawable.getDocument(); + } + if (document == null && documentId != 0L) { + document = AnimatedEmojiDrawable.findDocument(currentAccount, documentId); + } + if (document == null) { + return; + } + if (onDocumentSelected != null) { + onDocumentSelected.run(document); + } + dismiss(); + }); + listView.setOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { + containerView.invalidate(); + int pos; + if (lockTop < 0) { + pos = layoutManager.findFirstCompletelyVisibleItemPosition(); + } else { + pos = -1; + for (int i = 0; i < listView.getChildCount(); ++i) { + View child = listView.getChildAt(i); + if (child.getY() + child.getHeight() > lockTop + listView.getPaddingTop()) { + pos = listView.getChildAdapterPosition(child); + break; + } + } + if (pos == -1) { + return; + } + } + int sec = -1; + for (int i = adapter.positionToSection.size() - 1; i >= 0; --i) { + int position = adapter.positionToSection.keyAt(i); + int section = adapter.positionToSection.valueAt(i); + if (pos >= position) { + sec = section; + break; + } + } + if (sec >= 0) { + tabsStrip.select(sec, true); + } + if (keyboardVisible && listView.scrollingByUser && searchField != null && searchField.editText != null) { + closeKeyboard(); + } + } + + @Override + public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { + if (newState == RecyclerView.SCROLL_STATE_IDLE && lockTop >= 0 && atTop()) { + lockTop = -1; + } + } + }); + DefaultItemAnimator itemAnimator = new DefaultItemAnimator(); + itemAnimator.setAddDelay(0); + itemAnimator.setAddDuration(220); + itemAnimator.setMoveDuration(220); + itemAnimator.setChangeDuration(160); + itemAnimator.setMoveInterpolator(CubicBezierInterpolator.EASE_OUT); + listView.setItemAnimator(itemAnimator); + addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + + searchField = new SearchField(context, resourcesProvider); + searchField.setOnSearchQuery((query, category) -> { + EmojiBottomSheet.this.query = query; + EmojiBottomSheet.this.categoryIndex = category; + adapter.updateItems(query); + }); + addView(searchField, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP)); + + tabsStrip = new EmojiTabsStrip(context, resourcesProvider, false, false, true, 0, null) { + @Override + protected boolean onTabClick(int index) { + if (scrollingAnimation) { + return false; + } + if (searchField != null && searchField.categoriesListView != null) { + if (searchField.categoriesListView.getSelectedCategory() != null) { + listView.scrollToPosition(0, 0); + searchField.categoriesListView.selectCategory(null); + } + searchField.categoriesListView.scrollToStart(); + searchField.clear(); + } + if (adapter != null) { + adapter.updateItems(null); + } + int pos = -1; + for (int i = 0; i < adapter.positionToSection.size(); ++i) { + int position = adapter.positionToSection.keyAt(i); + int section = adapter.positionToSection.valueAt(i); + if (section == index) { + pos = position; + break; + } + } + if (pos >= 0) { + listView.scrollToPosition(pos, (int) lockTop() - dp(16 + 36 + 50)); + } + return true; + } + }; + addView(tabsStrip, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 36)); + } + + private float lockTop = -1; + public float top() { + if (lockTop >= 0) { + return lockTop; + } + for (int i = 0; i < listView.getChildCount(); ++i) { + View child = listView.getChildAt(i); + Object tag = child.getTag(); + if (tag instanceof Integer && (int) tag == 34) { + return Math.max(0, child.getBottom() - dp(16 + 36 + 50)); + } + } + return 0; + } + + public float lockTop() { + if (lockTop >= 0) { + return lockTop + listView.getPaddingTop(); + } + lockTop = top(); + return lockTop + listView.getPaddingTop(); + } + + public void updateTops() { + float top = Math.max(0, top()); + tabsStrip.setTranslationY(dp(16) + top); + searchField.setTranslationY(dp(16 + 36) + top); + listView.setBounds(top + listView.getPaddingTop(), listView.getHeight() - listView.getPaddingBottom()); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setPadding(backgroundPaddingLeft, 0, backgroundPaddingLeft, 0); + tabsStrip.setTranslationY(dp(16)); + searchField.setTranslationY(dp(16 + 36)); + listView.setPadding(dp(5), dp(16 + 36 + 50), dp(5), AndroidUtilities.navigationBarHeight + dp(40)); + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + private boolean resetOnce = false; + + public void bind(int type) { + currentType = type; + layoutManager.setSpanCount(spanCount = type == PAGE_TYPE_EMOJI ? 8 : 5); + if (!resetOnce) { + adapter.updateItems(null); + } + if (categoryIndex >= 0) { + searchField.ignoreTextChange = true; + searchField.editText.setText(""); + searchField.ignoreTextChange = false; + searchField.categoriesListView.selectCategory(categoryIndex); + searchField.categoriesListView.scrollToSelected(); + StickerCategoriesListView.EmojiCategory category = searchField.categoriesListView.getSelectedCategory(); + if (category != null) { + adapter.query = searchField.categoriesListView.getSelectedCategory().emojis; + AndroidUtilities.cancelRunOnUIThread(adapter.searchRunnable); + AndroidUtilities.runOnUIThread(adapter.searchRunnable); + } + } else if (!TextUtils.isEmpty(query)) { + searchField.editText.setText(query); + searchField.categoriesListView.selectCategory(null); + searchField.categoriesListView.scrollToStart(); + AndroidUtilities.cancelRunOnUIThread(adapter.searchRunnable); + AndroidUtilities.runOnUIThread(adapter.searchRunnable); + } else { + searchField.clear(); + } + + MediaDataController.getInstance(currentAccount).checkStickers(type == PAGE_TYPE_EMOJI ? MediaDataController.TYPE_EMOJIPACKS : MediaDataController.TYPE_IMAGE); + } + + public boolean atTop() { + return !listView.canScrollVertically(-1); + } + + private class Adapter extends RecyclerView.Adapter { + + private int lastAllSetsCount; + private final HashMap> allEmojis = new HashMap<>(); + private final HashMap> packsBySet = new HashMap<>(); + + private final ArrayList allStickerSets = new ArrayList<>(); + private final ArrayList stickerSets = new ArrayList<>(); + private final ArrayList packs = new ArrayList<>(); + private final ArrayList documents = new ArrayList<>(); + private final ArrayList documentIds = new ArrayList<>(); + private boolean includeNotFound; + private int itemsCount = 0; + private final SparseIntArray positionToSection = new SparseIntArray(); + + public void update() { + if (this.query == null) { + updateItems(null); + } + } + + private TLRPC.TL_messages_stickerSet faveSet; + private TLRPC.TL_messages_stickerSet recentSet; + + private void updateItems(String query) { + this.query = query; + if (query != null) { + searchField.showProgress(true); + tabsStrip.showSelected(false); + AndroidUtilities.cancelRunOnUIThread(searchRunnable); + AndroidUtilities.runOnUIThread(searchRunnable, 100); + return; + } + + tabsStrip.showSelected(true); + AndroidUtilities.cancelRunOnUIThread(searchRunnable); + + final MediaDataController mediaDataController = MediaDataController.getInstance(currentAccount); + + itemsCount = 0; + documents.clear(); + documentIds.clear(); + positionToSection.clear(); + stickerSets.clear(); + allStickerSets.clear(); + itemsCount++; // pan + documents.add(null); + packs.clear(); + if (currentType == PAGE_TYPE_STICKERS) { + ArrayList favorites = mediaDataController.getRecentStickers(MediaDataController.TYPE_FAVE); + if (favorites != null && !favorites.isEmpty()) { + if (faveSet == null) { + faveSet = new TLRPC.TL_messages_stickerSet(); + } + faveSet.documents = favorites; + faveSet.set = new TLRPC.TL_stickerSet(); + faveSet.set.title = LocaleController.getString("FavoriteStickers", R.string.FavoriteStickers); + stickerSets.add(faveSet); + } + + ArrayList recent = mediaDataController.getRecentStickers(MediaDataController.TYPE_IMAGE); + if (recent != null && !recent.isEmpty()) { + if (recentSet == null) { + recentSet = new TLRPC.TL_messages_stickerSet(); + } + recentSet.documents = recent; + recentSet.set = new TLRPC.TL_stickerSet(); + recentSet.set.title = LocaleController.getString("RecentStickers", R.string.RecentStickers); + stickerSets.add(recentSet); + } + } + stickerSets.addAll(mediaDataController.getStickerSets( + currentType == PAGE_TYPE_EMOJI ? + MediaDataController.TYPE_EMOJIPACKS : + MediaDataController.TYPE_IMAGE + )); + int i = 0; + for (; i < stickerSets.size(); ++i) { + TLRPC.TL_messages_stickerSet set = stickerSets.get(i); + + // header + positionToSection.put(itemsCount, i); + documents.add(null); + itemsCount++; + + // emoji/stickers + documents.addAll(set.documents); + itemsCount += set.documents.size(); + + EmojiView.EmojiPack pack = new EmojiView.EmojiPack(); + pack.documents = set.documents; + pack.set = set.set; + pack.installed = true; + pack.featured = false; + pack.expanded = true; + pack.free = true; + if (set == faveSet) { + pack.resId = R.drawable.emoji_tabs_faves; + } else if (set == recentSet) { + pack.resId = R.drawable.msg_emoji_recent; + } + packs.add(pack); + + allStickerSets.add(set); + } + if (currentType == PAGE_TYPE_EMOJI) { + ArrayList featuredSets = mediaDataController.getFeaturedEmojiSets(); + if (featuredSets != null) { + for (int j = 0; j < featuredSets.size(); ++j) { + TLRPC.StickerSetCovered setCovered = featuredSets.get(j); + TLRPC.TL_messages_stickerSet set; + if (setCovered instanceof TLRPC.TL_stickerSetNoCovered) { + set = MediaDataController.getInstance(currentAccount).getStickerSet(MediaDataController.getInputStickerSet(setCovered.set), false); + if (set == null) { + continue; + } + } else if (setCovered instanceof TLRPC.TL_stickerSetFullCovered) { + set = new TLRPC.TL_messages_stickerSet(); + set.set = setCovered.set; + set.documents = ((TLRPC.TL_stickerSetFullCovered) setCovered).documents; + set.packs = packsBySet.get(set.set.id); + if (set.packs == null) { + HashMap> packs = new HashMap<>(); + for (int a = 0; a < set.documents.size(); ++a) { + TLRPC.Document document = set.documents.get(a); + if (document == null) { + continue; + } + String emoticon = MessageObject.findAnimatedEmojiEmoticon(document, null); + ArrayList emojis = Emoji.parseEmojis(emoticon); + if (emojis != null) { + for (int e = 0; e < emojis.size(); ++e) { + String emoji = emojis.get(e).code.toString(); + ArrayList list = packs.get(emoji); + if (list == null) { + packs.put(emoji, list = new ArrayList<>()); + } + list.add(document.id); + } + } + } + set.packs = new ArrayList<>(); + for (Map.Entry> e : packs.entrySet()) { + TLRPC.TL_stickerPack pack = new TLRPC.TL_stickerPack(); + pack.emoticon = e.getKey(); + pack.documents = e.getValue(); + set.packs.add(pack); + } + packsBySet.put(set.set.id, set.packs); + } + } else { + continue; + } + + boolean found = false; + if (set == null || set.set == null) { + continue; + } + for (int a = 0; a < packs.size(); ++a) { + TLRPC.StickerSet set2 = packs.get(a).set; + if (set2 != null && set2.id == set.set.id) { + found = true; + break; + } + } + if (found) { + continue; + } + + stickerSets.add(set); + allStickerSets.add(set); + + // header + positionToSection.put(itemsCount, i); + i++; + documents.add(null); + itemsCount++; + + // emoji/stickers + documents.addAll(set.documents); + itemsCount += set.documents.size(); + + EmojiView.EmojiPack pack = new EmojiView.EmojiPack(); + pack.documents = set.documents; + pack.set = set.set; + pack.installed = false; + pack.featured = true; + pack.expanded = true; + pack.free = true; + packs.add(pack); + } + } + boolean containsStaticEmoji = false; + for (int a = 0; a < allStickerSets.size(); ++a) { + try { + containsStaticEmoji = allStickerSets.get(a).set.title.toLowerCase().contains("staticemoji"); + } catch (Exception ignore) {} + if (containsStaticEmoji) { + break; + } + } + if (!containsStaticEmoji) { + TLRPC.TL_inputStickerSetShortName inputStickerSet = new TLRPC.TL_inputStickerSetShortName(); + inputStickerSet.short_name = "StaticEmoji"; + TLRPC.TL_messages_stickerSet set = mediaDataController.getStickerSet(inputStickerSet, false); + if (set != null) { + allStickerSets.add(set); + } + } + } + resetOnce = true; + + if (lastAllSetsCount != allStickerSets.size()) { + allEmojis.clear(); + for (int a = 0; a < allStickerSets.size(); ++a) { + TLRPC.TL_messages_stickerSet set = allStickerSets.get(a); + if (set == null) { + continue; + } + for (int b = 0; b < set.packs.size(); ++b) { + String emoji = set.packs.get(b).emoticon; + ArrayList arr = allEmojis.get(emoji); + if (arr == null) { + allEmojis.put(emoji, arr = new ArrayList<>()); + } + arr.addAll(set.packs.get(b).documents); + } + } + lastAllSetsCount = allStickerSets.size(); + } + + includeNotFound = false; + tabsStrip.updateEmojiPacks(packs); + activeQuery = null; + notifyDataSetChanged(); + } + + private String query; + private String activeQuery; + private String[] lastLang; + private int searchId; + + private HashSet searchDocumentIds = new HashSet<>(); + + private final Runnable searchRunnable = () -> { + final String thisQuery = query; + final MediaDataController mediaDataController = MediaDataController.getInstance(currentAccount); + String[] lang = AndroidUtilities.getCurrentKeyboardLanguage(); + if (lastLang == null || !Arrays.equals(lang, lastLang)) { + MediaDataController.getInstance(currentAccount).fetchNewEmojiKeywords(lang); + } + mediaDataController.getEmojiSuggestions(lastLang = lang, query, false, (result, alias) -> { + if (!TextUtils.equals(thisQuery, query)) { + return; + } + + ArrayList emojis = Emoji.parseEmojis(query); + for (int i = 0; i < emojis.size(); ++i) { + try { + MediaDataController.KeywordResult k = new MediaDataController.KeywordResult(); + k.emoji = emojis.get(i).code.toString(); + result.add(k); + } catch (Exception ignore) {} + } + + itemsCount = 0; + documents.clear(); + documentIds.clear(); + positionToSection.clear(); + stickerSets.clear(); + itemsCount++; // pan + documents.add(null); + documentIds.add(0L); + if (currentType == PAGE_TYPE_EMOJI) { + searchDocumentIds.clear(); + for (int i = 0; i < result.size(); ++i) { + MediaDataController.KeywordResult r = result.get(i); + if (r.emoji != null && !r.emoji.startsWith("animated_")) { + ArrayList ids = allEmojis.get(r.emoji); + if (ids != null) { + searchDocumentIds.addAll(ids); + } + } + } + documentIds.addAll(searchDocumentIds); + for (int i = 0; i < searchDocumentIds.size(); ++i) { + documents.add(null); + } + itemsCount += searchDocumentIds.size(); + } else { + final HashMap> allStickers = mediaDataController.getAllStickers(); + for (int i = 0; i < result.size(); ++i) { + MediaDataController.KeywordResult r = result.get(i); + ArrayList stickers = allStickers.get(r.emoji); + if (stickers == null || stickers.isEmpty()) { + continue; + } + for (int j = 0; j < stickers.size(); ++j) { + TLRPC.Document d = stickers.get(j); + if (d != null && !documents.contains(d)) { + documents.add(d); + itemsCount++; + } + } + } + } + final String q = translitSafe((query + "").toLowerCase()); + for (int i = 0; i < allStickerSets.size(); ++i) { + TLRPC.TL_messages_stickerSet set = allStickerSets.get(i); + if (set == null || set.set == null) { + continue; + } + final String title = translitSafe((set.set.title + "").toLowerCase()); + if (title.startsWith(q) || title.contains(" " + q)) { + final int index = stickerSets.size(); + stickerSets.add(set); + + // header + positionToSection.put(itemsCount, index); + documents.add(null); + itemsCount++; + + // emoji/stickers + documents.addAll(set.documents); + itemsCount += set.documents.size(); + } + } + + if (includeNotFound = (documentIds.size() <= 1 && documents.size() <= 1)) { + itemsCount++; + } + if (!includeNotFound) { + searchId++; + } + activeQuery = query; + notifyDataSetChanged(); + + listView.scrollToPosition(0, 0); + searchField.showProgress(false); + tabsStrip.showSelected(false); + }, null, false, false, false, true, 50); + }; + + private static final int VIEW_TYPE_PAD = 0; + private static final int VIEW_TYPE_HEADER = 1; + private static final int VIEW_TYPE_EMOJI = 2; + private static final int VIEW_TYPE_NOT_FOUND = 3; + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view; + if (viewType == VIEW_TYPE_PAD) { + view = new View(getContext()); + } else if (viewType == VIEW_TYPE_HEADER) { + view = new StickerSetNameCell(getContext(), true, resourcesProvider); + } else if (viewType == VIEW_TYPE_NOT_FOUND) { + view = new NoEmojiView(getContext(), currentType == PAGE_TYPE_EMOJI); + } else { + view = new EmojiListView.EmojiImageView(getContext(), listView); + } + return new RecyclerListView.Holder(view); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + final int viewType = holder.getItemViewType(); + if (viewType == VIEW_TYPE_PAD) { + holder.itemView.setTag(34); + holder.itemView.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, (int) maxPadding)); + } else if (viewType == VIEW_TYPE_HEADER) { + final int section = positionToSection.get(position); + if (section < 0 || section >= stickerSets.size()) { + return; + } + final TLRPC.TL_messages_stickerSet set = stickerSets.get(section); + String title = set == null || set.set == null ? "" : set.set.title; + StickerSetNameCell cell = (StickerSetNameCell) holder.itemView; + if (activeQuery == null) { + cell.setText(title, 0); + } else { + int index = title.toLowerCase().indexOf(activeQuery.toLowerCase()); + if (index < 0) { + cell.setText(title, 0); + } else { + cell.setText(title, 0, index, activeQuery.length()); + } + } + } else if (viewType == VIEW_TYPE_EMOJI) { + final TLRPC.Document document = position >= documents.size() ? null : documents.get(position); + final long documentId = position >= documentIds.size() ? 0L : documentIds.get(position); + if (document == null && documentId == 0) { + return; + } + EmojiListView.EmojiImageView imageView = (EmojiListView.EmojiImageView) holder.itemView; + if (currentType == PAGE_TYPE_EMOJI) { + if (document != null) { + imageView.setSticker(null); + imageView.setEmoji(document); + } else { + imageView.setSticker(null); + imageView.setEmojiId(documentId); + } + } else { + imageView.setEmoji(null); + imageView.setSticker(document); + } + } else if (viewType == VIEW_TYPE_NOT_FOUND) { + ((NoEmojiView) holder.itemView).update(searchId); + } + } + + @Override + public int getItemViewType(int position) { + if (position == 0) { + return VIEW_TYPE_PAD; + } else if (includeNotFound && position == itemsCount - 1) { + return VIEW_TYPE_NOT_FOUND; + } else if (positionToSection.get(position, -1) >= 0) { + return VIEW_TYPE_HEADER; + } else { + return VIEW_TYPE_EMOJI; + } + } + + @Override + public int getItemCount() { + return itemsCount; + } + } + } + + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.stickersDidLoad || id == NotificationCenter.groupStickersDidLoad) { + View[] pages = viewPager.getViewPages(); + for (int i = 0; i < pages.length; ++i) { + View view = pages[i]; + if (view instanceof Page) { + Page page = (Page) view; + if (id == NotificationCenter.groupStickersDidLoad || + page.currentType == PAGE_TYPE_EMOJI && (int) args[0] == MediaDataController.TYPE_EMOJIPACKS || + page.currentType == PAGE_TYPE_STICKERS && (int) args[0] == MediaDataController.TYPE_IMAGE + ) { + page.adapter.update(); + } + } + } + } + } + + private final ViewPagerFixed viewPager; + private final TabsView tabsView; + private final ImageView galleryButton; + private float maxPadding = -1; + +// private final GestureDetector gestureDetector; + private boolean wasKeyboardVisible; + + public EmojiBottomSheet(Context context, Theme.ResourcesProvider resourcesProvider) { + super(context, true, resourcesProvider); + + useSmoothKeyboard = true; + fixNavigationBar(Theme.getColor(Theme.key_dialogBackground, resourcesProvider)); + + occupyNavigationBar = true; + setUseLightStatusBar(false); + + containerView = new ContainerView(context); + viewPager = new ViewPagerFixed(context) { + @Override + protected void onTabAnimationUpdate() { + tabsView.setType(viewPager.getPositionAnimated()); + containerView.invalidate(); + invalidate(); + } + }; + viewPager.setAdapter(new ViewPagerFixed.Adapter() { + @Override + public int getItemCount() { + return 2; + } + @Override + public View createView(int viewType) { + return new Page(context); + } + @Override + public void bindView(View view, int position, int viewType) { + ((Page) view).bind(position); + } + }); + containerView.addView(viewPager, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.BOTTOM | Gravity.FILL_HORIZONTAL)); + + new KeyboardNotifier(containerView, height -> { + if (wasKeyboardVisible != keyboardVisible) { + wasKeyboardVisible = keyboardVisible; + container.clearAnimation(); + final float ty = keyboardVisible ? Math.min(0, Math.max((AndroidUtilities.displaySize.y - keyboardHeight) * .3f - top, -keyboardHeight / 3f)) : 0; + container.animate().translationY(ty).setDuration(AdjustPanLayoutHelper.keyboardDuration).setInterpolator(AdjustPanLayoutHelper.keyboardInterpolator).start(); + } + }); + + tabsView = new TabsView(context); + tabsView.setOnTypeSelected(type -> { + if (!viewPager.isManualScrolling() && viewPager.getCurrentPosition() != type) { + viewPager.scrollToPosition(type); + tabsView.setType(type); + } + }); + containerView.addView(tabsView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM | Gravity.FILL_HORIZONTAL)); + + galleryButton = new ImageView(context); + galleryButton.setScaleType(ImageView.ScaleType.CENTER); + galleryButton.setVisibility(View.GONE); + galleryButton.setImageResource(R.drawable.msg_tabs_media); + galleryButton.setColorFilter(new PorterDuffColorFilter(0x70ffffff, PorterDuff.Mode.SRC_IN)); + ScaleStateListAnimator.apply(galleryButton); + containerView.addView(galleryButton, LayoutHelper.createFrame(40, 40, Gravity.BOTTOM | Gravity.LEFT, 8, 0, 0, 0)); + + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.stickersDidLoad); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.groupStickersDidLoad); + + MediaDataController.getInstance(currentAccount).checkStickers(MediaDataController.TYPE_EMOJIPACKS); + MediaDataController.getInstance(currentAccount).checkFeaturedEmoji(); + + MediaDataController.getInstance(currentAccount).checkStickers(MediaDataController.TYPE_IMAGE); + MediaDataController.getInstance(currentAccount).loadRecents(MediaDataController.TYPE_IMAGE, false, true, false); + MediaDataController.getInstance(currentAccount).loadRecents(MediaDataController.TYPE_FAVE, false, true, false); + } + + public void closeKeyboard() { + keyboardVisible = false; + container.animate().translationY(0).setDuration(AdjustPanLayoutHelper.keyboardDuration).setInterpolator(AdjustPanLayoutHelper.keyboardInterpolator).start(); + View[] views = viewPager.getViewPages(); + for (int i = 0; i < views.length; ++i) { + View view = views[i]; + if (view instanceof Page && ((Page) view).searchField != null) { + AndroidUtilities.hideKeyboard(((Page) view).searchField.editText); + } + } + } + + public void setOnGalleryClick(View.OnClickListener listener) { + galleryButton.setOnClickListener(listener); + galleryButton.setVisibility(listener != null ? View.VISIBLE : View.GONE); + } + + @Override + public void dismiss() { + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.stickersDidLoad); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.groupStickersDidLoad); + closeKeyboard(); + super.dismiss(); + } + + private Utilities.Callback2 drawBlurBitmap; + + public void setBlurDelegate(Utilities.Callback2 drawBlurBitmap) { + this.drawBlurBitmap = drawBlurBitmap; + } + + private float top; + + private class ContainerView extends FrameLayout { + + private static final float PADDING = .45f; + + private final Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint backgroundBlurPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + private final Paint handlePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private Bitmap blurBitmap; + private BitmapShader blurBitmapShader; + private Matrix blurBitmapMatrix; + + private final AnimatedFloat isActionBarT = new AnimatedFloat(this, 0, 250, CubicBezierInterpolator.EASE_OUT_QUINT); + private final RectF handleRect = new RectF(); + + public ContainerView(Context context) { + super(context); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + setupBlurBitmap(); + } + + private void setupBlurBitmap() { + if (blurBitmap != null || drawBlurBitmap == null || SharedConfig.getDevicePerformanceClass() <= SharedConfig.PERFORMANCE_CLASS_LOW || !LiteMode.isPowerSaverApplied()) { + return; + } + final int scale = 16; + Bitmap bitmap = Bitmap.createBitmap(AndroidUtilities.displaySize.x / scale, AndroidUtilities.displaySize.y / scale, Bitmap.Config.ARGB_8888); + drawBlurBitmap.run(bitmap, (float) scale); + Utilities.stackBlurBitmap(bitmap, 8); + blurBitmap = bitmap; + backgroundBlurPaint.setShader(blurBitmapShader = new BitmapShader(blurBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)); + if (blurBitmapMatrix == null) { + blurBitmapMatrix = new Matrix(); + } + blurBitmapMatrix.postScale(8, 8); + blurBitmapShader.setLocalMatrix(blurBitmapMatrix); + invalidate(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (blurBitmap != null) { + blurBitmap.recycle(); + } + backgroundBlurPaint.setShader(null); + } + + @Override + public void setTranslationY(float translationY) { + super.setTranslationY(translationY); + invalidate(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int width = MeasureSpec.getSize(widthMeasureSpec); + final int height = MeasureSpec.getSize(heightMeasureSpec); + final float newMaxPadding = Math.min(height * PADDING, dp(350) / (1f - PADDING) * PADDING); +// if (maxPadding > 0) { +// viewPager.setTranslationY(viewPager.getTranslationY() / maxPadding * newMaxPadding); +// } else { +// viewPager.setTranslationY(newMaxPadding); +// } + maxPadding = newMaxPadding; + viewPager.setPadding(0, AndroidUtilities.statusBarHeight, 0, 0); + viewPager.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); + tabsView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), 0); + galleryButton.measure(MeasureSpec.makeMeasureSpec(dp(40), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(dp(40), MeasureSpec.EXACTLY)); + galleryButton.setTranslationY(-AndroidUtilities.navigationBarHeight); + setMeasuredDimension(width, height); + } + + @Override + protected void dispatchDraw(Canvas canvas) { + backgroundPaint.setColor(Theme.getColor(Theme.key_dialogBackground, resourcesProvider)); + backgroundPaint.setAlpha((int) (0xFF * (blurBitmap == null ? .95f : .85f))); + View[] views = viewPager.getViewPages(); + top = 0; + if (views[0] instanceof Page) { + top += ((Page) views[0]).top() * Utilities.clamp(1f - Math.abs(views[0].getTranslationX() / (float) views[0].getMeasuredWidth()), 1, 0); + if (views[0].getVisibility() == View.VISIBLE) { + ((Page) views[0]).updateTops(); + } + } + if (views[1] instanceof Page) { + top += ((Page) views[1]).top() * Utilities.clamp(1f - Math.abs(views[1].getTranslationX() / (float) views[1].getMeasuredWidth()), 1, 0); + if (views[1].getVisibility() == View.VISIBLE) { + ((Page) views[1]).updateTops(); + } + } + final float statusBar = isActionBarT.set(top <= 0 ? 1 : 0); + final float y = top + viewPager.getPaddingTop() - lerp(dp(8), viewPager.getPaddingTop(), statusBar); + AndroidUtilities.rectTmp.set(backgroundPaddingLeft, y, getWidth() - backgroundPaddingLeft, getHeight() + AndroidUtilities.dp(8)); + if (blurBitmap != null) { + blurBitmapMatrix.reset(); + blurBitmapMatrix.postScale(12, 12); + blurBitmapMatrix.postTranslate(0, -getY()); + blurBitmapShader.setLocalMatrix(blurBitmapMatrix); + canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(14), dp(14), backgroundBlurPaint); + } + canvas.drawRoundRect(AndroidUtilities.rectTmp, (1f - statusBar) * dp(14), (1f - statusBar) * dp(14), backgroundPaint); + handleRect.set( + (getWidth() - dp(36)) / 2f, + y + dp(9.66f), + (getWidth() + dp(36)) / 2f, + y + dp(9.66f + 4) + ); + handlePaint.setColor(0x51838383); + handlePaint.setAlpha((int) (0x51 * (1f - statusBar))); + canvas.drawRoundRect(handleRect, dp(4), dp(4), handlePaint); + canvas.save(); + canvas.clipRect(AndroidUtilities.rectTmp); + super.dispatchDraw(canvas); + canvas.restore(); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN && event.getY() < top) { + dismiss(); + return true; + } + return super.onTouchEvent(event); + } + } + + @Override + public int getContainerViewHeight() { + if (containerView.getMeasuredHeight() <= 0) { + return AndroidUtilities.displaySize.y; + } + return (int) (containerView.getMeasuredHeight() - viewPager.getY()); + } + + private Utilities.Callback onDocumentSelected; + public EmojiBottomSheet whenSelected(Utilities.Callback listener) { + this.onDocumentSelected = listener; + return this; + } + + @Override + protected boolean canDismissWithSwipe() { + return viewPager.getTranslationY() >= (int) maxPadding; + } + + private static class EmojiListView extends RecyclerListView { + public static class EmojiImageView extends View { + + public boolean notDraw; + public boolean emoji; + + private final int currentAccount = UserConfig.selectedAccount; + private final EmojiListView listView; + public AnimatedEmojiDrawable drawable; + public ImageReceiver imageReceiver; + private long documentId; + + public ImageReceiver.BackgroundThreadDrawHolder[] backgroundThreadDrawHolder = new ImageReceiver.BackgroundThreadDrawHolder[DrawingInBackgroundThreadDrawable.THREAD_COUNT]; + public ImageReceiver imageReceiverToDraw; + + private final ButtonBounce bounce = new ButtonBounce(this); + + public EmojiImageView(Context context, EmojiListView parent) { + super(context); + setPadding(AndroidUtilities.dp(2), AndroidUtilities.dp(2), AndroidUtilities.dp(2), AndroidUtilities.dp(2)); + this.listView = parent; + } + + public void setEmoji(TLRPC.Document document) { + if (documentId == (document == null ? 0 : document.id)) { + return; + } + if (drawable != null) { + drawable.removeView(this); + } + if (document != null) { + emoji = true; + documentId = document.id; + drawable = AnimatedEmojiDrawable.make(currentAccount, AnimatedEmojiDrawable.CACHE_TYPE_ALERT_PREVIEW, document); + if (attached) { + drawable.addView(this); + } + } else { + emoji = false; + documentId = 0; + drawable = null; + } + } + + @Override + public void invalidate() { + listView.invalidate(); + } + + public void setEmojiId(long documentId) { + if (this.documentId == documentId) { + return; + } + if (drawable != null) { + drawable.removeView(this); + } + if (documentId != 0) { + emoji = true; + this.documentId = documentId; + drawable = AnimatedEmojiDrawable.make(currentAccount, AnimatedEmojiDrawable.CACHE_TYPE_ALERT_PREVIEW, documentId); + if (attached) { + drawable.addView(this); + } + } else { + emoji = false; + this.documentId = 0; + drawable = null; + } + } + + @Override + public void setPressed(boolean pressed) { + super.setPressed(pressed); + bounce.setPressed(pressed); + } + + public float getScale() { + return bounce.getScale(.15f); + } + + public void setSticker(TLRPC.Document document) { + emoji = false; + if (document != null) { + if (documentId == document.id) { + return; + } + documentId = document.id; + if (imageReceiver == null) { + imageReceiver = new ImageReceiver(); + imageReceiver.setLayerNum(7); + imageReceiver.setAspectFit(true); + imageReceiver.setParentView(listView); + if (attached) { + imageReceiver.onAttachedToWindow(); + } + } + SvgHelper.SvgDrawable svgThumb = DocumentObject.getSvgThumb(document, Theme.key_windowBackgroundWhiteGrayIcon, 0.2f); + TLRPC.PhotoSize thumb = FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 90); + String filter = "80_80"; + if ("video/webm".equals(document.mime_type)) { + filter += "_" + ImageLoader.AUTOPLAY_FILTER; + } + if (svgThumb != null) { + svgThumb.overrideWidthAndHeight(512, 512); + } + imageReceiver.setImage(ImageLocation.getForDocument(document), filter, ImageLocation.getForDocument(thumb, document), "80_80", svgThumb, 0, null, document, 0); + } else if (imageReceiver != null) { + documentId = 0; + imageReceiver.clearImage(); + } + } + + boolean attached; + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + attached = true; + if (drawable != null) { + drawable.addView(listView); + } + if (imageReceiver != null) { + imageReceiver.onAttachedToWindow(); + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + attached = false; + if (drawable != null) { + drawable.removeView(listView); + } + if (imageReceiver != null) { + imageReceiver.onDetachedFromWindow(); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int spec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY); + super.onMeasure(spec, spec); + } + + public void update(long time) { + if (imageReceiverToDraw != null) { + if (imageReceiverToDraw.getLottieAnimation() != null) { + imageReceiverToDraw.getLottieAnimation().updateCurrentFrame(time, true); + } + if (imageReceiverToDraw.getAnimation() != null) { + imageReceiverToDraw.getAnimation().updateCurrentFrame(time, true); + } + } + } + + @Override + protected void onDraw(Canvas canvas) { + if (imageReceiver != null) { + imageReceiver.setImageCoords(0, 0, getWidth(), getHeight()); + imageReceiver.draw(canvas); + } else if (drawable != null) { + drawable.setBounds(0, 0, getWidth(), getHeight()); + drawable.draw(canvas); + } + } + } + + private RecyclerAnimationScrollHelper scrollHelper; + public boolean emoji; + + public EmojiListView(Context context) { + super(context); + } + + @Override + public void setLayoutManager(@Nullable LayoutManager layout) { + super.setLayoutManager(layout); + + scrollHelper = null; + if (layout instanceof LinearLayoutManager) { + scrollHelper = new RecyclerAnimationScrollHelper(this, (LinearLayoutManager) layout); + scrollHelper.setAnimationCallback(new RecyclerAnimationScrollHelper.AnimationCallback() { + @Override + public void onPreAnimation() { + smoothScrolling = true; + } + + @Override + public void onEndAnimation() { + smoothScrolling = false; + } + }); + scrollHelper.setScrollListener(this::invalidate); + } + } + + private void scrollToPosition(int position, int offset) { + if (scrollHelper == null || !(getLayoutManager() instanceof GridLayoutManager)) { + return; + } + GridLayoutManager layoutManager = (GridLayoutManager) getLayoutManager(); + View view = layoutManager.findViewByPosition(position); + int firstPosition = layoutManager.findFirstVisibleItemPosition(); + if ((view == null && Math.abs(position - firstPosition) > layoutManager.getSpanCount() * 9f) || !SharedConfig.animationsEnabled()) { + scrollHelper.setScrollDirection(layoutManager.findFirstVisibleItemPosition() < position ? RecyclerAnimationScrollHelper.SCROLL_DIRECTION_DOWN : RecyclerAnimationScrollHelper.SCROLL_DIRECTION_UP); + scrollHelper.scrollToPosition(position, offset, false, true); + } else { + LinearSmoothScrollerCustom linearSmoothScroller = new LinearSmoothScrollerCustom(getContext(), LinearSmoothScrollerCustom.POSITION_TOP) { + @Override + public void onEnd() { + smoothScrolling = false; + } + @Override + protected void onStart() { + smoothScrolling = true; + } + }; + linearSmoothScroller.setTargetPosition(position); + linearSmoothScroller.setOffset(offset); + layoutManager.startSmoothScroll(linearSmoothScroller); + } + } + + private float topBound, bottomBound; + public void setBounds(float topBound, float bottomBound) { + this.topBound = topBound; + this.bottomBound = bottomBound; + } + + public boolean smoothScrolling = false; + + private final SparseArray> viewsGroupedByLines = new SparseArray<>(); + private final ArrayList> unusedArrays = new ArrayList<>(); + private final ArrayList unusedLineDrawables = new ArrayList<>(); + private final ArrayList lineDrawables = new ArrayList<>(); + private final ArrayList lineDrawablesTmp = new ArrayList<>(); + private boolean invalidated; + + @Override + protected void dispatchDraw(Canvas canvas) { + if (getVisibility() != View.VISIBLE) { + return; + } + invalidated = false; + int restoreTo = canvas.getSaveCount(); + + canvas.save(); + canvas.clipRect(0, topBound, getWidth(), bottomBound); + + if (!selectorRect.isEmpty()) { + selectorDrawable.setBounds(selectorRect); + canvas.save(); + if (selectorTransformer != null) { + selectorTransformer.accept(canvas); + } + selectorDrawable.draw(canvas); + canvas.restore(); + } + + for (int i = 0; i < viewsGroupedByLines.size(); i++) { + ArrayList arrayList = viewsGroupedByLines.valueAt(i); + arrayList.clear(); + unusedArrays.add(arrayList); + } + viewsGroupedByLines.clear(); + + for (int i = 0; i < getChildCount(); ++i) { + View child = getChildAt(i); + if (child instanceof EmojiImageView) { + EmojiImageView view = (EmojiImageView) child; + + if (view.getY() >= bottomBound || view.getY() + view.getHeight() <= topBound) { + continue; + } + + int top = smoothScrolling ? (int) view.getY() : view.getTop(); + ArrayList arrayList = viewsGroupedByLines.get(top); + + if (arrayList == null) { + if (!unusedArrays.isEmpty()) { + arrayList = unusedArrays.remove(unusedArrays.size() - 1); + } else { + arrayList = new ArrayList<>(); + } + viewsGroupedByLines.put(top, arrayList); + } + arrayList.add(view); + } + } + + lineDrawablesTmp.clear(); + lineDrawablesTmp.addAll(lineDrawables); + lineDrawables.clear(); + + canvas.save(); + canvas.clipRect(0, getPaddingTop(), getWidth(), getHeight() - getPaddingBottom()); + + long time = System.currentTimeMillis(); + for (int i = 0; i < viewsGroupedByLines.size(); i++) { + ArrayList arrayList = viewsGroupedByLines.valueAt(i); + EmojiImageView firstView = arrayList.get(0); + int position = getChildAdapterPosition(firstView); + DrawingInBackgroundLine drawable = null; + for (int k = 0; k < lineDrawablesTmp.size(); k++) { + if (lineDrawablesTmp.get(k).position == position) { + drawable = lineDrawablesTmp.get(k); + lineDrawablesTmp.remove(k); + break; + } + } + if (drawable == null) { + if (!unusedLineDrawables.isEmpty()) { + drawable = unusedLineDrawables.remove(unusedLineDrawables.size() - 1); + } else { + drawable = new DrawingInBackgroundLine(); + drawable.setLayerNum(7); + } + drawable.position = position; + drawable.onAttachToWindow(); + } + lineDrawables.add(drawable); + drawable.imageViewEmojis = arrayList; + canvas.save(); + canvas.translate(firstView.getLeft(), firstView.getY()/* + firstView.getPaddingTop()*/); + drawable.startOffset = firstView.getLeft(); + int w = getMeasuredWidth() - firstView.getLeft() * 2; + int h = firstView.getMeasuredHeight(); + if (w > 0 && h > 0) { + drawable.draw(canvas, time, w, h, getAlpha()); + } + canvas.restore(); + } + + for (int i = 0; i < lineDrawablesTmp.size(); i++) { + if (unusedLineDrawables.size() < 3) { + unusedLineDrawables.add(lineDrawablesTmp.get(i)); + lineDrawablesTmp.get(i).imageViewEmojis = null; + lineDrawablesTmp.get(i).reset(); + } else { + lineDrawablesTmp.get(i).onDetachFromWindow(); + } + } + lineDrawablesTmp.clear(); + + for (int i = 0; i < getChildCount(); ++i) { + View child = getChildAt(i); + if (child != null && !(child instanceof EmojiImageView)) { + + if (child.getY() > getHeight() - getPaddingBottom() || child.getY() + child.getHeight() < getPaddingTop()) { + continue; + } + + canvas.save(); + canvas.translate((int) child.getX(), (int) child.getY()); + child.draw(canvas); + canvas.restore(); + } + } + + canvas.restore(); + + canvas.restoreToCount(restoreTo); + } + + private final ColorFilter whiteFilter = new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN); + + public class DrawingInBackgroundLine extends DrawingInBackgroundThreadDrawable { + + public int position; + public int startOffset; + ArrayList imageViewEmojis; + ArrayList drawInBackgroundViews = new ArrayList<>(); + + boolean lite = LiteMode.isEnabled(LiteMode.FLAG_ANIMATED_EMOJI_REACTIONS); + + @Override + public void draw(Canvas canvas, long time, int w, int h, float alpha) { + if (imageViewEmojis == null) { + return; + } + boolean drawInUi = isAnimating() || imageViewEmojis.size() <= 4 || !lite; + if (!drawInUi) { + for (int i = 0; i < imageViewEmojis.size(); ++i) { + if (imageViewEmojis.get(i).getScale() != 1) { + drawInUi = true; + break; + } + } + } + if (drawInUi) { + prepareDraw(System.currentTimeMillis()); + drawInUiThread(canvas, alpha); + reset(); + } else { + super.draw(canvas, time, w, h, alpha); + } + } + + @Override + public void prepareDraw(long time) { + drawInBackgroundViews.clear(); + for (int i = 0; i < imageViewEmojis.size(); i++) { + EmojiImageView imageView = imageViewEmojis.get(i); + if (imageView.notDraw) { + continue; + } + ImageReceiver imageReceiver = imageView.drawable != null ? imageView.drawable.getImageReceiver() : imageView.imageReceiver; + if (imageReceiver == null) { + continue; + } + imageReceiver.setAlpha(imageView.getAlpha()); + if (imageView.drawable != null) { + imageView.drawable.setColorFilter(whiteFilter); + } + imageView.backgroundThreadDrawHolder[threadIndex] = imageReceiver.setDrawInBackgroundThread(imageView.backgroundThreadDrawHolder[threadIndex], threadIndex); + imageView.backgroundThreadDrawHolder[threadIndex].time = time; + imageView.imageReceiverToDraw = imageReceiver; + + imageView.update(time); + + int topOffset = 0; // (int) (imageView.getHeight() * .03f); +// int w = imageView.getWidth() - imageView.getPaddingLeft() - imageView.getPaddingRight(); +// int h = imageView.getHeight() - imageView.getPaddingTop() - imageView.getPaddingBottom(); + AndroidUtilities.rectTmp2.set(imageView.getPaddingLeft(), imageView.getPaddingTop(), imageView.getWidth() - imageView.getPaddingRight(), imageView.getHeight() - imageView.getPaddingBottom()); + if (imageReceiver != null) { + final float aspectRatio = getAspectRatio(imageReceiver); + if (aspectRatio < 0) { + final float w = AndroidUtilities.rectTmp2.height() * aspectRatio; + final int left = (int) (AndroidUtilities.rectTmp2.centerX() - w / 2); + final int right = (int) (AndroidUtilities.rectTmp2.centerX() + w / 2); + AndroidUtilities.rectTmp2.left = left; + AndroidUtilities.rectTmp2.right = right; + } else if (aspectRatio > 1) { + final float h = AndroidUtilities.rectTmp2.width() / aspectRatio; + final int top = (int) (AndroidUtilities.rectTmp2.centerY() - h / 2); + final int bottom = (int) (AndroidUtilities.rectTmp2.centerY() + h / 2); + AndroidUtilities.rectTmp2.top = top; + AndroidUtilities.rectTmp2.bottom = bottom; + } + } + AndroidUtilities.rectTmp2.offset(imageView.getLeft() + (int) imageView.getTranslationX() - startOffset, topOffset); + imageView.backgroundThreadDrawHolder[threadIndex].setBounds(AndroidUtilities.rectTmp2); + + drawInBackgroundViews.add(imageView); + } + } + + private float getAspectRatio(ImageReceiver imageReceiver) { + if (imageReceiver == null) { + return 1f; + } + RLottieDrawable rLottieDrawable = imageReceiver.getLottieAnimation(); + if (rLottieDrawable != null && rLottieDrawable.getIntrinsicHeight() != 0) { + return (float) rLottieDrawable.getIntrinsicWidth() / rLottieDrawable.getIntrinsicHeight(); + } + AnimatedFileDrawable animatedDrawable = imageReceiver.getAnimation(); + if (animatedDrawable != null && animatedDrawable.getIntrinsicHeight() != 0) { + return (float) animatedDrawable.getIntrinsicWidth() / (float) animatedDrawable.getIntrinsicHeight(); + } + Bitmap bitmap = imageReceiver.getBitmap(); + if (bitmap != null) { + return (float) bitmap.getWidth() / (float) bitmap.getHeight(); + } + Drawable thumbDrawable = imageReceiver.getStaticThumb(); + if (thumbDrawable != null && thumbDrawable.getIntrinsicHeight() != 0) { + return (float) thumbDrawable.getIntrinsicWidth() / (float) thumbDrawable.getIntrinsicHeight(); + } + return 1f; + } + + @Override + public void drawInBackground(Canvas canvas) { + for (int i = 0; i < drawInBackgroundViews.size(); i++) { + EmojiImageView imageView = drawInBackgroundViews.get(i); + if (!imageView.notDraw) { + if (imageView.drawable != null) { + imageView.drawable.setColorFilter(whiteFilter); + } + imageView.imageReceiverToDraw.draw(canvas, imageView.backgroundThreadDrawHolder[threadIndex]); + } + } + } + + @Override + protected void drawInUiThread(Canvas canvas, float palpha) { + if (imageViewEmojis != null) { + canvas.save(); + canvas.translate(-startOffset, 0); + for (int i = 0; i < imageViewEmojis.size(); i++) { + EmojiImageView imageView = imageViewEmojis.get(i); + if (imageView.notDraw) { + continue; + } + + float scale = imageView.getScale(); + float alpha = palpha * imageView.getAlpha(); + + AndroidUtilities.rectTmp2.set((int) imageView.getX() + imageView.getPaddingLeft(), imageView.getPaddingTop(), (int) imageView.getX() + imageView.getWidth() - imageView.getPaddingRight(), imageView.getHeight() - imageView.getPaddingBottom()); +// if (!smoothScrolling && !animatedExpandIn) { +// AndroidUtilities.rectTmp2.offset(0, (int) imageView.getTranslationY()); +// } + Drawable drawable = imageView.drawable; + if (drawable != null) { + drawable.setBounds(AndroidUtilities.rectTmp2); + } + if (imageView.imageReceiver != null) { + imageView.imageReceiver.setImageCoords(AndroidUtilities.rectTmp2); + } + if (whiteFilter != null && imageView.drawable instanceof AnimatedEmojiDrawable) { + imageView.drawable.setColorFilter(whiteFilter); + } + if (scale != 1) { + canvas.save(); + canvas.scale(scale, scale, AndroidUtilities.rectTmp2.centerX(), AndroidUtilities.rectTmp2.centerY()); + drawImage(canvas, drawable, imageView, alpha); + canvas.restore(); + } else { + drawImage(canvas, drawable, imageView, alpha); + } + } + canvas.restore(); + } + } + + private void drawImage(Canvas canvas, Drawable drawable, EmojiImageView imageView, float alpha) { + if (drawable != null) { + drawable.setAlpha((int) (255 * alpha)); + drawable.draw(canvas); +// drawable.setColorFilter(premiumStarColorFilter); + } else if (imageView.imageReceiver != null) { + canvas.save(); + canvas.clipRect(imageView.imageReceiver.getImageX(), imageView.imageReceiver.getImageY(), imageView.imageReceiver.getImageX2(), imageView.imageReceiver.getImageY2()); + imageView.imageReceiver.setAlpha(alpha); + imageView.imageReceiver.draw(canvas); + canvas.restore(); + } + } + + @Override + public void onFrameReady() { + super.onFrameReady(); + for (int i = 0; i < drawInBackgroundViews.size(); i++) { + EmojiImageView imageView = drawInBackgroundViews.get(i); + if (imageView.backgroundThreadDrawHolder[threadIndex] != null) { + imageView.backgroundThreadDrawHolder[threadIndex].release(); + } + } + EmojiListView.this.invalidate(); + } + } + + } + + private static class SearchField extends FrameLayout { + + private final Theme.ResourcesProvider resourcesProvider; + + private final FrameLayout box; + + private final ImageView searchImageView; + private final SearchStateDrawable searchImageDrawable; + + private final FrameLayout inputBox; + private final EditTextBoldCursor editText; + private final StickerCategoriesListView categoriesListView; + + public boolean ignoreTextChange; + + public SearchField(Context context, Theme.ResourcesProvider resourcesProvider) { + super(context); + this.resourcesProvider = resourcesProvider; + + box = new FrameLayout(context); + box.setBackground(Theme.createRoundRectDrawable(dp(18), Theme.getColor(Theme.key_chat_emojiSearchBackground, resourcesProvider))); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + box.setClipToOutline(true); + box.setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), (int) dp(18)); + } + }); + } + addView(box, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 36, Gravity.FILL, 10, 6, 10, 8)); + + inputBox = new FrameLayout(context); + box.addView(inputBox, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 40, Gravity.LEFT | Gravity.TOP, 38, 0, 0, 0)); + + searchImageView = new ImageView(context); + searchImageView.setScaleType(ImageView.ScaleType.CENTER); + searchImageDrawable = new SearchStateDrawable(); + searchImageDrawable.setIconState(SearchStateDrawable.State.STATE_SEARCH, false); + searchImageDrawable.setColor(Theme.getColor(Theme.key_chat_emojiSearchIcon, resourcesProvider)); + searchImageView.setImageDrawable(searchImageDrawable); + box.addView(searchImageView, LayoutHelper.createFrame(36, 36, Gravity.LEFT | Gravity.TOP)); + + editText = new EditTextBoldCursor(context) { + @Override + public boolean onTouchEvent(MotionEvent event) { + if (!editText.isEnabled()) { + return super.onTouchEvent(event); + } + if (event.getAction() == MotionEvent.ACTION_DOWN) { + // todo: open search + editText.requestFocus(); + AndroidUtilities.showKeyboard(editText); + } + return super.onTouchEvent(event); + } + }; + editText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + editText.setHintTextColor(Theme.getColor(Theme.key_chat_emojiSearchIcon, resourcesProvider)); + editText.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); + editText.setBackgroundDrawable(null); + editText.setPadding(0, 0, 0, 0); + editText.setMaxLines(1); + editText.setLines(1); + editText.setSingleLine(true); + editText.setImeOptions(EditorInfo.IME_ACTION_SEARCH | EditorInfo.IME_FLAG_NO_EXTRACT_UI); + editText.setHint(LocaleController.getString("Search", R.string.Search)); + editText.setCursorColor(Theme.getColor(Theme.key_featuredStickers_addedIcon, resourcesProvider)); + editText.setHandlesColor(Theme.getColor(Theme.key_featuredStickers_addedIcon, resourcesProvider)); + editText.setCursorSize(dp(20)); + editText.setCursorWidth(1.5f); + editText.setTranslationY(dp(-2)); + inputBox.addView(editText, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 40, Gravity.LEFT | Gravity.TOP, 0, 0, 28, 0)); + editText.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + @Override + public void afterTextChanged(Editable s) { + if (ignoreTextChange) { + return; + } + updateButton(); + final String query = editText.getText().toString(); + search(TextUtils.isEmpty(query) ? null : query, -1); + if (categoriesListView != null) { + categoriesListView.selectCategory(null); + categoriesListView.updateCategoriesShown(TextUtils.isEmpty(query), true); + } + if (editText != null) { + editText.animate().cancel(); + editText.animate().translationX(0).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).start(); + } + } + }); + + categoriesListView = new StickerCategoriesListView(context, null, StickerCategoriesListView.CategoriesType.DEFAULT, resourcesProvider) { + @Override + public void selectCategory(int categoryIndex) { + super.selectCategory(categoryIndex); +// if (type == 1 && emojiTabs != null) { +// emojiTabs.showSelected(categoriesListView.getSelectedCategory() == null); +// } else if (type == 0 && stickersTab != null) { +// stickersTab.showSelected(categoriesListView.getSelectedCategory() == null); +// } + updateButton(); + } + + @Override + protected boolean isTabIconsAnimationEnabled(boolean loaded) { + return LiteMode.isEnabled(LiteMode.FLAG_ANIMATED_EMOJI_REACTIONS); + } + }; + categoriesListView.setDontOccupyWidth((int) (editText.getPaint().measureText(editText.getHint() + "")) + dp(16)); + categoriesListView.setOnScrollIntoOccupiedWidth(scrolled -> { + editText.animate().cancel(); + editText.setTranslationX(-Math.max(0, scrolled)); +// showInputBoxGradient(scrolled > 0); + updateButton(); + }); +// categoriesListView.setOnTouchListener(new OnTouchListener() { +// @Override +// public boolean onTouch(View v, MotionEvent event) { +//// if (event.getAction() == MotionEvent.ACTION_DOWN) { +//// ignorePagerScroll = true; +//// } else if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL) { +//// ignorePagerScroll = false; +//// } +// return false; +// } +// }); + categoriesListView.setOnCategoryClick(category -> { +// if (category == recent) { +//// showInputBoxGradient(false); +// categoriesListView.selectCategory(recent); +// gifSearchField.searchEditText.setText(""); +// gifLayoutManager.scrollToPositionWithOffset(0, 0); +// return; +// } else +// if (category == trending) { +//// showInputBoxGradient(false); +// gifSearchField.searchEditText.setText(""); +// gifLayoutManager.scrollToPositionWithOffset(gifAdapter.trendingSectionItem, -dp(4)); +// categoriesListView.selectCategory(trending); +// final ArrayList gifSearchEmojies = MessagesController.getInstance(currentAccount).gifSearchEmojies; +// if (!gifSearchEmojies.isEmpty()) { +// gifSearchPreloader.preload(gifSearchEmojies.get(0)); +// } +// return; +// } + if (categoriesListView.getSelectedCategory() == category) { + categoriesListView.selectCategory(null); + search(null, -1); + } else { + categoriesListView.selectCategory(category); + search(category.emojis, categoriesListView.getCategoryIndex()); + } + }); + box.addView(categoriesListView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 36, Gravity.LEFT | Gravity.TOP, 36, 0, 0, 0)); + + searchImageView.setOnClickListener(e -> { + if (searchImageDrawable.getIconState() == SearchStateDrawable.State.STATE_BACK) { + clear(); + categoriesListView.scrollToStart(); + } else if (searchImageDrawable.getIconState() == SearchStateDrawable.State.STATE_SEARCH) { + editText.requestFocus(); + } + }); + } + + private void updateButton() { + updateButton(false); + } + + private boolean isprogress; + public void showProgress(boolean progress) { + isprogress = progress; + if (progress) { + searchImageDrawable.setIconState(SearchStateDrawable.State.STATE_PROGRESS); + } else { + updateButton(true); + } + } + + private void updateButton(boolean force) { + if (!isprogress || editText.length() == 0 && (categoriesListView == null || categoriesListView.getSelectedCategory() == null) || force) { + boolean backButton = editText.length() > 0 || categoriesListView != null && categoriesListView.isCategoriesShown() && (categoriesListView.isScrolledIntoOccupiedWidth() || categoriesListView.getSelectedCategory() != null); + searchImageDrawable.setIconState(backButton ? SearchStateDrawable.State.STATE_BACK : SearchStateDrawable.State.STATE_SEARCH); + isprogress = false; + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure( + MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(50), MeasureSpec.EXACTLY) + ); + } + + private Utilities.Callback2 onSearchQuery; + public void setOnSearchQuery(Utilities.Callback2 onSearchQuery) { + this.onSearchQuery = onSearchQuery; + } + + private void search(String query, int categoryIndex) { + if (onSearchQuery != null) { + onSearchQuery.run(query, categoryIndex); + } + } + + private void clear() { + editText.setText(""); + search(null, -1); + categoriesListView.selectCategory(null); + } + } + + private static class TabsView extends View { + + private final TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + private final Paint selectPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + private StaticLayout emojiLayout; + private float emojiLayoutWidth, emojiLayoutLeft; + private StaticLayout stickersLayout; + private float stickersLayoutWidth, stickersLayoutLeft; + private StaticLayout masksLayout; + private float masksLayoutWidth, masksLayoutLeft; + + private final RectF emojiRect = new RectF(); + private final RectF stickersRect = new RectF(); + private final RectF masksRect = new RectF(); + private final RectF selectRect = new RectF(); + + public TabsView(Context context) { + super(context); + } + + private float type; + public void setType(float type) { + this.type = type; + invalidate(); + } + + private Utilities.Callback onTypeSelected; + public void setOnTypeSelected(Utilities.Callback listener) { + onTypeSelected = listener; + } + + private int lastWidth; + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int width = MeasureSpec.getSize(widthMeasureSpec); + setMeasuredDimension(width, dp(40) + AndroidUtilities.navigationBarHeight); + if (getMeasuredWidth() != lastWidth || emojiLayout == null) { + updateLayouts(); + } + lastWidth = getMeasuredWidth(); + } + + private void updateLayouts() { + textPaint.setTextSize(dp(14)); + textPaint.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + + emojiLayout = new StaticLayout(LocaleController.getString("Emoji"), textPaint, getMeasuredWidth(), Layout.Alignment.ALIGN_NORMAL, 1, 0, false); + emojiLayoutWidth = emojiLayout.getLineCount() >= 1 ? emojiLayout.getLineWidth(0) : 0; + emojiLayoutLeft = emojiLayout.getLineCount() >= 1 ? emojiLayout.getLineLeft(0) : 0; + + stickersLayout = new StaticLayout(LocaleController.getString("AccDescrStickers"), textPaint, getMeasuredWidth(), Layout.Alignment.ALIGN_NORMAL, 1, 0, false); + stickersLayoutWidth = stickersLayout.getLineCount() >= 1 ? stickersLayout.getLineWidth(0) : 0; + stickersLayoutLeft = stickersLayout.getLineCount() >= 1 ? stickersLayout.getLineLeft(0) : 0; + +// masksLayout = new StaticLayout(LocaleController.getString("Masks"), textPaint, getMeasuredWidth(), Layout.Alignment.ALIGN_NORMAL, 1, 0, false); +// masksLayoutWidth = masksLayout.getLineCount() >= 1 ? masksLayout.getLineWidth(0) : 0; +// masksLayoutLeft = masksLayout.getLineCount() >= 1 ? masksLayout.getLineLeft(0) : 0; +// +// float w = dp(12) + emojiLayoutWidth + dp(12 + 12 + 12) + stickersLayoutWidth + dp(12 + 12 + 12) + masksLayoutWidth + dp(12); + float w = dp(12) + emojiLayoutWidth + dp(12 + 12 + 12) + stickersLayoutWidth + dp(12); + float t = dp(40 - 26) / 2f, b = dp(40 + 26) / 2f; + float l = (getMeasuredWidth() - w) / 2f; + + emojiRect.set(l, t, l + emojiLayoutWidth + dp(12 + 12), b); + l += emojiLayoutWidth + dp(12 + 12 + 12); + + stickersRect.set(l, t, l + stickersLayoutWidth + dp(12 + 12), b); + l += stickersLayoutWidth + dp(12 + 12 + 12); + +// masksLayout.set(l, t, l + masksLayoutWidth + dp(12 + 12), b); +// l += masksLayoutWidth + dp(12 + 12 + 12); + } + + private RectF getRect(int t) { + if (t <= 0) { + return emojiRect; + } else { // if (t == 1) + return stickersRect; + } +// else { +// return masksRect; +// } + } + + @Override + protected void dispatchDraw(Canvas canvas) { + canvas.drawColor(0xFF1F1F1F); + selectPaint.setColor(0xFF363636); + + lerp(getRect((int) type), getRect((int) Math.ceil(type)), type - (int) type, selectRect); + canvas.drawRoundRect(selectRect, dp(20), dp(20), selectPaint); + + if (emojiLayout != null) { + canvas.save(); + canvas.translate(emojiRect.left + dp(12) - emojiLayoutLeft, emojiRect.top + (emojiRect.height() - emojiLayout.getHeight()) / 2f); + textPaint.setColor(ColorUtils.blendARGB(0xFF838383, 0xFFFFFFFF, Utilities.clamp(1f - Math.abs(type - 0), 1, 0))); + emojiLayout.draw(canvas); + canvas.restore(); + } + + if (stickersLayout != null) { + canvas.save(); + canvas.translate(stickersRect.left + dp(12) - stickersLayoutLeft, stickersRect.top + (stickersRect.height() - stickersLayout.getHeight()) / 2f); + textPaint.setColor(ColorUtils.blendARGB(0xFF838383, 0xFFFFFFFF, Utilities.clamp(1f - Math.abs(type - 1), 1, 0))); + stickersLayout.draw(canvas); + canvas.restore(); + } + + if (masksLayout != null) { + canvas.save(); + canvas.translate(masksRect.left + dp(12) - masksLayoutLeft, masksRect.top + (masksRect.height() - masksLayout.getHeight()) / 2f); + textPaint.setColor(ColorUtils.blendARGB(0xFF838383, 0xFFFFFFFF, Utilities.clamp(1f - Math.abs(type - 2), 1, 0))); + masksLayout.draw(canvas); + canvas.restore(); + } + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + return true; + } else if (event.getAction() == MotionEvent.ACTION_UP && onTypeSelected != null) { + if (emojiRect.contains(event.getX(), event.getY())) { + onTypeSelected.run(0); + } else if (stickersRect.contains(event.getX(), event.getY())) { + onTypeSelected.run(1); + } + return true; + } + return super.onTouchEvent(event); + } + } + + private static class NoEmojiView extends FrameLayout { + + BackupImageView imageView; + TextView textView; + + public NoEmojiView(Context context, boolean emoji) { + super(context); + + imageView = new BackupImageView(context); + addView(imageView, LayoutHelper.createFrame(36, 36, Gravity.CENTER)); + + textView = new TextView(context); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + textView.setTextColor(-8553090); // Theme.getColor(Theme.key_chat_emojiPanelEmptyText, resourcesProvider) + textView.setText(emoji ? LocaleController.getString("NoEmojiFound", R.string.NoEmojiFound) : LocaleController.getString("NoStickersFound", R.string.NoStickersFound)); + addView(textView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 0, 18 + 16, 0, 0)); + } + + private int lastI = -1; + public void update(int i) { + if (lastI != i) { + lastI = i; + update(); + } + } + + public void update() { + SelectAnimatedEmojiDialog.updateSearchEmptyViewImage(UserConfig.selectedAccount, imageView); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure( + MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec((int) Math.max(AndroidUtilities.dp(170), AndroidUtilities.displaySize.y * (1f - .35f - .3f) - dp(50 + 16 + 36 + 40)), MeasureSpec.EXACTLY) + ); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/GalleryListView.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/GalleryListView.java new file mode 100644 index 0000000000..a9533642a3 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/GalleryListView.java @@ -0,0 +1,1161 @@ +package org.telegram.ui.Stories.recorder; + +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.AndroidUtilities.dpf2; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.content.ContentUris; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.LinearGradient; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.Rect; +import android.graphics.Shader; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; +import android.net.Uri; +import android.os.Build; +import android.provider.MediaStore; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.text.TextUtils; +import android.util.Log; +import android.util.LruCache; +import android.util.Pair; +import android.util.Size; +import android.util.TypedValue; +import android.view.GestureDetector; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.animation.OvershootInterpolator; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.LinearSmoothScrollerCustom; +import androidx.recyclerview.widget.RecyclerView; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.DispatchQueue; +import org.telegram.messenger.DispatchQueuePool; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.ImageLocation; +import org.telegram.messenger.ImageReceiver; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MediaController; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.R; +import org.telegram.messenger.Utilities; +import org.telegram.ui.ActionBar.ActionBar; +import org.telegram.ui.ActionBar.ActionBarMenu; +import org.telegram.ui.ActionBar.ActionBarMenuItem; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.BackupImageView; +import org.telegram.ui.Components.CombinedDrawable; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.DrawingInBackgroundThreadDrawable; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.RecyclerListView; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; + +public class GalleryListView extends FrameLayout implements NotificationCenter.NotificationCenterDelegate { + + private final int currentAccount; + private Theme.ResourcesProvider resourcesProvider; + private final Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + public final RecyclerListView listView; + public final GridLayoutManager layoutManager; + public final Adapter adapter; + + private boolean actionBarShown; + private final ActionBar actionBar; + + private final TextView dropDown; + private final Drawable dropDownDrawable; + private final ActionBarMenuItem dropDownContainer; + + public boolean ignoreScroll; + public final boolean onlyPhotos; + + public GalleryListView(int currentAccount, Context context, Theme.ResourcesProvider resourcesProvider, MediaController.AlbumEntry startAlbum, boolean onlyPhotos) { + super(context); + this.currentAccount = currentAccount; + this.resourcesProvider = resourcesProvider; + this.onlyPhotos = onlyPhotos; + + backgroundPaint.setColor(0xff1f1f1f); + backgroundPaint.setShadowLayer(dp(2.33f), 0, dp(-.4f), 0x08000000); + + listView = new RecyclerListView(context, resourcesProvider) { + @Override + public boolean onInterceptTouchEvent(MotionEvent e) { + if (ignoreScroll) { + return false; + } + return super.onInterceptTouchEvent(e); + } + + @Override + public boolean dispatchTouchEvent(MotionEvent e) { + if (ignoreScroll) { + return false; + } + return super.dispatchTouchEvent(e); + } + }; + listView.setItemSelectorColorProvider(pos -> 0); + listView.setAdapter(adapter = new Adapter()); + listView.setLayoutManager(layoutManager = new GridLayoutManager(context, 3) { + @Override + public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { + super.onLayoutChildren(recycler, state); + if (firstLayout) { + firstLayout = false; + firstLayout(); + } + } + }); + listView.setFastScrollEnabled(RecyclerListView.FastScroll.DATE_TYPE); + listView.setFastScrollVisible(true); + listView.getFastScroll().setAlpha(0f); +// listView.getFastScroll().usePadding = false; + layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { + @Override + public int getSpanSize(int position) { + return position == 0 || position == 1 || position == adapter.getItemCount() - 1 ? 3 : 1; + } + }); + listView.addItemDecoration(new RecyclerView.ItemDecoration() { + @Override + public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { + outRect.bottom = outRect.right = dp(5); + } + }); + listView.setClipToPadding(false); + addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); + listView.setOnItemClickListener((view, position) -> { + if (position < 2 || onSelectListener == null || !(view instanceof Cell)) { + return; + } + Cell cell = (Cell) view; + int index = position - 2; + if (containsDraftFolder) { + if (index == 0) { + selectAlbum(draftsAlbum, true); + return; + } + index--; + } else if (containsDrafts) { + if (index >= 0 && index < drafts.size()) { + StoryEntry entry = drafts.get(index); + onSelectListener.run(entry, entry.isVideo ? prepareBlurredThumb(cell) : null); + return; + } + index -= drafts.size(); + } + + if (index >= 0 && index < selectedPhotos.size()) { + MediaController.PhotoEntry entry = selectedPhotos.get(index); + onSelectListener.run(entry, entry.isVideo ? prepareBlurredThumb(cell) : null); + } + }); + listView.setOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { + onScroll(); + invalidate(); + } + }); + + actionBar = new ActionBar(context, resourcesProvider); + actionBar.setBackgroundColor(0xff1f1f1f); + actionBar.setTitleColor(0xFFFFFFFF); + actionBar.setAlpha(0f); + actionBar.setVisibility(View.GONE); + actionBar.setBackButtonImage(R.drawable.ic_ab_back); + actionBar.setItemsBackgroundColor(436207615, false); + actionBar.setItemsColor(0xFFFFFFFF, false); + addView(actionBar, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.FILL_HORIZONTAL)); + actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { + @Override + public void onItemClick(int id) { + if (id == -1) { + if (onBackClickListener != null) { + onBackClickListener.run(); + } + } else if (id >= 10) { + selectAlbum(dropDownAlbums.get(id - 10), false); + } + } + }); + + ActionBarMenu menu = actionBar.createMenu(); + dropDownContainer = new ActionBarMenuItem(context, menu, 0, 0, resourcesProvider) { + @Override + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(info); + info.setText(dropDown.getText()); + } + }; + dropDownContainer.setSubMenuOpenSide(1); + actionBar.addView(dropDownContainer, 0, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT, AndroidUtilities.isTablet() ? 64 : 56, 0, 40, 0)); + dropDownContainer.setOnClickListener(view -> dropDownContainer.toggleSubMenu()); + + dropDown = new TextView(context); + dropDown.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); + dropDown.setGravity(Gravity.LEFT); + dropDown.setSingleLine(true); + dropDown.setLines(1); + dropDown.setMaxLines(1); + dropDown.setEllipsize(TextUtils.TruncateAt.END); + dropDown.setTextColor(0xFFFFFFFF); + dropDown.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + dropDownDrawable = context.getResources().getDrawable(R.drawable.ic_arrow_drop_down).mutate(); + dropDownDrawable.setColorFilter(new PorterDuffColorFilter(0xFFFFFFFF, PorterDuff.Mode.MULTIPLY)); + dropDown.setCompoundDrawablePadding(AndroidUtilities.dp(4)); + dropDown.setPadding(0, AndroidUtilities.statusBarHeight, AndroidUtilities.dp(10), 0); + dropDownContainer.addView(dropDown, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL, 16, 0, 0, 0)); + + drafts.clear(); + if (!onlyPhotos) { + drafts.addAll(MessagesController.getInstance(currentAccount).getStoriesController().getDraftsController().drafts); + } + + updateAlbumsDropDown(); + if (startAlbum != null && (startAlbum != draftsAlbum || drafts.size() > 0)) { + selectedAlbum = startAlbum; + } else { + if (dropDownAlbums == null || dropDownAlbums.isEmpty()) { + selectedAlbum = MediaController.allMediaAlbumEntry; + } else { + selectedAlbum = dropDownAlbums.get(0); + } + } + selectedPhotos = getPhotoEntries(selectedAlbum); + updateContainsDrafts(); + if (selectedAlbum == MediaController.allMediaAlbumEntry) { + dropDown.setText(LocaleController.getString("ChatGallery", R.string.ChatGallery)); + } else if (selectedAlbum == draftsAlbum) { + dropDown.setText(LocaleController.getString("StoryDraftsAlbum")); + } else { + dropDown.setText(selectedAlbum.bucketName); + } + } + + private ArrayList getPhotoEntries(MediaController.AlbumEntry album) { + if (album == null) { + return new ArrayList<>(); + } + if (!onlyPhotos) { + return album.photos; + } + ArrayList photos = new ArrayList<>(); + for (int i = 0; i < album.photos.size(); ++i) { + MediaController.PhotoEntry entry = album.photos.get(i); + if (!entry.isVideo) { + photos.add(entry); + } + } + return photos; + } + + private final AnimatedFloat actionBarT = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + + @Override + protected void dispatchDraw(Canvas canvas) { + float top = top(); + final boolean shouldActionBarBeShown = top <= Math.max(0, AndroidUtilities.statusBarHeight + ActionBar.getCurrentActionBarHeight() - AndroidUtilities.dp(32)); + float actionBarT = this.actionBarT.set(shouldActionBarBeShown); + top = AndroidUtilities.lerp(top, 0, actionBarT); + if (shouldActionBarBeShown != actionBarShown) { + onFullScreen(actionBarShown = shouldActionBarBeShown); + listView.getFastScroll().animate().alpha(actionBarShown ? 1f : 0).start(); + } + if (actionBar != null) { + actionBar.setAlpha(actionBarT); + int visibility = actionBarT <= 0 ? View.GONE : View.VISIBLE; + if (actionBar.getVisibility() != visibility) { + actionBar.setVisibility(visibility); + } + } + if (headerView != null) { + headerView.setAlpha(1f - actionBarT); + } + AndroidUtilities.rectTmp.set(0, top, getWidth(), getHeight() + dp(14)); + canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(14), dp(14), backgroundPaint); + canvas.save(); + canvas.clipRect(0, top, getWidth(), getHeight()); + super.dispatchDraw(canvas); + canvas.restore(); + } + + public MediaController.AlbumEntry getSelectedAlbum() { + return selectedAlbum; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + listView.setPinnedSectionOffsetY(AndroidUtilities.statusBarHeight + ActionBar.getCurrentActionBarHeight()); + listView.setPadding(dp(6), AndroidUtilities.statusBarHeight + ActionBar.getCurrentActionBarHeight(), dp(1), AndroidUtilities.navigationBarHeight); + dropDown.setPadding(0, AndroidUtilities.statusBarHeight, AndroidUtilities.dp(10), 0); + dropDown.setTextSize(!AndroidUtilities.isTablet() && AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y ? 18 : 20); + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + private Bitmap prepareBlurredThumb(Cell cell) { + Bitmap bitmap = cell.bitmap; + if (bitmap != null && !bitmap.isRecycled()) { + return Utilities.stackBlurBitmapWithScaleFactor(bitmap, 6f); + } + return null; + } + + public boolean firstLayout = true; + protected void firstLayout() {} + protected void onScroll() {} + protected void onFullScreen(boolean isFullscreen) {} + + private Runnable onBackClickListener; + public void setOnBackClickListener(Runnable onBackClickListener) { + this.onBackClickListener = onBackClickListener; + } + + private Utilities.Callback2 onSelectListener; + public void setOnSelectListener(Utilities.Callback2 onSelectListener) { + this.onSelectListener = onSelectListener; + } + + private static final MediaController.AlbumEntry draftsAlbum = new MediaController.AlbumEntry(-1, null, null); + private final ArrayList drafts = new ArrayList<>(); + private boolean containsDraftFolder, containsDrafts; + + public MediaController.AlbumEntry selectedAlbum; + public ArrayList selectedPhotos; + private ArrayList dropDownAlbums; + + private void updateAlbumsDropDown() { + dropDownContainer.removeAllSubItems(); + ArrayList albums = MediaController.allMediaAlbums; + dropDownAlbums = new ArrayList<>(albums); + Collections.sort(dropDownAlbums, (o1, o2) -> { + if (o1.bucketId == 0 && o2.bucketId != 0) { + return -1; + } else if (o1.bucketId != 0 && o2.bucketId == 0) { + return 1; + } + int index1 = albums.indexOf(o1); + int index2 = albums.indexOf(o2); + if (index1 > index2) { + return 1; + } else if (index1 < index2) { + return -1; + } else { + return 0; + } + }); + if (!drafts.isEmpty()) { + dropDownAlbums.add(dropDownAlbums.isEmpty() ? 0 : 1, draftsAlbum); + } + if (dropDownAlbums.isEmpty()) { + dropDown.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null); + } else { + dropDown.setCompoundDrawablesWithIntrinsicBounds(null, null, dropDownDrawable, null); + for (int a = 0, N = dropDownAlbums.size(); a < N; a++) { + MediaController.AlbumEntry album = dropDownAlbums.get(a); + AlbumButton button; + if (album == draftsAlbum) { + button = new AlbumButton(getContext(), album.coverPhoto, LocaleController.getString("StoryDraftsAlbum"), drafts.size(), resourcesProvider); + } else { + ArrayList photoEntries = getPhotoEntries(album); + if (photoEntries.isEmpty()) { + continue; + } + button = new AlbumButton(getContext(), album.coverPhoto, album.bucketName, photoEntries.size(), resourcesProvider); + } + dropDownContainer.getPopupLayout().addView(button); + button.setOnClickListener(e -> { + selectAlbum(album, false); + dropDownContainer.closeSubMenu(); + }); + } + } + } + + private void selectAlbum(MediaController.AlbumEntry album, boolean scrollAnimated) { + selectedAlbum = album; + selectedPhotos = getPhotoEntries(selectedAlbum); + updateContainsDrafts(); + if (selectedAlbum == MediaController.allMediaAlbumEntry) { + dropDown.setText(LocaleController.getString("ChatGallery", R.string.ChatGallery)); + } else if (selectedAlbum == draftsAlbum) { + dropDown.setText(LocaleController.getString("StoryDraftsAlbum")); + } else { + dropDown.setText(selectedAlbum.bucketName); + } + adapter.notifyDataSetChanged(); + if (scrollAnimated) { + LinearSmoothScrollerCustom linearSmoothScroller = new LinearSmoothScrollerCustom(getContext(), LinearSmoothScrollerCustom.POSITION_TOP); + linearSmoothScroller.setTargetPosition(1); + linearSmoothScroller.setOffset(-ActionBar.getCurrentActionBarHeight() + AndroidUtilities.dp(16)); + layoutManager.startSmoothScroll(linearSmoothScroller); + } else { + layoutManager.scrollToPositionWithOffset(1, -ActionBar.getCurrentActionBarHeight() + AndroidUtilities.dp(16)); + } + } + + private static final int VIEW_TYPE_PADDING = 0; + private static final int VIEW_TYPE_HEADER = 1; + private static final int VIEW_TYPE_ENTRY = 2; + + private static final float ASPECT_RATIO = 1.39f; + + private static class Cell extends View { + + private Bitmap bitmap; + private final Paint bitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + private final Paint bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint gradientPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private LinearGradient gradient; + private final Matrix bitmapMatrix = new Matrix(); + private final Matrix gradientMatrix = new Matrix(); + + private final Paint durationBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final TextPaint durationTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + private final TextPaint draftTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + private final Drawable durationPlayDrawable; + + private boolean drawDurationPlay; + private StaticLayout durationLayout; + private float durationLayoutWidth, durationLayoutLeft; + + private StaticLayout draftLayout; + private float draftLayoutWidth, draftLayoutLeft; + + public Cell(Context context) { + super(context); + + bgPaint.setColor(0x10ffffff); + + durationBackgroundPaint.setColor(0x4c000000); + durationTextPaint.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + durationTextPaint.setTextSize(dpf2(12.66f)); + durationTextPaint.setColor(0xffffffff); + draftTextPaint.setTextSize(AndroidUtilities.dp(11.33f)); + draftTextPaint.setColor(0xffffffff); + durationPlayDrawable = context.getResources().getDrawable(R.drawable.play_mini_video).mutate(); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + AndroidUtilities.cancelRunOnUIThread(unload); + if (currentObject != null) { + loadBitmap(currentObject); + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + AndroidUtilities.runOnUIThread(unload, 250); + } + + private final Runnable unload = () -> loadBitmap(null); + + public void set(StoryEntry storyEntry, int draftsCount) { + currentObject = storyEntry; + if (draftsCount > 0) { + setDraft(false); + setDuration(LocaleController.formatPluralString("StoryDrafts", draftsCount)); + drawDurationPlay = false; + } else { + setDraft(storyEntry != null && storyEntry.isDraft); + setDuration(storyEntry != null && storyEntry.isVideo ? AndroidUtilities.formatShortDuration((int) Math.max(0L, storyEntry.duration * (storyEntry.right - storyEntry.left) / 1000L)) : null); + } + loadBitmap(storyEntry); + } + + public void set(MediaController.PhotoEntry photoEntry) { + currentObject = photoEntry; + setDuration(photoEntry != null && photoEntry.isVideo ? AndroidUtilities.formatShortDuration(photoEntry.duration) : null); + setDraft(false); + loadBitmap(photoEntry); + invalidate(); + } + + private DispatchQueue myQueue; + private static ArrayList allQueues = new ArrayList<>(); + private static int allQueuesIndex; + + private static final HashMap bitmapsUseCounts = new HashMap<>(); + private static final LruCache bitmapsCache = new LruCache(45) { + @Override + protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) { + if (oldValue.isRecycled() || bitmapsUseCounts.containsKey(key)) { + return; + } + oldValue.recycle(); + } + }; + + private static Bitmap getBitmap(String key) { + if (key == null) { + return null; + } + Bitmap bitmap = bitmapsCache.get(key); + if (bitmap != null) { + Integer count = bitmapsUseCounts.get(key); + bitmapsUseCounts.put(key, count == null ? 1 : count + 1); + } + return bitmap; + } + + private static void releaseBitmap(String key) { + if (key == null) { + return; + } + Integer count = bitmapsUseCounts.get(key); + if (count != null) { + count--; + if (count <= 0) { + bitmapsUseCounts.remove(key); + } else { + bitmapsUseCounts.put(key, count); + } + } + } + + private static void putBitmap(String key, Bitmap bitmap) { + if (key == null || bitmap == null) { + return; + } + bitmapsCache.put(key, bitmap); + Integer count = bitmapsUseCounts.get(key); + if (count != null) { + bitmapsUseCounts.put(key, count + 1); + } else { + bitmapsUseCounts.put(key, 1); + } + } + + private static void releaseAllBitmaps() { + bitmapsUseCounts.clear(); + bitmapsCache.evictAll(); + } + + public static void cleanupQueues() { + releaseAllBitmaps(); + for (int i = 0; i < allQueues.size(); ++i) { + allQueues.get(i).cleanupQueue(); + allQueues.get(i).recycle(); + } + allQueues.clear(); + } + + private DispatchQueue getQueue() { + if (myQueue != null) { + return myQueue; + } + if (allQueues.size() < 4) { + allQueues.add(myQueue = new DispatchQueue("gallery_load_" + allQueues.size())); + } else { + allQueuesIndex++; + if (allQueuesIndex >= allQueues.size()) { + allQueuesIndex = 0; + } + myQueue = allQueues.get(allQueuesIndex); + } + return myQueue; + } + + private String currentKey; + private Object currentObject; + + private Pair getThumbnail(Object entry) { + if (entry == null) { + return null; + } + + int rw = (int) Math.min(AndroidUtilities.displaySize.x / 3f, dp(330)); + int rh = (int) (rw * ASPECT_RATIO); + + int[] colors = null; + Bitmap bitmap = null; + if (entry instanceof MediaController.PhotoEntry) { + MediaController.PhotoEntry photoEntry = (MediaController.PhotoEntry) entry; + + BitmapFactory.Options opts = new BitmapFactory.Options(); + opts.inJustDecodeBounds = true; + readBitmap(photoEntry, opts); + + StoryEntry.setupScale(opts, rw, rh); + opts.inPreferredConfig = Bitmap.Config.ARGB_8888; + opts.inDither = true; + opts.inJustDecodeBounds = false; + bitmap = readBitmap(photoEntry, opts); + + final boolean needGradient = bitmap != null && ((float) bitmap.getHeight() / bitmap.getWidth()) < ASPECT_RATIO; + if (needGradient) { + if (photoEntry.gradientTopColor == 0 && photoEntry.gradientBottomColor == 0 && bitmap != null && !bitmap.isRecycled()) { + colors = DominantColors.getColorsSync(true, bitmap, true); + photoEntry.gradientTopColor = colors[0]; + photoEntry.gradientBottomColor = colors[1]; + } else if (photoEntry.gradientTopColor != 0 && photoEntry.gradientBottomColor != 0) { + colors = new int[] {photoEntry.gradientTopColor, photoEntry.gradientBottomColor}; + } + } + } else if (entry instanceof StoryEntry) { + File file = ((StoryEntry) entry).draftThumbFile; + + if (file != null) { + BitmapFactory.Options opts = new BitmapFactory.Options(); + opts.inJustDecodeBounds = true; + BitmapFactory.decodeFile(file.getPath(), opts); + + StoryEntry.setupScale(opts, rw, rh); + opts.inPreferredConfig = Bitmap.Config.ARGB_8888; + opts.inDither = true; + opts.inJustDecodeBounds = false; + bitmap = BitmapFactory.decodeFile(file.getPath(), opts); + } + } + + return new Pair<>(bitmap, colors); + } + + private Runnable loadingBitmap; + + private void loadBitmap(Object entry) { + if (entry == null) { + releaseBitmap(currentKey); + currentKey = null; + bitmap = null; + invalidate(); + return; + } + + final String key; + if (entry instanceof MediaController.PhotoEntry) { + key = key((MediaController.PhotoEntry) entry); + } else if (entry instanceof StoryEntry) { + key = "d" + ((StoryEntry) entry).draftId; + } else { + key = null; + } + if (TextUtils.equals(key, currentKey)) { + return; + } + if (currentKey != null) { + bitmap = null; + releaseBitmap(currentKey); + invalidate(); + } + currentKey = key; + gradientPaint.setShader(null); + gradient = null; + if (entry instanceof MediaController.PhotoEntry) { + MediaController.PhotoEntry photoEntry = (MediaController.PhotoEntry) entry; + if (photoEntry.gradientTopColor != 0 && photoEntry.gradientBottomColor != 0) { + gradientPaint.setShader(gradient = new LinearGradient(0, 0, 0, 1, new int[] { photoEntry.gradientTopColor, photoEntry.gradientBottomColor }, new float[] { 0, 1 }, Shader.TileMode.CLAMP)); + updateMatrix(); + } + } + if ((bitmap = getBitmap(key)) != null) { + invalidate(); + return; + } + if (loadingBitmap != null) { + getQueue().cancelRunnable(loadingBitmap); + loadingBitmap = null; + } + getQueue().postRunnable(loadingBitmap = () -> { + Pair result = getThumbnail(entry); + AndroidUtilities.runOnUIThread(() -> afterLoad(key, result.first, result.second)); + }); + } + + private void afterLoad(String key, Bitmap bitmap, int[] colors) { + if (bitmap == null) { + return; + } + putBitmap(key, bitmap); + if (!TextUtils.equals(key, currentKey)) { + releaseBitmap(key); + return; + } + this.bitmap = bitmap; + if (colors == null) { + gradientPaint.setShader(null); + gradient = null; + } else { + gradientPaint.setShader(gradient = new LinearGradient(0, 0, 0, 1, colors, new float[]{0, 1}, Shader.TileMode.CLAMP)); + } + updateMatrix(); + invalidate(); + } + + private void updateMatrix() { + if (getMeasuredWidth() > 0 && getMeasuredHeight() > 0 && bitmap != null) { + float s; + if ((float) bitmap.getHeight() / bitmap.getWidth() > ASPECT_RATIO - .1f) { + s = Math.max(getMeasuredWidth() / (float) bitmap.getWidth(), getMeasuredHeight() / (float) bitmap.getHeight()); + } else { + s = getMeasuredWidth() / (float) bitmap.getWidth(); + } + bitmapMatrix.reset(); + bitmapMatrix.postScale(s, s); + bitmapMatrix.postTranslate((getMeasuredWidth() - s * bitmap.getWidth()) / 2f, (getMeasuredHeight() - s * bitmap.getHeight()) / 2f); + } + if (getMeasuredHeight() > 0) { + gradientMatrix.reset(); + gradientMatrix.postScale(1, getMeasuredHeight()); + if (gradient != null) { + gradient.setLocalMatrix(gradientMatrix); + } + } + } + + private Bitmap readBitmap(MediaController.PhotoEntry photoEntry, BitmapFactory.Options options) { + if (photoEntry == null) { + return null; + } + + if (photoEntry.thumbPath != null) { + return BitmapFactory.decodeFile(photoEntry.thumbPath, options); + } else if (photoEntry.isVideo) { + return MediaStore.Video.Thumbnails.getThumbnail(getContext().getContentResolver(), photoEntry.imageId, MediaStore.Video.Thumbnails.MINI_KIND, options); + } else { +// Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, photoEntry.imageId); +// +// Bitmap bitmap = null; +// InputStream is = null; +// try { +// is = getContext().getContentResolver().openInputStream(uri); +// bitmap = BitmapFactory.decodeStream(is, null, options); +// } catch (Exception e) { +// FileLog.e(e, false); +// } finally { +// if (is != null) { +// try { +// is.close(); +// } catch (Exception e2) {} +// } +// } +// if (bitmap == null && options != null && !options.inJustDecodeBounds) { +// return BitmapFactory.decodeFile(photoEntry.path, options); +// } else { +// return bitmap; +// } + return MediaStore.Images.Thumbnails.getThumbnail(getContext().getContentResolver(), photoEntry.imageId, MediaStore.Video.Thumbnails.MINI_KIND, options); + } + } + + private String key(MediaController.PhotoEntry photoEntry) { + if (photoEntry == null) { + return ""; + } + if (photoEntry.thumbPath != null) { + return photoEntry.thumbPath; + } else if (photoEntry.isVideo) { + return "" + photoEntry.imageId; + } else { + return photoEntry.path; + } + } + + public void setRounding(boolean topLeft, boolean topRight) { + this.topLeft = topLeft; + this.topRight = topRight; + } + + private boolean topLeft, topRight; + private final Path clipPath = new Path(); + private final float[] radii = new float[8]; + + @Override + public void draw(Canvas canvas) { + + boolean restore = false; + if (topLeft || topRight) { + canvas.save(); + clipPath.rewind(); + AndroidUtilities.rectTmp.set(0, 0, getWidth(), getHeight()); + radii[0] = radii[1] = topLeft ? dp(6) : 0; + radii[2] = radii[3] = topRight ? dp(6) : 0; + clipPath.addRoundRect(AndroidUtilities.rectTmp, radii, Path.Direction.CW); + canvas.clipPath(clipPath); + restore = true; + } + + super.draw(canvas); + + canvas.drawRect(0, 0, getWidth(), getHeight(), bgPaint); + if (gradient != null) { + canvas.drawRect(0, 0, getWidth(), getHeight(), gradientPaint); + } + + if (bitmap != null && !bitmap.isRecycled()) { + canvas.drawBitmap(bitmap, bitmapMatrix, bitmapPaint); + } + + if (draftLayout != null) { + AndroidUtilities.rectTmp.set(dp(4), dp(4), dp(4 + 6) + draftLayoutWidth + dp(6), dp(5) + draftLayout.getHeight() + dp(2)); + canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(10), dp(10), durationBackgroundPaint); + + canvas.save(); + canvas.translate(AndroidUtilities.rectTmp.left + dp(6) - draftLayoutLeft, AndroidUtilities.rectTmp.top + dp(1.33f)); + draftLayout.draw(canvas); + canvas.restore(); + } + + if (durationLayout != null) { + AndroidUtilities.rectTmp.set(dp(4), getHeight() - dp(4) - durationLayout.getHeight() - dp(2), dp(4) + (drawDurationPlay ? dp(16) : dp(4)) + durationLayoutWidth + dp(5), getHeight() - dp(4)); + canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(10), dp(10), durationBackgroundPaint); + + if (drawDurationPlay) { + durationPlayDrawable.setBounds( + (int) (AndroidUtilities.rectTmp.left + dp(6)), + (int) (AndroidUtilities.rectTmp.centerY() - dp(8) / 2), + (int) (AndroidUtilities.rectTmp.left + dp(13)), + (int) (AndroidUtilities.rectTmp.centerY() + dp(8) / 2) + ); + durationPlayDrawable.draw(canvas); + } + + canvas.save(); + canvas.translate(AndroidUtilities.rectTmp.left + (drawDurationPlay ? dp(16) : dp(5)) - durationLayoutLeft, AndroidUtilities.rectTmp.top + dp(1)); + durationLayout.draw(canvas); + canvas.restore(); + } + + if (restore) { + canvas.restore(); + } + } + + private void setDuration(String duration) { + if (!TextUtils.isEmpty(duration)) { + durationLayout = new StaticLayout(duration, durationTextPaint, getMeasuredWidth() > 0 ? getMeasuredWidth() : AndroidUtilities.displaySize.x, Layout.Alignment.ALIGN_NORMAL, 1, 0, false); + durationLayoutWidth = durationLayout.getLineCount() > 0 ? durationLayout.getLineWidth(0) : 0; + durationLayoutLeft = durationLayout.getLineCount() > 0 ? durationLayout.getLineLeft(0) : 0; + } else { + durationLayout = null; + } + drawDurationPlay = true; + } + + private void setDraft(boolean draft) { + if (draft) { + draftLayout = new StaticLayout(LocaleController.getString("StoryDraft"), draftTextPaint, getMeasuredWidth() > 0 ? getMeasuredWidth() : AndroidUtilities.displaySize.x, Layout.Alignment.ALIGN_NORMAL, 1, 0, false); + draftLayoutWidth = draftLayout.getLineCount() > 0 ? draftLayout.getLineWidth(0) : 0; + draftLayoutLeft = draftLayout.getLineCount() > 0 ? draftLayout.getLineLeft(0) : 0; + } else { + draftLayout = null; + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int w = MeasureSpec.getSize(widthMeasureSpec); + setMeasuredDimension(w, (int) (w * ASPECT_RATIO)); + updateMatrix(); + } + } + + private class EmptyView extends View { + + int height; + + public EmptyView(Context context) { + super(context); + } + + public void setHeight(int height) { + this.height = height; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int width = MeasureSpec.getSize(widthMeasureSpec); + if (height == -1) { + final int photosCount; + if (selectedAlbum == draftsAlbum) { + photosCount = drafts.size(); + } else if (selectedPhotos != null) { + photosCount = selectedPhotos.size() + (containsDraftFolder ? 1 : 0) + (containsDrafts ? drafts.size() : 0); + } else { + photosCount = 0; + } + final int rowsCount = (int) Math.ceil(photosCount / (float) layoutManager.getSpanCount()); + final int fullHeight = AndroidUtilities.displaySize.y - AndroidUtilities.dp(62); + final int itemWidth = (int) (width / (float) layoutManager.getSpanCount()); + final int itemHeight = (int) (itemWidth * ASPECT_RATIO); + final int height = Math.max(0, fullHeight - itemHeight * rowsCount); + setMeasuredDimension(width, height); + } else { + setMeasuredDimension(width, height); + } + } + } + + private static class HeaderView extends TextView { + public HeaderView(Context context, boolean onlyPhotos) { + super(context); + + setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + setTextColor(0xFFFFFFFF); + setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + setPadding(dp(16), dp(16), dp(21), dp(10)); + + setText(LocaleController.getString(onlyPhotos ? R.string.AddImage : R.string.ChoosePhotoOrVideo)); + } + } + + private HeaderView headerView; + + private class Adapter extends RecyclerListView.FastScrollAdapter { + + @Override + public boolean isEnabled(RecyclerView.ViewHolder holder) { + return holder.getItemViewType() == VIEW_TYPE_ENTRY; + } + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view; + if (viewType == VIEW_TYPE_PADDING) { + view = new EmptyView(getContext()); + } else if (viewType == VIEW_TYPE_HEADER) { + view = headerView = new HeaderView(getContext(), onlyPhotos); + } else { + view = new Cell(getContext()); + } + return new RecyclerListView.Holder(view); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + final int viewType = holder.getItemViewType(); + if (viewType == VIEW_TYPE_PADDING) { + ((EmptyView) holder.itemView).setHeight(position == 0 ? getPadding() : -1); + } else if (viewType == VIEW_TYPE_ENTRY) { + Cell cell = (Cell) holder.itemView; + cell.setRounding(position == 2, position == 4); + + int index = position - 2; + if (containsDraftFolder) { + if (index == 0) { + cell.set(drafts.get(0), drafts.size()); + return; + } + index--; + } else if (containsDrafts) { + if (index >= 0 && index < drafts.size()) { + cell.set(drafts.get(index), 0); + return; + } + index -= drafts.size(); + } + + if (selectedPhotos == null || index < 0 || index >= selectedPhotos.size()) { + return; + } + cell.set(selectedPhotos.get(index)); + } + } + + @Override + public int getItemViewType(int position) { + if (position == 0 || position == getItemCount() - 1) { + return VIEW_TYPE_PADDING; + } else if (position == 1) { + return VIEW_TYPE_HEADER; + } else { + return VIEW_TYPE_ENTRY; + } + } + + @Override + public int getItemCount() { + return 2 + getTotalItemsCount() + 1; + } + + @Override + public String getLetter(int position) { + position -= 2; + if (containsDraftFolder) { + if (position == 0) { + return null; + } + position--; + } else if (containsDrafts) { + if (position >= 0 && position < drafts.size()) { + StoryEntry draft = drafts.get(position); + return LocaleController.formatYearMont(draft.draftDate / 1000L, true); + } + position -= drafts.size(); + } + if (selectedPhotos != null && position >= 0 && position < selectedPhotos.size()) { + MediaController.PhotoEntry entry = selectedPhotos.get(position); + if (entry != null) { + long date = entry.dateTaken; + if (Build.VERSION.SDK_INT <= 28) { + date /= 1000; + } + return LocaleController.formatYearMont(date, true); + } + } + return null; + } + + @Override + public int getTotalItemsCount() { + int count = selectedPhotos == null ? 0 : selectedPhotos.size(); + if (containsDraftFolder) { + count++; + } else if (containsDrafts) { + count += drafts.size(); + } + return count; + } + + @Override + public void getPositionForScrollProgress(RecyclerListView listView, float progress, int[] position) { + final int itemsCount = getTotalItemsCount(); + final int itemWidth = (int) ((listView.getWidth() - listView.getPaddingLeft() - listView.getPaddingRight()) / (float) layoutManager.getSpanCount()); + final int itemHeight = (int) (itemWidth * ASPECT_RATIO); + final int rowsCount = (int) Math.ceil(itemsCount / (float) layoutManager.getSpanCount()); + final int totalItemsHeight = rowsCount * itemHeight; + final int viewHeight = AndroidUtilities.displaySize.y - listView.getPaddingTop() - listView.getPaddingBottom(); + final int top = AndroidUtilities.lerp(0, Math.max(0, totalItemsHeight - viewHeight), progress); +// final int bottom = AndroidUtilities.lerp(viewHeight, totalItemsHeight, progress); + final float topRow = top / (float) totalItemsHeight * rowsCount; + final int topRowInt = Math.round(topRow); +// final int bottomRow = Math.round(bottom / (float) totalItemsHeight * rowsCount); + + position[0] = 2 + Math.max(0, topRowInt * layoutManager.getSpanCount()); + position[1] = listView.getPaddingTop() + (int) ((topRow - topRowInt) * itemHeight); + } + + @Override + public float getScrollProgress(RecyclerListView listView) { + final int itemsCount = getTotalItemsCount(); + final int itemWidth = (int) ((listView.getWidth() - listView.getPaddingLeft() - listView.getPaddingRight()) / (float) layoutManager.getSpanCount()); + final int itemHeight = (int) (itemWidth * ASPECT_RATIO); + final int rowsCount = (int) Math.ceil(itemsCount / (float) layoutManager.getSpanCount()); + final int totalItemsHeight = rowsCount * itemHeight; + final int viewHeight = AndroidUtilities.displaySize.y - listView.getPaddingTop(); + return (Math.max(0, listView.computeVerticalScrollOffset() - getPadding()) - listView.getPaddingTop()) / (float) (totalItemsHeight - viewHeight); + } + } + + public int getPadding() { + return (int) (AndroidUtilities.displaySize.y * 0.35f); + } + + public int top() { + if (listView == null || listView.getChildCount() <= 0) { + return getPadding(); + } + int top = Integer.MAX_VALUE; + if (listView != null) { + for (int i = 0; i < listView.getChildCount(); ++i) { + View child = listView.getChildAt(i); + int position = listView.getChildAdapterPosition(child); + if (position > 0) { + top = Math.min(top, (int) child.getY()); + } + } + } + return Math.max(0, Math.min(top, getHeight())); + } + + @Override + protected void onAttachedToWindow() { + NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.albumsDidLoad); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.storiesDraftsUpdated); + super.onAttachedToWindow(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.albumsDidLoad); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.storiesDraftsUpdated); + + Cell.cleanupQueues(); + } + + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.albumsDidLoad) { + updateAlbumsDropDown(); + if (selectedAlbum == null) { + if (dropDownAlbums == null || dropDownAlbums.isEmpty()) { + selectedAlbum = MediaController.allMediaAlbumEntry; + } else { + selectedAlbum = dropDownAlbums.get(0); + } + } else { + for (int a = 0; a < MediaController.allMediaAlbums.size(); a++) { + MediaController.AlbumEntry entry = MediaController.allMediaAlbums.get(a); + if (entry.bucketId == selectedAlbum.bucketId && entry.videoOnly == selectedAlbum.videoOnly) { + selectedAlbum = entry; + break; + } + } + } + selectedPhotos = getPhotoEntries(selectedAlbum); + updateContainsDrafts(); + if (adapter != null) { + adapter.notifyDataSetChanged(); + } + } else if (id == NotificationCenter.storiesDraftsUpdated) { + updateDrafts(); + } + } + + public void updateDrafts() { + drafts.clear(); + if (!onlyPhotos) { + drafts.addAll(MessagesController.getInstance(currentAccount).getStoriesController().getDraftsController().drafts); + } + updateAlbumsDropDown(); + updateContainsDrafts(); + if (adapter != null) { + adapter.notifyDataSetChanged(); + } + } + + private void updateContainsDrafts() { + containsDraftFolder = dropDownAlbums != null && !dropDownAlbums.isEmpty() && dropDownAlbums.get(0) == selectedAlbum && drafts.size() > 2; + containsDrafts = !containsDraftFolder && (selectedAlbum == draftsAlbum || dropDownAlbums != null && !dropDownAlbums.isEmpty() && dropDownAlbums.get(0) == selectedAlbum); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/HintTextView.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/HintTextView.java new file mode 100644 index 0000000000..540a7e70b4 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/HintTextView.java @@ -0,0 +1,55 @@ +package org.telegram.ui.Stories.recorder; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.view.Gravity; +import android.view.View; + +import androidx.annotation.NonNull; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.ui.Components.AnimatedTextView; +import org.telegram.ui.Components.CubicBezierInterpolator; + +public class HintTextView extends View { + + private final AnimatedTextView.AnimatedTextDrawable textDrawable; + + public HintTextView(Context context) { + super(context); + + textDrawable = new AnimatedTextView.AnimatedTextDrawable(true, true, true); + textDrawable.setAnimationProperties(.35f, 0, 300, CubicBezierInterpolator.EASE_OUT_QUINT); + textDrawable.setTextColor(0xffffffff); + textDrawable.setTextSize(AndroidUtilities.dp(14)); + textDrawable.getPaint().setShadowLayer(AndroidUtilities.dp(1.4f), 0, AndroidUtilities.dp(.4f), 0x4C000000); + textDrawable.setGravity(Gravity.CENTER_HORIZONTAL); + textDrawable.setCallback(this); + textDrawable.setOverrideFullWidth(AndroidUtilities.displaySize.x); + } + + public void setText(CharSequence text, boolean animated) { + textDrawable.setText(text, animated); + invalidate(); + } + + @Override + public void draw(Canvas canvas) { + super.draw(canvas); + + textDrawable.setBounds(0, 0, getWidth(), getHeight()); + textDrawable.draw(canvas); + } + + @Override + protected boolean verifyDrawable(@NonNull Drawable who) { + return who == textDrawable || super.verifyDrawable(who); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + textDrawable.setOverrideFullWidth(getMeasuredWidth()); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/HintView2.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/HintView2.java new file mode 100644 index 0000000000..d9b85e336a --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/HintView2.java @@ -0,0 +1,745 @@ +package org.telegram.ui.Stories.recorder; + +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.AndroidUtilities.lerp; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.CornerPathEffect; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.RectF; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.text.Layout; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.text.style.ClickableSpan; +import android.util.Log; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.animation.OvershootInterpolator; + +import androidx.annotation.NonNull; +import androidx.core.graphics.ColorUtils; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.R; +import org.telegram.messenger.Utilities; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.AnimatedTextView; +import org.telegram.ui.Components.ButtonBounce; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.LinkPath; +import org.telegram.ui.Components.LinkSpanDrawable; + +public class HintView2 extends View { + + // direction of an arrow to point + // the gravity of location would be the same, location of arrow opposite: + // f.ex. hint with DIRECTION_LEFT has arrow on right and would be forced to the left ({view} <{hint}) + public static final int DIRECTION_LEFT = 0; + public static final int DIRECTION_TOP = 1; + public static final int DIRECTION_RIGHT = 2; + public static final int DIRECTION_BOTTOM = 3; + // the layout (bounds of hint) are up to a user of this component + + private int direction; + private float joint = .5f, jointTranslate = 0; + + private long duration = 3500; + private boolean useScale = true; + private boolean useTranslate = true; + private boolean useAlpha = true; + private int textMaxWidth = -1; + + private Drawable closeButtonDrawable; + private boolean closeButton; + + private float rounding = dp(8); + private final RectF innerPadding = new RectF(dp(11), dp(6), dp(11), dp(7)); + private float closeButtonMargin = dp(2); + private float arrowHalfWidth = dp(7); + private float arrowHeight = dp(6); + + private final Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + private CharSequence textToSet; + private AnimatedTextView.AnimatedTextDrawable textDrawable; + + private boolean multiline; + private final TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + private Layout.Alignment textLayoutAlignment = Layout.Alignment.ALIGN_NORMAL; + private StaticLayout textLayout; + private float textLayoutLeft, textLayoutWidth; + private LinkSpanDrawable.LinkCollector links = new LinkSpanDrawable.LinkCollector(); + private float textX, textY; + + private boolean hideByTouch = true; + private boolean repeatedBounce = true; + + private boolean shown; + private AnimatedFloat show = new AnimatedFloat(this, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + + public HintView2(Context context, int direction) { + super(context); + this.direction = direction; + + backgroundPaint.setColor(0xcc282828); + backgroundPaint.setPathEffect(new CornerPathEffect(rounding)); + + textDrawable = new AnimatedTextView.AnimatedTextDrawable(true, true, false); + textDrawable.setAnimationProperties(.4f, 0, 320, CubicBezierInterpolator.EASE_OUT_QUINT); + textDrawable.setCallback(this); + + setTextSize(14); + setTextColor(0xffffffff); + } + + public HintView2 setRounding(float roundingDp) { + this.rounding = dp(roundingDp); + backgroundPaint.setPathEffect(new CornerPathEffect(rounding)); + return this; + } + + // mutliline disables text animations + public HintView2 setMultilineText(boolean enable) { + this.multiline = enable; + if (enable) { + innerPadding.set(dp(15), dp(8), dp(15), dp(8)); + closeButtonMargin = dp(6); + } else { + innerPadding.set(dp(11), dp(6), dp(closeButton ? 15 : 11), dp(7)); + closeButtonMargin = dp(2); + } + return this; + } + + public HintView2 setText(CharSequence text) { + if (getMeasuredWidth() < 0) { + textToSet = text; + } else if (!multiline) { + this.textDrawable.setText(text, false); + } else { + makeLayout(text, getTextMaxWidth()); + } + return this; + } + + public CharSequence getText() { + if (textToSet != null) { + return textToSet; + } else if (!multiline) { + return this.textDrawable.getText(); + } else if (textLayout != null) { + return textLayout.getText(); + } + return null; + } + + public HintView2 setText(CharSequence text, boolean animated) { + if (getMeasuredWidth() < 0) { + textToSet = text; + } else { + this.textDrawable.setText(text, !LocaleController.isRTL && animated); + } + return this; + } + + public HintView2 setTextSize(int sizeDp) { + textDrawable.setTextSize(dp(sizeDp)); + textPaint.setTextSize(dp(sizeDp)); + return this; + } + + public HintView2 setTextTypeface(Typeface typeface) { + textDrawable.setTypeface(typeface); + textPaint.setTypeface(typeface); + return this; + } + + public HintView2 setCloseButton(boolean show) { + this.closeButton = show; + if (!multiline) { + innerPadding.set(dp(11), dp(6), dp(closeButton ? 15 : 11), dp(7)); + } + return this; + } + + public HintView2 setMaxWidth(float widthDp) { + this.textMaxWidth = dp(widthDp); + return this; + } + + public HintView2 setMaxWidthPx(int widthPx) { + this.textMaxWidth = widthPx; + return this; + } + + private static boolean contains(CharSequence text, char c) { + if (text == null) { + return false; + } + for (int i = 0; i < text.length(); ++i) { + if (text.charAt(i) == c) { + return true; + } + } + return false; + } + + private static int getTextWidth(CharSequence text, TextPaint paint) { + if (text instanceof Spannable) { + StaticLayout layout = new StaticLayout(text, paint, 99999, Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false); + if (layout.getLineCount() > 0) + return (int) Math.ceil(layout.getLineWidth(0)); + return 0; + } + return (int) paint.measureText(text.toString()); + } + + // returns max width + public static int cutInFancyHalf(CharSequence text, TextPaint paint) { + if (text == null) { + return 0; + } + float fullLineWidth = getTextWidth(text, paint); + final int L = text.toString().length(), m = L / 2; + if (L <= 0 || contains(text, '\n')) { + return (int) Math.ceil(fullLineWidth); + } + int l = m - 1, r = m + 1; + int c = m; + while (l >= 0 && r < L) { + if (text.charAt(l) == ' ') { + c = l; + break; + } + if (text.charAt(r) == ' ') { + c = r; + break; + } + l--; + r++; + } + return (int) Math.ceil(Math.max(fullLineWidth * .3f, Math.max(c + .5f, L - c + .5f) / (float) L * fullLineWidth)); + } + + public HintView2 useScale(boolean enable) { + this.useScale = enable; + return this; + } + + public HintView2 useTranslate(boolean enable) { + this.useTranslate = enable; + return this; + } + + // duration < 0 means you would hide it on yourself + public HintView2 setDuration(long duration) { + this.duration = duration; + return this; + } + + public HintView2 setAnimatedTextHacks(boolean splitByWords, boolean preserveIndex, boolean startFromEnd) { + this.textDrawable.setHacks(splitByWords, preserveIndex, startFromEnd); + return this; + } + + // distances from text to inner hint bounds + // use setPadding() or custom layout to move possible bounds of the hint location + // call last, as paddings are dependent on multiline and closeButton + public HintView2 setInnerPadding(int leftDp, int topDp, int rightDp, int bottomDp) { + innerPadding.set(dp(leftDp), dp(topDp), dp(rightDp), dp(bottomDp)); + return this; + } + + public HintView2 setCloseButtonMargin(int marginDp) { + this.closeButtonMargin = dp(marginDp); + return this; + } + + public HintView2 setTextColor(int color) { + textDrawable.setTextColor(color); + textPaint.setColor(color); + return this; + } + + public HintView2 setHideByTouch(boolean enable) { + hideByTouch = enable; + return this; + } + + public HintView2 setBounce(boolean enable) { + repeatedBounce = enable; + return this; + } + + // works only for multiline=true + public HintView2 setTextAlign(Layout.Alignment alignment) { + textLayoutAlignment = alignment; + return this; + } + + public HintView2 setBgColor(int color) { + backgroundPaint.setColor(color); + return this; + } + + private Runnable onHidden; + public HintView2 setOnHiddenListener(Runnable listener) { + this.onHidden = listener; + return this; + } + + // joint is where should be a connection with an arrow + // f.ex. on DIRECTION_TOP: joint=0: left, joint=.5: at the center, joint=1: right + // jointTranslate translates joint in pixels amount + public HintView2 setJoint(float joint, float jointTranslateDp) { + if (Math.abs(this.joint - joint) >= 1 || Math.abs(this.jointTranslate - dp(jointTranslateDp)) >= 1) { + this.pathSet = false; + invalidate(); + } + this.joint = joint; + this.jointTranslate = dp(jointTranslateDp); + return this; + } + + public TextPaint getTextPaint() { + if (multiline) { + return textPaint; + } else { + return textDrawable.getPaint(); + } + } + + private final Runnable hideRunnable = this::hide; + + public HintView2 show() { + if (shown) { + bounceShow(); + } + shown = true; + invalidate(); + + AndroidUtilities.cancelRunOnUIThread(hideRunnable); + if (duration > 0) { + AndroidUtilities.runOnUIThread(hideRunnable, duration); + } + if (onHidden != null) { + AndroidUtilities.cancelRunOnUIThread(onHidden); + } + + return this; + } + + private ValueAnimator bounceAnimator; + private float bounceT = 1; + private void bounceShow() { + if (!repeatedBounce) { + return; + } + if (bounceAnimator != null) { + bounceAnimator.cancel(); + bounceAnimator = null; + } + bounceAnimator = ValueAnimator.ofFloat(0, 1); + bounceAnimator.addUpdateListener(anm -> { + bounceT = Math.max(1, (float) anm.getAnimatedValue()); + invalidate(); + }); + bounceAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + bounceT = 1; + invalidate(); + } + }); + bounceAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_BACK); + bounceAnimator.setDuration(300); + bounceAnimator.start(); + } + + public void hide() { + hide(true); + } + + public void hide(boolean animated) { + AndroidUtilities.cancelRunOnUIThread(hideRunnable); + if (onHidden != null) { + AndroidUtilities.cancelRunOnUIThread(onHidden); + } + shown = false; + if (!animated) { + show.set(shown, false); + } + invalidate(); + if (onHidden != null) { + AndroidUtilities.runOnUIThread(onHidden, (long) (show.get() * show.getDuration())); + } + links.clear(); + } + + public void pause() { + AndroidUtilities.cancelRunOnUIThread(hideRunnable); + } + + public void unpause() { + AndroidUtilities.cancelRunOnUIThread(hideRunnable); + if (duration > 0) { + AndroidUtilities.runOnUIThread(hideRunnable, duration); + } + } + + public boolean shown() { + return shown; + } + + private int getTextMaxWidth() { + int textWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - (int) (innerPadding.left + innerPadding.right); + if (textMaxWidth > 0) { + textWidth = Math.min(textMaxWidth, textWidth); + } + return Math.max(0, textWidth); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec)); + pathSet = false; + + final int textWidth = getTextMaxWidth(); + textDrawable.setOverrideFullWidth(textWidth); + if (multiline) { + CharSequence text; + if (textToSet != null) { + text = textToSet; + } else if (textLayout != null) { + text = textLayout.getText(); + } else { + return; + } + if (textLayout == null || textLayout.getWidth() != textWidth) { + makeLayout(text, textWidth); + } + } else if (textToSet != null) { + textDrawable.setText(textToSet, false); + } + textToSet = null; + } + + private void makeLayout(CharSequence text, int width) { + textLayout = new StaticLayout(text, textPaint, width, textLayoutAlignment, 1f, 0f, false); + float left = width, right = 0; + for (int i = 0; i < textLayout.getLineCount(); ++i) { + left = Math.min(left, textLayout.getLineLeft(i)); + right = Math.max(right, textLayout.getLineRight(i)); + } + textLayoutWidth = Math.max(0, right - left); + textLayoutLeft = left; + } + + private final ButtonBounce bounce = new ButtonBounce(this, 2f); + private float bounceX, bounceY; + + private final RectF bounds = new RectF(); + private final Path path = new Path(); + private float arrowX, arrowY; + private float pathLastWidth, pathLastHeight; + private boolean pathSet; + private boolean firstDraw = true; + + @Override + protected void dispatchDraw(Canvas canvas) { + if (multiline && textLayout == null) { + return; + } + + final float showT = show.set(shown && !firstDraw); + if (firstDraw) { + firstDraw = false; + invalidate(); + } + if (showT <= 0) { + return; + } + + float contentWidth = multiline ? textLayoutWidth : textDrawable.getCurrentWidth(); + float contentHeight = multiline ? (textLayout == null ? 0 : textLayout.getHeight()) : textDrawable.getHeight(); + if (closeButton) { + if (closeButtonDrawable == null) { + closeButtonDrawable = getContext().getResources().getDrawable(R.drawable.msg_mini_close_tooltip).mutate(); + closeButtonDrawable.setColorFilter(new PorterDuffColorFilter(0x7dffffff, PorterDuff.Mode.MULTIPLY)); + } + contentWidth += closeButtonMargin + closeButtonDrawable.getIntrinsicWidth(); + contentHeight = Math.max(closeButtonDrawable.getIntrinsicHeight(), contentHeight); + } + + final float width = innerPadding.left + contentWidth + innerPadding.right; + final float height = innerPadding.top + contentHeight + innerPadding.bottom; + if (!pathSet || Math.abs(width - pathLastWidth) > 0.1f || Math.abs(height - pathLastHeight) > 0.1f) { + rewindPath(pathLastWidth = width, pathLastHeight = height); + } + + float alpha = useAlpha ? showT : 1; + canvas.save(); + if (showT < 1 && useScale) { + final float scale = lerp(.5f, 1f, showT); + canvas.scale(scale, scale, arrowX, arrowY); + } + float bounceScale = bounce.getScale(.025f); + if (bounceScale != 1) { +// canvas.scale(bounceScale, bounceScale, bounceX, bounceY); + canvas.scale(bounceScale, bounceScale, arrowX, arrowY); + } + if (bounceT != 1) { + if (direction == DIRECTION_BOTTOM || direction == DIRECTION_TOP) { + int maxpad = direction == DIRECTION_BOTTOM ? getPaddingBottom() : getPaddingTop(); + canvas.translate(0, (bounceT - 1) * Math.max(maxpad, dp(24)) * (direction == DIRECTION_TOP ? -1 : 1)); + } else { + int maxpad = direction == DIRECTION_LEFT ? getPaddingLeft() : getPaddingRight(); + canvas.translate((bounceT - 1) * Math.max(maxpad, dp(24)) * (direction == DIRECTION_LEFT ? -1 : 1), 0); + } + } + + final int wasAlpha = backgroundPaint.getAlpha(); + backgroundPaint.setAlpha((int) (wasAlpha * alpha)); + canvas.drawPath(path, backgroundPaint); + backgroundPaint.setAlpha(wasAlpha); + + if (multiline) { + canvas.saveLayerAlpha(0, 0, getWidth(), Math.max(getHeight(), height), (int) (0xFF * alpha), Canvas.ALL_SAVE_FLAG); + canvas.translate(textX = bounds.left + innerPadding.left - textLayoutLeft, textY = bounds.top + innerPadding.top); + if (links.draw(canvas)) { + invalidate(); + } + textLayout.draw(canvas); + canvas.restore(); + } else { + if (textToSet != null) { + textDrawable.setText(textToSet, shown); + textToSet = null; + } + textDrawable.setBounds((int) (bounds.left + innerPadding.left), (int) (bounds.top + innerPadding.top), (int) (bounds.left + innerPadding.left + contentWidth), (int) (bounds.bottom - innerPadding.bottom)); + textDrawable.setAlpha((int) (0xFF * alpha)); + textDrawable.draw(canvas); + } + + if (closeButton) { + if (closeButtonDrawable == null) { + closeButtonDrawable = getContext().getResources().getDrawable(R.drawable.msg_mini_close_tooltip).mutate(); + closeButtonDrawable.setColorFilter(new PorterDuffColorFilter(0x7dffffff, PorterDuff.Mode.MULTIPLY)); + } + closeButtonDrawable.setAlpha((int) (0xFF * alpha)); + closeButtonDrawable.setBounds( + (int) (bounds.right - innerPadding.right * .66f - closeButtonDrawable.getIntrinsicWidth()), + (int) (bounds.centerY() - closeButtonDrawable.getIntrinsicHeight() / 2f), + (int) (bounds.right - innerPadding.right * .66f), + (int) (bounds.centerY() + closeButtonDrawable.getIntrinsicHeight() / 2f) + ); + closeButtonDrawable.draw(canvas); + } + canvas.restore(); + } + + private void rewindPath(float width, float height) { + float arrowXY; + if (direction == DIRECTION_TOP || direction == DIRECTION_BOTTOM) { + arrowXY = lerp(getPaddingLeft(), getMeasuredWidth() - getPaddingRight(), joint); + arrowXY = Utilities.clamp(arrowXY + jointTranslate, getMeasuredWidth() - getPaddingRight(), getPaddingLeft()); + float left = Math.max(getPaddingLeft(), arrowXY - width / 2f); + float right = Math.min(left + width, getMeasuredWidth() - getPaddingRight()); + left = right - width; + arrowXY = Utilities.clamp(arrowXY, right - rounding - arrowHalfWidth, left + rounding + arrowHalfWidth); + if (direction == DIRECTION_TOP) { + bounds.set(left, getPaddingTop() + arrowHeight, right, getPaddingTop() + arrowHeight + height); + } else { + bounds.set(left, getMeasuredHeight() - arrowHeight - getPaddingBottom() - height, right, getMeasuredHeight() - arrowHeight - getPaddingBottom()); + } + } else { + arrowXY = lerp(getPaddingTop(), getMeasuredHeight() - getPaddingBottom(), joint); + arrowXY = Utilities.clamp(arrowXY + jointTranslate, getMeasuredHeight() - getPaddingBottom(), getPaddingTop()); + float top = Math.max(getPaddingTop(), arrowXY - height / 2f); + float bottom = Math.min(top + height, getMeasuredHeight() - getPaddingBottom()); + top = bottom - height; + arrowXY = Utilities.clamp(arrowXY, bottom - rounding - arrowHalfWidth, top + rounding + arrowHalfWidth); + if (direction == DIRECTION_LEFT) { + bounds.set(getPaddingLeft() + arrowHeight, top, getPaddingLeft() + arrowHeight + width, bottom); + } else { + bounds.set(getMeasuredWidth() - getPaddingRight() - arrowHeight - width, top, getMeasuredWidth() - getPaddingRight() - arrowHeight, bottom); + } + } + + path.rewind(); + path.moveTo(bounds.left, bounds.bottom); + if (direction == DIRECTION_LEFT) { + path.lineTo(bounds.left, arrowXY + arrowHalfWidth + dp(2)); + path.lineTo(bounds.left, arrowXY + arrowHalfWidth); + path.lineTo(bounds.left - arrowHeight, arrowXY + dp(1)); + arrowX = bounds.left - arrowHeight; + arrowY = arrowXY; + path.lineTo(bounds.left - arrowHeight, arrowXY - dp(1)); + path.lineTo(bounds.left, arrowXY - arrowHalfWidth); + path.lineTo(bounds.left, arrowXY - arrowHalfWidth - dp(2)); + } + path.lineTo(bounds.left, bounds.top); + if (direction == DIRECTION_TOP) { + path.lineTo(arrowXY - arrowHalfWidth - dp(2), bounds.top); + path.lineTo(arrowXY - arrowHalfWidth, bounds.top); + path.lineTo(arrowXY - dp(1), bounds.top - arrowHeight); + arrowX = arrowXY; + arrowY = bounds.top - arrowHeight; + path.lineTo(arrowXY + dp(1), bounds.top - arrowHeight); + path.lineTo(arrowXY + arrowHalfWidth, bounds.top); + path.lineTo(arrowXY + arrowHalfWidth + dp(2), bounds.top); + } + path.lineTo(bounds.right, bounds.top); + if (direction == DIRECTION_RIGHT) { + path.lineTo(bounds.right, arrowXY - arrowHalfWidth - dp(2)); + path.lineTo(bounds.right, arrowXY - arrowHalfWidth); + path.lineTo(bounds.right + arrowHeight, arrowXY - dp(1)); + arrowX = bounds.right + arrowHeight; + arrowY = arrowXY; + path.lineTo(bounds.right + arrowHeight, arrowXY + dp(1)); + path.lineTo(bounds.right, arrowXY + arrowHalfWidth); + path.lineTo(bounds.right, arrowXY + arrowHalfWidth + dp(2)); + } + path.lineTo(bounds.right, bounds.bottom); + if (direction == DIRECTION_BOTTOM) { + path.lineTo(arrowXY + arrowHalfWidth + dp(2), bounds.bottom); + path.lineTo(arrowXY + arrowHalfWidth, bounds.bottom); + path.lineTo(arrowXY + dp(1), bounds.bottom + arrowHeight); + arrowX = arrowXY; + arrowY = bounds.bottom + arrowHeight; + path.lineTo(arrowXY - dp(1), bounds.bottom + arrowHeight); + path.lineTo(arrowXY - arrowHalfWidth, bounds.bottom); + path.lineTo(arrowXY - arrowHalfWidth - dp(2), bounds.bottom); + } + path.close(); + pathSet = true; + } + + @Override + protected boolean verifyDrawable(@NonNull Drawable who) { + return who == textDrawable || super.verifyDrawable(who); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (!hideByTouch || !shown) { + return false; + } + if (checkTouchLinks(event)) { + return true; + } else if (checkTouchTap(event)) { + return true; + } + return false; + } + + public boolean containsTouch(MotionEvent ev, float ox, float oy) { + return bounds.contains(ev.getX() - ox, ev.getY() - oy); + } + + private boolean checkTouchTap(MotionEvent event) { + final float x = event.getX(), y = event.getY(); + if (event.getAction() == MotionEvent.ACTION_DOWN && containsTouch(event, 0, 0)) { + bounceX = x; + bounceY = y; + bounce.setPressed(true); + return true; + } else if (event.getAction() == MotionEvent.ACTION_UP) { + hide(); + bounce.setPressed(false); + return true; + } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { + bounce.setPressed(false); + return true; + } + return false; + } + + private LinkSpanDrawable.LinksTextView.OnLinkPress onPressListener; + private LinkSpanDrawable.LinksTextView.OnLinkPress onLongPressListener; + + private LinkSpanDrawable pressedLink; + private boolean checkTouchLinks(MotionEvent event) { + if (textLayout != null) { + ClickableSpan span; + if ((span = hitLink((int) event.getX(), (int) event.getY())) != null) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + final LinkSpanDrawable link = new LinkSpanDrawable(span, null, event.getX(), event.getY()); + pressedLink = link; + links.addLink(pressedLink); + Spannable buffer = new SpannableString(textLayout.getText()); + int start = buffer.getSpanStart(pressedLink.getSpan()); + int end = buffer.getSpanEnd(pressedLink.getSpan()); + LinkPath path = pressedLink.obtainNewPath(); + path.setCurrentLayout(textLayout, start, 0); + textLayout.getSelectionPath(start, end, path); + invalidate(); + AndroidUtilities.runOnUIThread(() -> { + if (onLongPressListener != null && pressedLink == link) { + onLongPressListener.run(span); + pressedLink = null; + links.clear(); + } + }, ViewConfiguration.getLongPressTimeout()); + pause(); + return true; + } + } + if (event.getAction() == MotionEvent.ACTION_UP) { + links.clear(); + invalidate(); + unpause(); + if (pressedLink != null && pressedLink.getSpan() == span) { + if (onPressListener != null) { + onPressListener.run(pressedLink.getSpan()); + } else if (pressedLink.getSpan() != null) { + pressedLink.getSpan().onClick(this); + } + pressedLink = null; + return true; + } + pressedLink = null; + } + if (event.getAction() == MotionEvent.ACTION_CANCEL) { + links.clear(); + invalidate(); + unpause(); + pressedLink = null; + } + } + return pressedLink != null; + } + + private ClickableSpan hitLink(int x, int y) { + if (textLayout == null) { + return null; + } + x -= textX; + y -= textY; + final int line = textLayout.getLineForVertical(y); + final int off = textLayout.getOffsetForHorizontal(line, x); + final float left = textLayout.getLineLeft(line); + if (left <= x && left + textLayout.getLineWidth(line) >= x && y >= 0 && y <= textLayout.getHeight()) { + Spannable buffer = new SpannableString(textLayout.getText()); + ClickableSpan[] spans = buffer.getSpans(off, off, ClickableSpan.class); + if (spans.length != 0 && !AndroidUtilities.isAccessibilityScreenReaderEnabled()) { + return spans[0]; + } + } + return null; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/IStoryPart.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/IStoryPart.java new file mode 100644 index 0000000000..910e2c8a92 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/IStoryPart.java @@ -0,0 +1,11 @@ +package org.telegram.ui.Stories.recorder; + +import android.graphics.Matrix; + +import java.io.File; + +public abstract class IStoryPart { + public int id; + public int width, height; + public final Matrix matrix = new Matrix(); +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/KeyboardNotifier.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/KeyboardNotifier.java new file mode 100644 index 0000000000..e735d2a47f --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/KeyboardNotifier.java @@ -0,0 +1,104 @@ +package org.telegram.ui.Stories.recorder; + +import android.content.Context; +import android.graphics.Rect; +import android.util.Log; +import android.view.View; +import android.view.ViewTreeObserver; + +import androidx.annotation.NonNull; +import androidx.annotation.Size; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.Utilities; +import org.telegram.ui.Components.SizeNotifierFrameLayout; + +public class KeyboardNotifier { + + private final View rootView; + private final Utilities.Callback listener; + public boolean ignoring; + private boolean awaitingKeyboard; + + private final Rect rect = new Rect(); + + public KeyboardNotifier(@NonNull View rootView, Utilities.Callback listener) { + this.rootView = rootView; + this.listener = listener; + + if (this.rootView.isAttachedToWindow()) { + rootView.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener); + rootView.addOnLayoutChangeListener(onLayoutChangeListener); + } + this.rootView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(@NonNull View v) { + rootView.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener); + rootView.addOnLayoutChangeListener(onLayoutChangeListener); + } + + @Override + public void onViewDetachedFromWindow(@NonNull View v) { + rootView.getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayoutListener); + rootView.removeOnLayoutChangeListener(onLayoutChangeListener); + } + }); + } + + private final View.OnLayoutChangeListener onLayoutChangeListener = (view, l, t, r, b, ol, ot, or, ob) -> update(); + private final ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener = this::update; + + private int lastKeyboardHeight; + private int keyboardHeight; + + private void update() { + if (ignoring) { + return; + } + + rootView.getWindowVisibleDisplayFrame(rect); + final int screenHeight = rootView.getHeight(); + keyboardHeight = screenHeight - rect.bottom; + final boolean unique = lastKeyboardHeight != keyboardHeight; + lastKeyboardHeight = keyboardHeight; + + if (unique) { + fire(); + } + } + + public int getKeyboardHeight() { + return keyboardHeight; + } + + public boolean keyboardVisible() { + return keyboardHeight > AndroidUtilities.navigationBarHeight + AndroidUtilities.dp(20) || awaitingKeyboard; + } + + public void ignore(boolean ignore) { + final boolean update = ignoring && ignore; + ignoring = ignore; + if (update) { + update(); + } + } + + public void fire() { + if (awaitingKeyboard) { + if (keyboardHeight < AndroidUtilities.navigationBarHeight + AndroidUtilities.dp(20)) { + return; + } else { + awaitingKeyboard = false; + } + } + + if (listener != null) { + listener.run(keyboardHeight); + } + } + + public void awaitKeyboard() { + awaitingKeyboard = true; + } + +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/MuteButtonHint.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/MuteButtonHint.java new file mode 100644 index 0000000000..f47a04a610 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/MuteButtonHint.java @@ -0,0 +1,119 @@ +package org.telegram.ui.Stories.recorder; + +import static org.telegram.messenger.AndroidUtilities.dp; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.CornerPathEffect; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.drawable.Drawable; +import android.view.Gravity; +import android.view.View; + +import androidx.annotation.NonNull; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.LocaleController; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.AnimatedTextView; +import org.telegram.ui.Components.CubicBezierInterpolator; + +public class MuteButtonHint extends View { + + private final Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final AnimatedTextView.AnimatedTextDrawable textDrawable = new AnimatedTextView.AnimatedTextDrawable(false, false, false); + + private final Path path = new Path(); + + public MuteButtonHint(Context context) { + super(context); + + backgroundPaint.setColor(0xcc282828); + backgroundPaint.setPathEffect(new CornerPathEffect(dp(6))); + + textDrawable.updateAll = true; + textDrawable.setAnimationProperties(.4f, 0, 450, CubicBezierInterpolator.EASE_OUT_QUINT); + textDrawable.setGravity(Gravity.RIGHT); + textDrawable.setCallback(this); + textDrawable.setTextSize(dp(14)); + textDrawable.setTextColor(0xffffffff); + textDrawable.setOverrideFullWidth(AndroidUtilities.displaySize.x); + } + + private Runnable hideRunnable; + public void setMuted(boolean muted) { + textDrawable.setText(muted ? LocaleController.getString("StorySoundMuted") : LocaleController.getString("StorySoundNotMuted"), !LocaleController.isRTL && shown); + show(true, true); + if (hideRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(hideRunnable); + } + AndroidUtilities.runOnUIThread(hideRunnable = () -> hide(true), 3500); + } + + public void hide(boolean animated) { + show(false, animated); + } + + public void show(boolean show, boolean animated) { + if (!show && hideRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(hideRunnable); + hideRunnable = null; + } + shown = show; + if (!animated) { + showT.set(show, true); + } + invalidate(); + } + + @Override + protected boolean verifyDrawable(@NonNull Drawable who) { + return who == textDrawable || super.verifyDrawable(who); + } + + private boolean shown; + private AnimatedFloat showT = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + + @Override + protected void dispatchDraw(Canvas canvas) { + final float showT = this.showT.set(shown); + + if (showT <= 0) { + return; + } + + final float W = getMeasuredWidth(), H = getMeasuredHeight(); + final float width = dp(11 + 11) + textDrawable.getCurrentWidth(); + + final float cx = W - dp(56 + 28 - 12); + path.rewind(); + path.moveTo(W - width - dp(8), dp(6)); + path.lineTo(W - width - dp(8), H); + path.lineTo(W - dp(8), H); + path.lineTo(W - dp(8), dp(6)); + path.lineTo(cx + dp(7), dp(6)); + path.lineTo(cx + dp(1), 0); + path.lineTo(cx - dp(1), 0); + path.lineTo(cx - dp(7), dp(6)); + path.close(); + + backgroundPaint.setAlpha((int) (0xcc * showT)); + canvas.drawPath(path, backgroundPaint); + + textDrawable.setAlpha((int) (0xFF * showT)); + AndroidUtilities.rectTmp2.set((int) (W - width + dp(-8 + 11)), dp(6 + 7), (int) (W - dp(8 + 11)), (int) (H - dp(7))); + canvas.save(); + canvas.clipRect(AndroidUtilities.rectTmp2); + textDrawable.setBounds(AndroidUtilities.rectTmp2); + textDrawable.draw(canvas); + canvas.restore(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int width = MeasureSpec.getSize(widthMeasureSpec); + textDrawable.setOverrideFullWidth(width - dp(22)); + setMeasuredDimension(width, dp(38)); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/PaintView.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/PaintView.java new file mode 100644 index 0000000000..fb39c912fd --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/PaintView.java @@ -0,0 +1,3727 @@ +package org.telegram.ui.Stories.recorder; + +import static org.telegram.messenger.AndroidUtilities.lerp; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.ClipData; +import android.content.ClipDescription; +import android.content.ClipboardManager; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.Rect; +import android.graphics.SweepGradient; +import android.graphics.Typeface; +import android.graphics.drawable.GradientDrawable; +import android.media.ExifInterface; +import android.os.Build; +import android.os.Looper; +import android.text.Layout; +import android.text.SpannableString; +import android.text.Spanned; +import android.text.TextUtils; +import android.util.Log; +import android.util.Pair; +import android.util.SparseArray; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewParent; +import android.view.ViewPropertyAnimator; +import android.view.WindowManager; +import android.view.animation.OvershootInterpolator; +import android.widget.EditText; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.core.content.ContextCompat; +import androidx.core.graphics.ColorUtils; +import androidx.dynamicanimation.animation.FloatValueHolder; +import androidx.dynamicanimation.animation.SpringAnimation; +import androidx.dynamicanimation.animation.SpringForce; + +import com.google.android.gms.vision.Frame; +import com.google.android.gms.vision.face.Face; +import com.google.android.gms.vision.face.FaceDetector; + +import org.checkerframework.checker.units.qual.A; +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.Bitmaps; +import org.telegram.messenger.BuildVars; +import org.telegram.messenger.DispatchQueue; +import org.telegram.messenger.Emoji; +import org.telegram.messenger.FileLoader; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.ImageLocation; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MediaController; +import org.telegram.messenger.MessageObject; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.R; +import org.telegram.messenger.SharedConfig; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.Utilities; +import org.telegram.messenger.VideoEditedInfo; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.ActionBar; +import org.telegram.ui.ActionBar.ActionBarPopupWindow; +import org.telegram.ui.ActionBar.AdjustPanLayoutHelper; +import org.telegram.ui.ActionBar.AlertDialog; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.BubbleActivity; +import org.telegram.ui.Components.AnimatedEmojiDrawable; +import org.telegram.ui.Components.AnimatedEmojiSpan; +import org.telegram.ui.Components.ChatActivityEnterViewAnimatedIconView; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.EmojiView; +import org.telegram.ui.Components.IPhotoPaintView; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.Paint.Brush; +import org.telegram.ui.Components.Paint.ColorPickerBottomSheet; +import org.telegram.ui.Components.Paint.PaintTypeface; +import org.telegram.ui.Components.Paint.Painting; +import org.telegram.ui.Components.Paint.PersistColorPalette; +import org.telegram.ui.Components.Paint.PhotoFace; +import org.telegram.ui.Components.Paint.RenderView; +import org.telegram.ui.Components.Paint.Swatch; +import org.telegram.ui.Components.Paint.UndoStore; +import org.telegram.ui.Components.Paint.Views.EditTextOutline; +import org.telegram.ui.Components.Paint.Views.EntitiesContainerView; +import org.telegram.ui.Components.Paint.Views.EntityView; +import org.telegram.ui.Components.Paint.Views.PaintCancelView; +import org.telegram.ui.Components.Paint.Views.PaintColorsListView; +import org.telegram.ui.Components.Paint.Views.PaintDoneView; +import org.telegram.ui.Components.Paint.Views.PaintTextOptionsView; +import org.telegram.ui.Components.Paint.Views.PaintToolsView; +import org.telegram.ui.Components.Paint.Views.PaintTypefaceListView; +import org.telegram.ui.Components.Paint.Views.PaintWeightChooserView; +import org.telegram.ui.Components.Paint.Views.PhotoView; +import org.telegram.ui.Components.Paint.Views.StickerView; +import org.telegram.ui.Components.Paint.Views.TextPaintView; +import org.telegram.ui.Components.Point; +import org.telegram.ui.Components.RLottieDrawable; +import org.telegram.ui.Components.Size; +import org.telegram.ui.Components.SizeNotifierFrameLayout; +import org.telegram.ui.Components.SizeNotifierFrameLayoutPhoto; +import org.telegram.ui.Components.StickerMasksAlert; +import org.telegram.ui.PhotoViewer; + +import java.io.File; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class PaintView extends SizeNotifierFrameLayoutPhoto implements IPhotoPaintView, PaintToolsView.Delegate, EntityView.EntityViewDelegate, PaintTextOptionsView.Delegate, SizeNotifierFrameLayoutPhoto.SizeNotifierFrameLayoutPhotoDelegate, StoryRecorder.Touchable { + private PaintCancelView cancelButton; + private PaintDoneView doneButton; + private float offsetTranslationY; + + private Bitmap bitmapToEdit; + private Bitmap facesBitmap; + private UndoStore undoStore; + + private DispatchQueue queue; + + private MediaController.CropState currentCropState; + private float panTranslationProgress; + private float panTranslationY, scale, inputTransformX, inputTransformY, transformX, transformY, imageWidth, imageHeight; + private boolean ignoreLayout; + private float baseScale; + private Size paintingSize; + + private EntityView currentEntityView; + private boolean editingText; + private int selectedTextType; + public boolean enteredThroughText; + + private boolean inBubbleMode; + + private RenderView renderView; + private View renderInputView; + private FrameLayout selectionContainerView; + public EntitiesContainerView entitiesView; + private FrameLayout topLayout; + private FrameLayout bottomLayout; + private FrameLayout overlayLayout; + private FrameLayout pipetteContainerLayout; + private LinearLayout tabsLayout; + private View textDim; + + private int tabsSelectedIndex = 0; + private int tabsNewSelectedIndex = -1; + private float tabsSelectionProgress; + private ValueAnimator tabsSelectionAnimator; + + private boolean ignoreToolChangeAnimationOnce; + + private PaintWeightChooserView weightChooserView; + private PaintWeightChooserView.ValueOverride weightDefaultValueOverride = new PaintWeightChooserView.ValueOverride() { + @Override + public float get() { + Brush brush = renderView.getCurrentBrush(); + if (brush == null) { + return PersistColorPalette.getInstance(currentAccount).getCurrentWeight(); + } + return PersistColorPalette.getInstance(currentAccount).getWeight(String.valueOf(Brush.BRUSHES_LIST.indexOf(brush)), brush.getDefaultWeight()); + } + + @Override + public void set(float val) { + PersistColorPalette.getInstance(currentAccount).setWeight(String.valueOf(Brush.BRUSHES_LIST.indexOf(renderView.getCurrentBrush())), val); + colorSwatch.brushWeight = val; + setCurrentSwatch(colorSwatch, true); + } + }; + + private ArrayList faces; + private int originalBitmapRotation; + private BigInteger lcm; + + private TextView drawTab; + private TextView stickerTab; + private TextView textTab; + + private PaintToolsView paintToolsView; + private PaintTextOptionsView textOptionsView; + private PaintTypefaceListView typefaceListView; + private ImageView undoButton; + private LinearLayout zoomOutButton; + private ImageView zoomOutImage; + private TextView zoomOutText; + private TextView undoAllButton; + private TextView cancelTextButton; + private TextView doneTextButton; + + private Paint typefaceMenuOutlinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private Paint typefaceMenuBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private float typefaceMenuTransformProgress; + private boolean isTypefaceMenuShown; + private SpringAnimation typefaceMenuTransformAnimation; + + private PaintColorsListView colorsListView; + private Paint colorPickerRainbowPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private Paint colorSwatchPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private Paint colorSwatchOutlinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private Swatch colorSwatch = new Swatch(Color.WHITE, 1f, 0.016773745f); + private boolean fillShapes = false; + + private boolean isColorListShown; + private SpringAnimation toolsTransformAnimation; + private float toolsTransformProgress; + private Paint toolsPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + private int currentAccount; + private Theme.ResourcesProvider resourcesProvider; + + private ActionBarPopupWindow popupWindow; + private ActionBarPopupWindow.ActionBarPopupWindowLayout popupLayout; + private Rect popupRect; + + private Runnable onDoneButtonClickedListener; + private Runnable onCancelButtonClickedListener; + + private StoryRecorder.WindowView parent; + + private AnimatorSet keyboardAnimator; + public final KeyboardNotifier keyboardNotifier; + + private ArrayList initialEntities; + private int w, h; + + private ColorPickerBottomSheet colorPickerBottomSheet; + + @SuppressLint("NotifyDataSetChanged") + public PaintView(Context context, StoryRecorder.WindowView parent, Activity activity, int currentAccount, Bitmap bitmap, Bitmap originalBitmap, int originalRotation, ArrayList entities, int viewWidth, int viewHeight, MediaController.CropState cropState, Runnable onInit, Theme.ResourcesProvider resourcesProvider) { + super(context, activity, true); + setDelegate(this); + this.parent = parent; + this.initialEntities = entities; + this.w = viewWidth; + this.h = viewHeight; + + this.currentAccount = currentAccount; + this.resourcesProvider = new Theme.ResourcesProvider() { + @Override + public int getColor(int key) { + if (key == Theme.key_actionBarDefaultSubmenuBackground) { + return 0xFF282829; + } else if (key == Theme.key_actionBarDefaultSubmenuItem) { + return 0xFFFFFFFF; + } else if (key == Theme.key_dialogBackground) { + return 0xFF1F1F1F; + } else if (key == Theme.key_dialogTextBlack) { + return -592138; + } else if (key == Theme.key_dialogTextGray3) { + return -8553091; + } else if (key == Theme.key_chat_emojiPanelBackground) { + return 0xFF000000; + } else if (key == Theme.key_chat_emojiPanelShadowLine) { + return -1610612736; + } else if (key == Theme.key_chat_emojiBottomPanelIcon) { + return -9539985; + } else if (key == Theme.key_chat_emojiPanelBackspace) { + return -9539985; + } else if (key == Theme.key_chat_emojiPanelIcon) { + return -9539985; +// } else if (key == Theme.key_chat_emojiPanelIconSelected) { +// return -10177041; + } else if (key == Theme.key_windowBackgroundWhiteBlackText) { + return -1; + } else if (key == Theme.key_featuredStickers_addedIcon) { + return -11754001; + } else if (key == Theme.key_listSelector) { + return 0x1FFFFFFF; + } else if (key == Theme.key_profile_tabSelectedText) { + return 0xFFFFFFFF; + } else if (key == Theme.key_profile_tabText) { + return 0xFFFFFFFF; + } else if (key == Theme.key_profile_tabSelectedLine) { + return 0xFFFFFFFF; + } else if (key == Theme.key_profile_tabSelector) { + return 0x14FFFFFF; + } else if (key == Theme.key_chat_emojiSearchIcon || key == Theme.key_featuredStickers_addedIcon) { + return 0xFF878787; + } else if (key == Theme.key_chat_emojiSearchBackground) { + return 0x2E878787; + } + + + if (resourcesProvider != null) { + return resourcesProvider.getColor(key); + } else { + return Theme.getColor(key); + } + } + + private ColorFilter animatedEmojiColorFilter; + + @Override + public ColorFilter getAnimatedEmojiColorFilter() { + if (animatedEmojiColorFilter == null) { + animatedEmojiColorFilter = new PorterDuffColorFilter(0xFFFFFFFF, PorterDuff.Mode.SRC_IN); + } + return animatedEmojiColorFilter; + } + }; + this.currentCropState = cropState; + + inBubbleMode = context instanceof BubbleActivity; + + PersistColorPalette palette = PersistColorPalette.getInstance(currentAccount); + colorSwatch.color = palette.getColor(0); + colorSwatch.brushWeight = palette.getCurrentWeight(); + + queue = new DispatchQueue("Paint"); + + bitmapToEdit = bitmap; + facesBitmap = originalBitmap; + originalBitmapRotation = originalRotation; + undoStore = new UndoStore(); + undoStore.setDelegate(() -> { + boolean canUndo = undoStore.canUndo(); + undoButton.animate().cancel(); + undoButton.animate().alpha(canUndo ? 1f : 0.6f).translationY(0).setDuration(150).start(); + undoButton.setClickable(canUndo); + undoAllButton.animate().cancel(); + undoAllButton.animate().alpha(canUndo ? 1f : 0.6f).translationY(0).setDuration(150).start(); + undoAllButton.setClickable(canUndo); + }); + + textDim = new View(context); + textDim.setVisibility(View.GONE); + textDim.setBackgroundColor(0x4d000000); + textDim.setAlpha(0f); + + renderView = new RenderView(context, new Painting(getPaintingSize(), originalBitmap, originalRotation), bitmapToEdit) { + @Override + public void selectBrush(Brush brush) { + int index = 1 + Brush.BRUSHES_LIST.indexOf(brush); + if (index > 1 && originalBitmap == null) { + index--; + } + paintToolsView.select(index); + onBrushSelected(brush); + } + }; + renderView.setDelegate(new RenderView.RenderViewDelegate() { + + @Override + public void onFirstDraw() { + if (onInit != null) { + onInit.run(); + } + } + + @Override + public void onBeganDrawing() { + if (currentEntityView != null) { + selectEntity(null); + } + weightChooserView.setViewHidden(true); + } + + @Override + public void onFinishedDrawing(boolean moved) { + undoStore.getDelegate().historyChanged(); + weightChooserView.setViewHidden(false); + } + + @Override + public boolean shouldDraw() { + boolean draw = currentEntityView == null; + if (!draw) { + selectEntity(null); + } + return draw; + } + + @Override + public void invalidateInputView() { + if (renderInputView != null) { + renderInputView.invalidate(); + } + } + + @Override + public void resetBrush() { + if (ignoreToolChangeAnimationOnce) { + ignoreToolChangeAnimationOnce = false; + return; + } + paintToolsView.select(1); + onBrushSelected(Brush.BRUSHES_LIST.get(0)); + } + }); + renderView.setUndoStore(undoStore); + renderView.setQueue(queue); + renderView.setVisibility(View.INVISIBLE); +// addView(renderView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT)); + + renderInputView = new View(context) { + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + if (renderView != null) { + renderView.onDrawForInput(canvas); + } + } + }; + renderInputView.setVisibility(View.INVISIBLE); +// addView(renderInputView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT)); + + entitiesView = new EntitiesContainerView(context, new EntitiesContainerView.EntitiesContainerViewDelegate() { + @Override + public boolean shouldReceiveTouches() { + return true; + } + + @Override + public EntityView onSelectedEntityRequest() { + return currentEntityView; + } + + @Override + public void onEntityDeselect() { + selectEntity(null); + if (enteredThroughText) { + dismiss(); + enteredThroughText = false; + } + } + }) { + Paint linePaint = new Paint(); + + long lastUpdate; + float stickyXAlpha, stickyYAlpha; + + { + setWillNotDraw(false); + linePaint.setStrokeWidth(AndroidUtilities.dp(2)); + linePaint.setStyle(Paint.Style.STROKE); + linePaint.setColor(Color.WHITE); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + long dt = Math.min(16, System.currentTimeMillis() - lastUpdate); + lastUpdate = System.currentTimeMillis(); + boolean drawStickyX = false, drawStickyY = false; + + if (currentEntityView != null && currentEntityView.hasTouchDown() && currentEntityView.hasPanned()) { + drawStickyX = currentEntityView.hasStickyX(); + drawStickyY = currentEntityView.hasStickyY(); + } + + if (drawStickyX && stickyXAlpha != 1f) { + stickyXAlpha = Math.min(1f, stickyXAlpha + dt / 150f); + invalidate(); + } else if (!drawStickyX && stickyXAlpha != 0f) { + stickyXAlpha = Math.max(0f, stickyXAlpha - dt / 150f); + invalidate(); + } + + if (drawStickyY && stickyYAlpha != 1f) { + stickyYAlpha = Math.min(1f, stickyYAlpha + dt / 150f); + invalidate(); + } else if (!drawStickyY && stickyYAlpha != 0f) { + stickyYAlpha = Math.max(0f, stickyYAlpha - dt / 150f); + invalidate(); + } + + if (stickyYAlpha != 0f) { + linePaint.setAlpha((int) (stickyYAlpha * 0xFF)); + float y = getMeasuredHeight() / 2f; + canvas.drawLine(0, y, getMeasuredWidth(), y, linePaint); + } + if (stickyXAlpha != 0f) { + linePaint.setAlpha((int) (stickyXAlpha * 0xFF)); + float x = getMeasuredWidth() / 2f; + canvas.drawLine(x, 0, x, getMeasuredHeight(), linePaint); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + if (w <= 0) { + w = entitiesView.getMeasuredWidth(); + } + if (h <= 0) { + h = entitiesView.getMeasuredWidth(); + } + } + }; +// addView(entitiesView); + setupEntities(); + + entitiesView.setVisibility(INVISIBLE); + + selectionContainerView = new FrameLayout(context) { + @SuppressLint("ClickableViewAccessibility") + @Override + public boolean onTouchEvent(MotionEvent event) { + return false; + } + }; +// addView(selectionContainerView); + + topLayout = new FrameLayout(context); + topLayout.setPadding(AndroidUtilities.dp(4), AndroidUtilities.dp(12), AndroidUtilities.dp(4), AndroidUtilities.dp(12)); + topLayout.setBackground(new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, new int [] {0x40000000, 0x00000000} )); + addView(topLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP)); + + undoButton = new ImageView(context); + undoButton.setImageResource(R.drawable.photo_undo2); + undoButton.setPadding(AndroidUtilities.dp(3), AndroidUtilities.dp(3), AndroidUtilities.dp(3), AndroidUtilities.dp(3)); + undoButton.setBackground(Theme.createSelectorDrawable(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR)); + undoButton.setOnClickListener(v -> { + if (renderView != null && renderView.getCurrentBrush() instanceof Brush.Shape) { + renderView.clearShape(); + paintToolsView.setSelectedIndex(1); + onBrushSelected(Brush.BRUSHES_LIST.get(0)); + } else { + undoStore.undo(); + } + }); + undoButton.setAlpha(0.6f); + undoButton.setClickable(false); + topLayout.addView(undoButton, LayoutHelper.createFrame(32, 32, Gravity.TOP | Gravity.LEFT, 12, 0, 0, 0)); + + zoomOutButton = new LinearLayout(context); + zoomOutButton.setOrientation(LinearLayout.HORIZONTAL); + zoomOutButton.setBackground(Theme.createSelectorDrawable(0x30ffffff, Theme.RIPPLE_MASK_ROUNDRECT_6DP)); + zoomOutButton.setPadding(AndroidUtilities.dp(8), 0, AndroidUtilities.dp(8), 0); + zoomOutText = new TextView(context); + zoomOutText.setTextColor(Color.WHITE); + zoomOutText.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + zoomOutText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + zoomOutText.setText(LocaleController.getString(R.string.PhotoEditorZoomOut)); + zoomOutImage = new ImageView(context); + zoomOutImage.setImageResource(R.drawable.photo_zoomout); + zoomOutButton.addView(zoomOutImage, LayoutHelper.createLinear(24, 24, Gravity.CENTER_VERTICAL, 0, 0, 8, 0)); + zoomOutButton.addView(zoomOutText, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL)); + zoomOutButton.setAlpha(0); + zoomOutButton.setOnClickListener(e -> { + PhotoViewer.getInstance().zoomOut(); + }); + topLayout.addView(zoomOutButton, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 32, Gravity.CENTER)); + + undoAllButton = new TextView(context); + undoAllButton.setBackground(Theme.createSelectorDrawable(0x30ffffff, Theme.RIPPLE_MASK_ROUNDRECT_6DP)); + undoAllButton.setPadding(AndroidUtilities.dp(8), 0, AndroidUtilities.dp(8), 0); + undoAllButton.setText(LocaleController.getString(R.string.PhotoEditorClearAll)); + undoAllButton.setGravity(Gravity.CENTER_VERTICAL); + undoAllButton.setTextColor(Color.WHITE); + undoAllButton.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + undoAllButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + undoAllButton.setOnClickListener(v -> clearAll()); + undoAllButton.setAlpha(0.6f); + topLayout.addView(undoAllButton, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 32, Gravity.RIGHT, 0, 0, 4, 0)); + + cancelTextButton = new TextView(context); + cancelTextButton.setBackground(Theme.createSelectorDrawable(0x30ffffff, Theme.RIPPLE_MASK_ROUNDRECT_6DP)); + cancelTextButton.setText(LocaleController.getString(R.string.Clear)); + cancelTextButton.setPadding(AndroidUtilities.dp(8), 0, AndroidUtilities.dp(8), 0); + cancelTextButton.setGravity(Gravity.CENTER_VERTICAL); + cancelTextButton.setTextColor(Color.WHITE); + cancelTextButton.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + cancelTextButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + cancelTextButton.setOnClickListener(v -> { + if (currentEntityView instanceof TextPaintView) { + AndroidUtilities.hideKeyboard(((TextPaintView) currentEntityView).getFocusedView()); + } + if (emojiViewVisible) { + hideEmojiPopup(false); + } + removeEntity(currentEntityView); + selectEntity(null); + }); + cancelTextButton.setAlpha(0); + cancelTextButton.setVisibility(View.GONE); + topLayout.addView(cancelTextButton, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 32, Gravity.LEFT | Gravity.TOP, 4, 0, 0, 0)); + + doneTextButton = new TextView(context); + doneTextButton.setBackground(Theme.createSelectorDrawable(0x30ffffff, Theme.RIPPLE_MASK_ROUNDRECT_6DP)); + doneTextButton.setText(LocaleController.getString(R.string.Done)); + doneTextButton.setPadding(AndroidUtilities.dp(8), 0, AndroidUtilities.dp(8), 0); + doneTextButton.setGravity(Gravity.CENTER_VERTICAL); + doneTextButton.setTextColor(Color.WHITE); + doneTextButton.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + doneTextButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + doneTextButton.setOnClickListener(v -> { + selectEntity(null); + }); + doneTextButton.setAlpha(0); + doneTextButton.setVisibility(View.GONE); + topLayout.addView(doneTextButton, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 32, Gravity.RIGHT, 0, 0, 4, 0)); + + bottomLayout = new FrameLayout(context) { + private float lastRainbowX, lastRainbowY; + private Path path = new Path(); + + { + setWillNotDraw(false); + colorPickerRainbowPaint.setStyle(Paint.Style.STROKE); + colorPickerRainbowPaint.setStrokeWidth(AndroidUtilities.dp(2)); + } + + private void checkRainbow(float cx, float cy) { + if (cx != lastRainbowX || cy != lastRainbowY) { + lastRainbowX = cx; + lastRainbowY = cy; + + int[] colors = { + 0xffeb4b4b, + 0xffee82ee, + 0xff6080e4, + Color.CYAN, + 0xff8fce00, + Color.YELLOW, + 0xffffa500, + 0xffeb4b4b + }; + colorPickerRainbowPaint.setShader(new SweepGradient(cx, cy, colors, null)); + } + } + + @Override + public void setTranslationY(float translationY) { + super.setTranslationY(translationY); + if (overlayLayout != null) { + overlayLayout.invalidate(); + } + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + ViewGroup barView = getBarView(); + AndroidUtilities.rectTmp.set( + lerp(barView.getLeft(), colorsListView.getLeft(), toolsTransformProgress), + lerp(barView.getTop(), colorsListView.getTop(), toolsTransformProgress), + lerp(barView.getRight(), colorsListView.getRight(), toolsTransformProgress), + lerp(barView.getBottom(), colorsListView.getBottom(), toolsTransformProgress) + ); + final float radius = lerp(AndroidUtilities.dp(32), AndroidUtilities.dp(24), toolsTransformProgress); + canvas.drawRoundRect(AndroidUtilities.rectTmp, radius, radius, toolsPaint); + + if (barView != null && barView.getChildCount() >= 1 && toolsTransformProgress != 1f) { + canvas.save(); + canvas.translate(barView.getLeft(), barView.getTop()); + + View child = barView.getChildAt(0); + if (barView instanceof PaintTextOptionsView) { + child = ((PaintTextOptionsView) barView).getColorClickableView(); + } + + if (child.getAlpha() != 0f) { + canvas.scale(child.getScaleX(), child.getScaleY(), child.getPivotX(), child.getPivotY()); + + colorPickerRainbowPaint.setAlpha((int) ((1f - toolsTransformProgress) * child.getAlpha() * 0xFF)); + + int childWidth = child.getWidth() - child.getPaddingLeft() - child.getPaddingRight(); + int childHeight = child.getHeight() - child.getPaddingTop() - child.getPaddingBottom(); + float cx = child.getX() + child.getPaddingLeft() + childWidth / 2f, cy = child.getY() + child.getPaddingTop() + childHeight / 2f; + if (tabsNewSelectedIndex != -1) { + ViewGroup barView2 = (ViewGroup) getBarView(tabsNewSelectedIndex); + View newView = (barView2 == null ? barView : barView2).getChildAt(0); + if (barView2 instanceof PaintTextOptionsView) { + newView = ((PaintTextOptionsView) barView2).getColorClickableView(); + } + cx = lerp(cx, newView.getX() + newView.getPaddingLeft() + (newView.getWidth() - newView.getPaddingLeft() - newView.getPaddingRight()) / 2f, tabsSelectionProgress); + cy = lerp(cy, newView.getY() + newView.getPaddingTop() + (newView.getHeight() - newView.getPaddingTop() - newView.getPaddingBottom()) / 2f, tabsSelectionProgress); + } + if (colorsListView != null && colorsListView.getChildCount() > 0) { + View animateToView = colorsListView.getChildAt(0); + cx = lerp(cx, colorsListView.getX() - barView.getLeft() + animateToView.getX() + animateToView.getWidth() / 2f, toolsTransformProgress); + cy = lerp(cy, colorsListView.getY() - barView.getTop() + animateToView.getY() + animateToView.getHeight() / 2f, toolsTransformProgress); + } + checkRainbow(cx, cy); + + float rad = Math.min(childWidth, childHeight) / 2f - AndroidUtilities.dp(0.5f); + if (colorsListView != null && colorsListView.getChildCount() > 0) { + View animateToView = colorsListView.getChildAt(0); + rad = lerp(rad, Math.min(animateToView.getWidth() - animateToView.getPaddingLeft() - animateToView.getPaddingRight(), animateToView.getHeight() - animateToView.getPaddingTop() - animateToView.getPaddingBottom()) / 2f - AndroidUtilities.dp(2f), toolsTransformProgress); + } + AndroidUtilities.rectTmp.set(cx - rad, cy - rad, cx + rad, cy + rad); + canvas.drawArc(AndroidUtilities.rectTmp, 0, 360, false, colorPickerRainbowPaint); + + colorSwatchPaint.setColor(colorSwatch.color); + colorSwatchPaint.setAlpha((int) (colorSwatchPaint.getAlpha() * child.getAlpha())); + colorSwatchOutlinePaint.setColor(colorSwatch.color); + colorSwatchOutlinePaint.setAlpha((int) (0xFF * child.getAlpha())); + + float rad2 = rad - AndroidUtilities.dp(3f); + PaintColorsListView.drawColorCircle(canvas, cx, cy, rad2, colorSwatchPaint.getColor()); + + colorSwatchOutlinePaint.setAlpha((int) (colorSwatchOutlinePaint.getAlpha() * toolsTransformProgress * child.getAlpha())); + canvas.drawCircle(cx, cy, rad - (AndroidUtilities.dp(3f) + colorSwatchOutlinePaint.getStrokeWidth()) * (1f - toolsTransformProgress), colorSwatchOutlinePaint); + } + + canvas.restore(); + } + } + }; + bottomLayout.setPadding(AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8), 0); + bottomLayout.setBackground(new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, new int [] {0x00000000, 0x80000000} )); + addView(bottomLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 44 + 60, Gravity.BOTTOM)); + + paintToolsView = new PaintToolsView(context, originalBitmap != null); + paintToolsView.setPadding(AndroidUtilities.dp(16), 0, AndroidUtilities.dp(16), 0); + paintToolsView.setDelegate(this); +// paintToolsView.setSelectedIndex(MathUtils.clamp(palette.getCurrentBrush(), 0, Brush.BRUSHES_LIST.size()) + 1); + paintToolsView.setSelectedIndex(1); + bottomLayout.addView(paintToolsView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48)); + + textOptionsView = new PaintTextOptionsView(context); + textOptionsView.setPadding(AndroidUtilities.dp(16), 0, AndroidUtilities.dp(8), 0); + textOptionsView.setVisibility(GONE); + textOptionsView.setDelegate(this); + post(() -> textOptionsView.setTypeface(PersistColorPalette.getInstance(currentAccount).getCurrentTypeface())); + textOptionsView.setAlignment(PersistColorPalette.getInstance(currentAccount).getCurrentAlignment()); + bottomLayout.addView(textOptionsView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48)); + + overlayLayout = new FrameLayout(context) { + { + setWillNotDraw(false); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isTypefaceMenuShown) { + showTypefaceMenu(false); + return true; + } + return super.onTouchEvent(event); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + typefaceMenuOutlinePaint.setAlpha((int) (0x14 * textOptionsView.getAlpha() * (1f - typefaceMenuTransformProgress))); + + textOptionsView.getTypefaceCellBounds(AndroidUtilities.rectTmp); + float yOffset = bottomLayout.getTop() + textOptionsView.getTop() + bottomLayout.getTranslationY() + textOptionsView.getTranslationY(); + AndroidUtilities.rectTmp.set( + lerp(AndroidUtilities.rectTmp.left, typefaceListView.getLeft(), typefaceMenuTransformProgress), + lerp(yOffset + AndroidUtilities.rectTmp.top, typefaceListView.getTop() - typefaceListView.getTranslationY(), typefaceMenuTransformProgress), + lerp(AndroidUtilities.rectTmp.right, typefaceListView.getRight(), typefaceMenuTransformProgress), + lerp(yOffset + AndroidUtilities.rectTmp.bottom, typefaceListView.getBottom() - typefaceListView.getTranslationY(), typefaceMenuTransformProgress) + ); + float rad = AndroidUtilities.dp(lerp(32, 16, typefaceMenuTransformProgress)); + + int alpha = typefaceMenuBackgroundPaint.getAlpha(); + typefaceMenuBackgroundPaint.setAlpha((int) (alpha * typefaceMenuTransformProgress)); + canvas.drawRoundRect(AndroidUtilities.rectTmp, rad, rad, typefaceMenuBackgroundPaint); + typefaceMenuBackgroundPaint.setAlpha(alpha); + + canvas.drawRoundRect(AndroidUtilities.rectTmp, rad, rad, typefaceMenuOutlinePaint); + } + }; + addView(overlayLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + + typefaceListView = new PaintTypefaceListView(context); + typefaceListView.setVisibility(GONE); + typefaceListView.setOnItemClickListener((view, position) -> { + PaintTypeface typeface = PaintTypeface.get().get(position); + textOptionsView.setTypeface(typeface.getKey()); + onTypefaceSelected(typeface); + showTypefaceMenu(false); + }); + textOptionsView.setTypefaceListView(typefaceListView); + overlayLayout.addView(typefaceListView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.RIGHT | Gravity.BOTTOM, 0, 0, 8, 8)); + + typefaceMenuOutlinePaint.setStyle(Paint.Style.FILL); + typefaceMenuOutlinePaint.setColor(0x14ffffff); + + typefaceMenuBackgroundPaint.setColor(getThemedColor(Theme.key_actionBarDefaultSubmenuBackground)); + + colorsListView = new PaintColorsListView(context) { + private Path path = new Path(); + + @Override + public void draw(Canvas c) { + ViewGroup barView = getBarView(); + AndroidUtilities.rectTmp.set( + lerp(barView.getLeft() - getLeft(), 0, toolsTransformProgress), + lerp(barView.getTop() - getTop(), 0, toolsTransformProgress), + lerp(barView.getRight() - getLeft(), getWidth(), toolsTransformProgress), + lerp(barView.getBottom() - getTop(), getHeight(), toolsTransformProgress) + ); + + path.rewind(); + path.addRoundRect(AndroidUtilities.rectTmp, AndroidUtilities.dp(32), AndroidUtilities.dp(32), Path.Direction.CW); + + c.save(); + c.clipPath(path); + super.draw(c); + c.restore(); + } + }; + colorsListView.setVisibility(GONE); + colorsListView.setColorPalette(PersistColorPalette.getInstance(currentAccount)); + colorsListView.setColorListener(color -> { + setNewColor(color); + showColorList(false); + }); + bottomLayout.addView(colorsListView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 84, Gravity.TOP, 56, 0, 56, 6)); + + setupTabsLayout(context); + + cancelButton = new PaintCancelView(context); + cancelButton.setPadding(AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8)); + cancelButton.setBackground(Theme.createSelectorDrawable(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR)); + bottomLayout.addView(cancelButton, LayoutHelper.createFrame(32, 32, Gravity.BOTTOM | Gravity.LEFT, 12, 0, 0, 4)); + cancelButton.setOnClickListener(e -> { + if (isColorListShown) { + showColorList(false); + return; + } + if (emojiViewVisible) { + hideEmojiPopup(true); + return; + } + if (editingText) { + selectEntity(null); + return; + } +// clearAll(); + if (onCancelButtonClickedListener != null) { + onCancelButtonClickedListener.run(); + } + }); + + doneButton = new PaintDoneView(context); + doneButton.setPadding(AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8)); + doneButton.setBackground(Theme.createSelectorDrawable(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR)); + doneButton.setOnClickListener(v -> { + if (isColorListShown) { + colorPickerBottomSheet = new ColorPickerBottomSheet(context, this.resourcesProvider); + colorPickerBottomSheet.setColor(colorSwatch.color).setPipetteDelegate(new ColorPickerBottomSheet.PipetteDelegate() { + private boolean hasPipette; + + @Override + public void onStartColorPipette() { + hasPipette = true; + } + + @Override + public void onStopColorPipette() { + hasPipette = false; + } + + @Override + public ViewGroup getContainerView() { + return pipetteContainerLayout; + } + + @Override + public View getSnapshotDrawingView() { + return PaintView.this; + } + + @Override + public void onDrawImageOverCanvas(Bitmap bitmap, Canvas canvas) { + Matrix matrix = renderView.getMatrix(); + canvas.save(); + canvas.translate(renderView.getX(), renderView.getY()); + canvas.concat(matrix); + canvas.scale(renderView.getWidth() / (float) originalBitmap.getWidth(), renderView.getHeight() / (float) originalBitmap.getHeight(), 0, 0); + canvas.drawBitmap(originalBitmap, 0, 0, null); + canvas.restore(); + } + + @Override + public boolean isPipetteVisible() { + return hasPipette; + } + + @Override + public boolean isPipetteAvailable() { + // TODO: Get bitmap from VideoPlayer to support videos + return originalBitmap != null; + } + + @Override + public void onColorSelected(int color) { + showColorList(false); + + PersistColorPalette.getInstance(currentAccount).selectColor(color); + PersistColorPalette.getInstance(currentAccount).saveColors(); + setNewColor(color); + colorsListView.getAdapter().notifyDataSetChanged(); + } + }).setColorListener(color -> { + PersistColorPalette.getInstance(currentAccount).selectColor(color); + PersistColorPalette.getInstance(currentAccount).saveColors(); + setNewColor(color); + colorsListView.getAdapter().notifyDataSetChanged(); + colorPickerBottomSheet = null; + }).show(); + return; + } + if (onDoneButtonClickedListener != null) { + onDoneButtonClickedListener.run(); + } + }); + bottomLayout.addView(doneButton, LayoutHelper.createFrame(32, 32, Gravity.BOTTOM | Gravity.RIGHT, 0, 0, 12, 4)); + + weightChooserView = new PaintWeightChooserView(context); + weightChooserView.setColorSwatch(colorSwatch); + weightChooserView.setRenderView(renderView); + weightChooserView.setValueOverride(weightDefaultValueOverride); + colorSwatch.brushWeight = weightDefaultValueOverride.get(); + weightChooserView.setOnUpdate(()-> { + setCurrentSwatch(colorSwatch, true); + PersistColorPalette.getInstance(currentAccount).setCurrentWeight(colorSwatch.brushWeight); + }); + addView(weightChooserView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + + pipetteContainerLayout = new FrameLayout(context); + addView(pipetteContainerLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + + colorSwatchOutlinePaint.setStyle(Paint.Style.STROKE); + colorSwatchOutlinePaint.setStrokeWidth(AndroidUtilities.dp(2)); + + setCurrentSwatch(colorSwatch, true); +// onBrushSelected(Brush.BRUSHES_LIST.get(MathUtils.clamp(palette.getCurrentBrush(), 0, Brush.BRUSHES_LIST.size()))); + onBrushSelected(Brush.BRUSHES_LIST.get(0)); + updateColors(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + setSystemGestureExclusionRects(Arrays.asList(new Rect(0, (int) (AndroidUtilities.displaySize.y * .35f), AndroidUtilities.dp(100), (int) (AndroidUtilities.displaySize.y * .65)))); + } + + keyboardNotifier = new KeyboardNotifier(parent, keyboardHeight -> { + keyboardHeight = Math.max(keyboardHeight - parent.getBottomPadding(false), emojiPadding - parent.getPaddingUnderContainer()); + keyboardHeight = Math.max(0, keyboardHeight); + + notifyHeightChanged(); + final boolean keyboardVisible = keyboardHeight > 0 && currentEntityView instanceof TextPaintView && ((TextPaintView) currentEntityView).getEditText().isFocused(); + + if (keyboardAnimator != null) { + keyboardAnimator.cancel(); + } + keyboardAnimator = new AnimatorSet(); + final ArrayList animators = new ArrayList<>(); + animators.add(ObjectAnimator.ofFloat(weightChooserView, View.TRANSLATION_Y, keyboardHeight > 0 ? Math.min(0, -keyboardHeight / 2f - AndroidUtilities.dp(8)) : 0)); + animators.add(ObjectAnimator.ofFloat(bottomLayout, View.TRANSLATION_Y, (keyboardHeight > 0 ? Math.min(0, -keyboardHeight + AndroidUtilities.dp(40)) : 0)/* - (isColorListShown && keyboardVisible ? AndroidUtilities.dp(39) : 0)*/)); + animators.add(ObjectAnimator.ofFloat(tabsLayout, View.ALPHA, keyboardVisible ? 0f : 1f)); + animators.add(ObjectAnimator.ofFloat(doneButton, View.ALPHA, keyboardVisible && !isColorListShown ? 0f : 1f)); + animators.add(ObjectAnimator.ofFloat(cancelButton, View.ALPHA, keyboardVisible && !isColorListShown ? 0f : 1f)); + updatePreviewViewTranslationY(); + + keyboardAnimator.playTogether(animators); + if (keyboardVisible) { + keyboardAnimator.setDuration(AdjustPanLayoutHelper.keyboardDuration); + keyboardAnimator.setInterpolator(AdjustPanLayoutHelper.keyboardInterpolator); + } else { + keyboardAnimator.setDuration(350); + keyboardAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + } + keyboardAnimator.start(); + + for (int i = 0; i < animators.size(); ++i) { + animators.get(i).setDuration(keyboardVisible ? 350 : AdjustPanLayoutHelper.keyboardDuration); + animators.get(i).setInterpolator(keyboardVisible ? CubicBezierInterpolator.EASE_OUT_QUINT : AdjustPanLayoutHelper.keyboardInterpolator); + animators.get(i).start(); + } + + if (!keyboardVisible) { + showTypefaceMenu(false); + } + }) { + @Override + public void ignore(boolean ignore) { + super.ignore(ignore); + if (ignore) { + showTypefaceMenu(false); + } + } + }; + } + + private ObjectAnimator previewViewTranslationAnimator; + private void updatePreviewViewTranslationY() { + if (previewViewTranslationAnimator != null) { + previewViewTranslationAnimator.cancel(); + } + View previewView = (View) (renderView.getParent()); + if (previewView == null) { + return; + } + previewViewTranslationAnimator = ObjectAnimator.ofFloat(previewView, View.TRANSLATION_Y, + !(keyboardNotifier.keyboardVisible() && !keyboardNotifier.ignoring || emojiPadding > 0) || currentEntityView == null ? 0 : + -(currentEntityView.getPosition().y - previewView.getMeasuredHeight() * .3f) * (previewView.getScaleY()) + ); + previewViewTranslationAnimator.setDuration(350); + previewViewTranslationAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + previewViewTranslationAnimator.start(); + } + + @Override + public void onAnimationStateChanged(boolean isStart) { + weightChooserView.setLayerType(isStart ? LAYER_TYPE_HARDWARE : LAYER_TYPE_NONE, null); + } + + public View getWeightChooserView() { + return weightChooserView; + } + + public View getTopLayout() { + return topLayout; + } + + public View getBottomLayout() { + return bottomLayout; + } + + private void setNewColor(int color) { + int wasColor = colorSwatch.color; + colorSwatch.color = color; + setCurrentSwatch(colorSwatch, true); + + ValueAnimator animator = ValueAnimator.ofFloat(0, 1).setDuration(150); + animator.addUpdateListener(animation -> { + float val = (float) animation.getAnimatedValue(); + + colorSwatch.color = ColorUtils.blendARGB(wasColor, color, val); + bottomLayout.invalidate(); + }); + animator.start(); + } + + private TextPaintView createText(boolean select) { + onTextAdd(); + + Size paintingSize = getPaintingSize(); + Point position = startPositionRelativeToEntity(null); + TextPaintView view = new TextPaintView(getContext(), position, (int) (paintingSize.width / 9), "", colorSwatch, selectedTextType); + view.getEditText().betterFraming = true; + if (position.x == entitiesView.getMeasuredWidth() / 2f) { + view.setHasStickyX(true); + } + if (position.y == entitiesView.getMeasuredHeight() / 2f) { + view.setHasStickyY(true); + } + view.setDelegate(this); + view.setMaxWidth(w - AndroidUtilities.dp(7 + 7 + 18)); + view.setTypeface(PersistColorPalette.getInstance(currentAccount).getCurrentTypeface()); + view.setType(PersistColorPalette.getInstance(currentAccount).getCurrentTextType()); + entitiesView.addView(view, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT)); + if (currentCropState != null) { + view.scale(1.0f / currentCropState.cropScale); + view.rotate(-(currentCropState.transformRotation + currentCropState.cropRotate)); + } + + if (select) { + registerRemovalUndo(view); + + view.beginEditing(); + selectEntity(view, false); + view.getFocusedView().requestFocus(); + AndroidUtilities.showKeyboard(view.getFocusedView()); + editingText = true; + textOptionsView.setAlignment(PersistColorPalette.getInstance(currentAccount).getCurrentAlignment(), true); + textOptionsView.setOutlineType(PersistColorPalette.getInstance(currentAccount).getCurrentTextType()); + } + return view; + } + + public void clearAll() { + if (!undoStore.canUndo()) { + return; + } + if (renderView != null && renderView.getCurrentBrush() instanceof Brush.Shape) { + renderView.clearShape(); + paintToolsView.setSelectedIndex(1); + onBrushSelected(Brush.BRUSHES_LIST.get(0)); + } + if (renderView != null) { + renderView.clearAll(); + } + undoStore.reset(); + entitiesView.removeAllViews(); + } + + @Override + public void setOnDoneButtonClickedListener(Runnable callback) { + onDoneButtonClickedListener = callback; + } + + public void setOnCancelButtonClickedListener(Runnable callback) { + onCancelButtonClickedListener = callback; + } + + protected void dismiss() { + + } + + protected void editSelectedTextEntity() { + if (!(currentEntityView instanceof TextPaintView) || editingText) { + return; + } + + TextPaintView textPaintView = (TextPaintView) currentEntityView; + editingText = true; + + textPaintView.beginEditing(); + View view = textPaintView.getFocusedView(); + view.requestFocus(); + AndroidUtilities.showKeyboard(view); + } + + private boolean zoomOutVisible = false; + @Override + public void updateZoom(boolean zoomedOut) { + boolean shouldBeVisible = !zoomedOut; + if (zoomOutVisible != shouldBeVisible) { + zoomOutVisible = shouldBeVisible; + zoomOutButton.animate().cancel(); + zoomOutButton.animate().alpha(zoomedOut ? 0f : 1f).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).setDuration(240).start(); + } + } + + private boolean selectEntity(EntityView entityView) { + return selectEntity(entityView, true); + } + + private boolean selectEntity(EntityView entityView, boolean changeOptions) { + boolean changed = false; + + boolean animatingToTextTab = false; + if (entityView instanceof TextPaintView && (tabsNewSelectedIndex == -1 && tabsSelectedIndex != 2 || tabsNewSelectedIndex != -1 && tabsNewSelectedIndex != 2)) { + if (tabsSelectionAnimator != null && tabsNewSelectedIndex != 2) { + tabsSelectionAnimator.cancel(); + } + if (isColorListShown) { + showColorList(false); + } + switchTab(2); + animatingToTextTab = true; + } + + if (entityView instanceof TextPaintView && changeOptions) { + int align; + switch (((TextPaintView) entityView).getEditText().getGravity()) { + default: + case Gravity.LEFT | Gravity.CENTER_VERTICAL: + align = PaintTextOptionsView.ALIGN_LEFT; + break; + case Gravity.CENTER: + align = PaintTextOptionsView.ALIGN_CENTER; + break; + case Gravity.RIGHT | Gravity.CENTER_VERTICAL: + align = PaintTextOptionsView.ALIGN_RIGHT; + break; + } + textOptionsView.setAlignment(align); + PaintTypeface typeface = ((TextPaintView) entityView).getTypeface(); + if (typeface != null) { + textOptionsView.setTypeface(typeface.getKey()); + } + textOptionsView.setOutlineType(((TextPaintView) entityView).getType(), true); + overlayLayout.invalidate(); + } + + if (currentEntityView != null) { + if (currentEntityView == entityView) { + if (!editingText) { + if (entityView instanceof TextPaintView) { + enteredThroughText = true; + editSelectedTextEntity(); + } else { + showMenuForEntity(currentEntityView); + } + } else if (currentEntityView instanceof TextPaintView) { + AndroidUtilities.showKeyboard(((TextPaintView) currentEntityView).getFocusedView()); + hideEmojiPopup(false); + } + return true; + } else { + currentEntityView.deselect(); + if (currentEntityView instanceof TextPaintView) { + ((TextPaintView) currentEntityView).endEditing(); + if (!(entityView instanceof TextPaintView)) { + editingText = false; + AndroidUtilities.hideKeyboard(((TextPaintView) currentEntityView).getFocusedView()); + hideEmojiPopup(false); + } + } + } + changed = true; + } + + EntityView oldEntity = currentEntityView; + currentEntityView = entityView; + if (oldEntity instanceof TextPaintView) { + TextPaintView textPaintView = (TextPaintView) oldEntity; + if (TextUtils.isEmpty(textPaintView.getText())) { + removeEntity(oldEntity); + } + } + + if (currentEntityView != null) { + currentEntityView.select(selectionContainerView); + entitiesView.bringChildToFront(currentEntityView); + + if (currentEntityView instanceof TextPaintView) { + TextPaintView textPaintView = (TextPaintView) currentEntityView; + textPaintView.getSwatch().brushWeight = colorSwatch.brushWeight; + setCurrentSwatch(textPaintView.getSwatch(), true); + + float base = (int) (paintingSize.width / 9); + weightChooserView.setValueOverride(new PaintWeightChooserView.ValueOverride() { + @Override + public float get() { + return textPaintView.getBaseFontSize() / base; + } + + @Override + public void set(float val) { + textPaintView.setBaseFontSize((int) (base * val)); + } + }); + weightChooserView.setShowPreview(false); + } else { + weightChooserView.setValueOverride(weightDefaultValueOverride); + weightChooserView.setShowPreview(true); + colorSwatch.brushWeight = weightDefaultValueOverride.get(); + setCurrentSwatch(colorSwatch, true); + } + + changed = true; + } else { + if (tabsSelectionAnimator != null && tabsNewSelectedIndex != 0) { + tabsSelectionAnimator.cancel(); + } + if (isColorListShown) { + showColorList(false); + } + switchTab(0); + + weightChooserView.setValueOverride(weightDefaultValueOverride); + weightChooserView.setShowPreview(true); + colorSwatch.brushWeight = weightDefaultValueOverride.get(); + setCurrentSwatch(colorSwatch, true); + } + updateTextDim(); + + return changed; + } + + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + boolean restore = false; + if ((child == renderView || child == renderInputView || child == entitiesView || child == selectionContainerView) && currentCropState != null) { + canvas.save(); + + int status = (Build.VERSION.SDK_INT >= 21 && !inBubbleMode ? AndroidUtilities.statusBarHeight : 0); + int actionBarHeight = ActionBar.getCurrentActionBarHeight(); + int actionBarHeight2 = actionBarHeight + status; + + int vw = child.getMeasuredWidth(); + int vh = child.getMeasuredHeight(); + int tr = currentCropState.transformRotation; + if (tr == 90 || tr == 270) { + int temp = vw; + vw = vh; + vh = temp; + } + + int w = (int) (vw * currentCropState.cropPw * child.getScaleX() / currentCropState.cropScale); + int h = (int) (vh * currentCropState.cropPh * child.getScaleY() / currentCropState.cropScale); + float x = (float) Math.ceil((getMeasuredWidth() - w) / 2f) + transformX; + float y = (getMeasuredHeight() - actionBarHeight2 - AndroidUtilities.dp(48) + getAdditionalBottom() - h) / 2f + AndroidUtilities.dp(8) + status + transformY; + + canvas.clipRect(Math.max(0, x), Math.max(0, y), Math.min(x + w, getMeasuredWidth()), Math.min(getMeasuredHeight(), y + h)); + restore = true; + } + boolean result = super.drawChild(canvas, child, drawingTime); + if (restore) { + canvas.restore(); + } + return result; + } + + private ViewGroup getBarView() { + return tabsSelectedIndex == 2 ? textOptionsView : paintToolsView; + } + + private void setupTabsLayout(Context context) { + tabsLayout = new LinearLayout(context) { + Paint linePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + { + linePaint.setStrokeWidth(AndroidUtilities.dp(2)); + linePaint.setStyle(Paint.Style.STROKE); + linePaint.setStrokeCap(Paint.Cap.ROUND); + + setWillNotDraw(false); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + TextView selectedTab = (TextView) getChildAt(tabsSelectedIndex); + TextView newSelectedTab = tabsNewSelectedIndex != -1 ? (TextView) getChildAt(tabsNewSelectedIndex) : null; + linePaint.setColor(selectedTab.getCurrentTextColor()); + float y = selectedTab.getY() + selectedTab.getHeight() - selectedTab.getPaddingBottom() + AndroidUtilities.dp(3); + Layout layout = selectedTab.getLayout(); + if (layout == null) { + return; + } + Layout newLayout = newSelectedTab != null ? newSelectedTab.getLayout() : null; + float pr = newLayout == null ? 0 : CubicBezierInterpolator.DEFAULT.getInterpolation(tabsSelectionProgress); + float x = lerp(selectedTab.getX() + layout.getPrimaryHorizontal(layout.getLineStart(0)), newLayout != null ? newSelectedTab.getX() + newLayout.getPrimaryHorizontal(layout.getLineStart(0)) : 0, pr); + float width = lerp(layout.getPrimaryHorizontal(layout.getLineEnd(0)) - layout.getPrimaryHorizontal(layout.getLineStart(0)), newLayout != null ? newLayout.getPrimaryHorizontal(newLayout.getLineEnd(0)) - newLayout.getPrimaryHorizontal(newLayout.getLineStart(0)) : 0, pr); + canvas.drawLine(x, y, x + width, y, linePaint); + } + }; + tabsLayout.setClipToPadding(false); + tabsLayout.setOrientation(LinearLayout.HORIZONTAL); + bottomLayout.addView(tabsLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 32 + 8, Gravity.BOTTOM, 52, 0, 52, 0)); + + drawTab = new TextView(context); + drawTab.setText(LocaleController.getString(R.string.PhotoEditorDraw).toUpperCase()); + drawTab.setBackground(Theme.createSelectorDrawable(getThemedColor(Theme.key_listSelector), Theme.RIPPLE_MASK_ROUNDRECT_6DP)); + drawTab.setPadding(0, AndroidUtilities.dp(8), 0, AndroidUtilities.dp(8)); + drawTab.setTextColor(Color.WHITE); + drawTab.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + drawTab.setGravity(Gravity.CENTER_HORIZONTAL); + drawTab.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + drawTab.setSingleLine(); + drawTab.setOnClickListener(v -> { + if (editingText) { + selectEntity(null); + } else { + switchTab(0); + } + }); + tabsLayout.addView(drawTab, LayoutHelper.createLinear(0, LayoutHelper.WRAP_CONTENT, 1f)); + + stickerTab = new TextView(context); + stickerTab.setText(LocaleController.getString(R.string.PhotoEditorSticker).toUpperCase()); + stickerTab.setBackground(Theme.createSelectorDrawable(getThemedColor(Theme.key_listSelector), Theme.RIPPLE_MASK_ROUNDRECT_6DP)); + stickerTab.setPadding(0, AndroidUtilities.dp(8), 0, AndroidUtilities.dp(8)); + stickerTab.setOnClickListener(v -> openStickersView()); + stickerTab.setTextColor(Color.WHITE); + stickerTab.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + stickerTab.setGravity(Gravity.CENTER_HORIZONTAL); + stickerTab.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + stickerTab.setAlpha(0.6f); + stickerTab.setSingleLine(); + tabsLayout.addView(stickerTab, LayoutHelper.createLinear(0, LayoutHelper.WRAP_CONTENT, 1f)); + + textTab = new TextView(context); + textTab.setText(LocaleController.getString(R.string.PhotoEditorText).toUpperCase()); + textTab.setBackground(Theme.createSelectorDrawable(getThemedColor(Theme.key_listSelector), Theme.RIPPLE_MASK_ROUNDRECT_6DP)); + textTab.setPadding(0, AndroidUtilities.dp(8), 0, AndroidUtilities.dp(8)); + textTab.setTextColor(Color.WHITE); + textTab.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + textTab.setGravity(Gravity.CENTER_HORIZONTAL); + textTab.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + textTab.setAlpha(0.6f); + textTab.setSingleLine(); + textTab.setOnClickListener(v -> { + switchTab(2); + if (!(currentEntityView instanceof TextPaintView)) { + forceChanges = true; + createText(true); + } + }); + tabsLayout.addView(textTab, LayoutHelper.createLinear(0, LayoutHelper.WRAP_CONTENT, 1f)); + } + + private View getBarView(int index) { + if (index == 0) + return paintToolsView; + if (index == 2) + return textOptionsView; + return null; + } + + private void switchTab(int index) { + if (tabsSelectedIndex == index || tabsNewSelectedIndex == index) { + return; + } + if (tabsSelectionAnimator != null) { + tabsSelectionAnimator.cancel(); + } + + final View view = getBarView(tabsSelectedIndex); + tabsNewSelectedIndex = index; + final View newView = getBarView(tabsNewSelectedIndex); + + tabsSelectionAnimator = ValueAnimator.ofFloat(0, 1).setDuration(300); + tabsSelectionAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); + tabsSelectionAnimator.addUpdateListener(animation -> { + tabsSelectionProgress = (float) animation.getAnimatedValue(); + tabsLayout.invalidate(); + bottomLayout.invalidate(); + overlayLayout.invalidate(); + + for (int i = 0; i < tabsLayout.getChildCount(); i++) { + tabsLayout.getChildAt(i).setAlpha(0.6f + 0.4f * (i == tabsNewSelectedIndex ? tabsSelectionProgress : i == tabsSelectedIndex ? 1f - tabsSelectionProgress : 0f)); + } + float pr = CubicBezierInterpolator.DEFAULT.getInterpolation(tabsSelectionProgress); + + if (view != null && newView != null) { + float scale = 0.6f + 0.4f * (1f - pr); + view.setScaleX(scale); + view.setScaleY(scale); + view.setTranslationY(AndroidUtilities.dp(16) * Math.min(pr, 0.25f) / 0.25f); + view.setAlpha(1f - Math.min(pr, 0.25f) / 0.25f); + + scale = 0.6f + 0.4f * pr; + newView.setScaleX(scale); + newView.setScaleY(scale); + newView.setTranslationY(-AndroidUtilities.dp(16) * Math.min(1f - pr, 0.25f) / 0.25f); + newView.setAlpha(1f - Math.min(1f - pr, 0.25f) / 0.25f); + } + }); + tabsSelectionAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + if (view != null && newView != null) { + newView.setVisibility(VISIBLE); + } + if (index == 2) { + weightChooserView.setMinMax(0.5f, 2f); + } else { + Brush brush = renderView.getCurrentBrush(); + if (brush instanceof Brush.Blurer || brush instanceof Brush.Eraser) { + weightChooserView.setMinMax(0.4f, 1.75f); + } else { + weightChooserView.setMinMax(0.05f, 1f); + } + } + } + + @Override + public void onAnimationEnd(Animator animation) { + tabsSelectedIndex = tabsNewSelectedIndex; + tabsNewSelectedIndex = -1; + tabsLayout.invalidate(); + + if (view != null && newView != null) { + view.setVisibility(GONE); + } + + if (animation == tabsSelectionAnimator) { + tabsSelectionAnimator = null; + } + } + }); + tabsSelectionAnimator.start(); + } + + private EmojiBottomSheet emojiPopup; + + private void openStickersView() { + final int wasSelectedIndex = tabsSelectedIndex; + switchTab(1); + postDelayed(() -> { + if (facesBitmap != null) { + detectFaces(); + } + }, 350); + EmojiBottomSheet alert = emojiPopup = new EmojiBottomSheet(getContext(), resourcesProvider) { + @Override + public void onDismissAnimationStart() { + super.onDismissAnimationStart(); + switchTab(wasSelectedIndex); + } + }; + alert.setBlurDelegate(parent::drawBlurBitmap); + alert.setOnGalleryClick(v -> { + alert.dismiss(); + onGalleryClick(); + }); + alert.setOnDismissListener(di -> { + emojiPopup = null; + onOpenCloseStickersAlert(false); + switchTab(wasSelectedIndex); + }); + alert.whenSelected(document -> { + forceChanges = true; + appearAnimation(createSticker(null, document, false)); + }); + alert.show(); + onOpenCloseStickersAlert(true); + } + + protected void onOpenCloseStickersAlert(boolean open) {} + + protected void onTextAdd() {} + + @Override + public void requestLayout() { + if (ignoreLayout) { + return; + } + super.requestLayout(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + ignoreLayout = true; + int width = MeasureSpec.getSize(widthMeasureSpec); + int height = MeasureSpec.getSize(heightMeasureSpec); + + setMeasuredDimension(width, height); + + float bitmapW; + float bitmapH; + int fullHeight = AndroidUtilities.displaySize.y - ActionBar.getCurrentActionBarHeight() - getAdditionalTop() - getAdditionalBottom(); + int maxHeight = fullHeight - AndroidUtilities.dp(48); + if (bitmapToEdit != null) { + bitmapW = bitmapToEdit.getWidth(); + bitmapH = bitmapToEdit.getHeight(); + } else { + bitmapW = width; + bitmapH = height - ActionBar.getCurrentActionBarHeight() - AndroidUtilities.dp(48); + } + + float renderWidth = width; + float renderHeight = (float) Math.floor(renderWidth * bitmapH / bitmapW); + if (renderHeight > maxHeight) { + renderHeight = maxHeight; + renderWidth = (float) Math.floor(renderHeight * bitmapW / bitmapH); + } + +// renderView.measure(MeasureSpec.makeMeasureSpec((int) renderWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec((int) renderHeight, MeasureSpec.EXACTLY)); +// renderInputView.measure(MeasureSpec.makeMeasureSpec((int) renderWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec((int) renderHeight, MeasureSpec.EXACTLY)); + + baseScale = renderWidth / paintingSize.width; +// entitiesView.setScaleX(baseScale); +// entitiesView.setScaleY(baseScale); +// entitiesView.measure(MeasureSpec.makeMeasureSpec((int) paintingSize.width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec((int) paintingSize.height, MeasureSpec.EXACTLY)); + if (currentEntityView != null) { + currentEntityView.updateSelectionView(); + } +// selectionContainerView.measure(MeasureSpec.makeMeasureSpec((int) renderWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec((int) renderHeight, MeasureSpec.EXACTLY)); + measureChild(bottomLayout, widthMeasureSpec, heightMeasureSpec); + measureChild(weightChooserView, widthMeasureSpec, heightMeasureSpec); + measureChild(pipetteContainerLayout, widthMeasureSpec, heightMeasureSpec); + int keyboardPad = Math.max(emojiPadding - parent.getPaddingUnderContainer(), measureKeyboardHeight()); + measureChild(overlayLayout, widthMeasureSpec, MeasureSpec.makeMeasureSpec(height - keyboardPad, MeasureSpec.EXACTLY)); + + topLayout.setPadding(topLayout.getPaddingLeft(), AndroidUtilities.dp(12), topLayout.getPaddingRight(), topLayout.getPaddingBottom()); + measureChild(topLayout, widthMeasureSpec, heightMeasureSpec); + ignoreLayout = false; + + int keyboardSize = 0; + if (!waitingForKeyboardOpen && keyboardSize <= AndroidUtilities.dp(20) && !emojiViewVisible && !isAnimatePopupClosing) { + ignoreLayout = true; + hideEmojiView(); + ignoreLayout = false; + } + + if (keyboardSize <= AndroidUtilities.dp(20)) { + + } else { + hideEmojiView(); + } + +// if (emojiView != null) { +// measureChild(emojiView, widthMeasureSpec, heightMeasureSpec); +// } + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + + int width = right - left; + int height = bottom - top; + +// if (emojiView != null) { +// emojiView.layout(0, height - emojiView.getMeasuredHeight(), emojiView.getMeasuredWidth(), height); +// } + +// int status = (Build.VERSION.SDK_INT >= 21 && !inBubbleMode ? AndroidUtilities.statusBarHeight : 0); +// int actionBarHeight = ActionBar.getCurrentActionBarHeight(); +// int actionBarHeight2 = actionBarHeight + status; +// +// int x = (int) Math.ceil((width - renderView.getMeasuredWidth()) / 2f); +// int y = (height - actionBarHeight2 - AndroidUtilities.dp(48) - renderView.getMeasuredHeight()) / 2 + AndroidUtilities.dp(8) + status + (getAdditionalTop() - getAdditionalBottom()) / 2; + +// renderView.layout(x, y, x + renderView.getMeasuredWidth(), y + renderView.getMeasuredHeight()); +// renderInputView.layout(x, y, x + renderInputView.getMeasuredWidth(), y + renderInputView.getMeasuredHeight()); +// int x2 = x + (renderView.getMeasuredWidth() - entitiesView.getMeasuredWidth()) / 2; +// int y2 = y + (renderView.getMeasuredHeight() - entitiesView.getMeasuredHeight()) / 2; +// entitiesView.layout(x2, y2, x2 + entitiesView.getMeasuredWidth(), y2 + entitiesView.getMeasuredHeight()); +// selectionContainerView.layout(x, y, x + selectionContainerView.getMeasuredWidth(), y + selectionContainerView.getMeasuredHeight()); + } + + private Size getPaintingSize() { + if (paintingSize != null) { + return paintingSize; + } + return paintingSize = new Size(1080, 1920); + } + + @Override + public void init() { + entitiesView.setVisibility(VISIBLE); + renderView.setVisibility(View.VISIBLE); + renderInputView.setVisibility(View.VISIBLE); + } + + private void setupEntities() { + if (initialEntities != null) { + for (int a = 0, N = initialEntities.size(); a < N; a++) { + VideoEditedInfo.MediaEntity entity = initialEntities.get(a); + EntityView view; + if (entity.type == VideoEditedInfo.MediaEntity.TYPE_STICKER) { + StickerView stickerView = createSticker(entity.parentObject, entity.document, false); + if ((entity.subType & 2) != 0) { + stickerView.mirror(); + } + view = stickerView; + ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); + layoutParams.width = entity.viewWidth; + layoutParams.height = entity.viewHeight; + } else if (entity.type == VideoEditedInfo.MediaEntity.TYPE_TEXT) { + TextPaintView textPaintView = createText(false); + textPaintView.setType(entity.subType); + textPaintView.setTypeface(entity.textTypeface); + textPaintView.setBaseFontSize(entity.fontSize); + SpannableString text = new SpannableString(entity.text); + for (VideoEditedInfo.EmojiEntity e : entity.entities) { + text.setSpan(new AnimatedEmojiSpan(e.document_id, 1f, textPaintView.getFontMetricsInt()), e.offset, e.offset + e.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + CharSequence charSequence = text; + charSequence = Emoji.replaceEmoji(charSequence, textPaintView.getFontMetricsInt(), (int) (textPaintView.getFontSize() * .8f), false); + if (charSequence instanceof Spanned) { + Emoji.EmojiSpan[] spans = ((Spanned) charSequence).getSpans(0, charSequence.length(), Emoji.EmojiSpan.class); + if (spans != null) { + for (int i = 0; i < spans.length; ++i) { + spans[i].scale = .85f; + } + } + } + textPaintView.setText(charSequence); + setTextAlignment(textPaintView, entity.textAlign); + Swatch swatch = textPaintView.getSwatch(); + swatch.color = entity.color; + textPaintView.setSwatch(swatch); + view = textPaintView; + } else if (entity.type == VideoEditedInfo.MediaEntity.TYPE_PHOTO) { + PhotoView photoView = createPhoto(entity.text, false); + if ((entity.subType & 2) != 0) { + photoView.mirror(); + } + view = photoView; + ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); + layoutParams.width = entity.viewWidth; + layoutParams.height = entity.viewHeight; + } else { + continue; + } + view.setX(entity.x * w - entity.viewWidth * (1 - entity.scale) / 2); + view.setY(entity.y * h - entity.viewHeight * (1 - entity.scale) / 2); + view.setPosition(new Point(view.getX() + entity.viewWidth / 2f, view.getY() + entity.viewHeight / 2f)); + view.setScaleX(entity.scale); + view.setScaleY(entity.scale); + view.setRotation((float) (-entity.rotation / Math.PI * 180)); + } + initialEntities = null; + entitiesView.setVisibility(View.VISIBLE); + } + } + + private int getFrameRotation() { + switch (originalBitmapRotation) { + case 90: return Frame.ROTATION_90; + case 180: return Frame.ROTATION_180; + case 270: return Frame.ROTATION_270; + default: return Frame.ROTATION_0; + } + } + + private boolean isSidewardOrientation() { + return originalBitmapRotation % 360 == 90 || originalBitmapRotation % 360 == 270; + } + + private void detectFaces() { + queue.postRunnable(() -> { + FaceDetector faceDetector = null; + try { + faceDetector = new FaceDetector.Builder(getContext()) + .setMode(FaceDetector.ACCURATE_MODE) + .setLandmarkType(FaceDetector.ALL_LANDMARKS) + .setTrackingEnabled(false).build(); + if (!faceDetector.isOperational()) { + if (BuildVars.LOGS_ENABLED) { + FileLog.e("face detection is not operational"); + } + return; + } + + Frame frame = new Frame.Builder().setBitmap(facesBitmap).setRotation(getFrameRotation()).build(); + SparseArray faces; + try { + faces = faceDetector.detect(frame); + } catch (Throwable e) { + FileLog.e(e); + return; + } + ArrayList result = new ArrayList<>(); + Size targetSize = getPaintingSize(); + for (int i = 0; i < faces.size(); i++) { + int key = faces.keyAt(i); + Face f = faces.get(key); + PhotoFace face = new PhotoFace(f, facesBitmap, targetSize, isSidewardOrientation()); + if (face.isSufficient()) { + result.add(face); + } + } + PaintView.this.faces = result; + } catch (Exception e) { + FileLog.e(e); + } finally { + if (faceDetector != null) { + faceDetector.release(); + } + } + }, 200); + } + + @Override + public void shutdown() { + renderView.shutdown(); + entitiesView.setVisibility(GONE); + selectionContainerView.setVisibility(GONE); + + queue.postRunnable(() -> { + Looper looper = Looper.myLooper(); + if (looper != null) { + looper.quit(); + } + }); + + if (emojiPopup != null) { + emojiPopup.dismiss(); + } + if (colorPickerBottomSheet != null) { + colorPickerBottomSheet.dismiss(); + } + } + + @Override + public void onResume() { + renderView.redraw(); + } + + @Override + public void setOffsetTranslationY(float y, float progress, int keyboardHeight, boolean isPan) { + + } + + @Override + public float getOffsetTranslationY() { + return offsetTranslationY; + } + + @Override + public void updateColors() { + toolsPaint.setColor(0xff191919); + } + + private boolean forceChanges; + + @Override + public boolean hasChanges() { + return undoStore.canUndo() || forceChanges; + } + + @Override + public Bitmap getBitmap(ArrayList entities, Bitmap[] thumbBitmap) { + return getBitmap(entities, (int) paintingSize.width, (int) paintingSize.height, true, true); + } + + public Bitmap getBitmap(ArrayList entities, int resultWidth, int resultHeight, boolean drawPaint, boolean drawEntities) { + Bitmap bitmap = drawPaint ? renderView.getResultBitmap() : null; + lcm = BigInteger.ONE; + if ((bitmap != null || !drawEntities) && entitiesView.entitiesCount() > 0) { + Canvas canvas; + Canvas thumbCanvas = null; + int count = entitiesView.getChildCount(); + for (int i = 0; i < count; i++) { + boolean skipDrawToBitmap = false; + View v = entitiesView.getChildAt(i); + if (!(v instanceof EntityView)) { + continue; + } + EntityView entity = (EntityView) v; + Point position = entity.getPosition(); + VideoEditedInfo.MediaEntity mediaEntity = new VideoEditedInfo.MediaEntity(); + if (entities != null) { + if (entity instanceof TextPaintView) { + mediaEntity.type = VideoEditedInfo.MediaEntity.TYPE_TEXT; + TextPaintView textPaintView = (TextPaintView) entity; + CharSequence text = textPaintView.getText(); + if (text instanceof Spanned) { + Spanned spanned = (Spanned) text; + AnimatedEmojiSpan[] spans = spanned.getSpans(0, text.length(), AnimatedEmojiSpan.class); + if (spans != null) { + for (int j = 0; j < spans.length; ++j) { + AnimatedEmojiSpan span = spans[j]; + TLRPC.Document document = span.document; + if (document == null) { + document = AnimatedEmojiDrawable.findDocument(currentAccount, span.getDocumentId()); + } + if (document != null) { + AnimatedEmojiDrawable.getDocumentFetcher(currentAccount).putDocument(document); + } + + VideoEditedInfo.EmojiEntity tlentity = new VideoEditedInfo.EmojiEntity(); + tlentity.document_id = span.getDocumentId(); + tlentity.document = document; + tlentity.offset = spanned.getSpanStart(span); + tlentity.length = spanned.getSpanEnd(span) - tlentity.offset; + tlentity.documentAbsolutePath = FileLoader.getInstance(currentAccount).getPathToAttach(document, true).getAbsolutePath(); + int p = 0; + while (document != null && document.thumbs != null && !document.thumbs.isEmpty() && !new File(tlentity.documentAbsolutePath).exists()) { + tlentity.documentAbsolutePath = FileLoader.getInstance(currentAccount).getPathToAttach(document.thumbs.get(p), true).getAbsolutePath(); + p++; + if (p >= document.thumbs.size()) { + break; + } + } + boolean isAnimatedSticker = MessageObject.isAnimatedStickerDocument(tlentity.document, true); + if (isAnimatedSticker || MessageObject.isVideoStickerDocument(tlentity.document)) { + tlentity.subType |= isAnimatedSticker ? 1 : 4; + } + if (MessageObject.isTextColorEmoji(tlentity.document)) { + tlentity.subType |= 8; + } + mediaEntity.entities.add(tlentity); + + if (document != null) { + long duration = 5000; // TODO(dkaraush) + if (duration != 0) { + BigInteger x = BigInteger.valueOf(duration); + lcm = lcm.multiply(x).divide(lcm.gcd(x)); + } + } + } + } + } + mediaEntity.text = text.toString(); + mediaEntity.subType = (byte) textPaintView.getType(); + mediaEntity.color = textPaintView.getSwatch().color; + mediaEntity.fontSize = textPaintView.getTextSize(); + mediaEntity.textTypeface = textPaintView.getTypeface(); + mediaEntity.textAlign = textPaintView.getAlign(); + } else if (entity instanceof StickerView) { + mediaEntity.type = VideoEditedInfo.MediaEntity.TYPE_STICKER; + StickerView stickerView = (StickerView) entity; + Size size = stickerView.getBaseSize(); + mediaEntity.width = size.width; + mediaEntity.height = size.height; + mediaEntity.document = stickerView.getSticker(); + mediaEntity.parentObject = stickerView.getParentObject(); + TLRPC.Document document = stickerView.getSticker(); + mediaEntity.text = FileLoader.getInstance(UserConfig.selectedAccount).getPathToAttach(document, true).getAbsolutePath(); + if (MessageObject.isAnimatedStickerDocument(document, true) || MessageObject.isVideoStickerDocument(document)) { + boolean isAnimatedSticker = MessageObject.isAnimatedStickerDocument(document, true); + mediaEntity.subType |= isAnimatedSticker ? 1 : 4; + long duration; + if (isAnimatedSticker) { + duration = stickerView.getDuration(); + } else { + duration = 5000; + } + if (duration != 0) { + BigInteger x = BigInteger.valueOf(duration); + lcm = lcm.multiply(x).divide(lcm.gcd(x)); + } + } + if (MessageObject.isTextColorEmoji(document)) { + mediaEntity.color = 0xFFFFFFFF; + mediaEntity.subType |= 8; + } + if (stickerView.isMirrored()) { + mediaEntity.subType |= 2; + } + } else if (entity instanceof PhotoView) { + PhotoView photoView = (PhotoView) entity; + mediaEntity.type = VideoEditedInfo.MediaEntity.TYPE_PHOTO; + Size size = photoView.getBaseSize(); + mediaEntity.width = size.width; + mediaEntity.height = size.height; + mediaEntity.text = photoView.getPath(); + if (photoView.isMirrored()) { + mediaEntity.subType |= 2; + } + } else { + continue; + } + entities.add(mediaEntity); + float scaleX = v.getScaleX(); + float scaleY = v.getScaleY(); + float x = v.getX(); + float y = v.getY(); + mediaEntity.viewWidth = v.getWidth(); + mediaEntity.viewHeight = v.getHeight(); + mediaEntity.width = v.getWidth() * scaleX / (float) entitiesView.getMeasuredWidth(); + mediaEntity.height = v.getHeight() * scaleY / (float) entitiesView.getMeasuredHeight(); + mediaEntity.x = (x + v.getWidth() * (1 - scaleX) / 2) / entitiesView.getMeasuredWidth(); + mediaEntity.y = (y + v.getHeight() * (1 - scaleY) / 2) / entitiesView.getMeasuredHeight(); + mediaEntity.rotation = (float) (-v.getRotation() * (Math.PI / 180)); + + mediaEntity.textViewX = (x + v.getWidth() / 2f) / (float) entitiesView.getMeasuredWidth(); + mediaEntity.textViewY = (y + v.getHeight() / 2f) / (float) entitiesView.getMeasuredHeight(); + mediaEntity.textViewWidth = mediaEntity.viewWidth / (float) entitiesView.getMeasuredWidth(); + mediaEntity.textViewHeight = mediaEntity.viewHeight / (float) entitiesView.getMeasuredHeight(); + mediaEntity.scale = scaleX; + + if (entity instanceof StickerView) { + final float a = ((StickerView) entity).centerImage.getImageAspectRatio(); + final float cx = mediaEntity.x + mediaEntity.width / 2f; + final float cy = mediaEntity.y + mediaEntity.height / 2f; + final float A = (float) entitiesView.getMeasuredWidth() / (float) entitiesView.getMeasuredHeight(); + if (a > 1) { + // a = width / height + mediaEntity.height = mediaEntity.width * A / a; + mediaEntity.viewHeight = (int) (mediaEntity.viewWidth / a); + mediaEntity.y = cy - mediaEntity.height / 2f; + } else if (a < 1) { + mediaEntity.width = mediaEntity.height / A * a; + mediaEntity.viewWidth = (int) (mediaEntity.viewHeight * a); + mediaEntity.x = cx - mediaEntity.width / 2f; + } + } + } + if (drawEntities && bitmap != null) { + canvas = new Canvas(bitmap); + final float s = bitmap.getWidth() / (float) entitiesView.getMeasuredWidth(); + for (int k = 0; k < 2; k++) { + Canvas currentCanvas = k == 0 ? canvas : thumbCanvas; + if (currentCanvas == null || (k == 0 && skipDrawToBitmap)) { + continue; + } + currentCanvas.save(); + currentCanvas.scale(s, s); + currentCanvas.translate(mediaEntity.x * entitiesView.getMeasuredWidth(), mediaEntity.y * entitiesView.getMeasuredHeight()); + currentCanvas.scale(v.getScaleX(), v.getScaleY()); + currentCanvas.rotate(v.getRotation(), mediaEntity.width / 2f / v.getScaleX() * entitiesView.getMeasuredWidth(), mediaEntity.height / 2f / v.getScaleY() * entitiesView.getMeasuredHeight()); +// currentCanvas.translate(-entity.getWidth() / (float) entitiesView.getMeasuredWidth() * bitmap.getWidth() / 2f, -entity.getHeight() / (float) entitiesView.getMeasuredHeight() * bitmap.getHeight() / 2f); + if (v instanceof TextPaintView && v.getHeight() > 0 && v.getWidth() > 0) { + Bitmap b = Bitmaps.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888); + Canvas c = new Canvas(b); + v.draw(c); + currentCanvas.drawBitmap(b, null, new Rect(0, 0, b.getWidth(), b.getHeight()), null); + try { + c.setBitmap(null); + } catch (Exception e) { + FileLog.e(e); + } + b.recycle(); + } else { + v.draw(currentCanvas); + } + currentCanvas.restore(); + } + } + } + } + return bitmap; + } + + @Override + public void onCleanupEntities() { + entitiesView.removeAllViews(); + } + + @Override + public long getLcm() { + return lcm.longValue(); + } + + @Override + public View getDoneView() { + return doneButton; + } + + @Override + public View getCancelView() { + return cancelButton; + } + + @Override + public void maybeShowDismissalAlert(PhotoViewer photoViewer, Activity parentActivity, Runnable okRunnable) { + if (isColorListShown) { + showColorList(false); + return; + } + + if (emojiViewVisible) { + hideEmojiPopup(true); + return; + } + + if (editingText) { + selectEntity(null); + return; + } + + if (hasChanges()) { + if (parentActivity == null) { + return; + } + AlertDialog.Builder builder = new AlertDialog.Builder(parentActivity, resourcesProvider); + builder.setMessage(LocaleController.getString("PhotoEditorDiscardAlert", R.string.PhotoEditorDiscardAlert)); + builder.setTitle(LocaleController.getString("DiscardChanges", R.string.DiscardChanges)); + builder.setPositiveButton(LocaleController.getString("PassportDiscard", R.string.PassportDiscard), (dialogInterface, i) -> okRunnable.run()); + builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + photoViewer.showAlertDialog(builder); + } else { + okRunnable.run(); + } + } + + public void maybeDismiss(Activity parentActivity, Runnable okRunnable) { + if (isColorListShown) { + showColorList(false); + return; + } + + if (emojiViewVisible) { + hideEmojiPopup(true); + return; + } + + if (editingText) { + selectEntity(null); + return; + } + + if (hasChanges()) { + if (parentActivity == null) { + return; + } + AlertDialog.Builder builder = new AlertDialog.Builder(parentActivity, resourcesProvider); + builder.setMessage(LocaleController.getString("PhotoEditorDiscardAlert", R.string.PhotoEditorDiscardAlert)); + builder.setTitle(LocaleController.getString("DiscardChanges", R.string.DiscardChanges)); + builder.setPositiveButton(LocaleController.getString("PassportDiscard", R.string.PassportDiscard), (dialogInterface, i) -> okRunnable.run()); + builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + builder.show(); + } else { + okRunnable.run(); + } + } + + @Override + public boolean onTouch(MotionEvent ev) { + if (currentEntityView != null) { + selectEntity(null); + } + +// float x2 = (ev.getX() - renderView.getTranslationX() - getMeasuredWidth() / 2f) / renderView.getScaleX(); +// float y2 = (ev.getY() - renderView.getTranslationY() - getMeasuredHeight() / 2f + AndroidUtilities.dp(32) - (getAdditionalTop() - getAdditionalBottom()) / 2f) / renderView.getScaleY(); +// float rotation = (float) Math.toRadians(-renderView.getRotation()); +// float x = (float) (x2 * Math.cos(rotation) - y2 * Math.sin(rotation)) + renderView.getMeasuredWidth() / 2f; +// float y = (float) (x2 * Math.sin(rotation) + y2 * Math.cos(rotation)) + renderView.getMeasuredHeight() / 2f; + + float x = ev.getX(), y = ev.getY(); + + MotionEvent event = MotionEvent.obtain(ev); + event.setLocation(x, y); + renderView.onTouch(event); + event.recycle(); + return true; + } + + public List getPreviewViews() { + return Arrays.asList( + renderView, + renderInputView, + entitiesView, + selectionContainerView + ); + } + + public void clearSelection() { + selectEntity(null); + } + + public void openPaint() { + switchTab(0); + clearSelection(); + } + + public void openText() { + switchTab(2); + forceChanges = true; + createText(true); + } + + public void openStickers() { + switchTab(1); + openStickersView(); + } + + @Override + public int getAdditionalTop() { + return AndroidUtilities.dp(48); + } + + @Override + public int getAdditionalBottom() { + return AndroidUtilities.dp(24); + } + + @Override + public RenderView getRenderView() { + return renderView; + } + + public View getTextDimView() { + return textDim; + } + + public View getRenderInputView() { + return renderInputView; + } + + public View getEntitiesView() { + return entitiesView; + } + + public View getSelectionEntitiesView() { + return selectionContainerView; + } + + @Override + public void setTransform(float scale, float trX, float trY, float imageWidth, float imageHeight) { +// this.scale = scale; +// this.imageWidth = imageWidth; +// this.imageHeight = imageHeight; +// inputTransformX = trX; +// inputTransformY = trY; +// transformX = trX; +// trY += panTranslationY; +// transformY = trY; +// for (int a = 0; a < 4; a++) { +// View view; +// float additionlScale = 1.0f; +// if (a == 0) { +// view = entitiesView; +// } else if (a == 1) { +// view = selectionContainerView; +// } else if (a == 2) { +// view = renderView; +// } else { +// view = renderInputView; +// } +// float tx; +// float ty; +// float rotation = 0; +// if (currentCropState != null) { +// additionlScale *= currentCropState.cropScale; +// +// int w = view.getMeasuredWidth(); +// int h = view.getMeasuredHeight(); +// if (w == 0 || h == 0) { +// return; +// } +// int tr = currentCropState.transformRotation; +// int fw = w, rotatedW = w; +// int fh = h, rotatedH = h; +// if (tr == 90 || tr == 270) { +// int temp = fw; +// fw = rotatedW = fh; +// fh = rotatedH = temp; +// } +// fw *= currentCropState.cropPw; +// fh *= currentCropState.cropPh; +// +// float sc = Math.max(imageWidth / fw, imageHeight / fh); +// additionlScale *= sc; +// +// tx = trX + currentCropState.cropPx * rotatedW * scale * sc * currentCropState.cropScale; +// ty = trY + currentCropState.cropPy * rotatedH * scale * sc * currentCropState.cropScale; +// rotation = currentCropState.cropRotate + tr; +// } else { +// if (a == 0) { +// additionlScale *= baseScale; +// } +// tx = trX; +// ty = trY; +// } +// float finalScale = scale * additionlScale; +// if (Float.isNaN(finalScale)) { +// finalScale = 1f; +// } +// view.setScaleX(finalScale); +// view.setScaleY(finalScale); +// view.setTranslationX(tx); +// view.setTranslationY(ty); +// view.setRotation(rotation); +// view.invalidate(); +// } +// invalidate(); + } + + @Override + public List getMasks() { + ArrayList result = null; + int count = entitiesView.getChildCount(); + for (int a = 0; a < count; a++) { + View child = entitiesView.getChildAt(a); + if (child instanceof StickerView) { + TLRPC.Document document = ((StickerView) child).getSticker(); + if (result == null) { + result = new ArrayList<>(); + } + TLRPC.TL_inputDocument inputDocument = new TLRPC.TL_inputDocument(); + inputDocument.id = document.id; + inputDocument.access_hash = document.access_hash; + inputDocument.file_reference = document.file_reference; + if (inputDocument.file_reference == null) { + inputDocument.file_reference = new byte[0]; + } + result.add(inputDocument); + } else if (child instanceof TextPaintView) { + TextPaintView textPaintView = (TextPaintView) child; + CharSequence text = textPaintView.getText(); + if (text instanceof Spanned) { + AnimatedEmojiSpan[] spans = ((Spanned) text).getSpans(0, text.length(), AnimatedEmojiSpan.class); + if (spans != null) { + for (int i = 0; i < spans.length; ++i) { + AnimatedEmojiSpan span = spans[i]; + if (span != null) { + TLRPC.Document document; + if (span.document != null) { + document = span.document; + } else { + document = AnimatedEmojiDrawable.findDocument(currentAccount, span.getDocumentId()); + } + + if (document != null) { + if (result == null) { + result = new ArrayList<>(); + } + TLRPC.TL_inputDocument inputDocument = new TLRPC.TL_inputDocument(); + inputDocument.id = document.id; + inputDocument.access_hash = document.access_hash; + inputDocument.file_reference = document.file_reference; + if (inputDocument.file_reference == null) { + inputDocument.file_reference = new byte[0]; + } + result.add(inputDocument); + } + } + } + } + } + } + } + return result; + } + + @Override + public void onBrushSelected(Brush brush) { + if (brush instanceof Brush.Blurer || brush instanceof Brush.Eraser) { + weightChooserView.setMinMax(0.4f, 1.75f); + } else { + weightChooserView.setMinMax(0.05f, 1f); + } + weightChooserView.setDrawCenter(!(brush instanceof Brush.Shape)); + if (renderView.getCurrentBrush() instanceof Brush.Shape) { + ignoreToolChangeAnimationOnce = true; + } + renderView.setBrush(brush); + colorSwatch.brushWeight = weightDefaultValueOverride.get(); + setCurrentSwatch(colorSwatch, true); + renderInputView.invalidate(); + } + + @Override + public void onTypefaceButtonClicked() { + showTypefaceMenu(true); + } + + private void showTypefaceMenu(boolean show) { + if (isTypefaceMenuShown != show) { + isTypefaceMenuShown = show; + + if (typefaceMenuTransformAnimation != null) { + typefaceMenuTransformAnimation.cancel(); + } + + typefaceMenuTransformAnimation = new SpringAnimation(new FloatValueHolder(show ? 0 : 1000f)); + typefaceMenuTransformAnimation.setSpring(new SpringForce() + .setFinalPosition(show ? 1000f : 0f) + .setStiffness(1250f) + .setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY)); + + if (show) { + typefaceListView.setAlpha(0f); + typefaceListView.setVisibility(VISIBLE); + } + + typefaceMenuTransformAnimation.addUpdateListener((animation, value, velocity) -> { + typefaceMenuTransformProgress = value / 1000f; + typefaceListView.setAlpha(typefaceMenuTransformProgress); + typefaceListView.invalidate(); + overlayLayout.invalidate(); + + textOptionsView.getTypefaceCell().setAlpha(1f - typefaceMenuTransformProgress); + }); + typefaceMenuTransformAnimation.addEndListener((animation, canceled, value, velocity) -> { + if (animation == typefaceMenuTransformAnimation) { + typefaceMenuTransformAnimation = null; + + if (!show) { + typefaceListView.setVisibility(GONE); + } + typefaceListView.setMaskProvider(null); + } + }); + typefaceMenuTransformAnimation.start(); + } + } + + @SuppressLint("NotifyDataSetChanged") + private void showColorList(boolean show) { + if (isColorListShown != show) { + isColorListShown = show; + + if (toolsTransformAnimation != null) { + toolsTransformAnimation.cancel(); + } + + toolsTransformAnimation = new SpringAnimation(new FloatValueHolder(show ? 0 : 1000f)); + toolsTransformAnimation.setSpring(new SpringForce() + .setFinalPosition(show ? 1000f : 0f) + .setStiffness(1250f) + .setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY)); + + boolean[] moveBottomLayout = new boolean[] { keyboardNotifier.keyboardVisible() || emojiPadding > 0 }; + float bottomLayoutTranslationY = bottomLayout.getTranslationY(); + float doneButtonAlpha = doneButton.getAlpha(); + + View barView = getBarView(); + toolsTransformAnimation.addUpdateListener((animation, value, velocity) -> { + toolsTransformProgress = value / 1000f; + + float scale = 0.6f + 0.4f * (1f - toolsTransformProgress); + barView.setScaleX(scale); + barView.setScaleY(scale); + barView.setTranslationY(AndroidUtilities.dp(16) * Math.min(toolsTransformProgress, 0.25f) / 0.25f); + barView.setAlpha(1f - Math.min(toolsTransformProgress, 0.25f) / 0.25f); + + colorsListView.setProgress(toolsTransformProgress, show); + + doneButton.setProgress(toolsTransformProgress); + cancelButton.setProgress(toolsTransformProgress); + + tabsLayout.setTranslationY(AndroidUtilities.dp(32) * toolsTransformProgress); + if (keyboardAnimator != null && keyboardAnimator.isRunning()) { + moveBottomLayout[0] = false; + } + if (moveBottomLayout[0]) { + float progress = show ? toolsTransformProgress : 1f - toolsTransformProgress; + doneButton.setAlpha(lerp(doneButtonAlpha, show ? 1f : 0f, progress)); + cancelButton.setAlpha(lerp(doneButtonAlpha, show ? 1f : 0f, progress)); + bottomLayout.setTranslationY(bottomLayoutTranslationY - (AndroidUtilities.dp(39)) * progress * (show ? 1 : -1)); + } + bottomLayout.invalidate(); + + if (barView == textOptionsView) { + overlayLayout.invalidate(); + } + }); + toolsTransformAnimation.addEndListener((animation, canceled, value, velocity) -> { + if (animation == toolsTransformAnimation) { + toolsTransformAnimation = null; + + if (!show) { + colorsListView.setVisibility(GONE); + PersistColorPalette.getInstance(currentAccount).saveColors(); + colorsListView.getAdapter().notifyDataSetChanged(); + } + } + }); + toolsTransformAnimation.start(); + + if (show) { + colorsListView.setVisibility(VISIBLE); + colorsListView.setSelectedColorIndex(0); + } + } + } + + private void setCurrentSwatch(Swatch swatch, boolean updateInterface) { + if (colorSwatch != swatch) { + colorSwatch.color = swatch.color; + colorSwatch.colorLocation = swatch.colorLocation; + colorSwatch.brushWeight = swatch.brushWeight; + + PersistColorPalette.getInstance(currentAccount).selectColor(swatch.color); + PersistColorPalette.getInstance(currentAccount).setCurrentWeight(swatch.brushWeight); + } + + renderView.setColor(swatch.color); + renderView.setBrushSize(swatch.brushWeight); + + if (updateInterface) { + if (bottomLayout != null) { + bottomLayout.invalidate(); + } + } + + if (currentEntityView instanceof TextPaintView) { + ((TextPaintView) currentEntityView).setSwatch(new Swatch(swatch.color, swatch.colorLocation, swatch.brushWeight)); + } + } + + @Override + public boolean onBackPressed() { + if (isColorListShown) { + showColorList(false); + return true; + } + + if (emojiViewVisible) { + hideEmojiPopup(true); + return true; + } + + if (editingText) { + if (enteredThroughText) { + enteredThroughText = false; + keyboardNotifier.ignore(true); + return false; + } + selectEntity(null); + return true; + } + + return false; + } + + @Override + public void onColorPickerSelected() { + showColorList(true); + } + + @Override + public void onTextOutlineSelected(View v) { + setTextType((selectedTextType + 1) % 4); + } + + private PaintView.PopupButton buttonForPopup(String text, int icon, boolean selected, Runnable onClick) { + PaintView.PopupButton button = new PaintView.PopupButton(getContext()); + button.setIcon(icon); + button.setText(text); + button.setSelected(selected); + if (onClick != null) { + button.setOnClickListener(e -> onClick.run()); + } + return button; + } + + public class PopupButton extends LinearLayout { + + public TextView textView; + FrameLayout imagesView; + ImageView imageView; + ImageView image2View; + + float imageSwitchT; + boolean imageSwitchFill; + ValueAnimator imageSwitchAnimator; + + ImageView checkView; + + public PopupButton(Context context) { + super(context); + setOrientation(LinearLayout.HORIZONTAL); + setBackground(Theme.getSelectorDrawable(Theme.getColor(Theme.key_listSelector, resourcesProvider), false)); + + imagesView = new FrameLayout(context) { + Path path = new Path(); + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + if (imageSwitchAnimator != null) { + if (imageSwitchFill && child == image2View || !imageSwitchFill && child == imageView) { + float r = (imageSwitchFill ? imageSwitchT : 1f - imageSwitchT) * getMeasuredWidth() / 2f; + canvas.save(); + path.rewind(); + path.addCircle(getMeasuredWidth() / 2f, getMeasuredHeight() / 2f, r, Path.Direction.CW); + canvas.clipPath(path); + boolean res = super.drawChild(canvas, child, drawingTime); + canvas.restore(); + return res; + } + } + return super.drawChild(canvas, child, drawingTime); + } + }; + + addView(imagesView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.CENTER_VERTICAL, 16, 0, 16, 0)); + + imageView = new ImageView(context); + imageView.setScaleType(ImageView.ScaleType.CENTER); + imageView.setColorFilter(getThemedColor(Theme.key_actionBarDefaultSubmenuItem)); + imagesView.addView(imageView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); + image2View = new ImageView(context); + image2View.setScaleType(ImageView.ScaleType.CENTER); + image2View.setColorFilter(getThemedColor(Theme.key_actionBarDefaultSubmenuItem)); + image2View.setVisibility(View.GONE); + imagesView.addView(image2View, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); + + textView = new TextView(context); + textView.setTextColor(getThemedColor(Theme.key_actionBarDefaultSubmenuItem)); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.CENTER_VERTICAL, 0, 0, 16, 0)); + + checkView = new ImageView(context); + checkView.setImageResource(R.drawable.msg_text_check); + checkView.setScaleType(ImageView.ScaleType.CENTER); + checkView.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_radioBackgroundChecked), PorterDuff.Mode.MULTIPLY)); + checkView.setVisibility(View.GONE); + addView(checkView, LayoutHelper.createLinear(50, LayoutHelper.MATCH_PARENT)); + } + + public void setSelected(boolean selected) { + checkView.setVisibility(selected ? View.VISIBLE : View.GONE); + } + + public void setText(CharSequence text) { + textView.setText(text); + } + + public void setIcon(int resId) { + setIcon(resId, true,false); + } + + public void setIcon(int resId, boolean fillup, boolean animated) { + if (animated) { + if (imageSwitchAnimator != null) { + imageSwitchAnimator.cancel(); + imageSwitchAnimator = null; + setIcon(resId, false, false); + return; + } + imageSwitchFill = fillup; + image2View.setImageResource(resId); + image2View.setVisibility(View.VISIBLE); + image2View.setAlpha(1f); + imageSwitchAnimator = ValueAnimator.ofFloat(0, 1); + imageSwitchAnimator.addUpdateListener(anm -> { + imageSwitchT = (float) anm.getAnimatedValue(); + if (!fillup) { + imageView.setAlpha(1f - imageSwitchT); + } + imagesView.invalidate(); + }); + imageSwitchAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + ImageView buff = imageView; + imageView = image2View; + image2View = buff; + image2View.bringToFront(); + image2View.setVisibility(View.GONE); + imageSwitchAnimator = null; + } + }); + imageSwitchAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + imageSwitchAnimator.setDuration(420); + imageSwitchAnimator.start(); + } else { + imageView.setImageResource(resId); + } + } + + @Override + public boolean performClick() { + if (popupWindow != null && popupWindow.isShowing()) { + popupWindow.dismiss(true); + } + return super.performClick(); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + return true; + } + } + + private void setTextType(int type) { + selectedTextType = type; + if (currentEntityView instanceof TextPaintView) { + ((TextPaintView) currentEntityView).setType(type); + } + PersistColorPalette.getInstance(currentAccount).setCurrentTextType(type); + textOptionsView.setOutlineType(type, true); + } + + @Override + public void onNewTextSelected() { + if (keyboardVisible || emojiViewVisible) { + onEmojiButtonClick(); + } else { + forceChanges = true; + createText(true); + } + } + + @Override + public void onTypefaceSelected(PaintTypeface typeface) { + PersistColorPalette.getInstance(currentAccount).setCurrentTypeface(typeface.getKey()); + if (currentEntityView instanceof TextPaintView) { + ((TextPaintView) currentEntityView).setTypeface(typeface); + } + } + + @Override + public void onTextAlignmentSelected(int align) { + if (currentEntityView instanceof TextPaintView) { + setTextAlignment((TextPaintView) currentEntityView, align); + PersistColorPalette.getInstance(currentAccount).setCurrentAlignment(align); + } + } + + private void setTextAlignment(TextPaintView textPaintView, int align) { + textPaintView.setAlign(align); + + int gravity; + switch (align) { + default: + case PaintTextOptionsView.ALIGN_LEFT: + gravity = Gravity.LEFT | Gravity.CENTER_VERTICAL; + break; + case PaintTextOptionsView.ALIGN_CENTER: + gravity = Gravity.CENTER; + break; + case PaintTextOptionsView.ALIGN_RIGHT: + gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL; + break; + } + + textPaintView.getEditText().setGravity(gravity); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + int textAlign; + switch (align) { + default: + case PaintTextOptionsView.ALIGN_LEFT: + textAlign = LocaleController.isRTL ? TEXT_ALIGNMENT_TEXT_END : TEXT_ALIGNMENT_TEXT_START; + break; + case PaintTextOptionsView.ALIGN_CENTER: + textAlign = TEXT_ALIGNMENT_CENTER; + break; + case PaintTextOptionsView.ALIGN_RIGHT: + textAlign = LocaleController.isRTL ? TEXT_ALIGNMENT_TEXT_START : TEXT_ALIGNMENT_TEXT_END; + break; + } + textPaintView.getEditText().setTextAlignment(textAlign); + } + } + + @Override + public void onAddButtonPressed(View btn) { + showPopup(() -> { + boolean fill = PersistColorPalette.getInstance(currentAccount).getFillShapes(); + for (int a = 0; a < Brush.Shape.SHAPES_LIST.size(); a++) { + final Brush.Shape shape = Brush.Shape.SHAPES_LIST.get(a); + int icon = fill ? shape.getFilledIconRes() : shape.getIconRes(); + PaintView.PopupButton button = buttonForPopup(shape.getShapeName(), icon, false, () -> { + if (renderView.getCurrentBrush() instanceof Brush.Shape) { + ignoreToolChangeAnimationOnce = true; + } + onBrushSelected(shape); + paintToolsView.animatePlusToIcon(icon); + }); + button.setOnLongClickListener(e -> { + if (popupLayout != null) { + PersistColorPalette.getInstance(currentAccount).toggleFillShapes(); + boolean newfill = PersistColorPalette.getInstance(currentAccount).getFillShapes(); + for (int i = 0; i < popupLayout.getItemsCount(); ++i) { + View child = popupLayout.getItemAt(i); + if (child instanceof PaintView.PopupButton) { + final Brush.Shape thisshape = Brush.Shape.SHAPES_LIST.get(i); + int thisicon = newfill ? thisshape.getFilledIconRes() : thisshape.getIconRes(); + ((PaintView.PopupButton) child).setIcon(thisicon, newfill, true); + } + } + } + return true; + }); + popupLayout.addView(button, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48)); + } + }, this, Gravity.RIGHT | Gravity.TOP, 0, getHeight()); + } + + private void showMenuForEntity(final EntityView entityView) { + int[] pos = getCenterLocationInWindow(entityView); + int x = pos[0]; + int y = pos[1] - AndroidUtilities.dp(32); + + showPopup(() -> { + LinearLayout parent = new LinearLayout(getContext()); + parent.setOrientation(LinearLayout.HORIZONTAL); + + TextView deleteView = new TextView(getContext()); + deleteView.setTextColor(getThemedColor(Theme.key_actionBarDefaultSubmenuItem)); + deleteView.setBackground(Theme.getSelectorDrawable(false)); + deleteView.setGravity(Gravity.CENTER_VERTICAL); + deleteView.setEllipsize(TextUtils.TruncateAt.END); + deleteView.setPadding(AndroidUtilities.dp(16), 0, AndroidUtilities.dp(14), 0); + deleteView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + deleteView.setTag(0); + deleteView.setText(LocaleController.getString("PaintDelete", R.string.PaintDelete)); + deleteView.setOnClickListener(v -> { + removeEntity(entityView); + + if (popupWindow != null && popupWindow.isShowing()) { + popupWindow.dismiss(true); + } + }); + parent.addView(deleteView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 48)); + + if (entityView instanceof TextPaintView) { + TextView editView = new TextView(getContext()); + editView.setTextColor(getThemedColor(Theme.key_actionBarDefaultSubmenuItem)); + editView.setBackground(Theme.getSelectorDrawable(false)); + editView.setGravity(Gravity.CENTER_VERTICAL); + editView.setEllipsize(TextUtils.TruncateAt.END); + editView.setPadding(AndroidUtilities.dp(16), 0, AndroidUtilities.dp(16), 0); + editView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + if ((keyboardNotifier.keyboardVisible() && !keyboardNotifier.ignoring) || emojiPadding > 0) { + editView.setTag(3); + editView.setText(LocaleController.getString("Paste", R.string.Paste)); + editView.setOnClickListener(v -> { + try { + EditText editText = ((TextPaintView) entityView).getEditText(); + editText.onTextContextMenuItem(android.R.id.pasteAsPlainText); + } catch (Exception e) { + FileLog.e(e); + } + if (popupWindow != null && popupWindow.isShowing()) { + popupWindow.dismiss(true); + } + }); + } else { + editView.setTag(1); + editView.setText(LocaleController.getString("PaintEdit", R.string.PaintEdit)); + editView.setOnClickListener(v -> { + selectEntity(entityView); + editSelectedTextEntity(); + if (popupWindow != null && popupWindow.isShowing()) { + popupWindow.dismiss(true); + } + }); + } + parent.addView(editView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 48)); + } + + if (entityView instanceof StickerView || entityView instanceof PhotoView) { + TextView flipView = new TextView(getContext()); + flipView.setTextColor(getThemedColor(Theme.key_actionBarDefaultSubmenuItem)); + flipView.setBackground(Theme.getSelectorDrawable(false)); + flipView.setEllipsize(TextUtils.TruncateAt.END); + flipView.setGravity(Gravity.CENTER_VERTICAL); + flipView.setPadding(AndroidUtilities.dp(16), 0, AndroidUtilities.dp(16), 0); + flipView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + flipView.setTag(4); + flipView.setText(LocaleController.getString(R.string.Flip)); + flipView.setOnClickListener(v -> { + if (entityView instanceof StickerView) { + ((StickerView) entityView).mirror(true); + } else { + ((PhotoView) entityView).mirror(true); + } + if (popupWindow != null && popupWindow.isShowing()) { + popupWindow.dismiss(true); + } + }); + parent.addView(flipView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 48)); + } + + if (!(entityView instanceof PhotoView)) { + TextView duplicateView = new TextView(getContext()); + duplicateView.setTextColor(getThemedColor(Theme.key_actionBarDefaultSubmenuItem)); + duplicateView.setBackground(Theme.getSelectorDrawable(false)); + duplicateView.setEllipsize(TextUtils.TruncateAt.END); + duplicateView.setGravity(Gravity.CENTER_VERTICAL); + duplicateView.setPadding(AndroidUtilities.dp(14), 0, AndroidUtilities.dp(16), 0); + duplicateView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + duplicateView.setTag(2); + duplicateView.setText(LocaleController.getString("PaintDuplicate", R.string.PaintDuplicate)); + duplicateView.setOnClickListener(v -> { + duplicateEntity(entityView); + + if (popupWindow != null && popupWindow.isShowing()) { + popupWindow.dismiss(true); + } + }); + parent.addView(duplicateView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 48)); + } + + popupLayout.addView(parent); + + LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) parent.getLayoutParams(); + params.width = LayoutHelper.WRAP_CONTENT; + params.height = LayoutHelper.WRAP_CONTENT; + parent.setLayoutParams(params); + }, this, Gravity.LEFT | Gravity.TOP, x, y); + } + + private void duplicateEntity(EntityView thisEntityView) { + if (thisEntityView == null) { + return; + } + + EntityView entityView = null; + Point position = startPositionRelativeToEntity(thisEntityView); + + if (thisEntityView instanceof StickerView) { + StickerView newStickerView = new StickerView(getContext(), (StickerView) thisEntityView, position); + newStickerView.setDelegate(this); + entitiesView.addView(newStickerView); + entityView = newStickerView; + } else if (thisEntityView instanceof TextPaintView) { + TextPaintView newTextPaintView = new TextPaintView(getContext(), (TextPaintView) thisEntityView, position); + newTextPaintView.getEditText().betterFraming = true; + newTextPaintView.setDelegate(this); + newTextPaintView.setMaxWidth(w - AndroidUtilities.dp(7 + 7 + 18)); + entitiesView.addView(newTextPaintView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT)); + entityView = newTextPaintView; + } else { + return; + } + + registerRemovalUndo(entityView); + selectEntity(entityView); + } + + private Point startPositionRelativeToEntity(EntityView entityView) { + float offset = 200.0f; + if (currentCropState != null) { + offset /= currentCropState.cropScale; + } + + if (entityView != null) { + Point position = entityView.getPosition(); + return new Point(position.x, position.y + entityView.getHeight()); + } else { + float minimalDistance = 100.0f; + if (currentCropState != null) { + minimalDistance /= currentCropState.cropScale; + } + Point position = centerPositionForEntity(); + while (true) { + boolean occupied = false; + for (int index = 0; index < entitiesView.getChildCount(); index++) { + View view = entitiesView.getChildAt(index); + if (!(view instanceof EntityView)) + continue; + + Point location = ((EntityView) view).getPosition(); + float distance = (float) Math.sqrt(Math.pow(location.x - position.x, 2) + Math.pow(location.y - position.y, 2)); + if (distance < minimalDistance) { + offset = view.getHeight(); + occupied = true; + } + } + + if (!occupied) { + break; + } else { + position = new Point(position.x, position.y + offset); + } + } + return position; + } + } + + private void showPopup(Runnable setupRunnable, View parent, int gravity, int x, int y) { + if (popupWindow != null && popupWindow.isShowing()) { + popupWindow.dismiss(); + return; + } + + if (popupLayout == null) { + popupRect = new android.graphics.Rect(); + popupLayout = new ActionBarPopupWindow.ActionBarPopupWindowLayout(getContext(), resourcesProvider); + popupLayout.setAnimationEnabled(true); + popupLayout.setBackgroundColor(-14145495); + popupLayout.setOnTouchListener((v, event) -> { + if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { + if (popupWindow != null && popupWindow.isShowing()) { + v.getHitRect(popupRect); + if (!popupRect.contains((int) event.getX(), (int) event.getY())) { + popupWindow.dismiss(); + } + } + } + return false; + }); + popupLayout.setDispatchKeyEventListener(keyEvent -> { + if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_BACK && keyEvent.getRepeatCount() == 0 && popupWindow != null && popupWindow.isShowing()) { + popupWindow.dismiss(); + } + }); + popupLayout.setShownFromBottom(true); + } + + popupLayout.removeInnerViews(); + setupRunnable.run(); + + if (popupWindow == null) { + popupWindow = new ActionBarPopupWindow(popupLayout, LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT); + popupWindow.setAnimationEnabled(true); + popupWindow.setAnimationStyle(R.style.PopupAnimation); + popupWindow.setOutsideTouchable(true); + popupWindow.setClippingEnabled(true); + popupWindow.setInputMethodMode(ActionBarPopupWindow.INPUT_METHOD_NOT_NEEDED); + popupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED); + popupWindow.getContentView().setFocusableInTouchMode(true); + popupWindow.setOnDismissListener(() -> popupLayout.removeInnerViews()); + } + + popupLayout.measure(MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(1000), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(1000), MeasureSpec.AT_MOST)); + + popupWindow.setFocusable(true); + + if ((gravity & Gravity.TOP) != 0) { + x -= popupLayout.getMeasuredWidth() / 2; + y -= popupLayout.getMeasuredHeight(); + } + popupWindow.showAtLocation(parent, gravity, x, y); + popupWindow.startAnimation(popupLayout); + } + + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); + } + + @Override + public PersistColorPalette onGetPalette() { + return PersistColorPalette.getInstance(currentAccount); + } + + private Size baseStickerSize() { + float side = (float) Math.floor(getPaintingSize().width * 0.5); + return new Size(side, side); + } + + private Size basePhotoSize(String path) { + float a = 1f; + try { + BitmapFactory.Options opts = new BitmapFactory.Options(); + opts.inJustDecodeBounds = true; + BitmapFactory.decodeFile(path, opts); + a = (float) opts.outWidth / opts.outHeight; + } catch (Exception e) { + FileLog.e(e); + } + if (a > 1) { + float side = (float) Math.floor(Math.max(w, entitiesView.getMeasuredWidth()) * 0.5); + return new Size(side, side / a); + } else { + float side = (float) Math.floor(Math.max(h, entitiesView.getMeasuredHeight()) * 0.5); + return new Size(side * a, side); + } + } + + public void appearAnimation(View view) { + float scaleX = view.getScaleX(), scaleY = view.getScaleY(); + view.setScaleX(scaleX * .5f); + view.setScaleY(scaleY * .5f); + view.setAlpha(0f); + view.animate().scaleX(scaleX).scaleY(scaleY).alpha(1f).setInterpolator(new OvershootInterpolator(3f)).setDuration(240).withEndAction(() -> { + if (view instanceof EntityView) { + ((EntityView) view).updateSelectionView(); + selectEntity((EntityView) view); + } + }).start(); + } + + private Point centerPositionForEntity() { + int w = entitiesView.getMeasuredWidth(), h = entitiesView.getMeasuredHeight(); + if (w <= 0) w = this.w; + if (h <= 0) h = this.h; + float x = w / 2.0f; + float y = h / 2.0f; + return new Point(x, y); + } + + private PaintView.StickerPosition calculateStickerPosition(TLRPC.Document document) { + TLRPC.TL_maskCoords maskCoords = null; + + for (int a = 0; a < document.attributes.size(); a++) { + TLRPC.DocumentAttribute attribute = document.attributes.get(a); + if (attribute instanceof TLRPC.TL_documentAttributeSticker) { + maskCoords = attribute.mask_coords; + break; + } + } + + float rotation; + float baseScale; + if (currentCropState != null) { + rotation = -(currentCropState.transformRotation + currentCropState.cropRotate); + baseScale = 0.75f / currentCropState.cropScale; + } else { + rotation = 0.0f; + baseScale = 0.75f; + } + PaintView.StickerPosition defaultPosition = new PaintView.StickerPosition(centerPositionForEntity(), baseScale, rotation); + if (maskCoords == null || faces == null || faces.size() == 0) { + return defaultPosition; + } else { + int anchor = maskCoords.n; + + PhotoFace face = getRandomFaceWithVacantAnchor(anchor, document.id, maskCoords); + if (face == null) { + return defaultPosition; + } + + Point referencePoint = face.getPointForAnchor(anchor); + float referenceWidth = face.getWidthForAnchor(anchor); + float angle = face.getAngle(); + Size baseSize = baseStickerSize(); + + float scale = (float) (referenceWidth / baseSize.width * maskCoords.zoom); + +// float radAngle = (float) Math.toRadians(angle); +// float xCompX = (float) (Math.sin(Math.PI / 2.0f - radAngle) * referenceWidth * maskCoords.x); +// float xCompY = (float) (Math.cos(Math.PI / 2.0f - radAngle) * referenceWidth * maskCoords.x); +// +// float yCompX = (float) (Math.cos(Math.PI / 2.0f + radAngle) * referenceWidth * maskCoords.y); +// float yCompY = (float) (Math.sin(Math.PI / 2.0f + radAngle) * referenceWidth * maskCoords.y); + + float x = referencePoint.x; + float y = referencePoint.y; + + return new PaintView.StickerPosition(new Point(x, y), scale, angle); + } + } + + private PhotoFace getRandomFaceWithVacantAnchor(int anchor, long documentId, TLRPC.TL_maskCoords maskCoords) { + if (anchor < 0 || anchor > 3 || faces.isEmpty()) { + return null; + } + + int count = faces.size(); + int randomIndex = Utilities.random.nextInt(count); + int remaining = count; + + PhotoFace selectedFace = null; + for (int i = randomIndex; remaining > 0; i = (i + 1) % count, remaining--) { + PhotoFace face = faces.get(i); + if (!isFaceAnchorOccupied(face, anchor, documentId, maskCoords)) { + return face; + } + } + + return selectedFace; + } + + private boolean isFaceAnchorOccupied(PhotoFace face, int anchor, long documentId, TLRPC.TL_maskCoords maskCoords) { + Point anchorPoint = face.getPointForAnchor(anchor); + if (anchorPoint == null) { + return true; + } + + float minDistance = face.getWidthForAnchor(0) * 1.1f; + + for (int index = 0; index < entitiesView.getChildCount(); index++) { + View view = entitiesView.getChildAt(index); + if (!(view instanceof StickerView)) { + continue; + } + + StickerView stickerView = (StickerView) view; + if (stickerView.getAnchor() != anchor) { + continue; + } + + Point location = stickerView.getPosition(); + float distance = (float)Math.hypot(location.x - anchorPoint.x, location.y - anchorPoint.y); + if ((documentId == stickerView.getSticker().id || faces.size() > 1) && distance < minDistance) { + return true; + } + } + + return false; + } + + public PhotoView createPhoto(String path, boolean select) { + Size size = basePhotoSize(path); + Pair orientation = AndroidUtilities.getImageOrientation(path); + if ((orientation.first / 90 % 2) == 1) { + float w = size.width; + size.width = size.height; + size.height = w; + } + PhotoView view = new PhotoView(getContext(), centerPositionForEntity(), 0, 1f, size, path, orientation.first, orientation.second); + view.centerImage.setLayerNum(4 + 8); +// view.setHasStickyX(true); +// view.setHasStickyY(true); + view.setDelegate(this); + entitiesView.addView(view); + if (select) { + registerRemovalUndo(view); + selectEntity(view); + } + return view; + } + + private StickerView createSticker(Object parentObject, TLRPC.Document sticker, boolean select) { + PaintView.StickerPosition position = calculateStickerPosition(sticker); + StickerView view = new StickerView(getContext(), position.position, position.angle, position.scale, baseStickerSize(), sticker, parentObject) { + @Override + protected void didSetAnimatedSticker(RLottieDrawable drawable) { + PaintView.this.didSetAnimatedSticker(drawable); + } + }; + if (MessageObject.isTextColorEmoji(sticker)) { + view.centerImage.setColorFilter(new PorterDuffColorFilter(0xffffffff, PorterDuff.Mode.SRC_IN)); + } + view.centerImage.setLayerNum(4 + 8); +// if (position.position.x == entitiesView.getMeasuredWidth() / 2f) { +// view.setHasStickyX(true); +// } +// if (position.position.y == entitiesView.getMeasuredHeight() / 2f) { +// view.setHasStickyY(true); +// } + view.setDelegate(this); + entitiesView.addView(view); + if (select) { + registerRemovalUndo(view); + selectEntity(view); + } + return view; + } + + public void removeCurrentEntity() { + if (currentEntityView != null) { + removeEntity(currentEntityView); + } + } + + private void removeEntity(EntityView entityView) { + if (entityView == currentEntityView && currentEntityView != null) { + currentEntityView.deselect(); + selectEntity(null); + + if (entityView instanceof TextPaintView) { + if (tabsSelectionAnimator != null && tabsNewSelectedIndex != 0) { + tabsSelectionAnimator.cancel(); + } + switchTab(0); + } + } + entitiesView.removeView(entityView); + if (entityView != null) { + undoStore.unregisterUndo(entityView.getUUID()); + } + + weightChooserView.setValueOverride(weightDefaultValueOverride); + weightChooserView.setShowPreview(true); + colorSwatch.brushWeight = weightDefaultValueOverride.get(); + setCurrentSwatch(colorSwatch, true); + } + + private void registerRemovalUndo(final EntityView entityView) { + if (entityView == null) { + return; + } + undoStore.registerUndo(entityView.getUUID(), () -> removeEntity(entityView)); + } + + protected void didSetAnimatedSticker(RLottieDrawable drawable) {} + + @Override + public boolean onEntitySelected(EntityView entityView) { + return selectEntity(entityView); + } + + @Override + public void onEntityDragEnd(boolean delete) { + updatePreviewViewTranslationY(); + } + + @Override + public void onEntityDragStart() { + + } + + @Override + public boolean onEntityLongClicked(EntityView entityView) { + showMenuForEntity(entityView); + return true; + } + + private final float[] temp = new float[2]; + private final int[] loc = new int[2]; + + @Override + public float[] getTransformedTouch(MotionEvent e, float rawX, float rawY) { + View previewView = (View) renderView.getParent(); + View containerView = (View) previewView.getParent(); + float x = rawX - previewView.getX() - containerView.getLeft(); + float y = rawY - previewView.getY() - containerView.getTop(); + x = previewView.getPivotX() + (x - previewView.getPivotX()) / previewView.getScaleX(); + y = previewView.getPivotY() + (y - previewView.getPivotY()) / previewView.getScaleY(); + temp[0] = x; + temp[1] = y; + return temp; + } + + @Override + public int[] getCenterLocation(EntityView entityView) { + pos[0] = (int) entityView.getPosition().x; + pos[1] = (int) entityView.getPosition().y; + return pos; + } + + private int[] pos = new int[2]; + private int[] getCenterLocationInWindow(View view) { + view.getLocationInWindow(pos); +// float rotation = (float) Math.toRadians(view.getRotation() + (currentCropState != null ? currentCropState.cropRotate + currentCropState.transformRotation : 0)); + float width = view.getWidth() * view.getScaleX() * entitiesView.getScaleX(); + float height = view.getHeight() * view.getScaleY() * entitiesView.getScaleY(); +// float px = (float) (width * Math.cos(rotation) - height * Math.sin(rotation)); +// float py = (float) (width * Math.sin(rotation) + height * Math.cos(rotation)); + pos[0] += width / 2; + pos[1] += height / 2; + return pos; + } + + @Override + public boolean allowInteraction(EntityView entityView) { + return !editingText; + } + + @Override + public float getCropRotation() { + return currentCropState != null ? currentCropState.cropRotate + currentCropState.transformRotation : 0; + } + + private static class StickerPosition { + private Point position; + private float scale; + private float angle; + + StickerPosition(Point position, float scale, float angle) { + this.position = position; + this.scale = scale; + this.angle = angle; + } + } + + /* === emoji keyboard support === */ + + public EmojiView emojiView; + private boolean emojiViewVisible, emojiViewWasVisible; + private boolean keyboardVisible, isAnimatePopupClosing; + private int emojiPadding, emojiWasPadding; + private boolean translateBottomPanelAfterResize; + private int keyboardHeight, keyboardHeightLand; + private boolean waitingForKeyboardOpen; + private int lastSizeChangeValue1; + private boolean lastSizeChangeValue2; + private boolean destroyed; + + private Runnable openKeyboardRunnable = new Runnable() { + @Override + public void run() { + if (!(currentEntityView instanceof TextPaintView)) { + return; + } + final EditTextOutline editText = ((TextPaintView) currentEntityView).getEditText(); + if (!destroyed && editText != null && waitingForKeyboardOpen && !keyboardVisible && !AndroidUtilities.usingHardwareInput && !AndroidUtilities.isInMultiwindow && AndroidUtilities.isTablet()) { + editText.requestFocus(); + AndroidUtilities.showKeyboard(editText); + AndroidUtilities.cancelRunOnUIThread(openKeyboardRunnable); + AndroidUtilities.runOnUIThread(openKeyboardRunnable, 100); + } + } + }; + + @Override + public void onEmojiButtonClick() { + boolean emojiViewWasVisible = emojiViewVisible; + if (emojiViewWasVisible && currentEntityView instanceof TextPaintView) { + keyboardNotifier.awaitKeyboard(); + final EditTextOutline editText = ((TextPaintView) currentEntityView).getEditText(); + AndroidUtilities.showKeyboard(editText); + } + showEmojiPopup(emojiViewVisible ? 0 : 1); + } + + private void showEmojiPopup(int show) { + if (show == 1) { + boolean emojiWasVisible = emojiView != null && emojiView.getVisibility() == View.VISIBLE; + createEmojiView(); + + emojiView.setVisibility(VISIBLE); + emojiViewWasVisible = emojiViewVisible; + emojiViewVisible = true; + View currentView = emojiView; + + if (keyboardHeight <= 0) { + if (AndroidUtilities.isTablet()) { + keyboardHeight = AndroidUtilities.dp(150); + } else { + keyboardHeight = MessagesController.getGlobalEmojiSettings().getInt("kbd_height", AndroidUtilities.dp(200)); + } + } + if (keyboardHeightLand <= 0) { + if (AndroidUtilities.isTablet()) { + keyboardHeightLand = AndroidUtilities.dp(150); + } else { + keyboardHeightLand = MessagesController.getGlobalEmojiSettings().getInt("kbd_height_land3", AndroidUtilities.dp(200)); + } + } + int currentHeight = (AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y ? keyboardHeightLand : keyboardHeight) + parent.getPaddingUnderContainer(); + + FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) currentView.getLayoutParams(); + layoutParams.height = currentHeight; + currentView.setLayoutParams(layoutParams); + if (!AndroidUtilities.isInMultiwindow && !AndroidUtilities.isTablet() && currentEntityView instanceof TextPaintView) { + final EditTextOutline editText = ((TextPaintView) currentEntityView).getEditText(); + AndroidUtilities.hideKeyboard(editText); + } + + emojiPadding = emojiWasPadding = currentHeight; + keyboardNotifier.fire(); + requestLayout(); + + ChatActivityEnterViewAnimatedIconView emojiButton = textOptionsView.getEmojiButton(); + if (emojiButton != null) { + emojiButton.setState(ChatActivityEnterViewAnimatedIconView.State.KEYBOARD, true); + } + + if (!emojiWasVisible) { + if (keyboardVisible) { + translateBottomPanelAfterResize = true; + } else { + ValueAnimator animator = ValueAnimator.ofFloat(emojiPadding, 0); + animator.addUpdateListener(animation -> { + float v = (float) animation.getAnimatedValue(); + emojiView.setTranslationY(v); + }); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + emojiView.setTranslationY(0); + } + }); + animator.setDuration(AdjustPanLayoutHelper.keyboardDuration); + animator.setInterpolator(AdjustPanLayoutHelper.keyboardInterpolator); + animator.start(); + } + + } + } else { + ChatActivityEnterViewAnimatedIconView emojiButton = textOptionsView.getEmojiButton(); + if (emojiButton != null) { + emojiButton.setState(ChatActivityEnterViewAnimatedIconView.State.SMILE, true); + } + if (emojiView != null) { + emojiViewWasVisible = emojiViewVisible; + emojiViewVisible = false; + if (AndroidUtilities.usingHardwareInput || AndroidUtilities.isInMultiwindow) { + emojiView.setVisibility(GONE); + } + } + if (show == 0) { + emojiPadding = 0; + keyboardNotifier.fire(); + } + requestLayout(); + } + + updatePlusEmojiKeyboardButton(); + } + + private void hideEmojiPopup(boolean byBackButton) { + if (emojiViewVisible) { + showEmojiPopup(0); + } + if (byBackButton) { + if (emojiView != null && emojiView.getVisibility() == View.VISIBLE && !waitingForKeyboardOpen) { + int height = emojiView.getMeasuredHeight(); + ValueAnimator animator = ValueAnimator.ofFloat(0, height); + animator.addUpdateListener(animation -> { + float v = (float) animation.getAnimatedValue(); + emojiView.setTranslationY(v); + }); + isAnimatePopupClosing = true; + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + isAnimatePopupClosing = false; + emojiView.setTranslationY(0); + hideEmojiView(); + } + }); + animator.setDuration(AdjustPanLayoutHelper.keyboardDuration); + animator.setInterpolator(AdjustPanLayoutHelper.keyboardInterpolator); + animator.start(); + } else { + hideEmojiView(); + } + } + } + + @Override + public int getEmojiPadding(boolean panned) { + return emojiPadding; + } + + private void hideEmojiView() { + if (!emojiViewVisible && emojiView != null && emojiView.getVisibility() != GONE) { + emojiView.setVisibility(GONE); + } + int wasEmojiPadding = emojiPadding; + emojiPadding = 0; + if (wasEmojiPadding != emojiPadding) { + keyboardNotifier.fire(); + } + } + + @Override + public int measureKeyboardHeight() { + return keyboardNotifier.getKeyboardHeight() - parent.getBottomPadding(false); + } + + @Override + public void onSizeChanged(int height, boolean isWidthGreater) { + if (height > AndroidUtilities.dp(50) && keyboardVisible && !AndroidUtilities.isInMultiwindow && !AndroidUtilities.isTablet()) { + if (isWidthGreater) { + keyboardHeightLand = height; + MessagesController.getGlobalEmojiSettings().edit().putInt("kbd_height_land3", keyboardHeightLand).commit(); + } else { + keyboardHeight = height; + MessagesController.getGlobalEmojiSettings().edit().putInt("kbd_height", keyboardHeight).commit(); + } + } + + if (emojiViewVisible) { + int newHeight = (isWidthGreater ? keyboardHeightLand : keyboardHeight) + parent.getPaddingUnderContainer(); + + FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) emojiView.getLayoutParams(); + if (layoutParams.width != AndroidUtilities.displaySize.x || layoutParams.height != newHeight) { + layoutParams.width = AndroidUtilities.displaySize.x; + layoutParams.height = newHeight; + emojiView.setLayoutParams(layoutParams); + + emojiPadding = emojiWasPadding = layoutParams.height; + keyboardNotifier.fire(); + requestLayout(); + } + } + + if (lastSizeChangeValue1 == height && lastSizeChangeValue2 == isWidthGreater) { + return; + } + lastSizeChangeValue1 = height; + lastSizeChangeValue2 = isWidthGreater; + + boolean oldValue = keyboardVisible; + if (currentEntityView instanceof TextPaintView) { + final EditTextOutline editText = ((TextPaintView) currentEntityView).getEditText(); + keyboardVisible = editText.isFocused() && keyboardNotifier.keyboardVisible(); + } else { + keyboardVisible = false; + } + if (keyboardVisible && emojiViewVisible) { + showEmojiPopup(0); + } + if (emojiPadding != 0 && !keyboardVisible && keyboardVisible != oldValue && !emojiViewVisible) { + emojiPadding = 0; + keyboardNotifier.fire(); + requestLayout(); + } + updateTextDim(); + if (oldValue && !keyboardVisible && emojiPadding > 0 && translateBottomPanelAfterResize) { + translateBottomPanelAfterResize = false; + } + if (keyboardVisible && waitingForKeyboardOpen) { + waitingForKeyboardOpen = false; + AndroidUtilities.cancelRunOnUIThread(openKeyboardRunnable); + } + + updatePlusEmojiKeyboardButton(); + } + + private void updateTextDim() { + final boolean show = currentEntityView instanceof TextPaintView && (keyboardNotifier.keyboardVisible() || emojiPadding > 0) && !keyboardNotifier.ignoring; + textDim.animate().cancel(); + textDim.setVisibility(View.VISIBLE); + textDim.animate().alpha(show ? 1f : 0f).withEndAction(() -> { + if (!show) { + textDim.setVisibility(View.GONE); + } + }).start(); + } + + private void updatePlusEmojiKeyboardButton() { + if (textOptionsView != null) { + if (keyboardNotifier.keyboardVisible()) { + textOptionsView.animatePlusToIcon(R.drawable.input_smile); + } else if (emojiViewVisible) { + textOptionsView.animatePlusToIcon(R.drawable.input_keyboard); + } else { + textOptionsView.animatePlusToIcon(R.drawable.msg_add); + } + } + + final boolean text = keyboardNotifier.keyboardVisible() || emojiViewVisible; + + AndroidUtilities.updateViewShow(undoAllButton, !text, false, 1, true, null); + AndroidUtilities.updateViewShow(undoButton, !text, false, 1, true, null); + + AndroidUtilities.updateViewShow(doneTextButton, text, false, 1, true, null); + AndroidUtilities.updateViewShow(cancelTextButton, text, false, 1, true, null); + } + + protected void createEmojiView() { + if (emojiView != null && emojiView.currentAccount != UserConfig.selectedAccount) { + parent.removeView(emojiView); + emojiView = null; + } + if (emojiView != null) { + return; + } + emojiView = new EmojiView(null, true, false, false, getContext(), false, null, null, true, resourcesProvider); + emojiView.fixBottomTabContainerTranslation = false; + emojiView.allowEmojisForNonPremium(true); + emojiView.setVisibility(GONE); + if (AndroidUtilities.isTablet()) { + emojiView.setForseMultiwindowLayout(true); + } + emojiView.setDelegate(new EmojiView.EmojiViewDelegate() { + int innerTextChange; + + @Override + public boolean onBackspace() { + final EditTextOutline editText = ((TextPaintView) currentEntityView).getEditText(); + if (editText == null || editText.length() == 0) { + return false; + } + editText.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL)); + return true; + } + + @Override + public void onAnimatedEmojiUnlockClick() { +// BaseFragment fragment = parentFragment; +// if (fragment == null) { +// fragment = new BaseFragment() { +// @Override +// public int getCurrentAccount() { +// return currentAccount; +// } +// +// @Override +// public Context getContext() { +// return EditTextEmoji.this.getContext(); +// } +// +// @Override +// public Activity getParentActivity() { +// Context context = getContext(); +// while (context instanceof ContextWrapper) { +// if (context instanceof Activity) { +// return (Activity) context; +// } +// context = ((ContextWrapper) context).getBaseContext(); +// } +// return null; +// } +// +// @Override +// public Dialog getVisibleDialog() { +// return new Dialog(getContext()) { +// @Override +// public void dismiss() { +// hidePopup(false); +// closeParent(); +// } +// }; +// } +// }; +// new PremiumFeatureBottomSheet(fragment, PremiumPreviewFragment.PREMIUM_FEATURE_ANIMATED_EMOJI, false).show(); +// } else { +// fragment.showDialog(new PremiumFeatureBottomSheet(fragment, PremiumPreviewFragment.PREMIUM_FEATURE_ANIMATED_EMOJI, false)); +// } + } + + @Override + public void onEmojiSelected(String symbol) { + if (!(currentEntityView instanceof TextPaintView)) { + return; + } + TextPaintView textPaintView = (TextPaintView) currentEntityView; + final EditTextOutline editText = textPaintView.getEditText(); + if (editText == null) { + return; + } + int i = editText.getSelectionEnd(); + if (i < 0) { + i = 0; + } + try { + innerTextChange = 2; + CharSequence localCharSequence = Emoji.replaceEmoji(symbol, textPaintView.getFontMetricsInt(), (int) (textPaintView.getFontSize() * .8f), false); + if (localCharSequence instanceof Spanned) { + Emoji.EmojiSpan[] spans = ((Spanned) localCharSequence).getSpans(0, localCharSequence.length(), Emoji.EmojiSpan.class); + if (spans != null) { + for (int a = 0; a < spans.length; ++a) { + spans[a].scale = .85f; + } + } + } + editText.setText(editText.getText().insert(i, localCharSequence)); + int j = i + localCharSequence.length(); + editText.setSelection(j, j); + } catch (Exception e) { + FileLog.e(e); + } finally { + innerTextChange = 0; + } + } + + @Override + public void onCustomEmojiSelected(long documentId, TLRPC.Document document, String emoticon, boolean isRecent) { + final EditTextOutline editText = ((TextPaintView) currentEntityView).getEditText(); + if (editText == null) { + return; + } + int i = editText.getSelectionEnd(); + if (i < 0) { + i = 0; + } + try { + innerTextChange = 2; + SpannableString spannable = new SpannableString(emoticon); + AnimatedEmojiSpan span; + if (document != null) { + span = new AnimatedEmojiSpan(document, 1f, editText.getPaint().getFontMetricsInt()); + } else { + span = new AnimatedEmojiSpan(documentId, 1f, editText.getPaint().getFontMetricsInt()); + } + spannable.setSpan(span, 0, spannable.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + editText.setText(editText.getText().insert(i, spannable)); + int j = i + spannable.length(); + editText.setSelection(j, j); + } catch (Exception e) { + FileLog.e(e); + } finally { + innerTextChange = 0; + } + } + + @Override + public void onClearEmojiRecent() { + AlertDialog.Builder builder = new AlertDialog.Builder(getContext(), resourcesProvider); + builder.setTitle(LocaleController.getString("ClearRecentEmojiTitle", R.string.ClearRecentEmojiTitle)); + builder.setMessage(LocaleController.getString("ClearRecentEmojiText", R.string.ClearRecentEmojiText)); + builder.setPositiveButton(LocaleController.getString("ClearButton", R.string.ClearButton), (dialogInterface, i) -> emojiView.clearRecentEmoji()); + builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + builder.show(); + } + }); + parent.addView(emojiView); + } + + @Override + public float adjustPanLayoutHelperProgress() { + return 0; + } + + @Override + protected void onAttachedToWindow() { + destroyed = false; + super.onAttachedToWindow(); + } + + @Override + protected void onDetachedFromWindow() { + destroyed = true; + super.onDetachedFromWindow(); + } + + protected void onGalleryClick() { + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/PhotoVideoSwitcherView.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/PhotoVideoSwitcherView.java new file mode 100644 index 0000000000..838ab15fb8 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/PhotoVideoSwitcherView.java @@ -0,0 +1,248 @@ +package org.telegram.ui.Stories.recorder; + +import static org.telegram.messenger.AndroidUtilities.dp; + +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.RectF; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.View; +import android.view.ViewConfiguration; +import android.widget.Scroller; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.R; +import org.telegram.messenger.Utilities; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.CubicBezierInterpolator; + +public class PhotoVideoSwitcherView extends View { + + private TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + private Paint selectorPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + private StaticLayout photoText, videoText; + private float photoTextLeft, photoTextWidth, photoTextHeight; + private float videoTextLeft, videoTextWidth, videoTextHeight; + private float scrollWidth; + + private VelocityTracker mVelocityTracker; + private int mTouchSlop; + private float mLastX; + private long mLastTouchTime; + private boolean mIsScrolling, mIsTouch; + private ValueAnimator animator; + + public PhotoVideoSwitcherView(Context context) { + super(context); + + selectorPaint.setColor(0x32ffffff); + + textPaint.setColor(0xffffffff); + textPaint.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + textPaint.setTextSize(AndroidUtilities.dpf2(14)); + textPaint.setShadowLayer(AndroidUtilities.dpf2(1), 0, AndroidUtilities.dpf2(0.4f), 0x33000000); + + CharSequence text = LocaleController.getString("StoryPhoto"); + if (text == null) { + text = "Photo"; + } + photoText = new StaticLayout(text, textPaint, AndroidUtilities.displaySize.x / 2, Layout.Alignment.ALIGN_NORMAL, 1, 0, false); + photoTextLeft = photoText.getLineCount() > 0 ? photoText.getLineLeft(0) : 0; + photoTextWidth = photoText.getLineCount() > 0 ? photoText.getLineWidth(0) : 0; + photoTextHeight = photoText.getHeight(); + + text = LocaleController.getString("StoryVideo"); + if (text == null) { + text = "Video"; + } + videoText = new StaticLayout(text, textPaint, AndroidUtilities.displaySize.x / 2, Layout.Alignment.ALIGN_NORMAL, 1, 0, false); + videoTextLeft = videoText.getLineCount() > 0 ? videoText.getLineLeft(0) : 0; + videoTextWidth = videoText.getLineCount() > 0 ? videoText.getLineWidth(0) : 0; + videoTextHeight = videoText.getHeight(); + + scrollWidth = dp(32) + photoTextWidth / 2 + videoTextWidth / 2; + + mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); + } + + private float mode; + + private Utilities.Callback onSwitchModeListener; + private Utilities.Callback onSwitchingModeListener; + public void setOnSwitchModeListener(Utilities.Callback onSwitchModeListener) { + this.onSwitchModeListener = onSwitchModeListener; + } + + public void setOnSwitchingModeListener(Utilities.Callback onSwitchingModeListener) { + this.onSwitchingModeListener = onSwitchingModeListener; + } + + public void switchMode(boolean modeIsVideo) { + if (animator != null) { + animator.cancel(); + } + animator = ValueAnimator.ofFloat(mode, modeIsVideo ? 1 : 0); + animator.addUpdateListener(anm -> { + mode = (float) anm.getAnimatedValue(); + if (onSwitchingModeListener != null) { + onSwitchingModeListener.run(Utilities.clamp(mode, 1, 0)); + } + invalidate(); + }); + animator.setDuration(320); + animator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + animator.start(); + } + + private RectF photoRect = new RectF(), videoRect = new RectF(), selectorRect = new RectF(); + + private float getScrollCx() { + return getWidth() / 2f + AndroidUtilities.lerp( + (dp(4 + 12) + photoTextWidth / 2), + -(dp(4 + 12) + videoTextWidth / 2), + mode + ); + } + + public void scrollX(float dx) { + if (!mIsScrolling && Math.abs(dx) > mTouchSlop) { + mIsScrolling = true; + modeAtTouchDown = mode; + } + if (mIsScrolling) { + float overscrollFactor = 0.2f; + if (mode <= 0 && dx < 0) { + dx *= overscrollFactor; + } else if (mode >= 1 && dx > 0) { + dx *= overscrollFactor; + } + mode += dx / scrollWidth / 2.5f; + mode = Utilities.clamp(mode, 1 + overscrollFactor, -overscrollFactor); + if (onSwitchingModeListener != null) { + onSwitchingModeListener.run(Utilities.clamp(mode, 1, 0)); + } + invalidate(); + } + } + + public boolean stopScroll(float velocityX) { + if (!mIsScrolling) { + scrolledEnough = false; + return false; + } + + mIsScrolling = false; + + final boolean targetMode; + if (Math.abs(velocityX) > 500) { + targetMode = velocityX < 0; + } else { + targetMode = mode > 0.5f; + } + + switchMode(targetMode); + if (onSwitchModeListener != null) { + onSwitchModeListener.run(targetMode); + } + scrolledEnough = false; + return true; + } + + @Override + public void draw(Canvas canvas) { + super.draw(canvas); + + float cy = getHeight() / 2f; + float x = getScrollCx(); + + final int oy = -dp(1); + final int h = dp(26); + photoRect.set(x - dp(4 + 12 + 12) - photoTextWidth, cy - h / 2f + oy, x - dp(4), cy + h / 2f + oy); + videoRect.set(x + dp(4), cy - h / 2f + oy, x + dp(4 + 12 + 12) + videoTextWidth, cy + h / 2f + oy); + AndroidUtilities.lerp(photoRect, videoRect, Utilities.clamp(mode, 1.025f, -.025f), selectorRect); + canvas.drawRoundRect(selectorRect, h / 2f, h / 2f, selectorPaint); + + canvas.save(); + canvas.translate(x - dp(4 + 12) - photoTextWidth - photoTextLeft, cy - photoTextHeight / 2f + oy); + photoText.draw(canvas); + canvas.restore(); + + canvas.save(); + canvas.translate(x + dp(4 + 12) - videoTextLeft, cy - videoTextHeight / 2f + oy); + videoText.draw(canvas); + canvas.restore(); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } + mVelocityTracker.addMovement(event); + + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + mIsTouch = true; + modeAtTouchDown = mode; + mLastTouchTime = System.currentTimeMillis(); + mLastX = event.getX(); + return true; + + case MotionEvent.ACTION_MOVE: + float x = event.getX(); + float deltaX = mLastX - x; + scrollX(deltaX); + mLastX = x; + break; + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + mIsTouch = false; + + float xVelocity = 0; + if (mVelocityTracker != null) { + mVelocityTracker.computeCurrentVelocity(1000); + xVelocity = mVelocityTracker.getXVelocity(); + } + + if (!stopScroll(xVelocity) && (System.currentTimeMillis() - mLastTouchTime) <= ViewConfiguration.getTapTimeout() && Math.abs(event.getX() - mLastX) < AndroidUtilities.dp(4)) { + final boolean targetMode = event.getX() > getScrollCx(); + switchMode(targetMode); + if (onSwitchModeListener != null) { + onSwitchModeListener.run(targetMode); + } + } + + mVelocityTracker.recycle(); + mVelocityTracker = null; + scrolledEnough = false; + break; + } + return super.onTouchEvent(event); + } + + public boolean isTouch() { + return mIsTouch; + } + + public boolean isScrolling() { + return mIsScrolling; + } + + private float modeAtTouchDown; + private boolean scrolledEnough; + + public boolean scrolledEnough() { + return scrolledEnough || (scrolledEnough = Math.abs(mode - modeAtTouchDown) > .1f); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/PreviewButtons.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/PreviewButtons.java new file mode 100644 index 0000000000..ad3722c780 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/PreviewButtons.java @@ -0,0 +1,348 @@ +package org.telegram.ui.Stories.recorder; + +import static org.telegram.messenger.AndroidUtilities.dp; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.PorterDuffXfermode; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; +import android.text.Layout; +import android.text.SpannableString; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.text.TextUtils; +import android.text.style.ImageSpan; +import android.view.Gravity; +import android.view.View; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.animation.LinearInterpolator; +import android.view.animation.OvershootInterpolator; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.TextView; + +import com.google.common.primitives.Chars; + +import org.checkerframework.checker.units.qual.A; +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.R; +import org.telegram.messenger.Utilities; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.LayoutHelper; + +import java.util.ArrayList; + +public class PreviewButtons extends FrameLayout { + + public static final int BUTTON_PAINT = 0; + public static final int BUTTON_TEXT = 1; + public static final int BUTTON_STICKER = 2; + public static final int BUTTON_ADJUST = 3; + public static final int BUTTON_SHARE = 4; + + private View shadowView; + + private ArrayList buttons = new ArrayList<>(); + public ShareButtonView shareButton; + + private String shareText; + + public PreviewButtons(Context context) { + super(context); + + shadowView = new View(context); + shadowView.setBackground(new GradientDrawable(GradientDrawable.Orientation.BOTTOM_TOP, new int[] { 0x66000000, 0x00000000 })); + addView(shadowView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); + + addButton(BUTTON_PAINT, R.drawable.msg_draw_pen, LocaleController.getString("AccDescrPaint", R.string.AccDescrPaint)); + addButton(BUTTON_STICKER, R.drawable.msg_photo_sticker, LocaleController.getString("AccDescrStickers", R.string.AccDescrStickers)); + addButton(BUTTON_TEXT, R.drawable.msg_photo_text2, LocaleController.getString("AccDescrPlaceText", R.string.AccDescrPlaceText)); + addButton(BUTTON_ADJUST, R.drawable.msg_photo_settings, LocaleController.getString("AccDescrPhotoAdjust", R.string.AccDescrPhotoAdjust)); + + shareButton = new ShareButtonView(context, shareText = LocaleController.getString("Send", R.string.Send)); + shareButton.setContentDescription(LocaleController.getString("Send", R.string.Send)); + addView(shareButton, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT)); + + updateAppearT(); + } + + public void setShareText(String text) { + if (TextUtils.equals(text, shareText)) { + return; + } + removeView(shareButton); + shareButton = new ShareButtonView(getContext(), text); + shareButton.setContentDescription(text); + addView(shareButton, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT)); + updateAppearT(); + } + + private void addButton(int id, int resId, CharSequence contentDescription) { + ButtonView btn = new ButtonView(getContext(), id, resId); + btn.setContentDescription(contentDescription); + buttons.add(btn); + addView(btn); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + final int w = right - left; + final int h = bottom - top; + + shadowView.layout(0, 0, w, h); + shareButton.layout(w - shareButton.getMeasuredWidth(), (h - shareButton.getMeasuredHeight()) / 2, w, (h + shareButton.getMeasuredHeight()) / 2); + + int W = w - dp(10 + 10 + 12.33f) - shareButton.getMeasuredWidth(); + int maxPossibleMargin = buttons.size() < 2 ? 0 : (W - buttons.size() * dp(40)) / (buttons.size() - 1); + int margin = Math.min(dp(20), maxPossibleMargin); + + int t = (h - dp(40)) / 2, b = (h + dp(40)) / 2; + for (int i = 0, x = dp(12.33f); i < buttons.size(); ++i) { + buttons.get(i).layout(x, t, x + dp(40), b); + x += dp(40) + margin; + } + } + + private Utilities.Callback onClickListener; + public void setOnClickListener(Utilities.Callback onClickListener) { + this.onClickListener = onClickListener; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure( + MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(dp(52), MeasureSpec.EXACTLY) + ); + } + + private boolean isShareEnabled = true; + public void setShareEnabled(boolean enabled) { + if (isShareEnabled != enabled) { + isShareEnabled = enabled; + shareButton.enabled = enabled; + shareButton.invalidate(); + } + } + public boolean isShareEnabled() { + return isShareEnabled; + } + + private float appearT; + private boolean appearing; + private ValueAnimator appearAnimator; + + public void appear(boolean appear, boolean animated) { + if (appearing == appear) { + return; + } + if (appearAnimator != null) { + appearAnimator.cancel(); + } + appearing = appear; + if (animated) { + appearAnimator = ValueAnimator.ofFloat(appearT, appear ? 1 : 0); + appearAnimator.addUpdateListener(anm -> { + appearT = (float) anm.getAnimatedValue(); + updateAppearT(); + }); + if (appearing) { + appearAnimator.setDuration(450); + appearAnimator.setInterpolator(new LinearInterpolator()); + } else { + appearAnimator.setDuration(350); + appearAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + } + appearAnimator.start(); + } else { + appearT = appear ? 1 : 0; + updateAppearT(); + } + } + + private void updateAppearT() { + shadowView.setAlpha(appearT); + shadowView.setTranslationY((1f - appearT) * dp(16)); + for (int i = 1; i < getChildCount(); ++i) { + View child = getChildAt(i); + float t = appearT; + if (appearing) { + t = AndroidUtilities.cascade(appearT, i - 1, getChildCount() - 1, 3); + t = CubicBezierInterpolator.EASE_OUT_QUINT.getInterpolation(t); + } + child.setAlpha(t); + child.setTranslationY((1f - t) * dp(24)); + } + } + + private class ShareButtonView extends View { + + private final TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + private final Paint buttonPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint darkenPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final StaticLayout staticLayout; + private float left, width; + + private final int w, h; + + private AnimatedFloat enabledT = new AnimatedFloat(this, 0, 220, CubicBezierInterpolator.EASE_OUT_QUINT); + public boolean enabled = true; + + public ShareButtonView(Context context, String text) { + super(context); + +// buttonPaint.setColor(0xffffffff); + buttonPaint.setColor(0xff199cff); + darkenPaint.setColor(0x60000000); + + textPaint.setTextSize(dp(13)); + textPaint.setColor(0xffffffff); + textPaint.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); +// textPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); + + SpannableString arrow = new SpannableString(">"); + Drawable arrowDrawable = getResources().getDrawable(R.drawable.attach_arrow_right).mutate(); + arrowDrawable.setColorFilter(new PorterDuffColorFilter(0xffffffff, PorterDuff.Mode.SRC_IN)); + arrowDrawable.setBounds(0, 0, dp(12), dp(12)); + arrow.setSpan(new ImageSpan(arrowDrawable, ImageSpan.ALIGN_CENTER), 0, arrow.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + + CharSequence text2; + if (LocaleController.isRTL) { + text2 = new SpannableStringBuilder(arrow).append(" ").append(text.toUpperCase()); + } else { + text2 = new SpannableStringBuilder(text.toUpperCase()).append(" ").append(arrow); + } + + staticLayout = new StaticLayout(text2, textPaint, AndroidUtilities.dp(180), Layout.Alignment.ALIGN_NORMAL, 1, 0, false); + left = staticLayout.getLineCount() > 0 ? staticLayout.getLineLeft(0) : 0; + width = staticLayout.getLineCount() > 0 ? staticLayout.getLineWidth(0) : 0; + + w = (int) width + AndroidUtilities.dp(16 + 16 + 16); + h = AndroidUtilities.dp(32 + 8); + + setOnClickListener(e -> { + if (appearing && onClickListener != null) { + onClickListener.run(BUTTON_SHARE); + } + }); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY)); + } + + @Override + protected void onDraw(Canvas canvas) { + if (isPressed() && pressedProgress != 1f) { + pressedProgress += (float) Math.min(40, 1000f / AndroidUtilities.screenRefreshRate) / 80f; + pressedProgress = Utilities.clamp(pressedProgress, 1f, 0); + invalidate(); + } + + float alpha = enabledT.set(enabled ? 1 : .5f); + int restore = canvas.getSaveCount(); + if (alpha < 1) { + canvas.saveLayerAlpha(0, 0, getWidth(), getHeight(), (int) (0xFF * alpha), Canvas.ALL_SAVE_FLAG); + } + + final float scale = 0.9f + 0.1f * (1f - pressedProgress); + canvas.save(); + canvas.scale(scale, scale, getWidth() / 2f, getHeight() / 2f); + + canvas.drawRect(dp(25), dp(4), getWidth() - dp(25), getHeight() - dp(4), darkenPaint); + + canvas.saveLayerAlpha(0, 0, getWidth(), getHeight(), 0xFF, Canvas.ALL_SAVE_FLAG); + AndroidUtilities.rectTmp.set(dp(10), dp(4), getWidth() - dp(10), getHeight() - dp(4)); + canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(20), dp(20), buttonPaint); + + canvas.save(); + canvas.translate(dp(10 + 16) - left, (getHeight() - staticLayout.getHeight()) / 2f); + staticLayout.draw(canvas); + canvas.restore(); + + canvas.restoreToCount(restore); + } + + float pressedProgress; + ValueAnimator backAnimator; + + @Override + public void setPressed(boolean pressed) { + if (isPressed() != pressed) { + super.setPressed(pressed); + invalidate(); + if (pressed) { + if (backAnimator != null) { + backAnimator.removeAllListeners(); + backAnimator.cancel(); + } + } + if (!pressed && pressedProgress != 0) { + backAnimator = ValueAnimator.ofFloat(pressedProgress, 0); + backAnimator.addUpdateListener(animation -> { + pressedProgress = (float) animation.getAnimatedValue(); + invalidate(); + }); + backAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + backAnimator = null; + } + }); + backAnimator.setInterpolator(new OvershootInterpolator(1.5f)); + backAnimator.setDuration(350); + backAnimator.start(); + } + } + } + + @Override + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(info); + info.setClassName("android.widget.Button"); + } + } + + private class ButtonView extends ImageView { + public ButtonView(Context context, int id, int resId) { + super(context); + + setBackground(Theme.createSelectorDrawable(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR)); + setScaleType(ScaleType.CENTER); + setImageResource(resId); + setColorFilter(new PorterDuffColorFilter(0xffffffff, PorterDuff.Mode.MULTIPLY)); + + setOnClickListener(e -> { + if (appearing && onClickListener != null) { + onClickListener.run(id); + } + }); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setMeasuredDimension(dp(40), dp(40)); + } + + @Override + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(info); + info.setClassName("android.widget.Button"); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/PreviewHighlightView.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/PreviewHighlightView.java new file mode 100644 index 0000000000..4b9b18d1ba --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/PreviewHighlightView.java @@ -0,0 +1,179 @@ +package org.telegram.ui.Stories.recorder; + +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.AndroidUtilities.dpf2; + +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.RectF; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.core.graphics.ColorUtils; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.R; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.UserObject; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AnimatedEmojiSpan; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Stories.PeerStoriesView; +import org.telegram.ui.Stories.StoryCaptionView; + +public class PreviewHighlightView extends FrameLayout { + + private int currentAccount; + private int storiesCount = 1; + + private final FrameLayout top; + private final FrameLayout bottom; + + private final StoryCaptionView storyCaptionView; + + public PreviewHighlightView(Context context, int currentAccount, Theme.ResourcesProvider resourcesProvider) { + super(context); + + this.currentAccount = currentAccount; + + TLRPC.User me = UserConfig.getInstance(currentAccount).getCurrentUser(); + + top = new FrameLayout(getContext()) { + private RectF rectF = new RectF(); + private Paint barPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + + barPaint.setColor(0xffffffff); + float w = (getWidth() - dpf2(5) * 2 - dpf2(2 * (storiesCount - 1))) / storiesCount; + float x = dpf2(5); + for (int i = 0; i < storiesCount; ++i) { + rectF.set(x, dpf2(8), x + w, dpf2(8 + 2)); + barPaint.setAlpha(i < storiesCount - 1 ? 0xff : 0x55 + 0x30); + canvas.drawRoundRect(rectF, dpf2(1), dpf2(1), barPaint); + x += w + dpf2(2); + } + } + }; + PeerStoriesView.PeerHeaderView headerView = new PeerStoriesView.PeerHeaderView(getContext(), null); + headerView.backupImageView.getAvatarDrawable().setInfo(me); + headerView.backupImageView.setForUserOrChat(me, headerView.backupImageView.getAvatarDrawable()); + headerView.titleView.setText(UserObject.getUserName(me)); + headerView.setSubtitle(LocaleController.getString("RightNow", R.string.RightNow), false); + top.addView(headerView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 17, 0, 0)); + + ImageView closeIconView = new ImageView(context); + closeIconView.setImageDrawable(getContext().getResources().getDrawable(R.drawable.ic_close_white).mutate()); + closeIconView.setPadding(AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8)); + top.addView(closeIconView, LayoutHelper.createFrame(40, 40, Gravity.RIGHT | Gravity.TOP, 12, 15, 12, 0)); + addView(top, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + + bottom = new FrameLayout(getContext()); + storyCaptionView = new StoryCaptionView(getContext(), resourcesProvider); + storyCaptionView.disableTouches = true; + storyCaptionView.setTranslationY(dp(8)); + bottom.addView(storyCaptionView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.BOTTOM | Gravity.FILL_HORIZONTAL, 0, 0, 0, 64)); + +// ImageView shareButton = new ImageView(context); +// shareButton.setImageDrawable(getContext().getResources().getDrawable(R.drawable.msg_share_filled)); +// int padding = AndroidUtilities.dp(8); +// shareButton.setPadding(padding, padding, padding, padding); +// shareButton.setBackground(Theme.createCircleDrawable(AndroidUtilities.dp(20), ColorUtils.setAlphaComponent(Color.BLACK, 76))); +// bottom.addView(shareButton, LayoutHelper.createFrame(40, 40, Gravity.BOTTOM | Gravity.RIGHT, 10, 10, 10, 64)); + + ImageView shareButton = new ImageView(context); + shareButton.setImageResource(R.drawable.msg_share); + shareButton.setColorFilter(new PorterDuffColorFilter(0xffffffff, PorterDuff.Mode.MULTIPLY)); + bottom.addView(shareButton, LayoutHelper.createFrame(28, 28, Gravity.BOTTOM | Gravity.RIGHT, 0, 0, 12, 16)); + + FrameLayout editLayout = new FrameLayout(context); + editLayout.setBackground(Theme.createRoundRectDrawable(AndroidUtilities.dp(22), ColorUtils.setAlphaComponent(Color.BLACK, 122))); + + TextView editText = new TextView(context); + editText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); + editText.setTextColor(1694498815); + editText.setText(LocaleController.getString("ReplyPrivately", R.string.ReplyPrivately)); + editLayout.addView(editText, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.CENTER_VERTICAL, 24, 0, 24, 0)); + +// ImageView likeButton = new ImageView(context); +// likeButton.setImageResource(R.drawable.msg_input_like); +// likeButton.setColorFilter(new PorterDuffColorFilter(0xffffffff, PorterDuff.Mode.MULTIPLY)); +// editLayout.addView(likeButton, LayoutHelper.createFrame(24, 24, Gravity.CENTER_VERTICAL | Gravity.RIGHT, 0, 0, 103, 0)); + + ImageView attachButton = new ImageView(context); + attachButton.setImageResource(R.drawable.input_attach); + attachButton.setColorFilter(new PorterDuffColorFilter(0xffffffff, PorterDuff.Mode.MULTIPLY)); + editLayout.addView(attachButton, LayoutHelper.createFrame(28, 28, Gravity.CENTER_VERTICAL | Gravity.RIGHT, 0, 0, 9, 0)); + +// ImageView micButton = new ImageView(context); +// micButton.setImageResource(R.drawable.input_mic); +// micButton.setColorFilter(new PorterDuffColorFilter(0xffffffff, PorterDuff.Mode.MULTIPLY)); +// editLayout.addView(micButton, LayoutHelper.createFrame(28, 28, Gravity.CENTER_VERTICAL | Gravity.RIGHT, 0, 0, 9, 0)); + + bottom.addView(editLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 44, Gravity.BOTTOM | Gravity.FILL_HORIZONTAL, 9, 8, 55, 8)); + + addView(bottom, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + + top.setAlpha(0f); + bottom.setAlpha(0f); + } + + public void updateCount() { + storiesCount = MessagesController.getInstance(currentAccount).getStoriesController().getSelfStoriesCount() + 1; + top.invalidate(); + } + + public void updateCaption(CharSequence caption) { + caption = AnimatedEmojiSpan.cloneSpans(caption); + storyCaptionView.captionTextview.setText(caption); + } + + private boolean shownTop = false, shownBottom = false; + + public void show(boolean topOrBottom, boolean show, View fadeView) { + if (topOrBottom) { + if (shownTop == show) + return; + shownTop = show; + } else { + if (shownBottom == show) + return; + shownBottom = show; + } + + View view = topOrBottom ? top : bottom; + + view.clearAnimation(); + view.animate().alpha(show ? (topOrBottom ? .5f : .2f) : 0f).start(); + + if (fadeView != null) { + fadeView.clearAnimation(); + fadeView.animate().alpha(show ? 0f : 1f).start(); + } + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + return false; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + return false; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/PreviewView.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/PreviewView.java new file mode 100644 index 0000000000..7f9f08ff92 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/PreviewView.java @@ -0,0 +1,1084 @@ +package org.telegram.ui.Stories.recorder; + +import android.content.ContentUris; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.LinearGradient; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.PointF; +import android.graphics.Shader; +import android.graphics.SurfaceTexture; +import android.net.Uri; +import android.os.Build; +import android.provider.MediaStore; +import android.util.Log; +import android.util.Pair; +import android.util.Size; +import android.view.Gravity; +import android.view.HapticFeedbackConstants; +import android.view.MotionEvent; +import android.view.TextureView; +import android.view.View; +import android.view.ViewConfiguration; +import android.widget.FrameLayout; + +import com.google.android.exoplayer2.C; +import com.google.zxing.common.detector.MathUtils; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.FileLoader; +import org.telegram.messenger.SharedConfig; +import org.telegram.messenger.Utilities; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.ButtonBounce; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.VideoEditTextureView; +import org.telegram.ui.Components.VideoPlayer; +import org.telegram.ui.Components.VideoTimelinePlayView; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; + +public class PreviewView extends FrameLayout { + + private Bitmap bitmap; + private Bitmap thumbBitmap; + + private StoryEntry entry; + private VideoPlayer videoPlayer; + private int videoWidth, videoHeight; + private VideoEditTextureView textureView; + public TextureView filterTextureView; + + private VideoTimelinePlayView videoTimelineView; + + private final Paint snapPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + public static final long MAX_DURATION = 59_500L; + public static final long MIN_DURATION = 1_000L; + + private final HashMap partsBitmap = new HashMap<>(); + private final HashMap partsBounce = new HashMap<>(); + + public PreviewView(Context context) { + super(context); + + videoTimelineView = new VideoTimelinePlayView(context); + videoTimelineView.setMode(VideoTimelinePlayView.MODE_VIDEO); + videoTimelineView.setDelegate(new VideoTimelinePlayView.VideoTimelineViewDelegate() { + + private float durationOf(long ms) { + return (float) ms / (getDuration() == C.TIME_UNSET ? entry.duration : getDuration()); + } + + private long duration() { + return getDuration() == C.TIME_UNSET ? entry.duration : getDuration(); + } + + @Override + public void onLeftProgressChanged(float progress) { + if (videoPlayer != null) { + if (videoPlayer.isPlaying()) { + videoPlayer.pause(); + } + entry.left = progress; + entry.right = Utilities.clamp(Math.min(entry.right, entry.left + durationOf(MAX_DURATION)), 1, 0); + entry.left = Utilities.clamp(Math.min(entry.left, entry.right - durationOf(MIN_DURATION)), 1, 0); + videoTimelineView.setLeftRightProgress(entry.left, entry.right); + seekTo(entry.left); + videoTimelineView.setProgress(entry.left); + drag((long) (entry.left * duration())); + } + } + + @Override + public void onRightProgressChanged(float progress) { + if (videoPlayer != null) { + if (videoPlayer.isPlaying()) { + videoPlayer.pause(); + } + entry.right = progress; + entry.left = Utilities.clamp(Math.max(entry.left, entry.right - durationOf(MAX_DURATION)), 1, 0); + entry.right = Utilities.clamp(Math.max(entry.right, entry.left + durationOf(MIN_DURATION)), 1, 0); + videoTimelineView.setLeftRightProgress(entry.left, entry.right); + seekTo(entry.right); + videoTimelineView.setProgress(entry.right); + drag((long) (entry.right * duration())); + } + } + + @Override + public void onPlayProgressChanged(float progress) { + if (videoPlayer != null) { + seekTo(progress); + } + drag((long) (progress * duration())); + } + + private Runnable dragStart; + private boolean dragging; + private long lastDragTime; + + private void drag(long t) { + lastDragTime = t; + if (dragging) { + onTimeDrag(false, lastDragTime, false); + } else if (dragStart == null) { + AndroidUtilities.runOnUIThread(dragStart = () -> { + dragging = true; + dragStart = null; + onTimeDrag(true, lastDragTime, false); + }, 150); + } + } + + @Override + public void didStartDragging(int type) { + if (videoPlayer == null) { + return; + } + updatePauseReason(-1, true); + drag((long) (videoTimelineView.getProgressOf(type) * duration())); + } + + @Override + public void didStopDragging(int type) { + if (seekToRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(seekToRunnable); + seekToRunnable.run(); + } + if (videoPlayer != null && !videoPlayer.isPlaying()) { + float currentProgress = videoPlayer.getCurrentPosition() / (float) getDuration(); + if (currentProgress < entry.left || currentProgress > entry.right) { + seekTo(entry.left * getDuration()); + } + updatePauseReason(-1, false); + } + dragging = false; + if (dragStart != null) { + AndroidUtilities.cancelRunOnUIThread(dragStart); + dragStart = null; + } + onTimeDrag(false, lastDragTime, true); + } + + private Runnable seekToRunnable; + private int seekTo; + private boolean wasPlaying; + + private void seekTo(float progress) { + if (videoPlayer == null) { + return; + } + seekTo = (int) (getDuration() * progress); + if (SharedConfig.getDevicePerformanceClass() == SharedConfig.PERFORMANCE_CLASS_HIGH) { + if (videoPlayer != null) { + videoPlayer.seekTo(seekTo); + } + applyMatrix(); + seekToRunnable = null; + } else if (seekToRunnable == null) { + AndroidUtilities.runOnUIThread(seekToRunnable = () -> { + if (videoPlayer != null) { + videoPlayer.seekTo(seekTo); + } + applyMatrix(); + seekToRunnable = null; + }, 100); + } + } + }); + + snapPaint.setStrokeWidth(AndroidUtilities.dp(1)); + snapPaint.setStyle(Paint.Style.STROKE); + snapPaint.setColor(0xffffffff); + snapPaint.setShadowLayer(AndroidUtilities.dp(3), 0, AndroidUtilities.dp(1), 0x40000000); + } + + protected void onTimeDrag(boolean dragStart, long time, boolean dragEnd) {} + + public long getDuration() { + if (entry != null && entry.fileDuration >= 0) { + return (long) (1000 * entry.fileDuration); + } + if (videoPlayer != null) { + return videoPlayer.getDuration(); + } + return 0; + } + + public VideoTimelinePlayView getTimelineView() { + return videoTimelineView; + } + + public void set(StoryEntry entry) { + set(entry, null, 0); + } + + public void set(StoryEntry entry, Runnable whenReady, long seekTo) { + this.entry = entry; + if (entry == null) { + setupVideoPlayer(null, whenReady, seekTo); + setupImage(null); + setupParts(null); + gradientPaint.setShader(null); + return; + } + if (entry.isVideo) { + setupImage(entry); + setupVideoPlayer(entry, whenReady, seekTo); + if (entry.gradientTopColor != 0 || entry.gradientBottomColor != 0) { + setupGradient(); + } else { + entry.setupGradient(this::setupGradient); + } + } else { + setupVideoPlayer(null, whenReady, 0); + setupImage(entry); + setupGradient(); + } + setupParts(entry); + applyMatrix(); + } + + private void setupImage(StoryEntry entry) { + if (bitmap != null) { + bitmap.recycle(); + bitmap = null; + } + if (thumbBitmap != null) { + thumbBitmap.recycle(); + thumbBitmap = null; + } + if (entry != null) { + final int rw = getMeasuredWidth() <= 0 ? AndroidUtilities.displaySize.x : getMeasuredWidth(); + final int rh = (int) (rw * 16 / 9f); + long imageId = -1L; + if (entry.isVideo) { + if (entry.blurredVideoThumb != null) { + bitmap = entry.blurredVideoThumb; + } + if (bitmap == null && entry.thumbPath != null && entry.thumbPath.startsWith("vthumb://")) { + imageId = Long.parseLong(entry.thumbPath.substring(9)); + + if (bitmap == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + try { + Uri uri; + if (entry.isVideo) { + uri = ContentUris.withAppendedId(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, imageId); + } else { + uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, imageId); + } + bitmap = getContext().getContentResolver().loadThumbnail(uri, new Size(rw, rh), null); + } catch (Exception ignore) {} + } + } + } + if (imageId < 0 && entry.isVideo && entry.thumbPath == null) { + invalidate(); + return; + } + if (bitmap == null) { + String path = entry.getOriginalFile().getPath(); + + final long imageIdFinal = imageId; + bitmap = StoryEntry.getScaledBitmap(opts -> { + if (entry.isVideo) { + if (entry.thumbPath != null) { + return BitmapFactory.decodeFile(entry.thumbPath, opts); + } else { + try { + return MediaStore.Video.Thumbnails.getThumbnail(getContext().getContentResolver(), imageIdFinal, MediaStore.Video.Thumbnails.MINI_KIND, opts); + } catch (Throwable e) { + invalidate(); + return null; + } + } + } else { + return BitmapFactory.decodeFile(path, opts); + } + }, rw, rh, false); + +// this.thumbAlpha.set(0, true); +// thumbBitmap = StoryEntry.getScaledBitmap(opts -> { +// if (entry.isVideo) { +// if (entry.thumbPath != null) { +// return BitmapFactory.decodeFile(entry.thumbPath, opts); +// } else { +// try { +// return MediaStore.Video.Thumbnails.getThumbnail(getContext().getContentResolver(), imageIdFinal, MediaStore.Video.Thumbnails.MINI_KIND, opts); +// } catch (Throwable e) { +// invalidate(); +// return null; +// } +// } +// } else { +// return BitmapFactory.decodeFile(path, opts); +// } +// }, rw / 4, rh / 4, false); +// +// Utilities.themeQueue.postRunnable(() -> { +// final Bitmap bitmapFinal = StoryEntry.getScaledBitmap(opts -> { +// if (entry.isVideo) { +// if (entry.thumbPath != null) { +// return BitmapFactory.decodeFile(entry.thumbPath, opts); +// } else { +// try { +// return MediaStore.Video.Thumbnails.getThumbnail(getContext().getContentResolver(), imageIdFinal, MediaStore.Video.Thumbnails.MINI_KIND, opts); +// } catch (Throwable e) { +// invalidate(); +// return null; +// } +// } +// } else { +// return BitmapFactory.decodeFile(path, opts); +// } +// }, rw, rh, true); +// AndroidUtilities.runOnUIThread(() -> { +// if (PreviewView.this.entry != entry) { +// if (bitmapFinal != null) { +// bitmapFinal.recycle(); +// } +// return; +// } +// bitmap = bitmapFinal; +// if (!entry.isDraft && entry.isVideo && bitmap != null && entry.width <= 0) { +// entry.width = bitmap.getWidth(); +// entry.height = bitmap.getHeight(); +// entry.setupMatrix(); +// } +// invalidate(); +// }); +// }); + return; + } + if (!entry.isDraft && entry.isVideo && bitmap != null) { + entry.width = bitmap.getWidth(); + entry.height = bitmap.getHeight(); + entry.setupMatrix(); + } + } + invalidate(); + } + + private void setupGradient() { + final int height = getMeasuredHeight() > 0 ? getMeasuredHeight() : AndroidUtilities.displaySize.y; + if (entry.gradientTopColor == 0 || entry.gradientBottomColor == 0) { + if (bitmap != null) { + DominantColors.getColors(true, bitmap, true, colors -> { + entry.gradientTopColor = colors[0]; + entry.gradientBottomColor = colors[1]; + gradientPaint.setShader(new LinearGradient(0, 0, 0, height, colors, new float[]{0, 1}, Shader.TileMode.CLAMP)); + invalidate(); + }); + } else if (thumbBitmap != null) { + DominantColors.getColors(true, thumbBitmap, true, colors -> { + entry.gradientTopColor = colors[0]; + entry.gradientBottomColor = colors[1]; + gradientPaint.setShader(new LinearGradient(0, 0, 0, height, colors, new float[]{0, 1}, Shader.TileMode.CLAMP)); + invalidate(); + }); + } else { + gradientPaint.setShader(null); + } + } else { + gradientPaint.setShader(new LinearGradient(0, 0, 0, height, new int[] { entry.gradientTopColor, entry.gradientBottomColor }, new float[]{0, 1}, Shader.TileMode.CLAMP)); + } + + invalidate(); + } + + private void setupVideoPlayer(StoryEntry entry, Runnable whenReady, long seekTo) { + if (entry == null) { + if (videoPlayer != null) { + videoPlayer.pause(); + videoPlayer.releasePlayer(true); + videoPlayer = null; + } + if (textureView != null) { + textureView.clearAnimation(); + textureView.animate().alpha(0).withEndAction(() -> { + if (textureView != null) { + textureView.release(); + removeView(textureView); + textureView = null; + } + }).start(); + } + AndroidUtilities.cancelRunOnUIThread(updateProgressRunnable); + if (whenReady != null) { + AndroidUtilities.runOnUIThread(whenReady); + } + } else { + if (videoPlayer != null) { + videoPlayer.releasePlayer(true); + videoPlayer = null; + } + + final Runnable[] whenReadyFinal = new Runnable[] { whenReady }; + + videoPlayer = new VideoPlayer(); + videoPlayer.setDelegate(new VideoPlayer.VideoPlayerDelegate() { + @Override + public void onStateChanged(boolean playWhenReady, int playbackState) { + if (videoPlayer == null) { + return; + } + if (videoPlayer != null && videoPlayer.isPlaying()) { + AndroidUtilities.runOnUIThread(updateProgressRunnable); + } else { + AndroidUtilities.cancelRunOnUIThread(updateProgressRunnable); + } + } + + @Override + public void onError(VideoPlayer player, Exception e) { + if (onErrorListener != null) { + onErrorListener.run(); + } + } + + @Override + public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) { + if (entry != null) { + entry.hdrInfo = videoPlayer.getHDRStaticInfo(entry.hdrInfo); + if (textureView != null) { + textureView.setHDRInfo(entry.hdrInfo); + } + } + + videoWidth = (int) (width * pixelWidthHeightRatio); + videoHeight = (int) (height * pixelWidthHeightRatio); + if (entry != null && (entry.width != videoWidth || entry.height != videoHeight)) { + entry.width = videoWidth; + entry.height = videoHeight; + entry.setupMatrix(); + } + applyMatrix(); + if (textureView != null) { + textureView.setVideoSize(videoWidth, videoHeight); + } +// if (whenReadyFinal[0] != null && entry != null && entry.width > 0 && entry.height > 0) { +// post(whenReadyFinal[0]); +// whenReadyFinal[0] = null; +// } + } + + @Override + public void onRenderedFirstFrame() { + if (whenReadyFinal[0] != null) { + post(whenReadyFinal[0]); + whenReadyFinal[0] = null; + if (bitmap != null) { + bitmap.recycle(); + if (entry.blurredVideoThumb == bitmap) { + entry.blurredVideoThumb = null; + } + bitmap = null; + invalidate(); + } + } else if (textureView != null) { + textureView.animate().alpha(1f).setDuration(180).withEndAction(() -> { + if (bitmap != null) { + bitmap.recycle(); + if (entry.blurredVideoThumb == bitmap) { + entry.blurredVideoThumb = null; + } + bitmap = null; + invalidate(); + } + }).start(); + } + } + + @Override + public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {} + + @Override + public boolean onSurfaceDestroyed(SurfaceTexture surfaceTexture) { + return false; + } + }); + + if (textureView != null) { + textureView.clearAnimation(); + textureView.release(); + removeView(textureView); + textureView = null; + } + + textureView = new VideoEditTextureView(getContext(), videoPlayer); + textureView.setAlpha(whenReady == null ? 0f : 1f); + textureView.setOpaque(false); + applyMatrix(); + addView(textureView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.LEFT)); + + entry.detectHDR((hdrInfo) -> { + if (textureView != null) { + textureView.setHDRInfo(hdrInfo); + } + }); + + Uri uri = Uri.fromFile(entry.getOriginalFile()); + videoPlayer.preparePlayer(uri, "other"); + videoPlayer.setPlayWhenReady(pauseLinks.isEmpty()); + videoPlayer.setLooping(true); + if (seekTo > 0) { + videoPlayer.seekTo(seekTo); + } + + videoTimelineView.setVideoPath(uri.toString(), entry.left, entry.right); + } + } + + public long release() { + if (videoPlayer != null) { + long t = videoPlayer.getCurrentPosition(); + videoPlayer.pause(); + videoPlayer.releasePlayer(true); + videoPlayer = null; + return t; + } + return 0; + } + + public void setupParts(StoryEntry entry) { + if (entry == null) { + for (Bitmap bitmap : partsBitmap.values()) { + if (bitmap != null) { + bitmap.recycle(); + } + } + partsBitmap.clear(); + partsBounce.clear(); + return; + } + final int rw = getMeasuredWidth() <= 0 ? AndroidUtilities.displaySize.x : getMeasuredWidth(); + final int rh = (int) (rw * 16 / 9f); + for (int i = 0; i < entry.parts.size(); ++i) { + StoryEntry.Part part = entry.parts.get(i); + if (part == null) { + continue; + } + Bitmap bitmap = partsBitmap.get(part.id); + if (bitmap != null) { + continue; + } + String path = part.file.getPath(); + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeFile(path, options); + options.inJustDecodeBounds = false; + options.inSampleSize = StoryEntry.calculateInSampleSize(options, rw, rh); + bitmap = BitmapFactory.decodeFile(path, options); + partsBitmap.put(part.id, bitmap); + } + for (Iterator> i = partsBitmap.entrySet().iterator(); i.hasNext(); ) { + Map.Entry e = i.next(); + boolean found = false; + for (int j = 0; j < entry.parts.size(); ++j) { + if (entry.parts.get(j).id == e.getKey()) { + found = true; + break; + } + } + if (!found) { + i.remove(); + partsBounce.remove(e.getKey()); + } + } + } + + public void setFilterTextureView(TextureView view) { + if (filterTextureView != null) { + removeView(filterTextureView); + filterTextureView = null; + } + filterTextureView = view; + if (filterTextureView != null) { + addView(filterTextureView); + } + } + + private long seekedLastTime; + private final Runnable updateProgressRunnable = () -> { + if (videoPlayer == null) { + return; + } + float progress = videoPlayer.getCurrentPosition() / (float) getDuration(); + if (!videoTimelineView.isDragging() && (progress < entry.left || progress > entry.right) && System.currentTimeMillis() - seekedLastTime > MIN_DURATION / 2) { + seekedLastTime = System.currentTimeMillis(); + videoPlayer.seekTo((long) (entry.left * getDuration())); + } + progress = Utilities.clamp(progress, videoTimelineView.getRightProgress(), videoTimelineView.getLeftProgress()); + videoTimelineView.setProgress(progress); + if (videoPlayer.isPlaying()) { + AndroidUtilities.cancelRunOnUIThread(this.updateProgressRunnable); + AndroidUtilities.runOnUIThread(this.updateProgressRunnable, (long) (1000L / AndroidUtilities.screenRefreshRate)); + } + }; + + private Runnable onErrorListener; + public void whenError(Runnable listener) { + onErrorListener = listener; + } + + public void mute(boolean value) { + if (videoPlayer == null) { + return; + } + videoPlayer.setMute(value); + } + + private final Paint bitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG); + private final Paint gradientPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + private final Matrix matrix = new Matrix(); + + private final float[] vertices = new float[2]; + private float cx, cy, angle, w, h; + + private void extractPointsData(Matrix matrix) { + if (entry == null) { + return; + } + + vertices[0] = entry.width / 2f; + vertices[1] = entry.height / 2f; + matrix.mapPoints(vertices); + cx = vertices[0]; + cy = vertices[1]; + + vertices[0] = entry.width; + vertices[1] = entry.height / 2f; + matrix.mapPoints(vertices); + angle = (float) Math.toDegrees(Math.atan2(vertices[1] - cy, vertices[0] - cx)); + w = 2 * MathUtils.distance(cx, cy, vertices[0], vertices[1]); + + vertices[0] = entry.width / 2f; + vertices[1] = entry.height; + matrix.mapPoints(vertices); + h = 2 * MathUtils.distance(cx, cy, vertices[0], vertices[1]); + } + + private boolean draw = true; + public void setDraw(boolean draw) { + this.draw = draw; + invalidate(); + } + + private final AnimatedFloat thumbAlpha = new AnimatedFloat(this, 0, 320, CubicBezierInterpolator.EASE_OUT); + + @Override + protected void dispatchDraw(Canvas canvas) { + canvas.drawRect(0, 0, getWidth(), getHeight(), gradientPaint); + if (draw && entry != null) { + float alpha = this.thumbAlpha.set(bitmap != null); + if (thumbBitmap != null && (1f - alpha) > 0) { + matrix.set(entry.matrix); + matrix.preScale((float) entry.width / thumbBitmap.getWidth(), (float) entry.height / thumbBitmap.getHeight()); + matrix.postScale((float) getWidth() / entry.resultWidth, (float) getHeight() / entry.resultHeight); + bitmapPaint.setAlpha(0xFF); + canvas.drawBitmap(thumbBitmap, matrix, bitmapPaint); + } + if (bitmap != null) { + matrix.set(entry.matrix); + matrix.preScale((float) entry.width / bitmap.getWidth(), (float) entry.height / bitmap.getHeight()); + matrix.postScale((float) getWidth() / entry.resultWidth, (float) getHeight() / entry.resultHeight); + bitmapPaint.setAlpha((int) (0xFF * alpha)); + canvas.drawBitmap(bitmap, matrix, bitmapPaint); + } + } + super.dispatchDraw(canvas); + if (draw && entry != null) { + float trash = trashT.set(!inTrash); + for (int i = 0; i < entry.parts.size(); ++i) { + StoryEntry.Part part = entry.parts.get(i); + if (part == null) { + continue; + } + Bitmap bitmap = partsBitmap.get(part.id); + if (bitmap == null) { + continue; + } + float scale = 1f; + ButtonBounce bounce = partsBounce.get(part.id); + if (bounce != null) { + scale = bounce.getScale(.05f); + } + matrix.set(part.matrix); + canvas.save(); + if (scale != 1) { + tempVertices[0] = part.width / 2f; + tempVertices[1] = part.height / 2f; + matrix.mapPoints(tempVertices); + canvas.scale(scale, scale, tempVertices[0] / entry.resultWidth * getWidth(), tempVertices[1] / entry.resultHeight * getHeight()); + } + if (trashPartIndex == part.id) { + float trashScale = AndroidUtilities.lerp(.2f, 1f, trash); + canvas.scale(trashScale, trashScale, trashCx, trashCy); + } + matrix.preScale((float) part.width / bitmap.getWidth(), (float) part.height / bitmap.getHeight()); + matrix.postScale((float) getWidth() / entry.resultWidth, (float) getHeight() / entry.resultHeight); + canvas.drawBitmap(bitmap, matrix, bitmapPaint); + canvas.restore(); + } + } + } + + public VideoEditTextureView getTextureView() { + return textureView; + } + + public Pair getPaintSize() { + if (entry == null) { + return new Pair<>(1080, 1920); + } + return new Pair<>(entry.resultWidth, entry.resultHeight); + } + + public Bitmap getPhotoBitmap() { + return bitmap; + } + + public int getOrientation() { + return entry == null ? 0 : entry.orientation; + } + + @Override + public void setVisibility(int visibility) { + super.setVisibility(visibility); + if (visibility == View.GONE) { + set(null); + } + } + + public void applyMatrix() { + if (entry == null) { + return; + } + if (textureView != null) { + matrix.set(entry.matrix); + matrix.preScale( + 1f / getWidth() * (entry.width < 0 ? videoWidth : entry.width), + 1f / getHeight() * (entry.height < 0 ? videoHeight : entry.height) + ); + matrix.postScale( + (float) getWidth() / entry.resultWidth, + (float) getHeight() / entry.resultHeight + ); + textureView.setTransform(matrix); + textureView.invalidate(); + } + invalidate(); + } + + private boolean allowCropping = true; + public void setAllowCropping(boolean allow) { + this.allowCropping = allow; + } + + private final PointF lastTouch = new PointF(); + private final PointF touch = new PointF(); + private float lastTouchDistance; + private double lastTouchRotation; + private boolean multitouch; + private boolean allowWithSingleTouch, allowRotation; + private Matrix touchMatrix = new Matrix(), finalMatrix = new Matrix(); + private float rotationDiff; + private boolean snappedToCenterAndScaled, snappedRotation; + private boolean doNotSpanRotation; + private float[] tempPoint = new float[4]; + private IStoryPart activePart; + private boolean isPart; + private float Tx, Ty; + private boolean activePartPressed; + + private int trashPartIndex; + private boolean inTrash; + private AnimatedFloat trashT = new AnimatedFloat(this, 0, 280, CubicBezierInterpolator.EASE_OUT_QUINT); + private float trashCx, trashCy; + + public boolean additionalTouchEvent(MotionEvent ev) { + return false; + } + + private boolean touchEvent(MotionEvent ev) { + if (!allowCropping) { + return false; + } + final boolean currentMultitouch = ev.getPointerCount() > 1; + float distance = 0; + double rotation = 0; + if (currentMultitouch) { + touch.x = (ev.getX(0) + ev.getX(1)) / 2f; + touch.y = (ev.getY(0) + ev.getY(1)) / 2f; + distance = MathUtils.distance(ev.getX(0), ev.getY(0), ev.getX(1), ev.getY(1)); + rotation = Math.atan2(ev.getY(1) - ev.getY(0), ev.getX(1) - ev.getX(0)); + } else { + touch.x = ev.getX(0); + touch.y = ev.getY(0); + } + if (multitouch != currentMultitouch) { + lastTouch.x = touch.x; + lastTouch.y = touch.y; + lastTouchDistance = distance; + lastTouchRotation = rotation; + multitouch = currentMultitouch; + } + if (entry == null) { + return false; + } + + final float scale = (entry.resultWidth / (float) getWidth()); + if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) { + Tx = Ty = 0; + rotationDiff = 0; + snappedRotation = false; + snappedToCenterAndScaled = false; + doNotSpanRotation = false; + activePart = findPartAt(touch.x, touch.y); + if (isPart = (activePart instanceof StoryEntry.Part)) { + entry.parts.remove(activePart); + entry.parts.add((StoryEntry.Part) activePart); + trashPartIndex = activePart.id; + invalidate(); + allowWithSingleTouch = true; + ButtonBounce bounce = partsBounce.get(activePart.id); + if (bounce == null) { + partsBounce.put(activePart.id, bounce = new ButtonBounce(this)); + } + bounce.setPressed(true); + activePartPressed = true; + onEntityDragStart(); + onEntityDraggedTop(false); + onEntityDraggedBottom(false); + } else { + trashPartIndex = -1; + } + touchMatrix.set(activePart.matrix); + } + if (ev.getActionMasked() == MotionEvent.ACTION_MOVE && activePart != null) { + float tx = touch.x * scale, ty = touch.y * scale; + float ltx = lastTouch.x * scale, lty = lastTouch.y * scale; + if (ev.getPointerCount() > 1) { + if (lastTouchDistance != 0) { + float scaleFactor = distance / lastTouchDistance; + touchMatrix.postScale(scaleFactor, scaleFactor, tx, ty); + } + float rotate = (float) Math.toDegrees(rotation - lastTouchRotation); + rotationDiff += rotate; + if (!allowRotation) { + allowRotation = Math.abs(rotationDiff) > 20f; + if (!allowRotation) { + extractPointsData(touchMatrix); + allowRotation = Math.round(angle / 90f) * 90f - angle > 20f; + } + if (!snappedRotation) { + try { + performHapticFeedback(HapticFeedbackConstants.TEXT_HANDLE_MOVE, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); + } catch (Exception ignore) {} + snappedRotation = true; + } + } + if (allowRotation) { + touchMatrix.postRotate(rotate, tx, ty); + } + allowWithSingleTouch = true; + } + if (ev.getPointerCount() > 1 || allowWithSingleTouch) { + touchMatrix.postTranslate(tx - ltx, ty - lty); + Tx += (tx - ltx); + Ty += (ty - lty); + } + if (activePartPressed && MathUtils.distance(0, 0, Tx, Ty) > AndroidUtilities.touchSlop) { + ButtonBounce bounce = partsBounce.get(activePart.id); + if (bounce != null) { + bounce.setPressed(false); + } + activePartPressed = false; + } + finalMatrix.set(touchMatrix); + matrix.set(touchMatrix); + extractPointsData(matrix); + float rotDiff = Math.round(angle / 90f) * 90f - angle; + if (allowRotation && !doNotSpanRotation) { + if (Math.abs(rotDiff) < 3.5f) { + finalMatrix.postRotate(rotDiff, cx, cy); + if (!snappedRotation) { + try { + performHapticFeedback(HapticFeedbackConstants.TEXT_HANDLE_MOVE, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); + } catch (Exception ignore) {} + snappedRotation = true; + } + } else { + snappedRotation = false; + } + } + boolean trash = isPart && MathUtils.distance(touch.x, touch.y, getWidth() / 2f, getHeight() - AndroidUtilities.dp(76)) < AndroidUtilities.dp(35); + if (trash != inTrash) { + onEntityDragTrash(trash); + inTrash = trash; + } + if (trash) { + trashCx = touch.x; + trashCy = touch.y; + } + if (isPart) { + onEntityDraggedTop(cy - h / 2f < AndroidUtilities.dp(66) / (float) getHeight() * entry.resultHeight); + onEntityDraggedBottom(cy + h / 2f > entry.resultHeight - AndroidUtilities.dp(64 + 50) / (float) getHeight() * entry.resultHeight); + } + + activePart.matrix.set(finalMatrix); + entry.editedMedia = true; + applyMatrix(); + invalidate(); + } + if (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL) { + Tx = Ty = 0; + if (!(activePart instanceof StoryEntry.Part) || ev.getPointerCount() <= 1) { + ButtonBounce bounce = partsBounce.get(activePart.id); + if (bounce != null) { + bounce.setPressed(false); + } + activePartPressed = false; + allowWithSingleTouch = false; + onEntityDragEnd(inTrash && ev.getAction() == MotionEvent.ACTION_UP); + activePart = null; + inTrash = false; + onEntityDraggedTop(false); + onEntityDraggedBottom(false); + } + isPart = false; + allowRotation = false; + rotationDiff = 0; + + snappedToCenterAndScaled = false; + snappedRotation = false; + invalidate(); + } + lastTouch.x = touch.x; + lastTouch.y = touch.y; + lastTouchDistance = distance; + lastTouchRotation = rotation; + return true; + } + + public void deleteCurrentPart() { + if (activePart != null) { + entry.parts.remove(activePart); + setupParts(entry); + } + } + + private Matrix tempMatrix; + private float[] tempVertices = new float[2]; + private IStoryPart findPartAt(float x, float y) { + for (int i = entry.parts.size() - 1; i >= 0; --i) { + IStoryPart part = entry.parts.get(i); + tempVertices[0] = x / getWidth() * entry.resultWidth; + tempVertices[1] = y / getHeight() * entry.resultHeight; + if (tempMatrix == null) { + tempMatrix = new Matrix(); + } + part.matrix.invert(tempMatrix); + tempMatrix.mapPoints(tempVertices); + if (tempVertices[0] >= 0 && tempVertices[0] <= part.width && tempVertices[1] >= 0 && tempVertices[1] <= part.height) { + return part; + } + } + return entry; + } + + private long tapTime; + private float tapX, tapY; + private boolean tapTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + tapTime = System.currentTimeMillis(); + tapX = ev.getX(); + tapY = ev.getY(); + return true; + } else if (ev.getAction() == MotionEvent.ACTION_UP) { + if (System.currentTimeMillis() - tapTime <= ViewConfiguration.getTapTimeout() && onTap != null) { + onTap.run(); + } + tapTime = 0; + return true; + } else if (ev.getAction() == MotionEvent.ACTION_CANCEL) { + tapTime = 0; + } + return false; + } + + private Runnable onTap; + public void setOnTapListener(Runnable listener) { + onTap = listener; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + if (allowCropping) { + touchEvent(ev); + return true; + } + return super.onInterceptTouchEvent(ev); + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + boolean result = touchEvent(ev); + if (!(activePart instanceof StoryEntry.Part)) { + result = additionalTouchEvent(ev) || result; + tapTouchEvent(ev); + } + if (result) { + if (ev.getPointerCount() <= 1) { + return super.dispatchTouchEvent(ev) || true; + } + return true; + } + return super.dispatchTouchEvent(ev); + } + + public void onEntityDraggedTop(boolean value) { + + } + + public void onEntityDraggedBottom(boolean value) { + + } + + public void onEntityDragEnd(boolean delete) { + + } + + public void onEntityDragStart() { + + } + + public void onEntityDragTrash(boolean enter) { + + } + + private final HashSet pauseLinks = new HashSet<>(); + public void updatePauseReason(int reasonId, boolean pause) { + if (pause) { + pauseLinks.add(reasonId); + } else { + pauseLinks.remove(reasonId); + } + if (videoPlayer != null) { + videoPlayer.setPlayWhenReady(pauseLinks.isEmpty()); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/RecordControl.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/RecordControl.java new file mode 100644 index 0000000000..2026ff82ce --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/RecordControl.java @@ -0,0 +1,738 @@ +package org.telegram.ui.Stories.recorder; + +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.AndroidUtilities.dpf2; +import static org.telegram.messenger.AndroidUtilities.lerp; + +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.RadialGradient; +import android.graphics.Shader; +import android.graphics.SurfaceTexture; +import android.graphics.drawable.Drawable; +import android.os.SystemClock; +import android.util.Log; +import android.view.MotionEvent; +import android.view.TextureView; +import android.view.View; +import android.view.ViewConfiguration; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.core.graphics.ColorUtils; + +import com.google.zxing.common.detector.MathUtils; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ImageLocation; +import org.telegram.messenger.ImageReceiver; +import org.telegram.messenger.MediaController; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.R; +import org.telegram.messenger.Utilities; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.ButtonBounce; +import org.telegram.ui.Components.CircularProgressDrawable; +import org.telegram.ui.Components.CombinedDrawable; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.Point; + +import java.util.ArrayList; + +public class RecordControl extends View { + + public interface Delegate { + void onPhotoShoot(); + void onVideoRecordStart(boolean byLongPress, Runnable whenStarted); + void onVideoRecordPause(); + void onVideoRecordResume(); + void onVideoRecordEnd(boolean byDuration); + void onVideoDuration(long duration); + void onGalleryClick(); + void onFlipClick(); + void onFlipLongClick(); + void onZoom(float zoom); + void onVideoRecordLocked(); + boolean canRecordAudio(); + } + + public void startAsVideo(boolean isVideo) { + overrideStartModeIsVideoT = -1; + this.startModeIsVideo = isVideo; + invalidate(); + } + + public void startAsVideoT(float isVideoT) { + overrideStartModeIsVideoT = isVideoT; + invalidate(); + } + + public void setDelegate(Delegate delegate) { + this.delegate = delegate; + } + + private Delegate delegate; + + private final ImageReceiver galleryImage = new ImageReceiver(); + private final CombinedDrawable noGalleryDrawable; + private final Drawable flipDrawableWhite, flipDrawableBlack; + private final Drawable unlockDrawable, lockDrawable; + private final Drawable pauseDrawable; + + private final static int WHITE = 0xFFFFFFFF; + private final static int RED = 0xFFF73131; + private final static int BG = 0x64000000; + + private final Paint mainPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint outlinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint outlineFilledPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint buttonPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint buttonPaintWhite = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint redPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint hintLinePaintWhite = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint hintLinePaintBlack = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Matrix redMatrix = new Matrix(); + private RadialGradient redGradient; + + private final ButtonBounce recordButton = new ButtonBounce(this); + private final ButtonBounce flipButton = new ButtonBounce(this); + private final ButtonBounce lockButton = new ButtonBounce(this); + + private float flipDrawableRotate; + private final AnimatedFloat flipDrawableRotateT = new AnimatedFloat(this, 0, 310, CubicBezierInterpolator.EASE_OUT_QUINT); + private boolean dual; + private final AnimatedFloat dualT = new AnimatedFloat(this, 0, 330, CubicBezierInterpolator.EASE_OUT_QUINT); + + private static final long MAX_DURATION = 60 * 1000L; + private long recordingStart; + private long lastDuration; + + public RecordControl(Context context) { + super(context); + + setWillNotDraw(false); + + redGradient = new RadialGradient(0, 0, dp(30 + 18), new int[] {RED, RED, WHITE}, new float[] {0, .64f, 1f}, Shader.TileMode.CLAMP); + redGradient.setLocalMatrix(redMatrix); + redPaint.setShader(redGradient); + outlinePaint.setColor(WHITE); + outlinePaint.setStyle(Paint.Style.STROKE); + outlineFilledPaint.setColor(RED); + outlineFilledPaint.setStrokeCap(Paint.Cap.ROUND); + outlineFilledPaint.setStyle(Paint.Style.STROKE); + buttonPaint.setColor(BG); + buttonPaintWhite.setColor(WHITE); + hintLinePaintWhite.setColor(0x58ffffff); + hintLinePaintWhite.setStyle(Paint.Style.STROKE); + hintLinePaintWhite.setStrokeCap(Paint.Cap.ROUND); + hintLinePaintBlack.setColor(0x18000000); + hintLinePaintBlack.setStyle(Paint.Style.STROKE); + hintLinePaintBlack.setStrokeCap(Paint.Cap.ROUND); + + galleryImage.setParentView(this); + galleryImage.setCrossfadeWithOldImage(true); + galleryImage.setRoundRadius(dp(6)); + + final Drawable noPhotosIcon = context.getResources().getDrawable(R.drawable.msg_media_gallery).mutate(); + noPhotosIcon.setColorFilter(new PorterDuffColorFilter(0x4dFFFFFF, PorterDuff.Mode.MULTIPLY)); + noGalleryDrawable = new CombinedDrawable(Theme.createRoundRectDrawable(dp(6), 0xFF2E2E2F), noPhotosIcon); + noGalleryDrawable.setFullsize(false); + noGalleryDrawable.setIconSize(dp(24), dp(24)); + + flipDrawableWhite = context.getResources().getDrawable(R.drawable.msg_photo_switch2).mutate(); + flipDrawableWhite.setColorFilter(new PorterDuffColorFilter(0xffffffff, PorterDuff.Mode.MULTIPLY)); + flipDrawableBlack = context.getResources().getDrawable(R.drawable.msg_photo_switch2).mutate(); + flipDrawableBlack.setColorFilter(new PorterDuffColorFilter(0xff000000, PorterDuff.Mode.MULTIPLY)); + + unlockDrawable = context.getResources().getDrawable(R.drawable.msg_filled_unlockedrecord).mutate(); + unlockDrawable.setColorFilter(new PorterDuffColorFilter(0xffffffff, PorterDuff.Mode.MULTIPLY)); + lockDrawable = context.getResources().getDrawable(R.drawable.msg_filled_lockedrecord).mutate(); + lockDrawable.setColorFilter(new PorterDuffColorFilter(0xff000000, PorterDuff.Mode.MULTIPLY)); + + pauseDrawable = context.getResources().getDrawable(R.drawable.msg_round_pause_m).mutate(); + pauseDrawable.setColorFilter(new PorterDuffColorFilter(0xffffffff, PorterDuff.Mode.MULTIPLY)); + + updateGalleryImage(); + } + + public void updateGalleryImage() { + final String filter = "80_80"; + ArrayList drafts = MessagesController.getInstance(galleryImage.getCurrentAccount()).getStoriesController().getDraftsController().drafts; + galleryImage.setOrientation(0, 0, true); + if (drafts != null && !drafts.isEmpty() && drafts.get(0).draftThumbFile != null) { + galleryImage.setImage(ImageLocation.getForPath(drafts.get(0).draftThumbFile.getAbsolutePath()), filter, null, null, noGalleryDrawable, 0, null, null, 0); + return; + } + MediaController.AlbumEntry albumEntry = MediaController.allMediaAlbumEntry; + MediaController.PhotoEntry photoEntry = null; + if (albumEntry != null && albumEntry.photos != null && !albumEntry.photos.isEmpty()) { + photoEntry = albumEntry.photos.get(0); + } + if (photoEntry != null && photoEntry.thumbPath != null) { + galleryImage.setImage(ImageLocation.getForPath(photoEntry.thumbPath), filter, null, null, noGalleryDrawable, 0, null, null, 0); + } else if (photoEntry != null && photoEntry.path != null) { + if (photoEntry.isVideo) { + galleryImage.setImage(ImageLocation.getForPath("vthumb://" + photoEntry.imageId + ":" + photoEntry.path), filter, null, null, noGalleryDrawable, 0, null, null, 0); + } else { + galleryImage.setOrientation(photoEntry.orientation, photoEntry.invert, true); + galleryImage.setImage(ImageLocation.getForPath("thumb://" + photoEntry.imageId + ":" + photoEntry.path), filter, null, null, noGalleryDrawable, 0, null, null, 0); + } + } else { + galleryImage.setImageBitmap(noGalleryDrawable); + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + galleryImage.onAttachedToWindow(); + } + + @Override + protected void onDetachedFromWindow() { + galleryImage.onDetachedFromWindow(); + super.onDetachedFromWindow(); + } + + private float cx, cy; + private float leftCx, rightCx; + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int width = MeasureSpec.getSize(widthMeasureSpec); + final int height = dp(100); + + cx = width / 2f; + cy = height / 2f; + + final float dist = Math.min(dp(135), width * .35f); + leftCx = cx - dist; + rightCx = cx + dist; + + setDrawableBounds(flipDrawableWhite, rightCx, cy, dp(14)); + setDrawableBounds(flipDrawableBlack, rightCx, cy, dp(14)); + setDrawableBounds(unlockDrawable, leftCx, cy); + setDrawableBounds(lockDrawable, leftCx, cy); + setDrawableBounds(pauseDrawable, leftCx, cy); + galleryImage.setImageCoords(leftCx - dp(20), cy - dp(20), dp(40), dp(40)); + + redMatrix.reset(); + redMatrix.postTranslate(cx, cy); + redGradient.setLocalMatrix(redMatrix); + + setMeasuredDimension(width, height); + } + + private static void setDrawableBounds(Drawable drawable, float cx, float cy) { + setDrawableBounds(drawable, cx, cy, Math.max(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()) / 2f); + } + + private static void setDrawableBounds(Drawable drawable, float cx, float cy, float r) { + drawable.setBounds((int) (cx - r), (int) (cy - r), (int) (cx + r), (int) (cy + r)); + } + + private final AnimatedFloat startModeIsVideoT = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + private float overrideStartModeIsVideoT = -1; + private boolean startModeIsVideo = true; + + private final AnimatedFloat recordingT = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + private final AnimatedFloat recordingLongT = new AnimatedFloat(this, 0, 850, CubicBezierInterpolator.EASE_OUT_QUINT); + private boolean recording; + + private float loadingSegments[] = new float[2]; + private final AnimatedFloat recordingLoadingT = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + private boolean recordingLoading; + private long recordingLoadingStart; + + private boolean touch; + private boolean discardParentTouch; + private long touchStart; + private float touchX, touchY; + private boolean longpressRecording; + private boolean showLock; + private final AnimatedFloat touchT = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + private final AnimatedFloat touchIsCenterT = new AnimatedFloat(this, 0, 650, CubicBezierInterpolator.EASE_OUT_QUINT); + private final AnimatedFloat touchIsCenter2T = new AnimatedFloat(this, 0, 160, CubicBezierInterpolator.EASE_IN); + private final AnimatedFloat recordCx = new AnimatedFloat(this, 0, 750, CubicBezierInterpolator.EASE_OUT_QUINT); + private final AnimatedFloat touchIsButtonT = new AnimatedFloat(this, 0, 650, CubicBezierInterpolator.EASE_OUT_QUINT); + private final AnimatedFloat lockedT = new AnimatedFloat(this, 0, 320, CubicBezierInterpolator.EASE_OUT_QUINT); + + private final Runnable onRecordLongPressRunnable = () -> { + if (recording) { + return; + } + if (!delegate.canRecordAudio()) { + touch = false; + recordButton.setPressed(false); + flipButton.setPressed(false); + lockButton.setPressed(false); + return; + } + longpressRecording = true; + showLock = true; + delegate.onVideoRecordStart(true, () -> { + recordingStart = System.currentTimeMillis(); + recording = true; + delegate.onVideoDuration(lastDuration = 0); + }); + }; + + private final Runnable onFlipLongPressRunnable = () -> { + if (!recording) { + delegate.onFlipLongClick(); + rotateFlip(360); + + touch = false; + recordButton.setPressed(false); + flipButton.setPressed(false); + lockButton.setPressed(false); + } + }; + + private Path metaballsPath = new Path(); + private Path circlePath = new Path(); + + private final float HALF_PI = (float) Math.PI / 2; + + @Override + protected void onDraw(Canvas canvas) { + final float recordingT = this.recordingT.set(recording ? 1 : 0); + final float recordingLongT = this.recordingLongT.set(recording ? 1 : 0); + final float isVideo = Math.max(recordingT, overrideStartModeIsVideoT >= 0 ? overrideStartModeIsVideoT : this.startModeIsVideoT.set(startModeIsVideo ? 1 : 0)); + + float scale; + + float touchT = this.touchT.set(touch ? 1 : 0); + float touchIsCenterT = touchT * this.touchIsCenterT.set(Math.abs(touchX - cx) < dp(64) && (recording || recordButton.isPressed()) ? 1 : 0); + float touchIsCenter2T = touchT * this.touchIsCenter2T.set(Math.abs(touchX - cx) < dp(64) ? 1 : 0); + float touchCenterT16 = Utilities.clamp((touchX - cx) / dp(16), 1, -1); + float touchCenterT96 = Utilities.clamp((touchX - cx) / dp(64), 1, -1); + float touchIsButtonT = touchT * this.touchIsButtonT.set(Math.min(Math.abs(touchX - rightCx), Math.abs(touchX - leftCx)) < dp(16) ? 1 : 0); + + float hintLineT = longpressRecording ? recordingT * isVideo * touchT : 0; + if (hintLineT > 0) { + float lcx = cx - dp(42 + 8), rcx = cx + dp(42 + 8); + hintLinePaintWhite.setStrokeWidth(dp(2)); + hintLinePaintBlack.setStrokeWidth(dp(2)); + + canvas.drawLine(rcx, cy, lerp(rcx, rightCx - dp(22 + 8), hintLineT), cy, hintLinePaintBlack); + canvas.drawLine(rcx, cy, lerp(rcx, rightCx - dp(22 + 8), hintLineT), cy, hintLinePaintWhite); + + canvas.drawLine(lcx, cy, lerp(lcx, leftCx + dp(22 + 8), hintLineT), cy, hintLinePaintBlack); + canvas.drawLine(lcx, cy, lerp(lcx, leftCx + dp(22 + 8), hintLineT), cy, hintLinePaintWhite); + } + + canvas.save(); + scale = lerp(recordButton.getScale(startModeIsVideo ? 0 : .2f), 1, recordingT); + canvas.scale(scale, scale, cx, cy); + mainPaint.setColor(ColorUtils.blendARGB(WHITE, RED, isVideo)); + float acx = lerp(cx, recordCx.set(cx + dp(4) * touchCenterT16), touchIsCenterT); + float r = lerp(lerp(dp(29), dp(12), recordingT), dp(32) - dp(4) * Math.abs(touchCenterT96), touchIsCenterT); + float rad = lerp(lerp(dp(32), dp(7), recordingT), dp(32), touchIsCenterT); + AndroidUtilities.rectTmp.set(acx - r, cy - r, acx + r, cy + r); + canvas.drawRoundRect(AndroidUtilities.rectTmp, rad, rad, mainPaint); + canvas.restore(); + + canvas.save(); + scale = Math.max(scale, 1); + canvas.scale(scale, scale, cx, cy); + outlinePaint.setStrokeWidth(dp(3)); + float or = Math.max(dpf2(33.5f), r + lerp(dpf2(4.5f), dp(9), touchIsCenterT)); + canvas.drawCircle(cx, cy, or, outlinePaint); + + long duration = System.currentTimeMillis() - recordingStart; + AndroidUtilities.rectTmp.set(cx - or, cy - or, cx + or, cy + or); + float recordEndT = recording ? 0 : 1f - recordingLongT; + float sweepAngle = duration / (float) MAX_DURATION * 360; + + float recordingLoading = this.recordingLoadingT.set(this.recordingLoading); + + outlineFilledPaint.setStrokeWidth(dp(3)); + outlineFilledPaint.setAlpha((int) (0xFF * Math.max(.7f * recordingLoading, 1f - recordEndT))); + + if (recordingLoading <= 0) { + canvas.drawArc(AndroidUtilities.rectTmp, -90, sweepAngle, false, outlineFilledPaint); + } else { + final long now = SystemClock.elapsedRealtime(); + CircularProgressDrawable.getSegments((now - recordingLoadingStart) % 5400, loadingSegments); + invalidate(); + float fromAngle = loadingSegments[0], toAngle = loadingSegments[1]; + + float center = (fromAngle + toAngle) / 2f; + float amplitude = Math.abs(toAngle - fromAngle) / 2f; + + if (this.recordingLoading) { + center = lerp(-90 + sweepAngle / 2f, center, recordingLoading); + amplitude = lerp(sweepAngle / 2f, amplitude, recordingLoading); + } + + canvas.drawArc(AndroidUtilities.rectTmp, center - amplitude, amplitude * 2, false, outlineFilledPaint); + } + + if (recording) { + invalidate(); + + if (duration / 1000L != lastDuration / 1000L) { + delegate.onVideoDuration(duration / 1000L); + } + if (duration >= MAX_DURATION) { + post(() -> { + recording = false; + longpressRecording = false; + this.recordingLoadingStart = SystemClock.elapsedRealtime(); + this.recordingLoading = true; + touch = false; + recordButton.setPressed(false); + flipButton.setPressed(false); + lockButton.setPressed(false); + delegate.onVideoRecordEnd(true); + }); + } + lastDuration = duration; + } + + canvas.restore(); + + if (showLock) { + scale = lockButton.getScale(.2f) * recordingT; + if (scale > 0) { + canvas.save(); + canvas.scale(scale, scale, leftCx, cy); + canvas.drawCircle(leftCx, cy, dp(22), buttonPaint); + unlockDrawable.draw(canvas); + canvas.restore(); + } + } + + scale = lockButton.getScale(.2f) * (1f - recordingT); + if (scale > 0) { + canvas.save(); + canvas.scale(scale, scale, leftCx, cy); + galleryImage.draw(canvas); + canvas.restore(); + } + + float dualT = this.dualT.set(dual ? 1f : 0f); + if (dualT > 0) { + canvas.save(); + scale = flipButton.getScale(.2f) * dualT; + canvas.scale(scale, scale, rightCx, cy); + canvas.rotate(flipDrawableRotateT.set(flipDrawableRotate), rightCx, cy); + canvas.drawCircle(rightCx, cy, dp(22), buttonPaintWhite); + flipDrawableBlack.draw(canvas); + canvas.restore(); + } + if (dualT < 1) { + canvas.save(); + scale = flipButton.getScale(.2f) * (1f - dualT); + canvas.scale(scale, scale, rightCx, cy); + canvas.rotate(flipDrawableRotateT.set(flipDrawableRotate), rightCx, cy); + canvas.drawCircle(rightCx, cy, dp(22), buttonPaint); + flipDrawableWhite.draw(canvas); + canvas.restore(); + } + + final float tr; + if (longpressRecording) { + tr = ( + touchT * + isVideo * + recordingT * + lerp( + dp(16), + lerp( + dp(8) + dp(8) * Math.abs(touchCenterT96), + dp(22), + touchIsButtonT + ), + Math.max(touchIsButtonT, touchIsCenterT) + ) + ); + } else { + tr = 0; + } + float locked = lockedT.set(!longpressRecording && recording ? 1 : 0); + if (tr > 0) { + redPaint.setAlpha(0xFF); + canvas.drawCircle(touchX, cy, tr, redPaint); + + float x1 = acx, x2 = touchX; + final float handleSize = 2.4f; + final float v = Utilities.clamp(1f - touchT * Math.abs(touchCenterT96) / 1.3f, 1, 0); + final float d = Math.abs(x1 - x2); + final float maxdist = r + tr * 2f; + if (d < maxdist && v < .6f) { + + double u1, u2; + if (d < r + tr) { + u1 = Math.acos((r * r + d * d - tr * tr) / (2 * r * d)); + u2 = Math.acos((tr * tr + d * d - r * r) / (2 * tr * d)); + } else { + u1 = u2 = 0; + } + + final double angleBetweenCenters = x2 > x1 ? 0 : Math.PI; + final double maxSpread = (float) Math.acos((r - tr) / d); + + double angle1 = angleBetweenCenters + u1 + (maxSpread - u1) * v; + double angle2 = angleBetweenCenters - u1 - (maxSpread - u1) * v; + double angle3 = angleBetweenCenters + Math.PI - u2 - (Math.PI - u2 - maxSpread) * v; + double angle4 = angleBetweenCenters - Math.PI + u2 + (Math.PI - u2 - maxSpread) * v; + + getVector(x1, cy, angle1, r, p1); + getVector(x1, cy, angle2, r, p2); + getVector(x2, cy, angle3, tr, p3); + getVector(x2, cy, angle4, tr, p4); + + final float totalRadius = r + tr; + final float d2Base = Math.min(v * handleSize, dist(p1, p3) / totalRadius); + final float d2 = d2Base * Math.min(1, (d * 2) / (r + tr)); + + final float r1 = r * d2; + final float r2 = tr * d2; + + getVector(p1.x, p1.y, angle1 - HALF_PI, r1, h1); + getVector(p2.x, p2.y, angle2 + HALF_PI, r1, h2); + getVector(p3.x, p3.y, angle3 + HALF_PI, r2, h3); + getVector(p4.x, p4.y, angle4 - HALF_PI, r2, h4); + + float alpha = touchT * isVideo * recordingT * touchIsCenter2T; + + if (alpha > 0) { + metaballsPath.rewind(); + + metaballsPath.moveTo(p1.x, p1.y); + metaballsPath.cubicTo(h1.x, h1.y, h3.x, h3.y, p3.x, p3.y); + metaballsPath.lineTo(p4.x, p4.y); + metaballsPath.cubicTo(h4.x, h4.y, h2.x, h2.y, p2.x, p2.y); + metaballsPath.lineTo(p1.x, p1.y); + + redPaint.setAlpha((int) (0xFF * alpha)); + canvas.drawPath(metaballsPath, redPaint); + + AndroidUtilities.rectTmp.set(acx - r, cy - r, acx + r, cy + r); + canvas.drawRoundRect(AndroidUtilities.rectTmp, rad, rad, redPaint); + } + } + } + if (tr > 0 || locked > 0) { + scale = lockButton.getScale(.2f) * recordingT; + canvas.save(); + circlePath.rewind(); + if (tr > 0) { + circlePath.addCircle(touchX, cy, tr, Path.Direction.CW); + } + if (locked > 0 && showLock) { + circlePath.addCircle(leftCx, cy, locked * dp(22) * scale, Path.Direction.CW); + } + canvas.clipPath(circlePath); + + if (showLock) { + canvas.save(); + canvas.scale(scale, scale, leftCx, cy); + canvas.drawCircle(leftCx, cy, dp(22), buttonPaintWhite); + lockDrawable.draw(canvas); + canvas.restore(); + } + + scale = flipButton.getScale(.2f); + canvas.save(); + canvas.scale(scale, scale, rightCx, cy); + canvas.rotate(flipDrawableRotateT.set(flipDrawableRotate), rightCx, cy); + canvas.drawCircle(rightCx, cy, dp(22), buttonPaintWhite); + flipDrawableBlack.draw(canvas); + canvas.restore(); + + canvas.restore(); + } + } + + private final Point p1 = new Point(), p2 = new Point(), p3 = new Point(), p4 = new Point(), h1 = new Point(), h2 = new Point(), h3 = new Point(), h4 = new Point(); + private void getVector(float cx, float cy, double a, float r, Point point) { + point.x = (float) (cx + Math.cos(a) * r); + point.y = (float) (cy + Math.sin(a) * r); + } + + private float dist(Point a, Point b) { + return MathUtils.distance(a.x, a.y, b.x, b.y); + } + + public void rotateFlip(float angles) { + flipDrawableRotateT.setDuration(angles > 180 ? 620 : 310); + flipDrawableRotate += angles; + invalidate(); + } + + private boolean isPressed(float ex, float ey, float cx, float cy, float r, boolean ignoreWhenZoom) { + if (recording) { + if (ignoreWhenZoom && cy - ey > AndroidUtilities.dp(100)) { + return false; + } + return Math.abs(cx - ex) <= r; + } + return MathUtils.distance(ex, ey, cx, cy) <= r; + } + + private boolean flipButtonWasPressed; + + public boolean isTouch() { + return discardParentTouch; + } + + public void setDual(boolean active) { + if (active != dual) { + this.dual = active; + invalidate(); + } + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + float ox = 0, oy = 0; + final int action = event.getAction(); + + final float x = Utilities.clamp(event.getX() + ox, rightCx, leftCx), y = event.getY() + oy; + + final boolean innerFlipButton = isPressed(x, y, rightCx, cy, dp(7), true); + if (recordingLoading) { + recordButton.setPressed(false); + flipButton.setPressed(false); + lockButton.setPressed(false); + } else if (action == MotionEvent.ACTION_DOWN || touch) { + recordButton.setPressed(isPressed(x, y, cx, cy, dp(60), false)); + flipButton.setPressed(isPressed(x, y, rightCx, cy, dp(30), true)); + lockButton.setPressed(isPressed(x, y, leftCx, cy, dp(30), false)); + } + + boolean r = false; + if (action == MotionEvent.ACTION_DOWN) { + touch = true; + discardParentTouch = recordButton.isPressed() || flipButton.isPressed(); + touchStart = System.currentTimeMillis(); + touchX = x; + touchY = y; + + if (Math.abs(touchX - cx) < dp(50)) { + AndroidUtilities.runOnUIThread(onRecordLongPressRunnable, ViewConfiguration.getLongPressTimeout()); + } + + if (flipButton.isPressed()) { + AndroidUtilities.runOnUIThread(onFlipLongPressRunnable, ViewConfiguration.getLongPressTimeout()); + } + + r = true; + } else if (action == MotionEvent.ACTION_MOVE) { + if (!touch) { + return false; + } + touchX = Utilities.clamp(x, rightCx, leftCx); + touchY = y; + invalidate(); + + if (recording && !flipButtonWasPressed && innerFlipButton) { + rotateFlip(180); + delegate.onFlipClick(); + } + + if (recording && longpressRecording) { + final float dy = cy - dp(48) - y; + final float zoom = Utilities.clamp(dy / (AndroidUtilities.displaySize.y / 2f), 1, 0); + delegate.onZoom(zoom); + } + + r = true; + } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { + if (!touch) { + return false; + } + + touch = false; + discardParentTouch = false; + + AndroidUtilities.cancelRunOnUIThread(onRecordLongPressRunnable); + AndroidUtilities.cancelRunOnUIThread(onFlipLongPressRunnable); + + if (!recording && lockButton.isPressed()) { + delegate.onGalleryClick(); + } else if (recording && longpressRecording) { + if (lockButton.isPressed()) { + longpressRecording = false; + lockedT.set(1, true); + delegate.onVideoRecordLocked(); + } else { + recording = false; + this.recordingLoadingStart = SystemClock.elapsedRealtime(); + this.recordingLoading = true; + delegate.onVideoRecordEnd(false); + } + } else if (recordButton.isPressed()) { + if (!startModeIsVideo && !recording && !longpressRecording) { + delegate.onPhotoShoot(); + } else if (!recording) { + if (delegate.canRecordAudio()) { + lastDuration = 0; + recordingStart = System.currentTimeMillis(); + showLock = false; + delegate.onVideoRecordStart(false, () -> { + recordingStart = System.currentTimeMillis(); + lastDuration = 0; + recording = true; + delegate.onVideoDuration(lastDuration); + }); + } + } else { + recording = false; + this.recordingLoadingStart = SystemClock.elapsedRealtime(); + this.recordingLoading = true; + delegate.onVideoRecordEnd(false); + } + } + + longpressRecording = false; + + if (flipButton.isPressed()) { + rotateFlip(180); + delegate.onFlipClick(); + } + + recordButton.setPressed(false); + flipButton.setPressed(false); + lockButton.setPressed(false); + + invalidate(); + + r = true; + } + flipButtonWasPressed = innerFlipButton; + return r; + } + + public void stopRecording() { + if (!recording) { + return; + } + recording = false; + this.recordingLoadingStart = SystemClock.elapsedRealtime(); + this.recordingLoading = true; + delegate.onVideoRecordEnd(false); + recordButton.setPressed(false); + flipButton.setPressed(false); + lockButton.setPressed(false); + invalidate(); + } + + public void stopRecordingLoading(boolean animated) { + this.recordingLoading = false; + if (!animated) { + this.recordingLoadingT.set(false, true); + } + invalidate(); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/RecordControlRenderer.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/RecordControlRenderer.java new file mode 100644 index 0000000000..edc5dd5977 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/RecordControlRenderer.java @@ -0,0 +1,314 @@ +package org.telegram.ui.Stories.recorder; + +import android.content.Context; +import android.graphics.SurfaceTexture; +import android.opengl.EGL14; +import android.opengl.GLES11Ext; +import android.opengl.GLES20; +import android.opengl.GLUtils; +import android.os.Build; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.view.Display; +import android.view.WindowManager; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.BuildVars; +import org.telegram.messenger.DispatchQueue; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.Intro; +import org.telegram.messenger.camera.CameraSession; +import org.telegram.messenger.camera.CameraView; + +import java.io.File; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.util.concurrent.TimeUnit; + +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.egl.EGLContext; +import javax.microedition.khronos.egl.EGLDisplay; +import javax.microedition.khronos.egl.EGLSurface; +import javax.microedition.khronos.opengles.GL; + +public class RecordControlRenderer extends DispatchQueue { + + private static final String VERTEX_SHADER = + "attribute vec4 aPosition;\n" + + "void main() {\n" + + " gl_Position = aPosition;\n" + + "}\n"; + private final static String FRAGMENT_SHADER = + "precision lowp float;\n" + + "void main() {\n" + + " gl_FragColor = vec4(1., 0., 0., 1.);\n" + + "}\n"; + + private final Object layoutLock = new Object(); + private FloatBuffer vertexBuffer; + + private final static int EGL_CONTEXT_CLIENT_VERSION = 0x3098; + private final static int EGL_OPENGL_ES2_BIT = 4; + private SurfaceTexture surfaceTexture; + private EGL10 egl10; + private EGLDisplay eglDisplay; + private EGLContext eglContext; + private EGLSurface eglSurface; + private EGLConfig eglConfig; + private boolean initied; + + private final int DO_RENDER_MESSAGE = 0; + private final int DO_SHUTDOWN_MESSAGE = 1; + + private int drawProgram; + private int positionHandle; + private int resolutionUniform; + + private int width, height; + + private Integer cameraId = 0; + + public RecordControlRenderer(SurfaceTexture surface) { + super("RecordControlRenderer"); + surfaceTexture = surface; + } + + private boolean initGL() { + if (BuildVars.LOGS_ENABLED) { + FileLog.d("RecordControlRenderer " + "start init gl"); + } + egl10 = (EGL10) EGLContext.getEGL(); + + eglDisplay = egl10.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); + if (eglDisplay == EGL10.EGL_NO_DISPLAY) { + if (BuildVars.LOGS_ENABLED) { + FileLog.e("eglGetDisplay failed " + GLUtils.getEGLErrorString(egl10.eglGetError())); + } + eglDisplay = null; + finish(); + return false; + } + + int[] version = new int[2]; + if (!egl10.eglInitialize(eglDisplay, version)) { + if (BuildVars.LOGS_ENABLED) { + FileLog.e("eglInitialize failed " + GLUtils.getEGLErrorString(egl10.eglGetError())); + } + finish(); + return false; + } + + int[] configsCount = new int[1]; + EGLConfig[] configs = new EGLConfig[1]; + int[] configSpec = new int[]{ + EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL10.EGL_RED_SIZE, 8, + EGL10.EGL_GREEN_SIZE, 8, + EGL10.EGL_BLUE_SIZE, 8, + EGL10.EGL_ALPHA_SIZE, 0, + EGL10.EGL_DEPTH_SIZE, 0, + EGL10.EGL_STENCIL_SIZE, 0, + EGL10.EGL_NONE + }; + if (!egl10.eglChooseConfig(eglDisplay, configSpec, configs, 1, configsCount)) { + if (BuildVars.LOGS_ENABLED) { + FileLog.e("eglChooseConfig failed " + GLUtils.getEGLErrorString(egl10.eglGetError())); + } + finish(); + return false; + } else if (configsCount[0] > 0) { + eglConfig = configs[0]; + } else { + if (BuildVars.LOGS_ENABLED) { + FileLog.e("eglConfig not initialized"); + } + finish(); + return false; + } + int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE}; + eglContext = egl10.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list); + if (eglContext == null || eglContext == EGL10.EGL_NO_CONTEXT) { + eglContext = null; + if (BuildVars.LOGS_ENABLED) { + FileLog.e("eglCreateContext failed " + GLUtils.getEGLErrorString(egl10.eglGetError())); + } + finish(); + return false; + } + + if (surfaceTexture != null) { + eglSurface = egl10.eglCreateWindowSurface(eglDisplay, eglConfig, surfaceTexture, null); + } else { + finish(); + return false; + } + + if (eglSurface == null || eglSurface == EGL10.EGL_NO_SURFACE) { + if (BuildVars.LOGS_ENABLED) { + FileLog.e("createWindowSurface failed " + GLUtils.getEGLErrorString(egl10.eglGetError())); + } + finish(); + return false; + } + if (!egl10.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) { + if (BuildVars.LOGS_ENABLED) { + FileLog.e("eglMakeCurrent failed " + GLUtils.getEGLErrorString(egl10.eglGetError())); + } + finish(); + return false; + } + GL gl = eglContext.getGL(); + + int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER); + int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER); + if (vertexShader != 0 && fragmentShader != 0) { + drawProgram = GLES20.glCreateProgram(); + GLES20.glAttachShader(drawProgram, vertexShader); + GLES20.glAttachShader(drawProgram, fragmentShader); + GLES20.glLinkProgram(drawProgram); + int[] linkStatus = new int[1]; + GLES20.glGetProgramiv(drawProgram, GLES20.GL_LINK_STATUS, linkStatus, 0); + if (linkStatus[0] == 0) { + if (BuildVars.LOGS_ENABLED) { + FileLog.e("failed link shader"); + } + GLES20.glDeleteProgram(drawProgram); + drawProgram = 0; + } else { + positionHandle = GLES20.glGetAttribLocation(drawProgram, "aPosition"); + } + } else { + if (BuildVars.LOGS_ENABLED) { + FileLog.e("failed creating shader"); + } + finish(); + return false; + } + + if (BuildVars.LOGS_ENABLED) { + FileLog.e("gl initied"); + } + + float[] verticesData = { + -1.0f, -1.0f, 0, + 1.0f, -1.0f, 0, + -1.0f, 1.0f, 0, + 1.0f, 1.0f, 0 + }; + + vertexBuffer = ByteBuffer.allocateDirect(verticesData.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer(); + vertexBuffer.put(verticesData).position(0); + + return true; + } + + private int loadShader(int type, String shaderCode) { + int shader = GLES20.glCreateShader(type); + GLES20.glShaderSource(shader, shaderCode); + GLES20.glCompileShader(shader); + int[] compileStatus = new int[1]; + GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compileStatus, 0); + if (compileStatus[0] == 0) { + if (BuildVars.LOGS_ENABLED) { + FileLog.e(GLES20.glGetShaderInfoLog(shader)); + } + GLES20.glDeleteShader(shader); + shader = 0; + } + return shader; + } + + public void finish() { + if (eglSurface != null) { + egl10.eglMakeCurrent(eglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); + egl10.eglDestroySurface(eglDisplay, eglSurface); + eglSurface = null; + } + if (eglContext != null) { + egl10.eglDestroyContext(eglDisplay, eglContext); + eglContext = null; + } + if (eglDisplay != null) { + egl10.eglTerminate(eglDisplay); + eglDisplay = null; + } + } + + final int array[] = new int[1]; + + private void draw() { + if (!initied) { + return; + } + + if (!eglContext.equals(egl10.eglGetCurrentContext()) || !eglSurface.equals(egl10.eglGetCurrentSurface(EGL10.EGL_DRAW))) { + if (!egl10.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) { + if (BuildVars.LOGS_ENABLED) { + FileLog.e("eglMakeCurrent failed " + GLUtils.getEGLErrorString(egl10.eglGetError())); + } + return; + } + } + + egl10.eglQuerySurface(eglDisplay, eglSurface, EGL10.EGL_WIDTH, array); + int drawnWidth = array[0]; + egl10.eglQuerySurface(eglDisplay, eglSurface, EGL10.EGL_HEIGHT, array); + int drawnHeight = array[0]; + + GLES20.glViewport(0, 0, drawnWidth, drawnHeight); + + GLES20.glUseProgram(drawProgram); + + GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 12, vertexBuffer); + GLES20.glEnableVertexAttribArray(positionHandle); + + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + + GLES20.glDisableVertexAttribArray(positionHandle); + GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0); + GLES20.glUseProgram(0); + + egl10.eglSwapBuffers(eglDisplay, eglSurface); + } + + + @Override + public void run() { + initied = initGL(); + super.run(); + } + + @Override + public void handleMessage(Message inputMessage) { + int what = inputMessage.what; + + switch (what) { + case DO_RENDER_MESSAGE: + draw( + + ); + break; + } + } + + public void shutdown() { + postRunnable(() -> { + finish(); + Looper looper = Looper.myLooper(); + if (looper != null) { + looper.quit(); + } + }); + } + + public void requestRender() { + Handler handler = getHandler(); + if (handler != null) { + sendMessage(handler.obtainMessage(DO_RENDER_MESSAGE, cameraId), 0); + } + } +} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/StoryEntry.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/StoryEntry.java new file mode 100644 index 0000000000..a368a5b416 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/StoryEntry.java @@ -0,0 +1,940 @@ +package org.telegram.ui.Stories.recorder; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.BitmapShader; +import android.graphics.BlurMaskFilter; +import android.graphics.Canvas; +import android.graphics.LinearGradient; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Shader; +import android.media.MediaExtractor; +import android.media.MediaFormat; +import android.net.Uri; +import android.os.Build; +import android.provider.MediaStore; +import android.text.SpannableString; +import android.util.Log; + +import androidx.annotation.NonNull; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.Emoji; +import org.telegram.messenger.FileLoader; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.FileRefController; +import org.telegram.messenger.MediaController; +import org.telegram.messenger.MediaDataController; +import org.telegram.messenger.MessageObject; +import org.telegram.messenger.SendMessagesHelper; +import org.telegram.messenger.SharedConfig; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.Utilities; +import org.telegram.messenger.VideoEditedInfo; +import org.telegram.tgnet.AbstractSerializedData; +import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.RequestDelegate; +import org.telegram.tgnet.TLObject; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AnimatedFileDrawable; +import org.telegram.ui.Components.PhotoFilterView; +import org.telegram.ui.Components.RLottieDrawable; + +import java.io.File; +import java.io.FileOutputStream; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.List; + +public class StoryEntry extends IStoryPart { + + public final int currentAccount = UserConfig.selectedAccount; + + public long draftId; + public boolean isDraft; + public long draftDate; + + public int editStoryId; + public boolean isEdit; + public double fileDuration = -1; + public boolean editedMedia, editedCaption, editedPrivacy; + + public boolean isVideo; + public File file; + public boolean fileDeletable; + public String thumbPath; + + public boolean muted; + public float left, right = 1; + + public int orientation; + public int invert; + +// public int width, height; + public long duration; + + public int resultWidth = 720; + public int resultHeight = 1280; + + public int partsMaxId = 1; + public final ArrayList parts = new ArrayList<>(); + public static class Part extends IStoryPart { + public File file; + public boolean fileDeletable; + public int orientantion, invert; + + public void readParams(AbstractSerializedData stream, boolean exception) { + width = stream.readInt32(exception); + height = stream.readInt32(exception); + file = new File(stream.readString(exception)); + fileDeletable = stream.readBool(exception); + orientantion = stream.readInt32(exception); + invert = stream.readInt32(exception); + float[] values = new float[9]; + for (int i = 0; i < 9; ++i) { + values[i] = stream.readFloat(exception); + } + matrix.setValues(values); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(width); + stream.writeInt32(height); + stream.writeString(file == null ? "" : file.getAbsolutePath()); + stream.writeBool(fileDeletable); + stream.writeInt32(orientantion); + stream.writeInt32(invert); + float[] values = new float[9]; + matrix.getValues(values); + for (int i = 0; i < 9; ++i) { + stream.writeFloat(values[i]); + } + } + } + + // matrix describes transformations from width x height to resultWidth x resultHeight +// public final Matrix matrix = new Matrix(); + public int gradientTopColor, gradientBottomColor; + + public CharSequence caption; + public StoryPrivacyBottomSheet.StoryPrivacy privacy; + public final ArrayList privacyRules = new ArrayList<>(); + + public boolean pinned; + public boolean allowScreenshots; + + public int period = 86400; + + // share as message (postponed) + public ArrayList shareUserIds; + public boolean silent; + public int scheduleDate; + + public Bitmap blurredVideoThumb; + public File uploadThumbFile; + public File draftThumbFile; + + // paint + public File paintFile; + public long averageDuration = 5000; + public ArrayList mediaEntities; + public List stickers; + public List editStickers; + + // filter + public File filterFile; + public MediaController.SavedFilterState filterState; + + public Bitmap thumbBitmap; + private boolean fromCamera; + + public boolean wouldBeVideo() { + if (isVideo) { + return true; + } + if (mediaEntities != null && !mediaEntities.isEmpty()) { + for (int i = 0; i < mediaEntities.size(); ++i) { + VideoEditedInfo.MediaEntity entity = mediaEntities.get(i); + if (entity.type == VideoEditedInfo.MediaEntity.TYPE_STICKER) { + if (isAnimated(entity.document, entity.text)) { + return true; + } + } else if (entity.type == VideoEditedInfo.MediaEntity.TYPE_TEXT && entity.entities != null && !entity.entities.isEmpty()) { + for (int j = 0; j < entity.entities.size(); ++j) { + VideoEditedInfo.EmojiEntity e = entity.entities.get(j); + if (isAnimated(e.document, e.documentAbsolutePath)) { + return true; + } + } + } + } + } + return false; + } + + private boolean isAnimated(TLRPC.Document document, String path) { + return document != null && ( + MessageObject.isAnimatedStickerDocument(document) || + MessageObject.isAnimatedStickerDocument(document, true) && RLottieDrawable.getFramesCount(path, null) > 1 + ); + } + + + public void buildPhoto(File dest) { + + Matrix tempMatrix = new Matrix(); + + Paint bitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG); + Bitmap finalBitmap = Bitmap.createBitmap(resultWidth, resultHeight, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(finalBitmap); + + Paint gradientPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + gradientPaint.setShader(new LinearGradient(0, 0, 0, canvas.getHeight(), new int[] { gradientTopColor, gradientBottomColor }, new float[] {0, 1}, Shader.TileMode.CLAMP)); + canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), gradientPaint); + + tempMatrix.set(matrix); + File file = filterFile != null ? filterFile : this.file; + if (file != null) { + try { + Bitmap fileBitmap = getScaledBitmap(opts -> BitmapFactory.decodeFile(file.getPath(), opts), resultWidth, resultHeight, true); + final float scale = (float) width / fileBitmap.getWidth(); + tempMatrix.preScale(scale, scale); + canvas.drawBitmap(fileBitmap, tempMatrix, bitmapPaint); + fileBitmap.recycle(); + } catch (Exception e) { + FileLog.e(e); + } + } + + for (int i = 0; i < parts.size(); ++i) { + try { + final Part part = parts.get(i); + Bitmap fileBitmap = getScaledBitmap(opts -> BitmapFactory.decodeFile(part.file.getPath(), opts), resultWidth, resultHeight, false); + final float scale = (float) part.width / fileBitmap.getWidth(); + tempMatrix.set(part.matrix); + tempMatrix.preScale(scale, scale); + canvas.drawBitmap(fileBitmap, tempMatrix, bitmapPaint); + fileBitmap.recycle(); + } catch (Exception e) { + FileLog.e(e); + } + } + + if (paintFile != null) { + try { + Bitmap paintBitmap = getScaledBitmap(opts -> BitmapFactory.decodeFile(paintFile.getPath(), opts), resultWidth, resultHeight, false); + canvas.save(); + float scale = resultWidth / (float) paintBitmap.getWidth(); + canvas.scale(scale, scale); + canvas.drawBitmap(paintBitmap, 0, 0, bitmapPaint); + canvas.restore(); + paintBitmap.recycle(); + } catch (Exception e) { + FileLog.e(e); + } + } + + thumbBitmap = Bitmap.createScaledBitmap(finalBitmap, 40, 22, true); + + try { + FileOutputStream stream = new FileOutputStream(dest); + finalBitmap.compress(Bitmap.CompressFormat.JPEG, 95, stream); + stream.close(); + } catch (Exception e) { + FileLog.e(e); + } + + finalBitmap.recycle(); + } + + public static interface DecodeBitmap { + public Bitmap decode(BitmapFactory.Options options); + } + + public static Bitmap getScaledBitmap(DecodeBitmap decode, int maxWidth, int maxHeight, boolean allowBlur) { + BitmapFactory.Options opts = new BitmapFactory.Options(); + opts.inJustDecodeBounds = true; + decode.decode(opts); + + opts.inJustDecodeBounds = false; + opts.inScaled = false; + + final Runtime runtime = Runtime.getRuntime(); + final long availableMemory = runtime.maxMemory() - (runtime.totalMemory() - runtime.freeMemory()); + final boolean enoughMemory = (opts.outWidth * opts.outHeight * 4L + maxWidth * maxHeight * 4L) * 1.1 <= availableMemory; + + if (opts.outWidth <= maxWidth && opts.outHeight <= maxHeight) { + return decode.decode(opts); + } + + if (enoughMemory && SharedConfig.getDevicePerformanceClass() >= SharedConfig.PERFORMANCE_CLASS_AVERAGE) { + Bitmap bitmap = decode.decode(opts); + + final float scaleX = maxWidth / (float) bitmap.getWidth(), scaleY = maxHeight / (float) bitmap.getHeight(); + float scale = Math.max(scaleX, scaleY); +// if (SharedConfig.getDevicePerformanceClass() >= SharedConfig.PERFORMANCE_CLASS_HIGH) { +// scale = Math.min(scale * 2, 1); +// } + final int w = (int) (bitmap.getWidth() * scale), h = (int) (bitmap.getHeight() * scale); + + Bitmap scaledBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(scaledBitmap); + + final Matrix matrix = new Matrix(); + final BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); + final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + paint.setShader(shader); + + int blurRadius = Utilities.clamp(Math.round(1f / scale), 8, 0); + + matrix.reset(); + matrix.postScale(scale, scale); + shader.setLocalMatrix(matrix); + canvas.drawRect(0, 0, w, h, paint); + + if (allowBlur && blurRadius > 0) { + Utilities.stackBlurBitmap(scaledBitmap, blurRadius); + } + + return scaledBitmap; + } else { + opts.inScaled = true; + opts.inDensity = opts.outWidth; + opts.inTargetDensity = maxWidth; + return decode.decode(opts); + } + } + + public File getOriginalFile() { + if (filterFile != null) { + return filterFile; + } + return file; + } + + private String ext(File file) { + if (file == null) { + return null; + } + String s = file.getPath(); + int i; + if ((i = s.lastIndexOf('.')) > 0) + return s.substring(i + 1); + return null; + } + + public void updateFilter(PhotoFilterView filterView, Runnable whenDone) { + clearFilter(); + + filterState = filterView.getSavedFilterState(); + if (!isVideo) { + if (filterState.isEmpty()) { + if (whenDone != null) { + whenDone.run(); + } + return; + } + + Bitmap bitmap = filterView.getBitmap(); + if (bitmap == null) { + if (whenDone != null) { + whenDone.run(); + } + return; + } + + final Matrix matrix = new Matrix(); + matrix.postScale(invert == 1 ? -1.0f : 1.0f, invert == 2 ? -1.0f : 1.0f, width / 2f, height / 2f); + matrix.postRotate(-orientation); + final Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); + this.matrix.preScale((float) width / rotatedBitmap.getWidth(), (float) height / rotatedBitmap.getHeight()); + width = rotatedBitmap.getWidth(); + height = rotatedBitmap.getHeight(); + + bitmap.recycle(); + + if (filterFile != null && filterFile.exists()) { + filterFile.delete(); + } + String ext = ext(file); + final boolean supportTransparent = "png".equals(ext) || "webp".equals(ext); + filterFile = makeCacheFile(currentAccount, supportTransparent ? "webp" : "jpg"); + if (whenDone == null) { + try { + FileOutputStream stream = new FileOutputStream(filterFile); + rotatedBitmap.compress(supportTransparent ? Bitmap.CompressFormat.WEBP : Bitmap.CompressFormat.JPEG, 90, stream); + } catch (Exception e) { + FileLog.e(e); + } + rotatedBitmap.recycle(); + } else { + Utilities.themeQueue.postRunnable(() -> { + try { + FileOutputStream stream = new FileOutputStream(filterFile); + rotatedBitmap.compress(supportTransparent ? Bitmap.CompressFormat.WEBP : Bitmap.CompressFormat.JPEG, 90, stream); + } catch (Exception e) { + FileLog.e(e, false); + if (supportTransparent) { + try { + FileOutputStream stream = new FileOutputStream(filterFile); + rotatedBitmap.compress(Bitmap.CompressFormat.PNG, 90, stream); + } catch (Exception e2) { + FileLog.e(e2, false); + } + } + } + rotatedBitmap.recycle(); + AndroidUtilities.runOnUIThread(whenDone); + }); + } + } else { + if (whenDone != null) { + whenDone.run(); + } + } + } + + public void clearFilter() { + if (filterFile != null) { + filterFile.delete(); + filterFile = null; + } + } + + public void clearPaint() { + if (paintFile != null) { + paintFile.delete(); + paintFile = null; + } + } + + public void destroy(boolean draft) { + if (blurredVideoThumb != null && !blurredVideoThumb.isRecycled()) { + blurredVideoThumb.recycle(); + blurredVideoThumb = null; + } + if (uploadThumbFile != null) { + uploadThumbFile.delete(); + uploadThumbFile = null; + } + if (!draft) { + clearPaint(); + clearFilter(); + if (file != null) { + if (fileDeletable && (!isEdit || editedMedia)) { + file.delete(); + } + file = null; + } + if (thumbPath != null) { + if (fileDeletable) { + new File(thumbPath).delete(); + } + thumbPath = null; + } + for (Part part : parts) { + if (part.fileDeletable) { + part.file.delete(); + } + part.file = null; + } + } + cancelCheckStickers(); + } + + public static StoryEntry fromStoryItem(File file, TLRPC.StoryItem storyItem) { + StoryEntry entry = new StoryEntry(); + entry.isEdit = true; + entry.editStoryId = storyItem.id; + entry.file = file; + entry.fileDeletable = true; + entry.width = 720; + entry.height = 1280; + if (storyItem.media instanceof TLRPC.TL_messageMediaPhoto) { + entry.isVideo = false; + if (file != null) { + entry.decodeBounds(file.getAbsolutePath()); + } + } else if (storyItem.media instanceof TLRPC.TL_messageMediaDocument) { + entry.isVideo = true; + if (storyItem.media.document != null && storyItem.media.document.attributes != null) { + for (int i = 0; i < storyItem.media.document.attributes.size(); ++i) { + TLRPC.DocumentAttribute attr = storyItem.media.document.attributes.get(i); + if (attr instanceof TLRPC.TL_documentAttributeVideo) { + entry.width = attr.w; + entry.height = attr.h; + entry.fileDuration = attr.duration; + break; + } + } + } + if (storyItem.media.document != null) { + if (storyItem.firstFramePath != null) { + entry.thumbPath = storyItem.firstFramePath; + } else if (storyItem.media.document.thumbs != null) { + for (int i = 0; i < storyItem.media.document.thumbs.size(); ++i) { + TLRPC.PhotoSize photoSize = storyItem.media.document.thumbs.get(i); + if (photoSize instanceof TLRPC.TL_photoStrippedSize) { + continue; + } + File path = FileLoader.getInstance(entry.currentAccount).getPathToAttach(photoSize, true); + if (path != null && path.exists()) { + entry.thumbPath = path.getAbsolutePath(); + break; + } + } + } + } + } + entry.privacyRules.clear(); + entry.privacyRules.addAll(StoryPrivacyBottomSheet.StoryPrivacy.toInput(entry.currentAccount, storyItem.privacy)); + entry.period = storyItem.expire_date - storyItem.date; + try { + CharSequence caption = new SpannableString(storyItem.caption); + caption = Emoji.replaceEmoji(caption, Theme.chat_msgTextPaint.getFontMetricsInt(), true); + MessageObject.addEntitiesToText(caption, storyItem.entities, true, false, true, false); + caption = MessageObject.replaceAnimatedEmoji(caption, storyItem.entities, Theme.chat_msgTextPaint.getFontMetricsInt()); + entry.caption = caption; + } catch (Exception ignore) {} + entry.setupMatrix(); + entry.checkStickers(storyItem); + return entry; + } + + public static StoryEntry fromPhotoEntry(MediaController.PhotoEntry photoEntry) { + StoryEntry entry = new StoryEntry(); + entry.file = new File(photoEntry.path); + entry.orientation = photoEntry.orientation; + entry.invert = photoEntry.invert; + entry.isVideo = photoEntry.isVideo; + entry.thumbPath = photoEntry.thumbPath; + entry.duration = photoEntry.duration * 1000L; + entry.left = 0; + entry.right = Math.min(1, 59_500f / entry.duration); + if (entry.isVideo && entry.thumbPath == null) { + entry.thumbPath = "vthumb://" + photoEntry.imageId; + } + entry.gradientTopColor = photoEntry.gradientTopColor; + entry.gradientBottomColor = photoEntry.gradientBottomColor; + entry.decodeBounds(entry.file.getAbsolutePath()); + entry.setupMatrix(); + return entry; + } + + public static StoryEntry fromPhotoShoot(File file, int rotate) { + StoryEntry entry = new StoryEntry(); + entry.file = file; + entry.fileDeletable = true; + entry.orientation = rotate; + entry.invert = 0; + entry.isVideo = false; + if (file != null) { + entry.decodeBounds(file.getAbsolutePath()); + } + entry.setupMatrix(); + return entry; + } + + public void decodeBounds(String path) { + if (path != null) { + try { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeFile(path, options); + width = options.outWidth; + height = options.outHeight; + } catch (Exception ignore) {} + } + if (!isVideo) { + int side = (int) Math.max(width, height / 16f * 9f); +// if (side <= (480 + 720) / 2) { +// resultWidth = 480; +// resultHeight = 853; +// } else + if (side <= (720 + 1080) / 2) { + resultWidth = 720; + resultHeight = 1280; + } else { + resultWidth = 1080; + resultHeight = 1920; + } + } + } + + public void setupMatrix() { + setupMatrix(matrix, 0); + } + + public void setupMatrix(Matrix matrix, int rotate) { + matrix.reset(); + int width = this.width, height = this.height; + int or = orientation + rotate; + matrix.postScale(invert == 1 ? -1.0f : 1.0f, invert == 2 ? -1.0f : 1.0f, width / 2f, height / 2f); + if (or != 0) { + matrix.postTranslate(-width / 2f, -height / 2f); + matrix.postRotate(or); + if (or == 90 || or == 270) { + final int swap = height; + height = width; + width = swap; + } + matrix.postTranslate(width / 2f, height / 2f); + } + float scale = (float) resultWidth / width; + if ((float) height / (float) width > 1.29f) { + scale = Math.max(scale, (float) resultHeight / height); + } + matrix.postScale(scale, scale); + matrix.postTranslate((resultWidth - width * scale) / 2f, (resultHeight - height * scale) / 2f); + } + + public void setupGradient(Runnable done) { + if (isVideo && gradientTopColor == 0 && gradientBottomColor == 0) { + if (thumbPath != null) { + Bitmap bitmap = null; + try { + BitmapFactory.Options options = new BitmapFactory.Options(); + if (thumbPath.startsWith("vthumb://")) { + long id = Integer.parseInt(thumbPath.substring(9)); + options.inJustDecodeBounds = true; + MediaStore.Video.Thumbnails.getThumbnail(ApplicationLoader.applicationContext.getContentResolver(), id, MediaStore.Video.Thumbnails.MINI_KIND, options); + + options.inSampleSize = calculateInSampleSize(options, 240, 240); + options.inJustDecodeBounds = false; + options.inPreferredConfig = Bitmap.Config.RGB_565; + options.inDither = true; + bitmap = MediaStore.Video.Thumbnails.getThumbnail(ApplicationLoader.applicationContext.getContentResolver(), id, MediaStore.Video.Thumbnails.MINI_KIND, options); + } else { + options.inJustDecodeBounds = true; + BitmapFactory.decodeFile(thumbPath); + + options.inSampleSize = calculateInSampleSize(options, 240, 240); + options.inJustDecodeBounds = false; + options.inPreferredConfig = Bitmap.Config.RGB_565; + options.inDither = true; + bitmap = BitmapFactory.decodeFile(thumbPath); + } + } catch (Exception ignore) {} + if (bitmap != null) { + final Bitmap finalBitmap = bitmap; + DominantColors.getColors(true, finalBitmap, true, colors -> { + gradientTopColor = colors[0]; + gradientBottomColor = colors[1]; + finalBitmap.recycle(); + + if (done != null) { + done.run(); + } + }); + } + } + } + } + + public static StoryEntry fromVideoShoot(File file, String thumbPath, long duration) { + StoryEntry entry = new StoryEntry(); + entry.fromCamera = true; + entry.file = file; + entry.fileDeletable = true; + entry.orientation = 0; + entry.invert = 0; + entry.isVideo = true; + entry.duration = duration; + entry.thumbPath = thumbPath; + entry.left = 0; + entry.right = Math.min(1, 59_500f / entry.duration); + return entry; + } + + public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { + final int height = options.outHeight; + final int width = options.outWidth; + int inSampleSize = 1; + if (height > reqHeight || width > reqWidth) { + final int heightRatio = (int) Math.ceil((float) height / (float) reqHeight); + final int widthRatio = (int) Math.ceil((float) width / (float) reqWidth); + inSampleSize = Math.min(heightRatio, widthRatio); + } + return Math.max(1, (int) Math.pow(inSampleSize, Math.floor(Math.log(inSampleSize) / Math.log(2)))); + } + + public static void setupScale(BitmapFactory.Options options, int reqWidth, int reqHeight) { + Runtime runtime = Runtime.getRuntime(); + long maxMemory = runtime.maxMemory(); + long usedMemory = runtime.totalMemory() - runtime.freeMemory(); + long availableMemory = maxMemory - usedMemory; + final boolean enoughMemory = options.outWidth * options.outHeight * 4L * 2L <= availableMemory; + if (!enoughMemory || Math.max(options.outWidth, options.outHeight) > 4200 || SharedConfig.getDevicePerformanceClass() <= SharedConfig.PERFORMANCE_CLASS_LOW) { +// options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); + options.inScaled = true; + options.inDensity = options.outWidth; + options.inTargetDensity = reqWidth; + } + } + + public void getVideoEditedInfo(@NonNull Utilities.Callback whenDone) { + if (!wouldBeVideo()) { + whenDone.run(null); + return; + } + if (!isVideo && (resultWidth > 720 || resultHeight > 1280)) { + float s = 720f / resultWidth; + matrix.postScale(s, s, 0, 0); + resultWidth = 720; + resultHeight = 1280; + } + final String videoPath = file.getAbsolutePath(); + Utilities.globalQueue.postRunnable(() -> { + final int[] params = new int[AnimatedFileDrawable.PARAM_NUM_COUNT]; + AnimatedFileDrawable.getVideoInfo(videoPath, params); + AndroidUtilities.runOnUIThread(() -> { + VideoEditedInfo info = new VideoEditedInfo(); + + info.isStory = true; + info.fromCamera = fromCamera; + info.originalWidth = width; + info.originalHeight = height; + info.resultWidth = resultWidth; + info.resultHeight = resultHeight; + info.paintPath = paintFile == null ? null : paintFile.getPath(); + + final int encoderBitrate = MediaController.extractRealEncoderBitrate(info.resultWidth, info.resultHeight, info.bitrate, true); + if (isVideo) { + info.originalPath = videoPath; + info.isPhoto = false; + info.framerate = Math.min(59, params[AnimatedFileDrawable.PARAM_NUM_FRAMERATE]); + int videoBitrate = MediaController.getVideoBitrate(videoPath); + info.originalBitrate = videoBitrate == -1 ? params[AnimatedFileDrawable.PARAM_NUM_BITRATE] : videoBitrate; + if (info.originalBitrate < 1_000_000 && (mediaEntities != null && !mediaEntities.isEmpty())) { + info.bitrate = 2_000_000; + info.originalBitrate = -1; + } else if (info.originalBitrate < 500_000) { + info.bitrate = 2_500_000; + info.originalBitrate = -1; + } else { + info.bitrate = Utilities.clamp(info.originalBitrate, 3_000_000, 500_000); + } + FileLog.d("story bitrate, original = " + info.originalBitrate + " => " + info.bitrate); + info.originalDuration = (duration = params[AnimatedFileDrawable.PARAM_NUM_DURATION]) * 1000L; + info.startTime = (long) (left * duration) * 1000L; + info.endTime = (long) (right * duration) * 1000L; + info.estimatedDuration = info.endTime - info.startTime; + info.muted = muted; + info.estimatedSize = (long) (params[AnimatedFileDrawable.PARAM_NUM_AUDIO_FRAME_SIZE] + params[AnimatedFileDrawable.PARAM_NUM_DURATION] / 1000.0f * encoderBitrate / 8); + info.estimatedSize = Math.max(file.length(), info.estimatedSize); + info.filterState = filterState; + } else { + if (filterFile != null) { + info.originalPath = filterFile.getAbsolutePath(); + } else { + info.originalPath = videoPath; + } + info.isPhoto = true; + info.originalDuration = duration = averageDuration; + info.estimatedDuration = info.originalDuration; + info.startTime = -1; + info.endTime = -1; + info.muted = true; + info.originalBitrate = -1; + info.bitrate = -1; + info.framerate = 30; + info.estimatedSize = (long) (duration / 1000.0f * encoderBitrate / 8); + info.filterState = null; + } + info.avatarStartTime = -1; + + info.cropState = new MediaController.CropState(); + info.cropState.useMatrix = new Matrix(); + info.cropState.useMatrix.set(matrix); + + info.mediaEntities = mediaEntities; + + info.gradientTopColor = gradientTopColor; + info.gradientBottomColor = gradientBottomColor; + info.forceFragmenting = true; + + info.hdrInfo = hdrInfo; + info.parts = parts; + + whenDone.run(info); + }); + }); + } + + public static File makeCacheFile(final int account, boolean video) { + return makeCacheFile(account, video ? "mp4" : "jpg"); + } + + public static File makeCacheFile(final int account, String ext) { + TLRPC.TL_fileLocationToBeDeprecated location = new TLRPC.TL_fileLocationToBeDeprecated(); + location.volume_id = Integer.MIN_VALUE; + location.dc_id = Integer.MIN_VALUE; + location.local_id = SharedConfig.getLastLocalId(); + location.file_reference = new byte[0]; + + TLObject object; + if ("mp4".equals(ext)) { + TLRPC.VideoSize videoSize = new TLRPC.TL_videoSize_layer127(); + videoSize.location = location; + object = videoSize; + } else { + TLRPC.PhotoSize photoSize = new TLRPC.TL_photoSize_layer127(); + photoSize.location = location; + object = photoSize; + } + + return FileLoader.getInstance(account).getPathToAttach(object, ext, true); + } + + public static class HDRInfo { + + public int colorStandard; + public int colorRange; + public int colorTransfer; + + public float maxlum; + public float minlum; + + public int getHDRType() { + if (maxlum <= 0 && minlum <= 0) { + return 0; + } else if (colorStandard == MediaFormat.COLOR_STANDARD_BT2020) { + if (colorTransfer == MediaFormat.COLOR_TRANSFER_HLG) { + return 1; + } else if (colorTransfer == MediaFormat.COLOR_TRANSFER_ST2084) { + return 2; + } + } + return 0; + } + } + + public HDRInfo hdrInfo; + + public void detectHDR(Utilities.Callback whenDetected) { + if (whenDetected == null) { + return; + } + if (hdrInfo != null) { + whenDetected.run(hdrInfo); + return; + } + if (!isVideo || Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { + whenDetected.run(hdrInfo = new HDRInfo()); + return; + } + Utilities.globalQueue.postRunnable(() -> { + try { + HDRInfo hdrInfo; + if (this.hdrInfo == null) { + hdrInfo = this.hdrInfo = new HDRInfo(); + hdrInfo.maxlum = 1000f; + hdrInfo.minlum = 0.001f; + } else { + hdrInfo = this.hdrInfo; + } + MediaExtractor extractor = new MediaExtractor(); + extractor.setDataSource(file.getAbsolutePath()); + int videoIndex = MediaController.findTrack(extractor, false); + extractor.selectTrack(videoIndex); + MediaFormat videoFormat = extractor.getTrackFormat(videoIndex); + if (videoFormat.containsKey(MediaFormat.KEY_COLOR_TRANSFER)) { + hdrInfo.colorTransfer = videoFormat.getInteger(MediaFormat.KEY_COLOR_TRANSFER); + } + if (videoFormat.containsKey(MediaFormat.KEY_COLOR_STANDARD)) { + hdrInfo.colorStandard = videoFormat.getInteger(MediaFormat.KEY_COLOR_STANDARD); + } + if (videoFormat.containsKey(MediaFormat.KEY_COLOR_RANGE)) { + hdrInfo.colorRange = videoFormat.getInteger(MediaFormat.KEY_COLOR_RANGE); + } + } catch (Exception e) { + FileLog.e(e); + } finally { + this.hdrInfo = hdrInfo; + AndroidUtilities.runOnUIThread(() -> whenDetected.run(hdrInfo)); + } + }); + } + + public void checkStickers(TLRPC.StoryItem storyItem) { + if (storyItem == null || storyItem.media == null) { + return; + } + final TLRPC.TL_messages_getAttachedStickers req = new TLRPC.TL_messages_getAttachedStickers(); + if (storyItem.media.photo != null) { + TLRPC.Photo photo = (TLRPC.Photo) storyItem.media.photo; + if (!photo.has_stickers) { + return; + } + TLRPC.TL_inputStickeredMediaPhoto inputStickeredMediaPhoto = new TLRPC.TL_inputStickeredMediaPhoto(); + inputStickeredMediaPhoto.id = new TLRPC.TL_inputPhoto(); + inputStickeredMediaPhoto.id.id = photo.id; + inputStickeredMediaPhoto.id.access_hash = photo.access_hash; + inputStickeredMediaPhoto.id.file_reference = photo.file_reference; + if (inputStickeredMediaPhoto.id.file_reference == null) { + inputStickeredMediaPhoto.id.file_reference = new byte[0]; + } + req.media = inputStickeredMediaPhoto; + } else if (storyItem.media.document != null) { + TLRPC.Document document = (TLRPC.Document) storyItem.media.document; + if (!MessageObject.isDocumentHasAttachedStickers(document)) { + return; + } + TLRPC.TL_inputStickeredMediaDocument inputStickeredMediaDocument = new TLRPC.TL_inputStickeredMediaDocument(); + inputStickeredMediaDocument.id = new TLRPC.TL_inputDocument(); + inputStickeredMediaDocument.id.id = document.id; + inputStickeredMediaDocument.id.access_hash = document.access_hash; + inputStickeredMediaDocument.id.file_reference = document.file_reference; + if (inputStickeredMediaDocument.id.file_reference == null) { + inputStickeredMediaDocument.id.file_reference = new byte[0]; + } + req.media = inputStickeredMediaDocument; + } else { + return; + } + final RequestDelegate requestDelegate = (response, error) -> AndroidUtilities.runOnUIThread(() -> { + checkStickersReqId = 0; + if (response instanceof TLRPC.Vector) { + editStickers = new ArrayList<>(); + TLRPC.Vector vector = (TLRPC.Vector) response; + for (int i = 0; i < vector.objects.size(); ++i) { + TLRPC.StickerSetCovered setCovered = (TLRPC.StickerSetCovered) vector.objects.get(i); + TLRPC.Document document = setCovered.cover; + if (document == null && !setCovered.covers.isEmpty()) { + document = setCovered.covers.get(0); + } + if (document == null && setCovered instanceof TLRPC.TL_stickerSetFullCovered) { + TLRPC.TL_stickerSetFullCovered fullCovered = ((TLRPC.TL_stickerSetFullCovered) setCovered); + if (!fullCovered.documents.isEmpty()) { + document = fullCovered.documents.get(0); + } + } + if (document != null) { + TLRPC.InputDocument inputDocument = new TLRPC.TL_inputDocument(); + inputDocument.id = document.id; + inputDocument.access_hash = document.access_hash; + inputDocument.file_reference = document.file_reference; + editStickers.add(inputDocument); + } + } + } + }); + checkStickersReqId = ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> { + if (error != null && FileRefController.isFileRefError(error.text) && storyItem != null) { + FileRefController.getInstance(currentAccount).requestReference(storyItem, req, requestDelegate); + return; + } + requestDelegate.run(response, error); + }); + } + + private int checkStickersReqId = 0; + public void cancelCheckStickers() { + if (checkStickersReqId != 0) { + ConnectionsManager.getInstance(currentAccount).cancelRequest(checkStickersReqId, true); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/StoryPrivacyBottomSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/StoryPrivacyBottomSheet.java new file mode 100644 index 0000000000..18d1a99b2e --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/StoryPrivacyBottomSheet.java @@ -0,0 +1,3662 @@ +package org.telegram.ui.Stories.recorder; + +import static android.view.View.GONE; +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.AndroidUtilities.translitSafe; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.app.Activity; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.LinearGradient; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; +import android.graphics.Shader; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.text.Editable; +import android.text.InputType; +import android.text.SpannableString; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.SpannedString; +import android.text.TextPaint; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.text.style.DynamicDrawableSpan; +import android.text.style.ImageSpan; +import android.util.Log; +import android.util.TypedValue; +import android.view.GestureDetector; +import android.view.Gravity; +import android.view.HapticFeedbackConstants; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.view.animation.LinearInterpolator; +import android.view.inputmethod.EditorInfo; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ScrollView; +import android.widget.TextView; + +import androidx.annotation.Keep; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.collection.LongSparseArray; +import androidx.core.graphics.ColorUtils; +import androidx.recyclerview.widget.DefaultItemAnimator; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.LinearSmoothScrollerCustom; +import androidx.recyclerview.widget.RecyclerView; +import androidx.viewpager.widget.PagerAdapter; +import androidx.viewpager.widget.ViewPager; + +import com.google.zxing.common.detector.MathUtils; + +import org.checkerframework.checker.units.qual.A; +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.BotWebViewVibrationEffect; +import org.telegram.messenger.ChatObject; +import org.telegram.messenger.ContactsController; +import org.telegram.messenger.DialogObject; +import org.telegram.messenger.Emoji; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.MessagesStorage; +import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.R; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.UserObject; +import org.telegram.messenger.Utilities; +import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.TLObject; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.AdjustPanLayoutHelper; +import org.telegram.ui.ActionBar.AlertDialog; +import org.telegram.ui.ActionBar.BackDrawable; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.BottomSheet; +import org.telegram.ui.ActionBar.SimpleTextView; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Cells.GraySectionCell; +import org.telegram.ui.Cells.TextCell; +import org.telegram.ui.Cells.TextInfoPrivacyCell; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.AvatarDrawable; +import org.telegram.ui.Components.BackupImageView; +import org.telegram.ui.Components.BottomSheetWithRecyclerListView; +import org.telegram.ui.Components.Bulletin; +import org.telegram.ui.Components.BulletinFactory; +import org.telegram.ui.Components.CheckBox2; +import org.telegram.ui.Components.ColoredImageSpan; +import org.telegram.ui.Components.CombinedDrawable; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.EditTextBoldCursor; +import org.telegram.ui.Components.GroupCreateSpan; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.ListView.AdapterWithDiffUtils; +import org.telegram.ui.Components.RadioButton; +import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.Components.SizeNotifierFrameLayout; +import org.telegram.ui.Components.StickerEmptyView; +import org.telegram.ui.Components.TypefaceSpan; +import org.telegram.ui.Components.ViewPagerFixed; +import org.telegram.ui.UsersSelectActivity; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; + +public class StoryPrivacyBottomSheet extends BottomSheet implements NotificationCenter.NotificationCenterDelegate { + + private ViewPagerFixed viewPager; + + private static final int PAGE_TYPE_SHARE = 0; + private static final int PAGE_TYPE_CLOSE_FRIENDS = 1; + private static final int PAGE_TYPE_EXCLUDE_CONTACTS = 2; + private static final int PAGE_TYPE_SELECT_CONTACTS = 3; + private static final int PAGE_TYPE_SEND_AS_MESSAGE = 5; + + public static final int TYPE_CLOSE_FRIENDS = 1; + public static final int TYPE_CONTACTS = 2; + public static final int TYPE_SELECTED_CONTACTS = 3; + public static final int TYPE_EVERYONE = 4; + public static final int TYPE_AS_MESSAGE = 5; + + private final ArrayList excludedContacts = new ArrayList<>(); + private final ArrayList selectedContacts = new ArrayList<>(); + private final HashMap> selectedContactsByGroup = new HashMap<>(); + private int selectedContactsCount = 0; + + private boolean allowScreenshots = true; + private boolean keepOnMyPage = false; + + private HashSet mergeUsers(ArrayList users, HashMap> usersByGroup) { + HashSet set = new HashSet<>(); + if (users != null) { + set.addAll(users); + } + if (usersByGroup != null) { + for (ArrayList userIds : usersByGroup.values()) { + set.addAll(userIds); + } + } + return set; + } + + private final ArrayList messageUsers = new ArrayList<>(); + + private int activePage = PAGE_TYPE_CLOSE_FRIENDS; + private int selectedType = TYPE_EVERYONE; + private boolean startedFromSendAsMessage; + private boolean sendAsMessageEnabled = false; + + private HashMap smallChatsParticipantsCount = new HashMap<>(); + + private class Page extends FrameLayout implements View.OnClickListener, NotificationCenter.NotificationCenterDelegate { + public int pageType; + + private final ArrayList selectedUsers = new ArrayList<>(); + private final HashMap> selectedUsersByGroup = new HashMap<>(); + + private final FrameLayout contentView; + private RecyclerListView listView; + private LinearLayoutManager layoutManager; + private Adapter adapter; + + private final ButtonContainer buttonContainer; + private final View underKeyboardView; + private final ButtonWithCounterView button; + private final ButtonWithCounterView button2; + + private SearchUsersCell searchField; + private GraySectionCell sectionCell; + private HeaderCell headerView; + + private int searchPosition = -1; + + private boolean containsHeader; + + public Page(Context context, int pageType) { + this(context); + bind(pageType); + } + + public Page(Context context) { + super(context); + + sectionCell = new GraySectionCell(context, resourcesProvider); + + searchField = new SearchUsersCell(context, resourcesProvider, () -> { + adapter.notifyItemChanged(2); + this.listView.forceLayout(); + updateTops(); + }) { + @Override + public void setContainerHeight(float value) { + super.setContainerHeight(value); + sectionCell.setTranslationY(getY() - (contentView == null ? 0 : contentView.getPaddingTop()) + Math.min(dp(150), containerHeight) - 1); + if (contentView != null) { + contentView.invalidate(); + } + } + + @Override + public void setTranslationY(float translationY) { + super.setTranslationY(translationY); + sectionCell.setTranslationY(getY() - (contentView == null ? 0 : contentView.getPaddingTop()) + Math.min(dp(150), containerHeight) - 1); + if (contentView != null) { + contentView.invalidate(); + } + } + }; + searchField.setBackgroundColor(getThemedColor(Theme.key_dialogBackground)); + searchField.setOnSearchTextChange(this::onSearch); + + headerView = new HeaderCell(context, resourcesProvider); + headerView.setOnCloseClickListener(() -> { + if (pageType == PAGE_TYPE_SHARE) { + dismiss(); + } else { + onBackPressed(); + } + }); + + contentView = new FrameLayout(context); + contentView.setPadding(0, AndroidUtilities.statusBarHeight + AndroidUtilities.dp(56), 0, 0); + contentView.setClipToPadding(true); + addView(contentView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); + + listView = new RecyclerListView(context, resourcesProvider) { +// private long tapTime; +// private float tapX, tapY; +// @Override +// public boolean dispatchTouchEvent(MotionEvent ev) { +// if (ev.getAction() == MotionEvent.ACTION_DOWN) { +// tapTime = System.currentTimeMillis(); +// tapX = ev.getX(); +// tapY = ev.getY(); +// } else if (ev.getAction() == MotionEvent.ACTION_MOVE) { +// if (MathUtils.distance(tapX, tapY, ev.getX(), ev.getY()) >= AndroidUtilities.touchSlop) { +// tapTime = -1; +// if (searchField.currentDeletingSpan != null) { +// searchField.currentDeletingSpan.cancelDeleteAnimation(); +// searchField.currentDeletingSpan = null; +// } +// } +// } else if (ev.getAction() == MotionEvent.ACTION_UP) { +// if (MathUtils.distance(tapX, tapY, ev.getX(), ev.getY()) <= AndroidUtilities.touchSlop && System.currentTimeMillis() - tapTime <= ViewConfiguration.getTapTimeout() && searchField != null && searchField.spansContainer != null) { +// float x = ev.getX(), y = contentView.getPaddingTop() + ev.getY(); +// x -= searchField.getX(); +// y -= searchField.getY(); +// if (x < 0 || y < 0 || x > searchField.getWidth() || y > searchField.getHeight()) { +// return super.dispatchTouchEvent(ev); +// } +// for (int i = 0; i < searchField.spansContainer.getChildCount(); ++i) { +// View child = searchField.spansContainer.getChildAt(i); +// if (x < child.getX() || y < child.getY() || x >= child.getX() + child.getWidth() || y >= child.getY() + child.getHeight() || !(child instanceof GroupCreateSpan)) { +// continue; +// } +// GroupCreateSpan span = (GroupCreateSpan) child; +// onClick(span); +// break; +// } +// tapTime = -1; +// return true; +// } +// tapTime = -1; +// } else if (ev.getAction() == MotionEvent.ACTION_CANCEL) { +// tapTime = -1; +// if (searchField.currentDeletingSpan != null) { +// searchField.currentDeletingSpan.cancelDeleteAnimation(); +// searchField.currentDeletingSpan = null; +// } +// } +// return super.dispatchTouchEvent(ev); +// } +// +// @Override +// public boolean onTouchEvent(MotionEvent e) { +// return super.onTouchEvent(e); +// } + }; + listView.setClipToPadding(false); + listView.setTranslateSelector(true); + listView.setAdapter(adapter = new Adapter(context, resourcesProvider, searchField, StoryPrivacyBottomSheet.this::onBackPressed)); + adapter.listView = listView; + listView.setLayoutManager(layoutManager = new LinearLayoutManager(context)); + listView.setOnScrollListener(new RecyclerView.OnScrollListener() { + private boolean canScrollDown; + @Override + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { + boolean canScrollDown = listView.canScrollVertically(1); + if (canScrollDown != this.canScrollDown) { + buttonContainer.invalidate(); + this.canScrollDown = canScrollDown; + } + contentView.invalidate(); + containerView.invalidate(); + } + + @Override + public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { + if (newState == RecyclerView.SCROLL_STATE_DRAGGING && keyboardVisible && searchField != null) { + closeKeyboard(); + } + scrolling = newState != RecyclerView.SCROLL_STATE_IDLE; + } + }); + listView.setOnItemClickListener((view, position, x, y) -> { + if (position < 0 || position >= items.size()) { + return; + } + ItemInner item = items.get(position); + if (item.viewType == VIEW_TYPE_USER) { +// boolean subtitle = LocaleController.isRTL ? x < view.getWidth() - AndroidUtilities.dp(100) : x > AndroidUtilities.dp(100); + if (item.type == TYPE_CLOSE_FRIENDS) { + if (selectedType == TYPE_CLOSE_FRIENDS || getCloseFriends().isEmpty()) { + activePage = PAGE_TYPE_CLOSE_FRIENDS; + viewPager.scrollToPosition(1); + } + selectedType = TYPE_CLOSE_FRIENDS; + updateCheckboxes(true); + return; + } else if (item.type == TYPE_SELECTED_CONTACTS) { + if (selectedType == TYPE_SELECTED_CONTACTS || selectedContacts.isEmpty() && selectedContactsByGroup.isEmpty()) { + activePage = PAGE_TYPE_SELECT_CONTACTS; + viewPager.scrollToPosition(1); + } + selectedType = TYPE_SELECTED_CONTACTS; + updateCheckboxes(true); + return; + } else if (item.type == TYPE_CONTACTS) { + if (selectedType == TYPE_CONTACTS) { + activePage = PAGE_TYPE_EXCLUDE_CONTACTS; + viewPager.scrollToPosition(1); + } + selectedType = TYPE_CONTACTS; + updateCheckboxes(true); + return; + } + if (item.type > 0) { + selectedUsers.clear(); + selectedUsersByGroup.clear(); + selectedType = item.type; + searchField.spansContainer.removeAllSpans(true); + } else if (item.chat != null) { + final long id = item.chat.id; + if (getParticipantsCount(item.chat) > 200) { +// AndroidUtilities.shakeViewSpring(view, shiftDp = -shiftDp); +// BotWebViewVibrationEffect.APP_ERROR.vibrate(); + try { + performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); + } catch (Throwable ignore) {} + new AlertDialog.Builder(getContext(), resourcesProvider) + .setTitle(LocaleController.getString("GroupTooLarge", R.string.GroupTooLarge)) + .setMessage(LocaleController.getString("GroupTooLargeMessage", R.string.GroupTooLargeMessage)) + .setPositiveButton(LocaleController.getString("OK", R.string.OK), null) + .show(); + } else if (selectedUsersByGroup.containsKey(id)) { + selectedUsersByGroup.remove(id); + updateSpans(true); + } else { + TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(id); + TLRPC.ChatFull chatFull = MessagesController.getInstance(currentAccount).getChatFull(id); + if (chatFull != null && chatFull.participants != null && !chatFull.participants.participants.isEmpty()) { + selectChat(id, chatFull.participants); + } else { + if (progressDialog != null) { + progressDialog.dismiss(); + progressDialog = null; + } + waitingForChatId = id; + progressDialog = new AlertDialog(getContext(), AlertDialog.ALERT_TYPE_SPINNER, resourcesProvider); + progressDialog.showDelayed(50); + MessagesStorage storage = MessagesStorage.getInstance(currentAccount); + storage.getStorageQueue().postRunnable(() -> { + boolean isChannel = ChatObject.isChannel(chat); + TLRPC.ChatFull info = storage.loadChatInfoInQueue(id, isChannel, true, true, 0); + if (info == null || info.participants == null) { + AndroidUtilities.runOnUIThread(() -> { + if (isChannel) { + MessagesController.getInstance(currentAccount).loadChannelParticipants(id, participants -> { + if (progressDialog != null) { + progressDialog.dismissUnless(350); + progressDialog = null; + } + if (participants == null || participants.participants.isEmpty()) { + return; + } + TLRPC.ChatParticipants participantsObj = new TLRPC.TL_chatParticipants(); + for (int i = 0; i < participants.participants.size(); ++i) { + TLRPC.ChannelParticipant participant = participants.participants.get(i); + TLRPC.TL_chatParticipant chatParticipant = new TLRPC.TL_chatParticipant(); + long userId; + if (participant.peer != null) { + long did = DialogObject.getPeerDialogId(participant.peer); + if (did < 0) { + continue; + } + userId = did; + } else { + userId = participant.user_id; + } + chatParticipant.user_id = userId; + participantsObj.participants.add(chatParticipant); + } + selectChat(id, participantsObj); + }); + } else { + MessagesController.getInstance(currentAccount).loadFullChat(id, 0, true); + } + }); + } else { + AndroidUtilities.runOnUIThread(() -> selectChat(id, info.participants)); + } + }); + } + if (!TextUtils.isEmpty(query)) { + searchField.setText(""); + query = null; + updateItems(false); + } + } + } else if (item.user != null) { + if (pageType == PAGE_TYPE_SHARE) { + selectedType = 0; + } + final long id = item.user.id; + HashSet userIds = new HashSet<>(selectedUsers); + if (selectedUsers.contains(id)) { + Iterator>> iterator = selectedUsersByGroup.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry> entry = iterator.next(); + if (entry.getValue().contains(id)) { + iterator.remove(); + userIds.addAll(entry.getValue()); + } + } + userIds.remove(id); + } else { + Iterator>> iterator = selectedUsersByGroup.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry> entry = iterator.next(); + if (entry.getValue().contains(id)) { + iterator.remove(); + userIds.addAll(entry.getValue()); + } + } + userIds.add(id); + if (!TextUtils.isEmpty(query)) { + searchField.setText(""); + query = null; + updateItems(false); + } + } + selectedUsers.clear(); + selectedUsers.addAll(userIds); + updateSpans(true); + } + updateButton(true); + updateCheckboxes(true); + searchField.scrollToBottom(); + } else if (item.viewType == VIEW_TYPE_CHECK) { + if (!(view instanceof TextCell)) { + return; + } + TextCell cell = (TextCell) view; + cell.setChecked(!cell.isChecked()); + item.checked = cell.isChecked(); + if (item.resId == 0) { + allowScreenshots = cell.isChecked(); + boolean allowShare = selectedType == TYPE_EVERYONE; + if (allowScreenshots) { + BulletinFactory.of(container, resourcesProvider) + .createSimpleBulletin(R.raw.ic_save_to_gallery, LocaleController.getString(allowShare ? R.string.StoryEnabledScreenshotsShare : R.string.StoryEnabledScreenshots), 4) + .setDuration(5000) + .show(true); + } else { + BulletinFactory.of(container, resourcesProvider) + .createSimpleBulletin(R.raw.passcode_lock_close, LocaleController.getString(allowShare ? R.string.StoryDisabledScreenshotsShare : R.string.StoryDisabledScreenshots), 4) + .setDuration(5000) + .show(true); + } + } else { + keepOnMyPage = cell.isChecked(); + if (keepOnMyPage) { + BulletinFactory.of(container, resourcesProvider) + .createSimpleBulletin(R.raw.msg_story_keep, LocaleController.getString(R.string.StoryEnableKeep), 4) + .setDuration(5000) + .show(true); + } else { + BulletinFactory.of(container, resourcesProvider) + .createSimpleBulletin(R.raw.fire_on, LocaleController.getString(R.string.StoryDisableKeep), 4) + .setDuration(5000) + .show(true); + } + } + } + }); + contentView.addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + + DefaultItemAnimator itemAnimator = new DefaultItemAnimator() { + @Override + protected void onMoveAnimationUpdate(RecyclerView.ViewHolder holder) { + containerView.invalidate(); + contentView.invalidate(); + listView.invalidate(); + } + + @Override + public boolean canReuseUpdatedViewHolder(RecyclerView.ViewHolder viewHolder) { + return true; + } + }; + itemAnimator.setDurations(350); + itemAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + itemAnimator.setDelayAnimations(false); + itemAnimator.setSupportsChangeAnimations(false); + listView.setItemAnimator(itemAnimator); + + contentView.addView(searchField, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.FILL_HORIZONTAL)); + contentView.addView(sectionCell, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 32, Gravity.TOP | Gravity.FILL_HORIZONTAL)); + addView(headerView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.FILL_HORIZONTAL)); + + buttonContainer = new ButtonContainer(context); + buttonContainer.setClickable(true); + buttonContainer.setOrientation(LinearLayout.VERTICAL); + buttonContainer.setPadding(dp(10) + backgroundPaddingLeft, dp(10), dp(10) + backgroundPaddingLeft, dp(10)); + buttonContainer.setBackgroundColor(Theme.getColor(Theme.key_dialogBackground, resourcesProvider)); + + button = new ButtonWithCounterView(context, resourcesProvider); + button.setOnClickListener(this::onButton1Click); + buttonContainer.addView(button, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48, Gravity.BOTTOM | Gravity.FILL_HORIZONTAL)); + + button2 = new ButtonWithCounterView(context, false, resourcesProvider); + button2.setOnClickListener(this::onButton2Click); + buttonContainer.addView(button2, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48, Gravity.BOTTOM | Gravity.FILL_HORIZONTAL, 0, 8, 0, 0)); + + underKeyboardView = new View(context); + underKeyboardView.setBackgroundColor(Theme.getColor(Theme.key_dialogBackground, resourcesProvider)); + addView(underKeyboardView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 500, Gravity.BOTTOM | Gravity.FILL_HORIZONTAL, 0, 0, 0, -500)); + + addView(buttonContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM | Gravity.FILL_HORIZONTAL)); + } + + private class ButtonContainer extends LinearLayout { + public ButtonContainer(Context context) { + super(context); + } + + private float translationY, translationY2; + private ValueAnimator hideAnimator; + public void hide(boolean hide, boolean animated) { + if (hideAnimator != null) { + hideAnimator.cancel(); + } + if (animated) { + setVisibility(View.VISIBLE); + hideAnimator = ValueAnimator.ofFloat(translationY2, hide ? getMeasuredHeight() : 0); + hideAnimator.addUpdateListener(anm -> { + translationY2 = (float) anm.getAnimatedValue(); + super.setTranslationY(translationY2 + translationY); + }); + hideAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (hide) { + setVisibility(View.GONE); + } + hideAnimator = null; + } + }); + hideAnimator.setDuration(320); + hideAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + hideAnimator.start(); + } else { + setVisibility(hide ? View.GONE : View.VISIBLE); + translationY2 = hide ? getMeasuredHeight() : 0; + super.setTranslationY(translationY2 + translationY); + } + } + + private ValueAnimator animator; + public void translateY(float from, float to) { + if (animator != null) { + animator.cancel(); + animator = null; + } + animator = ValueAnimator.ofFloat(from, to); + animator.addUpdateListener(anm -> { + setTranslationY((float) anm.getAnimatedValue()); + }); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + setTranslationY(to); + animator = null; + } + }); + animator.setDuration(AdjustPanLayoutHelper.keyboardDuration); + animator.setInterpolator(AdjustPanLayoutHelper.keyboardInterpolator); + animator.start(); + } + + @Override + public void setTranslationY(float translationY) { + super.setTranslationY(translationY2 + (this.translationY = translationY)); + } + +// @Override +// public float getTranslationY() { +// return translationY; +// } + + final Paint dividerPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + final AnimatedFloat alpha = new AnimatedFloat(this); + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + dividerPaint.setColor(Theme.getColor(Theme.key_windowBackgroundGray, resourcesProvider)); + dividerPaint.setAlpha((int) (0xFF * alpha.set(listView.canScrollVertically(1) ? 1 : 0))); + canvas.drawRect(0, 0, getWidth(), 1, dividerPaint); + } + } + + private AlertDialog progressDialog; + private long waitingForChatId; + + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.chatInfoDidLoad) { + TLRPC.ChatFull chatFull = (TLRPC.ChatFull) args[0]; + if (chatFull == null || progressDialog == null || waitingForChatId != chatFull.id) { + return; + } + progressDialog.dismissUnless(350); + progressDialog = null; + waitingForChatId = -1; + selectChat(chatFull.id, chatFull.participants); + } + } + + private void selectChat(long id, TLRPC.ChatParticipants participants) { + ArrayList groupUsers = new ArrayList<>(); + ArrayList nonContactsUsers = new ArrayList<>(); + final boolean mustBeContacts = pageType == PAGE_TYPE_CLOSE_FRIENDS || pageType == PAGE_TYPE_EXCLUDE_CONTACTS; + if (participants != null && participants.participants != null) { + for (int i = 0; i < participants.participants.size(); ++i) { + long userId = participants.participants.get(i).user_id; + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(userId); + if (user != null && !UserObject.isUserSelf(user) && !user.bot && user.id != 777000 && userId != 0) { + if (mustBeContacts && !user.contact) { + nonContactsUsers.add(userId); + } else { + groupUsers.add(userId); + } + selectedUsers.remove(userId); + } + } + } + if (!nonContactsUsers.isEmpty()) { + if (groupUsers.isEmpty()) { + new AlertDialog.Builder(getContext(), resourcesProvider) + .setMessage("All group members are not in your contact list.") + .setNegativeButton("Cancel", null) + .show(); + } else { + new AlertDialog.Builder(getContext(), resourcesProvider) + .setMessage(nonContactsUsers.size() + " members are not in your contact list") + .setPositiveButton("Add " + groupUsers.size() + " contacts", (di, a) -> { + selectedUsersByGroup.put(id, groupUsers); + updateSpans(true); + updateButton(true); + updateCheckboxes(true); + di.dismiss(); + searchField.scrollToBottom(); + }) + .setNegativeButton("Cancel", null) + .show(); + } + } else { + selectedUsersByGroup.put(id, groupUsers); + updateSpans(true); + updateButton(true); + updateCheckboxes(true); + searchField.scrollToBottom(); + } + } + + private void updateSpans(boolean animated) { + HashSet userIds = mergeUsers(selectedUsers, selectedUsersByGroup); + if (pageType == PAGE_TYPE_SELECT_CONTACTS) { + selectedContactsCount = userIds.size(); + } + + final MessagesController messagesController = MessagesController.getInstance(currentAccount); + final ArrayList toDelete = new ArrayList<>(); + final ArrayList toAdd = new ArrayList<>(); + + // remove spans + for (int i = 0; i < searchField.allSpans.size(); ++i) { + GroupCreateSpan span = searchField.allSpans.get(i); + if (!userIds.contains(span.getUid())) { + toDelete.add(span); + } + } + + for (long id : userIds) { + boolean found = false; + for (int j = 0; j < searchField.allSpans.size(); ++j) { + GroupCreateSpan span = searchField.allSpans.get(j); + if (span.getUid() == id) { + found = true; + break; + } + } + + if (!found) { + TLObject obj = null; + if (id >= 0) { + obj = messagesController.getUser(id); + } else { + obj = messagesController.getChat(id); + } + if (obj == null) { + continue; + } + GroupCreateSpan span = new GroupCreateSpan(getContext(), obj, null, true, resourcesProvider); + span.setOnClickListener(this); + toAdd.add(span); + } + } + + if (!toDelete.isEmpty() || !toAdd.isEmpty()) { + searchField.spansContainer.updateSpans(toDelete, toAdd, animated); + } + } + + private void onButton1Click(View v) { + if (button.isLoading()) { + return; + } + final MessagesController messagesController = MessagesController.getInstance(currentAccount); + if (pageType == PAGE_TYPE_SEND_AS_MESSAGE) { + if (onDone2 != null) { + onDone2.run(selectedUsers); + } + dismiss(); + } else if (pageType == PAGE_TYPE_CLOSE_FRIENDS) { + TLRPC.TL_editCloseFriends req = new TLRPC.TL_editCloseFriends(); + req.id.addAll(selectedUsers); + button.setLoading(true); + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + button.setLoading(false); + if (res != null) { + ArrayList users = getContacts(); + for (int i = 0; i < users.size(); ++i) { + TLRPC.User u = (TLRPC.User) users.get(i); + if (u == null) { + continue; + } + boolean shouldBeCloseFriend = selectedUsers.contains(u.id); + if (shouldBeCloseFriend != u.close_friend) { + u.flags2 = (u.close_friend = shouldBeCloseFriend) ? (u.flags2 | 4) : (u.flags2 &~ 4); + messagesController.putUser(u, false); + } + } + } + closeKeyboard(); + if (isEdit) { + done(new StoryPrivacy(TYPE_CLOSE_FRIENDS, currentAccount, null), StoryPrivacyBottomSheet.this::dismiss); + } else { + closeKeyboard(); + viewPager.scrollToPosition(0); + } + })); + } else if (pageType == PAGE_TYPE_SHARE) { + if (!applyWhenDismiss) { + StoryPrivacy privacy; + if (selectedType == TYPE_SELECTED_CONTACTS) { + HashSet users = mergeUsers(selectedContacts, selectedContactsByGroup); + privacy = new StoryPrivacy(selectedType, currentAccount, new ArrayList<>(users)); + privacy.selectedUserIds.clear(); + privacy.selectedUserIds.addAll(selectedContacts); + privacy.selectedUserIdsByGroup.clear(); + privacy.selectedUserIdsByGroup.putAll(selectedContactsByGroup); + } else if (selectedType == TYPE_CONTACTS) { + privacy = new StoryPrivacy(selectedType, currentAccount, excludedContacts); + } else { + privacy = new StoryPrivacy(selectedType, currentAccount, null); + } + done(privacy, StoryPrivacyBottomSheet.this::dismiss); + } else { + dismiss(); + } + } else if (pageType == PAGE_TYPE_EXCLUDE_CONTACTS) { + if (isEdit) { + closeKeyboard(); + done(new StoryPrivacy(TYPE_CONTACTS, currentAccount, selectedUsers), StoryPrivacyBottomSheet.this::dismiss); + } else { + closeKeyboard(); + viewPager.scrollToPosition(0); + } + } else if (pageType == PAGE_TYPE_SELECT_CONTACTS) { + if (isEdit) { + HashSet users = mergeUsers(selectedUsers, selectedUsersByGroup); + if (users.isEmpty()) { + return; + } + closeKeyboard(); + StoryPrivacy privacy = new StoryPrivacy(TYPE_SELECTED_CONTACTS, currentAccount, new ArrayList<>(users)); + privacy.selectedUserIds.clear(); + privacy.selectedUserIds.addAll(selectedUsers); + privacy.selectedUserIdsByGroup.clear(); + privacy.selectedUserIdsByGroup.putAll(selectedUsersByGroup); + done(privacy, () -> { + Bulletin.removeDelegate(container); + StoryPrivacyBottomSheet.super.dismiss(); + }); + } else { + HashSet users = mergeUsers(selectedUsers, selectedUsersByGroup); + if (users.isEmpty()) { + return; + } + selectedType = PAGE_TYPE_SELECT_CONTACTS; + closeKeyboard(); + viewPager.scrollToPosition(0); + } + } else { + selectedType = pageType; + closeKeyboard(); + viewPager.scrollToPosition(0); + } + } + + private void onButton2Click(View v) { + if (startedFromSendAsMessage) { + activePage = PAGE_TYPE_SEND_AS_MESSAGE; + viewPager.scrollToPosition(1); + } else { + StoryPrivacyBottomSheet sheet = new StoryPrivacyBottomSheet(PAGE_TYPE_SEND_AS_MESSAGE, getContext(), resourcesProvider) + .whenSelectedShare(users -> { + done(new StoryPrivacy(TYPE_AS_MESSAGE, currentAccount, users), StoryPrivacyBottomSheet.this::dismiss); + }); + sheet.storyPeriod = storyPeriod; + sheet.show(); + } + } + + public float top() { + float top = layoutManager.getReverseLayout() ? AndroidUtilities.displaySize.y : 0; + for (int i = 0; i < listView.getChildCount(); ++i) { + View child = listView.getChildAt(i); + if (layoutManager.getReverseLayout()) { + top = Math.min(top, contentView.getPaddingTop() + child.getY()); + } else if (child.getTag() instanceof Integer && (int) child.getTag() == 33) { + return contentView.getPaddingTop() + child.getBottom() + child.getTranslationY(); + } else if (child.getTag() instanceof Integer && (int) child.getTag() == 35) { + return contentView.getPaddingTop() + child.getY(); + } + } + return top; + } + + public void bind(int pageType) { + this.pageType = pageType; + selectedUsers.clear(); + selectedUsersByGroup.clear(); + if (pageType == PAGE_TYPE_SEND_AS_MESSAGE) { + selectedUsers.addAll(messageUsers); + } else if (pageType == PAGE_TYPE_CLOSE_FRIENDS) { + ArrayList closeFriends = getCloseFriends(); + for (int i = 0; i < closeFriends.size(); ++i) { + selectedUsers.add(((TLRPC.User) closeFriends.get(i)).id); + } + } else if (pageType == PAGE_TYPE_EXCLUDE_CONTACTS) { + selectedUsers.addAll(excludedContacts); + } else if (pageType == PAGE_TYPE_SELECT_CONTACTS) { + selectedUsers.addAll(selectedContacts); + selectedUsersByGroup.putAll(selectedContactsByGroup); + } + layoutManager.setReverseLayout(adapter.reversedLayout = pageType == PAGE_TYPE_SHARE); + updateSpans(false); + searchField.setText(""); + searchField.setVisibility(pageType == PAGE_TYPE_SHARE ? View.GONE : View.VISIBLE); + searchField.scrollToBottom(); + query = null; + updateItems(false); + updateButton(false); + updateCheckboxes(false); + scrollToTop(); + listView.requestLayout(); + lastSelectedType = -1; + } + + private String query; + + private ArrayList atTop = new ArrayList<>(); + private ArrayList oldItems = new ArrayList<>(); + private ArrayList items = new ArrayList<>(); + public void updateItems(boolean animated) { + updateItems(animated, true); + } + public void updateItems(boolean animated, boolean notify) { + oldItems.clear(); + oldItems.addAll(items); + + items.clear(); + + float h = 0; + if (pageType == PAGE_TYPE_SHARE) { + containsHeader = false; + sectionCell.setVisibility(View.GONE); +// items.add(ItemInner.asPad(dp(84) + 4 * dp(56) + (sendAsMessageEnabled ? dp(120) : dp(64)))); + items.add(ItemInner.asHeader2( + isEdit ? + LocaleController.getString("StoryPrivacyAlertEditTitle", R.string.StoryPrivacyAlertEditTitle) : + LocaleController.getString("StoryPrivacyAlertTitle", R.string.StoryPrivacyAlertTitle), + storyPeriod != Integer.MAX_VALUE ? + LocaleController.formatPluralString("StoryPrivacyAlertSubtitle", storyPeriod / 3600) : + LocaleController.getString("StoryPrivacyAlertSubtitleProfile", R.string.StoryPrivacyAlertSubtitleProfile) + )); + items.add(ItemInner.asType(TYPE_EVERYONE, selectedType == TYPE_EVERYONE)); + ItemInner item; + items.add(item = ItemInner.asType(TYPE_CONTACTS, selectedType == TYPE_CONTACTS, excludedContacts.size())); + if (excludedContacts.size() == 1) { + item.user = MessagesController.getInstance(currentAccount).getUser(excludedContacts.get(0)); + } + ArrayList closeFriends = getCloseFriends(); + items.add(item = ItemInner.asType(TYPE_CLOSE_FRIENDS, selectedType == TYPE_CLOSE_FRIENDS, closeFriends.size())); + if (closeFriends.size() == 1 && closeFriends.get(0) instanceof TLRPC.User) { + item.user = (TLRPC.User) closeFriends.get(0); + } + items.add(item = ItemInner.asType(TYPE_SELECTED_CONTACTS, selectedType == TYPE_SELECTED_CONTACTS, selectedContactsCount)); + if (selectedContactsCount == 1) { + if (selectedContacts.size() == 1) { + item.user = MessagesController.getInstance(currentAccount).getUser(selectedContacts.get(0)); + } else { + for (ArrayList userIds : selectedContactsByGroup.values()) { + if (userIds.size() >= 1) { + item.user = MessagesController.getInstance(currentAccount).getUser(userIds.get(0)); + break; + } + } + } + } + if (!isEdit) { + items.add(ItemInner.asShadow(null)); + items.add(ItemInner.asCheck(LocaleController.getString(R.string.StoryAllowScreenshots), 0, allowScreenshots)); + items.add(ItemInner.asCheck(LocaleController.getString(R.string.StoryKeep), 1, keepOnMyPage)); + items.add(ItemInner.asShadow(LocaleController.formatPluralString("StoryKeepInfo", storyPeriod / 3600))); + } + } else if (pageType == PAGE_TYPE_CLOSE_FRIENDS) { + headerView.setText(LocaleController.getString("StoryPrivacyAlertCloseFriendsTitle", R.string.StoryPrivacyAlertCloseFriendsTitle)); + headerView.setCloseImageVisible(true); + headerView.backDrawable.setRotation(0f, false); + items.add(ItemInner.asPad()); + items.add(ItemInner.asHeader()); + h += dp(56); + searchPosition = items.size(); + items.add(ItemInner.asSearchField()); + h += dp(150); + items.add(ItemInner.asSection()); + h += dp(32); + sectionCell.setText(LocaleController.getString("StoryPrivacyAlertCloseFriendsSubtitle", R.string.StoryPrivacyAlertCloseFriendsSubtitle)); + updateSectionCell(animated); + containsHeader = true; + } else if (pageType == PAGE_TYPE_EXCLUDE_CONTACTS) { + headerView.setText(LocaleController.getString("StoryPrivacyAlertExcludedContactsTitle", R.string.StoryPrivacyAlertExcludedContactsTitle)); + headerView.setCloseImageVisible(true); + headerView.backDrawable.setRotation(0f, false); + items.add(ItemInner.asPad()); + items.add(ItemInner.asHeader()); + h += dp(56); + searchPosition = items.size(); + items.add(ItemInner.asSearchField()); + h += dp(150); + items.add(ItemInner.asSection()); + h += dp(32); + sectionCell.setText(LocaleController.getString("StoryPrivacyAlertExcludedContactsSubtitle", R.string.StoryPrivacyAlertExcludedContactsSubtitle)); + updateSectionCell(animated); + containsHeader = true; + } else if (pageType == PAGE_TYPE_SELECT_CONTACTS) { + headerView.setText(LocaleController.getString("StoryPrivacyAlertSelectContactsTitle", R.string.StoryPrivacyAlertSelectContactsTitle)); + headerView.setCloseImageVisible(true); + headerView.backDrawable.setRotation(0f, false); + items.add(ItemInner.asPad()); + items.add(ItemInner.asHeader()); + h += dp(56); + searchPosition = items.size(); + items.add(ItemInner.asSearchField()); + h += dp(150); + items.add(ItemInner.asSection()); + h += dp(32); + sectionCell.setText(LocaleController.getString("StoryPrivacyAlertSelectContactsSubtitle", R.string.StoryPrivacyAlertSelectContactsSubtitle)); + updateSectionCell(animated); + containsHeader = true; + } else if (pageType == PAGE_TYPE_SEND_AS_MESSAGE) { + headerView.setText(LocaleController.getString("StoryPrivacyAlertAsMessageTitle", R.string.StoryPrivacyAlertAsMessageTitle)); + headerView.setCloseImageVisible(startedFromSendAsMessage); + headerView.backDrawable.setRotation(0f, false); + items.add(ItemInner.asPad()); + items.add(ItemInner.asHeader()); + h += dp(56); + searchPosition = items.size(); + items.add(ItemInner.asSearchField()); + h += dp(150); + items.add(ItemInner.asSection()); + h += dp(32); + sectionCell.setText(LocaleController.getString("StoryPrivacyAlertAsMessageSubtitle", R.string.StoryPrivacyAlertAsMessageSubtitle)); + updateSectionCell(animated); + containsHeader = true; + } + + boolean searching = !TextUtils.isEmpty(query); + if (pageType != PAGE_TYPE_SHARE) { + int count = 0; + String q = translitSafe(query).toLowerCase(); + ArrayList allOtherUsers; + if (pageType == PAGE_TYPE_SEND_AS_MESSAGE) { + allOtherUsers = getChats(); + } else { + allOtherUsers = getUsers(pageType == PAGE_TYPE_CLOSE_FRIENDS || pageType == PAGE_TYPE_EXCLUDE_CONTACTS, allowSmallChats && pageType == PAGE_TYPE_SELECT_CONTACTS); + } + if (!searching) { + if (!animated) { + atTop.clear(); + for (int i = 0; i < allOtherUsers.size(); ++i) { + TLObject object = allOtherUsers.get(i); + boolean selected = false; + if (object instanceof TLRPC.User) { + TLRPC.User user = (TLRPC.User) object; + selected = selectedUsers.contains(user.id); + } else if (object instanceof TLRPC.Chat) { + TLRPC.Chat chat = (TLRPC.Chat) object; + selected = selectedUsersByGroup.containsKey(chat.id); + } + if (selected) { + atTop.add(object); + } + } + } + for (int i = 0; i < atTop.size(); ++i) { + TLObject object = atTop.get(i); + if (object instanceof TLRPC.User) { + TLRPC.User user = (TLRPC.User) object; + items.add(ItemInner.asUser(user, selectedUsers.contains(user.id)).red(pageType == PAGE_TYPE_EXCLUDE_CONTACTS)); + h += dp(56); + count++; + } else if (object instanceof TLRPC.Chat) { + TLRPC.Chat chat = (TLRPC.Chat) object; + items.add(ItemInner.asChat(chat, selectedUsersByGroup.containsKey(chat.id)).red(pageType == PAGE_TYPE_EXCLUDE_CONTACTS)); + h += dp(56); + count++; + } + } + } + for (int i = 0; i < allOtherUsers.size(); ++i) { + TLObject object = allOtherUsers.get(i); + if (!searching && atTop.contains(object) || !match(object, q)) { + continue; + } + if (object instanceof TLRPC.User) { + TLRPC.User user = (TLRPC.User) object; + items.add(ItemInner.asUser(user, selectedUsers.contains(user.id)).red(pageType == PAGE_TYPE_EXCLUDE_CONTACTS)); + h += dp(56); + count++; + } else if (object instanceof TLRPC.Chat) { + TLRPC.Chat chat = (TLRPC.Chat) object; + items.add(ItemInner.asChat(chat, selectedUsersByGroup.containsKey(chat.id)).red(pageType == PAGE_TYPE_EXCLUDE_CONTACTS)); + h += dp(56); + count++; + } + } + if (searching) { + if (count == 0) { + items.add(ItemInner.asNoUsers()); + h += dp(150); + } + float lh; + if (listView != null) { + lh = listView.getMeasuredHeight() - listView.getPaddingTop() - listView.getPaddingBottom() + (keyboardVisible ? keyboardHeight : 0); + } else { + lh = AndroidUtilities.displaySize.y - dp(56) - AndroidUtilities.navigationBarHeight - dp(42); + } + if (lh - h > 0) { + items.add(ItemInner.asPadding((int) (lh - h))); + } + } + } + + if (layoutManager.getReverseLayout()) { + Collections.reverse(items); + } + + if (notify && adapter != null) { + if (animated && selectedType != PAGE_TYPE_SHARE) { + adapter.setItems(oldItems, items); + } else { + adapter.notifyDataSetChanged(); + } + } + } + + private boolean match(TLObject obj, String q) { + if (TextUtils.isEmpty(q)) { + return true; + } + if (obj instanceof TLRPC.User) { + TLRPC.User user = (TLRPC.User) obj; + String name = translitSafe(UserObject.getUserName(user)).toLowerCase(); + if (name.startsWith(q) || name.contains(" " + q)) { + return true; + } + String username = translitSafe(UserObject.getPublicUsername(user)).toLowerCase(); + if (username.startsWith(q) || username.contains(" " + q)) { + return true; + } + if (user.usernames != null) { + ArrayList usernames = user.usernames; + for (int j = 0; j < usernames.size(); ++j) { + TLRPC.TL_username username2 = usernames.get(j); + if (!username2.active) { + continue; + } + String u = translitSafe(username2.username).toLowerCase(); + if (u.startsWith(q)) { + return true; + } + } + } + } else if (obj instanceof TLRPC.Chat) { + TLRPC.Chat chat = (TLRPC.Chat) obj; + String name = translitSafe(chat.title).toLowerCase(); + if (name.startsWith(q) || name.contains(" " + q)) { + return true; + } + String username = translitSafe(ChatObject.getPublicUsername(chat)).toLowerCase(); + if (username.startsWith(q) || username.contains(" " + q)) { + return true; + } + if (chat.usernames != null) { + ArrayList usernames = chat.usernames; + for (int j = 0; j < usernames.size(); ++j) { + TLRPC.TL_username username2 = usernames.get(j); + if (!username2.active) { + continue; + } + String u = translitSafe(username2.username).toLowerCase(); + if (u.startsWith(q)) { + return true; + } + } + } + } + return false; + } + + public void onSearch(String text) { + if (text != null && text.isEmpty()) { + text = null; + } + this.query = text; + updateItems(false); + } + + public void updateTops() { + updateSearchFieldTop(); + updateHeaderTop(); + } + + private float getSearchFieldTop() { + float top = -Math.max(0, Math.min(dp(150), searchField.resultContainerHeight) - dp(150)); + for (int i = 0; i < listView.getChildCount(); ++i) { + View child = listView.getChildAt(i); + if (child.getTag() instanceof Integer && (int) child.getTag() == 34) { + top = Math.max(top, child.getY()); + break; + } + } + return top; + } + + private boolean scrolling; + private boolean searchTranslationAnimating; + private float searchTranslationAnimatingTo; + private ValueAnimator searchFieldAnimator; + private void updateSearchFieldTop() { + float ty = getSearchFieldTop(); + if (scrolling || keyboardMoving || getTranslationX() != 0) { + searchTranslationAnimating = false; + if (searchFieldAnimator != null) { + searchFieldAnimator.cancel(); + searchFieldAnimator = null; + } + searchField.setTranslationY(ty); + } else if (!searchTranslationAnimating || Math.abs(searchTranslationAnimatingTo - ty) > 1) { + searchTranslationAnimating = true; + if (searchFieldAnimator != null) { + searchFieldAnimator.cancel(); + searchFieldAnimator = null; + } + searchFieldAnimator = ValueAnimator.ofFloat(searchField.getTranslationY(), searchTranslationAnimatingTo = ty); + searchFieldAnimator.addUpdateListener(anm -> { + searchField.setTranslationY((float) anm.getAnimatedValue()); + }); + searchFieldAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + searchTranslationAnimating = false; + } + }); + searchFieldAnimator.setInterpolator(new LinearInterpolator()); + searchFieldAnimator.setDuration(180); + searchFieldAnimator.start(); + } + } + + private boolean isActionBar; + private void updateHeaderTop() { + if (!containsHeader) { + headerView.setVisibility(View.GONE); + return; + } else { + headerView.setVisibility(View.VISIBLE); + } + boolean isActionBar = true; + float top = -headerView.getHeight(); + for (int i = 0; i < listView.getChildCount(); ++i) { + View child = listView.getChildAt(i); + if (child.getTag() instanceof Integer && (int) child.getTag() == 35) { + top = contentView.getPaddingTop() + child.getY(); + isActionBar = false; + break; + } + } + if (this.isActionBar != isActionBar) { + this.isActionBar = isActionBar; + headerView.backDrawable.setRotation(isActionBar || pageType != PAGE_TYPE_SHARE ? 0f : 1f, true); + } + headerView.setTranslationY(Math.max(AndroidUtilities.statusBarHeight, top)); + } + + public void updateButton(boolean animated) { + if (pageType == PAGE_TYPE_SHARE) { + button.setShowZero(false); + button.setEnabled(true); + button.setCount(0, animated); + if (isEdit) { + button.setText(LocaleController.getString("StoryPrivacyButtonSave"), animated); + } else { + button.setText(LocaleController.getString("StoryPrivacyButtonPost", R.string.StoryPrivacyButtonPost), animated); +// if (selectedType == TYPE_CLOSE_FRIENDS) { +// button.setText(LocaleController.getString("StoryPrivacyButtonCloseFriends", R.string.StoryPrivacyButtonCloseFriends), animated); +// button.setCount(getCloseFriends().size(), animated); +// } else if (selectedType == TYPE_CONTACTS) { +// if (excludedContacts.isEmpty()) { +// button.setText(LocaleController.getString("StoryPrivacyButtonAllContacts", R.string.StoryPrivacyButtonAllContacts), animated); +// button.setCount(0, animated); +// } else { +// button.setText(LocaleController.formatPluralString("StoryPrivacyButtonContacts", 99), animated); +// button.setCount("-" + excludedContacts.size(), animated); +// } +// } else if (selectedType == TYPE_SELECTED_CONTACTS) { +// button.setText(LocaleController.formatPluralString("StoryPrivacyButtonContacts", selectedContacts.size()), animated); +// button.setCount(selectedContacts.size(), animated); +// } else if (selectedType == 0 && !selectedUsers.isEmpty()) { +// button.setText(LocaleController.formatPluralString("StoryPrivacyButtonSelectedContacts", selectedUsers.size()), animated); +// button.setCount(selectedUsers.size(), animated); +// } else { +// button.setText(LocaleController.getString("StoryPrivacyButtonEveryone", R.string.StoryPrivacyButtonEveryone), animated); +// button.setCount(selectedUsers.size(), animated); +// } + } + button2.setVisibility(sendAsMessageEnabled ? View.VISIBLE : View.GONE); +// button2.setText(LocaleController.getString("StoryPrivacyButtonMessage", R.string.StoryPrivacyButtonMessage), animated); + } else if (pageType == PAGE_TYPE_CLOSE_FRIENDS) { + button.setShowZero(false); + button.setEnabled(true); // button.setEnabled(!selectedUsers.isEmpty()); + button.setText(LocaleController.getString("StoryPrivacyButtonSaveCloseFriends", R.string.StoryPrivacyButtonSaveCloseFriends), animated); + button.setCount(selectedUsers.size(), animated); + button2.setVisibility(View.GONE); + } else if (pageType == PAGE_TYPE_SELECT_CONTACTS) { + int count = selectedContactsCount = mergeUsers(selectedUsers, selectedUsersByGroup).size(); +// button.setText(LocaleController.formatPluralString("StoryPrivacyButtonContacts", count), animated); + button.setText(LocaleController.getString("StoryPrivacyButtonSave"), animated); + button.setShowZero(false); + buttonContainer.hide(count <= 0, animated); + button.setCount(count, animated); + button.setEnabled(count > 0); + button2.setVisibility(View.GONE); + } else if (pageType == PAGE_TYPE_EXCLUDE_CONTACTS) { + button.setShowZero(false); + button.setEnabled(true); + if (selectedUsers.isEmpty()) { + button.setText(LocaleController.getString("StoryPrivacyButtonSave"), animated); + button.setCount(0, animated); + } else { + button.setText(LocaleController.getString("StoryPrivacyButtonExcludeContacts", R.string.StoryPrivacyButtonExcludeContacts), animated); + button.setCount(selectedUsers.size(), animated); + } + button2.setVisibility(View.GONE); + } else if (pageType == PAGE_TYPE_SEND_AS_MESSAGE) { + button.setShowZero(true); + button.setEnabled(!selectedUsers.isEmpty()); +// button.setText(LocaleController.formatPluralString("StoryPrivacyButtonMessageChats", selectedUsers.size()), animated); + button.setCount(selectedUsers.size(), animated); + button2.setVisibility(View.GONE); + } + } + + private void updateSectionCell(boolean animated) { + if (sectionCell == null) { + return; + } + if (mergeUsers(selectedUsers, selectedUsersByGroup).size() > 0) { + sectionCell.setRightText(LocaleController.getString(R.string.DeselectAll), true, v -> { + selectedUsers.clear(); + selectedUsersByGroup.clear(); + messageUsers.clear(); + searchField.spansContainer.removeAllSpans(true); + updateCheckboxes(true); + updateButton(true); + }); + } else { + if (animated) { + sectionCell.setRightText(null); + } else { + sectionCell.setRightText(null, null); + } + } + } + + private int lastSelectedType = -1; + + public void updateCheckboxes(boolean animated) { + if (pageType == PAGE_TYPE_EXCLUDE_CONTACTS) { + excludedContacts.clear(); + excludedContacts.addAll(selectedUsers); + } else if (pageType == PAGE_TYPE_SELECT_CONTACTS) { + selectedContacts.clear(); + selectedContactsByGroup.clear(); + selectedContacts.addAll(selectedUsers); + selectedContactsByGroup.putAll(selectedUsersByGroup); + } else if (pageType == PAGE_TYPE_SHARE) { + messageUsers.clear(); + messageUsers.addAll(selectedUsers); + } + + if (pageType == PAGE_TYPE_SELECT_CONTACTS && (selectedType != TYPE_SELECTED_CONTACTS || selectedUsers.isEmpty() && selectedUsersByGroup.isEmpty())) { + if (!(selectedUsers.isEmpty() && selectedUsersByGroup.isEmpty())) { + lastSelectedType = selectedType; + selectedType = TYPE_SELECTED_CONTACTS; + } else if (lastSelectedType != -1) { + selectedType = lastSelectedType; + } + } + + for (int position = 0; position < items.size(); ++position) { + ItemInner item = items.get(position); + if (item != null) { + if (item.type > 0) { + item.checked = selectedType == item.type; + } else if (item.user != null) { + item.checked = selectedUsers.contains(item.user.id); + } else if (item.chat != null) { + item.checked = selectedUsersByGroup.containsKey(item.chat.id); + } + } + } + + for (int i = 0; i < listView.getChildCount(); ++i) { + View child = listView.getChildAt(i); + if (child instanceof UserCell) { + int position = listView.getChildAdapterPosition(child); + if (position < 0 || position >= items.size()) { + continue; + } + ItemInner item = items.get(position); + ((UserCell) child).setChecked(item.checked, animated); + } + } + + updateSectionCell(animated); + } + + public void updateLastSeen() { + for (int i = 0; i < listView.getChildCount(); ++i) { + View child = listView.getChildAt(i); + if (child instanceof UserCell) { + int position = listView.getChildAdapterPosition(child); + if (position < 0 || position >= items.size()) { + continue; + } + ItemInner item = items.get(position); + if (item.user != null) { + ((UserCell) child).setUser(item.user); + } else if (item.chat != null) { + ((UserCell) child).setChat(item.chat, getParticipantsCount(item.chat)); + } + } + } + } + + public void scrollToTopSmoothly() { + LinearSmoothScrollerCustom linearSmoothScroller = new LinearSmoothScrollerCustom(getContext(), LinearSmoothScrollerCustom.POSITION_TOP, .7f); + linearSmoothScroller.setTargetPosition(1); + linearSmoothScroller.setOffset(-AndroidUtilities.dp(56)); + layoutManager.startSmoothScroll(linearSmoothScroller); + } + + public void scrollToTop() { + if (pageType != PAGE_TYPE_SHARE) { + listView.scrollToPosition(0); +// layoutManager.scrollToPositionWithOffset(0, (int) (-AndroidUtilities.displaySize.y * .4f)); + } + } + + public int getTypeOn(MotionEvent e) { + if (pageType != PAGE_TYPE_SHARE || e == null) { + return -1; + } + View child = listView.findChildViewUnder(e.getX(), e.getY() - contentView.getPaddingTop()); + if (child == null) { + return -1; + } + int position = listView.getChildAdapterPosition(child); + if (position < 0 || position >= items.size()) { + return -1; + } + ItemInner item = items.get(position); + if (item.viewType != VIEW_TYPE_USER || item.type == TYPE_EVERYONE) { + return -1; + } + if (LocaleController.isRTL ? e.getX() < getWidth() - AndroidUtilities.dp(100) : e.getX() > AndroidUtilities.dp(100)) { + return item.type; + } + return -1; + } + + public boolean atTop() { + return !listView.canScrollVertically(-1); + } + + private int keyboardHeight; + private boolean keyboardMoving; + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (StoryPrivacyBottomSheet.super.keyboardHeight > 0) { + keyboardHeight = StoryPrivacyBottomSheet.super.keyboardHeight; + } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + contentView.setPadding(0, AndroidUtilities.statusBarHeight + AndroidUtilities.dp(56), 0, 0); + if (wasKeyboardVisible != keyboardVisible) { + float searchFieldTop = getSearchFieldTop(); + if (keyboardVisible && searchFieldTop + Math.min(dp(150), searchField.resultContainerHeight) > listView.getPaddingTop()) { + scrollToTopSmoothly(); + } + if (pageType == PAGE_TYPE_SHARE) { + buttonContainer.setTranslationY(keyboardVisible ? keyboardHeight : 0); + underKeyboardView.setTranslationY(keyboardVisible ? keyboardHeight : 0); + } else { + buttonContainer.translateY(keyboardVisible ? keyboardHeight : -keyboardHeight, 0); + underKeyboardView.setTranslationY(keyboardVisible ? keyboardHeight : -keyboardHeight); + keyboardMoving = true; + underKeyboardView.animate().translationY(0).setDuration(AdjustPanLayoutHelper.keyboardDuration).setInterpolator(AdjustPanLayoutHelper.keyboardInterpolator).withEndAction(() -> { + keyboardMoving = false; + }).start(); + } + wasKeyboardVisible = keyboardVisible; + } + listView.setPadding(0, 0, 0, buttonContainer.getMeasuredHeight()); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + } + + private boolean wasKeyboardVisible; + + @Override + public void onClick(View v) { + if (!searchField.allSpans.contains(v)) { + return; + } + GroupCreateSpan span = (GroupCreateSpan) v; + if (span.isDeleting()) { + searchField.currentDeletingSpan = null; + searchField.spansContainer.removeSpan(span); + long id = span.getUid(); + Iterator>> iterator = selectedUsersByGroup.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry> entry = iterator.next(); + if (entry.getValue().contains(id)) { + iterator.remove(); + selectedUsers.addAll(entry.getValue()); + selectedUsers.remove(id); + } + } + selectedUsers.remove(id); + updateCheckboxes(true); + updateButton(true); + } else { + if (searchField.currentDeletingSpan != null) { + searchField.currentDeletingSpan.cancelDeleteAnimation(); + searchField.currentDeletingSpan = null; + } + searchField.currentDeletingSpan = span; + span.startDeleteAnimation(); + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.chatInfoDidLoad); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.chatInfoDidLoad); + } + + private class Adapter extends AdapterWithDiffUtils { + + private Context context; + private Theme.ResourcesProvider resourcesProvider; + private Runnable onBack; + private SearchUsersCell searchField; + + public Adapter(Context context, Theme.ResourcesProvider resourcesProvider, SearchUsersCell searchField, Runnable onBack) { + this.context = context; + this.resourcesProvider = resourcesProvider; + this.searchField = searchField; + this.onBack = onBack; + } + + private RecyclerListView listView; + public boolean reversedLayout; + + @Override + public boolean isEnabled(RecyclerView.ViewHolder holder) { + return holder.getItemViewType() == VIEW_TYPE_USER || holder.getItemViewType() == VIEW_TYPE_CHECK; + } + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view; + if (viewType == VIEW_TYPE_PAD) { + view = new View(context); + } else if (viewType == VIEW_TYPE_HEADER) { + view = new View(context); + view.setTag(35); + } else if (viewType == VIEW_TYPE_SEARCH) { + view = new View(context); + view.setTag(34); + } else if (viewType == VIEW_TYPE_USER) { + view = new UserCell(context, resourcesProvider); + } else if (viewType == VIEW_TYPE_HEADER2) { + view = new HeaderCell2(context, resourcesProvider); + } else if (viewType == VIEW_TYPE_NO_USERS) { + StickerEmptyView searchEmptyView = new StickerEmptyView(context, null, StickerEmptyView.STICKER_TYPE_SEARCH, resourcesProvider); + searchEmptyView.title.setText(LocaleController.getString("NoResult", R.string.NoResult)); + searchEmptyView.subtitle.setText(LocaleController.getString("SearchEmptyViewFilteredSubtitle2", R.string.SearchEmptyViewFilteredSubtitle2)); + searchEmptyView.linearLayout.setTranslationY(AndroidUtilities.dp(24)); + view = searchEmptyView; + } else if (viewType == VIEW_TYPE_SHADOW) { + view = new TextInfoPrivacyCell(context, resourcesProvider); + view.setBackgroundColor(0xFF0D0D0D); + } else if (viewType == VIEW_TYPE_CHECK) { + view = new TextCell(context, 23, true, true, resourcesProvider); + } else { + view = new View(context) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(dp(32), MeasureSpec.EXACTLY)); + } + }; + } + return new RecyclerListView.Holder(view); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + if (items == null || position < 0 || position >= items.size()) { + return; + } + final ItemInner item = items.get(position); + final int viewType = holder.getItemViewType(); + final ItemInner neighbour = reversedLayout ? (position > 0 ? items.get(position - 1) : null) : (position + 1 < items.size() ? items.get(position + 1) : null); + final boolean divider = neighbour != null && neighbour.viewType == viewType; + if (viewType == VIEW_TYPE_USER) { + UserCell userCell = (UserCell) holder.itemView; + if (item.type > 0) { + userCell.setType(item.type, item.typeCount, item.user); + } else if (item.user != null) { + userCell.setUser(item.user); + } else if (item.chat != null) { + userCell.setChat(item.chat, getParticipantsCount(item.chat)); + } + userCell.setChecked(item.checked, false); + userCell.setDivider(divider); + userCell.setRedCheckbox(item.red); + } else if (viewType == VIEW_TYPE_SECTION) { + + } else if (viewType == VIEW_TYPE_HEADER) { + holder.itemView.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, AndroidUtilities.dp(56))); + } else if (viewType == VIEW_TYPE_PAD) { + int height; + if (item.subtractHeight > 0) { + int h = listView != null && listView.getMeasuredHeight() > 0 ? listView.getMeasuredHeight() + keyboardHeight : AndroidUtilities.displaySize.y; + height = h - item.subtractHeight; + holder.itemView.setTag(33); + } else if (item.padHeight >= 0) { + height = item.padHeight; + holder.itemView.setTag(null); + } else { + height = (int) (AndroidUtilities.displaySize.y * .3f); + holder.itemView.setTag(33); + } + holder.itemView.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, height)); + } else if (viewType == VIEW_TYPE_SEARCH) { + holder.itemView.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, Math.min(dp(150), searchField.resultContainerHeight))); + } else if (viewType == VIEW_TYPE_HEADER2) { + ((HeaderCell2) holder.itemView).setText(item.text, item.text2); + } else if (viewType == VIEW_TYPE_NO_USERS) { + try { + ((StickerEmptyView) holder.itemView).stickerView.getImageReceiver().startAnimation(); + } catch (Exception ignore) {} + } else if (viewType == VIEW_TYPE_SHADOW) { + TextInfoPrivacyCell cell = (TextInfoPrivacyCell) holder.itemView; + if (item.text == null) { + cell.setFixedSize(12); + cell.setText(null); + } else { + cell.setFixedSize(0); + cell.setText(item.text); + } + } else if (viewType == VIEW_TYPE_CHECK) { + ((TextCell) holder.itemView).setTextAndCheck(item.text, item.resId == 0 ? allowScreenshots : keepOnMyPage, divider); + } + } + + @Override + public int getItemViewType(int position) { + if (items == null || position < 0 || position >= items.size()) { + return VIEW_TYPE_PAD; + } + return items.get(position).viewType; + } + + @Override + public int getItemCount() { + return items == null ? 0 : items.size(); + } + }; + } + + private int shiftDp = -6; + private int storyPeriod = 86400; + + private final Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + public StoryPrivacyBottomSheet(Context context, int period, Theme.ResourcesProvider resourcesProvider) { + super(context, true, resourcesProvider); + this.storyPeriod = period; + pullSaved(); + init(context); + viewPager.setAdapter(new ViewPagerFixed.Adapter() { + @Override + public int getItemCount() { + return 2; + } + + @Override + public View createView(int viewType) { + return new Page(context); + } + + @Override + public int getItemViewType(int position) { + return position == 0 ? PAGE_TYPE_SHARE : activePage; + } + + @Override + public void bindView(View view, int position, int viewType) { + ((Page) view).bind(viewType); + } + }); + + MessagesStorage storage = MessagesStorage.getInstance(currentAccount); + storage.getStorageQueue().postRunnable(() -> { + HashMap participantsCountByChat = storage.getSmallGroupsParticipantsCount(); + if (participantsCountByChat == null || participantsCountByChat.isEmpty()) { + return; + } + AndroidUtilities.runOnUIThread(() -> { + if (smallChatsParticipantsCount == null) { + smallChatsParticipantsCount = new HashMap<>(); + } + smallChatsParticipantsCount.putAll(participantsCountByChat); + }); + }); + } + + private void init(Context context) { +// useSmoothKeyboard = true; +// smoothKeyboardAnimationEnabled = true; + Bulletin.addDelegate(container, new Bulletin.Delegate() { + @Override + public int getTopOffset(int tag) { + return AndroidUtilities.statusBarHeight; + } + }); + + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.contactsDidLoad); + + backgroundPaint.setColor(Theme.getColor(Theme.key_dialogBackground, resourcesProvider)); + fixNavigationBar(Theme.getColor(Theme.key_dialogBackground, resourcesProvider)); + + containerView = new ContainerView(context); + + viewPager = new ViewPagerFixed(context) { + @Override + protected void onTabAnimationUpdate() { + containerView.invalidate(); + } + + @Override + protected boolean canScroll(MotionEvent e) { + View currentView = viewPager.getCurrentView(); + if (currentView instanceof Page) { + if (getCurrentPosition() > 0) { + closeKeyboard(); + return true; + } + int page = ((Page) currentView).getTypeOn(e); + if (page != -1) { + activePage = page; + if (page != TYPE_SELECTED_CONTACTS || !selectedContacts.isEmpty() && !selectedContactsByGroup.isEmpty()) { + selectedType = page; + } + ((Page) currentView).updateCheckboxes(true); + ((Page) currentView).updateButton(true); + } + if (page != -1) { + closeKeyboard(); + } + return page != -1; + } + return true; + } + + @Override + protected void onItemSelected(View currentPage, View oldPage, int position, int oldPosition) { + if (keyboardVisible) { + closeKeyboard(); + } + } + }; + viewPager.setPadding(backgroundPaddingLeft, 0, backgroundPaddingLeft, 0); + containerView.addView(viewPager, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); + } + + @Override + public void dismissInternal() { + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.contactsDidLoad); + super.dismissInternal(); + } + + private StoryPrivacyBottomSheet(int singlePageType, Context context, Theme.ResourcesProvider resourcesProvider) { + super(context, true, resourcesProvider); + init(context); + viewPager.setAdapter(new ViewPagerFixed.Adapter() { + @Override + public int getItemCount() { + return 1; + } + @Override + public View createView(int viewType) { + return new Page(context); + } + + @Override + public int getItemViewType(int position) { + return singlePageType; + } + @Override + public void bindView(View view, int position, int viewType) { + ((Page) view).bind(viewType); + } + }); + } + + public void closeKeyboard() { + View[] pages = viewPager.getViewPages(); + for (int i = 0; i < pages.length; ++i) { + View page = pages[i]; + if (page instanceof Page && ((Page) page).searchField != null) { + AndroidUtilities.hideKeyboard(((Page) page).searchField.editText); + } + } + } + + private void done(StoryPrivacy privacy, Runnable loaded) { + done(privacy, loaded, false); + } + + private void done(StoryPrivacy privacy, Runnable loaded, boolean ignoreRestriction) { + ArrayList restrictedUsers = new ArrayList<>(); + if (warnUsers != null && privacy != null) { + MessagesController messagesController = MessagesController.getInstance(currentAccount); + for (int i = 0; i < warnUsers.size(); ++i) { + String username = warnUsers.get(i); + TLObject obj = messagesController.getUserOrChat(username); + if (!(obj instanceof TLRPC.User)) { + continue; + } + TLRPC.User user = (TLRPC.User) obj; + TLRPC.User user2 = messagesController.getUser(user.id); + if (user2 != null) { + user = user2; + } + if (!user.bot && !privacy.containsUser(user)) { + restrictedUsers.add(username); + } + } + } + if (!restrictedUsers.isEmpty() && !ignoreRestriction) { + SpannableStringBuilder usersString = new SpannableStringBuilder(); + for (int i = 0; i < Math.min(2, restrictedUsers.size()); ++i) { + if (i > 0) { + usersString.append(", "); + } + SpannableString username = new SpannableString("@" + restrictedUsers.get(i)); + username.setSpan(new TypefaceSpan(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)), 0, username.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + usersString.append(username); + } + new AlertDialog.Builder(getContext(), resourcesProvider) + .setTitle(LocaleController.getString(R.string.StoryRestrictions)) + .setMessage(AndroidUtilities.replaceCharSequence("%s", LocaleController.getString(R.string.StoryRestrictionsInfo), usersString)) + .setPositiveButton(LocaleController.getString(R.string.Proceed), (di, i) -> { + done(privacy, loaded, true); + }) + .setNegativeButton(LocaleController.getString(R.string.Cancel), null) + .show(); + return; + } + View[] pages = viewPager.getViewPages(); + ButtonWithCounterView button = pages[0] instanceof Page ? ((Page) pages[0]).button : null; + if (loaded != null && button != null) { + button.setLoading(true); + } + if (onDone != null) { + onDone.run(privacy, allowScreenshots, keepOnMyPage, loaded != null ? () -> { + if (button != null) { + button.setLoading(false); + } + if (loaded != null) { + loaded.run(); + } + } : null); + } else if (loaded != null) { + loaded.run(); + } + } + + @Override + public void dismiss() { + if (onDismiss != null) { + StoryPrivacy privacy; + if (selectedType == TYPE_SELECTED_CONTACTS) { + HashSet users = mergeUsers(selectedContacts, selectedContactsByGroup); + privacy = new StoryPrivacy(selectedType, currentAccount, new ArrayList<>(users)); + privacy.selectedUserIds.clear(); + privacy.selectedUserIds.addAll(selectedContacts); + privacy.selectedUserIdsByGroup.clear(); + privacy.selectedUserIdsByGroup.putAll(selectedContactsByGroup); + } else if (selectedType == TYPE_CONTACTS) { + privacy = new StoryPrivacy(selectedType, currentAccount, excludedContacts); + } else { + privacy = new StoryPrivacy(selectedType, currentAccount, null); + } + onDismiss.run(privacy); + onDismiss = null; + } + Bulletin.removeDelegate(container); + save(); + super.dismiss(); + } + + private class ContainerView extends FrameLayout { + + public ContainerView(Context context) { + super(context); + } + + private final AnimatedFloat isActionBar = new AnimatedFloat(this, 250, CubicBezierInterpolator.EASE_OUT_QUINT); + private float top; + + @Override + protected void dispatchDraw(Canvas canvas) { + View[] views = viewPager.getViewPages(); + top = 0; + float keyboardT = 0; + for (int i = 0; i < views.length; ++i) { + if (views[i] == null) { + continue; + } + final Page page = (Page) views[i]; + float t = Utilities.clamp(1f - Math.abs(page.getTranslationX() / (float) page.getMeasuredWidth()), 1, 0); + top += page.top() * t; + keyboardT += (keyboardVisible && page.pageType != PAGE_TYPE_SHARE ? 1 : 0) * t; + if (page.getVisibility() == View.VISIBLE) { + page.updateTops(); + } + } + float actionBarT = isActionBar.set(top <= AndroidUtilities.statusBarHeight ? 1f : 0f); + top = Math.max(AndroidUtilities.statusBarHeight, top) - AndroidUtilities.statusBarHeight * actionBarT; + AndroidUtilities.rectTmp.set(backgroundPaddingLeft, top, getWidth() - backgroundPaddingLeft, getHeight() + dp(8)); + final float r = AndroidUtilities.lerp(dp(14), 0, actionBarT); + canvas.drawRoundRect(AndroidUtilities.rectTmp, r, r, backgroundPaint); + canvas.save(); + canvas.clipRect(AndroidUtilities.rectTmp); + super.dispatchDraw(canvas); + canvas.restore(); + } + + @Override + public boolean dispatchTouchEvent(MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN && event.getY() < top) { + dismiss(); + return true; + } + return super.dispatchTouchEvent(event); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure( + MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY) + ); + } + } + + @Override + public void onBackPressed() { + if (viewPager.getCurrentPosition() > 0) { + closeKeyboard(); + viewPager.scrollToPosition(viewPager.getCurrentPosition() - 1); + return; + } + super.onBackPressed(); + } + + @Override + protected boolean canDismissWithSwipe() { + View currentView = viewPager.getCurrentView(); + if (currentView instanceof Page) { + return ((Page) currentView).atTop(); + } + return true; + } + + private ArrayList warnUsers; + private Utilities.Callback4 onDone; + private Utilities.Callback onDismiss; + private Utilities.Callback> onDone2; + private boolean applyWhenDismiss = false; + private boolean allowSmallChats = true; + private boolean isEdit = false; + public StoryPrivacyBottomSheet whenDismiss(Utilities.Callback listener) { + this.onDismiss = listener; + return this; + } + public StoryPrivacyBottomSheet whenSelectedRules(Utilities.Callback4 onDone, boolean whenDismiss) { + this.onDone = onDone; + this.applyWhenDismiss = whenDismiss; + return this; + } + private StoryPrivacyBottomSheet whenSelectedShare(Utilities.Callback> onDone2) { + this.onDone2 = onDone2; + return this; + } + public StoryPrivacyBottomSheet enableSharing(boolean enable) { + this.sendAsMessageEnabled = enable; + if (viewPager != null) { + View[] viewPages = viewPager.getViewPages(); + for (int i = 0; i < viewPages.length; ++i) { + View view = viewPages[i]; + if (view instanceof Page) { + ((Page) view).updateButton(false); + } + } + } + return this; + } + public StoryPrivacyBottomSheet allowSmallChats(boolean allowSmallChats) { + this.allowSmallChats = allowSmallChats; + return this; + } + public StoryPrivacyBottomSheet isEdit(boolean isEdit) { + this.isEdit = isEdit; + if (viewPager != null) { + View[] viewPages = viewPager.getViewPages(); + for (int i = 0; i < viewPages.length; ++i) { + View view = viewPages[i]; + if (view instanceof Page) { + ((Page) view).updateItems(false); + ((Page) view).updateButton(false); + } + } + } + return this; + } + public StoryPrivacyBottomSheet setWarnUsers(ArrayList users) { + this.warnUsers = users; + return this; + } + + public StoryPrivacyBottomSheet setValue(StoryPrivacy privacy) { + if (privacy == null) { + return this; + } + selectedType = privacy.type; + if (selectedType == TYPE_CONTACTS) { + excludedContacts.clear(); + excludedContacts.addAll(privacy.selectedUserIds); + } else if (selectedType == TYPE_SELECTED_CONTACTS) { + selectedContacts.clear(); + selectedContacts.addAll(privacy.selectedUserIds); + selectedContactsByGroup.clear(); + selectedContactsByGroup.putAll(privacy.selectedUserIdsByGroup); + selectedContactsCount = mergeUsers(selectedContacts, selectedContactsByGroup).size(); + } + if (privacy.isShare()) { + startedFromSendAsMessage = true; + activePage = PAGE_TYPE_SEND_AS_MESSAGE; + messageUsers.clear(); + messageUsers.addAll(privacy.sendToUsers); + viewPager.setPosition(1); + } + View[] viewPages = viewPager.getViewPages(); + if (viewPages[0] instanceof Page) { + ((Page) viewPages[0]).bind(((Page) viewPages[0]).pageType); + } + if (viewPages[1] instanceof Page) { + ((Page) viewPages[1]).bind(((Page) viewPages[1]).pageType); + } + return this; + } + + public static final int VIEW_TYPE_PAD = -1; + public static final int VIEW_TYPE_HEADER = 0; + public static final int VIEW_TYPE_SEARCH = 1; + public static final int VIEW_TYPE_SECTION = 2; + public static final int VIEW_TYPE_USER = 3; + public static final int VIEW_TYPE_HEADER2 = 4; + public static final int VIEW_TYPE_NO_USERS = 5; + public static final int VIEW_TYPE_EMPTY_VIEW = 5; + public static final int VIEW_TYPE_SHADOW = 6; + public static final int VIEW_TYPE_CHECK = 7; + + private static class ItemInner extends AdapterWithDiffUtils.Item { + + public int resId; + public CharSequence text, text2; + public TLRPC.User user; + public TLRPC.Chat chat; + public int type; + public int typeCount; + public boolean checked; + public boolean red; + public int subtractHeight; + public int padHeight = -1; + + private ItemInner(int viewType, boolean selectable) { + super(viewType, selectable); + } + + public static ItemInner asPad() { + return asPad(-1); + } + public static ItemInner asPad(int subtractHeight) { + ItemInner item = new ItemInner(VIEW_TYPE_PAD, false); + item.subtractHeight = subtractHeight; + return item; + } + public static ItemInner asHeader() { + return new ItemInner(VIEW_TYPE_HEADER, false); + } + public static ItemInner asHeader2(CharSequence title, CharSequence subtitle) { + ItemInner item = new ItemInner(VIEW_TYPE_HEADER2, false); + item.text = title; + item.text2 = subtitle; + return item; + } + public static ItemInner asSearchField() { + return new ItemInner(VIEW_TYPE_SEARCH, false); + } + public static ItemInner asSection() { + ItemInner item = new ItemInner(VIEW_TYPE_SECTION, false); + return item; + } + public static ItemInner asUser(TLRPC.User user, boolean checked) { + ItemInner item = new ItemInner(VIEW_TYPE_USER, true); + item.user = user; + item.checked = checked; + return item; + } + public static ItemInner asChat(TLRPC.Chat chat, boolean checked) { + ItemInner item = new ItemInner(VIEW_TYPE_USER, true); + item.chat = chat; + item.checked = checked; + return item; + } + public static ItemInner asType(int type, boolean checked) { + ItemInner item = new ItemInner(VIEW_TYPE_USER, false); + item.type = type; + item.checked = checked; + return item; + } + public static ItemInner asType(int type, boolean checked, int count) { + ItemInner item = new ItemInner(VIEW_TYPE_USER, false); + item.type = type; + item.checked = checked; + item.typeCount = count; + return item; + } + public static ItemInner asShadow(CharSequence text) { + ItemInner item = new ItemInner(VIEW_TYPE_SHADOW, false); + item.text = text; + return item; + } + public static ItemInner asCheck(CharSequence text, int id, boolean checked) { + ItemInner item = new ItemInner(VIEW_TYPE_CHECK, false); + item.resId = id; + item.text = text; + item.checked = checked; + return item; + } + + public static ItemInner asNoUsers() { + return new ItemInner(VIEW_TYPE_NO_USERS, false); + } + public static ItemInner asPadding(int padHeight) { + ItemInner item = new ItemInner(VIEW_TYPE_PAD, false); + item.padHeight = padHeight; + return item; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ItemInner i = (ItemInner) o; + if (viewType != i.viewType) { + return false; + } + if (viewType == VIEW_TYPE_PAD && (subtractHeight != i.subtractHeight || padHeight != i.padHeight)) { + return false; + } else if (viewType == VIEW_TYPE_USER && (user != i.user || chat != i.chat || type != i.type || typeCount != i.typeCount || checked != i.checked || red != i.red)) { + return false; + } else if (viewType == VIEW_TYPE_HEADER && resId != i.resId) { + return false; + } else if (viewType == VIEW_TYPE_SECTION && !TextUtils.equals(text, i.text)) { + return false; + } else if (viewType == VIEW_TYPE_HEADER2 && (!TextUtils.equals(text, i.text) || !TextUtils.equals(text2, i.text2))) { + return false; + } else if (viewType == VIEW_TYPE_SHADOW && (!TextUtils.equals(text, i.text))) { + return false; + } else if (viewType == VIEW_TYPE_CHECK && (resId != i.resId || !TextUtils.equals(text, i.text) || checked != i.checked)) { + return false; + } + return true; + } + + public ItemInner red(boolean red) { + this.red = red; + return this; + } + } + + private boolean loadedContacts; + + private ArrayList getContacts() { + final ArrayList chats = new ArrayList<>(); + final ArrayList contacts = ContactsController.getInstance(currentAccount).contacts; + if (contacts == null || contacts.isEmpty()) { + ContactsController.getInstance(currentAccount).loadContacts(false, 0); + } + final MessagesController messagesController = MessagesController.getInstance(currentAccount); + if (contacts != null) { + for (int i = 0; i < contacts.size(); ++i) { + final TLRPC.TL_contact contact = contacts.get(i); + if (contact != null) { + final TLRPC.User user = messagesController.getUser(contact.user_id); + if (user != null && !UserObject.isUserSelf(user) && !user.bot && user.id != 777000) { + chats.add(user); + } + } + } + } + return chats; + } + + private ArrayList getCloseFriends() { + final ArrayList contacts = getContacts(); + for (int i = 0; i < contacts.size(); ++i) { + final TLObject chat = contacts.get(i); + if (!(chat instanceof TLRPC.User)) { + continue; + } + final TLRPC.User user = (TLRPC.User) chat; + if (user == null || !user.close_friend) { + contacts.remove(i); + i--; + } + } + return contacts; + } + + private ArrayList getUsers(boolean onlyContacts, boolean includeSmallChats) { + final MessagesController messagesController = MessagesController.getInstance(currentAccount); + final HashMap contains = new HashMap<>(); + final ArrayList users = new ArrayList<>(); + final ArrayList dialogs = messagesController.getAllDialogs(); + final ConcurrentHashMap contacts; + if (onlyContacts) { + contacts = ContactsController.getInstance(currentAccount).contactsDict; + if (contacts == null || contacts.isEmpty()) { + if (!loadedContacts) { + ContactsController.getInstance(currentAccount).loadContacts(false, 0); + } + loadedContacts = true; + } + } else { + contacts = null; + } + for (int i = 0; i < dialogs.size(); ++i) { + TLRPC.Dialog dialog = dialogs.get(i); + if (DialogObject.isUserDialog(dialog.id)) { + TLRPC.User user = messagesController.getUser(dialog.id); + if (user != null && !user.bot && user.id != 777000 && !UserObject.isUserSelf(user)) { + if (onlyContacts && (contacts == null || contacts.get(user.id) == null)) { + continue; + } + contains.put(user.id, true); + users.add(user); + } + } else if (includeSmallChats && DialogObject.isChatDialog(dialog.id)) { + TLRPC.Chat chat = messagesController.getChat(-dialog.id); + if (chat == null || ChatObject.isForum(chat) || ChatObject.isChannelAndNotMegaGroup(chat)) { + continue; + } + int participants_count = getParticipantsCount(chat); + if (participants_count > 1) { + contains.put(-chat.id, true); + users.add(chat); + } + } + } + if (contacts != null) { + for (Map.Entry e : contacts.entrySet()) { + long id = e.getKey(); + if (!contains.containsKey(id)) { + TLRPC.User user = messagesController.getUser(id); + if (user != null && !user.bot && user.id != 777000 && !UserObject.isUserSelf(user)) { + users.add(user); + contains.put(user.id, true); + } + } + } + } + return users; + } + + private int getParticipantsCount(TLRPC.Chat chat) { + TLRPC.ChatFull chatFull = MessagesController.getInstance(currentAccount).getChatFull(chat.id); + if (chatFull != null && chatFull.participants_count > 0) { + return chatFull.participants_count; + } else if (smallChatsParticipantsCount != null) { + Integer count = smallChatsParticipantsCount.get(chat.id); + if (count != null) { + return count; + } + } + return chat.participants_count; + } + + private ArrayList getChats() { + final ArrayList chats = new ArrayList<>(); + final MessagesController messagesController = MessagesController.getInstance(currentAccount); + final ArrayList dialogs = messagesController.getAllDialogs(); + for (int i = 0; i < dialogs.size(); ++i) { + TLRPC.Dialog dialog = dialogs.get(i); + if (!messagesController.canAddToForward(dialog)) { + continue; + } + if (DialogObject.isUserDialog(dialog.id)) { + TLRPC.User user = messagesController.getUser(dialog.id); + if (user != null && !user.bot && user.id != 777000 && !UserObject.isUserSelf(user)) { + chats.add(user); + } + } else if (DialogObject.isChatDialog(dialog.id)) { + TLRPC.Chat chat = messagesController.getChat(-dialog.id); + if (chat != null && !ChatObject.isForum(chat)) { + chats.add(chat); + } + } + } + return chats; + } + + private static class UserCell extends FrameLayout { + + private final Theme.ResourcesProvider resourcesProvider; + + private final AvatarDrawable avatarDrawable = new AvatarDrawable(); + private final BackupImageView imageView; + + private final SimpleTextView titleTextView; + private final SimpleTextView subtitleTextView; + + private final CheckBox2 checkBox; + private final RadioButton radioButton; + + private final Paint dividerPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + public UserCell(Context context, Theme.ResourcesProvider resourcesProvider) { + super(context); + this.resourcesProvider = resourcesProvider; + + avatarDrawable.setRoundRadius(AndroidUtilities.dp(40)); + + imageView = new BackupImageView(context); + imageView.setRoundRadius(AndroidUtilities.dp(40)); + addView(imageView, LayoutHelper.createFrame(40, 40, Gravity.CENTER_VERTICAL | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), 53, 0, 53, 0)); + + titleTextView = new SimpleTextView(context); + titleTextView.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + titleTextView.setTextSize(16); + titleTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider)); + titleTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); + NotificationCenter.listenEmojiLoading(titleTextView); + addView(titleTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), LocaleController.isRTL ? 16 : 105, 0, LocaleController.isRTL ? 105 : 16, 0)); + + subtitleTextView = new SimpleTextView(context); + subtitleTextView.setTextSize(14); + subtitleTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider)); + subtitleTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); + NotificationCenter.listenEmojiLoading(subtitleTextView); + addView(subtitleTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), LocaleController.isRTL ? 16 : 105, 0, LocaleController.isRTL ? 105 : 16, 0)); + + checkBox = new CheckBox2(context, 21, resourcesProvider); + checkBox.setColor(Theme.key_dialogRoundCheckBox, Theme.key_checkboxDisabled, Theme.key_dialogRoundCheckBoxCheck); + checkBox.setDrawUnchecked(true); + checkBox.setDrawBackgroundAsArc(10); + addView(checkBox, LayoutHelper.createFrame(24, 24, Gravity.CENTER_VERTICAL | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), 14, 0, 14, 0)); + checkBox.setChecked(false, false); + checkBox.setVisibility(View.GONE); + + radioButton = new RadioButton(context); + radioButton.setSize(AndroidUtilities.dp(20)); + radioButton.setColor(Theme.getColor(Theme.key_checkboxDisabled, resourcesProvider), Theme.getColor(Theme.key_dialogRadioBackgroundChecked, resourcesProvider)); + addView(radioButton, LayoutHelper.createFrame(22, 22, Gravity.CENTER_VERTICAL | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), 15, 0, 15, 0)); + radioButton.setVisibility(View.GONE); + } + + public void setRedCheckbox(boolean red) { + checkBox.setColor(red ? Theme.key_color_red : Theme.key_dialogRoundCheckBox, Theme.key_checkboxDisabled, Theme.key_dialogRoundCheckBoxCheck); + } + + public void setChecked(boolean checked, boolean animated) { + if (checkBox.getVisibility() == View.VISIBLE) { + checkBox.setChecked(checked, animated); + } + if (radioButton.getVisibility() == View.VISIBLE) { + radioButton.setChecked(checked, animated); + } + } + + private boolean[] isOnline = new boolean[1]; + + public void setUser(TLRPC.User user) { + avatarDrawable.setInfo(user); + imageView.setForUserOrChat(user, avatarDrawable); + + CharSequence text = UserObject.getUserName(user); + text = Emoji.replaceEmoji(text, titleTextView.getPaint().getFontMetricsInt(), false); + titleTextView.setText(text); + isOnline[0] = false; + setSubtitle(LocaleController.formatUserStatus(UserConfig.selectedAccount, user, isOnline)); + subtitleTextView.setTextColor(Theme.getColor(isOnline[0] ? Theme.key_dialogTextBlue2 : Theme.key_dialogTextGray3, resourcesProvider)); + + checkBox.setVisibility(View.VISIBLE); + checkBox.setAlpha(1f); + radioButton.setVisibility(View.GONE); + } + + public void setChat(TLRPC.Chat chat, int participants_count) { + avatarDrawable.setInfo(chat); + imageView.setForUserOrChat(chat, avatarDrawable); + + CharSequence text = chat.title; + text = Emoji.replaceEmoji(text, titleTextView.getPaint().getFontMetricsInt(), false); + titleTextView.setText(text); + + isOnline[0] = false; + String subtitle; + if (ChatObject.isChannel(chat) && !chat.megagroup) { + if (participants_count > 1) { + subtitle = LocaleController.formatPluralStringComma("Subscribers", participants_count - 1); + } else { + if (!ChatObject.isPublic(chat)) { + subtitle = LocaleController.getString("ChannelPrivate", R.string.ChannelPrivate).toLowerCase(); + } else { + subtitle = LocaleController.getString("ChannelPublic", R.string.ChannelPublic).toLowerCase(); + } + } + } else { + if (participants_count > 1) { + subtitle = LocaleController.formatPluralStringComma("Members", participants_count - 1); + } else { + if (chat.has_geo) { + subtitle = LocaleController.getString("MegaLocation", R.string.MegaLocation); + } else if (!ChatObject.isPublic(chat)) { + subtitle = LocaleController.getString("MegaPrivate", R.string.MegaPrivate).toLowerCase(); + } else { + subtitle = LocaleController.getString("MegaPublic", R.string.MegaPublic).toLowerCase(); + } + } + } + setSubtitle(subtitle); + subtitleTextView.setTextColor(Theme.getColor(isOnline[0] ? Theme.key_dialogTextBlue2 : Theme.key_dialogTextGray3, resourcesProvider)); + + checkBox.setVisibility(View.VISIBLE); + checkBox.setAlpha(participants_count > 200 ? .3f : 1f); + radioButton.setVisibility(View.GONE); + } + + private CharSequence withArrow(CharSequence text) { + SpannableString arrow = new SpannableString(">"); + Drawable arrowDrawable = getContext().getResources().getDrawable(R.drawable.attach_arrow_right); + ColoredImageSpan span = new ColoredImageSpan(arrowDrawable, ColoredImageSpan.ALIGN_CENTER); + arrowDrawable.setBounds(0, dp(1), dp(11), dp(1 + 11)); + arrow.setSpan(span, 0, arrow.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + SpannableStringBuilder finalText = new SpannableStringBuilder(); + finalText.append(text).append(" ").append(arrow); + return finalText; + } + + public void setType(int type, int count, TLRPC.User singleUser) { + if (type == TYPE_EVERYONE) { + titleTextView.setText(LocaleController.getString("StoryPrivacyOptionEveryone", R.string.StoryPrivacyOptionEveryone)); + setSubtitle(null); + avatarDrawable.setAvatarType(AvatarDrawable.AVATAR_TYPE_FILTER_CHANNELS); + avatarDrawable.setColor(0xFF16A5F2, 0xFF1180F7); + } else if (type == TYPE_CONTACTS) { + titleTextView.setText(LocaleController.getString("StoryPrivacyOptionContacts", R.string.StoryPrivacyOptionContacts)); + if (count == 1 && singleUser != null) { + CharSequence text = LocaleController.formatString(R.string.StoryPrivacyOptionExcludePerson, UserObject.getUserName(singleUser)); + text = Emoji.replaceEmoji(text, subtitleTextView.getPaint().getFontMetricsInt(), false); + setSubtitle(withArrow(text)); + } else if (count > 0) { + setSubtitle(withArrow(LocaleController.formatPluralString("StoryPrivacyOptionExcludePeople", count))); + } else { + setSubtitle(withArrow(LocaleController.getString("StoryPrivacyOptionContactsDetail", R.string.StoryPrivacyOptionContactsDetail))); + } + subtitleTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlue2, resourcesProvider)); + avatarDrawable.setAvatarType(AvatarDrawable.AVATAR_TYPE_FILTER_CONTACTS); + avatarDrawable.setColor(0xFFC468F2, 0xFF965CFA); + } else if (type == TYPE_CLOSE_FRIENDS) { + titleTextView.setText(LocaleController.getString("StoryPrivacyOptionCloseFriends", R.string.StoryPrivacyOptionCloseFriends)); + if (count == 1 && singleUser != null) { + CharSequence text = UserObject.getUserName(singleUser); + text = Emoji.replaceEmoji(text, subtitleTextView.getPaint().getFontMetricsInt(), false); + setSubtitle(withArrow(text)); + } else if (count > 0) { + setSubtitle(withArrow(LocaleController.formatPluralString("StoryPrivacyOptionPeople", count))); + } else { + setSubtitle(withArrow(LocaleController.getString("StoryPrivacyOptionCloseFriendsDetail", R.string.StoryPrivacyOptionCloseFriendsDetail))); + } + subtitleTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlue2, resourcesProvider)); + avatarDrawable.setAvatarType(AvatarDrawable.AVATAR_TYPE_CLOSE_FRIENDS); + avatarDrawable.setColor(0xFF88D93A, 0xFF2DB63B); + } else if (type == TYPE_SELECTED_CONTACTS) { + titleTextView.setText(LocaleController.getString("StoryPrivacyOptionSelectedContacts", R.string.StoryPrivacyOptionSelectedContacts)); + if (count == 1 && singleUser != null) { + CharSequence text = UserObject.getUserName(singleUser); + text = Emoji.replaceEmoji(text, subtitleTextView.getPaint().getFontMetricsInt(), false); + setSubtitle(withArrow(text)); + } else if (count > 0) { + setSubtitle(withArrow(LocaleController.formatPluralString("StoryPrivacyOptionPeople", count))); + } else { + setSubtitle(withArrow(LocaleController.getString("StoryPrivacyOptionSelectedContactsDetail", R.string.StoryPrivacyOptionSelectedContactsDetail))); + } + subtitleTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlue2, resourcesProvider)); + avatarDrawable.setAvatarType(AvatarDrawable.AVATAR_TYPE_FILTER_GROUPS); + avatarDrawable.setColor(0xFFFFB743, 0xFFF68E34); + } + checkBox.setVisibility(View.GONE); + radioButton.setVisibility(View.VISIBLE); + imageView.setImageDrawable(avatarDrawable); + } + + private void setSubtitle(CharSequence text) { + if (text == null) { + titleTextView.setTranslationY(0); + subtitleTextView.setVisibility(View.GONE); + } else { + titleTextView.setTranslationY(AndroidUtilities.dp(-9)); + subtitleTextView.setTranslationY(AndroidUtilities.dp(12)); + subtitleTextView.setText(text); + subtitleTextView.setVisibility(View.VISIBLE); + } + } + + private boolean needDivider; + public void setDivider(boolean divider) { + setWillNotDraw(!(needDivider = divider)); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure( + MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(56), MeasureSpec.EXACTLY) + ); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + if (needDivider) { + dividerPaint.setColor(Theme.getColor(Theme.key_divider, resourcesProvider)); + if (LocaleController.isRTL) { + canvas.drawRect(0, getHeight() - 1, getWidth() - AndroidUtilities.dp(105), getHeight(), dividerPaint); + } else { + canvas.drawRect(AndroidUtilities.dp(105), getHeight() - 1, getWidth(), getHeight(), dividerPaint); + } + } + } + } + + private static class HeaderCell2 extends LinearLayout { + + private final Theme.ResourcesProvider resourcesProvider; + private final TextView titleTextView; + private final TextView subtitleTextView; + + public HeaderCell2(Context context, Theme.ResourcesProvider resourcesProvider) { + super(context); + setOrientation(VERTICAL); + this.resourcesProvider = resourcesProvider; + + titleTextView = new TextView(context); + titleTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider)); + titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); + titleTextView.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + addView(titleTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.FILL_HORIZONTAL, 27, 16, 27, 0)); + + subtitleTextView = new TextView(context); + subtitleTextView.setTextColor(Theme.getColor(Theme.key_dialogTextGray2, resourcesProvider)); + subtitleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + addView(subtitleTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.FILL_HORIZONTAL, 27, 5, 27, 13)); + } + + public void setText(CharSequence title, CharSequence subtitle) { + titleTextView.setText(title); + subtitleTextView.setText(subtitle); + } + } + + private static class HeaderCell extends FrameLayout { + + private final Theme.ResourcesProvider resourcesProvider; + + private ImageView closeView; + private TextView textView; + public BackDrawable backDrawable; + + private final Paint dividerPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + public HeaderCell(Context context, Theme.ResourcesProvider resourcesProvider) { + super(context); + this.resourcesProvider = resourcesProvider; + + textView = new TextView(context); + textView.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); + textView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); + textView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider)); + addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL | Gravity.FILL_HORIZONTAL, LocaleController.isRTL ? 16 : 53, 0, LocaleController.isRTL ? 53 : 16, 0)); + + closeView = new ImageView(context); + closeView.setImageDrawable(backDrawable = new BackDrawable(false)); + backDrawable.setColor(0xffffffff); + backDrawable.setRotatedColor(0xffffffff); + backDrawable.setAnimationTime(220); +// closeView.setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_dialogButtonSelector, resourcesProvider))); + addView(closeView, LayoutHelper.createFrame(24, 24, Gravity.CENTER_VERTICAL | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), 16, 0, 16, 0)); + closeView.setOnClickListener(e -> { + if (onCloseClickListener != null) { + onCloseClickListener.run(); + } + }); + } + + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + + dividerPaint.setColor(Theme.getColor(Theme.key_divider, resourcesProvider)); + canvas.drawRect(0, getHeight() - AndroidUtilities.getShadowHeight(), getWidth(), getHeight(), dividerPaint); + } + + public void setText(CharSequence text) { + textView.setText(text); + } + + public void setCloseImageVisible(boolean visible) { + closeView.setVisibility(visible ? View.VISIBLE : View.GONE); + textView.setLayoutParams(LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL | Gravity.FILL_HORIZONTAL, LocaleController.isRTL || !visible ? 22 : 53, 0, LocaleController.isRTL && visible ? 53 : 22, 0)); + } + + public void setBackImage(int resId) { + closeView.setImageResource(resId); + } + + private Runnable onCloseClickListener; + public void setOnCloseClickListener(Runnable onCloseClickListener) { + this.onCloseClickListener = onCloseClickListener; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure( + MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(56), MeasureSpec.EXACTLY) + ); + } + } + + public class SearchUsersCell extends ScrollView { + + private final Theme.ResourcesProvider resourcesProvider; + + private EditTextBoldCursor editText; + private int hintTextWidth; + + public SpansContainer spansContainer; + private int selectedCount; + public ArrayList allSpans = new ArrayList<>(); + private GroupCreateSpan currentDeletingSpan; + private Runnable updateHeight; + + private boolean ignoreTextChange; + private Utilities.Callback onSearchTextChange; + + public SearchUsersCell(Context context, Theme.ResourcesProvider resourcesProvider, Runnable updateHeight) { + super(context); + this.resourcesProvider = resourcesProvider; + this.updateHeight = updateHeight; + + setVerticalScrollBarEnabled(false); + AndroidUtilities.setScrollViewEdgeEffectColor(this, Theme.getColor(Theme.key_windowBackgroundWhite)); + + spansContainer = new SpansContainer(context); + addView(spansContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); +// spansContainer.setOnClickListener(v -> { +// editText.clearFocus(); +// editText.requestFocus(); +// AndroidUtilities.showKeyboard(editText); +// }); + + editText = new EditTextBoldCursor(context) { + @Override + public boolean onTouchEvent(MotionEvent event) { + if (currentDeletingSpan != null) { + currentDeletingSpan.cancelDeleteAnimation(); + currentDeletingSpan = null; + } + if (event.getAction() == MotionEvent.ACTION_DOWN) { + if (!AndroidUtilities.showKeyboard(this)) { + fullScroll(View.FOCUS_DOWN); + clearFocus(); + requestFocus(); + } + } + return super.onTouchEvent(event); + } + }; + editText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + editText.setHintColor(Theme.getColor(Theme.key_groupcreate_hintText, resourcesProvider)); + editText.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); + editText.setCursorColor(Theme.getColor(Theme.key_groupcreate_cursor, resourcesProvider)); + editText.setHandlesColor(Theme.getColor(Theme.key_groupcreate_cursor, resourcesProvider)); + editText.setCursorWidth(1.5f); + editText.setInputType(InputType.TYPE_TEXT_VARIATION_FILTER | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType.TYPE_TEXT_FLAG_MULTI_LINE); + editText.setSingleLine(true); + editText.setBackgroundDrawable(null); + editText.setVerticalScrollBarEnabled(false); + editText.setHorizontalScrollBarEnabled(false); + editText.setTextIsSelectable(false); + editText.setPadding(0, 0, 0, 0); + editText.setImeOptions(EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI); + editText.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); + spansContainer.addView(editText); + editText.setHintText(LocaleController.getString("Search", R.string.Search)); + hintTextWidth = (int) editText.getPaint().measureText(LocaleController.getString("Search", R.string.Search)); + editText.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + + } + + @Override + public void afterTextChanged(Editable s) { + if (ignoreTextChange) { + return; + } + if (onSearchTextChange != null && s != null) { + onSearchTextChange.run(s.toString()); + } + } + }); + editText.setOnKeyListener(new OnKeyListener() { + boolean wasEmpty; + @Override + public boolean onKey(View v, int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_DEL) { + if (event.getAction() == KeyEvent.ACTION_DOWN) { + wasEmpty = editText.length() == 0; + } else if (event.getAction() == KeyEvent.ACTION_UP && wasEmpty) { + if (!allSpans.isEmpty()) { + GroupCreateSpan lastSpan = allSpans.get(allSpans.size() - 1); + if (lastSpan == null) { + return false; + } + View[] viewPages = viewPager.getViewPages(); + if (viewPages[0] instanceof Page) { + ((Page) viewPages[0]).onClick(lastSpan); + } + if (viewPages[1] instanceof Page) { + ((Page) viewPages[1]).onClick(lastSpan); + } + return true; + } + } + } + return false; + } + }); + } + + private final AnimatedFloat topGradientAlpha = new AnimatedFloat(this, 0, 300, CubicBezierInterpolator.EASE_OUT_QUINT); + private final LinearGradient topGradient = new LinearGradient(0, 0, 0, dp(8), new int[] { 0xff000000, 0x00000000 }, new float[] { 0, 1 }, Shader.TileMode.CLAMP); + private final Paint topGradientPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Matrix topGradientMatrix = new Matrix(); + + private final AnimatedFloat bottomGradientAlpha = new AnimatedFloat(this, 0, 300, CubicBezierInterpolator.EASE_OUT_QUINT); + private final LinearGradient bottomGradient = new LinearGradient(0, 0, 0, dp(8), new int[] { 0x00000000, 0xff000000 }, new float[] { 0, 1 }, Shader.TileMode.CLAMP); + private final Paint bottomGradientPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Matrix bottomGradientMatrix = new Matrix(); + { + topGradientPaint.setShader(topGradient); + topGradientPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); + bottomGradientPaint.setShader(bottomGradient); + bottomGradientPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); + } + + @Override + protected void dispatchDraw(Canvas canvas) { + final int y = getScrollY(); + + canvas.saveLayerAlpha(0, y, getWidth(), y + getHeight(), 0xFF, Canvas.ALL_SAVE_FLAG); + super.dispatchDraw(canvas); + + canvas.save(); + + float alpha = topGradientAlpha.set(canScrollVertically(-1)); + topGradientMatrix.reset(); + topGradientMatrix.postTranslate(0, y); + topGradient.setLocalMatrix(topGradientMatrix); + topGradientPaint.setAlpha((int) (0xFF * alpha)); + canvas.drawRect(0, y, getWidth(), y + dp(8), topGradientPaint); + + alpha = bottomGradientAlpha.set(canScrollVertically(1)); + bottomGradientMatrix.reset(); + bottomGradientMatrix.postTranslate(0, y + getHeight() - dp(8)); + bottomGradient.setLocalMatrix(bottomGradientMatrix); + bottomGradientPaint.setAlpha((int) (0xFF * alpha)); + canvas.drawRect(0, y + getHeight() - dp(8), getWidth(), y + getHeight(), bottomGradientPaint); + + canvas.restore(); + + canvas.restore(); + } + + @Override + public void requestChildFocus(View child, View focused) { + + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { +// if (!AndroidUtilities.findClickableView(this, ev.getX(), ev.getY())) { +// return false; +// } + return super.dispatchTouchEvent(ev); + } + + public void setText(CharSequence text) { + ignoreTextChange = true; + editText.setText(text); + ignoreTextChange = false; + } + + public void setOnSearchTextChange(Utilities.Callback listener) { + this.onSearchTextChange = listener; + } + + private boolean ignoreScrollEvent; + private int fieldY; + public float containerHeight; + public int resultContainerHeight; + private int prevResultContainerHeight; + + @Override + public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { + if (ignoreScrollEvent) { + ignoreScrollEvent = false; + return false; + } + rectangle.offset(child.getLeft() - child.getScrollX(), child.getTop() - child.getScrollY()); + rectangle.top += fieldY + AndroidUtilities.dp(20); + rectangle.bottom += fieldY + AndroidUtilities.dp(50); + return super.requestChildRectangleOnScreen(child, rectangle, immediate); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure( + MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(dp(150), MeasureSpec.AT_MOST) + ); + } + + public void setContainerHeight(float value) { + containerHeight = value; + if (spansContainer != null) { + spansContainer.requestLayout(); + } + } + + private Animator getContainerHeightAnimator(float newHeight) { + ValueAnimator animator = ValueAnimator.ofFloat(this.containerHeight, newHeight); + animator.addUpdateListener(anm -> setContainerHeight((float) anm.getAnimatedValue())); + return animator; + } + + private boolean scroll; + public void scrollToBottom() { + scroll = true; + } + + public class SpansContainer extends ViewGroup { + + private AnimatorSet currentAnimation; + private boolean animationStarted; + private ArrayList animAddingSpans = new ArrayList<>(); + private ArrayList animRemovingSpans = new ArrayList<>(); + private ArrayList animators = new ArrayList<>(); + private View addingSpan; + private final ArrayList removingSpans = new ArrayList<>(); + + private final int padDp = 7; + private final int padYDp = 4; + private final int padXDp = 4; + private final int heightDp = 28; // 32; + + public SpansContainer(Context context) { + super(context); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int count = getChildCount(); + int width = MeasureSpec.getSize(widthMeasureSpec); + int maxWidth = width - AndroidUtilities.dp(padDp * 2); + int currentLineWidth = 0; + int y = AndroidUtilities.dp(10); + int allCurrentLineWidth = 0; + int allY = AndroidUtilities.dp(10); + int x; + for (int a = 0; a < count; a++) { + View child = getChildAt(a); + if (!(child instanceof GroupCreateSpan)) { + continue; + } + child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(dp(heightDp), MeasureSpec.EXACTLY)); + boolean isRemoving = removingSpans.contains(child); + if (!isRemoving && currentLineWidth + child.getMeasuredWidth() > maxWidth) { + y += child.getMeasuredHeight() + dp(padYDp); + currentLineWidth = 0; + } + if (allCurrentLineWidth + child.getMeasuredWidth() > maxWidth) { + allY += child.getMeasuredHeight() + AndroidUtilities.dp(padYDp); + allCurrentLineWidth = 0; + } + x = AndroidUtilities.dp(padDp) + currentLineWidth; + if (!animationStarted) { + if (isRemoving) { + child.setTranslationX(AndroidUtilities.dp(padDp) + allCurrentLineWidth); + child.setTranslationY(allY); + } else if (!removingSpans.isEmpty()) { + if (child.getTranslationX() != x) { + animators.add(ObjectAnimator.ofFloat(child, View.TRANSLATION_X, x)); + } + if (child.getTranslationY() != y) { + animators.add(ObjectAnimator.ofFloat(child, View.TRANSLATION_Y, y)); + } + } else { + child.setTranslationX(x); + child.setTranslationY(y); + } + } + if (!isRemoving) { + currentLineWidth += child.getMeasuredWidth() + AndroidUtilities.dp(padXDp); + } + allCurrentLineWidth += child.getMeasuredWidth() + AndroidUtilities.dp(padXDp); + } + int minWidth; + if (AndroidUtilities.isTablet()) { + minWidth = AndroidUtilities.dp(530 - padDp * 2 - padXDp * 2 - 57 * 2) / 3; + } else { + minWidth = (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) - AndroidUtilities.dp(padDp * 2 + padXDp * 2 + 57 * 2)) / 3; + } + if (maxWidth - currentLineWidth < minWidth) { + currentLineWidth = 0; + y += AndroidUtilities.dp(heightDp + 8); + } + if (maxWidth - allCurrentLineWidth < minWidth) { + allY += AndroidUtilities.dp(heightDp + 8); + } + editText.measure(MeasureSpec.makeMeasureSpec(maxWidth - currentLineWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(heightDp), MeasureSpec.EXACTLY)); + editText.setHintVisible(editText.getMeasuredWidth() > hintTextWidth, true); + if (!animationStarted) { + int currentHeight = allY + AndroidUtilities.dp(heightDp + 10); + int fieldX = currentLineWidth + AndroidUtilities.dp(16); + fieldY = y; + if (currentAnimation != null) { + int resultHeight = y + AndroidUtilities.dp(heightDp + 10); + resultContainerHeight = resultHeight; + if (containerHeight != resultHeight) { + animators.add(getContainerHeightAnimator(resultHeight)); + } + if (editText.getTranslationX() != fieldX) { + animators.add(ObjectAnimator.ofFloat(editText, View.TRANSLATION_X, fieldX)); + } + if (editText.getTranslationY() != fieldY) { + animators.add(ObjectAnimator.ofFloat(editText, View.TRANSLATION_Y, fieldY)); + } + editText.setAllowDrawCursor(false); + currentAnimation.playTogether(animators); + currentAnimation.setDuration(180); + currentAnimation.setInterpolator(new LinearInterpolator()); + currentAnimation.start(); + animationStarted = true; + if (updateHeight != null) { + updateHeight.run(); + } + } else { + containerHeight = resultContainerHeight = currentHeight; + editText.setTranslationX(fieldX); + editText.setTranslationY(fieldY); + if (updateHeight != null) { + updateHeight.run(); + } + } + prevResultContainerHeight = resultContainerHeight; + } else if (currentAnimation != null) { + if (!ignoreScrollEvent && removingSpans.isEmpty()) { + editText.bringPointIntoView(editText.getSelectionStart()); + } + } + if (scroll) { + fullScroll(View.FOCUS_DOWN); + scroll = false; + } + setMeasuredDimension(width, (int) containerHeight); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + int count = getChildCount(); + for (int a = 0; a < count; a++) { + View child = getChildAt(a); + child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight()); + } + } + + public void removeSpan(final GroupCreateSpan span) { + ignoreScrollEvent = true; + allSpans.remove(span); + span.setOnClickListener(null); + + setupEndValues(); + animationStarted = false; + currentAnimation = new AnimatorSet(); + currentAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animator) { + removeView(span); + removingSpans.clear(); + currentAnimation = null; + animationStarted = false; + editText.setAllowDrawCursor(true); + if (updateHeight != null) { + updateHeight.run(); + } + } + }); + removingSpans.clear(); + removingSpans.add(span); + animAddingSpans.clear(); + animRemovingSpans.clear(); + animAddingSpans.add(span); + animators.clear(); + animators.add(ObjectAnimator.ofFloat(span, View.SCALE_X, 1.0f, 0.01f)); + animators.add(ObjectAnimator.ofFloat(span, View.SCALE_Y, 1.0f, 0.01f)); + animators.add(ObjectAnimator.ofFloat(span, View.ALPHA, 1.0f, 0.0f)); + requestLayout(); + } + + public void updateSpans(ArrayList toDelete, ArrayList toAdd, boolean animated) { + ignoreScrollEvent = true; + + allSpans.removeAll(toDelete); + allSpans.addAll(toAdd); + + removingSpans.clear(); + removingSpans.addAll(toDelete); + + for (int i = 0; i < toDelete.size(); ++i) { + toDelete.get(i).setOnClickListener(null); + } + + setupEndValues(); + if (animated) { + animationStarted = false; + currentAnimation = new AnimatorSet(); + currentAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animator) { + for (int i = 0; i < toDelete.size(); ++i) { + removeView(toDelete.get(i)); + } + addingSpan = null; + removingSpans.clear(); + currentAnimation = null; + animationStarted = false; + editText.setAllowDrawCursor(true); + if (updateHeight != null) { + updateHeight.run(); + } + } + }); + animators.clear(); + animAddingSpans.clear(); + animRemovingSpans.clear(); + for (int i = 0; i < toDelete.size(); ++i) { + GroupCreateSpan span = toDelete.get(i); + animRemovingSpans.add(span); + animators.add(ObjectAnimator.ofFloat(span, View.SCALE_X, 1.0f, 0.01f)); + animators.add(ObjectAnimator.ofFloat(span, View.SCALE_Y, 1.0f, 0.01f)); + animators.add(ObjectAnimator.ofFloat(span, View.ALPHA, 1.0f, 0.0f)); + } + for (int i = 0; i < toAdd.size(); ++i) { + GroupCreateSpan addingSpan = toAdd.get(i); + animAddingSpans.add(addingSpan); + animators.add(ObjectAnimator.ofFloat(addingSpan, View.SCALE_X, 0.01f, 1.0f)); + animators.add(ObjectAnimator.ofFloat(addingSpan, View.SCALE_Y, 0.01f, 1.0f)); + animators.add(ObjectAnimator.ofFloat(addingSpan, View.ALPHA, 0.0f, 1.0f)); + } + } else { + for (int i = 0; i < toDelete.size(); ++i) { + removeView(toDelete.get(i)); + } + addingSpan = null; + removingSpans.clear(); + currentAnimation = null; + animationStarted = false; + editText.setAllowDrawCursor(true); + } + for (int i = 0; i < toAdd.size(); ++i) { + addView(toAdd.get(i)); + } + requestLayout(); + } + + public void removeAllSpans(boolean animated) { + ignoreScrollEvent = true; + + ArrayList spans = new ArrayList<>(allSpans); + removingSpans.clear(); + removingSpans.addAll(allSpans); + allSpans.clear(); + + for (int i = 0; i < spans.size(); ++i) { + spans.get(i).setOnClickListener(null); + } + + setupEndValues(); + if (animated) { + animationStarted = false; + currentAnimation = new AnimatorSet(); + currentAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animator) { + for (int i = 0; i < spans.size(); ++i) { + removeView(spans.get(i)); + } + removingSpans.clear(); + currentAnimation = null; + animationStarted = false; + editText.setAllowDrawCursor(true); + if (updateHeight != null) { + updateHeight.run(); + } + } + }); + animators.clear(); + animAddingSpans.clear(); + animRemovingSpans.clear(); + for (int i = 0; i < spans.size(); ++i) { + GroupCreateSpan span = spans.get(i); + animAddingSpans.add(span); + animators.add(ObjectAnimator.ofFloat(span, View.SCALE_X, 1.0f, 0.01f)); + animators.add(ObjectAnimator.ofFloat(span, View.SCALE_Y, 1.0f, 0.01f)); + animators.add(ObjectAnimator.ofFloat(span, View.ALPHA, 1.0f, 0.0f)); + } + } else { + for (int i = 0; i < spans.size(); ++i) { + removeView(spans.get(i)); + } + removingSpans.clear(); + currentAnimation = null; + animationStarted = false; + editText.setAllowDrawCursor(true); + } + requestLayout(); + } + + private void setupEndValues() { + if (currentAnimation != null) { + currentAnimation.cancel(); + } + for (int i = 0; i < animAddingSpans.size(); ++i) { + animAddingSpans.get(i).setScaleX(1f); + animAddingSpans.get(i).setScaleY(1f); + animAddingSpans.get(i).setAlpha(1f); + } + for (int i = 0; i < animRemovingSpans.size(); ++i) { + animRemovingSpans.get(i).setScaleX(0f); + animRemovingSpans.get(i).setScaleY(0f); + animRemovingSpans.get(i).setAlpha(0f); + } + animAddingSpans.clear(); + animRemovingSpans.clear(); + } + } + } + + public static class StoryPrivacy { + public final int type; + + public final ArrayList rules = new ArrayList<>(); + public final ArrayList selectedUserIds = new ArrayList<>(); + public final HashMap> selectedUserIdsByGroup = new HashMap(); + public final ArrayList selectedInputUsers = new ArrayList<>(); + public final ArrayList sendToUsers = new ArrayList<>(); + + public StoryPrivacy(int currentAccount, ArrayList rules) { + TLRPC.TL_privacyValueAllowUsers allowUsers; + if (containsRule(rules, TLRPC.TL_privacyValueAllowAll.class) != null) { + type = TYPE_EVERYONE; + this.rules.add(new TLRPC.TL_inputPrivacyValueAllowAll()); + } else if (containsRule(rules, TLRPC.TL_privacyValueAllowCloseFriends.class) != null) { + type = TYPE_CLOSE_FRIENDS; + this.rules.add(new TLRPC.TL_inputPrivacyValueAllowCloseFriends()); + } else if ((allowUsers = containsRule(rules, TLRPC.TL_privacyValueAllowUsers.class)) != null) { + type = TYPE_SELECTED_CONTACTS; + + final TLRPC.TL_inputPrivacyValueAllowUsers rule = new TLRPC.TL_inputPrivacyValueAllowUsers(); + final MessagesController messagesController = MessagesController.getInstance(currentAccount); + for (int i = 0; i < allowUsers.users.size(); ++i) { + long userId = allowUsers.users.get(i); + TLRPC.InputUser inputUser = messagesController.getInputUser(userId); + if (!(inputUser instanceof TLRPC.TL_inputUserEmpty)) { + rule.users.add(inputUser); + selectedUserIds.add(userId); + selectedInputUsers.add(inputUser); + } + } + this.rules.add(rule); + } else if (containsRule(rules, TLRPC.TL_privacyValueAllowContacts.class) != null) { + type = TYPE_CONTACTS; + this.rules.add(new TLRPC.TL_inputPrivacyValueAllowContacts()); + + TLRPC.TL_privacyValueDisallowUsers disallowUsers = containsRule(rules, TLRPC.TL_privacyValueDisallowUsers.class); + if (disallowUsers != null) { + final TLRPC.TL_inputPrivacyValueDisallowUsers rule = new TLRPC.TL_inputPrivacyValueDisallowUsers(); + final MessagesController messagesController = MessagesController.getInstance(currentAccount); + for (int i = 0; i < disallowUsers.users.size(); ++i) { + long userId = disallowUsers.users.get(i); + TLRPC.InputUser inputUser = messagesController.getInputUser(userId); + if (!(inputUser instanceof TLRPC.TL_inputUserEmpty)) { + rule.users.add(inputUser); + selectedUserIds.add(userId); + selectedInputUsers.add(inputUser); + } + } + this.rules.add(rule); + } + } else { + type = TYPE_EVERYONE; + } + } + + public StoryPrivacy(ArrayList rules) { + this.rules.addAll(rules); + TLRPC.TL_inputPrivacyValueAllowUsers allowUsers; + if (containsInputRule(rules, TLRPC.TL_inputPrivacyValueAllowAll.class) != null) { + type = TYPE_EVERYONE; + } else if (containsInputRule(rules, TLRPC.TL_inputPrivacyValueAllowCloseFriends.class) != null) { + type = TYPE_CLOSE_FRIENDS; + } else if ((allowUsers = containsInputRule(rules, TLRPC.TL_inputPrivacyValueAllowUsers.class)) != null) { + type = TYPE_SELECTED_CONTACTS; + for (int i = 0; i < allowUsers.users.size(); ++i) { + TLRPC.InputUser inputUser = allowUsers.users.get(i); + if (inputUser != null) { + selectedUserIds.add(inputUser.user_id); + selectedInputUsers.add(inputUser); + } + } + } else if (containsInputRule(rules, TLRPC.TL_inputPrivacyValueAllowContacts.class) != null) { + type = TYPE_CONTACTS; + TLRPC.TL_inputPrivacyValueDisallowUsers disallowUsers = containsInputRule(rules, TLRPC.TL_inputPrivacyValueDisallowUsers.class); + if (disallowUsers != null) { + for (int i = 0; i < disallowUsers.users.size(); ++i) { + TLRPC.InputUser inputUser = disallowUsers.users.get(i); + if (inputUser != null) { + selectedUserIds.add(inputUser.user_id); + selectedInputUsers.add(inputUser); + } + } + } + } else { + type = TYPE_EVERYONE; + } + } + + private T containsRule(ArrayList rules, Class clazz) { + for (int i = 0; i < rules.size(); ++i) { + TLRPC.PrivacyRule rule = rules.get(i); + if (clazz.isInstance(rule)) { + return (T) rule; + } + } + return null; + } + + private T containsInputRule(ArrayList rules, Class clazz) { + for (int i = 0; i < rules.size(); ++i) { + TLRPC.InputPrivacyRule rule = rules.get(i); + if (clazz.isInstance(rule)) { + return (T) rule; + } + } + return null; + } + + public StoryPrivacy() { + this.type = TYPE_EVERYONE; + this.rules.add(new TLRPC.TL_inputPrivacyValueAllowAll()); + } + + public StoryPrivacy(int type, int currentAccount, ArrayList userIds) { + this.type = type; + if (type == TYPE_EVERYONE) { + this.rules.add(new TLRPC.TL_inputPrivacyValueAllowAll()); + } else if (type == TYPE_CLOSE_FRIENDS) { + this.rules.add(new TLRPC.TL_inputPrivacyValueAllowCloseFriends()); + } else if (type == TYPE_CONTACTS) { + this.rules.add(new TLRPC.TL_inputPrivacyValueAllowContacts()); + if (currentAccount >= 0 && userIds != null && !userIds.isEmpty()) { + final TLRPC.TL_inputPrivacyValueDisallowUsers rule = new TLRPC.TL_inputPrivacyValueDisallowUsers(); + for (int i = 0; i < userIds.size(); ++i) { + long userId = userIds.get(i); + selectedUserIds.add(userId); + TLRPC.InputUser user = MessagesController.getInstance(currentAccount).getInputUser(userId); + if (user != null && !(user instanceof TLRPC.TL_inputUserEmpty)) { + rule.users.add(user); + selectedInputUsers.add(user); + } + } + this.rules.add(rule); + } + } else if (type == TYPE_SELECTED_CONTACTS) { + if (currentAccount >= 0 && userIds != null && !userIds.isEmpty()) { + final TLRPC.TL_inputPrivacyValueAllowUsers rule = new TLRPC.TL_inputPrivacyValueAllowUsers(); + for (int i = 0; i < userIds.size(); ++i) { + long userId = userIds.get(i); + selectedUserIds.add(userId); + TLRPC.InputUser user = MessagesController.getInstance(currentAccount).getInputUser(userId); + if (user != null && !(user instanceof TLRPC.TL_inputUserEmpty)) { + rule.users.add(user); + selectedInputUsers.add(user); + } + } + this.rules.add(rule); + } + } else if (type == TYPE_AS_MESSAGE) { + if (userIds != null) { + this.sendToUsers.addAll(userIds); + } + } + } + + public StoryPrivacy(int type, ArrayList inputUserIds, int a) { + this.type = type; + if (type == TYPE_EVERYONE) { + this.rules.add(new TLRPC.TL_inputPrivacyValueAllowAll()); + } else if (type == TYPE_CLOSE_FRIENDS) { + this.rules.add(new TLRPC.TL_inputPrivacyValueAllowCloseFriends()); + } else if (type == TYPE_CONTACTS) { + this.rules.add(new TLRPC.TL_inputPrivacyValueAllowContacts()); + if (inputUserIds != null && !inputUserIds.isEmpty()) { + final TLRPC.TL_inputPrivacyValueDisallowUsers rule = new TLRPC.TL_inputPrivacyValueDisallowUsers(); + for (int i = 0; i < inputUserIds.size(); ++i) { + TLRPC.InputUser user = inputUserIds.get(i); + if (user != null) { + rule.users.add(user); + selectedUserIds.add(user.user_id); + selectedInputUsers.add(user); + } + } + this.rules.add(rule); + } + } else if (type == TYPE_SELECTED_CONTACTS) { + if (inputUserIds != null && !inputUserIds.isEmpty()) { + final TLRPC.TL_inputPrivacyValueAllowUsers rule = new TLRPC.TL_inputPrivacyValueAllowUsers(); + for (int i = 0; i < inputUserIds.size(); ++i) { + TLRPC.InputUser user = inputUserIds.get(i); + if (user != null) { + rule.users.add(user); + selectedUserIds.add(user.user_id); + selectedInputUsers.add(user); + } + } + this.rules.add(rule); + } + } else if (type == TYPE_AS_MESSAGE) { + if (inputUserIds != null) { + for (int i = 0; i < inputUserIds.size(); ++i) { + TLRPC.InputUser user = inputUserIds.get(i); + if (user != null) { + this.sendToUsers.add(user.user_id); + } + } + } + } + } + + public boolean isShare() { + return type == TYPE_AS_MESSAGE; + } + + public boolean isNone() { + return sendToUsers.isEmpty() && rules.isEmpty(); + } + + public boolean isCloseFriends() { + return type == TYPE_CLOSE_FRIENDS; + } + + @NonNull + @Override + public String toString() { + if (!sendToUsers.isEmpty()) { + return LocaleController.formatPluralString("StoryPrivacyRecipients", sendToUsers.size()); + } + if (rules.isEmpty()) { + return LocaleController.getString("StoryPrivacyNone", R.string.StoryPrivacyNone); + } + TLRPC.InputPrivacyRule rule1 = rules.get(0); + if (type == TYPE_EVERYONE) { + return LocaleController.getString("StoryPrivacyEveryone", R.string.StoryPrivacyEveryone); + } else if (type == TYPE_CLOSE_FRIENDS) { + return LocaleController.getString("StoryPrivacyCloseFriends", R.string.StoryPrivacyCloseFriends); + } else if (type == TYPE_SELECTED_CONTACTS && rule1 instanceof TLRPC.TL_inputPrivacyValueAllowUsers) { + final int usersCount = ((TLRPC.TL_inputPrivacyValueAllowUsers) rule1).users.size(); + return LocaleController.formatPluralString("StoryPrivacyContacts", usersCount); + } else if (type == TYPE_CONTACTS) { + TLRPC.InputPrivacyRule rule2 = rules.size() >= 2 ? rules.get(1) : null; + if (rule2 instanceof TLRPC.TL_inputPrivacyValueDisallowUsers) { + final int usersCount = ((TLRPC.TL_inputPrivacyValueDisallowUsers) rule2).users.size(); + if (usersCount > 0) { + return LocaleController.formatPluralString("StoryPrivacyContactsExclude", usersCount); + } else { + return LocaleController.getString("StoryPrivacyAllContacts", R.string.StoryPrivacyAllContacts); + } + } else { + return LocaleController.getString("StoryPrivacyAllContacts", R.string.StoryPrivacyAllContacts); + } + } else if (type == 0) { + if (rule1 instanceof TLRPC.TL_inputPrivacyValueAllowUsers) { + final int usersCount = ((TLRPC.TL_inputPrivacyValueAllowUsers) rule1).users.size(); + if (usersCount <= 0) { + return LocaleController.getString("StoryPrivacyNone", R.string.StoryPrivacyNone); + } else { + return LocaleController.formatPluralString("StoryPrivacyContacts", usersCount); + } + } else { + return LocaleController.getString("StoryPrivacyNone", R.string.StoryPrivacyNone); + } + } + return LocaleController.getString("StoryPrivacyNone", R.string.StoryPrivacyNone); + } + + public ArrayList toValue() { + ArrayList result = new ArrayList<>(); + for (int i = 0; i < rules.size(); ++i) { + TLRPC.InputPrivacyRule inputPrivacyRule = rules.get(i); + if (inputPrivacyRule instanceof TLRPC.TL_inputPrivacyValueAllowAll) { + result.add(new TLRPC.TL_privacyValueAllowAll()); + } else if (inputPrivacyRule instanceof TLRPC.TL_inputPrivacyValueAllowCloseFriends) { + result.add(new TLRPC.TL_privacyValueAllowCloseFriends()); + } else if (inputPrivacyRule instanceof TLRPC.TL_inputPrivacyValueAllowContacts) { + result.add(new TLRPC.TL_privacyValueAllowContacts()); + } else if (inputPrivacyRule instanceof TLRPC.TL_inputPrivacyValueDisallowUsers) { + TLRPC.TL_inputPrivacyValueDisallowUsers inputRule = (TLRPC.TL_inputPrivacyValueDisallowUsers) inputPrivacyRule; + TLRPC.TL_privacyValueDisallowUsers rule = new TLRPC.TL_privacyValueDisallowUsers(); + for (int j = 0; j < inputRule.users.size(); ++j) { + rule.users.add(inputRule.users.get(j).user_id); + } + result.add(rule); + } else if (inputPrivacyRule instanceof TLRPC.TL_inputPrivacyValueAllowUsers) { + TLRPC.TL_inputPrivacyValueAllowUsers inputRule = (TLRPC.TL_inputPrivacyValueAllowUsers) inputPrivacyRule; + TLRPC.TL_privacyValueAllowUsers rule = new TLRPC.TL_privacyValueAllowUsers(); + for (int j = 0; j < inputRule.users.size(); ++j) { + rule.users.add(inputRule.users.get(j).user_id); + } + result.add(rule); + } + } + return result; + } + + public static ArrayList toInput(int currentAccount, ArrayList rules) { + MessagesController messagesController = MessagesController.getInstance(currentAccount); + final ArrayList arr = new ArrayList<>(); + for (int i = 0; i < rules.size(); ++i) { + TLRPC.PrivacyRule rule = rules.get(i); + if (rule == null) { + continue; + } + if (rule instanceof TLRPC.TL_privacyValueAllowAll) { + arr.add(new TLRPC.TL_inputPrivacyValueAllowAll()); + } else if (rule instanceof TLRPC.TL_privacyValueAllowCloseFriends) { + arr.add(new TLRPC.TL_inputPrivacyValueAllowCloseFriends()); + } else if (rule instanceof TLRPC.TL_privacyValueAllowContacts) { + arr.add(new TLRPC.TL_inputPrivacyValueAllowContacts()); + } else if (rule instanceof TLRPC.TL_privacyValueDisallowUsers) { + TLRPC.TL_privacyValueDisallowUsers rule2 = (TLRPC.TL_privacyValueDisallowUsers) rule; + TLRPC.TL_inputPrivacyValueDisallowUsers inputRule = new TLRPC.TL_inputPrivacyValueDisallowUsers(); + for (int j = 0; j < rule2.users.size(); ++j) { + TLRPC.InputUser user = messagesController.getInputUser(rule2.users.get(j)); + if (!(user instanceof TLRPC.TL_inputUserEmpty)) { + inputRule.users.add(user); + } + } + arr.add(inputRule); + } else if (rule instanceof TLRPC.TL_privacyValueAllowUsers) { + TLRPC.TL_privacyValueAllowUsers rule2 = (TLRPC.TL_privacyValueAllowUsers) rule; + TLRPC.TL_inputPrivacyValueAllowUsers inputRule = new TLRPC.TL_inputPrivacyValueAllowUsers(); + for (int j = 0; j < rule2.users.size(); ++j) { + TLRPC.InputUser user = messagesController.getInputUser(rule2.users.get(j)); + if (!(user instanceof TLRPC.TL_inputUserEmpty)) { + inputRule.users.add(user); + } + } + arr.add(inputRule); + } + } + return arr; + } + + public static ArrayList toOutput(ArrayList rules) { + final ArrayList arr = new ArrayList<>(); + for (int i = 0; i < rules.size(); ++i) { + TLRPC.InputPrivacyRule rule = rules.get(i); + if (rule == null) { + continue; + } + if (rule instanceof TLRPC.TL_inputPrivacyValueAllowAll) { + arr.add(new TLRPC.TL_privacyValueAllowAll()); + } else if (rule instanceof TLRPC.TL_inputPrivacyValueAllowCloseFriends) { + arr.add(new TLRPC.TL_privacyValueAllowCloseFriends()); + } else if (rule instanceof TLRPC.TL_inputPrivacyValueAllowContacts) { + arr.add(new TLRPC.TL_privacyValueAllowContacts()); + } else if (rule instanceof TLRPC.TL_inputPrivacyValueDisallowUsers) { + TLRPC.TL_inputPrivacyValueDisallowUsers rule2 = (TLRPC.TL_inputPrivacyValueDisallowUsers) rule; + TLRPC.TL_privacyValueDisallowUsers outputRule = new TLRPC.TL_privacyValueDisallowUsers(); + for (int j = 0; j < rule2.users.size(); ++j) { + outputRule.users.add(rule2.users.get(j).user_id); + } + arr.add(outputRule); + } else if (rule instanceof TLRPC.TL_inputPrivacyValueAllowUsers) { + TLRPC.TL_inputPrivacyValueAllowUsers rule2 = (TLRPC.TL_inputPrivacyValueAllowUsers) rule; + TLRPC.TL_privacyValueAllowUsers outputRule = new TLRPC.TL_privacyValueAllowUsers(); + for (int j = 0; j < rule2.users.size(); ++j) { + outputRule.users.add(rule2.users.get(j).user_id); + } + arr.add(outputRule); + } + } + return arr; + } + + public boolean containsUser(TLRPC.User user) { + if (user == null) { + return false; + } + if (type == TYPE_EVERYONE) { + return true; + } else if (type == TYPE_CONTACTS) { + return !selectedUserIds.contains(user.id); + } else if (type == TYPE_CLOSE_FRIENDS) { + return user.close_friend; + } else if (type == TYPE_SELECTED_CONTACTS) { + return selectedUserIds.contains(user.id); + } + return false; + } + } + + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.contactsDidLoad && viewPager != null) { + View[] views = viewPager.getViewPages(); + if (views[0] instanceof Page) { + ((Page) views[0]).updateItems(true); + } + if (views[1] instanceof Page) { + ((Page) views[1]).updateItems(true); + } + } + } + + private void pullSaved() { + String selectedContactsString = MessagesController.getInstance(currentAccount).getMainSettings().getString("story_prv_contacts", null); + if (selectedContactsString != null) { + String[] parts = selectedContactsString.split(","); + selectedContacts.clear(); + for (int i = 0; i < parts.length; ++i) { + try { + selectedContacts.add(Long.parseLong(parts[i])); + } catch (Exception ignore) {} + } + } + + String selectedContactsGroupsString = MessagesController.getInstance(currentAccount).getMainSettings().getString("story_prv_grpcontacts", null); + if (selectedContactsGroupsString != null) { + String[] parts = selectedContactsGroupsString.split(";"); + selectedContactsByGroup.clear(); + for (int i = 0; i < parts.length; ++i) { + String[] parts2 = parts[i].split(","); + if (parts2.length <= 0) { + continue; + } + long id; + try { + id = Long.parseLong(parts2[0]); + } catch (Exception ignore) { + continue; + } + ArrayList userIds = new ArrayList<>(); + for (int j = 1; j < parts2.length; ++j) { + userIds.add(Long.parseLong(parts2[j])); + } + selectedContactsByGroup.put(id, userIds); + } + } + + String excludedContactsString = MessagesController.getInstance(currentAccount).getMainSettings().getString("story_prv_excluded", null); + if (excludedContactsString != null) { + String[] parts = excludedContactsString.split(","); + excludedContacts.clear(); + for (int i = 0; i < parts.length; ++i) { + try { + excludedContacts.add(Long.parseLong(parts[i])); + } catch (Exception ignore) {} + } + } + + selectedContactsCount = mergeUsers(selectedContacts, selectedContactsByGroup).size(); + + allowScreenshots = !MessagesController.getInstance(currentAccount).getMainSettings().getBoolean("story_noforwards", false); + keepOnMyPage = MessagesController.getInstance(currentAccount).getMainSettings().getBoolean("story_keep", false); + } + + private void save() { + StringBuilder stringBuilder = new StringBuilder(); + for (Map.Entry> entry : selectedContactsByGroup.entrySet()) { + if (stringBuilder.length() > 0) { + stringBuilder.append(";"); + } + stringBuilder.append(entry.getKey()).append(",").append(TextUtils.join(",", entry.getValue())); + } + MessagesController.getInstance(currentAccount).getMainSettings().edit() + .putString("story_prv_contacts", TextUtils.join(",", selectedContacts)) + .putString("story_prv_grpcontacts", stringBuilder.toString()) + .putString("story_prv_excluded", TextUtils.join(",", excludedContacts)) + .putBoolean("story_noforwards", !allowScreenshots) + .putBoolean("story_keep", keepOnMyPage) + .apply(); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/StoryPrivacySelector.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/StoryPrivacySelector.java new file mode 100644 index 0000000000..dbb78bafe7 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/StoryPrivacySelector.java @@ -0,0 +1,509 @@ +package org.telegram.ui.Stories.recorder; + +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.AndroidUtilities.dpf2; + +import android.content.Context; +import android.content.SharedPreferences; +import android.graphics.Canvas; +import android.graphics.CornerPathEffect; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.RippleDrawable; +import android.os.Build; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.text.TextUtils; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.HapticFeedbackConstants; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.MessagesStorage; +import org.telegram.messenger.R; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.Utilities; +import org.telegram.tgnet.AbstractSerializedData; +import org.telegram.tgnet.NativeByteBuffer; +import org.telegram.tgnet.SerializedData; +import org.telegram.tgnet.TLObject; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AnimatedTextView; +import org.telegram.ui.Components.ButtonBounce; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.HintView; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + +public class StoryPrivacySelector extends View { + + private final Theme.ResourcesProvider resourcesProvider; + private final int currentAccount; + + private final Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private Drawable rippleDrawable; + + private final AnimatedTextView.AnimatedTextDrawable textDrawable = new AnimatedTextView.AnimatedTextDrawable(true, true, false); + + @NonNull + private StoryPrivacyBottomSheet.StoryPrivacy value = new StoryPrivacyBottomSheet.StoryPrivacy(); + + public StoryPrivacySelector(Context context, int currentAccount, Theme.ResourcesProvider resourcesProvider) { + super(context); + + this.currentAccount = currentAccount; + this.resourcesProvider = resourcesProvider; + + backgroundPaint.setColor(0x4d000000); + + textDrawable.setCallback(this); + textDrawable.setAnimationProperties(.55f, 0, 460, CubicBezierInterpolator.EASE_OUT_QUINT); + textDrawable.setTextColor(0xffffffff); + textDrawable.setTextSize(dpf2(14)); + textDrawable.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + textDrawable.setGravity(Gravity.CENTER); + + textDrawable.setText(value.toString()); + } + + private final RectF rect = new RectF(); + private final RectF clickRect = new RectF(); + + @Override + protected void dispatchDraw(Canvas canvas) { + final float width = textDrawable.getCurrentWidth() + dp(13 + 13); + rect.set( + (getWidth() - width) / 2f, + dp(13), + (getWidth() + width) / 2f, + dp(13 + 30) + ); + clickRect.set(rect); + clickRect.inset(-dp(28), -dp(14)); + canvas.drawRoundRect(rect, dp(15), dp(15), backgroundPaint); + + if (rippleDrawable != null) { + rippleDrawable.setBounds((int) rect.left, (int) rect.top, (int) rect.right, (int) rect.bottom); + rippleDrawable.draw(canvas); + } + + textDrawable.setBounds(0, -dp(1), getWidth(), getHeight() - dp(1)); + textDrawable.draw(canvas); + } + + private long tapTime; + private Runnable longPressRunnable; + + @Override + public boolean onTouchEvent(MotionEvent event) { + boolean hit = clickRect.contains(event.getX(), event.getY()); + if (event.getAction() == MotionEvent.ACTION_DOWN) { + if (longPressRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(longPressRunnable); + longPressRunnable = null; + } + if (hit) { + rippleDrawable = Theme.createRadSelectorDrawable(234881023, dp(15), dp(15)); + rippleDrawable.setBounds((int) rect.left, (int) rect.top, (int) rect.right, (int) rect.bottom); + rippleDrawable.setState(new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled}); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + rippleDrawable.setHotspot(event.getX(), event.getY()); + } + rippleDrawable.setCallback(this); + tapTime = System.currentTimeMillis(); + AndroidUtilities.runOnUIThread(longPressRunnable = () -> { + if (rippleDrawable != null) { + rippleDrawable.setState(new int[]{}); + } + tapTime = -1; + open(); + try { + performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); + } catch (Exception ignore) {} + }, ViewConfiguration.getLongPressTimeout()); + invalidate(); + return true; + } else { + tapTime = -1; + } + } else if (event.getAction() == MotionEvent.ACTION_UP) { + if (longPressRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(longPressRunnable); + longPressRunnable = null; + } + if (hit && rippleDrawable != null && System.currentTimeMillis() - tapTime <= ViewConfiguration.getTapTimeout()) { + open(); + } + if (rippleDrawable != null) { + rippleDrawable.setState(new int[]{}); + } + } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { + if (longPressRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(longPressRunnable); + longPressRunnable = null; + } + if (rippleDrawable != null) { + rippleDrawable.setState(new int[]{}); + } + } + return super.onTouchEvent(event); + } + + private boolean edited; + public boolean isEdited() { + return edited; + } + + public void open() { + onPopupOpen(() -> { + StoryPrivacyBottomSheet sheet = new StoryPrivacyBottomSheet(getContext(), storyPeriod, resourcesProvider); + sheet.setValue(getStoryPrivacy()); + sheet.isEdit(false); + sheet.whenSelectedRules((privacy, a, b, whenDone) -> { + edited = true; + CharSequence text = (value = privacy).toString(); + textDrawable.setText(text); + setContentDescription(text); + save(currentAccount, value); + if (whenDone != null) { + whenDone.run(); + } + }, true); + sheet.setOnDismissListener(d -> onPopupClose()); + sheet.show(); + }); + } + + protected void onPopupOpen(Runnable after) {} + protected void onPopupClose() {} + + public void set(StoryEntry entry, boolean animated) { + edited = false; + if (entry.privacy != null) { + value = entry.privacy; + } else { + value = new StoryPrivacyBottomSheet.StoryPrivacy(entry.privacyRules); + } + textDrawable.setText(value.toString(), animated && !LocaleController.isRTL); + } + + private int storyPeriod = 86400; + public void setStoryPeriod(int period) { + this.storyPeriod = period; + } + + @NonNull + public StoryPrivacyBottomSheet.StoryPrivacy getStoryPrivacy() { + return value; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(dp(56), MeasureSpec.EXACTLY)); + } + + @Override + protected boolean verifyDrawable(@NonNull Drawable who) { + return who == textDrawable || who == rippleDrawable || super.verifyDrawable(who); + } + + public static class StoryPrivacyHint extends View { + + private final Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + + private final StaticLayout layout; + private final float layoutWidth, layoutLeft; + + private final Drawable closeDrawable; + private final Rect clickCloseRect = new Rect(); + private final ButtonBounce closeBounce = new ButtonBounce(this); + + private final Path path = new Path(); + + public StoryPrivacyHint(Context context) { + super(context); + + backgroundPaint.setColor(0x4d000000); + backgroundPaint.setPathEffect(new CornerPathEffect(dp(6))); + + textPaint.setColor(0xffffffff); + textPaint.setTextSize(dp(14)); + + CharSequence text = LocaleController.getString("StoryPrivacyHint", R.string.StoryPrivacyHint); + layout = new StaticLayout(text, textPaint, AndroidUtilities.displaySize.x, Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false); + layoutLeft = layout.getLineCount() > 0 ? layout.getLineLeft(0) : 0; + layoutWidth = layout.getLineCount() > 0 ? layout.getLineWidth(0) : 0; + + closeDrawable = getContext().getResources().getDrawable(R.drawable.msg_mini_close_tooltip); + closeDrawable.setColorFilter(new PorterDuffColorFilter(Theme.multAlpha(0xffffffff, .6f), PorterDuff.Mode.MULTIPLY)); + + setAlpha(0f); + setTranslationY(dp(6)); + setVisibility(View.GONE); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int width = dp(11 + 8 + 11) + (int) layoutWidth + closeDrawable.getIntrinsicWidth(); + final int height = dp(8 + 8 + 6) + layout.getHeight(); + setMeasuredDimension(width, height); + + path.rewind(); + path.moveTo(0, dp(6)); + path.lineTo(0, height); + path.lineTo(width, height); + path.lineTo(width, dp(6)); + path.lineTo(width / 2f + dp(7), dp(6)); + path.lineTo(width / 2f, -dp(2)); + path.lineTo(width / 2f - dp(7), dp(6)); + path.close(); + } + + @Override + public void draw(Canvas canvas) { + super.draw(canvas); + + canvas.drawPath(path, backgroundPaint); + + canvas.save(); + canvas.translate(dp(11) - layoutLeft, dp(6 + 8)); + layout.draw(canvas); + canvas.restore(); + + closeDrawable.setBounds( + getWidth() - dp(11) - closeDrawable.getIntrinsicWidth(), + dp(6) + (getHeight() - dp(6) - closeDrawable.getIntrinsicHeight()) / 2, + getWidth() - dp(11), + dp(6) + (getHeight() - dp(6) + closeDrawable.getIntrinsicHeight()) / 2 + ); + clickCloseRect.set(closeDrawable.getBounds()); + clickCloseRect.inset(-dp(10), -dp(6)); + canvas.save(); + float scale = closeBounce.getScale(.12f); + canvas.scale(scale, scale, closeDrawable.getBounds().centerX(), closeDrawable.getBounds().centerY()); + closeDrawable.draw(canvas); + canvas.restore(); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (getAlpha() < 1 || getVisibility() != View.VISIBLE) { + return false; + } + boolean hitClose = clickCloseRect.contains((int) event.getX(), (int) event.getY()); + if (event.getAction() == MotionEvent.ACTION_DOWN && hitClose) { + closeBounce.setPressed(true); + if (hideRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(hideRunnable); + hideRunnable = null; + } + return true; + } else if (event.getAction() == MotionEvent.ACTION_UP) { + closeBounce.setPressed(false); + hide(); + neverShow(); + } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { + closeBounce.setPressed(false); + if (hideRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(hideRunnable); + hideRunnable = null; + } + AndroidUtilities.runOnUIThread(hideRunnable = this::hide, 5000); + } + return super.onTouchEvent(event); + } + + private Runnable hideRunnable; + + public void show(boolean force) { + if (!shouldShow() && !force) { + return; + } + + clearAnimation(); + setVisibility(View.VISIBLE); + setAlpha(1f); + setTranslationY(dp(6)); + animate().alpha(1f).translationY(0).setInterpolator(CubicBezierInterpolator.EASE_OUT_BACK).start(); + + if (hideRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(hideRunnable); + } +// AndroidUtilities.runOnUIThread(hideRunnable = this::hide, 5000); + } + + public void hide() { + if (hideRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(hideRunnable); + hideRunnable = null; + } + clearAnimation(); + animate().alpha(0).translationY(-dp(8)).withEndAction(() -> setVisibility(View.GONE)).start(); + } + + private boolean shouldShow() { + return !MessagesController.getGlobalMainSettings().getBoolean("storyprvhint", false); + } + + public static void neverShow() { + MessagesController.getGlobalMainSettings().edit().putBoolean("storyprvhint", true).apply(); + } + } + + private static StoryPrivacyBottomSheet.StoryPrivacy read(AbstractSerializedData stream) { + final int type = stream.readInt32(true); + + if (stream.readInt32(true) != 0x1cb5c415) { + throw new RuntimeException("wrong Vector magic in TL_StoryPrivacy"); + } + int count = stream.readInt32(true); + final ArrayList inputUsers = new ArrayList<>(count); + for (int i = 0; i < count; ++i) { + inputUsers.add(TLRPC.InputUser.TLdeserialize(stream, stream.readInt32(true), true)); + } + + if (stream.readInt32(true) != 0x1cb5c415) { + throw new RuntimeException("wrong Vector magic in TL_StoryPrivacy (2)"); + } + count = stream.readInt32(true); + final ArrayList userIds = new ArrayList<>(count); + for (int i = 0; i < count; ++i) { + userIds.add(stream.readInt64(true)); + } + + if (stream.readInt32(true) != 0x1cb5c415) { + throw new RuntimeException("wrong Vector magic in TL_StoryPrivacy (3)"); + } + count = stream.readInt32(true); + final HashMap> userIdsByGroup = new HashMap<>(); + for (int i = 0; i < count; ++i) { + long key = stream.readInt64(true); + if (stream.readInt32(true) != 0x1cb5c415) { + throw new RuntimeException("wrong Vector magic in TL_StoryPrivacy (4)"); + } + final int count2 = stream.readInt32(true); + final ArrayList groupUserIds = new ArrayList<>(count2); + for (int j = 0; j < count2; ++j) { + groupUserIds.add(stream.readInt64(true)); + } + userIdsByGroup.put(key, groupUserIds); + } + + HashSet allUserIds = new HashSet<>(); + allUserIds.addAll(userIds); + for (ArrayList groupUserIds : userIdsByGroup.values()) { + allUserIds.addAll(groupUserIds); + } + + StoryPrivacyBottomSheet.StoryPrivacy privacy = new StoryPrivacyBottomSheet.StoryPrivacy(type, inputUsers, 0); + privacy.selectedUserIds.clear(); + privacy.selectedUserIds.addAll(userIds); + privacy.selectedUserIdsByGroup.clear(); + privacy.selectedUserIdsByGroup.putAll(userIdsByGroup); + return privacy; + } + + private static void write(AbstractSerializedData stream, StoryPrivacyBottomSheet.StoryPrivacy privacy) { + stream.writeInt32(privacy.type); + stream.writeInt32(0x1cb5c415); + stream.writeInt32(privacy.selectedInputUsers.size()); + for (TLRPC.InputUser inputUser : privacy.selectedInputUsers) { + inputUser.serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + stream.writeInt32(privacy.selectedUserIds.size()); + for (long id : privacy.selectedUserIds) { + stream.writeInt64(id); + } + stream.writeInt32(0x1cb5c415); + stream.writeInt32(privacy.selectedUserIdsByGroup.size()); + for (Map.Entry> entry : privacy.selectedUserIdsByGroup.entrySet()) { + stream.writeInt64(entry.getKey()); + stream.writeInt32(0x1cb5c415); + stream.writeInt32(entry.getValue().size()); + for (long id : entry.getValue()) { + stream.writeInt64(id); + } + } + } + + public static void save(int currentAccount, StoryPrivacyBottomSheet.StoryPrivacy privacy) { + if (privacy == null) { + MessagesController.getInstance(currentAccount).getMainSettings().edit().remove("story_privacy2").apply(); + return; + } + SerializedData calc = new SerializedData(true); + write(calc, privacy); + SerializedData data = new SerializedData(calc.length()); + calc.cleanup(); + write(data, privacy); + SharedPreferences prefs = MessagesController.getInstance(currentAccount).getMainSettings(); + prefs.edit().putString("story_privacy2", Utilities.bytesToHex(data.toByteArray())).apply(); + data.cleanup(); + } + + private static StoryPrivacyBottomSheet.StoryPrivacy getSaved(int currentAccount) { + try { + SharedPreferences prefs = MessagesController.getInstance(currentAccount).getMainSettings(); + String hex = prefs.getString("story_privacy2", null); + if (hex == null) { + return new StoryPrivacyBottomSheet.StoryPrivacy(); + } + SerializedData data = new SerializedData(Utilities.hexToBytes(hex)); + StoryPrivacyBottomSheet.StoryPrivacy privacy = read(data); + data.cleanup(); + if (privacy.isNone()) { + return new StoryPrivacyBottomSheet.StoryPrivacy(); + } + HashSet userIds = new HashSet<>(); + userIds.addAll(privacy.selectedUserIds); + for (ArrayList groupUserIds : privacy.selectedUserIdsByGroup.values()) { + userIds.addAll(groupUserIds); + } + if (!userIds.isEmpty()) { + MessagesStorage storage = MessagesStorage.getInstance(currentAccount); + storage.getStorageQueue().postRunnable(() -> { + ArrayList users = storage.getUsers(new ArrayList<>(userIds)); + AndroidUtilities.runOnUIThread(() -> { + MessagesController.getInstance(currentAccount).putUsers(users, true); + }); + }); + } + return privacy; + } catch (Exception e) { + FileLog.e(e); + } + return new StoryPrivacyBottomSheet.StoryPrivacy(); + } + + public static void applySaved(int currentAccount, StoryEntry entry) { + entry.privacy = getSaved(currentAccount); + entry.privacyRules.clear(); + entry.privacyRules.addAll(entry.privacy.rules); + if (UserConfig.getInstance(currentAccount).isPremium()) { + entry.period = MessagesController.getInstance(currentAccount).getMainSettings().getInt("story_period", 86400); + } else { + entry.period = 86400; + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/StoryRecorder.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/StoryRecorder.java new file mode 100644 index 0000000000..48547aed37 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/StoryRecorder.java @@ -0,0 +1,4208 @@ +package org.telegram.ui.Stories.recorder; + +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.AndroidUtilities.dpf2; +import static org.telegram.messenger.AndroidUtilities.touchSlop; + +import android.Manifest; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.StateListAnimator; +import android.animation.ValueAnimator; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Insets; +import android.graphics.LinearGradient; +import android.graphics.Matrix; +import android.graphics.Outline; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PixelFormat; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Shader; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.hardware.Camera; +import android.net.Uri; +import android.os.Build; +import android.os.Parcelable; +import android.provider.MediaStore; +import android.text.Layout; +import android.text.SpannableString; +import android.text.Spanned; +import android.text.TextUtils; +import android.text.style.ImageSpan; +import android.util.Log; +import android.util.Pair; +import android.util.TypedValue; +import android.view.GestureDetector; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.ScaleGestureDetector; +import android.view.TextureView; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewOutlineProvider; +import android.view.WindowInsets; +import android.view.WindowManager; +import android.view.inputmethod.InputMethodManager; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.graphics.ColorUtils; +import androidx.core.view.WindowCompat; +import androidx.core.view.WindowInsetsCompat; +import androidx.dynamicanimation.animation.DynamicAnimation; +import androidx.dynamicanimation.animation.SpringAnimation; +import androidx.exifinterface.media.ExifInterface; +import androidx.interpolator.view.animation.FastOutSlowInInterpolator; + +import org.checkerframework.checker.units.qual.A; +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimationNotificationsLocker; +import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.BotWebViewVibrationEffect; +import org.telegram.messenger.BuildVars; +import org.telegram.messenger.FileLoader; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.ImageLoader; +import org.telegram.messenger.ImageReceiver; +import org.telegram.messenger.LiteMode; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MediaController; +import org.telegram.messenger.MediaDataController; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.R; +import org.telegram.messenger.SharedConfig; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.UserObject; +import org.telegram.messenger.Utilities; +import org.telegram.messenger.VideoEditedInfo; +import org.telegram.messenger.camera.CameraController; +import org.telegram.messenger.camera.CameraSession; +import org.telegram.tgnet.TLObject; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.ActionBar; +import org.telegram.ui.ActionBar.AlertDialog; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.SimpleTextView; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AlertsCreator; +import org.telegram.ui.Components.Bulletin; +import org.telegram.ui.Components.BulletinFactory; +import org.telegram.ui.Components.CombinedDrawable; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.EmojiView; +import org.telegram.ui.Components.FilterShaders; +import org.telegram.ui.Components.GestureDetector2; +import org.telegram.ui.Components.GestureDetectorFixDoubleTap; +import org.telegram.ui.Components.HintView; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.PhotoFilterBlurControl; +import org.telegram.ui.Components.PhotoFilterCurvesControl; +import org.telegram.ui.Components.PhotoFilterView; +import org.telegram.ui.Components.Premium.PremiumFeatureBottomSheet; +import org.telegram.ui.Components.RLottieDrawable; +import org.telegram.ui.Components.RLottieImageView; +import org.telegram.ui.Components.ScaleStateListAnimator; +import org.telegram.ui.Components.SizeNotifierFrameLayout; +import org.telegram.ui.Components.URLSpanUserMention; +import org.telegram.ui.Components.VideoEditTextureView; +import org.telegram.ui.Components.VideoTimelinePlayView; +import org.telegram.ui.Components.ZoomControlView; +import org.telegram.ui.LaunchActivity; +import org.telegram.ui.Stories.DarkThemeResourceProvider; +import org.telegram.ui.Stories.DialogStoriesCell; +import org.telegram.ui.Stories.PeerStoriesView; +import org.telegram.ui.Stories.StoryViewer; +import org.telegram.ui.Stories.StoryWaveEffectView; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class StoryRecorder implements NotificationCenter.NotificationCenterDelegate { + + private final Theme.ResourcesProvider resourcesProvider = new DarkThemeResourceProvider(); + + private Activity activity; + private int currentAccount; + + private boolean isShown; + private boolean prepareClosing; + + WindowManager windowManager; + private final WindowManager.LayoutParams windowLayoutParams; + private WindowView windowView; + private ContainerView containerView; + + private static StoryRecorder instance; + private boolean wasSend; + private ClosingViewProvider closingSourceProvider; + + public static StoryRecorder getInstance(Activity activity, int currentAccount) { + if (instance != null && (instance.activity != activity || instance.currentAccount != currentAccount)) { + instance.close(false); + instance = null; + } + if (instance == null) { + instance = new StoryRecorder(activity, currentAccount); + } + return instance; + } + + public static void destroyInstance() { + if (instance != null) { + instance.close(false); + } + instance = null; + } + + public static boolean isVisible() { + return instance != null && instance.isShown; + } + public StoryRecorder(Activity activity, int currentAccount) { + this.activity = activity; + this.currentAccount = currentAccount; + + windowLayoutParams = new WindowManager.LayoutParams(); + windowLayoutParams.height = WindowManager.LayoutParams.MATCH_PARENT; + windowLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT; + windowLayoutParams.format = PixelFormat.TRANSLUCENT; + windowLayoutParams.gravity = Gravity.TOP | Gravity.LEFT; + windowLayoutParams.type = WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; + if (Build.VERSION.SDK_INT >= 28) { + windowLayoutParams.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; + } + windowLayoutParams.flags = ( + WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | + WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR | + WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION + ); + if (Build.VERSION.SDK_INT >= 21) { + windowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + } + windowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; + + windowManager = (WindowManager) activity.getSystemService(Context.WINDOW_SERVICE); + + initViews(); + } + + private ValueAnimator openCloseAnimator; + private SourceView fromSourceView; + private float fromRounding; + private RectF fromRect = new RectF(); + private float openProgress; + private int openType; + private float dismissProgress; + private Float frozenDismissProgress; + + public static class SourceView { + + int type = 0; + float rounding; + RectF screenRect = new RectF(); + ImageReceiver backgroundImageReceiver; + Paint backgroundPaint; + Drawable iconDrawable; + int iconSize; + View view; + + protected void show() {} + protected void hide() {} + protected void drawAbove(Canvas canvas, float alpha) {} + + public static SourceView fromStoryViewer(StoryViewer storyViewer) { + if (storyViewer == null) { + return null; + } + SourceView src = new SourceView() { + @Override + protected void show() { + final PeerStoriesView peerView = storyViewer.getCurrentPeerView(); + if (peerView != null) { + peerView.animateOut(false); + } + if (view != null) { + view.setTranslationX(0); + view.setTranslationY(0); + } + } + + @Override + protected void hide() { + final PeerStoriesView peerView = storyViewer.getCurrentPeerView(); + if (peerView != null) { + peerView.animateOut(true); + } + } + }; + if (!storyViewer.getStoryRect(src.screenRect)) { + return null; + } + src.type = 1; + src.rounding = dp(8); + final PeerStoriesView peerView = storyViewer.getCurrentPeerView(); + if (peerView != null) { + src.view = peerView.storyContainer; + } + return src; + } + + public static SourceView fromFloatingButton(FrameLayout floatingButton) { + if (floatingButton == null) { + return null; + } + SourceView src = new SourceView() { + @Override + protected void show() { + floatingButton.setVisibility(View.VISIBLE); + } + @Override + protected void hide() { + floatingButton.post(() -> { + floatingButton.setVisibility(View.GONE); + }); + } + }; + int[] loc = new int[2]; + final View imageView = floatingButton.getChildAt(0); + imageView.getLocationOnScreen(loc); + src.screenRect.set(loc[0], loc[1], loc[0] + imageView.getWidth(), loc[1] + imageView.getHeight()); + src.backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + src.backgroundPaint.setColor(Theme.getColor(Theme.key_chats_actionBackground)); + src.iconDrawable = floatingButton.getContext().getResources().getDrawable(R.drawable.story_camera).mutate(); + src.iconSize = AndroidUtilities.dp(56); + src.rounding = Math.max(src.screenRect.width(), src.screenRect.height()) / 2f; + return src; + } + + public static SourceView fromStoryCell(DialogStoriesCell.StoryCell storyCell) { + if (storyCell == null || storyCell.getRootView() == null) { + return null; + } + final float size = storyCell.avatarImage.getImageWidth(); + final float radius = size / 2f; + SourceView src = new SourceView() { + @Override + protected void show() { + storyCell.drawAvatar = true; + storyCell.invalidate(); + } + + @Override + protected void hide() { + storyCell.post(() -> { + storyCell.drawAvatar = false; + storyCell.invalidate(); + }); + } + + @Override + protected void drawAbove(Canvas canvas, float alpha) { + storyCell.drawPlus(canvas, radius, radius, (float) Math.pow(alpha, 16)); + } + }; + final int[] loc = new int[2]; + final float[] locPositon = new float[2]; + storyCell.getRootView().getLocationOnScreen(loc); + AndroidUtilities.getViewPositionInParent(storyCell, (ViewGroup) storyCell.getRootView(), locPositon); + final float x = loc[0] + locPositon[0] + storyCell.avatarImage.getImageX(); + final float y = loc[1] + locPositon[1] + storyCell.avatarImage.getImageY(); + + src.screenRect.set(x, y, x + size, y + size); + src.backgroundImageReceiver = storyCell.avatarImage; + src.rounding = Math.max(src.screenRect.width(), src.screenRect.height()) / 2f; + return src; + } + } + + public StoryRecorder closeToWhenSent(ClosingViewProvider closingSourceProvider) { + this.closingSourceProvider = closingSourceProvider; + return this; + } + + public void open(SourceView sourceView) { + open(sourceView, true); + } + + public void open(SourceView sourceView, boolean animated) { + if (isShown) { + return; + } + + prepareClosing = false; +// privacySelectorHintOpened = false; + forceBackgroundVisible = false; + + if (windowManager != null && windowView != null && windowView.getParent() == null) { + windowManager.addView(windowView, windowLayoutParams); + } + + cameraViewThumb.setImageDrawable(getCameraThumb()); + + navigateTo(PAGE_CAMERA, false); + switchToEditMode(EDIT_MODE_NONE, false); + + if (sourceView != null) { + fromSourceView = sourceView; + openType = sourceView.type; + fromRect.set(sourceView.screenRect); + fromRounding = sourceView.rounding; + fromSourceView.hide(); + } else { + openType = 0; + fromRect.set(0, dp(100), AndroidUtilities.displaySize.x, dp(100) + AndroidUtilities.displaySize.y); + fromRounding = dp(8); + } + containerView.updateBackground(); + previewContainer.setBackgroundColor(openType == 1 ? 0 : 0xff1f1f1f); + + containerView.setTranslationX(0); + containerView.setTranslationY(0); + containerView.setTranslationY2(0); + containerView.setScaleX(1f); + containerView.setScaleY(1f); + dismissProgress = 0; + + AndroidUtilities.lockOrientation(activity, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + + animateOpenTo(1, animated, this::onOpenDone); + + addNotificationObservers(); + } + + public void openEdit(SourceView sourceView, StoryEntry entry, long time, boolean animated) { + if (isShown) { + return; + } + + prepareClosing = false; +// privacySelectorHintOpened = false; + forceBackgroundVisible = false; + + if (windowManager != null && windowView != null && windowView.getParent() == null) { + windowManager.addView(windowView, windowLayoutParams); + } + + outputEntry = entry; + isVideo = outputEntry != null && outputEntry.isVideo; + + if (sourceView != null) { + fromSourceView = sourceView; + openType = sourceView.type; + fromRect.set(sourceView.screenRect); + fromRounding = sourceView.rounding; + fromSourceView.hide(); + } else { + openType = 0; + fromRect.set(0, dp(100), AndroidUtilities.displaySize.x, dp(100) + AndroidUtilities.displaySize.y); + fromRounding = dp(8); + } + containerView.updateBackground(); + previewContainer.setBackgroundColor(openType == 1 ? 0 : 0xff1f1f1f); + + containerView.setTranslationX(0); + containerView.setTranslationY(0); + containerView.setTranslationY2(0); + containerView.setScaleX(1f); + containerView.setScaleY(1f); + dismissProgress = 0; + + AndroidUtilities.lockOrientation(activity, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + + navigateToPreviewWithPlayerAwait(() -> { + animateOpenTo(1, animated, this::onOpenDone); + previewButtons.appear(true, true); + }, time); + navigateTo(PAGE_PREVIEW, false); + switchToEditMode(EDIT_MODE_NONE, false); + + if (outputEntry != null) { + captionEdit.setText(outputEntry.caption); + } + + addNotificationObservers(); + } + + public void close(boolean animated) { + if (!isShown) { + return; + } + + if (privacySheet != null) { + privacySheet.dismiss(); + privacySheet = null; + } + + if (outputEntry != null) { + if (wasSend && outputEntry.isEdit) { + outputEntry.editedMedia = false; + } + outputEntry.destroy(false); + outputEntry = null; + } + + if (onClosePrepareListener != null && previewView != null) { + if (prepareClosing) { + return; + } + prepareClosing = true; + onClosePrepareListener.run(previewView.release(), () -> { + onClosePrepareListener = null; + prepareClosing = false; + close(animated); + }, wasSend); + return; + } + + if (previewView != null && !animated) { + previewView.set(null); + } + + animateOpenTo(0, animated, this::onCloseDone); + if (openType == 1) { + windowView.setBackgroundColor(0x00000000); + previewButtons.appear(false, true); + } + + removeNotificationObservers(); + } + + private AnimationNotificationsLocker notificationsLocker = new AnimationNotificationsLocker(); + private StoryWaveEffectView waveEffect; + + private void animateOpenTo(final float value, boolean animated, Runnable onDone) { + if (openCloseAnimator != null) { + openCloseAnimator.cancel(); + openCloseAnimator = null; + } + + if (animated) { + notificationsLocker.lock(); + NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.stopAllHeavyOperations, 512); + frozenDismissProgress = dismissProgress; + openCloseAnimator = ValueAnimator.ofFloat(openProgress, value); + openCloseAnimator.addUpdateListener(anm -> { + openProgress = (float) anm.getAnimatedValue(); + checkBackgroundVisibility(); + containerView.invalidate(); + windowView.invalidate(); + if (openProgress < .3f && waveEffect != null) { + waveEffect.start(); + waveEffect = null; + } + }); + openCloseAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + frozenDismissProgress = null; + openProgress = value; + containerView.invalidate(); + windowView.invalidate(); + if (onDone != null) { + onDone.run(); + } + if (fromSourceView != null && waveEffect != null) { + waveEffect.start(); + waveEffect = null; + } + notificationsLocker.unlock(); + NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.startAllHeavyOperations, 512); + NotificationCenter.getGlobalInstance().runDelayedNotifications(); + checkBackgroundVisibility(); + } + }); + if (value < 1 && wasSend) { + openCloseAnimator.setDuration(250); + openCloseAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + } else { + if (value > 0 || containerView.getTranslationY1() < AndroidUtilities.dp(20)) { + openCloseAnimator.setDuration(270L); + openCloseAnimator.setInterpolator(new FastOutSlowInInterpolator()); + } else { + openCloseAnimator.setDuration(400L); + openCloseAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + } + } + openCloseAnimator.start(); + } else { + frozenDismissProgress = null; + openProgress = value; + containerView.invalidate(); + windowView.invalidate(); + if (onDone != null) { + onDone.run(); + } + checkBackgroundVisibility(); + } + } + + private void onOpenDone() { + isShown = true; + wasSend = false; + if (openType == 1) { + previewContainer.setAlpha(1f); + previewContainer.setTranslationX(0); + previewContainer.setTranslationY(0); + actionBarContainer.setAlpha(1f); + controlContainer.setAlpha(1f); + windowView.setBackgroundColor(0xff000000); + } + + if (whenOpenDone != null) { + whenOpenDone.run(); + whenOpenDone = null; + } else { + onResumeInternal(); + } + } + + private void onCloseDone() { + isShown = false; + AndroidUtilities.unlockOrientation(activity); + if (cameraView != null) { + if (takingVideo) { + CameraController.getInstance().stopVideoRecording(cameraView.getCameraSession(), false); + } + destroyCameraView(false); + } + if (previewView != null) { + previewView.set(null); + } + destroyPhotoPaintView(); + destroyPhotoFilterView(); + if (outputFile != null && !wasSend) { + try { + outputFile.delete(); + } catch (Exception ignore) {} + } + outputFile = null; + AndroidUtilities.runOnUIThread(() -> { + if (windowManager != null && windowView != null && windowView.getParent() != null) { + windowManager.removeView(windowView); + } + }, 16); + if (fromSourceView != null) { + fromSourceView.show(); + } + if (whenOpenDone != null) { + whenOpenDone = null; + } + lastGalleryScrollPosition = null; + if (instance != null) { + instance.close(false); + } + instance = null; + + if (onCloseListener != null) { + onCloseListener.run(); + onCloseListener = null; + } + if (windowView != null) { + Bulletin.removeDelegate(windowView); + } + } + + private Runnable onCloseListener; + public void setOnCloseListener(Runnable listener) { + onCloseListener = listener; + } + + private Utilities.Callback3 onClosePrepareListener; + public void setOnPrepareCloseListener(Utilities.Callback3 listener) { + onClosePrepareListener = listener; + } + + private int previewW, previewH; + private int underControls; + private boolean underStatusBar; + private boolean scrollingY, scrollingX; + + private int insetLeft, insetTop, insetRight, insetBottom; + + public class WindowView extends SizeNotifierFrameLayout { + + private GestureDetectorFixDoubleTap gestureDetector; + private ScaleGestureDetector scaleGestureDetector; + + public WindowView(Context context) { + super(context); + gestureDetector = new GestureDetectorFixDoubleTap(context, new GestureListener()); + scaleGestureDetector = new ScaleGestureDetector(context, new ScaleListener()); + } + + private RectF rectF = new RectF(), fullRectF = new RectF(); + private Path clipPath = new Path(); + private Rect rect = new Rect(); + private int lastKeyboardHeight; + + public int getBottomPadding(boolean withUnderControls) { + return getHeight() - containerView.getBottom() + (withUnderControls ? underControls : 0); + } + + public int getPaddingUnderContainer() { + return getHeight() - insetBottom - containerView.getBottom(); + } + + @Override + protected void dispatchDraw(Canvas canvas) { + float dismiss = frozenDismissProgress != null ? frozenDismissProgress : dismissProgress; + if (openType == 0) { + canvas.drawColor(ColorUtils.setAlphaComponent(Color.BLACK, (int) (255 * openProgress * (1f - dismiss)))); + } + boolean restore = false; + final float r = AndroidUtilities.lerp(fromRounding, 0, openProgress); + if (openProgress != 1) { + if (openType == 0) { + fullRectF.set(0, 0, getWidth(), getHeight()); + fullRectF.offset(containerView.getTranslationX(), containerView.getTranslationY()); + AndroidUtilities.lerp(fromRect, fullRectF, openProgress, rectF); + + canvas.save(); + clipPath.rewind(); + clipPath.addRoundRect(rectF, r, r, Path.Direction.CW); + canvas.clipPath(clipPath); + + final float alpha = Utilities.clamp(openProgress * 3, 1, 0); + canvas.saveLayerAlpha(0, 0, getWidth(), getHeight(), (int) (0xFF * alpha), Canvas.ALL_SAVE_FLAG); + canvas.translate(rectF.left, rectF.top - containerView.getTranslationY() * openProgress); + final float s = Math.max(rectF.width() / getWidth(), rectF.height() / getHeight()); + canvas.scale(s, s); + restore = true; + } else if (openType == 1) { + fullRectF.set(previewContainer.getLeft(), previewContainer.getTop(), previewContainer.getMeasuredWidth(), previewContainer.getMeasuredHeight()); + fullRectF.offset(containerView.getX(), containerView.getY()); + AndroidUtilities.lerp(fromRect, fullRectF, openProgress, rectF); + previewContainer.setAlpha(openProgress); + previewContainer.setTranslationX(rectF.left - previewContainer.getLeft() - containerView.getX()); + previewContainer.setTranslationY(rectF.top - previewContainer.getTop() - containerView.getY()); + if (fromSourceView != null && fromSourceView.view != null) { + fromSourceView.view.setTranslationX((fullRectF.left - fromRect.left) * openProgress); + fromSourceView.view.setTranslationY((fullRectF.top - fromRect.top) * openProgress); + } + previewContainer.setScaleX(rectF.width() / previewContainer.getMeasuredWidth()); + previewContainer.setScaleY(rectF.height() / previewContainer.getMeasuredHeight()); + actionBarContainer.setAlpha(openProgress); + controlContainer.setAlpha(openProgress); + captionContainer.setAlpha(openProgress); + } + } + super.dispatchDraw(canvas); + if (restore) { + canvas.restore(); + canvas.restore(); + + if (fromSourceView != null) { + final float alpha = Utilities.clamp(1f - openProgress * 1.5f, 1, 0); + final float bcx = rectF.centerX(), + bcy = rectF.centerY(), + br = Math.min(rectF.width(), rectF.height()) / 2f; + if (fromSourceView.backgroundImageReceiver != null) { + fromSourceView.backgroundImageReceiver.setImageCoords(rectF); + fromSourceView.backgroundImageReceiver.setAlpha(alpha); + fromSourceView.backgroundImageReceiver.draw(canvas); + } else if (fromSourceView.backgroundPaint != null) { + fromSourceView.backgroundPaint.setShadowLayer(dp(2), 0, dp(3), Theme.multAlpha(0x33000000, alpha)); + fromSourceView.backgroundPaint.setAlpha((int) (0xFF * alpha)); + canvas.drawRoundRect(rectF, r, r, fromSourceView.backgroundPaint); + } + if (fromSourceView.iconDrawable != null) { + rect.set(fromSourceView.iconDrawable.getBounds()); + fromSourceView.iconDrawable.setBounds( + (int) (bcx - fromSourceView.iconSize / 2), + (int) (bcy - fromSourceView.iconSize / 2), + (int) (bcx + fromSourceView.iconSize / 2), + (int) (bcy + fromSourceView.iconSize / 2) + ); + int wasAlpha = fromSourceView.iconDrawable.getAlpha(); + fromSourceView.iconDrawable.setAlpha((int) (wasAlpha * alpha)); + fromSourceView.iconDrawable.draw(canvas); + fromSourceView.iconDrawable.setBounds(rect); + fromSourceView.iconDrawable.setAlpha(wasAlpha); + } + + canvas.save(); + canvas.translate(fromRect.left, fromRect.top); + fromSourceView.drawAbove(canvas, alpha); + canvas.restore(); + } + } + } + + private boolean flingDetected; + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + flingDetected = false; + scaleGestureDetector.onTouchEvent(ev); + gestureDetector.onTouchEvent(ev); + if (ev.getAction() == MotionEvent.ACTION_UP && !flingDetected) { + allowModeScroll = true; + if (containerView.getTranslationY() > 0) { + if (dismissProgress > .4f) { + close(true); + } else { + animateContainerBack(); + } + } else if (galleryListView != null && galleryListView.getTranslationY() > 0) { + animateGalleryListView(!takingVideo && galleryListView.getTranslationY() < galleryListView.getPadding()); + } + modeSwitcherView.stopScroll(0); + scrollingY = false; + scrollingX = false; + } + return super.dispatchTouchEvent(ev); + } + + @Override + public boolean dispatchKeyEventPreIme(KeyEvent event) { + if (event != null && event.getKeyCode() + == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) { + onBackPressed(); + return true; + } + return super.dispatchKeyEventPreIme(event); + } + + private boolean scaling = false; + private final class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { + @Override + public boolean onScale(ScaleGestureDetector detector) { + if (!scaling || cameraView == null || currentPage != PAGE_CAMERA || cameraView.isDualTouch()) { + return false; + } + final float deltaScaleFactor = (detector.getScaleFactor() - 1.0f) * .75f; + cameraZoom += deltaScaleFactor; + cameraZoom = Utilities.clamp(cameraZoom, 1, 0); + cameraView.setZoom(cameraZoom); + if (zoomControlView != null) { + zoomControlView.setZoom(cameraZoom, false); + } + showZoomControls(true, true); + return true; + } + + @Override + public boolean onScaleBegin(@NonNull ScaleGestureDetector detector) { + if (cameraView == null || currentPage != PAGE_CAMERA || wasGalleryOpen) { + return false; + } + scaling = true; + return super.onScaleBegin(detector); + } + + @Override + public void onScaleEnd(@NonNull ScaleGestureDetector detector) { + scaling = false; + animateGalleryListView(false); + animateContainerBack(); + super.onScaleEnd(detector); + } + } + + private float ty, sty, stx; + private boolean allowModeScroll = true; + + private final class GestureListener extends GestureDetectorFixDoubleTap.OnGestureListener { + @Override + public boolean onDown(@NonNull MotionEvent e) { + sty = 0; + stx = 0; + return false; + } + + @Override + public void onShowPress(@NonNull MotionEvent e) { + + } + + @Override + public boolean onSingleTapUp(@NonNull MotionEvent e) { + scrollingY = false; + scrollingX = false; + if (!hasDoubleTap(e)) { + if (onSingleTapConfirmed(e)) { + return true; + } + } + if (isGalleryOpen() && e.getY() < galleryListView.top()) { + animateGalleryListView(false); + return true; + } + return false; + } + + @Override + public boolean onScroll(@NonNull MotionEvent e1, @NonNull MotionEvent e2, float distanceX, float distanceY) { + if (openCloseAnimator != null && openCloseAnimator.isRunning() || galleryOpenCloseSpringAnimator != null || galleryOpenCloseAnimator != null || recordControl.isTouch() || cameraView != null && cameraView.isDualTouch() || scaling || zoomControlView != null && zoomControlView.isTouch()) { + return false; + } + if (takingVideo || takingPhoto || currentPage != PAGE_CAMERA) { + return false; + } + if (!scrollingX) { + sty += distanceY; + if (!scrollingY && Math.abs(sty) >= touchSlop) { + scrollingY = true; + } + } + if (scrollingY) { + int galleryMax = windowView.getMeasuredHeight() - (int) (AndroidUtilities.displaySize.y * 0.35f) - (AndroidUtilities.statusBarHeight + ActionBar.getCurrentActionBarHeight()); + if (galleryListView == null || galleryListView.getTranslationY() >= galleryMax) { + ty = containerView.getTranslationY1(); + } else { + ty = galleryListView.getTranslationY() - galleryMax; + } + if (galleryListView != null && galleryListView.listView.canScrollVertically(-1)) { + distanceY = Math.max(0, distanceY); + } + ty -= distanceY; + ty = Math.max(-galleryMax, ty); + if (currentPage == PAGE_PREVIEW) { + ty = Math.max(0, ty); + } + if (ty >= 0) { + containerView.setTranslationY(ty); + if (galleryListView != null) { + galleryListView.setTranslationY(galleryMax); + } + } else { + containerView.setTranslationY(0); + if (galleryListView == null) { + createGalleryListView(); + } + galleryListView.setTranslationY(galleryMax + ty); + } + } + if (!scrollingY) { + stx += distanceX; + if (!scrollingX && Math.abs(stx) >= touchSlop) { + scrollingX = true; + } + } + if (scrollingX) { + modeSwitcherView.scrollX(distanceX); + } + return true; + } + + @Override + public void onLongPress(@NonNull MotionEvent e) { + + } + + @Override + public boolean onFling(@NonNull MotionEvent e1, @NonNull MotionEvent e2, float velocityX, float velocityY) { + if (openCloseAnimator != null && openCloseAnimator.isRunning() || recordControl.isTouch() || cameraView != null && cameraView.isDualTouch() || scaling || zoomControlView != null && zoomControlView.isTouch()) { + return false; + } + flingDetected = true; + allowModeScroll = true; + boolean r = false; + if (scrollingY) { + if (Math.abs(containerView.getTranslationY1()) >= dp(1)) { + if (velocityY > 0 && Math.abs(velocityY) > 2000 && Math.abs(velocityY) > Math.abs(velocityX) || dismissProgress > .4f) { + close(true); + } else { + animateContainerBack(); + } + r = true; + } else if (galleryListView != null) { + if (Math.abs(velocityY) > 200 && (!galleryListView.listView.canScrollVertically(-1) || !wasGalleryOpen)) { + animateGalleryListView(!takingVideo && velocityY < 0); + r = true; + } else { + animateGalleryListView(!takingVideo && galleryListView.getTranslationY() < galleryListView.getPadding()); + r = true; + } + } + } + if (scrollingX) { + r = modeSwitcherView.stopScroll(velocityX) || r; + } + scrollingY = false; + scrollingX = false; + return r; + } + + @Override + public boolean onSingleTapConfirmed(MotionEvent e) { + if (cameraView != null) { + cameraView.allowToTapFocus(); + return true; + } + return false; + } + + @Override + public boolean onDoubleTap(MotionEvent e) { + if (cameraView == null || awaitingPlayer || takingPhoto || !cameraView.isInited() || currentPage != PAGE_CAMERA) { + return false; + } + cameraView.switchCamera(); + recordControl.rotateFlip(180); + saveCameraFace(cameraView.isFrontface()); + return true; + } + + @Override + public boolean onDoubleTapEvent(MotionEvent e) { + if (cameraView != null) { + cameraView.clearTapFocus(); + } + return false; + } + + @Override + public boolean hasDoubleTap(MotionEvent e) { + return currentPage == PAGE_CAMERA && cameraView != null && !awaitingPlayer && cameraView.isInited() && !takingPhoto && !recordControl.isTouch() && !isGalleryOpen() && galleryListViewOpening == null; + } + }; + + private boolean ignoreLayout; + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (Build.VERSION.SDK_INT < 21) { + insetTop = AndroidUtilities.statusBarHeight; + insetBottom = AndroidUtilities.navigationBarHeight; + } + + final int W = MeasureSpec.getSize(widthMeasureSpec); + final int H = MeasureSpec.getSize(heightMeasureSpec); + final int w = W - insetLeft - insetRight; + + final int statusbar = insetTop; + final int navbar = insetBottom; + + final int hFromW = (int) Math.ceil(w / 9f * 16f); + underControls = dp(48); + if (hFromW + underControls <= H - navbar) { + previewW = w; + previewH = hFromW; + underStatusBar = previewH + underControls > H - navbar - statusbar; + } else { + underStatusBar = false; + previewH = H - underControls - navbar - statusbar; + previewW = (int) Math.ceil(previewH * 9f / 16f); + } + underControls = Utilities.clamp(H - previewH - (underStatusBar ? 0 : statusbar), dp(68), dp(48)); + + int flags = getSystemUiVisibility(); + if (underStatusBar) { + flags |= View.SYSTEM_UI_FLAG_FULLSCREEN; + } else { + flags &= ~View.SYSTEM_UI_FLAG_FULLSCREEN; + } + setSystemUiVisibility(flags); + + containerView.measure( + MeasureSpec.makeMeasureSpec(previewW, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(previewH + underControls, MeasureSpec.EXACTLY) + ); + + if (galleryListView != null) { + galleryListView.measure(MeasureSpec.makeMeasureSpec(previewW, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(H, MeasureSpec.EXACTLY)); + } + + if (captionEdit != null) { + EmojiView emojiView = captionEdit.editText.getEmojiView(); + if (measureKeyboardHeight() > AndroidUtilities.dp(20)) { + ignoreLayout = true; +// captionEdit.editText.hideEmojiView(); + ignoreLayout = false; + } + if (emojiView != null) { + emojiView.measure( + MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(emojiView.getLayoutParams().height, MeasureSpec.EXACTLY) + ); + } + } + + if (paintView != null && paintView.emojiView != null) { + paintView.emojiView.measure( + MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(paintView.emojiView.getLayoutParams().height, MeasureSpec.EXACTLY) + ); + } + + for (int i = 0; i < getChildCount(); ++i) { + View child = getChildAt(i); + if (child instanceof DownloadButton.PreparingVideoToast) { + child.measure( + MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(H, MeasureSpec.EXACTLY) + ); + } else if (child instanceof Bulletin.ParentLayout) { + child.measure( + MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(Math.min(dp(340), H - (underStatusBar ? 0 : statusbar)), MeasureSpec.EXACTLY) + ); + } + } + + setMeasuredDimension(W, H); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + if (ignoreLayout) { + return; + } + final int W = right - left; + final int H = bottom - top; + + final int statusbar = insetTop; + final int underControls = navbarContainer.getMeasuredHeight(); + + final int T = underStatusBar ? 0 : statusbar; + int l = insetLeft + (W - insetRight - previewW) / 2, + r = insetLeft + (W - insetRight + previewW) / 2, t, b; + if (underStatusBar) { + t = T; + b = T + previewH + underControls; + } else { + t = T + ((H - T - insetBottom) - previewH - underControls) / 2; + if (openType == 1 && fromRect.top + previewH + underControls < H - insetBottom) { + t = (int) fromRect.top; + } else if (t - T < dp(40)) { + t = T; + } + b = t + previewH + underControls; + } + + containerView.layout(l, t, r, b); + + if (galleryListView != null) { + galleryListView.layout((W - galleryListView.getMeasuredWidth()) / 2, 0, (W + galleryListView.getMeasuredWidth()) / 2, H); + } + + if (captionEdit != null) { + EmojiView emojiView = captionEdit.editText.getEmojiView(); + if (emojiView != null) { + emojiView.layout(insetLeft, H - insetBottom - emojiView.getMeasuredHeight(), W - insetRight, H - insetBottom); + } + } + + if (paintView != null && paintView.emojiView != null) { + paintView.emojiView.layout(insetLeft, H - insetBottom - paintView.emojiView.getMeasuredHeight(), W - insetRight, H - insetBottom); + } + + for (int i = 0; i < getChildCount(); ++i) { + View child = getChildAt(i); + if (child instanceof DownloadButton.PreparingVideoToast) { + child.layout(0, 0, W, H); + } else if (child instanceof Bulletin.ParentLayout) { + child.layout(0, t, child.getMeasuredWidth(), t + child.getMeasuredHeight()); + } + } + } + + public void drawBlurBitmap(Bitmap bitmap, float amount) { + Canvas canvas = new Canvas(bitmap); + canvas.drawColor(0xff000000); + final float scale = (float) bitmap.getWidth() / windowView.getWidth(); + canvas.scale(scale, scale); + + TextureView textureView = previewView.getTextureView(); + if (textureView == null) { + textureView = previewView.filterTextureView; + } + if (textureView != null) { + canvas.save(); + canvas.translate(containerView.getX() + previewContainer.getX(), containerView.getY() + previewContainer.getY()); + int w = (int) (textureView.getWidth() / amount), h = (int) (textureView.getHeight() / amount); + try { + Bitmap textureBitmap = textureView.getBitmap(w, h); + canvas.scale(1f / scale, 1f / scale); + canvas.drawBitmap(textureBitmap, 0, 0, new Paint(Paint.FILTER_BITMAP_FLAG)); + textureBitmap.recycle(); + } catch (Exception ignore) {} + canvas.restore(); + } + canvas.save(); + canvas.translate(containerView.getX(), containerView.getY()); + for (int i = 0; i < containerView.getChildCount(); ++i) { + View child = containerView.getChildAt(i); + canvas.save(); + canvas.translate(child.getX(), child.getY()); + if (child.getVisibility() != View.VISIBLE) { + continue; + } else if (child == previewContainer) { + for (int j = 0; j < previewContainer.getChildCount(); ++j) { + child = previewContainer.getChildAt(j); + if (child == previewView || child == cameraView || child == cameraViewThumb || child.getVisibility() != View.VISIBLE) { + continue; + } + canvas.save(); + canvas.translate(child.getX(), child.getY()); + child.draw(canvas); + canvas.restore(); + } + } else { + child.draw(canvas); + } + canvas.restore(); + } + canvas.restore(); + } + } + + private class ContainerView extends FrameLayout { + public ContainerView(Context context) { + super(context); + } + + public void updateBackground() { + if (openType == 0) { + setBackground(Theme.createRoundRectDrawable(dp(12), 0xff000000)); + } else { + setBackground(null); + } + } + + private float translationY1; + private float translationY2; + + public void setTranslationY2(float translationY2) { + super.setTranslationY(this.translationY1 + (this.translationY2 = translationY2)); + } + + public float getTranslationY1() { + return translationY1; + } + + public float getTranslationY2() { + return translationY2; + } + + @Override + public void setTranslationY(float translationY) { + super.setTranslationY((this.translationY1 = translationY) + translationY2); + + dismissProgress = Utilities.clamp(translationY / getMeasuredHeight() * 4, 1, 0); + checkBackgroundVisibility(); + windowView.invalidate(); + + final float scale = 1f - .1f * Utilities.clamp(getTranslationY() / AndroidUtilities.dp(320), 1, 0); + setScaleX(scale); + setScaleY(scale); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + final int t = underStatusBar ? insetTop : 0; + + previewContainer.layout(0, 0, previewW, previewH); + previewContainer.setPivotX(previewW * .5f); + actionBarContainer.layout(0, t, previewW, t + actionBarContainer.getMeasuredHeight()); + controlContainer.layout(0, previewH - controlContainer.getMeasuredHeight(), previewW, previewH); + navbarContainer.layout(0, previewH, previewW, previewH + navbarContainer.getMeasuredHeight()); + captionContainer.layout(0, 0, previewW, previewH); + + if (captionEdit.mentionContainer != null) { + captionEdit.mentionContainer.layout(0, 0, previewW, previewH); + captionEdit.updateMentionsLayoutPosition(); + } + + if (photoFilterView != null) { + photoFilterView.layout(0, 0, photoFilterView.getMeasuredWidth(), photoFilterView.getMeasuredHeight()); + } + if (paintView != null) { + paintView.layout(0, 0, paintView.getMeasuredWidth(), paintView.getMeasuredHeight()); + } + + final int w = right - left; + final int h = bottom - top; + + setPivotX((right - left) / 2f); + setPivotY(-h * .2f); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int W = MeasureSpec.getSize(widthMeasureSpec); + final int H = MeasureSpec.getSize(heightMeasureSpec); + + measureChildExactly(previewContainer, previewW, previewH); + measureChildExactly(actionBarContainer, previewW, dp(56 + 56 + 38)); + measureChildExactly(controlContainer, previewW, dp(220)); + measureChildExactly(navbarContainer, previewW, underControls); + measureChildExactly(captionContainer, previewW, previewH); + if (captionEdit.mentionContainer != null) { + measureChildExactly(captionEdit.mentionContainer, previewW, previewH); + } + + if (photoFilterView != null) { + measureChildExactly(photoFilterView, W, H); + } + if (paintView != null) { + measureChildExactly(paintView, W, H); + } + setMeasuredDimension(W, H); + } + + private void measureChildExactly(View child, int width, int height) { + child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); + } + + private final Paint topGradientPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private LinearGradient topGradient; + + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + boolean r = super.drawChild(canvas, child, drawingTime); + if (child == previewContainer) { + final float top = underStatusBar ? AndroidUtilities.statusBarHeight : 0; + if (topGradient == null) { + topGradient = new LinearGradient(0, top, 0, top + dp(72), new int[] {0x40000000, 0x00000000}, new float[] { top / (top + dp(72)), 1 }, Shader.TileMode.CLAMP ); + topGradientPaint.setShader(topGradient); + } + topGradientPaint.setAlpha((int) (0xFF * openProgress)); + canvas.drawRect(0, 0, getWidth(), dp(72) + top, topGradientPaint); + } + return r; + } + } + + public static final int PAGE_CAMERA = 0; + public static final int PAGE_PREVIEW = 1; + private int currentPage = PAGE_CAMERA; + + public static final int EDIT_MODE_NONE = -1; + public static final int EDIT_MODE_PAINT = 0; + public static final int EDIT_MODE_FILTER = 1; + private int currentEditMode = EDIT_MODE_NONE; + + private FrameLayout previewContainer; + private FrameLayout actionBarContainer; + private FrameLayout controlContainer; + private FrameLayout captionContainer; + private FrameLayout navbarContainer; + + private ImageView backButton; + private SimpleTextView titleTextView; + private StoryPrivacyBottomSheet privacySheet; + + /* PAGE_CAMERA */ + private ImageView cameraViewThumb; + private DualCameraView cameraView; + + private int flashButtonResId; + private ImageView flashButton; + private ToggleButton dualButton; + private VideoTimerView videoTimerView; + private boolean wasGalleryOpen; + private GalleryListView galleryListView; + private DraftSavedHint draftSavedHint; + private RecordControl recordControl; + private PhotoVideoSwitcherView modeSwitcherView; + private HintTextView hintTextView; + private ZoomControlView zoomControlView; + private HintView2 cameraHint; + + /* PAGE_PREVIEW */ + private PreviewView previewView; + private FrameLayout videoTimelineContainerView; + private VideoTimelinePlayView videoTimelineView; + private VideoTimeView videoTimeView; + private PreviewButtons previewButtons; + private CaptionContainerView captionEdit; + private DownloadButton downloadButton; + private RLottieDrawable muteButtonDrawable; + private RLottieImageView muteButton; + private HintView2 muteHint; + private HintView2 dualHint; + private HintView2 savedDualHint; +// private StoryPrivacySelector privacySelector; +// private boolean privacySelectorHintOpened; +// private StoryPrivacySelector.StoryPrivacyHint privacySelectorHint; + private PreviewHighlightView previewHighlight; + private TrashView trash; + + /* EDIT_MODE_PAINT */ + private Bitmap paintViewBitmap; + private PaintView paintView; + private View paintViewRenderView; + private View paintViewRenderInputView; + private View paintViewTextDim; + private View paintViewEntitiesView; + private View paintViewSelectionContainerView; + + /* EDIT_MODE_FILTER */ + private Bitmap photoFilterBitmap; + private PhotoFilterView photoFilterView; + private PhotoFilterView.EnhanceView photoFilterEnhanceView; + private TextureView photoFilterViewTextureView; + private PhotoFilterBlurControl photoFilterViewBlurControl; + private PhotoFilterCurvesControl photoFilterViewCurvesControl; + + private File outputFile; + private StoryEntry outputEntry; + private boolean fromGallery; + + private boolean isVideo = false; + private boolean takingPhoto = false; + private boolean takingVideo = false; + private boolean stoppingTakingVideo = false; + private boolean awaitingPlayer = false; + + private float cameraZoom; + + private int shiftDp = -3; + private boolean showSavedDraftHint; + + public Context getContext() { + return activity; + } + + @SuppressLint("ClickableViewAccessibility") + private void initViews() { + Context context = getContext(); + + windowView = new WindowView(context); + if (Build.VERSION.SDK_INT >= 21) { + windowView.setFitsSystemWindows(true); + windowView.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() { + @NonNull + @Override + public WindowInsets onApplyWindowInsets(@NonNull View v, @NonNull WindowInsets insets) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + Insets r = insets.getInsets(WindowInsetsCompat.Type.displayCutout() | WindowInsetsCompat.Type.systemBars()); + insetTop = r.top; + insetBottom = r.bottom; + insetLeft = r.left; + insetRight = r.right; + } else { + insetTop = insets.getStableInsetTop(); + insetBottom = insets.getStableInsetBottom(); + insetLeft = insets.getStableInsetLeft(); + insetRight = insets.getStableInsetRight(); + } + windowView.requestLayout(); + if (Build.VERSION.SDK_INT >= 30) { + return WindowInsets.CONSUMED; + } else { + return insets.consumeSystemWindowInsets(); + } + } + }); + } + windowView.setFocusable(true); + + windowView.addView(containerView = new ContainerView(context)); + + containerView.addView(previewContainer = new FrameLayout(context) { + @Override + public boolean onTouchEvent(MotionEvent event) { + if (previewTouchable != null) { + previewTouchable.onTouch(event); + return true; + } + return super.onTouchEvent(event); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + if (photoFilterViewCurvesControl != null) { + photoFilterViewCurvesControl.setActualArea(0, 0, photoFilterViewCurvesControl.getMeasuredWidth(), photoFilterViewCurvesControl.getMeasuredHeight()); + } + if (photoFilterViewBlurControl != null) { + photoFilterViewBlurControl.setActualAreaSize(photoFilterViewBlurControl.getMeasuredWidth(), photoFilterViewBlurControl.getMeasuredHeight()); + } + } + }); + containerView.addView(actionBarContainer = new FrameLayout(context)); // 150dp + containerView.addView(controlContainer = new FrameLayout(context)); // 220dp + containerView.addView(captionContainer = new FrameLayout(context) { + @Override + public void setTranslationY(float translationY) { + if (getTranslationY() != translationY && captionEdit != null) { + super.setTranslationY(translationY); + captionEdit.updateMentionsLayoutPosition(); + } + } + }); // full height + captionContainer.setVisibility(View.GONE); + captionContainer.setAlpha(0f); + containerView.addView(navbarContainer = new FrameLayout(context)); // 48dp + + Bulletin.addDelegate(windowView, new Bulletin.Delegate() { + @Override + public int getTopOffset(int tag) { + return (int) (dp(56) + dp(56 - 10) * muteButton.getAlpha()); + } + }); + + cameraViewThumb = new ImageView(context); + cameraViewThumb.setScaleType(ImageView.ScaleType.CENTER_CROP); + cameraViewThumb.setOnClickListener(v -> { + if (noCameraPermission) { + requestCameraPermission(true); + } + }); + cameraViewThumb.setClickable(true); + previewContainer.addView(cameraViewThumb, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); + + previewContainer.setBackgroundColor(openType == 1 ? 0 : 0xff1f1f1f); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + previewContainer.setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setRoundRect(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight(), dp(12)); + } + }); + previewContainer.setClipToOutline(true); + } + photoFilterEnhanceView = new PhotoFilterView.EnhanceView(context, this::createFilterPhotoView); + previewView = new PreviewView(context) { + @Override + public boolean additionalTouchEvent(MotionEvent ev) { + return photoFilterEnhanceView.onTouch(ev); + } + + @Override + public void applyMatrix() { + super.applyMatrix(); + applyFilterMatrix(); + } + @Override + public void onEntityDraggedTop(boolean value) { + previewHighlight.show(true, value, actionBarContainer); + } + + @Override + public void onEntityDraggedBottom(boolean value) { + previewHighlight.updateCaption(captionEdit.getText()); +// previewHighlight.show(false, value, null); + } + + @Override + public void onEntityDragEnd(boolean delete) { + controlContainer.clearAnimation(); + controlContainer.animate().alpha(1f).setDuration(180).setInterpolator(CubicBezierInterpolator.EASE_OUT).start(); + trash.onDragInfo(false, delete); + trash.clearAnimation(); + trash.animate().alpha(0f).withEndAction(() -> { + trash.setVisibility(View.GONE); + }).setDuration(180).setInterpolator(CubicBezierInterpolator.EASE_OUT).setStartDelay(delete ? 500 : 0).start(); + if (delete) { + deleteCurrentPart(); + } + super.onEntityDragEnd(delete); + } + + @Override + public void onEntityDragStart() { + controlContainer.clearAnimation(); + controlContainer.animate().alpha(0f).setDuration(180).setInterpolator(CubicBezierInterpolator.EASE_OUT).start(); + + trash.setVisibility(View.VISIBLE); + trash.setAlpha(0f); + trash.clearAnimation(); + trash.animate().alpha(1f).setDuration(180).setInterpolator(CubicBezierInterpolator.EASE_OUT).start(); + } + + @Override + public void onEntityDragTrash(boolean enter) { + trash.onDragInfo(enter, false); + } + + @Override + protected void onTimeDrag(boolean dragStart, long time, boolean dragEnd) { + videoTimeView.setTime(time, !dragStart); + videoTimeView.show(!dragEnd, true); + } + }; + previewView.setOnTapListener(() -> { + if (currentEditMode != EDIT_MODE_NONE || currentPage != PAGE_PREVIEW || captionEdit.keyboardShown) { + return; + } + switchToEditMode(EDIT_MODE_PAINT, true); + if (paintView != null) { + paintView.openText(); + paintView.enteredThroughText = true; + } + }); + previewView.setVisibility(View.GONE); + previewView.whenError(() -> { + previewButtons.setShareEnabled(false); + downloadButton.showFailedVideo(); + }); + previewContainer.addView(previewView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); + + previewContainer.addView(photoFilterEnhanceView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); + + captionEdit = new CaptionContainerView(context, currentAccount, windowView, containerView, resourcesProvider) { + @Override + protected void drawBlurBitmap(Bitmap bitmap, float amount) { + windowView.drawBlurBitmap(bitmap, amount); + super.drawBlurBitmap(bitmap, amount); + } + }; + captionEdit.setOnHeightUpdate(height -> { + if (videoTimelineContainerView != null) { + videoTimelineContainerView.setTranslationY(-(captionEdit.getEditTextHeight() + AndroidUtilities.dp(12)) + AndroidUtilities.dp(64)); + } + }); + captionEdit.setOnPeriodUpdate(period -> { + if (outputEntry != null) { + outputEntry.period = period; + MessagesController.getGlobalMainSettings().edit().putInt("story_period", period).apply(); +// privacySelector.setStoryPeriod(period); + } + }); + captionEdit.setOnPremiumHint(this::showPremiumPeriodBulletin); + captionEdit.setOnKeyboardOpen(open -> { + previewView.updatePauseReason(2, open); + videoTimelineContainerView.clearAnimation(); + videoTimelineContainerView.animate().alpha(open ? 0f : 1f).setDuration(120).start(); + }); + + videoTimelineView = previewView.getTimelineView(); + videoTimelineView.setVisibility(View.GONE); + videoTimelineView.setAlpha(0f); + videoTimelineContainerView = new FrameLayout(context); + videoTimelineContainerView.addView(videoTimelineView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 58, Gravity.FILL_HORIZONTAL | Gravity.BOTTOM, 0, 0, 0, 0)); + videoTimeView = new VideoTimeView(context); + videoTimeView.setVisibility(View.GONE); + videoTimeView.show(false, false); + videoTimelineContainerView.addView(videoTimeView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 25, Gravity.FILL_HORIZONTAL | Gravity.TOP, 0, 0, 0, 0)); + captionContainer.addView(videoTimelineContainerView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 58 + 25, Gravity.FILL_HORIZONTAL | Gravity.BOTTOM, 0, 0, 0, 64)); + captionContainer.addView(captionEdit, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL_HORIZONTAL | Gravity.BOTTOM, 0, 200, 0, 0)); + + backButton = new ImageView(context); + backButton.setContentDescription(LocaleController.getString("AccDescrGoBack", R.string.AccDescrGoBack)); + backButton.setScaleType(ImageView.ScaleType.CENTER); + backButton.setImageResource(R.drawable.msg_photo_back); + backButton.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.MULTIPLY)); + backButton.setBackground(Theme.createSelectorDrawable(0x20ffffff)); + backButton.setOnClickListener(e -> { + if (awaitingPlayer) { + return; + } + onBackPressed(); + }); + actionBarContainer.addView(backButton, LayoutHelper.createFrame(56, 56, Gravity.TOP | Gravity.LEFT)); + + titleTextView = new SimpleTextView(context); + titleTextView.setTextSize(20); + titleTextView.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT); + titleTextView.setTextColor(0xffffffff); + titleTextView.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + titleTextView.setText(LocaleController.getString("RecorderNewStory", R.string.RecorderNewStory)); + titleTextView.getPaint().setShadowLayer(dpf2(1), 0, 1, 0x40000000); + titleTextView.setAlpha(0f); + titleTextView.setVisibility(View.GONE); + titleTextView.setEllipsizeByGradient(true); + titleTextView.setRightPadding(AndroidUtilities.dp(96)); + actionBarContainer.addView(titleTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 56, Gravity.TOP | Gravity.FILL_HORIZONTAL, 71, 0, 0, 0)); + + downloadButton = new DownloadButton(context, done -> { + applyPaint(true); + applyFilter(done); + }, currentAccount, windowView, resourcesProvider); + actionBarContainer.addView(downloadButton, LayoutHelper.createFrame(56, 56, Gravity.TOP | Gravity.RIGHT)); + + muteHint = new HintView2(activity, HintView2.DIRECTION_TOP) + .setJoint(1, -68) + .setDuration(2000) + .setBounce(false) + .setAnimatedTextHacks(true, true, false); + muteHint.setPadding(dp(8), 0, dp(8), 0); + actionBarContainer.addView(muteHint, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP, 0, 52, 0, 0)); + + muteButton = new RLottieImageView(context); + muteButton.setScaleType(ImageView.ScaleType.CENTER); + muteButton.setImageResource(outputEntry != null && outputEntry.muted ? R.drawable.media_unmute : R.drawable.media_mute); + muteButton.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.MULTIPLY)); + muteButton.setBackground(Theme.createSelectorDrawable(0x20ffffff)); + muteButton.setOnClickListener(e -> { + if (outputEntry == null || awaitingPlayer) { + return; + } + outputEntry.muted = !outputEntry.muted; + muteHint.setText(outputEntry.muted ? LocaleController.getString("StorySoundMuted") : LocaleController.getString("StorySoundNotMuted"), muteHint.shown()); + muteHint.show(); + setIconMuted(outputEntry.muted, true); + previewView.mute(outputEntry.muted); + }); + muteButton.setVisibility(View.GONE); + muteButton.setAlpha(0f); + actionBarContainer.addView(muteButton, LayoutHelper.createFrame(56, 56, Gravity.TOP | Gravity.RIGHT, 0, 0, 48, 0)); + + flashButton = new ImageView(context); + flashButton.setScaleType(ImageView.ScaleType.CENTER); + flashButton.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.MULTIPLY)); + flashButton.setBackground(Theme.createSelectorDrawable(0x20ffffff)); + flashButton.setOnClickListener(e -> { + if (cameraView == null || awaitingPlayer) { + return; + } + CameraSession cameraSession = cameraView.getCameraSession(); + if (cameraSession == null) { + return; + } + String current = cameraSession.getCurrentFlashMode(); + String next = cameraSession.getNextFlashMode(); + if (current.equals(next)) { + return; + } + cameraView.getCameraSession().setCurrentFlashMode(next); + setCameraFlashModeIcon(next, true); + }); + flashButton.setVisibility(View.GONE); + flashButton.setAlpha(0f); + actionBarContainer.addView(flashButton, LayoutHelper.createFrame(56, 56, Gravity.TOP | Gravity.RIGHT)); + + dualButton = new ToggleButton(context, R.drawable.media_dual_camera2_shadow, R.drawable.media_dual_camera2); + dualButton.setOnClickListener(v -> { + if (cameraView == null || currentPage != PAGE_CAMERA) { + return; + } + cameraView.toggleDual(); + dualButton.setValue(cameraView.isDual()); + + dualHint.hide(); + MessagesController.getGlobalMainSettings().edit().putInt("storydualhint", 2).apply(); + if (savedDualHint.shown()) { + MessagesController.getGlobalMainSettings().edit().putInt("storysvddualhint", 2).apply(); + } + savedDualHint.hide(); + }); + dualButton.setVisibility(DualCameraView.dualAvailableStatic(context) ? View.VISIBLE : View.GONE); + actionBarContainer.addView(dualButton, LayoutHelper.createFrame(56, 56, Gravity.TOP | Gravity.RIGHT)); + + dualHint = new HintView2(activity, HintView2.DIRECTION_TOP) + .setJoint(1, -20) + .setDuration(5000) + .setCloseButton(true) + .setText(LocaleController.getString(R.string.StoryCameraDualHint)) + .setOnHiddenListener(() -> MessagesController.getGlobalMainSettings().edit().putInt("storydualhint", MessagesController.getGlobalMainSettings().getInt("storydualhint", 0) + 1).apply()); + dualHint.setPadding(dp(8), 0, dp(8), 0); + actionBarContainer.addView(dualHint, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP, 0, 52, 0, 0)); + + savedDualHint = new HintView2(activity, HintView2.DIRECTION_RIGHT) + .setJoint(0, 56 / 2) + .setDuration(5000) + .setMultilineText(true); + actionBarContainer.addView(savedDualHint, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP, 0, 0, 52, 0)); + + videoTimerView = new VideoTimerView(context); + showVideoTimer(false, false); + actionBarContainer.addView(videoTimerView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 45, Gravity.TOP | Gravity.FILL_HORIZONTAL, 56, 0, 56, 0)); + + if (Build.VERSION.SDK_INT >= 21) { + MediaController.loadGalleryPhotosAlbums(0); + } + + recordControl = new RecordControl(context); + recordControl.setDelegate(recordControlDelegate); + recordControl.startAsVideo(isVideo); + controlContainer.addView(recordControl, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 100, Gravity.BOTTOM | Gravity.FILL_HORIZONTAL)); + cameraHint = new HintView2(activity, HintView2.DIRECTION_BOTTOM) + .setMultilineText(true) + .setText(LocaleController.getString(R.string.StoryCameraHint2)) + .setMaxWidth(320) + .setDuration(5000L) + .setTextAlign(Layout.Alignment.ALIGN_CENTER); + controlContainer.addView(cameraHint, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.BOTTOM, 0, 0, 0, 100)); + + zoomControlView = new ZoomControlView(context); + zoomControlView.enabledTouch = false; + zoomControlView.setAlpha(0.0f); + controlContainer.addView(zoomControlView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 50, Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM, 0, 0, 0, 100 + 8)); + zoomControlView.setDelegate(zoom -> { + if (cameraView != null) { + cameraView.setZoom(cameraZoom = zoom); + } + showZoomControls(true, true); + }); + zoomControlView.setZoom(cameraZoom = 0, false); + + modeSwitcherView = new PhotoVideoSwitcherView(context); + modeSwitcherView.setOnSwitchModeListener(newIsVideo -> { + if (takingPhoto || takingVideo) { + return; + } + + isVideo = newIsVideo; + showVideoTimer(isVideo, true); + modeSwitcherView.switchMode(isVideo); + recordControl.startAsVideo(isVideo); + }); + modeSwitcherView.setOnSwitchingModeListener(t -> { + recordControl.startAsVideoT(t); + }); + navbarContainer.addView(modeSwitcherView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.BOTTOM | Gravity.FILL_HORIZONTAL)); + + hintTextView = new HintTextView(context); + navbarContainer.addView(hintTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 32, Gravity.CENTER, 8, 0, 8, 8)); + + previewButtons = new PreviewButtons(context); + previewButtons.setVisibility(View.GONE); + previewButtons.setOnClickListener((Integer btn) -> { + if (outputEntry == null) { + return; + } + captionEdit.clearFocus(); + if (btn == PreviewButtons.BUTTON_SHARE) { + if (privacySheet != null) { + privacySheet.dismiss(); + privacySheet = null; + } + if (!previewButtons.isShareEnabled()) { + downloadButton.showFailedVideo(); + BotWebViewVibrationEffect.APP_ERROR.vibrate(); + AndroidUtilities.shakeViewSpring(previewButtons.shareButton, shiftDp = -shiftDp); + return; + } + if (outputEntry.isEdit) { + outputEntry.editedPrivacy = false; + applyFilter(null); + upload(true); + } else { + previewView.updatePauseReason(3, true); + privacySheet = new StoryPrivacyBottomSheet(activity, outputEntry.period, resourcesProvider) + .setValue(outputEntry.privacy) + .whenDismiss(privacy -> { + if (outputEntry != null) { + outputEntry.privacy = privacy; + } + }) + .isEdit(false) + .setWarnUsers(getUsersFrom(captionEdit.getText())) + .whenSelectedRules((privacy, allowScreenshots, keepInProfile, whenDone) -> { + if (outputEntry == null) { + return; + } + previewView.updatePauseReason(5, true); + outputEntry.privacy = privacy; + StoryPrivacySelector.save(currentAccount, outputEntry.privacy); + outputEntry.pinned = keepInProfile; + outputEntry.allowScreenshots = allowScreenshots; + outputEntry.privacyRules.clear(); + outputEntry.privacyRules.addAll(privacy.rules); + outputEntry.editedPrivacy = true; + applyFilter(() -> { + whenDone.run(); + upload(true); + }); + }, false); + privacySheet.setOnDismissListener(di -> { + previewView.updatePauseReason(3, false); + privacySheet = null; + }); + privacySheet.show(); + } + } else if (btn == PreviewButtons.BUTTON_PAINT) { + switchToEditMode(EDIT_MODE_PAINT, true); + if (paintView != null) { + paintView.enteredThroughText = false; + paintView.openPaint(); + } + } else if (btn == PreviewButtons.BUTTON_TEXT) { + switchToEditMode(EDIT_MODE_PAINT, true); + if (paintView != null) { + paintView.openText(); + paintView.enteredThroughText = true; + } + } else if (btn == PreviewButtons.BUTTON_STICKER) { + createPhotoPaintView(); + hidePhotoPaintView(); + if (paintView != null) { + paintView.openStickers(); + } + } else if (btn == PreviewButtons.BUTTON_ADJUST) { + switchToEditMode(EDIT_MODE_FILTER, true); + } + }); + navbarContainer.addView(previewButtons, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 52, Gravity.CENTER_VERTICAL | Gravity.FILL_HORIZONTAL)); + + trash = new TrashView(context); + trash.setAlpha(0f); + trash.setVisibility(View.GONE); + previewContainer.addView(trash, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 120, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, 0, 0, 16)); + + previewHighlight = new PreviewHighlightView(context, currentAccount, resourcesProvider); + previewContainer.addView(previewHighlight, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); + } + + private ArrayList getUsersFrom(CharSequence caption) { + ArrayList users = new ArrayList<>(); + if (caption instanceof Spanned) { + URLSpanUserMention[] spans = ((Spanned) caption).getSpans(0, caption.length(), URLSpanUserMention.class); + for (int i = 0; i < spans.length; ++i) { + URLSpanUserMention span = spans[i]; + if (span != null) { + try { + Long userId = Long.parseLong(span.getURL()); + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(userId); + if (user != null && UserObject.getPublicUsername(user) != null && !users.contains(user)) { + users.add(UserObject.getPublicUsername(user)); + } + } catch (Exception ignore) {} + } + } + } + if (caption != null) { + int u = -1; + for (int i = 0; i < caption.length(); ++i) { + char c = caption.charAt(i); + if (c == '@') { + u = i + 1; + } else if (c == ' ') { + if (u != -1) { + String username = caption.subSequence(u, i).toString(); + TLObject obj = MessagesController.getInstance(currentAccount).getUserOrChat(username); + if (obj instanceof TLRPC.User && !((TLRPC.User) obj).bot && ((TLRPC.User) obj).id != 777000 && !UserObject.isReplyUser((TLRPC.User) obj) && !users.contains(username)) { + users.add(username); + } + } + u = -1; + } + } + if (u != -1) { + String username = caption.subSequence(u, caption.length()).toString(); + TLObject obj = MessagesController.getInstance(currentAccount).getUserOrChat(username); + if (obj instanceof TLRPC.User && !((TLRPC.User) obj).bot && ((TLRPC.User) obj).id != 777000 && !UserObject.isReplyUser((TLRPC.User) obj) && !users.contains(username)) { + users.add(username); + } + } + } + return users; + } + + private DraftSavedHint getDraftSavedHint() { + if (draftSavedHint == null) { + draftSavedHint = new DraftSavedHint(getContext()); + controlContainer.addView(draftSavedHint, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM | Gravity.FILL_HORIZONTAL, 0, 0, 0, 66 + 12)); + } + return draftSavedHint; + } + + private void upload(boolean asStory) { + applyPaint(true); + if (outputEntry == null) { + close(true); + return; + } + destroyPhotoFilterView(); + prepareThumb(outputEntry, false); + CharSequence caption = captionEdit.getText(); + outputEntry.editedCaption = !TextUtils.equals(outputEntry.caption, caption); + outputEntry.caption = caption; + MessagesController.getInstance(currentAccount).getStoriesController().uploadStory(outputEntry, asStory); + if (outputEntry.isDraft) { + MessagesController.getInstance(currentAccount).getStoriesController().getDraftsController().delete(outputEntry); + } + outputEntry.cancelCheckStickers(); + outputEntry = null; + + wasSend = true; + forceBackgroundVisible = true; + checkBackgroundVisibility(); + + Runnable runnable = () -> { + if (asStory) { + if (fromSourceView != null) { + fromSourceView.show(); + fromSourceView = null; + } + fromSourceView = closingSourceProvider != null ? closingSourceProvider.getView() : null; + if (fromSourceView != null) { + openType = fromSourceView.type; + containerView.updateBackground(); + previewContainer.setBackgroundColor(openType == 1 ? 0 : 0xff1f1f1f); + fromRect.set(fromSourceView.screenRect); + fromRounding = fromSourceView.rounding; + fromSourceView.hide(); + + if (waveEffect == null && SharedConfig.getDevicePerformanceClass() > SharedConfig.PERFORMANCE_CLASS_AVERAGE && LiteMode.isEnabled(LiteMode.FLAGS_CHAT) && false) { + waveEffect = new StoryWaveEffectView(getContext(), fromSourceView.screenRect.centerX(), fromSourceView.screenRect.centerY(), fromSourceView.screenRect.width() / 2f); + } + } + closingSourceProvider = null; + + if (activity instanceof LaunchActivity) { + ((LaunchActivity) activity).drawerLayoutContainer.post(() -> { + if (waveEffect != null) { + waveEffect.prepare(); + } + close(true); + }); + } else { + close(true); + } + } else { + close(true); + } + }; + if (closingSourceProvider != null) { + closingSourceProvider.preLayout(runnable); + } else { + runnable.run(); + } + MessagesController.getGlobalMainSettings().edit().putInt("storyhint2", 2).apply(); + } + + private File prepareThumb(StoryEntry storyEntry, boolean forDraft) { + if (storyEntry == null || previewView.getWidth() <= 0 || previewView.getHeight() <= 0) { + return null; + } + if (!forDraft && !storyEntry.wouldBeVideo() && !storyEntry.isEdit) { + return null; + } + File file = forDraft ? storyEntry.draftThumbFile : storyEntry.uploadThumbFile; + if (file != null) { + file.delete(); + file = null; + } + + final float scale = forDraft ? 1 / 3f : 1f; + final int w = (int) (previewView.getWidth() * scale); + final int h = (int) (previewView.getHeight() * scale); + Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.RGB_565); + Canvas canvas = new Canvas(bitmap); + + canvas.save(); + canvas.scale(scale, scale); + previewView.draw(canvas); + canvas.restore(); + + final Paint bitmapPaint = new Paint(Paint.FILTER_BITMAP_FLAG); + + TextureView textureView = previewView.getTextureView(); + if (storyEntry.isVideo && textureView != null) { + Bitmap previewTextureView = textureView.getBitmap(); + Matrix matrix = textureView.getTransform(null); + if (matrix != null) { + matrix = new Matrix(matrix); + matrix.postScale(scale, scale); + } + canvas.drawBitmap(previewTextureView, matrix, bitmapPaint); + previewTextureView.recycle(); + } + + if (storyEntry.paintFile != null) { + try { + Bitmap paintBitmap = BitmapFactory.decodeFile(storyEntry.paintFile.getPath()); + canvas.save(); + float scale2 = w / (float) paintBitmap.getWidth(); + canvas.scale(scale2, scale2); + canvas.drawBitmap(paintBitmap, 0, 0, bitmapPaint); + canvas.restore(); + paintBitmap.recycle(); + } catch (Exception e) { + FileLog.e(e); + } + } + + if (paintView != null && paintView.entitiesView != null) { + canvas.save(); + canvas.scale(scale, scale); + paintView.entitiesView.draw(canvas); + canvas.restore(); + } + + Bitmap thumbBitmap = Bitmap.createScaledBitmap(bitmap, 40, 22, true); + + file = StoryEntry.makeCacheFile(currentAccount, false); + try { + bitmap.compress(Bitmap.CompressFormat.JPEG, forDraft ? 95 : 75, new FileOutputStream(file)); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + bitmap.recycle(); + + if (forDraft) { + storyEntry.draftThumbFile = file; + } else { + storyEntry.uploadThumbFile = file; + } + storyEntry.thumbBitmap = thumbBitmap; + return file; + } + + private void setCameraFlashModeIcon(String mode, boolean animated) { + flashButton.clearAnimation(); + if (cameraView != null && cameraView.isDual() || animatedRecording) { + mode = null; + } + if (mode == null) { + if (animated) { + flashButton.setVisibility(View.VISIBLE); + flashButton.animate().alpha(0).withEndAction(() -> { + flashButton.setVisibility(View.GONE); + }).start(); + } else { + flashButton.setVisibility(View.GONE); + flashButton.setAlpha(0f); + } + return; + } + final int resId; + switch (mode) { + case Camera.Parameters.FLASH_MODE_ON: + resId = R.drawable.media_photo_flash_on2; + flashButton.setContentDescription(LocaleController.getString("AccDescrCameraFlashOn", R.string.AccDescrCameraFlashOn)); + break; + case Camera.Parameters.FLASH_MODE_AUTO: + resId = R.drawable.media_photo_flash_auto2; + flashButton.setContentDescription(LocaleController.getString("AccDescrCameraFlashAuto", R.string.AccDescrCameraFlashAuto)); + break; + default: + case Camera.Parameters.FLASH_MODE_OFF: + resId = R.drawable.media_photo_flash_off2; + flashButton.setContentDescription(LocaleController.getString("AccDescrCameraFlashOff", R.string.AccDescrCameraFlashOff)); + break; + } + if (animated && flashButtonResId != resId) { + AndroidUtilities.updateImageViewImageAnimated(flashButton, flashButtonResId = resId); + } else { + flashButton.setImageResource(flashButtonResId = resId); + } + flashButton.setVisibility(View.VISIBLE); + if (animated) { + flashButton.animate().alpha(1f).start(); + } else { + flashButton.setAlpha(1f); + } + } + + private final RecordControl.Delegate recordControlDelegate = new RecordControl.Delegate() { + @Override + public boolean canRecordAudio() { + return requestAudioPermission(); + } + + @Override + public void onPhotoShoot() { + if (takingPhoto || awaitingPlayer || currentPage != PAGE_CAMERA || cameraView == null || !cameraView.isInited()) { + return; + } + cameraHint.hide(); + if (outputFile != null) { + try { + outputFile.delete(); + } catch (Exception ignore) {} + outputFile = null; + } + outputFile = StoryEntry.makeCacheFile(currentAccount, false); + cameraView.startTakePictureAnimation(); + boolean savedFromTextureView = false; + if (cameraView.isDual() && TextUtils.equals(cameraView.getCameraSession().getCurrentFlashMode(), Camera.Parameters.FLASH_MODE_OFF)) { + takingPhoto = true; + cameraView.pauseAsTakingPicture(); + final Bitmap bitmap = cameraView.getTextureView().getBitmap(); + try (FileOutputStream out = new FileOutputStream(outputFile.getAbsoluteFile())) { + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out); + savedFromTextureView = true; + } catch (Exception e) { + FileLog.e(e); + } + bitmap.recycle(); + } + if (!savedFromTextureView) { + final CameraSession cameraSession = cameraView.getCameraSession(); + takingPhoto = CameraController.getInstance().takePicture(outputFile, true, cameraSession, (orientation) -> { + takingPhoto = false; + if (outputFile == null) { + return; + } + int rotate = orientation == -1 ? 0 : 90; + if (orientation == -1) { + int w = -1, h = -1; + try { + BitmapFactory.Options opts = new BitmapFactory.Options(); + opts.inJustDecodeBounds = true; + BitmapFactory.decodeFile(outputFile.getAbsolutePath(), opts); + w = opts.outWidth; + h = opts.outHeight; + } catch (Exception ignore) {} + if (w > h) { + rotate = 270; + } + } + outputEntry = StoryEntry.fromPhotoShoot(outputFile, rotate); + StoryPrivacySelector.applySaved(currentAccount, outputEntry); + fromGallery = false; + navigateTo(PAGE_PREVIEW, true); + }); + } else { + takingPhoto = false; + outputEntry = StoryEntry.fromPhotoShoot(outputFile, 0); + StoryPrivacySelector.applySaved(currentAccount, outputEntry); + fromGallery = false; + navigateTo(PAGE_PREVIEW, true); + } + } + + @Override + public void onVideoRecordStart(boolean byLongPress, Runnable whenStarted) { + if (takingVideo || stoppingTakingVideo || awaitingPlayer || currentPage != PAGE_CAMERA || cameraView == null || cameraView.getCameraSession() == null) { + return; + } + if (dualHint != null) { + dualHint.hide(); + } + if (savedDualHint != null) { + savedDualHint.hide(); + } + cameraHint.hide(); + takingVideo = true; + if (outputFile != null) { + try { + outputFile.delete(); + } catch (Exception ignore) {} + outputFile = null; + } + outputFile = StoryEntry.makeCacheFile(currentAccount, true); + CameraController.getInstance().recordVideo(cameraView.getCameraSession(), outputFile, false, (thumbPath, duration) -> { + if (recordControl != null) { + recordControl.stopRecordingLoading(true); + } + if (outputFile == null || cameraView == null) { + return; + } + + takingVideo = false; + stoppingTakingVideo = false; + + if (duration <= 800) { + animateRecording(false, true); + setAwakeLock(false); + videoTimerView.setRecording(false, true); + if (recordControl != null) { + recordControl.stopRecordingLoading(true); + } + try { + outputFile.delete(); + outputFile = null; + } catch (Exception e) { + FileLog.e(e); + } + if (thumbPath != null) { + try { + new File(thumbPath).delete(); + } catch (Exception e) { + FileLog.e(e); + } + } + return; + } + + showVideoTimer(false, true); + + outputEntry = StoryEntry.fromVideoShoot(outputFile, thumbPath, duration); + StoryPrivacySelector.applySaved(currentAccount, outputEntry); + fromGallery = false; + int width = cameraView.getVideoWidth(), height = cameraView.getVideoHeight(); + if (width > 0 && height > 0) { + outputEntry.width = width; + outputEntry.height = height; + outputEntry.setupMatrix(); + } + navigateToPreviewWithPlayerAwait(() -> { + navigateTo(PAGE_PREVIEW, true); + }, 0); + }, () /* onVideoStart */ -> { + whenStarted.run(); + + hintTextView.setText(byLongPress ? LocaleController.getString("StoryHintSwipeToZoom", R.string.StoryHintSwipeToZoom) : LocaleController.getString("StoryHintPinchToZoom", R.string.StoryHintPinchToZoom), false); + animateRecording(true, true); + setAwakeLock(true); + + videoTimerView.setRecording(true, true); + showVideoTimer(true, true); + }, cameraView, false); + + if (!isVideo) { + isVideo = true; + showVideoTimer(isVideo, true); + modeSwitcherView.switchMode(isVideo); + recordControl.startAsVideo(isVideo); + } + } + + @Override + public void onVideoRecordLocked() { + hintTextView.setText(LocaleController.getString("StoryHintPinchToZoom", R.string.StoryHintPinchToZoom), true); + } + + @Override + public void onVideoRecordPause() { + + } + + @Override + public void onVideoRecordResume() { + + } + + @Override + public void onVideoRecordEnd(boolean byDuration) { + if (stoppingTakingVideo || !takingVideo) { + return; + } + stoppingTakingVideo = true; + AndroidUtilities.runOnUIThread(() -> { + if (takingVideo && stoppingTakingVideo && cameraView != null) { + showZoomControls(false, true); +// animateRecording(false, true); +// setAwakeLock(false); + CameraController.getInstance().stopVideoRecording(cameraView.getCameraSessionRecording(), false, false); + } + }, byDuration ? 0 : 400); + } + + @Override + public void onVideoDuration(long duration) { + videoTimerView.setDuration(duration, true); + } + + @Override + public void onGalleryClick() { + if (currentPage == PAGE_CAMERA && requestGalleryPermission()) { + animateGalleryListView(true); + } + } + + @Override + public void onFlipClick() { + if (cameraView == null || awaitingPlayer || takingPhoto || !cameraView.isInited() || currentPage != PAGE_CAMERA) { + return; + } + if (savedDualHint != null) { + savedDualHint.hide(); + } + cameraView.switchCamera(); + saveCameraFace(cameraView.isFrontface()); + } + + @Override + public void onFlipLongClick() { + if (cameraView != null) { + cameraView.toggleDual(); + } + } + + @Override + public void onZoom(float zoom) { + zoomControlView.setZoom(zoom, true); + showZoomControls(false, true); + } + }; + + private void setAwakeLock(boolean lock) { + if (lock) { + windowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; + } else { + windowLayoutParams.flags &= ~WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; + } + try { + windowManager.updateViewLayout(windowView, windowLayoutParams); + } catch (Exception e) { + FileLog.e(e); + } + } + + private AnimatorSet recordingAnimator; + private boolean animatedRecording; + private void animateRecording(boolean recording, boolean animated) { + if (recording) { + if (dualHint != null) { + dualHint.hide(); + } + if (savedDualHint != null) { + savedDualHint.hide(); + } + if (muteHint != null) { + muteHint.hide(); + } + if (cameraHint != null) { + cameraHint.hide(); + } + } + if (animatedRecording == recording) { + return; + } + if (recordingAnimator != null) { + recordingAnimator.cancel(); + recordingAnimator = null; + } + animatedRecording = recording; + if (animated) { + backButton.setVisibility(View.VISIBLE); + flashButton.setVisibility(View.VISIBLE); + dualButton.setVisibility(cameraView != null && cameraView.dualAvailable() ? View.VISIBLE : View.GONE); + recordingAnimator = new AnimatorSet(); + recordingAnimator.playTogether( + ObjectAnimator.ofFloat(backButton, View.ALPHA, recording ? 0 : 1), + ObjectAnimator.ofFloat(flashButton, View.ALPHA, recording || currentPage != PAGE_CAMERA ? 0 : 1), + ObjectAnimator.ofFloat(dualButton, View.ALPHA, recording || currentPage != PAGE_CAMERA || cameraView == null || !cameraView.dualAvailable() ? 0 : 1), + ObjectAnimator.ofFloat(hintTextView, View.ALPHA, recording && currentPage == PAGE_CAMERA ? 1 : 0), + ObjectAnimator.ofFloat(hintTextView, View.TRANSLATION_Y, recording || currentPage != PAGE_CAMERA ? 0 : dp(16)), + ObjectAnimator.ofFloat(modeSwitcherView, View.ALPHA, recording || currentPage != PAGE_CAMERA ? 0 : 1), + ObjectAnimator.ofFloat(modeSwitcherView, View.TRANSLATION_Y, recording || currentPage != PAGE_CAMERA ? dp(16) : 0) + ); + recordingAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (recording) { + backButton.setVisibility(View.GONE); + } + if (recording || currentPage != PAGE_CAMERA) { + flashButton.setVisibility(View.GONE); + } + } + }); + recordingAnimator.setDuration(260); + recordingAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + recordingAnimator.start(); + } else { + backButton.setAlpha(recording ? 0 : 1f); + backButton.setVisibility(recording ? View.GONE : View.VISIBLE); + flashButton.setAlpha(recording || currentPage != PAGE_CAMERA ? 0 : 1f); + flashButton.setVisibility(recording || currentPage != PAGE_CAMERA ? View.GONE : View.VISIBLE); + dualButton.setAlpha(recording || currentPage != PAGE_CAMERA ? 0 : 1f); + dualButton.setVisibility(recording || currentPage != PAGE_CAMERA || cameraView == null || !cameraView.dualAvailable() ? View.GONE : View.VISIBLE); + hintTextView.setAlpha(recording && currentPage == PAGE_CAMERA ? 1f : 0); + hintTextView.setTranslationY(recording || currentPage != PAGE_CAMERA ? 0 : dp(16)); + modeSwitcherView.setAlpha(recording || currentPage != PAGE_CAMERA ? 0 : 1f); + modeSwitcherView.setTranslationY(recording || currentPage != PAGE_CAMERA ? dp(16) : 0); + } + } + + private boolean videoTimerShown = true; + private void showVideoTimer(boolean show, boolean animated) { + if (videoTimerShown == show) { + return; + } + + videoTimerShown = show; + if (animated) { + videoTimerView.animate().alpha(show ? 1 : 0).setDuration(350).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).withEndAction(() -> { + if (!show) { + videoTimerView.setRecording(false, false); + } + }).start(); + } else { + videoTimerView.clearAnimation(); + videoTimerView.setAlpha(show ? 1 : 0); + if (!show) { + videoTimerView.setRecording(false, false); + } + } + } + + private Runnable zoomControlHideRunnable; + private AnimatorSet zoomControlAnimation; + + private void showZoomControls(boolean show, boolean animated) { + if (zoomControlView.getTag() != null && show || zoomControlView.getTag() == null && !show) { + if (show) { + if (zoomControlHideRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(zoomControlHideRunnable); + } + AndroidUtilities.runOnUIThread(zoomControlHideRunnable = () -> { + showZoomControls(false, true); + zoomControlHideRunnable = null; + }, 2000); + } + return; + } + if (zoomControlAnimation != null) { + zoomControlAnimation.cancel(); + } + zoomControlView.setTag(show ? 1 : null); + zoomControlAnimation = new AnimatorSet(); + zoomControlAnimation.setDuration(180); + if (show) { + zoomControlView.setVisibility(View.VISIBLE); + } + zoomControlAnimation.playTogether(ObjectAnimator.ofFloat(zoomControlView, View.ALPHA, show ? 1.0f : 0.0f)); + zoomControlAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (!show) { + zoomControlView.setVisibility(View.GONE); + } + zoomControlAnimation = null; + } + }); + zoomControlAnimation.start(); + if (show) { + AndroidUtilities.runOnUIThread(zoomControlHideRunnable = () -> { + showZoomControls(false, true); + zoomControlHideRunnable = null; + }, 2000); + } + } + + public boolean onBackPressed() { + if (takingVideo) { + recordControl.stopRecording(); + return false; + } + if (takingPhoto) { + return false; + } + if (captionEdit.onBackPressed()) { + return false; + } else if (galleryListView != null) { + animateGalleryListView(false); + lastGallerySelectedAlbum = null; + return false; + } else if (currentEditMode == EDIT_MODE_PAINT && paintView != null && paintView.onBackPressed()) { + return false; + } else if (currentEditMode > EDIT_MODE_NONE) { + switchToEditMode(EDIT_MODE_NONE, true); + return false; + } else if (currentPage == PAGE_PREVIEW && (outputEntry == null || !outputEntry.isEdit)) { + if (fromGallery && (paintView == null || !paintView.hasChanges()) && (outputEntry == null || outputEntry.filterFile == null) || !previewButtons.isShareEnabled()) { + navigateTo(PAGE_CAMERA, true); + } else { + showDismissEntry(); + } + return false; + } else { + close(true); + return true; + } + } + + private Runnable afterPlayerAwait; + private boolean previewAlreadySet; + public void navigateToPreviewWithPlayerAwait(Runnable open, long seekTo) { + if (awaitingPlayer || outputEntry == null) { + return; + } + if (afterPlayerAwait != null) { + AndroidUtilities.cancelRunOnUIThread(afterPlayerAwait); + } + previewAlreadySet = true; + awaitingPlayer = true; + afterPlayerAwait = () -> { + animateGalleryListView(false); + AndroidUtilities.cancelRunOnUIThread(afterPlayerAwait); + afterPlayerAwait = null; + awaitingPlayer = false; + open.run(); + }; + previewView.setAlpha(0f); + previewView.setVisibility(View.VISIBLE); + previewView.set(outputEntry, afterPlayerAwait, seekTo); + AndroidUtilities.runOnUIThread(afterPlayerAwait, 400); + } + + private AnimatorSet pageAnimator; + public void navigateTo(int page, boolean animated) { + if (page == currentPage) { + return; + } + + final int oldPage = currentPage; + currentPage = page; + + if (pageAnimator != null) { + pageAnimator.cancel(); + } + + onNavigateStart(oldPage, page); + if (previewButtons != null) { + previewButtons.appear(page == PAGE_PREVIEW && openProgress > 0, animated); + } + showVideoTimer(page == PAGE_CAMERA && isVideo, animated); + if (page != PAGE_PREVIEW) { + videoTimeView.show(false, animated); + } + if (animated) { + pageAnimator = new AnimatorSet(); + + ArrayList animators = new ArrayList<>(); + + if (cameraView != null) { + animators.add(ObjectAnimator.ofFloat(cameraView, View.ALPHA, page == PAGE_CAMERA ? 1 : 0)); + } + cameraViewThumb.setVisibility(View.VISIBLE); + animators.add(ObjectAnimator.ofFloat(cameraViewThumb, View.ALPHA, page == PAGE_CAMERA ? 1 : 0)); + animators.add(ObjectAnimator.ofFloat(previewView, View.ALPHA, page == PAGE_PREVIEW ? 1 : 0)); + + animators.add(ObjectAnimator.ofFloat(recordControl, View.ALPHA, page == PAGE_CAMERA ? 1 : 0)); + animators.add(ObjectAnimator.ofFloat(flashButton, View.ALPHA, page == PAGE_CAMERA ? 1 : 0)); + animators.add(ObjectAnimator.ofFloat(dualButton, View.ALPHA, page == PAGE_CAMERA && cameraView != null && cameraView.dualAvailable() ? 1 : 0)); + animators.add(ObjectAnimator.ofFloat(recordControl, View.TRANSLATION_Y, page == PAGE_CAMERA ? 0 : dp(24))); + animators.add(ObjectAnimator.ofFloat(modeSwitcherView, View.ALPHA, page == PAGE_CAMERA ? 1 : 0)); + animators.add(ObjectAnimator.ofFloat(modeSwitcherView, View.TRANSLATION_Y, page == PAGE_CAMERA ? 0 : dp(24))); + backButton.setVisibility(View.VISIBLE); + animators.add(ObjectAnimator.ofFloat(backButton, View.ALPHA, 1)); + animators.add(ObjectAnimator.ofFloat(hintTextView, View.ALPHA, page == PAGE_CAMERA && animatedRecording ? 1 : 0)); + animators.add(ObjectAnimator.ofFloat(captionContainer, View.ALPHA, page == PAGE_PREVIEW ? 1f : 0)); + animators.add(ObjectAnimator.ofFloat(captionContainer, View.TRANSLATION_Y, page == PAGE_PREVIEW ? 0 : dp(12))); + animators.add(ObjectAnimator.ofFloat(titleTextView, View.ALPHA, page == PAGE_PREVIEW ? 1f : 0)); + + animators.add(ObjectAnimator.ofFloat(videoTimelineView, View.ALPHA, page == PAGE_PREVIEW && isVideo ? 1f : 0)); + + animators.add(ObjectAnimator.ofFloat(muteButton, View.ALPHA, page == PAGE_PREVIEW && isVideo ? 1f : 0)); + animators.add(ObjectAnimator.ofFloat(downloadButton, View.ALPHA, page == PAGE_PREVIEW ? 1f : 0)); +// animators.add(ObjectAnimator.ofFloat(privacySelector, View.ALPHA, page == PAGE_PREVIEW ? 1f : 0)); + + animators.add(ObjectAnimator.ofFloat(zoomControlView, View.ALPHA, 0)); + + pageAnimator.playTogether(animators); + pageAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + onNavigateEnd(oldPage, page); + } + }); + pageAnimator.setDuration(460); + pageAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + pageAnimator.start(); + } else { + if (cameraView != null) { + cameraView.setAlpha(page == PAGE_CAMERA ? 1 : 0); + } + cameraViewThumb.setAlpha(page == PAGE_CAMERA ? 1f : 0); + cameraViewThumb.setVisibility(page == PAGE_CAMERA ? View.VISIBLE : View.GONE); + previewView.setAlpha(page == PAGE_PREVIEW ? 1f : 0); + flashButton.setAlpha(page == PAGE_CAMERA ? 1f : 0); + dualButton.setAlpha(page == PAGE_CAMERA && cameraView != null && cameraView.dualAvailable() ? 1f : 0); + recordControl.setAlpha(page == PAGE_CAMERA ? 1f : 0); + recordControl.setTranslationY(page == PAGE_CAMERA ? 0 : dp(16)); + modeSwitcherView.setAlpha(page == PAGE_CAMERA ? 1f : 0); + modeSwitcherView.setTranslationY(page == PAGE_CAMERA ? 0 : dp(16)); + backButton.setVisibility(View.VISIBLE); + backButton.setAlpha(1f); + hintTextView.setAlpha(page == PAGE_CAMERA && animatedRecording ? 1f : 0); + captionContainer.setAlpha(page == PAGE_PREVIEW ? 1f : 0); + captionContainer.setTranslationY(page == PAGE_PREVIEW ? 0 : dp(12)); + muteButton.setAlpha(page == PAGE_PREVIEW && isVideo ? 1f : 0); + downloadButton.setAlpha(page == PAGE_PREVIEW ? 1f : 0); +// privacySelector.setAlpha(page == PAGE_PREVIEW ? 1f : 0); + videoTimelineView.setAlpha(page == PAGE_PREVIEW && isVideo ? 1f : 0); + titleTextView.setAlpha(page == PAGE_PREVIEW ? 1f : 0f); + onNavigateEnd(oldPage, page); + } + } + + private ValueAnimator containerViewBackAnimator; + private boolean applyContainerViewTranslation2 = true; + private void animateContainerBack() { + if (containerViewBackAnimator != null) { + containerViewBackAnimator.cancel(); + containerViewBackAnimator = null; + } + applyContainerViewTranslation2 = false; + float y1 = containerView.getTranslationY1(), y2 = containerView.getTranslationY2(), a = containerView.getAlpha(); + containerViewBackAnimator = ValueAnimator.ofFloat(1, 0); + containerViewBackAnimator.addUpdateListener(anm -> { + final float t = (float) anm.getAnimatedValue(); + containerView.setTranslationY(y1 * t); + containerView.setTranslationY2(y2 * t); +// containerView.setAlpha(AndroidUtilities.lerp(a, 1f, t)); + }); + containerViewBackAnimator.setDuration(340); + containerViewBackAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + containerViewBackAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + containerViewBackAnimator = null; + containerView.setTranslationY(0); + containerView.setTranslationY2(0); + } + }); + containerViewBackAnimator.start(); + } + + private Parcelable lastGalleryScrollPosition; + private MediaController.AlbumEntry lastGallerySelectedAlbum; + + private void createGalleryListView() { + createGalleryListView(false); + } + + private void destroyGalleryListView() { + if (galleryListView == null) { + return; + } + windowView.removeView(galleryListView); + galleryListView = null; + if (galleryOpenCloseAnimator != null) { + galleryOpenCloseAnimator.cancel(); + galleryOpenCloseAnimator = null; + } + if (galleryOpenCloseSpringAnimator != null) { + galleryOpenCloseSpringAnimator.cancel(); + galleryOpenCloseSpringAnimator = null; + } + galleryListViewOpening = null; + } + + private void createGalleryListView(boolean forAddingPart) { + if (galleryListView != null || getContext() == null) { + return; + } + + galleryListView = new GalleryListView(currentAccount, getContext(), resourcesProvider, lastGallerySelectedAlbum, forAddingPart) { + @Override + public void setTranslationY(float translationY) { + super.setTranslationY(translationY); + if (applyContainerViewTranslation2) { + final float amplitude = windowView.getMeasuredHeight() - galleryListView.top(); + float t = Utilities.clamp(1f - translationY / amplitude, 1, 0); + containerView.setTranslationY2(t * dp(-32)); + containerView.setAlpha(1 - .6f * t); + actionBarContainer.setAlpha(1f - t); + } + } + + @Override + public void firstLayout() { + galleryListView.setTranslationY(windowView.getMeasuredHeight() - galleryListView.top()); + if (galleryLayouted != null) { + galleryLayouted.run(); + galleryLayouted = null; + } + } + + @Override + protected void onFullScreen(boolean isFullscreen) { + if (currentPage == PAGE_CAMERA && isFullscreen) { + AndroidUtilities.runOnUIThread(() -> { + destroyCameraView(true); + cameraViewThumb.setImageDrawable(getCameraThumb()); + }); + } + } + }; + galleryListView.setOnBackClickListener(() -> { + animateGalleryListView(false); + lastGallerySelectedAlbum = null; + }); + galleryListView.setOnSelectListener((entry, blurredBitmap) -> { + if (entry == null || galleryListViewOpening != null || scrollingY || !isGalleryOpen()) { + return; + } + + if (forAddingPart) { + if (outputEntry == null || !(entry instanceof MediaController.PhotoEntry)) { + return; + } + MediaController.PhotoEntry photoEntry = (MediaController.PhotoEntry) entry; + createPhotoPaintView(); + outputEntry.editedMedia = true; + paintView.appearAnimation(paintView.createPhoto(photoEntry.path, false)); +// StoryEntry.Part part = new StoryEntry.Part(); +// part.id = outputEntry.partsMaxId++; +// part.file = new File(photoEntry.path); +// if (photoEntry.width <= 0 || photoEntry.height <= 0) { +// BitmapFactory.Options opts = new BitmapFactory.Options(); +// opts.inJustDecodeBounds = true; +// BitmapFactory.decodeFile(photoEntry.path, opts); +// part.width = opts.outWidth; +// part.height = opts.outHeight; +// } else { +// part.width = photoEntry.width; +// part.height = photoEntry.height; +// } +// part.fileDeletable = false; +// part.matrix.reset(); +// int width = part.width, height = part.height; +// part.matrix.postScale(photoEntry.invert == 1 ? -1.0f : 1.0f, photoEntry.invert == 2 ? -1.0f : 1.0f, width / 2f, height / 2f); +// if (photoEntry.orientation != 0) { +// part.matrix.postTranslate(-width / 2f, -height / 2f); +// part.matrix.postRotate(photoEntry.orientation); +// if (photoEntry.orientation == 90 || photoEntry.orientation == 270) { +// final int swap = height; +// height = width; +// width = swap; +// } +// part.matrix.postTranslate(width / 2f, height / 2f); +// } +// float scale = (float) outputEntry.resultWidth / width; +// if ((float) height / (float) width > 1.29f) { +// scale = Math.max(scale, (float) outputEntry.resultHeight / height); +// } +// part.matrix.postScale(scale, scale); +// part.matrix.postTranslate((outputEntry.resultWidth - width * scale) / 2f, (outputEntry.resultHeight - height * scale) / 2f); +// +// final float randScale = .5f; +//// final float hw = (1f - randScale) * outputEntry.resultWidth / 2f, hh = (1f - randScale) * outputEntry.resultHeight / 2f; +//// float randTranslateX = AndroidUtilities.lerp(-hw, hw, Utilities.fastRandom.nextFloat()); +//// float randTranslateY = AndroidUtilities.lerp(-hh, hh, Utilities.fastRandom.nextFloat()); +//// float randRotate = AndroidUtilities.lerp(-10, 10, Utilities.fastRandom.nextFloat()); +// part.matrix.postScale(randScale, randScale, outputEntry.resultWidth / 2f, outputEntry.resultHeight / 2f); +//// part.matrix.postRotate(randRotate, outputEntry.resultWidth / 2f, outputEntry.resultHeight / 2f); +//// part.matrix.postTranslate(randTranslateX, randTranslateY); +// outputEntry.parts.add(part); +// previewView.set(outputEntry); +// + animateGalleryListView(false); + } else { + if (entry instanceof MediaController.PhotoEntry) { + MediaController.PhotoEntry photoEntry = (MediaController.PhotoEntry) entry; + isVideo = photoEntry.isVideo; + outputEntry = StoryEntry.fromPhotoEntry(photoEntry); + StoryPrivacySelector.applySaved(currentAccount, outputEntry); + outputEntry.blurredVideoThumb = blurredBitmap; + fromGallery = true; + } else if (entry instanceof StoryEntry) { + StoryEntry storyEntry = (StoryEntry) entry; + if (storyEntry.file == null) { + downloadButton.showToast(R.raw.error, "Failed to load draft"); + MessagesController.getInstance(currentAccount).getStoriesController().getDraftsController().delete(storyEntry); + return; + } + + isVideo = storyEntry.isVideo; + outputEntry = storyEntry; + outputEntry.blurredVideoThumb = blurredBitmap; + fromGallery = false; + } + + showVideoTimer(false, true); + modeSwitcherView.switchMode(isVideo); + recordControl.startAsVideo(isVideo); + + animateGalleryListView(false); + navigateTo(PAGE_PREVIEW, true); + } + + if (galleryListView != null) { + lastGalleryScrollPosition = galleryListView.layoutManager.onSaveInstanceState(); + lastGallerySelectedAlbum = galleryListView.getSelectedAlbum(); + } + }); + if (lastGalleryScrollPosition != null) { + galleryListView.layoutManager.onRestoreInstanceState(lastGalleryScrollPosition); + } + windowView.addView(galleryListView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); + } + + private boolean isGalleryOpen() { + return !scrollingY && galleryListView != null && galleryListView.getTranslationY() < (windowView.getMeasuredHeight() - (int) (AndroidUtilities.displaySize.y * 0.35f) - (AndroidUtilities.statusBarHeight + ActionBar.getCurrentActionBarHeight())); + } + + private ValueAnimator galleryOpenCloseAnimator; + private SpringAnimation galleryOpenCloseSpringAnimator; + private Boolean galleryListViewOpening; + private Runnable galleryLayouted; + + private void animateGalleryListView(boolean open) { + wasGalleryOpen = open; + if (galleryListViewOpening != null && galleryListViewOpening == open) { + return; + } + + if (galleryListView == null) { + if (open) { + createGalleryListView(); + } + if (galleryListView == null) { + return; + } + } + + if (galleryListView.firstLayout) { + galleryLayouted = () -> animateGalleryListView(open); + return; + } + + if (galleryOpenCloseAnimator != null) { + galleryOpenCloseAnimator.cancel(); + galleryOpenCloseAnimator = null; + } + if (galleryOpenCloseSpringAnimator != null) { + galleryOpenCloseSpringAnimator.cancel(); + galleryOpenCloseSpringAnimator = null; + } + + if (galleryListView == null) { + if (open) { + createGalleryListView(); + } + if (galleryListView == null) { + return; + } + } + if (galleryListView != null) { + galleryListView.ignoreScroll = false; + } + + if (open && draftSavedHint != null) { + draftSavedHint.hide(true); + } + + galleryListViewOpening = open; + + float from = galleryListView.getTranslationY(); + float to = open ? 0 : windowView.getHeight() - galleryListView.top() + AndroidUtilities.navigationBarHeight * 2.5f; + float fulldist = Math.max(1, windowView.getHeight()); + + galleryListView.ignoreScroll = !open; + + applyContainerViewTranslation2 = containerViewBackAnimator == null; + if (open) { + galleryOpenCloseSpringAnimator = new SpringAnimation(galleryListView, DynamicAnimation.TRANSLATION_Y, to); + galleryOpenCloseSpringAnimator.getSpring().setDampingRatio(0.75f); + galleryOpenCloseSpringAnimator.getSpring().setStiffness(350.0f); + galleryOpenCloseSpringAnimator.addEndListener((a, canceled, c, d) -> { + if (canceled) { + return; + } + galleryListView.setTranslationY(to); + galleryListView.ignoreScroll = false; + galleryOpenCloseSpringAnimator = null; + galleryListViewOpening = null; + }); + galleryOpenCloseSpringAnimator.start(); + } else { + galleryOpenCloseAnimator = ValueAnimator.ofFloat(from, to); + galleryOpenCloseAnimator.addUpdateListener(anm -> { + galleryListView.setTranslationY((float) anm.getAnimatedValue()); + }); + galleryOpenCloseAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + windowView.removeView(galleryListView); + galleryListView = null; + galleryOpenCloseAnimator = null; + galleryListViewOpening = null; + } + }); + galleryOpenCloseAnimator.setDuration(450L); + galleryOpenCloseAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + galleryOpenCloseAnimator.start(); + } + + if (!open && !awaitingPlayer) { + lastGalleryScrollPosition = null; + } + + if (!open && currentPage == PAGE_CAMERA && !noCameraPermission) { + createCameraView(); + } + } + + private void onNavigateStart(int fromPage, int toPage) { + if (toPage == PAGE_CAMERA) { + requestCameraPermission(false); + recordControl.setVisibility(View.VISIBLE); + if (recordControl != null) { + recordControl.stopRecordingLoading(false); + } + modeSwitcherView.setVisibility(View.VISIBLE); + zoomControlView.setVisibility(View.VISIBLE); + zoomControlView.setAlpha(0); + videoTimerView.setDuration(0, true); + + if (outputEntry != null) { + outputEntry.destroy(false); + outputEntry = null; + } + } + if (fromPage == PAGE_CAMERA) { + setCameraFlashModeIcon(null, true); + saveLastCameraBitmap(() -> cameraViewThumb.setImageDrawable(getCameraThumb())); + if (draftSavedHint != null) { + draftSavedHint.setVisibility(View.GONE); + } + cameraHint.hide(); + if (dualHint != null) { + dualHint.hide(); + } + } + if (toPage == PAGE_PREVIEW || fromPage == PAGE_PREVIEW) { + downloadButton.setEntry(toPage == PAGE_PREVIEW ? outputEntry : null); + if (isVideo) { + muteButton.setVisibility(View.VISIBLE); + setIconMuted(outputEntry != null && outputEntry.muted, false); + titleTextView.setRightPadding(AndroidUtilities.dp(96)); + } else { + titleTextView.setRightPadding(AndroidUtilities.dp(48)); + } + downloadButton.setVisibility(View.VISIBLE); +// privacySelector.setVisibility(View.VISIBLE); + previewButtons.setVisibility(View.VISIBLE); + previewView.setVisibility(View.VISIBLE); + captionContainer.setVisibility(View.VISIBLE); + captionContainer.clearFocus(); + +// privacySelector.setStoryPeriod(outputEntry == null || !UserConfig.getInstance(currentAccount).isPremium() ? 86400 : outputEntry.period); + captionEdit.setPeriod(outputEntry == null ? 86400 : outputEntry.period, false); + captionEdit.setPeriodVisible(outputEntry == null || !outputEntry.isEdit); + } + if (toPage == PAGE_PREVIEW) { + previewButtons.setShareText(outputEntry != null && outputEntry.isEdit ? LocaleController.getString("Done", R.string.Done) : LocaleController.getString("Next", R.string.Next)); + previewButtons.setShareEnabled(true); +// privacySelector.set(outputEntry, false); + if (!previewAlreadySet) { + previewView.set(outputEntry); + } + previewAlreadySet = false; + if (outputEntry != null && outputEntry.isDraft) { + if (outputEntry.paintFile != null) { + destroyPhotoPaintView(); + createPhotoPaintView(); + hidePhotoPaintView(); + } + if (outputEntry.filterState != null) { + destroyPhotoFilterView(); + createFilterPhotoView(); + } + if (outputEntry.isVideo && outputEntry.filterState != null) { + VideoEditTextureView textureView = previewView.getTextureView(); + if (textureView != null) { + textureView.setDelegate(eglThread -> { + if (eglThread != null && outputEntry != null && outputEntry.filterState != null) { + eglThread.setFilterGLThreadDelegate(FilterShaders.getFilterShadersDelegate(outputEntry.filterState)); + } + }); + } + } + captionEdit.setText(outputEntry.caption); + } else { + captionEdit.clear(); + } + muteButton.setImageResource(outputEntry != null && outputEntry.muted ? R.drawable.media_unmute : R.drawable.media_mute); + previewView.setVisibility(View.VISIBLE); + videoTimelineView.setVisibility(isVideo ? View.VISIBLE : View.GONE); + titleTextView.setVisibility(View.VISIBLE); + titleTextView.setText(outputEntry != null && outputEntry.isEdit ? LocaleController.getString(R.string.RecorderEditStory) : LocaleController.getString(R.string.RecorderNewStory)); + MediaDataController.getInstance(currentAccount).checkStickers(MediaDataController.TYPE_EMOJIPACKS); + MediaDataController.getInstance(currentAccount).checkFeaturedEmoji(); + } + if (fromPage == PAGE_PREVIEW) { +// privacySelectorHint.hide(); + captionEdit.hidePeriodPopup(); + muteHint.hide(); + } + if (photoFilterEnhanceView != null) { + photoFilterEnhanceView.setAllowTouch(false); + } + cameraViewThumb.setClickable(false); + if (savedDualHint != null) { + savedDualHint.hide(); + } + Bulletin.hideVisible(); + + if (captionEdit != null) { + captionEdit.closeKeyboard(); + captionEdit.ignoreTouches = true; + } + } + + private void onNavigateEnd(int fromPage, int toPage) { + if (fromPage == PAGE_CAMERA) { + destroyCameraView(false); + recordControl.setVisibility(View.GONE); + zoomControlView.setVisibility(View.GONE); + modeSwitcherView.setVisibility(View.GONE); + dualButton.setVisibility(View.GONE); + animateRecording(false, false); + setAwakeLock(false); + } + cameraViewThumb.setClickable(toPage == PAGE_CAMERA); + if (fromPage == PAGE_PREVIEW) { + previewButtons.setVisibility(View.GONE); + previewView.setVisibility(View.GONE); + captionContainer.setVisibility(View.GONE); + muteButton.setVisibility(View.GONE); + downloadButton.setVisibility(View.GONE); +// privacySelector.setVisibility(View.GONE); + previewView.setVisibility(View.GONE); + videoTimelineView.setVisibility(View.GONE); + destroyPhotoPaintView(); + destroyPhotoFilterView(); + titleTextView.setVisibility(View.GONE); + destroyGalleryListView(); + trash.setAlpha(0f); + trash.setVisibility(View.GONE); + videoTimeView.setVisibility(View.GONE); + } + if (toPage == PAGE_PREVIEW) { + createPhotoPaintView(); + hidePhotoPaintView(); + createFilterPhotoView(); + previewView.updatePauseReason(2, false); + previewView.updatePauseReason(3, false); + previewView.updatePauseReason(4, false); + previewView.updatePauseReason(5, false); + videoTimeView.setVisibility(outputEntry != null && outputEntry.duration >= 30_000 ? View.VISIBLE : View.GONE); + } + if (toPage == PAGE_CAMERA && showSavedDraftHint) { + getDraftSavedHint().setVisibility(View.VISIBLE); + getDraftSavedHint().show(); + recordControl.updateGalleryImage(); + } + showSavedDraftHint = false; + + if (photoFilterEnhanceView != null) { + photoFilterEnhanceView.setAllowTouch(toPage == PAGE_PREVIEW && (currentEditMode == EDIT_MODE_NONE || currentEditMode == EDIT_MODE_FILTER)); + } +// if (toPage == PAGE_PREVIEW && !privacySelectorHintOpened) { +// privacySelectorHint.show(false); +// privacySelectorHintOpened = true; +// } + if (captionEdit != null) { + captionEdit.ignoreTouches = toPage != PAGE_PREVIEW; + } + } + + private AnimatorSet editModeAnimator; + public void switchToEditMode(int editMode, boolean animated) { + if (currentEditMode == editMode) { + return; + } + + final int oldEditMode = currentEditMode; + currentEditMode = editMode; + + if (editModeAnimator != null) { + editModeAnimator.cancel(); + editModeAnimator = null; + } + + previewButtons.appear(editMode == EDIT_MODE_NONE && openProgress > 0, animated); + + ArrayList animators = new ArrayList<>(); + + boolean delay = photoFilterView == null && editMode == EDIT_MODE_FILTER; + if (editMode == EDIT_MODE_FILTER) { + createFilterPhotoView(); +// animatePhotoFilterTexture(true, animated); + previewTouchable = photoFilterView; + photoFilterView.getToolsView().setAlpha(0f); + photoFilterView.getToolsView().setVisibility(View.VISIBLE); + animators.add(ObjectAnimator.ofFloat(photoFilterView.getToolsView(), View.TRANSLATION_Y, 0)); + animators.add(ObjectAnimator.ofFloat(photoFilterView.getToolsView(), View.ALPHA, 1)); + TextureView textureView = photoFilterView.getMyTextureView(); + if (textureView != null) { + animators.add(ObjectAnimator.ofFloat(textureView, View.ALPHA, 1)); + } + } else if (oldEditMode == EDIT_MODE_FILTER && photoFilterView != null) { + previewTouchable = null; +// animatePhotoFilterTexture(false, animated); + animators.add(ObjectAnimator.ofFloat(photoFilterView.getToolsView(), View.TRANSLATION_Y, dp(186 + 40))); + animators.add(ObjectAnimator.ofFloat(photoFilterView.getToolsView(), View.ALPHA, 0)); + TextureView textureView = photoFilterView.getMyTextureView(); + if (textureView != null) { + animators.add(ObjectAnimator.ofFloat(textureView, View.ALPHA, 0)); + } + } + + if (editMode == EDIT_MODE_PAINT) { + createPhotoPaintView(); + previewTouchable = paintView; + animators.add(ObjectAnimator.ofFloat(backButton, View.ALPHA, 0)); + animators.add(ObjectAnimator.ofFloat(paintView.getTopLayout(), View.ALPHA, 0, 1)); + animators.add(ObjectAnimator.ofFloat(paintView.getTopLayout(), View.TRANSLATION_Y, -AndroidUtilities.dp(16), 0)); + animators.add(ObjectAnimator.ofFloat(paintView.getBottomLayout(), View.ALPHA, 0, 1)); + animators.add(ObjectAnimator.ofFloat(paintView.getBottomLayout(), View.TRANSLATION_Y, AndroidUtilities.dp(48), 0)); + animators.add(ObjectAnimator.ofFloat(paintView.getWeightChooserView(), View.TRANSLATION_X, -AndroidUtilities.dp(32), 0)); + } else if (oldEditMode == EDIT_MODE_PAINT && paintView != null) { + previewTouchable = null; + animators.add(ObjectAnimator.ofFloat(backButton, View.ALPHA, 1)); + animators.add(ObjectAnimator.ofFloat(paintView.getTopLayout(), View.ALPHA, 0)); + animators.add(ObjectAnimator.ofFloat(paintView.getTopLayout(), View.TRANSLATION_Y, -AndroidUtilities.dp(16))); + animators.add(ObjectAnimator.ofFloat(paintView.getBottomLayout(), View.ALPHA, 0)); + animators.add(ObjectAnimator.ofFloat(paintView.getBottomLayout(), View.TRANSLATION_Y, AndroidUtilities.dp(48))); + animators.add(ObjectAnimator.ofFloat(paintView.getWeightChooserView(), View.TRANSLATION_X, -AndroidUtilities.dp(32))); + } + + animators.add(ObjectAnimator.ofFloat(muteButton, View.ALPHA, editMode == EDIT_MODE_NONE && isVideo ? 1 : 0)); + animators.add(ObjectAnimator.ofFloat(downloadButton, View.ALPHA, editMode == EDIT_MODE_NONE ? 1 : 0)); +// animators.add(ObjectAnimator.ofFloat(privacySelector, View.ALPHA, editMode == EDIT_MODE_NONE ? 1 : 0)); + +// animators.add(ObjectAnimator.ofFloat(videoTimelineView, View.ALPHA, currentPage == PAGE_PREVIEW && isVideo && editMode == EDIT_MODE_NONE ? 1f : 0f)); + animators.add(ObjectAnimator.ofFloat(titleTextView, View.ALPHA, currentPage == PAGE_PREVIEW && editMode == EDIT_MODE_NONE ? 1f : 0f)); + + int bottomMargin = 0; + if (editMode == EDIT_MODE_FILTER) { + previewContainer.setPivotY(previewContainer.getMeasuredHeight() * .2f); + bottomMargin = dp(164); + } else if (editMode == EDIT_MODE_PAINT) { + previewContainer.setPivotY(previewContainer.getMeasuredHeight() * .6f); + bottomMargin = dp(40); + } + + float scale = 1f; + if (bottomMargin > 0) { + final int bottomPivot = previewContainer.getHeight() - (int) previewContainer.getPivotY(); + scale = (float) (bottomPivot - bottomMargin) / bottomPivot; + } + + animators.add(ObjectAnimator.ofFloat(previewContainer, View.SCALE_X, scale)); + animators.add(ObjectAnimator.ofFloat(previewContainer, View.SCALE_Y, scale)); + if (editMode == EDIT_MODE_NONE) { + animators.add(ObjectAnimator.ofFloat(previewContainer, View.TRANSLATION_Y, 0)); + } + + if (photoFilterViewCurvesControl != null) { + animators.add(ObjectAnimator.ofFloat(photoFilterViewCurvesControl, View.ALPHA, editMode == EDIT_MODE_FILTER ? 1f : 0)); + } + if (photoFilterViewBlurControl != null) { + animators.add(ObjectAnimator.ofFloat(photoFilterViewBlurControl, View.ALPHA, editMode == EDIT_MODE_FILTER ? 1f : 0)); + } + + animators.add(ObjectAnimator.ofFloat(captionContainer, View.ALPHA, editMode == EDIT_MODE_NONE ? 1f : 0)); + + onSwitchEditModeStart(oldEditMode, editMode); + if (animated) { + editModeAnimator = new AnimatorSet(); + editModeAnimator.playTogether(animators); + editModeAnimator.setDuration(320); + editModeAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + editModeAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + onSwitchEditModeEnd(oldEditMode, editMode); + } + }); + if (delay) { + editModeAnimator.setStartDelay(120L); + } + editModeAnimator.start(); + } else { + for (int i = 0; i < animators.size(); ++i) { + Animator a = animators.get(i); + a.setDuration(1); + a.start(); + } + onSwitchEditModeEnd(oldEditMode, editMode); + } + } + + private void hidePhotoPaintView() { + if (paintView == null) { + return; + } + previewTouchable = null; + paintView.getTopLayout().setAlpha(0f); + paintView.getTopLayout().setTranslationY(-AndroidUtilities.dp(16)); + paintView.getBottomLayout().setAlpha(0f); + paintView.getBottomLayout().setTranslationY(AndroidUtilities.dp(48)); + paintView.getWeightChooserView().setTranslationX(-AndroidUtilities.dp(32)); + paintView.setVisibility(View.GONE); + paintView.keyboardNotifier.ignore(true); + } + + private void createPhotoPaintView() { + if (paintView != null) { + return; + } + Pair size = previewView.getPaintSize(); + if (paintViewBitmap != null) { + paintViewBitmap.recycle(); + paintViewBitmap = null; + } + if (outputEntry != null && outputEntry.isDraft && outputEntry.paintFile != null) { + paintViewBitmap = BitmapFactory.decodeFile(outputEntry.paintFile.getPath()); + } + if (paintViewBitmap == null) { + paintViewBitmap = Bitmap.createBitmap(size.first, size.second, Bitmap.Config.ARGB_8888); + } + paintView = new PaintView(activity, windowView, activity, currentAccount, paintViewBitmap, null, previewView.getOrientation(), outputEntry == null ? null : outputEntry.mediaEntities, previewContainer.getMeasuredWidth(), previewContainer.getMeasuredHeight(), new MediaController.CropState(), null, resourcesProvider) { + @Override + public void onEntityDraggedTop(boolean value) { + previewHighlight.show(true, value, actionBarContainer); + } + + @Override + protected void onGalleryClick() { + destroyGalleryListView(); + createGalleryListView(true); + animateGalleryListView(true); + } + + @Override + public void onEntityDraggedBottom(boolean value) { + previewHighlight.updateCaption(captionEdit.getText()); +// previewHighlight.show(false, value, null); + } + + @Override + public void onEntityDragEnd(boolean delete) { + captionContainer.clearAnimation(); + captionContainer.animate().alpha(1f).setDuration(180).setInterpolator(CubicBezierInterpolator.EASE_OUT).start(); + trash.onDragInfo(false, delete); + trash.clearAnimation(); + trash.animate().alpha(0f).withEndAction(() -> { + trash.setVisibility(View.GONE); + }).setDuration(180).setInterpolator(CubicBezierInterpolator.EASE_OUT).setStartDelay(delete ? 500 : 0).start(); + if (delete) { + removeCurrentEntity(); + } + super.onEntityDragEnd(delete); + } + + @Override + public void onEntityDragStart() { + captionContainer.clearAnimation(); + captionContainer.animate().alpha(0f).setDuration(180).setInterpolator(CubicBezierInterpolator.EASE_OUT).start(); + + trash.setVisibility(View.VISIBLE); + trash.setAlpha(0f); + trash.clearAnimation(); + trash.animate().alpha(1f).setDuration(180).setInterpolator(CubicBezierInterpolator.EASE_OUT).start(); + } + + @Override + public void onEntityDragTrash(boolean enter) { + trash.onDragInfo(enter, false); + } + + @Override + protected void editSelectedTextEntity() { + captionEdit.editText.closeKeyboard(); + switchToEditMode(EDIT_MODE_PAINT, true); + super.editSelectedTextEntity(); + } + + @Override + public void dismiss() { + captionEdit.editText.closeKeyboard(); + switchToEditMode(EDIT_MODE_NONE, true); + } + + @Override + protected void onOpenCloseStickersAlert(boolean open) { + if (previewView != null) { + previewView.updatePauseReason(6, open); + } + if (captionEdit != null) { + captionEdit.ignoreTouches = open; + captionEdit.keyboardNotifier.ignore(open); + } + } + }; + containerView.addView(paintView); + paintViewRenderView = paintView.getRenderView(); + if (paintViewRenderView != null) { + previewContainer.addView(paintViewRenderView); + } + paintViewRenderInputView = paintView.getRenderInputView(); + if (paintViewRenderInputView != null) { + previewContainer.addView(paintViewRenderInputView); + } + paintViewTextDim = paintView.getTextDimView(); + if (paintViewTextDim != null) { + previewContainer.addView(paintViewTextDim); + } + paintViewEntitiesView = paintView.getEntitiesView(); + if (paintViewEntitiesView != null) { + previewContainer.addView(paintViewEntitiesView); + } + paintViewSelectionContainerView = paintView.getSelectionEntitiesView(); + if (paintViewSelectionContainerView != null) { + previewContainer.addView(paintViewSelectionContainerView); + } + orderPreviewViews(); + paintView.setOnDoneButtonClickedListener(() -> { + switchToEditMode(EDIT_MODE_NONE, true); + }); + paintView.setOnCancelButtonClickedListener(() -> { + switchToEditMode(EDIT_MODE_NONE, true); + }); + paintView.init(); + } + + private void orderPreviewViews() { + if (paintViewRenderView != null) { + paintViewRenderView.bringToFront(); + } + if (paintViewRenderInputView != null) { + paintViewRenderInputView.bringToFront(); + } + if (paintViewTextDim != null) { + paintViewTextDim.bringToFront(); + } + if (paintViewEntitiesView != null) { + paintViewEntitiesView.bringToFront(); + } + if (paintViewSelectionContainerView != null) { + paintViewSelectionContainerView.bringToFront(); + } + if (trash != null) { + trash.bringToFront(); + } + if (photoFilterEnhanceView != null) { + photoFilterEnhanceView.bringToFront(); + } + if (photoFilterViewBlurControl != null) { + photoFilterViewBlurControl.bringToFront(); + } + if (photoFilterViewCurvesControl != null) { + photoFilterViewCurvesControl.bringToFront(); + } + if (previewHighlight != null) { + previewHighlight.bringToFront(); + } + } + + private void destroyPhotoPaintView() { + if (paintView == null) { + return; + } + paintView.onCleanupEntities(); + + paintView.shutdown(); + containerView.removeView(paintView); + if (paintViewBitmap != null) { + paintViewBitmap.recycle(); + paintViewBitmap = null; + } + paintView = null; + if (paintViewRenderView != null) { + previewContainer.removeView(paintViewRenderView); + paintViewRenderView = null; + } + if (paintViewTextDim != null) { + previewContainer.removeView(paintViewTextDim); + paintViewTextDim = null; + } + if (paintViewRenderInputView != null) { + previewContainer.removeView(paintViewRenderInputView); + paintViewRenderInputView = null; + } + if (paintViewEntitiesView != null) { + previewContainer.removeView(paintViewEntitiesView); + paintViewEntitiesView = null; + } + if (paintViewSelectionContainerView != null) { + previewContainer.removeView(paintViewSelectionContainerView); + paintViewSelectionContainerView = null; + } + } + + private void onSwitchEditModeStart(int fromMode, int toMode) { + if (toMode == EDIT_MODE_NONE) { + backButton.setVisibility(View.VISIBLE); + captionContainer.setVisibility(View.VISIBLE); + if (paintView != null) { + paintView.clearSelection(); + } + downloadButton.setVisibility(View.VISIBLE); + titleTextView.setVisibility(View.VISIBLE); +// privacySelector.setVisibility(View.VISIBLE); + if (isVideo) { + muteButton.setVisibility(View.VISIBLE); + } + videoTimelineView.setVisibility(View.VISIBLE); + } + if (toMode == EDIT_MODE_PAINT && paintView != null) { + paintView.setVisibility(View.VISIBLE); + } + if ((toMode == EDIT_MODE_PAINT || fromMode == EDIT_MODE_PAINT) && paintView != null) { + paintView.onAnimationStateChanged(true); + } + + if (paintView != null) { + paintView.keyboardNotifier.ignore(toMode != EDIT_MODE_PAINT); + } + captionEdit.keyboardNotifier.ignore(toMode != EDIT_MODE_NONE); +// privacySelectorHint.hide(); + Bulletin.hideVisible(); + if (photoFilterView != null && fromMode == EDIT_MODE_FILTER) { + applyFilter(null); + } + if (photoFilterEnhanceView != null) { + photoFilterEnhanceView.setAllowTouch(false); + } + muteHint.hide(); + } + + private void onSwitchEditModeEnd(int fromMode, int toMode) { + if (fromMode == EDIT_MODE_FILTER && toMode == EDIT_MODE_NONE) { + destroyPhotoFilterView(); + } + if (toMode == EDIT_MODE_PAINT) { + backButton.setVisibility(View.GONE); + } + if (fromMode == EDIT_MODE_PAINT && paintView != null) { + paintView.setVisibility(View.GONE); + } + if (fromMode == EDIT_MODE_NONE) { + captionContainer.setVisibility(View.GONE); + muteButton.setVisibility(View.GONE); + downloadButton.setVisibility(View.GONE); +// privacySelector.setVisibility(View.GONE); + videoTimelineView.setVisibility(View.GONE); + titleTextView.setVisibility(View.GONE); + } + previewView.setAllowCropping(toMode == EDIT_MODE_NONE); + if ((toMode == EDIT_MODE_PAINT || fromMode == EDIT_MODE_PAINT) && paintView != null) { + paintView.onAnimationStateChanged(false); + } + if (photoFilterEnhanceView != null) { + photoFilterEnhanceView.setAllowTouch(toMode == EDIT_MODE_FILTER || toMode == EDIT_MODE_NONE); + } + } + + private void applyPaint(boolean drawEntities) { + if (paintView == null || outputEntry == null) { + return; + } + + outputEntry.clearPaint(); + outputEntry.editedMedia |= paintView.hasChanges(); + + if (outputEntry.mediaEntities == null) { + outputEntry.mediaEntities = new ArrayList<>(); + } else { + outputEntry.mediaEntities.clear(); + } + paintView.getBitmap(outputEntry.mediaEntities, outputEntry.resultWidth, outputEntry.resultHeight, false, false); + ArrayList entities = new ArrayList<>(); + Bitmap bitmap = paintView.getBitmap(entities, outputEntry.resultWidth, outputEntry.resultHeight, true, drawEntities && !outputEntry.wouldBeVideo()); + List masks = paintView.getMasks(); + + outputEntry.stickers = masks != null ? new ArrayList<>(masks) : null; + TLRPC.PhotoSize size = ImageLoader.scaleAndSaveImage(bitmap, Bitmap.CompressFormat.PNG, outputEntry.resultWidth, outputEntry.resultHeight, 87, false, 101, 101); + outputEntry.mediaEntities = entities == null || entities.isEmpty() ? null : entities; + if (!outputEntry.isVideo) { + outputEntry.averageDuration = Utilities.clamp(paintView.getLcm(), 7500L, 5000L); + } + outputEntry.paintFile = FileLoader.getInstance(currentAccount).getPathToAttach(size, true); + } + + private void applyFilter(Runnable whenDone) { + if (photoFilterView == null || outputEntry == null) { + if (whenDone != null) { + whenDone.run(); + } + return; + } + outputEntry.editedMedia |= photoFilterView.hasChanges(); + outputEntry.updateFilter(photoFilterView, whenDone); + if (whenDone == null && !outputEntry.isVideo && previewView != null) { + previewView.set(outputEntry); + } + } + +// private Matrix photoFilterStartMatrix, photoFilterEndMatrix; + + private void createFilterPhotoView() { + if (photoFilterView != null || outputEntry == null) { + return; + } + + Bitmap photoBitmap = null; + if (!outputEntry.isVideo) { + if (outputEntry.filterFile == null) { + photoBitmap = previewView.getPhotoBitmap(); + } else { + if (photoFilterBitmap != null) { + photoFilterBitmap.recycle(); + photoFilterBitmap = null; + } + photoBitmap = photoFilterBitmap = StoryEntry.getScaledBitmap(opts -> BitmapFactory.decodeFile(outputEntry.file.getAbsolutePath(), opts), AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y, true); + } + } + if (photoBitmap == null && !outputEntry.isVideo) { + return; + } + + photoFilterView = new PhotoFilterView(activity, previewView.getTextureView(), photoBitmap, previewView.getOrientation(), outputEntry == null ? null : outputEntry.filterState, null, 0, false, false, resourcesProvider); + containerView.addView(photoFilterView); + if (photoFilterEnhanceView != null) { + photoFilterEnhanceView.setFilterView(photoFilterView); + } + photoFilterViewTextureView = photoFilterView.getMyTextureView(); + if (photoFilterViewTextureView != null) { + photoFilterViewTextureView.setOpaque(false); + } + previewView.setFilterTextureView(photoFilterViewTextureView); + if (photoFilterViewTextureView != null) { + photoFilterViewTextureView.setAlpha(0f); + photoFilterViewTextureView.animate().alpha(1f).setDuration(220).start(); + } + applyFilterMatrix(); + photoFilterViewBlurControl = photoFilterView.getBlurControl(); + if (photoFilterViewBlurControl != null) { + previewContainer.addView(photoFilterViewBlurControl); + } + photoFilterViewCurvesControl = photoFilterView.getCurveControl(); + if (photoFilterViewCurvesControl != null) { + previewContainer.addView(photoFilterViewCurvesControl); + } + orderPreviewViews(); + + photoFilterView.getDoneTextView().setOnClickListener(v -> { + applyFilter(null); + switchToEditMode(EDIT_MODE_NONE, true); + }); + photoFilterView.getCancelTextView().setOnClickListener(v -> { +// if (photoFilterView.hasChanges()) { +// if (parentActivity == null) { +// return; +// } +// AlertDialog.Builder builder = new AlertDialog.Builder(parentActivity, resourcesProvider); +// builder.setMessage(LocaleController.getString("DiscardChanges", R.string.DiscardChanges)); +// builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); +// builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), (dialogInterface, i) -> switchToEditMode(0)); +// builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); +// showAlertDialog(builder); +// } else { + switchToEditMode(EDIT_MODE_NONE, true); +// } + }); + photoFilterView.getToolsView().setVisibility(View.GONE); + photoFilterView.getToolsView().setAlpha(0f); + photoFilterView.getToolsView().setTranslationY(AndroidUtilities.dp(186)); + photoFilterView.init(); + } + + private void applyFilterMatrix() { + if (outputEntry != null && photoFilterViewTextureView != null) { + Matrix photoFilterStartMatrix = new Matrix(); + photoFilterStartMatrix.reset(); + if (outputEntry.orientation != 0) { + photoFilterStartMatrix.postRotate(-outputEntry.orientation, previewContainer.getMeasuredWidth() / 2f, previewContainer.getMeasuredHeight() / 2f); + if (outputEntry.orientation / 90 % 2 == 1) { + photoFilterStartMatrix.postScale( + (float) previewContainer.getMeasuredWidth() / previewContainer.getMeasuredHeight(), + (float) previewContainer.getMeasuredHeight() / previewContainer.getMeasuredWidth(), + previewContainer.getMeasuredWidth() / 2f, + previewContainer.getMeasuredHeight() / 2f + ); + } + } + photoFilterStartMatrix.postScale( + 1f / previewContainer.getMeasuredWidth() * outputEntry.width, + 1f / previewContainer.getMeasuredHeight() * outputEntry.height + ); + photoFilterStartMatrix.postConcat(outputEntry.matrix); + photoFilterStartMatrix.postScale( + (float) previewContainer.getMeasuredWidth() / outputEntry.resultWidth, + (float) previewContainer.getMeasuredHeight() / outputEntry.resultHeight + ); + photoFilterViewTextureView.setTransform(photoFilterStartMatrix); + photoFilterViewTextureView.invalidate(); + } + } + +// private float photoFilterShow; +// private ValueAnimator photoFilterAnimator; +// private void animatePhotoFilterTexture(boolean show, boolean animated) { +// if (photoFilterView == null || photoFilterView.getMyTextureView() == null || photoFilterStartMatrix == null || photoFilterEndMatrix == null) { +// return; +// } +// TextureView textureView = photoFilterView.getMyTextureView(); +// if (photoFilterAnimator != null) { +// photoFilterAnimator.cancel(); +// photoFilterAnimator = null; +// } +// if (animated) { +// if (show) { +// previewView.setDraw(false); +// } +// +// float[] startValues = new float[9]; +// float[] endValues = new float[9]; +// photoFilterStartMatrix.getValues(startValues); +// photoFilterEndMatrix.getValues(endValues); +// +// Matrix interpolatedMatrix = new Matrix(); +// float[] interpolatedValues = new float[9]; +// +// photoFilterAnimator = ValueAnimator.ofFloat(photoFilterShow, show ? 1 : 0); +// photoFilterAnimator.addUpdateListener(anm -> { +// photoFilterShow = (float) anm.getAnimatedValue(); +// for (int i = 0; i < 9; i++) { +// interpolatedValues[i] = startValues[i] + photoFilterShow * (endValues[i] - startValues[i]); +// } +// interpolatedMatrix.setValues(interpolatedValues); +// textureView.setTransform(interpolatedMatrix); +// textureView.invalidate(); +// }); +// photoFilterAnimator.addListener(new AnimatorListenerAdapter() { +// @Override +// public void onAnimationEnd(Animator animation) { +// photoFilterShow = show ? 1 : 0; +// final Matrix matrix = show ? photoFilterEndMatrix : photoFilterStartMatrix; +// if (matrix != null) { +// textureView.setTransform(matrix); +// } +// textureView.invalidate(); +// if (!show) { +// previewView.setDraw(true); +// } +// } +// }); +// photoFilterAnimator.setDuration(320); +// photoFilterAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); +// photoFilterAnimator.start(); +// } else { +// previewView.setDraw(!show); +// photoFilterShow = show ? 1 : 0; +// final Matrix matrix = show ? photoFilterEndMatrix : photoFilterStartMatrix; +// textureView.setTransform(matrix); +// textureView.invalidate(); +// } +// } + + private void destroyPhotoFilterView() { + if (photoFilterView == null) { + return; + } + photoFilterView.shutdown(); + photoFilterEnhanceView.setFilterView(null); + containerView.removeView(photoFilterView); + if (photoFilterViewTextureView != null) { + previewContainer.removeView(photoFilterViewTextureView); + photoFilterViewTextureView = null; + } + previewView.setFilterTextureView(null); + if (photoFilterViewBlurControl != null) { + previewContainer.removeView(photoFilterViewBlurControl); + photoFilterViewBlurControl = null; + } + if (photoFilterViewCurvesControl != null) { + previewContainer.removeView(photoFilterViewCurvesControl); + photoFilterViewCurvesControl = null; + } + photoFilterView = null; + if (photoFilterBitmap != null) { + photoFilterBitmap.recycle(); + photoFilterBitmap = null; + } +// photoFilterStartMatrix = null; +// photoFilterEndMatrix = null; +// if (photoFilterAnimator != null) { +// photoFilterAnimator.cancel(); +// photoFilterAnimator = null; +// } + } + + private boolean noCameraPermission; + + @SuppressLint("ClickableViewAccessibility") + private void createCameraView() { + if (cameraView != null || getContext() == null) { + return; + } + cameraView = new DualCameraView(getContext(), getCameraFace(), false) { + @Override + public void onEntityDraggedTop(boolean value) { + previewHighlight.show(true, value, actionBarContainer); + } + + @Override + public void onEntityDraggedBottom(boolean value) { + previewHighlight.updateCaption(captionEdit.getText()); + previewHighlight.show(false, value, controlContainer); + } + + @Override + public void toggleDual() { + super.toggleDual(); + dualButton.setValue(isDual()); +// recordControl.setDual(isDual()); + setCameraFlashModeIcon(isDual() || getCameraSession() == null || isFrontface() ? null : getCameraSession().getCurrentFlashMode(), true); + } + + @Override + protected void onSavedDualCameraSuccess() { + if (MessagesController.getGlobalMainSettings().getInt("storysvddualhint", 0) < 2) { + AndroidUtilities.runOnUIThread(() -> { + if (takingVideo || takingPhoto || cameraView == null || currentPage != PAGE_CAMERA) { + return; + } + if (savedDualHint != null) { + CharSequence text = isFrontface() ? LocaleController.getString(R.string.StoryCameraSavedDualBackHint) : LocaleController.getString(R.string.StoryCameraSavedDualFrontHint); + savedDualHint.setMaxWidthPx(HintView2.cutInFancyHalf(text, savedDualHint.getTextPaint())); + savedDualHint.setText(text); + savedDualHint.show(); + MessagesController.getGlobalMainSettings().edit().putInt("storysvddualhint", MessagesController.getGlobalMainSettings().getInt("storysvddualhint", 0) + 1).apply(); + } + }, 340); + } + dualButton.setValue(isDual()); + } + }; + cameraView.isStory = true; + cameraView.setThumbDrawable(getCameraThumb()); + cameraView.initTexture(); + cameraView.setDelegate(() -> { + String currentFlashMode = cameraView.getCameraSession().getCurrentFlashMode(); + if (TextUtils.equals(currentFlashMode, cameraView.getCameraSession().getNextFlashMode())) { + currentFlashMode = null; + } + setCameraFlashModeIcon(currentPage == PAGE_CAMERA ? currentFlashMode : null, true); + if (zoomControlView != null) { + zoomControlView.setZoom(cameraZoom = 0, false); + } + }); + dualButton.setVisibility(cameraView.dualAvailable() && currentPage == PAGE_CAMERA ? View.VISIBLE : View.GONE); + flashButton.setTranslationX(cameraView.dualAvailable() ? -dp(46) : 0); + previewContainer.addView(cameraView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); + if (MessagesController.getGlobalMainSettings().getInt("storyhint2", 0) < 1) { + cameraHint.show(); + MessagesController.getGlobalMainSettings().edit().putInt("storyhint2", MessagesController.getGlobalMainSettings().getInt("storyhint2", 0) + 1).apply(); + } else if (!cameraView.isSavedDual() && cameraView.dualAvailable() && MessagesController.getGlobalMainSettings().getInt("storydualhint", 0) < 2) { + dualHint.show(); + } + } + + private Drawable getCameraThumb() { + Bitmap bitmap = null; + try { + File file = new File(ApplicationLoader.getFilesDirFixed(), "cthumb.jpg"); + bitmap = BitmapFactory.decodeFile(file.getAbsolutePath()); + } catch (Throwable ignore) {} + if (bitmap != null) { + return new BitmapDrawable(bitmap); + } else { + return getContext().getResources().getDrawable(R.drawable.icplaceholder); + } + } + + private void saveLastCameraBitmap(Runnable whenDone) { + if (cameraView == null || cameraView.getTextureView() == null) { + return; + } + try { + TextureView textureView = cameraView.getTextureView(); + final Bitmap bitmap = textureView.getBitmap(); + Utilities.themeQueue.postRunnable(() -> { + try { + if (bitmap != null) { + Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), cameraView.getMatrix(), true); + bitmap.recycle(); + Bitmap bitmap2 = newBitmap; + Bitmap lastBitmap = Bitmap.createScaledBitmap(bitmap2, 80, (int) (bitmap2.getHeight() / (bitmap2.getWidth() / 80.0f)), true); + if (lastBitmap != null) { + if (lastBitmap != bitmap2) { + bitmap2.recycle(); + } + Utilities.blurBitmap(lastBitmap, 7, 1, lastBitmap.getWidth(), lastBitmap.getHeight(), lastBitmap.getRowBytes()); + File file = new File(ApplicationLoader.getFilesDirFixed(), "cthumb.jpg"); + FileOutputStream stream = new FileOutputStream(file); + lastBitmap.compress(Bitmap.CompressFormat.JPEG, 87, stream); + lastBitmap.recycle(); + stream.close(); + } + } + } catch (Throwable ignore) { + + } finally { + AndroidUtilities.runOnUIThread(whenDone); + } + }); + } catch (Throwable ignore) {} + } + + private void showDismissEntry() { + AlertDialog.Builder builder = new AlertDialog.Builder(getContext(), resourcesProvider); + builder.setTitle(LocaleController.getString("DiscardChanges", R.string.DiscardChanges)); + builder.setMessage(LocaleController.getString("PhotoEditorDiscardAlert", R.string.PhotoEditorDiscardAlert)); + if (outputEntry != null) { + builder.setNeutralButton(outputEntry.isDraft ? LocaleController.getString("StoryKeepDraft") : LocaleController.getString("StorySaveDraft"), (di, i) -> { + if (outputEntry == null) { + return; + } + showSavedDraftHint = !outputEntry.isDraft; + applyFilter(null); + applyPaint(false); + destroyPhotoFilterView(); + StoryEntry storyEntry = outputEntry; + storyEntry.destroy(true); + storyEntry.caption = captionEdit.getText(); + outputEntry = null; + prepareThumb(storyEntry, true); + DraftsController drafts = MessagesController.getInstance(currentAccount).getStoriesController().getDraftsController(); + if (storyEntry.isDraft) { + drafts.edit(storyEntry); + } else { + drafts.append(storyEntry); + } + navigateTo(PAGE_CAMERA, true); + }); + } + builder.setPositiveButton(outputEntry != null && outputEntry.isDraft ? LocaleController.getString("StoryDeleteDraft") : LocaleController.getString("Discard", R.string.Discard), (dialogInterface, i) -> { + if (outputEntry != null && outputEntry.isDraft) { + MessagesController.getInstance(currentAccount).getStoriesController().getDraftsController().delete(outputEntry); + outputEntry = null; + } + navigateTo(PAGE_CAMERA, true); + }); + builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + AlertDialog dialog = builder.create(); + dialog.show(); + View positiveButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE); + if (positiveButton instanceof TextView) { + ((TextView) positiveButton).setTextColor(Theme.getColor(Theme.key_text_RedBold, resourcesProvider)); + positiveButton.setBackground(Theme.createRadSelectorDrawable(ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_text_RedBold, resourcesProvider), (int) (0.2f * 255)), 6, 6)); + } + } + + private void destroyCameraView(boolean waitForThumb) { + if (cameraView != null) { + if (waitForThumb) { + saveLastCameraBitmap(() -> { + cameraViewThumb.setImageDrawable(getCameraThumb()); + if (cameraView != null) { + cameraView.destroy(true, null); + previewContainer.removeView(cameraView); + cameraView = null; + } + }); + } else { + saveLastCameraBitmap(() -> { + cameraViewThumb.setImageDrawable(getCameraThumb()); + }); + cameraView.destroy(true, null); + previewContainer.removeView(cameraView); + cameraView = null; + } + } + } + + public interface Touchable { + boolean onTouch(MotionEvent event); + } + + private Touchable previewTouchable; + private boolean requestedCameraPermission; + + private void requestCameraPermission(boolean force) { + if (requestedCameraPermission && !force) { + return; + } + noCameraPermission = false; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && activity != null) { + noCameraPermission = activity.checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED; + if (noCameraPermission) { + Drawable iconDrawable = getContext().getResources().getDrawable(R.drawable.story_camera).mutate(); + iconDrawable.setColorFilter(new PorterDuffColorFilter(0x3dffffff, PorterDuff.Mode.MULTIPLY)); + CombinedDrawable drawable = new CombinedDrawable(new ColorDrawable(0xff222222), iconDrawable); + drawable.setIconSize(dp(64), dp(64)); + cameraViewThumb.setImageDrawable(drawable); + if (activity.shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) { + new AlertDialog.Builder(getContext()) + .setTopAnimation(R.raw.permission_request_camera, AlertsCreator.PERMISSIONS_REQUEST_TOP_ICON_SIZE, false, Theme.getColor(Theme.key_dialogTopBackground)) + .setMessage(AndroidUtilities.replaceTags(LocaleController.getString("PermissionNoCameraWithHint", R.string.PermissionNoCameraWithHint))) + .setPositiveButton(LocaleController.getString("PermissionOpenSettings", R.string.PermissionOpenSettings), (dialogInterface, i) -> { + try { + Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + intent.setData(Uri.parse("package:" + ApplicationLoader.applicationContext.getPackageName())); + activity.startActivity(intent); + } catch (Exception e) { + FileLog.e(e); + } + }) + .setNegativeButton(LocaleController.getString("ContactsPermissionAlertNotNow", R.string.ContactsPermissionAlertNotNow), null) + .create() + .show(); + return; + } + activity.requestPermissions(new String[]{Manifest.permission.CAMERA}, 111); + requestedCameraPermission = true; + } + } + + if (!noCameraPermission) { + if (CameraController.getInstance().isCameraInitied()) { + createCameraView(); + } else { + CameraController.getInstance().initCamera(this::createCameraView); + } + } + } + + private boolean requestGalleryPermission() { + if (activity != null) { + boolean noGalleryPermission = false; +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { +// noGalleryPermission = ( +// activity.checkSelfPermission(Manifest.permission.READ_MEDIA_IMAGES) != PackageManager.PERMISSION_GRANTED || +// activity.checkSelfPermission(Manifest.permission.READ_MEDIA_VIDEO) != PackageManager.PERMISSION_GRANTED +// ); +// if (noGalleryPermission) { +// activity.requestPermissions(new String[]{Manifest.permission.READ_MEDIA_IMAGES, Manifest.permission.READ_MEDIA_VIDEO}, 114); +// } +// } else + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + noGalleryPermission = activity.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED; + if (noGalleryPermission) { + activity.requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 114); + } + } + return !noGalleryPermission; + } + return true; + } + + private boolean requestAudioPermission() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && activity != null) { + boolean granted = activity.checkSelfPermission(Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED; + if (!granted) { + activity.requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO}, 112); + return false; + } + } + return true; + } + + public static void onResume() { + if (instance != null) { + instance.onResumeInternal(); + } + } + + private Runnable whenOpenDone; + private void onResumeInternal() { + if (currentPage == PAGE_CAMERA) { +// requestedCameraPermission = false; + if (openCloseAnimator != null && openCloseAnimator.isRunning()) { + whenOpenDone = () -> requestCameraPermission(false); + } else { + requestCameraPermission(false); + } + } + if (captionEdit != null) { + captionEdit.onResume(); + } + if (recordControl != null) { + recordControl.updateGalleryImage(); + } + if (previewHighlight != null) { + previewHighlight.updateCount(); + } + if (paintView != null) { + paintView.onResume(); + } + if (previewView != null) { + previewView.updatePauseReason(0, false); + } + + MessagesController.getInstance(currentAccount).getStoriesController().getDraftsController().load(); + } + + public static void onPause() { + if (instance != null) { + instance.onPauseInternal(); + } + } + private void onPauseInternal() { + destroyCameraView(false); + if (captionEdit != null) { + captionEdit.onPause(); + } + if (previewView != null) { + previewView.updatePauseReason(0, true); + } + } + + public static void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + if (instance != null) { + instance.onRequestPermissionsResultInternal(requestCode, permissions, grantResults); + } + } + + private void onRequestPermissionsResultInternal(int requestCode, String[] permissions, int[] grantResults) { + final boolean granted = grantResults != null && grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED; + if (requestCode == 111) { + noCameraPermission = !granted; + if (granted && currentPage == PAGE_CAMERA) { + cameraViewThumb.setImageDrawable(null); + if (CameraController.getInstance().isCameraInitied()) { + createCameraView(); + } else { + CameraController.getInstance().initCamera(this::createCameraView); + } + } + } else if (requestCode == 114) { + if (granted) { + MediaController.loadGalleryPhotosAlbums(0); + animateGalleryListView(true); + } + } else if (requestCode == 112) { + if (!granted) { + new AlertDialog.Builder(getContext()) + .setTopAnimation(R.raw.permission_request_camera, AlertsCreator.PERMISSIONS_REQUEST_TOP_ICON_SIZE, false, Theme.getColor(Theme.key_dialogTopBackground)) + .setMessage(AndroidUtilities.replaceTags(LocaleController.getString("PermissionNoCameraMicVideo", R.string.PermissionNoCameraMicVideo))) + .setPositiveButton(LocaleController.getString("PermissionOpenSettings", R.string.PermissionOpenSettings), (dialogInterface, i) -> { + try { + Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + intent.setData(Uri.parse("package:" + ApplicationLoader.applicationContext.getPackageName())); + activity.startActivity(intent); + } catch (Exception e) { + FileLog.e(e); + } + }) + .setNegativeButton(LocaleController.getString("ContactsPermissionAlertNotNow", R.string.ContactsPermissionAlertNotNow), null) + .create() + .show(); + } + } + } + + private void saveCameraFace(boolean frontface) { + MessagesController.getGlobalMainSettings().edit().putBoolean("stories_camera", frontface).apply(); + } + + private boolean getCameraFace() { + return MessagesController.getGlobalMainSettings().getBoolean("stories_camera", false); + } + + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.albumsDidLoad) { + if (recordControl != null) { + recordControl.updateGalleryImage(); + } + if (lastGallerySelectedAlbum != null && MediaController.allMediaAlbums != null) { + for (int a = 0; a < MediaController.allMediaAlbums.size(); a++) { + MediaController.AlbumEntry entry = MediaController.allMediaAlbums.get(a); + if (entry.bucketId == lastGallerySelectedAlbum.bucketId && entry.videoOnly == lastGallerySelectedAlbum.videoOnly) { + lastGallerySelectedAlbum = entry; + break; + } + } + } + } else if (id == NotificationCenter.storiesDraftsUpdated) { + if (recordControl != null && !showSavedDraftHint) { + recordControl.updateGalleryImage(); + } + } + } + + public void addNotificationObservers() { + NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.albumsDidLoad); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.storiesDraftsUpdated); + } + + public void removeNotificationObservers() { + NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.albumsDidLoad); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.storiesDraftsUpdated); + } + + private boolean isBackgroundVisible; + private boolean forceBackgroundVisible; + private void checkBackgroundVisibility() { + boolean shouldBeVisible = dismissProgress != 0 || openProgress < 1 || forceBackgroundVisible; + if (shouldBeVisible == isBackgroundVisible) { + return; + } + if (activity instanceof LaunchActivity) { + LaunchActivity launchActivity = (LaunchActivity) activity; + launchActivity.drawerLayoutContainer.setAllowDrawContent(shouldBeVisible); + } + isBackgroundVisible = shouldBeVisible; + } + + public interface ClosingViewProvider { + void preLayout(Runnable runnable); + SourceView getView(); + } + + private void showPremiumPeriodBulletin(int period) { + final int hours = period / 3600; + BulletinFactory.of(windowView, resourcesProvider) + .createSimpleBulletin(R.raw.fire_on, AndroidUtilities.replaceSingleTag(LocaleController.formatPluralString("StoryPeriodPremium", hours), () -> { + if (previewView != null) { + previewView.updatePauseReason(4, true); + } + PremiumFeatureBottomSheet sheet = new PremiumFeatureBottomSheet(new BaseFragment() { + { currentAccount = StoryRecorder.this.currentAccount; } + @Override + public Dialog showDialog(Dialog dialog) { + dialog.show(); + return dialog; + } + @Override + public Activity getParentActivity() { + return StoryRecorder.this.activity; + } + }, 0, false); + sheet.setOnDismissListener(d -> { + if (previewView != null) { + previewView.updatePauseReason(4, false); + } + }); + sheet.show(); + })).show(true); + } + + public void setIconMuted(boolean muted, boolean animated) { + if (muteButtonDrawable == null) { + muteButtonDrawable = new RLottieDrawable(R.raw.media_mute_unmute, "media_mute_unmute", AndroidUtilities.dp(28), AndroidUtilities.dp(28), true, null); + muteButtonDrawable.multiplySpeed(1.5f); + } + muteButton.setAnimation(muteButtonDrawable); + if (!animated) { + muteButtonDrawable.setCurrentFrame(muted ? 20 : 0, false); + return; + } + if (muted) { + if (muteButtonDrawable.getCurrentFrame() > 20) { + muteButtonDrawable.setCurrentFrame(0, false); + } + muteButtonDrawable.setCustomEndFrame(20); + muteButtonDrawable.start(); + } else { + if (muteButtonDrawable.getCurrentFrame() == 0 || muteButtonDrawable.getCurrentFrame() >= 43) { + return; + } + muteButtonDrawable.setCustomEndFrame(43); + muteButtonDrawable.start(); + } + } + + public static CharSequence cameraBtnSpan(Context context) { + SpannableString cameraStr = new SpannableString("c"); + Drawable cameraDrawable = context.getResources().getDrawable(R.drawable.story_camera).mutate(); + final int sz = AndroidUtilities.dp(35); + cameraDrawable.setBounds(-sz / 4, -sz, sz / 4 * 3, 0); + cameraStr.setSpan(new ImageSpan(cameraDrawable) { + @Override + public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, @Nullable Paint.FontMetricsInt fm) { + return super.getSize(paint, text, start, end, fm) / 3 * 2; + } + + @Override + public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) { + canvas.save(); + canvas.translate(0, (bottom - top) / 2 + dp(1)); + cameraDrawable.setAlpha(paint.getAlpha()); + super.draw(canvas, text, start, end, x, top, y, bottom, paint); + canvas.restore(); + } + }, 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + return cameraStr; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/StoryUploadingService.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/StoryUploadingService.java new file mode 100644 index 0000000000..dd6a19674d --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/StoryUploadingService.java @@ -0,0 +1,115 @@ +package org.telegram.ui.Stories.recorder; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; + +import androidx.annotation.Nullable; +import androidx.core.app.NotificationCompat; +import androidx.core.app.NotificationManagerCompat; + +import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.BuildVars; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.NotificationsController; +import org.telegram.messenger.R; +import org.telegram.messenger.UserConfig; + +public class StoryUploadingService extends Service implements NotificationCenter.NotificationCenterDelegate { + + private NotificationCompat.Builder builder; + private String path; + private float currentProgress; + private int currentAccount = -1; + + public StoryUploadingService() { + super(); + NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.uploadStoryEnd); + } + + @Override + public void onDestroy() { + super.onDestroy(); + try { + stopForeground(true); + } catch (Exception ignore) {} + NotificationManagerCompat.from(ApplicationLoader.applicationContext).cancel(33); + NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.uploadStoryEnd); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.uploadStoryProgress); + if (BuildVars.LOGS_ENABLED) { + FileLog.d("upload story destroy"); + } + } + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.uploadStoryProgress) { + if (path != null && path.equals((String) args[0])) { + currentProgress = (float) args[1]; + builder.setProgress(100, Math.round(100 * currentProgress), currentProgress <= 0); + try { + NotificationManagerCompat.from(ApplicationLoader.applicationContext).notify(33, builder.build()); + } catch (Throwable e) { + FileLog.e(e); + } + } + } else if (id == NotificationCenter.uploadStoryEnd) { + if (path != null && path.equals((String) args[0])) { + stopSelf(); + } + } + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + path = intent.getStringExtra("path"); + int oldAccount = currentAccount; + currentAccount = intent.getIntExtra("currentAccount", UserConfig.selectedAccount); + if (!UserConfig.isValidAccount(currentAccount)) { + stopSelf(); + return Service.START_NOT_STICKY; + } + if (oldAccount != currentAccount) { + if (oldAccount != -1) { + NotificationCenter.getInstance(oldAccount).removeObserver(this, NotificationCenter.uploadStoryProgress); + } + if (currentAccount != -1) { + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.uploadStoryProgress); + } + } + if (path == null) { + stopSelf(); + return Service.START_NOT_STICKY; + } + if (BuildVars.LOGS_ENABLED) { + FileLog.d("start upload story"); + } + if (builder == null) { + NotificationsController.checkOtherNotificationsChannel(); + builder = new NotificationCompat.Builder(ApplicationLoader.applicationContext); + builder.setSmallIcon(android.R.drawable.stat_sys_upload); + builder.setWhen(System.currentTimeMillis()); + builder.setChannelId(NotificationsController.OTHER_NOTIFICATIONS_CHANNEL); + builder.setContentTitle(LocaleController.getString("AppName", R.string.AppName)); + builder.setTicker(LocaleController.getString("StoryUploading", R.string.StoryUploading)); + builder.setContentText(LocaleController.getString("StoryUploading", R.string.StoryUploading)); + } + currentProgress = 0; + builder.setProgress(100, Math.round(100 * currentProgress), false); + startForeground(33, builder.build()); + try { + NotificationManagerCompat.from(ApplicationLoader.applicationContext).notify(33, builder.build()); + } catch (Throwable e) { + FileLog.e(e); + } + return Service.START_NOT_STICKY; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/ToggleButton.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/ToggleButton.java new file mode 100644 index 0000000000..852798a9f9 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/ToggleButton.java @@ -0,0 +1,107 @@ +package org.telegram.ui.Stories.recorder; + +import static org.telegram.messenger.AndroidUtilities.dp; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.PorterDuffXfermode; +import android.graphics.Region; +import android.graphics.Xfermode; +import android.graphics.drawable.Drawable; +import android.view.View; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.R; +import org.telegram.messenger.Utilities; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.CubicBezierInterpolator; + +public class ToggleButton extends View { + + private Drawable drawable; + + private int activeResId; + private Bitmap activeBitmap; + private final Paint activePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint activeBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + + public ToggleButton(Context context, int resId) { + this(context, resId, resId); + } + + public ToggleButton(Context context, int resId, int activeResId) { + super(context); + + drawable = context.getResources().getDrawable(resId).mutate(); + + this.activeResId = activeResId; + activePaint.setColor(0xFFFFFFFF); + activeBitmapPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (activeBitmap == null) { + activeBitmap = BitmapFactory.decodeResource(getResources(), activeResId); + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (activeBitmap != null) { + activeBitmap.recycle(); + } + } + + public void setValue(boolean value) { + this.value = value ? 1 : 0; + invalidate(); + } + + private float value; + private final AnimatedFloat valueAnimated = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + + private final Path clipPath = new Path(); + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + float t = valueAnimated.set(value); + + final int w = drawable.getIntrinsicWidth(), h = drawable.getIntrinsicHeight(); + + AndroidUtilities.rectTmp2.set((getWidth() - w) / 2, (getHeight() - h) / 2, (getWidth() + w) / 2, (getHeight() + h) / 2); + if (t <= 0) { + drawable.setBounds(AndroidUtilities.rectTmp2); + drawable.draw(canvas); + } else if (t < 1) { + canvas.save(); + clipPath.rewind(); + clipPath.addCircle(getWidth() / 2f, getHeight() / 2f, dp(16) * t, Path.Direction.CW); + canvas.clipPath(clipPath, Region.Op.DIFFERENCE); + drawable.setBounds(AndroidUtilities.rectTmp2); + drawable.draw(canvas); + canvas.restore(); + } + + if (t > 0) { + canvas.saveLayerAlpha(0, 0, getWidth(), getHeight(), 0xFF, Canvas.ALL_SAVE_FLAG); + canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, dp(16) * t, activePaint); + canvas.save(); + if (activeBitmap != null) { + canvas.drawBitmap(activeBitmap, null, AndroidUtilities.rectTmp2, activeBitmapPaint); + } + canvas.restore(); + canvas.restore(); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/TrashView.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/TrashView.java new file mode 100644 index 0000000000..7662d173d0 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/TrashView.java @@ -0,0 +1,116 @@ +package org.telegram.ui.Stories.recorder; + +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.AndroidUtilities.dpf2; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.RadialGradient; +import android.graphics.Shader; +import android.graphics.drawable.Drawable; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.view.Gravity; +import android.view.View; + +import androidx.annotation.NonNull; + +import org.checkerframework.checker.units.qual.A; +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.R; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.AnimatedTextView; +import org.telegram.ui.Components.ButtonBounce; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.RLottieDrawable; + +public class TrashView extends View { + + private final RLottieDrawable drawable; + + private final AnimatedTextView.AnimatedTextDrawable textDrawable; + + private final Paint circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint greyPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + private final ButtonBounce bounce = new ButtonBounce(this); + + public TrashView(Context context) { + super(context); + + circlePaint.setColor(0xffffffff); + circlePaint.setStyle(Paint.Style.STROKE); + circlePaint.setStrokeWidth(dpf2(2.66f)); + circlePaint.setShadowLayer(dpf2(3f), 0, dp(1.66f), 0x30000000); + greyPaint.setColor(0x33000000); + + drawable = new RLottieDrawable(R.raw.group_pip_delete_icon, "" + R.raw.group_pip_delete_icon, dp(48), dp(48), true, null); + drawable.setMasterParent(this); + drawable.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.MULTIPLY)); + drawable.setPlayInDirectionOfCustomEndFrame(true); + drawable.setCustomEndFrame(0); + drawable.setAllowDecodeSingleFrame(true); + drawable.start(); + + textDrawable = new AnimatedTextView.AnimatedTextDrawable(true, true, false); + textDrawable.setAnimationProperties(.3f, 0, 250, CubicBezierInterpolator.EASE_OUT_QUINT); + textDrawable.setOverrideFullWidth(AndroidUtilities.displaySize.x); + textDrawable.setTextSize(dp(14)); + textDrawable.setTextColor(0xffffffff); + textDrawable.setShadowLayer(dpf2(1.33f), 0, dp(1), 0x40000000); + textDrawable.setText(LocaleController.getString("TrashHintDrag", R.string.TrashHintDrag)); + textDrawable.setGravity(Gravity.CENTER); + } + + @Override + protected boolean verifyDrawable(@NonNull Drawable who) { + return who == textDrawable || super.verifyDrawable(who); + } + + private boolean dragged; + private final AnimatedFloat draggedT = new AnimatedFloat(this, 0, 240, CubicBezierInterpolator.EASE_OUT_QUINT); + + @Override + protected void dispatchDraw(Canvas canvas) { + final float r = dp(30); + final float cx = getWidth() / 2f, cy = getHeight() / 2f; + + final float R = r + dp(3) * draggedT.set(dragged); + canvas.drawCircle(cx, cy, R, greyPaint); + canvas.drawCircle(cx, cy, R, circlePaint); + + final float sz = dp(48); + drawable.setBounds((int) (cx - sz / 2f), (int) (cy - sz / 2f), (int) (cx + sz / 2f), (int) (cy + sz / 2f)); + drawable.draw(canvas); + + textDrawable.setBounds(0, (int) (cy + r + dp(7)), getWidth(), getHeight()); + textDrawable.draw(canvas); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setMeasuredDimension(widthMeasureSpec, dp(120)); + } + + public void onDragInfo(boolean dragged, boolean deleted) { + bounce.setPressed(dragged); + textDrawable.setText(dragged || deleted ? LocaleController.getString("TrashHintRelease", R.string.TrashHintRelease) : LocaleController.getString("TrashHintDrag", R.string.TrashHintDrag)); + if (this.dragged = (dragged && !deleted)) { + if (drawable.getCurrentFrame() > 34) { + drawable.setCurrentFrame(0, false); + } + drawable.setCustomEndFrame(33); + drawable.start(); + } else { + drawable.setCustomEndFrame(deleted ? 66 : 0); + drawable.start(); + } + invalidate(); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/VideoTimeView.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/VideoTimeView.java new file mode 100644 index 0000000000..9d147a5d89 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/VideoTimeView.java @@ -0,0 +1,122 @@ +package org.telegram.ui.Stories.recorder; + +import static org.telegram.messenger.AndroidUtilities.dp; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.drawable.Drawable; +import android.text.TextUtils; +import android.view.Gravity; +import android.view.View; + +import androidx.annotation.NonNull; + +import org.checkerframework.checker.units.qual.A; +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.Utilities; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.AnimatedTextView; +import org.telegram.ui.Components.CubicBezierInterpolator; + +public class VideoTimeView extends View { + + private final Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final AnimatedTextView.AnimatedTextDrawable textDrawable; + + public VideoTimeView(Context context) { + super(context); + + backgroundPaint.setColor(0x80000000); + + textDrawable = new AnimatedTextView.AnimatedTextDrawable(false, true, true); + textDrawable.setAnimationProperties(.2f, 0, 200, CubicBezierInterpolator.EASE_OUT_QUINT); + textDrawable.setTextSize(dp(13)); + textDrawable.setTextColor(0xffffffff); + textDrawable.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + textDrawable.setCallback(this); + textDrawable.setGravity(Gravity.CENTER_HORIZONTAL); + + setTime(0, false); + } + + @Override + protected boolean verifyDrawable(@NonNull Drawable who) { + return textDrawable == who || super.verifyDrawable(who); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(dp(25), MeasureSpec.EXACTLY)); + } + + public void setTime(long time, boolean animated) { + long ms = time % 1000; + time /= 1000L; + long s = time % 60, m = (time - s) / 60, h = (time - s - m * 60) / 60; + StringBuilder str = new StringBuilder(8); + if (h < 10) + str.append('0'); + str.append(h).append(':'); + if (m < 10) + str.append('0'); + str.append(m).append(':'); + if (s < 10) + str.append('0'); + str.append(s); +// str.append('.'); +// if (ms < 100) +// str.append("00"); +// else if (ms < 10) +// str.append('0'); +// str.append(ms); + if (!TextUtils.equals(str, textDrawable.getText())) { + textDrawable.cancelAnimation(); + textDrawable.setText(str, animated && !LocaleController.isRTL); + } + } + + private boolean shown = true; + public void show(boolean show, boolean animated) { + if (show == shown && animated) { + return; + } + shown = show; + animate().cancel(); + if (animated) { + animate() + .translationY(show ? 0 : dp(6)) + .alpha(show ? 1 : 0) + .scaleX(show ? 1 : .8f) + .scaleY(show ? 1 : .8f) + .setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT) + .setDuration(220) + .start(); + } else { + setTranslationY(show ? 0 : dp(6)); + setScaleX(show ? 1 : .8f); + setScaleY(show ? 1 : .8f); + setAlpha(show ? 1 : 0); + } + } + + @Override + public void onDraw(Canvas canvas) { + super.onDraw(canvas); + + float w = textDrawable.getCurrentWidth(); + + AndroidUtilities.rectTmp.set( + (getWidth() - w) / 2 - dp(6), + dp(2), + (getWidth() + w) / 2 + dp(6), + dp(2 + 21) + ); + canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(5), dp(5), backgroundPaint); + + textDrawable.setBounds((int) (AndroidUtilities.rectTmp.left), (int) AndroidUtilities.rectTmp.top - dp(1), (int) AndroidUtilities.rectTmp.right, (int) AndroidUtilities.rectTmp.bottom); + textDrawable.draw(canvas); + } + +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/VideoTimerView.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/VideoTimerView.java new file mode 100644 index 0000000000..29f8fbcaac --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/VideoTimerView.java @@ -0,0 +1,103 @@ +package org.telegram.ui.Stories.recorder; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.drawable.Drawable; +import android.view.Gravity; +import android.view.View; + +import androidx.annotation.NonNull; + +import org.checkerframework.checker.units.qual.A; +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.Utilities; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.AnimatedTextView; +import org.telegram.ui.Components.CubicBezierInterpolator; + +public class VideoTimerView extends View { + + private Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private Paint recordPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private AnimatedTextView.AnimatedTextDrawable textDrawable; + + public VideoTimerView(Context context) { + super(context); + + recordPaint.setColor(0xFFF22828); + + backgroundPaint.setColor(0x3f000000); + + textDrawable = new AnimatedTextView.AnimatedTextDrawable(false, true, true); + textDrawable.setAnimationProperties(.3f, 0, 250, CubicBezierInterpolator.EASE_OUT_QUINT); + textDrawable.setTextSize(AndroidUtilities.dp(13)); + textDrawable.setTextColor(0xffffffff); + textDrawable.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + textDrawable.setCallback(this); + textDrawable.setGravity(Gravity.CENTER_HORIZONTAL); + + setDuration(0, false); + } + + @Override + protected boolean verifyDrawable(@NonNull Drawable who) { + return textDrawable == who || super.verifyDrawable(who); + } + + private boolean recording; + private AnimatedFloat recordingT = new AnimatedFloat(this, 0, 250, CubicBezierInterpolator.EASE_OUT_QUINT); + public void setRecording(boolean value, boolean animated) { + recording = value; + if (!animated) { + recordingT.set(recording ? 1 : 0, true); + } + invalidate(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(45), MeasureSpec.EXACTLY)); + } + + public void setDuration(long duration, boolean animated) { + long s = duration % 60, m = (duration - s) / 60; + StringBuilder str = new StringBuilder(5); + if (m < 10) + str.append('0'); + str.append(m).append(':'); + if (s < 10) + str.append('0'); + str.append(s); + textDrawable.setText(str, animated); + } + + @Override + public void onDraw(Canvas canvas) { + super.onDraw(canvas); + + float recordingT = this.recordingT.set(recording ? 1 : 0); + + float recordingPad = recordingT * AndroidUtilities.dp(12.66f); + float w = textDrawable.getCurrentWidth() + recordingPad; + + AndroidUtilities.rectTmp.set( + (getWidth() - w) / 2 - AndroidUtilities.dp(8), + AndroidUtilities.dp(18), + (getWidth() + w) / 2 + AndroidUtilities.dp(8), + AndroidUtilities.dp(18 + 22) + ); + canvas.drawRoundRect(AndroidUtilities.rectTmp, AndroidUtilities.dp(18), AndroidUtilities.dp(18), backgroundPaint); + + if (recordingT > 0) { + long t = System.currentTimeMillis() % 2000L; + recordPaint.setAlpha((int) (0xFF * Utilities.clamp((float) Math.sin(t / 1000f * Math.PI) / 4f + .75f, 1f, 0f))); + invalidate(); + canvas.drawCircle(AndroidUtilities.rectTmp.left + AndroidUtilities.dp(6.66f + 4), AndroidUtilities.rectTmp.centerY(), AndroidUtilities.dp(4) * recordingT, recordPaint); + } + + textDrawable.setBounds((int) (AndroidUtilities.rectTmp.left + recordingPad), (int) AndroidUtilities.rectTmp.top - AndroidUtilities.dp(1), (int) AndroidUtilities.rectTmp.right, (int) AndroidUtilities.rectTmp.bottom); + textDrawable.draw(canvas); + } + +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SuggestClearDatabaseBottomSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/SuggestClearDatabaseBottomSheet.java index c024b6e00e..0881f8c802 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SuggestClearDatabaseBottomSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SuggestClearDatabaseBottomSheet.java @@ -92,7 +92,7 @@ private SuggestClearDatabaseBottomSheet(BaseFragment fragment) { fragment.showDialog(alertDialog); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } }); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/TextMessageEnterTransition.java b/TMessagesProj/src/main/java/org/telegram/ui/TextMessageEnterTransition.java index 08c7aed5e6..25df992810 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/TextMessageEnterTransition.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/TextMessageEnterTransition.java @@ -22,7 +22,6 @@ import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; -import android.util.Log; import android.view.View; import android.view.animation.LinearInterpolator; @@ -30,9 +29,9 @@ import androidx.recyclerview.widget.ChatListItemAnimator; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.Emoji; import org.telegram.messenger.MessageObject; -import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.SharedConfig; import org.telegram.messenger.UserConfig; import org.telegram.tgnet.TLRPC; @@ -96,7 +95,7 @@ public class TextMessageEnterTransition implements MessageEnterTransitionContain private float scaleFrom; private final int currentAccount; - private int animationIndex = -1; + private AnimationNotificationsLocker notificationsLocker = new AnimationNotificationsLocker(); MessageObject.TextLayoutBlock textLayoutBlock; Drawable fromMessageDrawable; ChatActivityEnterView enterView; @@ -376,12 +375,12 @@ public TextMessageEnterTransition(ChatMessageCell messageView, ChatActivity chat animator.setDuration(ChatListItemAnimator.DEFAULT_DURATION); container.addTransition(this); - animationIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(animationIndex, null); + notificationsLocker.lock(); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - NotificationCenter.getInstance(currentAccount).onAnimationFinish(animationIndex); + notificationsLocker.unlock(); container.removeTransition(TextMessageEnterTransition.this); messageView.setEnterTransitionInProgress(false); messageView.getTransitionParams().lastDrawingBackgroundRect.set(messageView.getBackgroundDrawableLeft(), messageView.getBackgroundDrawableTop(), messageView.getBackgroundDrawableRight(), messageView.getBackgroundDrawableBottom()); @@ -743,8 +742,7 @@ public void onDraw(Canvas canvas) { } } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ThemeActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ThemeActivity.java index a31163b6f3..324932ed34 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ThemeActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ThemeActivity.java @@ -69,6 +69,7 @@ import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.EmojiThemes; import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.ActionBar.ThemeColors; import org.telegram.ui.ActionBar.ThemeDescription; import org.telegram.ui.Cells.AppIconsSelectorCell; import org.telegram.ui.Cells.BrightnessControlCell; @@ -102,7 +103,6 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.concurrent.atomic.AtomicReference; @@ -142,6 +142,7 @@ public class ThemeActivity extends BaseFragment implements NotificationCenter.No private int directShareRow; private int raiseToSpeakRow; private int raiseToListenRow; + private int nextMediaTapRow; private int sendByEnterRow; private int saveToGalleryOption1Row; private int saveToGalleryOption2Row; @@ -198,6 +199,8 @@ public class ThemeActivity extends BaseFragment implements NotificationCenter.No private int lastShadowRow; private int stickersRow; private int stickersInfoRow; + private int stickersSectionRow, mediaSoundSectionRow, otherSectionRow; + private int mediaSoundHeaderRow, otherHeaderRow; private int liteModeRow; private int liteModeInfoRow; @@ -531,6 +534,11 @@ private void updateRows(boolean notify) { pauseOnMediaRow = -1; stickersRow = -1; stickersInfoRow = -1; + stickersSectionRow = -1; + mediaSoundHeaderRow = -1; + otherHeaderRow = -1; + mediaSoundSectionRow = -1; + otherSectionRow = -1; liteModeRow = -1; liteModeInfoRow = -1; @@ -542,6 +550,7 @@ private void updateRows(boolean notify) { enableAnimationsRow = -1; raiseToSpeakRow = -1; raiseToListenRow = -1; + nextMediaTapRow = -1; sendByEnterRow = -1; saveToGalleryOption1Row = -1; saveToGalleryOption2Row = -1; @@ -633,30 +642,28 @@ private void updateRows(boolean notify) { swipeGestureRow = rowCount++; swipeGestureInfoRow = rowCount++; + nightThemeRow = rowCount++; liteModeRow = rowCount++; - liteModeInfoRow = rowCount++; - stickersRow = rowCount++; - stickersInfoRow = rowCount++; + stickersSectionRow = rowCount++; - settingsRow = rowCount++; - nightThemeRow = rowCount++; - customTabsRow = rowCount++; - directShareRow = rowCount++; -// enableAnimationsRow = rowCount++; + mediaSoundHeaderRow = rowCount++; + nextMediaTapRow = rowCount++; raiseToListenRow = rowCount++; if (SharedConfig.raiseToListen) { raiseToSpeakRow = rowCount++; } - sendByEnterRow = rowCount++; - pauseOnMediaRow = rowCount++; pauseOnRecordRow = rowCount++; + pauseOnMediaRow = rowCount++; bluetoothScoRow = rowCount++; -// if (SharedConfig.canBlurChat()) { -// chatBlurRow = rowCount++; -// } + mediaSoundSectionRow = rowCount++; + + otherHeaderRow = rowCount++; + customTabsRow = rowCount++; + directShareRow = rowCount++; + sendByEnterRow = rowCount++; distanceRow = rowCount++; - settings2Row = rowCount++; + otherSectionRow = rowCount++; } else { nightDisabledRow = rowCount++; nightScheduledRow = rowCount++; @@ -969,7 +976,7 @@ public void onItemClick(int id) { NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.needSetDayNightTheme, currentTheme, currentType == THEME_TYPE_NIGHT, null, Theme.DEFALT_THEME_ACCENT_ID); listAdapter.notifyItemChanged(themeAccentListRow); } else { - Theme.reloadWallpaper(); + Theme.reloadWallpaper(true); } } }); @@ -978,7 +985,7 @@ public void onItemClick(int id) { showDialog(alertDialog); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } else if (id == day_night_switch) { SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("themeconfig", Activity.MODE_PRIVATE); @@ -1058,6 +1065,11 @@ public void onItemClick(int id) { if (view instanceof TextCheckCell) { ((TextCheckCell) view).setChecked(SharedConfig.raiseToSpeak); } + } else if (position == nextMediaTapRow) { + SharedConfig.toggleNextMediaTap(); + if (view instanceof TextCheckCell) { + ((TextCheckCell) view).setChecked(SharedConfig.nextMediaTap); + } } else if (position == raiseToListenRow) { SharedConfig.toggleRaiseToListen(); if (view instanceof TextCheckCell) { @@ -1242,7 +1254,7 @@ public void onItemClick(int id) { } value = type + " " + value; } - checkCell.setTextAndValueAndCheck(LocaleController.getString("AutoNightTheme", R.string.AutoNightTheme), value, enabled, true); + checkCell.setTextAndValueAndIconAndCheck(LocaleController.getString("AutoNightTheme", R.string.AutoNightTheme), value, R.drawable.msg2_night_auto, enabled, 0, false, true); } else { presentFragment(new ThemeActivity(THEME_TYPE_NIGHT)); } @@ -1844,8 +1856,9 @@ private void showOptionsForTheme(Theme.ThemeInfo themeInfo) { File currentFile; if (themeInfo.pathToFile == null && themeInfo.assetName == null) { StringBuilder result = new StringBuilder(); - for (HashMap.Entry entry : Theme.getDefaultColors().entrySet()) { - result.append(entry.getKey()).append("=").append(entry.getValue()).append("\n"); + int[] defaultColors = Theme.getDefaultColors(); + for (int i = 0; i < defaultColors.length; i++) { + result.append(ThemeColors.getStringName(i)).append("=").append(defaultColors[i]).append("\n"); } currentFile = new File(ApplicationLoader.getFilesDirFixed(), "default_theme.attheme"); FileOutputStream stream = null; @@ -1920,14 +1933,14 @@ private void showOptionsForTheme(Theme.ThemeInfo themeInfo) { showDialog(alertDialog); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } }); AlertDialog alertDialog = builder.create(); showDialog(alertDialog); if (hasDelete) { - alertDialog.setItemColor(alertDialog.getItemsCount() - 1, Theme.getColor(Theme.key_dialogTextRed), Theme.getColor(Theme.key_dialogRedIcon)); + alertDialog.setItemColor(alertDialog.getItemsCount() - 1, Theme.getColor(Theme.key_text_RedBold), Theme.getColor(Theme.key_text_RedRegular)); } } @@ -1942,7 +1955,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int break; case TYPE_TEXT_INFO_PRIVACY: view = new TextInfoPrivacyCell(mContext); - view.setBackground(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + view.setBackground(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); break; case TYPE_SHADOW: view = new ShadowSectionCell(mContext); @@ -1956,7 +1969,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); break; case TYPE_BRIGHTNESS: - view = new BrightnessControlCell(mContext) { + view = new BrightnessControlCell(mContext, BrightnessControlCell.TYPE_DEFAULT) { @Override protected void didChangedValue(float value) { int oldValue = (int) (Theme.autoNightBrighnessThreshold * 100); @@ -1992,7 +2005,7 @@ protected void didSelectChatType(boolean threeLines) { view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); break; case TYPE_NIGHT_THEME: - view = new NotificationsCheckCell(mContext, 21, 64, false); + view = new NotificationsCheckCell(mContext, 21, 60, true); view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); break; case TYPE_THEME_LIST: @@ -2124,13 +2137,13 @@ public boolean onInterceptTouchEvent(MotionEvent e) { showDialog(alertDialog); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } }); AlertDialog alertDialog = builder.create(); showDialog(alertDialog); - alertDialog.setItemColor(alertDialog.getItemsCount() - 1, Theme.getColor(Theme.key_dialogTextRed), Theme.getColor(Theme.key_dialogRedIcon)); + alertDialog.setItemColor(alertDialog.getItemsCount() - 1, Theme.getColor(Theme.key_text_RedBold), Theme.getColor(Theme.key_text_RedRegular)); return true; } return false; @@ -2222,31 +2235,33 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { cell.setTextAndValue(LocaleController.getString("DistanceUnits", R.string.DistanceUnits), value, updateDistance, false); updateDistance = false; } else if (position == bluetoothScoRow) { - cell.setTextAndValue(LocaleController.getString(R.string.MicrophoneForVoiceMessages), LocaleController.getString(SharedConfig.recordViaSco ? R.string.MicrophoneForVoiceMessagesSco : R.string.MicrophoneForVoiceMessagesBuiltIn), updateRecordViaSco, true); + cell.setTextAndValue(LocaleController.getString(R.string.MicrophoneForVoiceMessages), LocaleController.getString(SharedConfig.recordViaSco ? R.string.MicrophoneForVoiceMessagesSco : R.string.MicrophoneForVoiceMessagesBuiltIn), updateRecordViaSco, false); updateRecordViaSco = false; } break; } case TYPE_TEXT_INFO_PRIVACY: { TextInfoPrivacyCell cell = (TextInfoPrivacyCell) holder.itemView; + cell.setFixedSize(0); if (position == automaticBrightnessInfoRow) { cell.setText(LocaleController.formatString("AutoNightBrightnessInfo", R.string.AutoNightBrightnessInfo, (int) (100 * Theme.autoNightBrighnessThreshold))); } else if (position == scheduleLocationInfoRow) { cell.setText(getLocationSunString()); } else if (position == swipeGestureInfoRow) { cell.setText(LocaleController.getString("ChatListSwipeGestureInfo", R.string.ChatListSwipeGestureInfo)); - } else if (position == stickersInfoRow) { - cell.setText(LocaleController.getString("StickersNameInfo", R.string.StickersNameInfo)); } else if (position == liteModeInfoRow) { cell.setText(LocaleController.getString("LiteModeInfo", R.string.LiteModeInfo)); + } else { + cell.setFixedSize(12); + cell.setText(""); } break; } case TYPE_SHADOW: { if (position == nightTypeInfoRow && themeInfoRow == -1 || position == lastShadowRow || position == themeInfoRow && nightTypeInfoRow != -1 || position == saveToGallerySectionRow || position == settings2Row) { - holder.itemView.setBackground(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + holder.itemView.setBackground(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } else { - holder.itemView.setBackground(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + holder.itemView.setBackground(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); } break; } @@ -2291,6 +2306,10 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { headerCell.setText(LocaleController.getString("SelectTheme", R.string.SelectTheme)); } else if (position == appIconHeaderRow) { headerCell.setText(LocaleController.getString(R.string.AppIcon)); + } else if (position == otherHeaderRow) { + headerCell.setText(LocaleController.getString("OtherSettings", R.string.OtherSettings)); + } else if (position == mediaSoundHeaderRow) { + headerCell.setText(LocaleController.getString("MediaAndSoundSettings", R.string.MediaAndSoundSettings)); } break; } @@ -2310,11 +2329,13 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); textCheckCell.setTextAndCheck(LocaleController.getString("SendByEnter", R.string.SendByEnter), preferences.getBoolean("send_by_enter", false), true); } else if (position == raiseToSpeakRow) { - textCheckCell.setTextAndCheck(LocaleController.getString("RaiseToSpeak", R.string.RaiseToSpeak), SharedConfig.raiseToSpeak, true); + textCheckCell.setTextAndValueAndCheck(LocaleController.getString("RaiseToSpeak", R.string.RaiseToSpeak), LocaleController.getString("RaiseToSpeakInfo", R.string.RaiseToSpeakInfo), SharedConfig.raiseToSpeak, true, true); } else if (position == raiseToListenRow) { - textCheckCell.setTextAndCheck(LocaleController.getString("RaiseToListen", R.string.RaiseToListen), SharedConfig.raiseToListen, true); + textCheckCell.setTextAndValueAndCheck(LocaleController.getString("RaiseToListen", R.string.RaiseToListen), LocaleController.getString("RaiseToListenInfo", R.string.RaiseToListenInfo), SharedConfig.raiseToListen, true, true); + } else if (position == nextMediaTapRow) { + textCheckCell.setTextAndValueAndCheck(LocaleController.getString("NextMediaTap", R.string.NextMediaTap), LocaleController.getString("NextMediaTapInfo", R.string.NextMediaTapInfo), SharedConfig.nextMediaTap, true, true); } else if (position == pauseOnRecordRow) { - textCheckCell.setTextAndCheck(LocaleController.getString(R.string.PauseMusicOnRecord), SharedConfig.pauseMusicOnRecord, true); + textCheckCell.setTextAndValueAndCheck(LocaleController.getString(R.string.PauseMusicOnRecord), LocaleController.getString("PauseMusicOnRecordInfo", R.string.PauseMusicOnRecordInfo), SharedConfig.pauseMusicOnRecord, true, true); } else if (position == pauseOnMediaRow) { textCheckCell.setTextAndCheck(LocaleController.getString(R.string.PauseMusicOnMedia), SharedConfig.pauseMusicOnMedia, true); } else if (position == customTabsRow) { @@ -2342,7 +2363,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } value = type + " " + value; } - checkCell.setTextAndValueAndCheck(LocaleController.getString("AutoNightTheme", R.string.AutoNightTheme), value, enabled, true); + checkCell.setTextAndValueAndIconAndCheck(LocaleController.getString("AutoNightTheme", R.string.AutoNightTheme), value, R.drawable.msg2_night_auto, enabled, 0, false, true); } break; } @@ -2368,21 +2389,33 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } case TYPE_TEXT_PREFERENCE: { TextCell cell = (TextCell) holder.itemView; + cell.heightDp = 48; if (position == backgroundRow) { + cell.setSubtitle(null); cell.setColors(Theme.key_windowBackgroundWhiteBlueText4, Theme.key_windowBackgroundWhiteBlueText4); cell.setTextAndIcon(LocaleController.getString("ChangeChatBackground", R.string.ChangeChatBackground), R.drawable.msg_background, false); } else if (position == editThemeRow) { + cell.setSubtitle(null); cell.setColors(Theme.key_windowBackgroundWhiteBlueText4, Theme.key_windowBackgroundWhiteBlueText4); cell.setTextAndIcon(LocaleController.getString("EditCurrentTheme", R.string.EditCurrentTheme), R.drawable.msg_theme, true); } else if (position == createNewThemeRow) { + cell.setSubtitle(null); cell.setColors(Theme.key_windowBackgroundWhiteBlueText4, Theme.key_windowBackgroundWhiteBlueText4); cell.setTextAndIcon(LocaleController.getString("CreateNewTheme", R.string.CreateNewTheme), R.drawable.msg_colors, false); + } else if (position == liteModeRow) { + cell.setColors(Theme.key_dialogIcon, Theme.key_windowBackgroundWhiteBlackText); + cell.setTextAndIcon(LocaleController.getString("LiteMode", R.string.LiteMode), R.drawable.msg2_animations, true); + cell.setSubtitle(LocaleController.getString("LiteModeInfo", R.string.LiteModeInfo)); + cell.heightDp = 60; + cell.offsetFromImage = 64; + cell.imageLeft = 20; } else if (position == stickersRow) { - cell.setColors(Theme.key_windowBackgroundWhiteGrayIcon, Theme.key_windowBackgroundWhiteBlackText); + cell.setColors(Theme.key_dialogIcon, Theme.key_windowBackgroundWhiteBlackText); cell.setTextAndIcon(LocaleController.getString("StickersName", R.string.StickersName), R.drawable.msg2_sticker, false); - } else if (position == liteModeRow) { - cell.setColors(Theme.key_windowBackgroundWhiteGrayIcon, Theme.key_windowBackgroundWhiteBlackText); - cell.setTextAndIcon(LocaleController.getString("LiteMode", R.string.LiteMode), R.drawable.msg2_animations, false); + cell.setSubtitle(LocaleController.getString("StickersNameInfo2", R.string.StickersNameInfo2)); + cell.offsetFromImage = 64; + cell.heightDp = 60; + cell.imageLeft = 20; } break; } @@ -2426,20 +2459,22 @@ public int getItemViewType(int position) { return TYPE_TEXT_INFO_PRIVACY; } else if (position == themeInfoRow || position == nightTypeInfoRow || position == scheduleFromToInfoRow || position == settings2Row || position == newThemeInfoRow || position == chatListInfoRow || position == bubbleRadiusInfoRow || - position == saveToGallerySectionRow || position == appIconShadowRow || position == lastShadowRow) { + position == saveToGallerySectionRow || position == appIconShadowRow || position == lastShadowRow || position == stickersSectionRow || + position == mediaSoundSectionRow || position == otherSectionRow) { return TYPE_SHADOW; } else if (position == nightDisabledRow || position == nightScheduledRow || position == nightAutomaticRow || position == nightSystemDefaultRow) { return TYPE_THEME_TYPE; } else if (position == scheduleHeaderRow || position == automaticHeaderRow || position == preferedHeaderRow || position == settingsRow || position == themeHeaderRow || position == textSizeHeaderRow || position == chatListHeaderRow || position == bubbleRadiusHeaderRow || position == swipeGestureHeaderRow || - position == selectThemeHeaderRow || position == appIconHeaderRow) { + position == selectThemeHeaderRow || position == appIconHeaderRow || position == mediaSoundHeaderRow || + position == otherHeaderRow) { return TYPE_HEADER; } else if (position == automaticBrightnessRow) { return TYPE_BRIGHTNESS; - } else if (position == scheduleLocationRow || position == enableAnimationsRow || position == sendByEnterRow || + } else if (position == scheduleLocationRow || position == sendByEnterRow || position == raiseToSpeakRow || position == raiseToListenRow || position == pauseOnRecordRow || position == customTabsRow || - position == directShareRow || position == chatBlurRow || position == pauseOnMediaRow) { + position == directShareRow || position == chatBlurRow || position == pauseOnMediaRow || position == nextMediaTapRow) { return TYPE_TEXT_CHECK; } else if (position == textSizeRow) { return TYPE_TEXT_SIZE; @@ -2453,7 +2488,8 @@ public int getItemViewType(int position) { return TYPE_THEME_ACCENT_LIST; } else if (position == bubbleRadiusRow) { return TYPE_BUBBLE_RADIUS; - } else if (position == backgroundRow || position == editThemeRow || position == createNewThemeRow || position == stickersRow || position == liteModeRow) { + } else if (position == backgroundRow || position == editThemeRow || position == createNewThemeRow || + position == liteModeRow || position == stickersRow) { return TYPE_TEXT_PREFERENCE; } else if (position == swipeGestureRow) { return TYPE_SWIPE_GESTURE; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ThemePreviewActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ThemePreviewActivity.java index bc6978b9a1..5f419229ba 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ThemePreviewActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ThemePreviewActivity.java @@ -13,6 +13,7 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.StateListAnimator; +import android.animation.ValueAnimator; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; @@ -23,8 +24,10 @@ import android.graphics.Bitmap; import android.graphics.BlendMode; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Outline; import android.graphics.Paint; +import android.graphics.Point; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; @@ -38,6 +41,7 @@ import android.os.SystemClock; import android.text.TextPaint; import android.text.TextUtils; +import android.util.Log; import android.util.TypedValue; import android.view.Gravity; import android.view.MotionEvent; @@ -49,9 +53,12 @@ import android.widget.EditText; import android.widget.FrameLayout; import android.widget.ImageView; +import android.widget.Scroller; import android.widget.TextView; +import androidx.annotation.NonNull; import androidx.collection.LongSparseArray; +import androidx.core.graphics.ColorUtils; import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -61,6 +68,7 @@ import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.BuildVars; +import org.telegram.messenger.ChatThemeController; import org.telegram.messenger.DownloadController; import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; @@ -75,9 +83,12 @@ import org.telegram.messenger.MessagesStorage; import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; +import org.telegram.messenger.SendMessagesHelper; +import org.telegram.messenger.SharedConfig; import org.telegram.messenger.SvgHelper; import org.telegram.messenger.UserConfig; import org.telegram.messenger.Utilities; +import org.telegram.messenger.VideoEditedInfo; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.ActionBar; @@ -89,6 +100,7 @@ import org.telegram.ui.ActionBar.MenuDrawable; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.ActionBar.ThemeDescription; +import org.telegram.ui.Cells.BrightnessControlCell; import org.telegram.ui.Cells.ChatActionCell; import org.telegram.ui.Cells.ChatMessageCell; import org.telegram.ui.Cells.DialogCell; @@ -101,11 +113,12 @@ import org.telegram.ui.Components.ColorPicker; import org.telegram.ui.Components.CombinedDrawable; import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.GestureDetector2; import org.telegram.ui.Components.HintView; import org.telegram.ui.Components.LayoutHelper; -import org.telegram.ui.Components.MediaActionDrawable; import org.telegram.ui.Components.MotionBackgroundDrawable; -import org.telegram.ui.Components.RadialProgress2; +import org.telegram.ui.Components.RLottieDrawable; +import org.telegram.ui.Components.RadialProgressView; import org.telegram.ui.Components.RecyclerListView; import org.telegram.ui.Components.SeekBarView; import org.telegram.ui.Components.ShareAlert; @@ -114,6 +127,7 @@ import org.telegram.ui.Components.WallpaperParallaxEffect; import java.io.File; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.util.ArrayList; import java.util.HashMap; @@ -124,8 +138,12 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro public static final int SCREEN_TYPE_PREVIEW = 0; public static final int SCREEN_TYPE_ACCENT_COLOR = 1; public static final int SCREEN_TYPE_CHANGE_BACKGROUND = 2; + private static final int OPTION_DAY_NIGHT = 6; + private static final int OPTION_PHOTO_EDIT = 7; + private static final int BRIGHTNESS_CONTROL_HEIGHT = 88; private final int screenType; + private Scroller scroller; public boolean useDefaultThemeForButtons = true; private ActionBarMenuItem dropDownContainer; @@ -155,10 +173,10 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro private long watchForKeyboardEndTime; private ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener; - Theme.MessageDrawable msgOutDrawable = new Theme.MessageDrawable(Theme.MessageDrawable.TYPE_TEXT, true, false); - Theme.MessageDrawable msgOutDrawableSelected = new Theme.MessageDrawable(Theme.MessageDrawable.TYPE_TEXT, true, true); - Theme.MessageDrawable msgOutMediaDrawable = new Theme.MessageDrawable(Theme.MessageDrawable.TYPE_MEDIA, true, false); - Theme.MessageDrawable msgOutMediaDrawableSelected = new Theme.MessageDrawable(Theme.MessageDrawable.TYPE_MEDIA, true, true); + Theme.MessageDrawable msgOutDrawable = new MessageDrawable(Theme.MessageDrawable.TYPE_TEXT, true, false); + Theme.MessageDrawable msgOutDrawableSelected = new MessageDrawable(Theme.MessageDrawable.TYPE_TEXT, true, true); + Theme.MessageDrawable msgOutMediaDrawable = new MessageDrawable(Theme.MessageDrawable.TYPE_MEDIA, true, false); + Theme.MessageDrawable msgOutMediaDrawableSelected = new MessageDrawable(Theme.MessageDrawable.TYPE_MEDIA, true, true); private ColorPicker colorPicker; private int lastPickedColor; @@ -191,6 +209,7 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro private RecyclerListView listView; private DialogsAdapter dialogsAdapter; private ImageView floatingButton; + MessageObject serverWallpaper; private boolean wasScroll; @@ -198,12 +217,12 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro private FrameLayout page2; private RecyclerListView listView2; private MessagesAdapter messagesAdapter; - private BackupImageView backgroundImage; + private BackgroundView[] backgroundImages = new BackgroundView[2]; + private BackgroundView backgroundImage; private FrameLayout backgroundButtonsContainer; private FrameLayout messagesButtonsContainer; private HintView animationHint; private AnimatorSet motionAnimation; - private RadialProgress2 radialProgress; private FrameLayout bottomOverlayChat; private FrameLayout backgroundPlayAnimationView; private FrameLayout messagesPlayAnimationView; @@ -241,6 +260,9 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro private int checkColor; private float currentIntensity = 0.5f; private float previousIntensity; + private float dimAmount = 0f; + private float progressToDarkTheme; + DayNightSwitchDelegate onSwitchDayNightDelegate; private AnimatorSet patternViewAnimation; @@ -255,6 +277,7 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro private float parallaxScale = 1.0f; private TextView bottomOverlayChatText; + private RadialProgressView buttonProgressView; private String loadingFile = null; private File loadingFileObject = null; @@ -275,6 +298,154 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro private int maxWallpaperSize = 1920; private WallpaperActivityDelegate delegate; + long dialogId; + private boolean shouldShowDayNightIcon; + private boolean shouldShowBrightnessControll; + private RLottieDrawable sunDrawable; + private ActionBarMenuItem dayNightItem; + private float changeDayNightViewProgress; + private ValueAnimator changeDayNightViewAnimator; + private HeaderCell dimmingHeaderCell; + private BrightnessControlCell brightnessControlCell; + + GestureDetector2 gestureDetector2 = new GestureDetector2(new GestureDetector2.OnGestureListener() { + @Override + public boolean onDown(MotionEvent e) { + if (scroller != null) { + scroller.abortAnimation(); + } + return true; + } + + @Override + public void onUp(MotionEvent e) { + + } + + @Override + public void onShowPress(MotionEvent e) { + + } + + @Override + public boolean onSingleTapUp(MotionEvent e) { + return false; + } + + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { + if (scroller != null) { + scroller.abortAnimation(); + } + currentScrollOffset = Utilities.clamp(currentScrollOffset + distanceX, maxScrollOffset, 0); + backgroundImage.invalidate(); + return true; + } + + @Override + public void onLongPress(MotionEvent e) { + + } + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + if (scroller != null) { + scroller.abortAnimation(); + scroller.fling((int) currentScrollOffset, 0, Math.round(-velocityX), Math.round(velocityY), 0, (int) maxScrollOffset, 0, Integer.MAX_VALUE); + backgroundImage.postInvalidate(); + } + return true; + } + }); + + float maxScrollOffset; + float currentScrollOffset; + float defaultScrollOffset; + float croppedWidth; + private boolean hasScrollingBackground; + private int lastSizeHash; + private ValueAnimator valueAnimator; + private boolean setupFinished; + private TextView patternTitleView; + + public static void showFor(ChatActivity chatActivity, MessageObject message) { + if (message.messageOwner.action instanceof TLRPC.TL_messageActionSetChatWallPaper) { + TLRPC.TL_messageActionSetChatWallPaper actionSetChatWallPaper = (TLRPC.TL_messageActionSetChatWallPaper) message.messageOwner.action; + TLRPC.WallPaper res = actionSetChatWallPaper.wallpaper; + Object object; + if (res.pattern || res.document == null) { + WallpapersListActivity.ColorWallpaper colorWallpaper = new WallpapersListActivity.ColorWallpaper(res.slug, res.settings.background_color, res.settings.second_background_color, res.settings.third_background_color, res.settings.fourth_background_color, AndroidUtilities.getWallpaperRotation(res.settings.rotation, false), res.settings.intensity / 100.0f, res.settings.motion, null); + if (res instanceof TLRPC.TL_wallPaper) { + colorWallpaper.pattern = (TLRPC.TL_wallPaper) res; + } + object = colorWallpaper; + } else { + object = res; + } + boolean initialIsDark = Theme.getActiveTheme().isDark(); + ThemePreviewActivity wallpaperActivity = new ThemePreviewActivity(object, null, true, false) { + + @Override + public void onFragmentClosed() { + super.onFragmentClosed(); + chatActivity.themeDelegate.setCurrentTheme(chatActivity.themeDelegate.getCurrentTheme(), chatActivity.themeDelegate.getCurrentWallpaper(), false, initialIsDark); + } + +// @Override +// public boolean finishFragment(boolean animated) { +// boolean b = super.finishFragment(animated); +// if (b) { +// chatActivity.themeDelegate.setCurrentTheme(chatActivity.themeDelegate.getCurrentTheme(), chatActivity.themeDelegate.getCurrentWallpaper(), false, initialIsDark); +// } +// return b; +// } +// +// @Override +// public boolean onBackPressed() { +// boolean b = super.onBackPressed(); +// if (b) { +// chatActivity.themeDelegate.setCurrentTheme(chatActivity.themeDelegate.getCurrentTheme(), chatActivity.themeDelegate.getCurrentWallpaper(), false, initialIsDark); +// } +// return b; +// } + }; + if (res.settings != null) { + wallpaperActivity.setInitialModes(res.settings.blur, res.settings.motion, res.settings.intensity / 100f); + } + wallpaperActivity.setCurrentServerWallpaper(message); + wallpaperActivity.setDialogId(message.getDialogId()); + wallpaperActivity.setResourceProvider(chatActivity.themeDelegate); + wallpaperActivity.setOnSwitchDayNightDelegate(new DayNightSwitchDelegate() { + + boolean forceDark = initialIsDark; + + @Override + public boolean isDark() { + return forceDark; + } + + @Override + public void switchDayNight() { + forceDark = !forceDark; + chatActivity.themeDelegate.setCurrentTheme(chatActivity.themeDelegate.getCurrentTheme(), chatActivity.themeDelegate.getCurrentWallpaper(), true, forceDark); + } + }); + chatActivity.presentFragment(wallpaperActivity); + } + + } + + private void setCurrentServerWallpaper(MessageObject messageObject) { + serverWallpaper = messageObject; + } + + public void setDialogId(long dialogId) { + this.dialogId = dialogId; + } + + public void setOnSwitchDayNightDelegate(DayNightSwitchDelegate delegate) { + this.onSwitchDayNightDelegate = delegate; + } public interface WallpaperActivityDelegate { void didSetNewBackground(); @@ -362,19 +533,54 @@ public ThemePreviewActivity(Theme.ThemeInfo themeInfo, boolean deleteFile, int s msgOutMediaDrawableSelected.themePreview = true; } - public void setInitialModes(boolean blur, boolean motion) { + public void setInitialModes(boolean blur, boolean motion, float dim) { isBlurred = blur; isMotion = motion; + dimAmount = dim; } @SuppressLint("Recycle") @Override public View createView(Context context) { + msgOutDrawable.setResourceProvider(getResourceProvider()); + msgOutDrawableSelected.setResourceProvider(getResourceProvider()); + msgOutMediaDrawable.setResourceProvider(getResourceProvider()); + msgOutMediaDrawableSelected.setResourceProvider(getResourceProvider()); + hasOwnBackground = true; + shouldShowDayNightIcon = onSwitchDayNightDelegate != null && dialogId != 0; + shouldShowBrightnessControll = shouldShowDayNightIcon && (currentWallpaper instanceof WallpapersListActivity.FileWallpaper || (currentWallpaper instanceof TLRPC.TL_wallPaper && ((TLRPC.TL_wallPaper) currentWallpaper).document != null && !((TLRPC.TL_wallPaper) currentWallpaper).pattern)); + if (shouldShowBrightnessControll) { + progressToDarkTheme = onSwitchDayNightDelegate.isDark() ? 1f : 0; + } if (AndroidUtilities.isTablet()) { actionBar.setOccupyStatusBar(false); } page1 = new FrameLayout(context); + + if (shouldShowBrightnessControll && SharedConfig.dayNightWallpaperSwitchHint < 3) { + AndroidUtilities.runOnUIThread(() -> { + if (getParentActivity() == null || getContext() == null) { + return; + } + SharedConfig.increaseDayNightWallpaperSiwtchHint(); + HintView hintView = new HintView(getContext(), 7, true); + hintView.setAlpha(0.0f); + hintView.setVisibility(View.INVISIBLE); + hintView.setShowingDuration(4000); + frameLayout.addView(hintView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 4, 0, 4, 0)); + if (onSwitchDayNightDelegate.isDark()) { + hintView.setText(LocaleController.getString("PreviewWallpaperDay", R.string.PreviewWallpaperDay)); + } else { + hintView.setText(LocaleController.getString("PreviewWallpaperNight", R.string.PreviewWallpaperNight)); + } + hintView.setBackgroundColor(0xea272f38, 0xffffffff); + hintView.showForView(dayNightItem, true); + hintView.setExtraTranslationY(-AndroidUtilities.dp(14)); + }, 2000); + } + + ActionBarMenu menu = actionBar.createMenu(); final ActionBarMenuItem item = menu.addItem(0, R.drawable.ic_ab_search).setIsSearchField(true).setActionBarMenuItemSearchListener(new ActionBarMenuItem.ActionBarMenuItemSearchListener() { @Override @@ -432,7 +638,7 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { return result; } }; - page1.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + page1.setBackgroundColor(getThemedColor(Theme.key_windowBackgroundWhite)); page1.addView(actionBar, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); listView = new RecyclerListView(context); @@ -450,7 +656,7 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { floatingButton = new ImageView(context); floatingButton.setScaleType(ImageView.ScaleType.CENTER); - Drawable drawable = Theme.createSimpleSelectorCircleDrawable(AndroidUtilities.dp(56), Theme.getColor(Theme.key_chats_actionBackground), Theme.getColor(Theme.key_chats_actionPressedBackground)); + Drawable drawable = Theme.createSimpleSelectorCircleDrawable(AndroidUtilities.dp(56), getThemedColor(Theme.key_chats_actionBackground), getThemedColor(Theme.key_chats_actionPressedBackground)); if (Build.VERSION.SDK_INT < 21) { Drawable shadowDrawable = context.getResources().getDrawable(R.drawable.floating_shadow).mutate(); shadowDrawable.setColorFilter(new PorterDuffColorFilter(0xff000000, PorterDuff.Mode.SRC_IN)); @@ -459,7 +665,7 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { drawable = combinedDrawable; } floatingButton.setBackgroundDrawable(drawable); - floatingButton.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_chats_actionIcon), PorterDuff.Mode.SRC_IN)); + floatingButton.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_chats_actionIcon), PorterDuff.Mode.MULTIPLY)); floatingButton.setImageResource(R.drawable.floating_pencil); if (Build.VERSION.SDK_INT >= 21) { StateListAnimator animator = new StateListAnimator(); @@ -648,92 +854,120 @@ protected void onSend(LongSparseArray dids, int count, TLRPC.TL_fo } } }); - } - } - }); + } else if (id == OPTION_DAY_NIGHT) { + if (SharedConfig.dayNightWallpaperSwitchHint <= 3) { + SharedConfig.dayNightWallpaperSwitchHint = 10; + SharedConfig.increaseDayNightWallpaperSiwtchHint(); + } + boolean isDark = onSwitchDayNightDelegate.isDark(); + sunDrawable.setPlayInDirectionOfCustomEndFrame(true); + if (isDark) { + sunDrawable.setCustomEndFrame(0); + } else { + sunDrawable.setCustomEndFrame(36); + } + sunDrawable.start(); + if (onSwitchDayNightDelegate != null) { + onSwitchDayNightDelegate.switchDayNight(); - backgroundImage = new BackupImageView(context) { + if (onSwitchDayNightDelegate.isDark() && shouldShowBrightnessControll) { + dimmingHeaderCell.setVisibility(View.VISIBLE); + brightnessControlCell.setVisibility(View.VISIBLE); + } + } + if (shouldShowBrightnessControll) { + if (changeDayNightViewAnimator != null) { + changeDayNightViewAnimator.removeAllListeners(); + changeDayNightViewAnimator.cancel(); + } + changeDayNightViewAnimator = ValueAnimator.ofFloat(progressToDarkTheme, onSwitchDayNightDelegate.isDark() ? 1 : 0); + changeDayNightViewAnimator.addUpdateListener(animation -> { + progressToDarkTheme = (float) animation.getAnimatedValue(); + backgroundImage.invalidate(); + bottomOverlayChat.invalidate(); + dimmingHeaderCell.setAlpha(progressToDarkTheme); + brightnessControlCell.setAlpha(progressToDarkTheme); + listView2.setTranslationY(-AndroidUtilities.dp(BRIGHTNESS_CONTROL_HEIGHT) * progressToDarkTheme); + }); + changeDayNightViewAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (!onSwitchDayNightDelegate.isDark()) { + dimmingHeaderCell.setVisibility(View.GONE); + brightnessControlCell.setVisibility(View.GONE); + } + } + }); + changeDayNightViewAnimator.setDuration(250); + changeDayNightViewAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); + changeDayNightViewAnimator.start(); + } + } else if (id == OPTION_PHOTO_EDIT) { + if (currentWallpaper instanceof WallpapersListActivity.FileWallpaper) { + WallpapersListActivity.FileWallpaper fileWallpaper = (WallpapersListActivity.FileWallpaper) currentWallpaper; + if (fileWallpaper.originalPath == null) { + return; + } + MediaController.PhotoEntry photoEntry = new MediaController.PhotoEntry(0, 0, 0, fileWallpaper.originalPath.getAbsolutePath(), 0, false, 0, 0, 0); + photoEntry.isVideo = false; + photoEntry.thumbPath = null; + ArrayList arrayList = new ArrayList<>(); + arrayList.add(photoEntry); + PhotoViewer.getInstance().openPhotoForSelect(arrayList, 0, PhotoViewer.SELECT_TYPE_WALLPAPER, false, new PhotoViewer.EmptyPhotoViewerProvider() { + @Override + public void sendButtonPressed(int index, VideoEditedInfo videoEditedInfo, boolean notify, int scheduleDate, boolean forceDocument) { + if (photoEntry.imagePath != null) { + File currentWallpaperPath = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), Utilities.random.nextInt() + ".jpg"); + Point screenSize = AndroidUtilities.getRealScreenSize(); + Bitmap bitmap = ImageLoader.loadBitmap(photoEntry.imagePath, null, screenSize.x, screenSize.y, true); + FileOutputStream stream = null; + try { + stream = new FileOutputStream(currentWallpaperPath); + + bitmap.compress(Bitmap.CompressFormat.JPEG, 87, stream); + + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + File file = new File(photoEntry.imagePath); + currentWallpaper = new WallpapersListActivity.FileWallpaper("", file, file); + currentWallpaperBitmap = bitmap; + lastSizeHash = 0; + backgroundImage.requestLayout(); + setCurrentImage(false); + blurredBitmap = null; + updateBlurred(); + } + } + }, null); +// AndroidUtilities.runOnUIThread(() -> { +// PhotoViewer.getInstance().switchToEditMode(PhotoViewer.EDIT_MODE_FILTER); +// }, 200); + } - private Drawable background; - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - parallaxScale = parallaxEffect.getScale(getMeasuredWidth(), getMeasuredHeight()); - if (isMotion) { - setScaleX(parallaxScale); - setScaleY(parallaxScale); - } - if (radialProgress != null) { - int size = AndroidUtilities.dp(44); - int x = (getMeasuredWidth() - size) / 2; - int y = (getMeasuredHeight() - size) / 2; - radialProgress.setProgressRect(x, y, x + size, y + size); - } - - progressVisible = screenType == SCREEN_TYPE_CHANGE_BACKGROUND && getMeasuredWidth() <= getMeasuredHeight(); - } - - @Override - protected void onDraw(Canvas canvas) { - if (background instanceof ColorDrawable || background instanceof GradientDrawable || background instanceof MotionBackgroundDrawable) { - background.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight()); - background.draw(canvas); - } else if (background instanceof BitmapDrawable) { - BitmapDrawable bitmapDrawable = (BitmapDrawable) background; - if (bitmapDrawable.getTileModeX() == Shader.TileMode.REPEAT) { - canvas.save(); - float scale = 2.0f / AndroidUtilities.density; - canvas.scale(scale, scale); - background.setBounds(0, 0, (int) Math.ceil(getMeasuredWidth() / scale), (int) Math.ceil(getMeasuredHeight() / scale)); - background.draw(canvas); - canvas.restore(); - } else { - int viewHeight = getMeasuredHeight(); - float scaleX = (float) getMeasuredWidth() / (float) background.getIntrinsicWidth(); - float scaleY = (float) (viewHeight) / (float) background.getIntrinsicHeight(); - float scale = Math.max(scaleX, scaleY); - int width = (int) Math.ceil(background.getIntrinsicWidth() * scale * parallaxScale); - int height = (int) Math.ceil(background.getIntrinsicHeight() * scale * parallaxScale); - int x = (getMeasuredWidth() - width) / 2; - int y = (viewHeight - height) / 2; - background.setBounds(x, y, x + width, y + height); - background.draw(canvas); - } - } - super.onDraw(canvas); - if (progressVisible && radialProgress != null) { - radialProgress.draw(canvas); } } + }); - @Override - public Drawable getBackground() { - return background; - } + for (int i = 0; i < 2; i++) { + backgroundImages[i] = new BackgroundView(getContext()); - @Override - public void setBackground(Drawable drawable) { - background = drawable; - } + page2.addView(backgroundImages[i], LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 0, 0, 0, 48)); + } + backgroundImage = backgroundImages[0]; + backgroundImage.setVisibility(View.VISIBLE); + backgroundImages[1].setVisibility(View.GONE); - @Override - public void setAlpha(float alpha) { - if (radialProgress != null) { - radialProgress.setOverrideAlpha(alpha); - } - } - }; - page2.addView(backgroundImage, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 0, 0, 0, 48)); if (screenType == SCREEN_TYPE_CHANGE_BACKGROUND) { backgroundImage.getImageReceiver().setDelegate((imageReceiver, set, thumb, memCache) -> { if (!(currentWallpaper instanceof WallpapersListActivity.ColorWallpaper)) { Drawable dr = imageReceiver.getDrawable(); if (set && dr != null) { - if (!Theme.hasThemeKey(Theme.key_chat_serviceBackground) || backgroundImage.getBackground() instanceof MotionBackgroundDrawable) { + // if (!Theme.hasThemeKey(Theme.key_chat_serviceBackground) || backgroundImage.getBackground() instanceof MotionBackgroundDrawable) { Theme.applyChatServiceMessageColor(AndroidUtilities.calcDrawableColor(dr), dr); - } + // } listView2.invalidateViews(); if (backgroundButtonsContainer != null) { for (int a = 0, N = backgroundButtonsContainer.getChildCount(); a < N; a++) { @@ -745,9 +979,6 @@ public void setAlpha(float alpha) { messagesButtonsContainer.getChildAt(a).invalidate(); } } - if (radialProgress != null) { - radialProgress.setColors(Theme.key_chat_serviceBackground, Theme.key_chat_serviceBackground, Theme.key_chat_serviceText, Theme.key_chat_serviceText); - } if (!thumb && isBlurred && blurredBitmap == null) { backgroundImage.getImageReceiver().setCrossfadeWithOldImage(false); updateBlurred(); @@ -763,11 +994,41 @@ public void setAlpha(float alpha) { actionBar2.setSubtitle(LocaleController.formatPluralString("Members", 505)); } else { if (screenType == SCREEN_TYPE_CHANGE_BACKGROUND) { - actionBar2.setTitle(LocaleController.getString("BackgroundPreview", R.string.BackgroundPreview)); - if (BuildVars.DEBUG_PRIVATE_VERSION && Theme.getActiveTheme().getAccent(false) != null || currentWallpaper instanceof WallpapersListActivity.ColorWallpaper && !Theme.DEFAULT_BACKGROUND_SLUG.equals(((WallpapersListActivity.ColorWallpaper) currentWallpaper).slug) || currentWallpaper instanceof TLRPC.TL_wallPaper) { - ActionBarMenu menu2 = actionBar2.createMenu(); + if (dialogId != 0) { + actionBar2.setTitle(LocaleController.getString("WallpaperPreview", R.string.WallpaperPreview)); + } else { + actionBar2.setTitle(LocaleController.getString("BackgroundPreview", R.string.BackgroundPreview)); + } + ActionBarMenu menu2 = actionBar2.createMenu(); + if (currentWallpaper instanceof WallpapersListActivity.FileWallpaper) { + WallpapersListActivity.FileWallpaper fileWallpaper = (WallpapersListActivity.FileWallpaper) currentWallpaper; + if (fileWallpaper.originalPath != null) { + menu2.addItem(OPTION_PHOTO_EDIT, R.drawable.msg_header_draw); + } + } + if (dialogId == 0 && (BuildVars.DEBUG_PRIVATE_VERSION && Theme.getActiveTheme().getAccent(false) != null || currentWallpaper instanceof WallpapersListActivity.ColorWallpaper && !Theme.DEFAULT_BACKGROUND_SLUG.equals(((WallpapersListActivity.ColorWallpaper) currentWallpaper).slug) || currentWallpaper instanceof TLRPC.TL_wallPaper)) { menu2.addItem(5, R.drawable.msg_share_filled); } + if (dialogId != 0 && shouldShowDayNightIcon) { + sunDrawable = new RLottieDrawable(R.raw.sun, "" + R.raw.sun, AndroidUtilities.dp(28), AndroidUtilities.dp(28), true, null); + dayNightItem = menu2.addItem(OPTION_DAY_NIGHT, sunDrawable); + + sunDrawable.setPlayInDirectionOfCustomEndFrame(true); + if (onSwitchDayNightDelegate != null && !onSwitchDayNightDelegate.isDark()) { + sunDrawable.setCustomEndFrame(0); + sunDrawable.setCurrentFrame(0); + } else { + sunDrawable.setCurrentFrame(35); + sunDrawable.setCustomEndFrame(36); + } + sunDrawable.beginApplyLayerColors(); + int color = Theme.getColor(Theme.key_chats_menuName); + sunDrawable.setLayerColor("Sunny.**", color); + sunDrawable.setLayerColor("Path 6.**", color); + sunDrawable.setLayerColor("Path.**", color); + sunDrawable.setLayerColor("Path 5.**", color); + sunDrawable.commitApplyLayerColors(); + } } else if (screenType == SCREEN_TYPE_ACCENT_COLOR) { ActionBarMenu menu2 = actionBar2.createMenu(); saveItem = menu2.addItem(4, LocaleController.getString("Save", R.string.Save).toUpperCase()); @@ -795,11 +1056,11 @@ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { dropDown.setLines(1); dropDown.setMaxLines(1); dropDown.setEllipsize(TextUtils.TruncateAt.END); - dropDown.setTextColor(Theme.getColor(Theme.key_actionBarDefaultTitle)); + dropDown.setTextColor(getThemedColor(Theme.key_actionBarDefaultTitle)); dropDown.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); dropDown.setText(LocaleController.getString("ColorPickerMainColor", R.string.ColorPickerMainColor)); Drawable dropDownDrawable = context.getResources().getDrawable(R.drawable.ic_arrow_drop_down).mutate(); - dropDownDrawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_actionBarDefaultTitle), PorterDuff.Mode.SRC_IN)); + dropDownDrawable.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_actionBarDefaultTitle), PorterDuff.Mode.MULTIPLY)); dropDown.setCompoundDrawablesWithIntrinsicBounds(null, null, dropDownDrawable, null); dropDown.setCompoundDrawablePadding(AndroidUtilities.dp(4)); dropDown.setPadding(0, 0, AndroidUtilities.dp(10), 0); @@ -938,10 +1199,28 @@ protected boolean allowSelectChildAtPosition(View child) { return super.allowSelectChildAtPosition(child); } + boolean scrollingBackground; + float lastX, lastY, startX, startY; + @Override public boolean onTouchEvent(MotionEvent e) { checkMotionEvent(e); - return super.onTouchEvent(e); + if (hasScrollingBackground) { + if (e.getAction() == MotionEvent.ACTION_DOWN) { + lastX = startX = e.getX(); + lastY = startY = e.getY(); + } else if (e.getAction() == MotionEvent.ACTION_MOVE) { + if (!scrollingBackground && Math.abs(startX - e.getX()) > AndroidUtilities.touchSlop) { + getParent().requestDisallowInterceptTouchEvent(true); + scrollingBackground = true; + } + } else if (e.getAction() == MotionEvent.ACTION_CANCEL || e.getAction() == MotionEvent.ACTION_UP) { + scrollingBackground = false; + getParent().requestDisallowInterceptTouchEvent(false); + } + gestureDetector2.onTouchEvent(e); + } + return scrollingBackground || super.onTouchEvent(e); } private void checkMotionEvent(MotionEvent e) { @@ -1025,23 +1304,54 @@ public void onScrollStateChanged(RecyclerView recyclerView, int newState) { }); if (screenType == SCREEN_TYPE_ACCENT_COLOR || screenType == SCREEN_TYPE_CHANGE_BACKGROUND) { - radialProgress = new RadialProgress2(backgroundImage); - radialProgress.setColors(Theme.key_chat_serviceBackground, Theme.key_chat_serviceBackground, Theme.key_chat_serviceText, Theme.key_chat_serviceText); - if (screenType == SCREEN_TYPE_CHANGE_BACKGROUND) { bottomOverlayChat = new FrameLayout(context) { + final Paint dividerPaint = new Paint(); + final Paint backgroundPaint = new Paint(); + @Override - public void onDraw(Canvas canvas) { + protected void dispatchDraw(Canvas canvas) { + int offset = (int) (AndroidUtilities.dp(BRIGHTNESS_CONTROL_HEIGHT) * (1f - progressToDarkTheme)); int bottom = Theme.chat_composeShadowDrawable.getIntrinsicHeight(); - Theme.chat_composeShadowDrawable.setBounds(0, 0, getMeasuredWidth(), bottom); + Theme.chat_composeShadowDrawable.setBounds(0, offset, getMeasuredWidth(), offset + bottom); Theme.chat_composeShadowDrawable.draw(canvas); - canvas.drawRect(0, bottom, getMeasuredWidth(), getMeasuredHeight(), Theme.chat_composeBackgroundPaint); + backgroundPaint.setColor(getThemedColor(Theme.key_chat_messagePanelBackground)); + canvas.drawRect(0, offset + bottom, getMeasuredWidth(), getMeasuredHeight(), backgroundPaint); + if (shouldShowBrightnessControll) { + dividerPaint.setColor(getThemedColor(Theme.key_divider)); + dividerPaint.setAlpha((int) (dividerPaint.getAlpha() * progressToDarkTheme)); + int y = getMeasuredHeight() - AndroidUtilities.dp(53); + canvas.drawRect(0, y, getMeasuredWidth(), y + 1, dividerPaint); + } + canvas.save(); + canvas.clipRect(0, offset, getMeasuredWidth(), getMeasuredHeight()); + super.dispatchDraw(canvas); + canvas.restore(); } }; bottomOverlayChat.setWillNotDraw(false); bottomOverlayChat.setPadding(0, AndroidUtilities.dp(3), 0, 0); - page2.addView(bottomOverlayChat, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 51, Gravity.BOTTOM)); - bottomOverlayChat.setOnClickListener(view -> { + page2.addView(bottomOverlayChat, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 51 + BRIGHTNESS_CONTROL_HEIGHT, Gravity.BOTTOM)); + + + bottomOverlayChatText = new TextView(context); + bottomOverlayChatText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); + bottomOverlayChatText.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + bottomOverlayChatText.setTextColor(getThemedColor(Theme.key_chat_fieldOverlayText)); + if (dialogId != 0) { + bottomOverlayChatText.setText(LocaleController.getString("ApplyBackgroundForThisChat", R.string.ApplyBackgroundForThisChat)); + } else { + bottomOverlayChatText.setText(LocaleController.getString("SetBackground", R.string.SetBackground)); + } + FrameLayout textContainer = new FrameLayout(getContext()); + + buttonProgressView = new RadialProgressView(getContext()); + buttonProgressView.setSize(AndroidUtilities.dp(18)); + textContainer.addView(buttonProgressView, LayoutHelper.createFrame(28, 28, Gravity.CENTER)); + textContainer.setBackground(Theme.createSimpleSelectorRoundRectDrawable(0, Color.TRANSPARENT, ColorUtils.setAlphaComponent(getThemedColor(Theme.key_chat_fieldOverlayText), (int) (0.3f * 255)))); + textContainer.addView(bottomOverlayChatText, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); + bottomOverlayChat.addView(textContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 52, Gravity.BOTTOM)); + textContainer.setOnClickListener(view -> { boolean done; boolean sameFile = false; Theme.ThemeInfo theme = Theme.getActiveTheme(); @@ -1130,7 +1440,25 @@ public void onDraw(Canvas canvas) { done = true; } else { try { - File fromFile = wallpaper.originalPath != null ? wallpaper.originalPath : wallpaper.path; + File fromFile; + if (hasScrollingBackground && currentScrollOffset != defaultScrollOffset) { + Bitmap bitmap = Bitmap.createBitmap((int) croppedWidth, currentWallpaperBitmap.getHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + float k = currentScrollOffset / (float) maxScrollOffset * (currentWallpaperBitmap.getWidth() - bitmap.getWidth()); + canvas.translate(-k, 0); + canvas.drawBitmap(currentWallpaperBitmap, 0, 0, null); + + wallpaper.path = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), Utilities.random.nextInt() + ".jpg"); + FileOutputStream stream = new FileOutputStream(wallpaper.path); + bitmap.compress(Bitmap.CompressFormat.JPEG, 87, stream); + + stream.close(); + bitmap.recycle(); + + fromFile = wallpaper.path; + } else { + fromFile = wallpaper.originalPath != null ? wallpaper.originalPath : wallpaper.path; + } if (sameFile = fromFile.equals(toFile)) { done = true; } else { @@ -1228,7 +1556,11 @@ public void onDraw(Canvas canvas) { wallpaperInfo.gradientColor2 = gradientColor2; wallpaperInfo.gradientColor3 = gradientColor3; wallpaperInfo.rotation = rotation; - wallpaperInfo.intensity = currentIntensity; + if (shouldShowBrightnessControll && dimAmount >= 0) { + wallpaperInfo.intensity = dimAmount; + } else { + wallpaperInfo.intensity = currentIntensity; + } if (currentWallpaper instanceof WallpapersListActivity.ColorWallpaper) { WallpapersListActivity.ColorWallpaper colorWallpaper = (WallpapersListActivity.ColorWallpaper) currentWallpaper; String slugStr; @@ -1248,37 +1580,109 @@ public void onDraw(Canvas canvas) { wallpaperInfo.accessHash = colorWallpaper.parentWallpaper.access_hash; } } - MessagesController.getInstance(currentAccount).saveWallpaperToServer(path, wallpaperInfo, slug != null, 0); + wallpaperInfo.dialogId = dialogId; + if (dialogId != 0) { + TLRPC.UserFull userFull = getMessagesController().getUserFull(dialogId); + if (userFull != null) { + wallpaperInfo.prevUserWallpaper = userFull.wallpaper; + } + } + MessagesController.getInstance(currentAccount).saveWallpaperToServer(path, wallpaperInfo, slug != null && dialogId == 0, 0); + boolean needFinishFragment = true; if (done) { - Theme.serviceMessageColorBackup = Theme.getColor(Theme.key_chat_serviceBackground); - if (Theme.THEME_BACKGROUND_SLUG.equals(wallpaperInfo.slug)) { - wallpaperInfo = null; - } - Theme.getActiveTheme().setOverrideWallpaper(wallpaperInfo); - Theme.reloadWallpaper(); - if (!sameFile) { - ImageLoader.getInstance().removeImage(ImageLoader.getHttpFileName(toFile.getAbsolutePath()) + "@100_100"); + if (dialogId != 0) { + needFinishFragment = false; + + if (path != null && getMessagesController().uploadingWallpaperInfo == wallpaperInfo) { + TLRPC.WallPaper wallPaper = new TLRPC.TL_wallPaper(); + wallPaper.settings = new TLRPC.TL_wallPaperSettings(); + wallPaper.settings.intensity = (int) (wallpaperInfo.intensity * 100); + wallPaper.settings.blur = wallpaperInfo.isBlurred; + wallPaper.settings.motion = wallpaperInfo.isMotion; + wallPaper.uploadingImage = path.getAbsolutePath(); + Bitmap bitmap = Bitmap.createBitmap(50, 50, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + float s = Math.max(50 / (float) backgroundImage.getMeasuredWidth(), 50 / (float) backgroundImage.getMeasuredHeight()); + canvas.scale(s, s); + if (backgroundImage.getMeasuredHeight() > backgroundImage.getMeasuredWidth()) { + canvas.translate(0, -(backgroundImage.getMeasuredHeight() - backgroundImage.getMeasuredWidth()) / 2f); + } else { + canvas.translate(-(backgroundImage.getMeasuredWidth() - backgroundImage.getMeasuredHeight()) / 2f, 0); + } + float currentDim = dimAmount; + dimAmount = 0; + backgroundImage.draw(canvas); + dimAmount = currentDim; + Utilities.blurBitmap(bitmap, 3, 1, bitmap.getWidth(), bitmap.getHeight(), bitmap.getRowBytes()); + wallPaper.stripedThumb = bitmap; + + createServiceMessageLocal(wallPaper); + + TLRPC.UserFull fullUser = getMessagesController().getUserFull(dialogId); + if (fullUser != null) { + fullUser.wallpaper = wallPaper; + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.userInfoDidLoad, dialogId, fullUser); + } + } else { + ChatThemeController.getInstance(currentAccount).setWallpaperToUser(dialogId, null, wallpaperInfo, serverWallpaper, () -> { + + }); + } + setupFinished = true; + if (delegate != null) { + delegate.didSetNewBackground(); + } + finishFragment(); + } else { + Theme.serviceMessageColorBackup = getThemedColor(Theme.key_chat_serviceBackground); + if (Theme.THEME_BACKGROUND_SLUG.equals(wallpaperInfo.slug)) { + wallpaperInfo = null; + } + Theme.getActiveTheme().setOverrideWallpaper(wallpaperInfo); + Theme.reloadWallpaper(true); + if (!sameFile) { + ImageLoader.getInstance().removeImage(ImageLoader.getHttpFileName(toFile.getAbsolutePath()) + "@100_100"); + } } } - if (delegate != null) { - delegate.didSetNewBackground(); + if (needFinishFragment) { + if (delegate != null) { + delegate.didSetNewBackground(); + } + finishFragment(); } - finishFragment(); }); - bottomOverlayChatText = new TextView(context); - bottomOverlayChatText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); - bottomOverlayChatText.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - bottomOverlayChatText.setTextColor(Theme.getColor(Theme.key_chat_fieldOverlayText)); - bottomOverlayChatText.setText(LocaleController.getString("SetBackground", R.string.SetBackground)); - bottomOverlayChat.addView(bottomOverlayChatText, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); + AndroidUtilities.updateViewVisibilityAnimated(buttonProgressView, false, 0.5f, false); + AndroidUtilities.updateViewVisibilityAnimated(bottomOverlayChatText, true, 0.8f, false); + + if (shouldShowBrightnessControll) { + dimmingHeaderCell = new HeaderCell(getContext(), getResourceProvider()); + dimmingHeaderCell.setText(LocaleController.getString("BackgroundDimming", R.string.BackgroundDimming)); + brightnessControlCell = new BrightnessControlCell(getContext(), BrightnessControlCell.TYPE_WALLPAPER_DIM, getResourceProvider()) { + @Override + protected void didChangedValue(float value) { + dimAmount = value; + backgroundImage.invalidate(); + } + }; + brightnessControlCell.setProgress(dimAmount); + bottomOverlayChat.addView(dimmingHeaderCell, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP)); + bottomOverlayChat.addView(brightnessControlCell, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, BRIGHTNESS_CONTROL_HEIGHT, Gravity.BOTTOM, 0, 0, 0, 56)); + + if (onSwitchDayNightDelegate != null) { + dimmingHeaderCell.setVisibility(onSwitchDayNightDelegate.isDark() ? View.VISIBLE : View.GONE); + brightnessControlCell.setVisibility(onSwitchDayNightDelegate.isDark() ? View.VISIBLE : View.GONE); + listView2.setTranslationY(-AndroidUtilities.dp(BRIGHTNESS_CONTROL_HEIGHT) * progressToDarkTheme); + } + } } Rect paddings = new Rect(); sheetDrawable = context.getResources().getDrawable(R.drawable.sheet_shadow_round).mutate(); sheetDrawable.getPadding(paddings); - sheetDrawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_windowBackgroundWhite), PorterDuff.Mode.SRC_IN)); + sheetDrawable.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_windowBackgroundWhite), PorterDuff.Mode.MULTIPLY)); TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); textPaint.setTextSize(AndroidUtilities.dp(14)); @@ -1367,7 +1771,7 @@ public void onClick(View v) { for (int a = 0; a < textsCount; a++) { final int num = a; - backgroundCheckBoxView[a] = new WallpaperCheckBoxView(context, screenType != SCREEN_TYPE_ACCENT_COLOR && !(currentWallpaper instanceof WallpapersListActivity.ColorWallpaper) || a != 0, backgroundImage); + backgroundCheckBoxView[a] = new WallpaperCheckBoxView(context, screenType != SCREEN_TYPE_ACCENT_COLOR && !(currentWallpaper instanceof WallpapersListActivity.ColorWallpaper) || a != 0, backgroundImage, getResourceProvider()); backgroundCheckBoxView[a].setBackgroundColor(backgroundColor); backgroundCheckBoxView[a].setText(texts[a], textSizes[a], maxTextSize); @@ -1544,7 +1948,7 @@ public void onClick(View v) { for (int a = 0; a < 2; a++) { final int num = a; - messagesCheckBoxView[a] = new WallpaperCheckBoxView(context, a == 0, backgroundImage); + messagesCheckBoxView[a] = new WallpaperCheckBoxView(context, a == 0, backgroundImage, getResourceProvider()); messagesCheckBoxView[a].setText(texts[a], textSizes[a], maxTextSize); if (a == 0) { @@ -1611,12 +2015,15 @@ public void onDraw(Canvas canvas) { if (a == 1 || screenType == SCREEN_TYPE_CHANGE_BACKGROUND) { patternsButtonsContainer[a] = new FrameLayout(context) { + + Paint paint = new Paint(); @Override public void onDraw(Canvas canvas) { int bottom = Theme.chat_composeShadowDrawable.getIntrinsicHeight(); Theme.chat_composeShadowDrawable.setBounds(0, 0, getMeasuredWidth(), bottom); Theme.chat_composeShadowDrawable.draw(canvas); - canvas.drawRect(0, bottom, getMeasuredWidth(), getMeasuredHeight(), Theme.chat_composeBackgroundPaint); + paint.setColor(getThemedColor(Theme.key_chat_messagePanelBackground)); + canvas.drawRect(0, bottom, getMeasuredWidth(), getMeasuredHeight(), paint); } }; patternsButtonsContainer[a].setWillNotDraw(false); @@ -1627,11 +2034,11 @@ public void onDraw(Canvas canvas) { patternsCancelButton[a] = new TextView(context); patternsCancelButton[a].setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); patternsCancelButton[a].setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - patternsCancelButton[a].setTextColor(Theme.getColor(Theme.key_chat_fieldOverlayText)); + patternsCancelButton[a].setTextColor(getThemedColor(Theme.key_chat_fieldOverlayText)); patternsCancelButton[a].setText(LocaleController.getString("Cancel", R.string.Cancel).toUpperCase()); patternsCancelButton[a].setGravity(Gravity.CENTER); patternsCancelButton[a].setPadding(AndroidUtilities.dp(21), 0, AndroidUtilities.dp(21), 0); - patternsCancelButton[a].setBackgroundDrawable(Theme.createSelectorDrawable(Theme.getColor(Theme.key_listSelector), 0)); + patternsCancelButton[a].setBackgroundDrawable(Theme.createSelectorDrawable(getThemedColor(Theme.key_listSelector), 0)); patternsButtonsContainer[a].addView(patternsCancelButton[a], LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP)); patternsCancelButton[a].setOnClickListener(v -> { if (patternViewAnimation != null) { @@ -1676,11 +2083,11 @@ public void onDraw(Canvas canvas) { patternsSaveButton[a] = new TextView(context); patternsSaveButton[a].setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); patternsSaveButton[a].setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - patternsSaveButton[a].setTextColor(Theme.getColor(Theme.key_chat_fieldOverlayText)); + patternsSaveButton[a].setTextColor(getThemedColor(Theme.key_chat_fieldOverlayText)); patternsSaveButton[a].setText(LocaleController.getString("ApplyTheme", R.string.ApplyTheme).toUpperCase()); patternsSaveButton[a].setGravity(Gravity.CENTER); patternsSaveButton[a].setPadding(AndroidUtilities.dp(21), 0, AndroidUtilities.dp(21), 0); - patternsSaveButton[a].setBackgroundDrawable(Theme.createSelectorDrawable(Theme.getColor(Theme.key_listSelector), 0)); + patternsSaveButton[a].setBackgroundDrawable(Theme.createSelectorDrawable(getThemedColor(Theme.key_listSelector), 0)); patternsButtonsContainer[a].addView(patternsSaveButton[a], LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT, Gravity.RIGHT | Gravity.TOP)); patternsSaveButton[a].setOnClickListener(v -> { if (patternViewAnimation != null) { @@ -1695,19 +2102,19 @@ public void onDraw(Canvas canvas) { } if (a == 1) { - TextView titleView = new TextView(context); - titleView.setLines(1); - titleView.setSingleLine(true); - titleView.setText(LocaleController.getString("BackgroundChoosePattern", R.string.BackgroundChoosePattern)); + patternTitleView = new TextView(context); + patternTitleView.setLines(1); + patternTitleView.setSingleLine(true); + patternTitleView.setText(LocaleController.getString("BackgroundChoosePattern", R.string.BackgroundChoosePattern)); - titleView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); - titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); - titleView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - titleView.setPadding(AndroidUtilities.dp(21), AndroidUtilities.dp(6), AndroidUtilities.dp(21), AndroidUtilities.dp(8)); + patternTitleView.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteBlackText)); + patternTitleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); + patternTitleView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + patternTitleView.setPadding(AndroidUtilities.dp(21), AndroidUtilities.dp(6), AndroidUtilities.dp(21), AndroidUtilities.dp(8)); - titleView.setEllipsize(TextUtils.TruncateAt.MIDDLE); - titleView.setGravity(Gravity.CENTER_VERTICAL); - patternLayout[a].addView(titleView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT | Gravity.TOP, 0, 21, 0, 0)); + patternTitleView.setEllipsize(TextUtils.TruncateAt.MIDDLE); + patternTitleView.setGravity(Gravity.CENTER_VERTICAL); + patternLayout[a].addView(patternTitleView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT | Gravity.TOP, 0, 21, 0, 0)); patternsListView = new RecyclerListView(context) { @Override @@ -1757,7 +2164,7 @@ public void getItemOffsets(Rect outRect, View view, RecyclerView parent, Recycle intensityCell.setText(LocaleController.getString("BackgroundIntensity", R.string.BackgroundIntensity)); patternLayout[a].addView(intensityCell, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 0, 175, 0, 0)); - intensitySeekBar = new SeekBarView(context) { + intensitySeekBar = new SeekBarView(context, getResourceProvider()) { @Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { @@ -1772,23 +2179,7 @@ public boolean onTouchEvent(MotionEvent event) { @Override public void onSeekBarDrag(boolean stop, float progress) { currentIntensity = progress; - backgroundImage.getImageReceiver().setAlpha(Math.abs(currentIntensity)); - backgroundImage.invalidate(); - patternsListView.invalidateViews(); - if (currentIntensity >= 0) { - if (Build.VERSION.SDK_INT >= 29 && backgroundImage.getBackground() instanceof MotionBackgroundDrawable) { - backgroundImage.getImageReceiver().setBlendMode(BlendMode.SOFT_LIGHT); - } - backgroundImage.getImageReceiver().setGradientBitmap(null); - } else { - if (Build.VERSION.SDK_INT >= 29) { - backgroundImage.getImageReceiver().setBlendMode(null); - } - if (backgroundImage.getBackground() instanceof MotionBackgroundDrawable) { - MotionBackgroundDrawable motionBackgroundDrawable = (MotionBackgroundDrawable) backgroundImage.getBackground(); - backgroundImage.getImageReceiver().setGradientBitmap(motionBackgroundDrawable.getBitmap()); - } - } + updateIntensity(); } @Override @@ -1844,7 +2235,7 @@ public void deleteTheme() { showDialog(alertDialog); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(getThemedColor(Theme.key_text_RedBold)); } } @@ -1867,6 +2258,7 @@ public boolean hasChanges() { return ThemePreviewActivity.this.hasChanges(colorType); } }); + colorPicker.setResourcesProvider(getResourceProvider()); if (screenType == SCREEN_TYPE_ACCENT_COLOR) { patternLayout[a].addView(colorPicker, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.CENTER_HORIZONTAL)); if (applyingTheme.isDark()) { @@ -1991,7 +2383,7 @@ public void unregisterDataSetObserver(DataSetObserver observer) { } } }); - AndroidUtilities.setViewPagerEdgeEffectColor(viewPager, Theme.getColor(Theme.key_actionBarDefault)); + AndroidUtilities.setViewPagerEdgeEffectColor(viewPager, getThemedColor(Theme.key_actionBarDefault)); frameLayout.addView(viewPager, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 0, 0, 0, screenType == SCREEN_TYPE_PREVIEW ? 48 : 0)); undoView = new UndoView(context, this); @@ -2000,7 +2392,7 @@ public void unregisterDataSetObserver(DataSetObserver observer) { if (screenType == SCREEN_TYPE_PREVIEW) { View shadow = new View(context); - shadow.setBackgroundColor(Theme.getColor(Theme.key_dialogShadowLine)); + shadow.setBackgroundColor(getThemedColor(Theme.key_dialogShadowLine)); FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1, Gravity.LEFT | Gravity.BOTTOM); layoutParams.bottomMargin = AndroidUtilities.dp(48); frameLayout.addView(shadow, layoutParams); @@ -2091,9 +2483,40 @@ protected void onDraw(Canvas canvas) { showPatternsView(0, true, false); } + scroller = new Scroller(getContext()); + + if (parentLayout != null && parentLayout.getBottomSheet() != null) { + parentLayout.getBottomSheet().fixNavigationBar(getThemedColor(Theme.key_dialogBackground)); + } + return fragmentView; } + private void updateIntensity() { + backgroundImage.getImageReceiver().setAlpha(Math.abs(currentIntensity)); + backgroundImage.invalidate(); + patternsListView.invalidateViews(); + if (currentIntensity >= 0) { +// if (Build.VERSION.SDK_INT >= 29 && backgroundImage.getBackground() instanceof MotionBackgroundDrawable) { +// backgroundImage.getImageReceiver().setBlendMode(BlendMode.SOFT_LIGHT); +// } + backgroundImage.getImageReceiver().setGradientBitmap(null); + } else { + if (Build.VERSION.SDK_INT >= 29) { + backgroundImage.getImageReceiver().setBlendMode(null); + } + if (backgroundImage.getBackground() instanceof MotionBackgroundDrawable) { + MotionBackgroundDrawable motionBackgroundDrawable = (MotionBackgroundDrawable) backgroundImage.getBackground(); + backgroundImage.getImageReceiver().setGradientBitmap(motionBackgroundDrawable.getBitmap()); + } + } + } + + private void showProgress() { + AndroidUtilities.updateViewVisibilityAnimated(buttonProgressView, true, 0.5f, true); + AndroidUtilities.updateViewVisibilityAnimated(bottomOverlayChatText, false, 0.8f, true); + } + private void onColorsRotate() { if (screenType == SCREEN_TYPE_CHANGE_BACKGROUND) { backgroundRotation += 45; @@ -2222,10 +2645,10 @@ private void selectColorType(int id, boolean ask) { case 2: { dropDown.setText(LocaleController.getString("ColorPickerBackground", R.string.ColorPickerBackground)); - int defaultBackground = Theme.getColor(Theme.key_chat_wallpaper); - int defaultGradient1 = Theme.hasThemeKey(Theme.key_chat_wallpaper_gradient_to1) ? Theme.getColor(Theme.key_chat_wallpaper_gradient_to1) : 0; - int defaultGradient2 = Theme.hasThemeKey(Theme.key_chat_wallpaper_gradient_to2) ? Theme.getColor(Theme.key_chat_wallpaper_gradient_to2) : 0; - int defaultGradient3 = Theme.hasThemeKey(Theme.key_chat_wallpaper_gradient_to3) ? Theme.getColor(Theme.key_chat_wallpaper_gradient_to3) : 0; + int defaultBackground = getThemedColor(Theme.key_chat_wallpaper); + int defaultGradient1 = Theme.hasThemeKey(Theme.key_chat_wallpaper_gradient_to1) ? getThemedColor(Theme.key_chat_wallpaper_gradient_to1) : 0; + int defaultGradient2 = Theme.hasThemeKey(Theme.key_chat_wallpaper_gradient_to2) ? getThemedColor(Theme.key_chat_wallpaper_gradient_to2) : 0; + int defaultGradient3 = Theme.hasThemeKey(Theme.key_chat_wallpaper_gradient_to3) ? getThemedColor(Theme.key_chat_wallpaper_gradient_to3) : 0; int backgroundGradientOverrideColor1 = (int) accent.backgroundGradientOverrideColor1; if (backgroundGradientOverrideColor1 == 0 && accent.backgroundGradientOverrideColor1 != 0) { @@ -2335,7 +2758,51 @@ private void selectPattern(int position) { if (wallPaper == null) { return; } - backgroundImage.setImage(ImageLocation.getForDocument(wallPaper.document), imageFilter, null, null, "jpg", wallPaper.document.size, 1, wallPaper); + if (valueAnimator != null) { + valueAnimator.removeAllListeners(); + valueAnimator.cancel(); + } + BackgroundView imageView = backgroundImages[0]; + + backgroundImages[0] = backgroundImages[1]; + backgroundImages[1] = imageView; + + page2.removeView(backgroundImages[0]); + int index = page2.indexOfChild(backgroundImages[1]); + page2.addView(backgroundImages[0], index + 1); + + backgroundImage = backgroundImages[0]; + backgroundImage.setBackground(backgroundImages[1].getBackground()); + updateIntensity(); + backgroundImages[1].setVisibility(View.VISIBLE); + backgroundImages[1].setAlpha(1f); + backgroundImage.setVisibility(View.VISIBLE); + + valueAnimator = ValueAnimator.ofFloat(0, 1f); + valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(@NonNull ValueAnimator animation) { + float value = (Float) animation.getAnimatedValue(); + // backgroundImages[1].getImageReceiver().setAlpha(Math.abs(currentIntensity) * (1f - value)); + backgroundImage.setAlpha(value); + } + }); + valueAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + // backgroundImage.drawBackground = true; + backgroundImage.invalidate(); + backgroundImages[1].setVisibility(View.GONE); + valueAnimator = null; + } + }); + valueAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); + valueAnimator.setDuration(300); + valueAnimator.start(); + backgroundImage.getImageReceiver().setCrossfadeDuration(300); + backgroundImage.getImageReceiver().setImage(ImageLocation.getForDocument(wallPaper.document), imageFilter, null, null, null, wallPaper.document.size, "jpg", wallPaper, 1); + backgroundImage.onNewImageSet(); selectedPattern = wallPaper; isMotion = backgroundCheckBoxView[2].isChecked(); updateButtonState(false, true); @@ -2476,21 +2943,21 @@ private boolean hasChanges(int type) { private boolean checkDiscard() { if (screenType == SCREEN_TYPE_ACCENT_COLOR && ( accent.accentColor != backupAccentColor || - accent.accentColor2 != backupAccentColor2 || - accent.myMessagesAccentColor != backupMyMessagesAccentColor || - accent.myMessagesGradientAccentColor1 != backupMyMessagesGradientAccentColor1 || - accent.myMessagesGradientAccentColor2 != backupMyMessagesGradientAccentColor2 || - accent.myMessagesGradientAccentColor3 != backupMyMessagesGradientAccentColor3 || - accent.myMessagesAnimated != backupMyMessagesAnimated || - accent.backgroundOverrideColor != backupBackgroundOverrideColor || - accent.backgroundGradientOverrideColor1 != backupBackgroundGradientOverrideColor1 || - accent.backgroundGradientOverrideColor2 != backupBackgroundGradientOverrideColor2 || - accent.backgroundGradientOverrideColor3 != backupBackgroundGradientOverrideColor3 || - Math.abs(accent.patternIntensity - backupIntensity) > 0.001f || - accent.backgroundRotation != backupBackgroundRotation || - !accent.patternSlug.equals(selectedPattern != null ? selectedPattern.slug : "") || - selectedPattern != null && accent.patternMotion != isMotion || - selectedPattern != null && accent.patternIntensity != currentIntensity + accent.accentColor2 != backupAccentColor2 || + accent.myMessagesAccentColor != backupMyMessagesAccentColor || + accent.myMessagesGradientAccentColor1 != backupMyMessagesGradientAccentColor1 || + accent.myMessagesGradientAccentColor2 != backupMyMessagesGradientAccentColor2 || + accent.myMessagesGradientAccentColor3 != backupMyMessagesGradientAccentColor3 || + accent.myMessagesAnimated != backupMyMessagesAnimated || + accent.backgroundOverrideColor != backupBackgroundOverrideColor || + accent.backgroundGradientOverrideColor1 != backupBackgroundGradientOverrideColor1 || + accent.backgroundGradientOverrideColor2 != backupBackgroundGradientOverrideColor2 || + accent.backgroundGradientOverrideColor3 != backupBackgroundGradientOverrideColor3 || + Math.abs(accent.patternIntensity - backupIntensity) > 0.001f || + accent.backgroundRotation != backupBackgroundRotation || + !accent.patternSlug.equals(selectedPattern != null ? selectedPattern.slug : "") || + selectedPattern != null && accent.patternMotion != isMotion || + selectedPattern != null && accent.patternIntensity != currentIntensity )) { AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); builder.setTitle(LocaleController.getString("SaveChangesAlertTitle", R.string.SaveChangesAlertTitle)); @@ -2507,6 +2974,7 @@ private boolean checkDiscard() { public boolean onFragmentCreate() { NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.emojiLoaded); NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.invalidateMotionBackground); + getNotificationCenter().addObserver(this, NotificationCenter.wallpaperSettedToUser); if (screenType == SCREEN_TYPE_ACCENT_COLOR || screenType == SCREEN_TYPE_PREVIEW) { NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.didSetNewWallpapper); } @@ -2538,10 +3006,11 @@ public boolean onFragmentCreate() { public void onFragmentDestroy() { NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.emojiLoaded); NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.invalidateMotionBackground); + getNotificationCenter().removeObserver(this, NotificationCenter.wallpaperSettedToUser); if (frameLayout != null && onGlobalLayoutListener != null) { frameLayout.getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayoutListener); } - if (screenType == SCREEN_TYPE_CHANGE_BACKGROUND || screenType == SCREEN_TYPE_ACCENT_COLOR) { + if ((screenType == SCREEN_TYPE_CHANGE_BACKGROUND || screenType == SCREEN_TYPE_ACCENT_COLOR) && onSwitchDayNightDelegate == null) { AndroidUtilities.runOnUIThread(() -> Theme.setChangingWallpaper(false)); } @@ -2586,8 +3055,7 @@ public void onResume() { if (isMotion) { parallaxEffect.setEnabled(true); } - AndroidUtilities.requestAdjustResize(getParentActivity(), classGuid); - AndroidUtilities.removeAdjustResize(getParentActivity(), classGuid); + Theme.disallowChangeServiceMessageColor = true; } @Override @@ -2596,6 +3064,7 @@ public void onPause() { if (isMotion) { parallaxEffect.setEnabled(false); } + Theme.disallowChangeServiceMessageColor = false; } @Override @@ -2605,25 +3074,17 @@ public boolean isSwipeBackEnabled(MotionEvent event) { @Override public void onFailedDownload(String fileName, boolean canceled) { - updateButtonState( true, canceled); + updateButtonState(true, canceled); } @Override public void onSuccessDownload(String fileName) { - if (radialProgress != null) { - radialProgress.setProgress(1, progressVisible); - } updateButtonState(false, true); } @Override public void onProgressDownload(String fileName, long downloadedSize, long totalSize) { - if (radialProgress != null) { - radialProgress.setProgress(Math.min(1f, downloadedSize / (float) totalSize), progressVisible); - if (radialProgress.getIcon() != MediaActionDrawable.ICON_EMPTY) { - updateButtonState(false, true); - } - } + } @Override @@ -2792,6 +3253,10 @@ public void didReceivedNotification(int id, int account, Object... args) { } })); ConnectionsManager.getInstance(currentAccount).bindRequestToGuid(reqId, classGuid); + } else if (id == NotificationCenter.wallpaperSettedToUser) { + if (dialogId != 0) { + finishFragment(); + } } } @@ -2836,8 +3301,8 @@ private void cancelThemeApply(boolean back) { } } - private int getButtonsColor(String key) { - return useDefaultThemeForButtons ? Theme.getDefaultColor(key) : Theme.getColor(key); + private int getButtonsColor(int key) { + return useDefaultThemeForButtons ? Theme.getDefaultColor(key) : getThemedColor(key); } private void scheduleApplyColor(int color, int num, boolean applyNow) { @@ -2996,7 +3461,7 @@ private void applyColor(int color, int num) { for (int i = 0, size = themeDescriptions.size(); i < size; i++) { ThemeDescription description = themeDescriptions.get(i); - description.setColor(Theme.getColor(description.getCurrentKey()), false, false); + description.setColor(getThemedColor(description.getCurrentKey()), false, false); } listView.invalidateViews(); @@ -3047,13 +3512,9 @@ private void updateButtonState(boolean ifSame, boolean animated) { } if (fileExists = path.exists()) { DownloadController.getInstance(currentAccount).removeLoadingFileObserver(this); - if (radialProgress != null) { - radialProgress.setProgress(1, animated); - radialProgress.setIcon(MediaActionDrawable.ICON_NONE, ifSame, animated); - } backgroundImage.invalidate(); if (screenType == SCREEN_TYPE_CHANGE_BACKGROUND) { - if (size != 0) { + if (size != 0 && dialogId == 0) { actionBar2.setSubtitle(AndroidUtilities.formatFileSize(size)); } else { actionBar2.setSubtitle(null); @@ -3061,17 +3522,7 @@ private void updateButtonState(boolean ifSame, boolean animated) { } } else { DownloadController.getInstance(currentAccount).addLoadingFileObserver(fileName, null, this); - if (radialProgress != null) { - boolean isLoading = FileLoader.getInstance(currentAccount).isLoadingFile(fileName); - Float progress = ImageLoader.getInstance().getFileProgress(fileName); - if (progress != null) { - radialProgress.setProgress(progress, animated); - } else { - radialProgress.setProgress(0, animated); - } - radialProgress.setIcon(MediaActionDrawable.ICON_EMPTY, ifSame, animated); - } - if (screenType == SCREEN_TYPE_CHANGE_BACKGROUND) { + if (screenType == SCREEN_TYPE_CHANGE_BACKGROUND && dialogId == 0) { actionBar2.setSubtitle(LocaleController.getString("LoadingFullImage", R.string.LoadingFullImage)); } backgroundImage.invalidate(); @@ -3089,10 +3540,6 @@ private void updateButtonState(boolean ifSame, boolean animated) { saveItem.setEnabled(fileExists); saveItem.setAlpha(fileExists ? 1.0f : 0.5f); } - } else { - if (radialProgress != null) { - radialProgress.setIcon(MediaActionDrawable.ICON_NONE, ifSame, animated); - } } } @@ -3414,7 +3861,7 @@ private void updatePlayAnimationView(boolean animated) { if (accent != null) { color2 = (int) accent.backgroundGradientOverrideColor2; } else { - color2 = Theme.getColor(Theme.key_chat_wallpaper_gradient_to2); + color2 = getThemedColor(Theme.key_chat_wallpaper_gradient_to2); } } else if (screenType == SCREEN_TYPE_ACCENT_COLOR) { int defaultBackgroundGradient2 = Theme.getDefaultAccentColor(Theme.key_chat_wallpaper_gradient_to2); @@ -3511,7 +3958,7 @@ public void onAnimationEnd(Animator animation) { ObjectAnimator.ofFloat(messagesPlayAnimationView, View.SCALE_Y, visible ? 1.0f : 0.0f), ObjectAnimator.ofFloat(messagesCheckBoxView[0], View.TRANSLATION_X, visible ? -AndroidUtilities.dp(34) : 0.0f), ObjectAnimator.ofFloat(messagesCheckBoxView[1], View.TRANSLATION_X, visible ? AndroidUtilities.dp(34) : 0.0f)); - messagesPlayViewAnimator.setDuration(180); + messagesPlayViewAnimator.setDuration(180); messagesPlayViewAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -3582,14 +4029,14 @@ private void setBackgroundColor(int color, int num, boolean applyNow, boolean an if (!Theme.hasThemeKey(Theme.key_chat_serviceBackground) || backgroundImage.getBackground() instanceof MotionBackgroundDrawable) { Theme.applyChatServiceMessageColor(new int[]{checkColor, checkColor, checkColor, checkColor}, backgroundImage.getBackground()); } else if (Theme.getCachedWallpaperNonBlocking() instanceof MotionBackgroundDrawable) { - int c = Theme.getColor(Theme.key_chat_serviceBackground); + int c = getThemedColor(Theme.key_chat_serviceBackground); Theme.applyChatServiceMessageColor(new int[]{c, c, c, c}, backgroundImage.getBackground()); } if (backgroundPlayAnimationImageView != null) { - backgroundPlayAnimationImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_chat_serviceText), PorterDuff.Mode.MULTIPLY)); + backgroundPlayAnimationImageView.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_chat_serviceText), PorterDuff.Mode.MULTIPLY)); } if (messagesPlayAnimationImageView != null) { - messagesPlayAnimationImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_chat_serviceText), PorterDuff.Mode.MULTIPLY)); + messagesPlayAnimationImageView.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_chat_serviceText), PorterDuff.Mode.MULTIPLY)); } if (backgroundImage != null) { backgroundImage.getImageReceiver().setColorFilter(new PorterDuffColorFilter(patternColor, blendMode)); @@ -3625,9 +4072,6 @@ private void setBackgroundColor(int color, int num, boolean applyNow, boolean an messagesButtonsContainer.getChildAt(a).invalidate(); } } - if (radialProgress != null) { - radialProgress.setColors(Theme.key_chat_serviceBackground, Theme.key_chat_serviceBackground, Theme.key_chat_serviceText, Theme.key_chat_serviceText); - } } private void setCurrentImage(boolean setThumb) { @@ -3809,10 +4253,10 @@ public void onSizeReady(int width, int height) { } } if (backgroundPlayAnimationImageView != null) { - backgroundPlayAnimationImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_chat_serviceText), PorterDuff.Mode.MULTIPLY)); + backgroundPlayAnimationImageView.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_chat_serviceText), PorterDuff.Mode.MULTIPLY)); } if (messagesPlayAnimationImageView != null) { - messagesPlayAnimationImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_chat_serviceText), PorterDuff.Mode.MULTIPLY)); + messagesPlayAnimationImageView.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_chat_serviceText), PorterDuff.Mode.MULTIPLY)); } if (backgroundButtonsContainer != null) { for (int a = 0, N = backgroundButtonsContainer.getChildCount(); a < N; a++) { @@ -4030,7 +4474,9 @@ public MessagesAdapter(Context context) { messages.add(messageObject); message = new TLRPC.TL_message(); - if (currentWallpaper instanceof WallpapersListActivity.ColorWallpaper) { + if (dialogId != 0) { + message.message = LocaleController.getString("BackgroundColorSinglePreviewLine3", R.string.BackgroundColorSinglePreviewLine3); + } else if (currentWallpaper instanceof WallpapersListActivity.ColorWallpaper) { message.message = LocaleController.getString("BackgroundColorSinglePreviewLine1", R.string.BackgroundColorSinglePreviewLine1); } else { message.message = LocaleController.getString("BackgroundPreviewLine1", R.string.BackgroundPreviewLine1); @@ -4048,6 +4494,34 @@ public MessagesAdapter(Context context) { messageObject.eventId = 1; messageObject.resetLayout(); messages.add(messageObject); + + if (dialogId != 0) { + message = new TLRPC.TL_message(); + message.message = ""; + messageObject = new MessageObject(currentAccount, message, true, false); + messageObject.eventId = 1; + messageObject.contentType = 5; + messages.add(messageObject); + + message = new TLRPC.TL_message(); + TLRPC.User user = getMessagesController().getUser(dialogId); + String username = user == null ? "DELETED" : user.first_name; + message.message = LocaleController.formatString("ChatBackgroundHint", R.string.ChatBackgroundHint, username); + message.date = date + 60; + message.dialog_id = 1; + message.flags = 257 + 8; + message.from_id = new TLRPC.TL_peerUser(); + message.id = 1; + message.media = new TLRPC.TL_messageMediaEmpty(); + message.out = false; + message.peer_id = new TLRPC.TL_peerUser(); + message.peer_id.user_id = UserConfig.getInstance(currentAccount).getClientUserId(); + messageObject = new MessageObject(currentAccount, message, true, false); + messageObject.eventId = 1; + messageObject.resetLayout(); + messageObject.contentType = 1; + messages.add(messageObject); + } } else if (screenType == SCREEN_TYPE_ACCENT_COLOR) { message = new TLRPC.TL_message(); message.media = new TLRPC.TL_messageMediaDocument(); @@ -4365,7 +4839,7 @@ public MessagesAdapter(Context context) { private boolean hasButtons() { return messagesButtonsContainer != null && screenType == SCREEN_TYPE_ACCENT_COLOR && colorType == 3 && accent.myMessagesGradientAccentColor2 != 0 || - backgroundButtonsContainer != null && (screenType == SCREEN_TYPE_CHANGE_BACKGROUND || screenType == SCREEN_TYPE_ACCENT_COLOR && colorType == 2); + backgroundButtonsContainer != null && (screenType == SCREEN_TYPE_CHANGE_BACKGROUND || screenType == SCREEN_TYPE_ACCENT_COLOR && colorType == 2); } @Override @@ -4386,10 +4860,10 @@ public boolean isEnabled(RecyclerView.ViewHolder holder) { public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { View view; if (viewType == 0) { - view = new ChatMessageCell(mContext, false, new Theme.ResourcesProvider() { + view = new ChatMessageCell(mContext, false, null, new Theme.ResourcesProvider() { @Override - public Integer getColor(String key) { - return Theme.getColor(key); + public int getColor(int key) { + return getThemedColor(key); } @Override @@ -4406,8 +4880,20 @@ public Drawable getDrawable(String drawableKey) { if (drawableKey.equals(Theme.key_drawable_msgOutMediaSelected)) { return msgOutMediaDrawableSelected; } + if (getResourceProvider() != null) { + return getResourceProvider().getDrawable(drawableKey); + } return Theme.getThemeDrawable(drawableKey); } + + @Override + public void applyServiceShaderMatrix(int w, int h, float translationX, float translationY) { + if (getResourceProvider() != null) { + getResourceProvider().applyServiceShaderMatrix(w, h, translationX, translationY); + } else { + Theme.ResourcesProvider.super.applyServiceShaderMatrix(w, h, translationX, translationY); + } + } }); ChatMessageCell chatMessageCell = (ChatMessageCell) view; chatMessageCell.setDelegate(new ChatMessageCell.ChatMessageCellDelegate() { @@ -4430,6 +4916,13 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { }; frameLayout.addView(backgroundButtonsContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 76, Gravity.CENTER)); view = frameLayout; + } else if (viewType == 5) { + view = new View(getContext()) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(4), MeasureSpec.EXACTLY)); + } + }; } else { if (messagesButtonsContainer.getParent() != null) { ((ViewGroup) messagesButtonsContainer.getParent()).removeView(messagesButtonsContainer); @@ -4630,18 +5123,54 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } } - private List getThemeDescriptionsInternal() { + + public ArrayList getThemeDescriptionsInternal() { ThemeDescription.ThemeDescriptionDelegate descriptionDelegate = () -> { if (dropDownContainer != null) { - dropDownContainer.redrawPopup(Theme.getColor(Theme.key_actionBarDefaultSubmenuBackground)); - dropDownContainer.setPopupItemsColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuItem), false); + dropDownContainer.redrawPopup(getThemedColor(Theme.key_actionBarDefaultSubmenuBackground)); + dropDownContainer.setPopupItemsColor(getThemedColor(Theme.key_actionBarDefaultSubmenuItem), false); } if (sheetDrawable != null) { - sheetDrawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_windowBackgroundWhite), PorterDuff.Mode.SRC_IN)); + sheetDrawable.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_windowBackgroundWhite), PorterDuff.Mode.MULTIPLY)); + } + if (bottomOverlayChat != null) { + bottomOverlayChat.invalidate(); + } + if (brightnessControlCell != null) { + brightnessControlCell.invalidate(); + brightnessControlCell.seekBarView.invalidate(); + } + if (onSwitchDayNightDelegate != null) { + if (parentLayout != null && parentLayout.getBottomSheet() != null) { + parentLayout.getBottomSheet().fixNavigationBar(getThemedColor(Theme.key_dialogBackground)); + } else { + setNavigationBarColor(getThemedColor(Theme.key_dialogBackground)); + } + } + if (backgroundCheckBoxView != null) { + for (int i = 0; i < backgroundCheckBoxView.length; i++) { + if (backgroundCheckBoxView[i] != null) { + backgroundCheckBoxView[i].invalidate(); + } + } + } + + if (messagesCheckBoxView != null) { + for (int i = 0; i < messagesCheckBoxView.length; i++) { + if (messagesCheckBoxView[i] != null) { + messagesCheckBoxView[i].invalidate(); + } + } + } + if (patternTitleView != null) { + patternTitleView.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteBlackText)); + } + if (colorPicker != null) { + colorPicker.invalidate(); } }; - List items = new ArrayList<>(); + ArrayList items = new ArrayList<>(); items.add(new ThemeDescription(page1, ThemeDescription.FLAG_BACKGROUND, null, null, null, descriptionDelegate, Theme.key_windowBackgroundWhite)); items.add(new ThemeDescription(viewPager, ThemeDescription.FLAG_LISTGLOWCOLOR, null, null, null, null, Theme.key_actionBarDefault)); @@ -4733,7 +5262,183 @@ private List getThemeDescriptionsInternal() { items.add(new ThemeDescription(listView2, 0, new Class[]{ChatMessageCell.class}, null, null, null, Theme.key_chat_inTimeSelectedText)); items.add(new ThemeDescription(listView2, 0, new Class[]{ChatMessageCell.class}, null, null, null, Theme.key_chat_outTimeSelectedText)); } + items.add(new ThemeDescription(dimmingHeaderCell, 0, new Class[]{HeaderCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueHeader)); + items.add(new ThemeDescription(null, 0, null, null, null, null, descriptionDelegate, Theme.key_divider)); + items.add(new ThemeDescription(null, 0, null, null, null, null, descriptionDelegate, Theme.key_dialogBackground)); + items.add(new ThemeDescription(null, 0, null, null, null, null, descriptionDelegate, Theme.key_windowBackgroundWhiteBlackText)); + items.add(new ThemeDescription(null, 0, null, null, null, null, descriptionDelegate, Theme.key_dialogBackgroundGray)); + + + for (int i = 0; i < items.size(); i++) { + items.get(i).resourcesProvider = getResourceProvider(); + } return items; } + + @Override + public ArrayList getThemeDescriptions() { + if (shouldShowDayNightIcon) { + return getThemeDescriptionsInternal(); + } else { + return super.getThemeDescriptions(); + } + } + + private void createServiceMessageLocal(TLRPC.WallPaper wallPaper) { + + TLRPC.TL_messageService message = new TLRPC.TL_messageService(); + message.random_id = SendMessagesHelper.getInstance(currentAccount).getNextRandomId(); + message.dialog_id = dialogId; + message.unread = true; + message.out = true; + message.local_id = message.id = getUserConfig().getNewMessageId(); + message.from_id = new TLRPC.TL_peerUser(); + message.from_id.user_id = getUserConfig().getClientUserId(); + message.flags |= 256; + message.peer_id = new TLRPC.TL_peerUser(); + message.peer_id.user_id = dialogId; + message.date = getConnectionsManager().getCurrentTime(); + TLRPC.TL_messageActionSetChatWallPaper setChatWallPaper = new TLRPC.TL_messageActionSetChatWallPaper(); + message.action = setChatWallPaper; + setChatWallPaper.wallpaper = wallPaper; + + ArrayList objArr = new ArrayList<>(); + objArr.add(new MessageObject(currentAccount, message, false, false)); + ArrayList arr = new ArrayList<>(); + arr.add(message); + // MessagesStorage.getInstance(currentAccount).putMessages(arr, false, true, false, 0, false, 0); + MessagesController.getInstance(currentAccount).updateInterfaceWithMessages(dialogId, objArr, false); + } + + public interface DayNightSwitchDelegate { + boolean isDark(); + + void switchDayNight(); + } + + private class BackgroundView extends BackupImageView { + + private Drawable background; + boolean drawBackground = true; + + public BackgroundView(Context context) { + super(context); + } + + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + parallaxScale = parallaxEffect.getScale(getMeasuredWidth(), getMeasuredHeight()); + if (isMotion) { + setScaleX(parallaxScale); + setScaleY(parallaxScale); + } + progressVisible = screenType == SCREEN_TYPE_CHANGE_BACKGROUND && getMeasuredWidth() <= getMeasuredHeight(); + + int sizeHash = getMeasuredWidth() + (getMeasuredHeight() << 16); + if (lastSizeHash != sizeHash) { + hasScrollingBackground = false; + if (currentWallpaperBitmap != null) { + int scaledWidth = (int) (currentWallpaperBitmap.getWidth() * (getMeasuredHeight() / (float) currentWallpaperBitmap.getHeight())); + if (scaledWidth - getMeasuredWidth() > 100) { + hasScrollingBackground = true; + croppedWidth = (int) (getMeasuredWidth() * (currentWallpaperBitmap.getHeight() / (float) getMeasuredHeight())); + defaultScrollOffset = currentScrollOffset = (scaledWidth - getMeasuredWidth()) / 2f; + maxScrollOffset = currentScrollOffset * 2f; + setSize(scaledWidth, getMeasuredHeight()); + drawFromStart = true; + } + } + if (!hasScrollingBackground) { + setSize(-1, -1); + drawFromStart = false; + } + } + lastSizeHash = sizeHash; + } + + @Override + protected void onDraw(Canvas canvas) { + if (drawBackground) { + if (background instanceof ColorDrawable || background instanceof GradientDrawable || background instanceof MotionBackgroundDrawable) { + background.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight()); + background.draw(canvas); + } else if (background instanceof BitmapDrawable) { + BitmapDrawable bitmapDrawable = (BitmapDrawable) background; + if (bitmapDrawable.getTileModeX() == Shader.TileMode.REPEAT) { + canvas.save(); + float scale = 2.0f / AndroidUtilities.density; + canvas.scale(scale, scale); + background.setBounds(0, 0, (int) Math.ceil(getMeasuredWidth() / scale), (int) Math.ceil(getMeasuredHeight() / scale)); + background.draw(canvas); + canvas.restore(); + } else { + int viewHeight = getMeasuredHeight(); + float scaleX = (float) getMeasuredWidth() / (float) background.getIntrinsicWidth(); + float scaleY = (float) (viewHeight) / (float) background.getIntrinsicHeight(); + float scale = Math.max(scaleX, scaleY); + int width = (int) Math.ceil(background.getIntrinsicWidth() * scale * parallaxScale); + int height = (int) Math.ceil(background.getIntrinsicHeight() * scale * parallaxScale); + int x = (getMeasuredWidth() - width) / 2; + int y = (viewHeight - height) / 2; + background.setBounds(x, y, x + width, y + height); + background.draw(canvas); + } + } + } + if (hasScrollingBackground) { + if (!scroller.isFinished()) { + if (scroller.computeScrollOffset()) { + if (scroller.getStartX() < maxScrollOffset && scroller.getStartX() > 0) { + currentScrollOffset = scroller.getCurrX(); + } + invalidate(); + } + } + canvas.save(); + canvas.translate(-currentScrollOffset, 0); + super.onDraw(canvas); + canvas.restore(); + } else { + super.onDraw(canvas); + } + if (shouldShowBrightnessControll && dimAmount > 0 && onSwitchDayNightDelegate != null && onSwitchDayNightDelegate.isDark()) { + canvas.drawColor(ColorUtils.setAlphaComponent(Color.BLACK, (int) (255 * dimAmount * progressToDarkTheme))); + } + } + + @Override + public Drawable getBackground() { + return background; + } + + @Override + public void setBackground(Drawable drawable) { + background = drawable; + } + } + + private class MessageDrawable extends Theme.MessageDrawable { + public MessageDrawable(int typeMedia, boolean b, boolean b1) { + super(typeMedia, b, b1); + } + + @Override + public void setTop(int top, int backgroundWidth, int backgroundHeight, boolean topNear, boolean bottomNear) { + if (setupFinished) { + return; + } + super.setTop(top, backgroundWidth, backgroundHeight, topNear, bottomNear); + } + + @Override + public void setTop(int top, int backgroundWidth, int backgroundHeight, int heightOffset, int blurredViewTopOffset, int blurredViewBottomOffset, boolean topNear, boolean bottomNear) { + if (setupFinished) { + return; + } + super.setTop(top, backgroundWidth, backgroundHeight, heightOffset, blurredViewTopOffset, blurredViewBottomOffset, topNear, bottomNear); + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ThemeSetUrlActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ThemeSetUrlActivity.java index aae01f4e1b..baf14ece6f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ThemeSetUrlActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ThemeSetUrlActivity.java @@ -322,7 +322,7 @@ public void afterTextChanged(Editable editable) { } checkInfoCell = new TextInfoPrivacyCell(context); - checkInfoCell.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + checkInfoCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); checkInfoCell.setVisibility(View.GONE); checkInfoCell.setBottomPadding(0); linearLayout.addView(checkInfoCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); @@ -338,7 +338,7 @@ public void afterTextChanged(Editable editable) { linearLayout.addView(helpInfoCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); if (creatingNewTheme) { - helpInfoCell.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + helpInfoCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); messagesCell = new ThemePreviewMessagesCell(context, parentLayout, 1); linearLayout.addView(messagesCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); @@ -386,10 +386,10 @@ protected void updateRows() { createInfoCell = new TextInfoPrivacyCell(context); createInfoCell.setText(AndroidUtilities.replaceTags(LocaleController.getString("UseDifferentThemeInfo", R.string.UseDifferentThemeInfo))); - createInfoCell.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + createInfoCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); linearLayout.addView(createInfoCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); } else { - helpInfoCell.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + helpInfoCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } if (info != null) { @@ -458,7 +458,7 @@ private boolean checkUrl(final String url, boolean alert) { lastNameAvailable = false; if (url != null) { if (url.startsWith("_") || url.endsWith("_")) { - setCheckText(LocaleController.getString("SetUrlInvalid", R.string.SetUrlInvalid), Theme.key_windowBackgroundWhiteRedText4); + setCheckText(LocaleController.getString("SetUrlInvalid", R.string.SetUrlInvalid), Theme.key_text_RedRegular); return false; } for (int a = 0; a < url.length(); a++) { @@ -467,7 +467,7 @@ private boolean checkUrl(final String url, boolean alert) { if (alert) { AlertsCreator.showSimpleAlert(this, LocaleController.getString("Theme", R.string.Theme), LocaleController.getString("SetUrlInvalidStartNumber", R.string.SetUrlInvalidStartNumber)); } else { - setCheckText(LocaleController.getString("SetUrlInvalidStartNumber", R.string.SetUrlInvalidStartNumber), Theme.key_windowBackgroundWhiteRedText4); + setCheckText(LocaleController.getString("SetUrlInvalidStartNumber", R.string.SetUrlInvalidStartNumber), Theme.key_text_RedRegular); } return false; } @@ -475,7 +475,7 @@ private boolean checkUrl(final String url, boolean alert) { if (alert) { AlertsCreator.showSimpleAlert(this, LocaleController.getString("Theme", R.string.Theme), LocaleController.getString("SetUrlInvalid", R.string.SetUrlInvalid)); } else { - setCheckText(LocaleController.getString("SetUrlInvalid", R.string.SetUrlInvalid), Theme.key_windowBackgroundWhiteRedText4); + setCheckText(LocaleController.getString("SetUrlInvalid", R.string.SetUrlInvalid), Theme.key_text_RedRegular); } return false; } @@ -485,7 +485,7 @@ private boolean checkUrl(final String url, boolean alert) { if (alert) { AlertsCreator.showSimpleAlert(this, LocaleController.getString("Theme", R.string.Theme), LocaleController.getString("SetUrlInvalidShort", R.string.SetUrlInvalidShort)); } else { - setCheckText(LocaleController.getString("SetUrlInvalidShort", R.string.SetUrlInvalidShort), Theme.key_windowBackgroundWhiteRedText4); + setCheckText(LocaleController.getString("SetUrlInvalidShort", R.string.SetUrlInvalidShort), Theme.key_text_RedRegular); } return false; } @@ -493,7 +493,7 @@ private boolean checkUrl(final String url, boolean alert) { if (alert) { AlertsCreator.showSimpleAlert(this, LocaleController.getString("Theme", R.string.Theme), LocaleController.getString("SetUrlInvalidLong", R.string.SetUrlInvalidLong)); } else { - setCheckText(LocaleController.getString("SetUrlInvalidLong", R.string.SetUrlInvalidLong), Theme.key_windowBackgroundWhiteRedText4); + setCheckText(LocaleController.getString("SetUrlInvalidLong", R.string.SetUrlInvalidLong), Theme.key_text_RedRegular); } return false; } @@ -519,7 +519,7 @@ private boolean checkUrl(final String url, boolean alert) { setCheckText(LocaleController.formatString("SetUrlAvailable", R.string.SetUrlAvailable, url), Theme.key_windowBackgroundWhiteGreenText); lastNameAvailable = true; } else { - setCheckText(LocaleController.getString("SetUrlInUse", R.string.SetUrlInUse), Theme.key_windowBackgroundWhiteRedText4); + setCheckText(LocaleController.getString("SetUrlInUse", R.string.SetUrlInUse), Theme.key_text_RedRegular); lastNameAvailable = false; } } @@ -530,21 +530,21 @@ private boolean checkUrl(final String url, boolean alert) { return true; } - private void setCheckText(String text, String colorKey) { + private void setCheckText(String text, int colorKey) { if (TextUtils.isEmpty(text)) { checkInfoCell.setVisibility(View.GONE); if (creatingNewTheme) { - helpInfoCell.setBackgroundDrawable(Theme.getThemedDrawable(getParentActivity(), R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + helpInfoCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(getParentActivity(), R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); } else { - helpInfoCell.setBackgroundDrawable(Theme.getThemedDrawable(getParentActivity(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + helpInfoCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(getParentActivity(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } } else { checkInfoCell.setVisibility(View.VISIBLE); checkInfoCell.setText(text); checkInfoCell.setTag(colorKey); - checkInfoCell.setTextColor(colorKey); + checkInfoCell.setTextColorByKey(colorKey); if (creatingNewTheme) { - helpInfoCell.setBackgroundDrawable(Theme.getThemedDrawable(getParentActivity(), R.drawable.greydivider_top, Theme.key_windowBackgroundGrayShadow)); + helpInfoCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(getParentActivity(), R.drawable.greydivider_top, Theme.key_windowBackgroundGrayShadow)); } else { helpInfoCell.setBackgroundDrawable(null); } @@ -659,7 +659,7 @@ public ArrayList getThemeDescriptions() { themeDescriptions.add(new ThemeDescription(helpInfoCell, 0, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText4)); themeDescriptions.add(new ThemeDescription(checkInfoCell, ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{TextInfoPrivacyCell.class}, null, null, null, Theme.key_windowBackgroundGrayShadow)); - themeDescriptions.add(new ThemeDescription(checkInfoCell, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteRedText4)); + themeDescriptions.add(new ThemeDescription(checkInfoCell, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_text_RedRegular)); themeDescriptions.add(new ThemeDescription(checkInfoCell, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText8)); themeDescriptions.add(new ThemeDescription(checkInfoCell, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteGreenText)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/TooManyCommunitiesActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/TooManyCommunitiesActivity.java index 0914ffd5bf..a27d0cb854 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/TooManyCommunitiesActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/TooManyCommunitiesActivity.java @@ -248,7 +248,7 @@ protected void onDraw(Canvas canvas) { buttonTextView.setGravity(Gravity.CENTER); buttonTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); buttonTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - buttonTextView.setBackground(Theme.AdaptiveRipple.filledRect(Theme.key_featuredStickers_addButton, 4)); + buttonTextView.setBackground(Theme.AdaptiveRipple.filledRectByKey(Theme.key_featuredStickers_addButton, 4)); contentView.addView(buttonLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 64, Gravity.BOTTOM)); buttonLayout.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); buttonLayout.addView(buttonTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, 0, 16, 12, 16, 12)); @@ -464,7 +464,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int break; case 2: view = new ShadowSectionCell(parent.getContext()); - Drawable drawable = Theme.getThemedDrawable(parent.getContext(), R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow); + Drawable drawable = Theme.getThemedDrawableByKey(parent.getContext(), R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow); CombinedDrawable combinedDrawable = new CombinedDrawable(new ColorDrawable(Theme.getColor(Theme.key_windowBackgroundGray)), drawable); combinedDrawable.setFullsize(true); view.setBackground(combinedDrawable); @@ -670,7 +670,7 @@ public ArrayList getThemeDescriptions() { } } - buttonTextView.setBackground(Theme.AdaptiveRipple.filledRect(Theme.key_featuredStickers_addButton, 4)); + buttonTextView.setBackground(Theme.AdaptiveRipple.filledRectByKey(Theme.key_featuredStickers_addButton, 4)); progressBar.setProgressColor(Theme.getColor(Theme.key_progressCircle)); }; @@ -719,7 +719,7 @@ public ArrayList getThemeDescriptions() { themeDescriptions.add(new ThemeDescription(buttonTextView, 0, null, null, null, cellDelegate, Theme.key_featuredStickers_addButton)); themeDescriptions.add(new ThemeDescription(buttonTextView, 0, null, null, null, cellDelegate, Theme.key_featuredStickers_addButtonPressed)); themeDescriptions.add(new ThemeDescription(progressBar, 0, null, null, null, cellDelegate, Theme.key_featuredStickers_addButtonPressed)); - themeDescriptions.add(new ThemeDescription(hintCell, 0, new Class[]{TooManyCommunitiesHintCell.class}, new String[]{"imageLayout"}, null, null, null, Theme.key_dialogRedIcon)); + themeDescriptions.add(new ThemeDescription(hintCell, 0, new Class[]{TooManyCommunitiesHintCell.class}, new String[]{"imageLayout"}, null, null, null, Theme.key_text_RedRegular)); return themeDescriptions; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/TopicCreateFragment.java b/TMessagesProj/src/main/java/org/telegram/ui/TopicCreateFragment.java index 7b10ce8685..c267790ed8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/TopicCreateFragment.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/TopicCreateFragment.java @@ -24,6 +24,7 @@ import android.widget.LinearLayout; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MediaDataController; import org.telegram.messenger.MessageObject; @@ -485,7 +486,7 @@ protected void onEmojiSelected(View view, Long documentId, TLRPC.Document docume TextInfoPrivacyCell infoCell = new TextInfoPrivacyCell(context); infoCell.setText(LocaleController.getString("EditTopicHideInfo", R.string.EditTopicHideInfo)); - infoCell.setBackground(Theme.getThemedDrawable(getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow, getResourceProvider())); + infoCell.setBackground(Theme.getThemedDrawableByKey(getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow, getResourceProvider())); emojiContainer.addView(infoCell, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP, 0, 8 + 50, 0, 0)); } linearLayout.addView(emojiContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); @@ -549,13 +550,13 @@ private void selectEmoji(Long documentId, boolean free) { AndroidUtilities.updateViewVisibilityAnimated(backupImageView[1], false, 0.5f, true); } - int animationIndex = 0; + AnimationNotificationsLocker notificationsLocker = new AnimationNotificationsLocker(); @Override public void onTransitionAnimationStart(boolean isOpen, boolean backward) { super.onTransitionAnimationStart(isOpen, backward); if (isOpen) { - animationIndex = getNotificationCenter().setAnimationInProgress(animationIndex, null); + notificationsLocker.lock(); } } @@ -566,7 +567,7 @@ public void onTransitionAnimationEnd(boolean isOpen, boolean backward) { removeSelfFromStack(); } - getNotificationCenter().onAnimationFinish(animationIndex); + notificationsLocker.unlock(); if (selectAnimatedEmojiDialog != null) { selectAnimatedEmojiDialog.setAnimationsEnabled(fragmentBeginToShow); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/TopicsFragment.java b/TMessagesProj/src/main/java/org/telegram/ui/TopicsFragment.java index 2df3dfd45f..27f3fbfb5b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/TopicsFragment.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/TopicsFragment.java @@ -56,6 +56,7 @@ import org.telegram.messenger.AccountInstance; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.BuildVars; import org.telegram.messenger.ChatObject; import org.telegram.messenger.DialogObject; @@ -253,8 +254,9 @@ public class TopicsFragment extends BaseFragment implements NotificationCenter.N private boolean updateAnimated; - private int transitionAnimationIndex; - private int transitionAnimationGlobalIndex; + private final AnimationNotificationsLocker notificationsLocker = new AnimationNotificationsLocker(new int[]{ + NotificationCenter.topicsDidLoaded + }); private View blurredView; private int selectedTopicForTablet; @@ -471,10 +473,13 @@ protected void dispatchDraw(Canvas canvas) { actionBarPaint.setColor(getThemedColor(Theme.key_windowBackgroundWhite)); actionBarPaint.setAlpha((int) (255 * searchAnimationProgress)); canvas.drawRect(0, 0, getWidth(), AndroidUtilities.statusBarHeight, actionBarPaint); + canvas.drawLine(0, 0, 0, getHeight(), Theme.dividerPaint); } } }; + + contentView.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); contentView.needBlur = !inPreviewMode; actionBar.setAddToContainer(false); @@ -726,8 +731,8 @@ public boolean emptyViewIsVisible() { generalIconDrawable.setBounds(0, AndroidUtilities.dp(2), AndroidUtilities.dp(16), AndroidUtilities.dp(18)); generalIcon.setSpan(new ImageSpan(generalIconDrawable, DynamicDrawableSpan.ALIGN_CENTER), 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); pullForegroundDrawable = new PullForegroundDrawable( - AndroidUtilities.replaceCharSequence("#", LocaleController.getString("AccSwipeForGeneral", R.string.AccSwipeForGeneral), generalIcon), - AndroidUtilities.replaceCharSequence("#", LocaleController.getString("AccReleaseForGeneral", R.string.AccReleaseForGeneral), generalIcon) + AndroidUtilities.replaceCharSequence("#", LocaleController.getString("AccSwipeForGeneral", R.string.AccSwipeForGeneral), generalIcon), + AndroidUtilities.replaceCharSequence("#", LocaleController.getString("AccReleaseForGeneral", R.string.AccReleaseForGeneral), generalIcon) ) { @Override protected float getViewOffset() { @@ -1766,7 +1771,7 @@ public void onClick(DialogInterface dialog, int which) { alertDialog.show(); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } @@ -1898,8 +1903,8 @@ public void toggleMute() { if (ChatObject.canDeleteTopic(currentAccount, getCurrentChat(), topic)) { ActionBarMenuSubItem deleteItem = new ActionBarMenuSubItem(getParentActivity(), false, true); deleteItem.setTextAndIcon(LocaleController.getPluralString("DeleteTopics", 1), R.drawable.msg_delete); - deleteItem.setIconColor(getThemedColor(Theme.key_dialogRedIcon)); - deleteItem.setTextColor(getThemedColor(Theme.key_dialogTextRed)); + deleteItem.setIconColor(getThemedColor(Theme.key_text_RedRegular)); + deleteItem.setTextColor(getThemedColor(Theme.key_text_RedBold)); deleteItem.setMinimumWidth(160); deleteItem.setOnClickListener(e -> { HashSet hashSet = new HashSet(); @@ -2455,7 +2460,7 @@ public void onAnimationEnd(Animator animation) { private void setButtonType(int bottomButtonType) { if (this.bottomButtonType != bottomButtonType) { this.bottomButtonType = bottomButtonType; - bottomOverlayChatText.setTextColorKey(bottomButtonType == BOTTOM_BUTTON_TYPE_JOIN ? Theme.key_chat_fieldOverlayText : Theme.key_chat_reportSpam); + bottomOverlayChatText.setTextColorKey(bottomButtonType == BOTTOM_BUTTON_TYPE_JOIN ? Theme.key_chat_fieldOverlayText : Theme.key_text_RedBold); closeReportSpam.setVisibility(bottomButtonType == BOTTOM_BUTTON_TYPE_REPORT ? View.VISIBLE : View.GONE); updateChatInfo(); } @@ -2518,8 +2523,7 @@ public boolean onFragmentCreate() { @Override public void onFragmentDestroy() { - getNotificationCenter().onAnimationFinish(transitionAnimationIndex); - NotificationCenter.getGlobalInstance().onAnimationFinish(transitionAnimationGlobalIndex); + notificationsLocker.unlock(); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.chatInfoDidLoad); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.topicsDidLoaded); @@ -3320,7 +3324,7 @@ public String getItemTitle(int position) { } else if (items.get(position).type == DOWNLOADS_TYPE) { return LocaleController.getString("DownloadsTabs", R.string.DownloadsTabs); } else { - return FiltersView.filters[items.get(position).filterIndex].title; + return FiltersView.filters[items.get(position).filterIndex].getTitle(); } } @@ -3767,8 +3771,7 @@ private void setSlideTransitionProgress(float progress) { public void onTransitionAnimationStart(boolean isOpen, boolean backward) { super.onTransitionAnimationStart(isOpen, backward); - transitionAnimationIndex = getNotificationCenter().setAnimationInProgress(transitionAnimationIndex, new int[]{NotificationCenter.topicsDidLoaded}); - transitionAnimationGlobalIndex = NotificationCenter.getGlobalInstance().setAnimationInProgress(transitionAnimationGlobalIndex, new int[0]); + notificationsLocker.lock(); } @Override @@ -3781,8 +3784,7 @@ public void onTransitionAnimationEnd(boolean isOpen, boolean backward) { blurredView.setBackground(null); } - getNotificationCenter().onAnimationFinish(transitionAnimationIndex); - NotificationCenter.getGlobalInstance().onAnimationFinish(transitionAnimationGlobalIndex); + notificationsLocker.unlock(); if (!isOpen && (opnendForSelect && removeFragmentOnTransitionEnd)) { removeSelfFromStack(); @@ -3948,6 +3950,7 @@ public ArrayList getThemeDescriptions() { ArrayList arrayList = new ArrayList<>(); + arrayList.add(new ThemeDescription(fragmentView, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_windowBackgroundWhite)); arrayList.add(new ThemeDescription(null, 0, null, null, null, cellDelegate, Theme.key_windowBackgroundWhite)); arrayList.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_actionBarDefault)); arrayList.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_ITEMSCOLOR, null, null, null, null, Theme.key_actionBarDefaultIcon)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/TopicsNotifySettingsFragments.java b/TMessagesProj/src/main/java/org/telegram/ui/TopicsNotifySettingsFragments.java index 98040c224f..08a3b8d3e5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/TopicsNotifySettingsFragments.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/TopicsNotifySettingsFragments.java @@ -142,7 +142,7 @@ public void didRemoveException(long dialog_id) { showDialog(alertDialog); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } } @@ -229,7 +229,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int case VIEW_TYPE_DELETE_ALL: textCell = new TextCell(parent.getContext()); textCell.setText(LocaleController.getString("NotificationsDeleteAllException", R.string.NotificationsDeleteAllException), false); - textCell.setColors(null, Theme.key_windowBackgroundWhiteRedText5); + textCell.setColors(-1, Theme.key_text_RedRegular); view = textCell; view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); break; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/TwoStepVerificationActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/TwoStepVerificationActivity.java index 709c9487bc..5cdf2e3349 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/TwoStepVerificationActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/TwoStepVerificationActivity.java @@ -417,7 +417,7 @@ public void getOutline(View view, Outline outline) { showDialog(alertDialog); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } }); @@ -629,7 +629,7 @@ protected void onReset() { showDialog(dialog); TextView button = (TextView) dialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } else { cancelPasswordReset(); @@ -1215,10 +1215,10 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { TextInfoPrivacyCell privacyCell = (TextInfoPrivacyCell) holder.itemView; if (position == setPasswordDetailRow) { privacyCell.setText(LocaleController.getString("SetAdditionalPasswordInfo", R.string.SetAdditionalPasswordInfo)); - privacyCell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + privacyCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } else if (position == passwordEnabledDetailRow) { privacyCell.setText(LocaleController.getString("EnabledPasswordText", R.string.EnabledPasswordText)); - privacyCell.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + privacyCell.setBackgroundDrawable(Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } break; } @@ -1254,7 +1254,7 @@ public ArrayList getThemeDescriptions() { themeDescriptions.add(new ThemeDescription(emptyView, ThemeDescription.FLAG_PROGRESSBAR, null, null, null, null, Theme.key_progressCircle)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); - themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteRedText3)); + themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_text_RedRegular)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_TEXTCOLOR, new Class[]{EditTextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_HINTTEXTCOLOR, new Class[]{EditTextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteHintText)); @@ -1290,7 +1290,7 @@ private void showSetForcePasswordAlert() { builder.setNegativeButton(LocaleController.getString("ForceSetPasswordCancel", R.string.ForceSetPasswordCancel), (a1, a2) -> finishFragment()); AlertDialog alertDialog = builder.show(); - ((TextView)alertDialog.getButton(DialogInterface.BUTTON_NEGATIVE)).setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + ((TextView)alertDialog.getButton(DialogInterface.BUTTON_NEGATIVE)).setTextColor(Theme.getColor(Theme.key_text_RedBold)); } public void setBlockingAlert(int otherwiseRelogin) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/TwoStepVerificationSetupActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/TwoStepVerificationSetupActivity.java index f517fbcf09..372e437d59 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/TwoStepVerificationSetupActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/TwoStepVerificationSetupActivity.java @@ -285,7 +285,7 @@ public void onItemClick(int id) { showDialog(alertDialog); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } } @@ -398,7 +398,7 @@ public void getOutline(View view, Outline outline) { showDialog(alertDialog); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } else if (currentType == TYPE_ENTER_HINT) { onHintDone(); @@ -451,7 +451,7 @@ public void getOutline(View view, Outline outline) { buttonTextView.setTextColor(Theme.getColor(Theme.key_featuredStickers_buttonText)); buttonTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); buttonTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - buttonTextView.setBackground(Theme.AdaptiveRipple.filledRect(Theme.key_featuredStickers_addButton, 6)); + buttonTextView.setBackground(Theme.AdaptiveRipple.filledRectByKey(Theme.key_featuredStickers_addButton, 6)); buttonTextView.setOnClickListener(v -> processNext()); switch (currentType) { @@ -1403,7 +1403,7 @@ private void processNext() { needHideProgress(); if ("PASSWORD_HASH_INVALID".equals(error.text)) { descriptionText.setText(LocaleController.getString("CheckPasswordWrong", R.string.CheckPasswordWrong)); - descriptionText.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText4)); + descriptionText.setTextColor(Theme.getColor(Theme.key_text_RedRegular)); onFieldError(outlineTextFirstRow, editTextFirstRow, true); showDoneButton(false); } else if (error.text.startsWith("FLOOD_WAIT")) { @@ -2174,7 +2174,7 @@ private void showSetForcePasswordAlert() { builder.setNegativeButton(LocaleController.getString("ForceSetPasswordCancel", R.string.ForceSetPasswordCancel), (a1, a2) -> finishFragment()); AlertDialog alertDialog = builder.show(); - ((TextView)alertDialog.getButton(DialogInterface.BUTTON_NEGATIVE)).setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + ((TextView)alertDialog.getButton(DialogInterface.BUTTON_NEGATIVE)).setTextColor(Theme.getColor(Theme.key_text_RedBold)); } public void setBlockingAlert(int otherwiseRelogin) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/UsersSelectActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/UsersSelectActivity.java index bfe2233073..b32c8b8d62 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/UsersSelectActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/UsersSelectActivity.java @@ -104,6 +104,7 @@ public class UsersSelectActivity extends BaseFragment implements NotificationCen AnimatedAvatarContainer animatedAvatarContainer; + public boolean noChatTypes; private boolean isInclude; private int filterFlags; private ArrayList initialIds; @@ -280,7 +281,7 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto public void addSpan(final GroupCreateSpan span, boolean animated) { allSpans.add(span); long uid = span.getUid(); - if (uid > Integer.MIN_VALUE + 7) { + if (uid > Long.MIN_VALUE + 7) { selectedCount++; } selectedContacts.put(uid, span); @@ -315,7 +316,7 @@ public void onAnimationEnd(Animator animator) { public void removeSpan(final GroupCreateSpan span) { ignoreScrollEvent = true; long uid = span.getUid(); - if (uid > Integer.MIN_VALUE + 7) { + if (uid > Long.MIN_VALUE + 7) { selectedCount--; } selectedContacts.remove(uid); @@ -388,21 +389,21 @@ public void onClick(View v) { if (span.isDeleting()) { currentDeletingSpan = null; spansContainer.removeSpan(span); - if (span.getUid() == Integer.MIN_VALUE) { + if (span.getUid() == Long.MIN_VALUE) { filterFlags &=~ MessagesController.DIALOG_FILTER_FLAG_CONTACTS; - } else if (span.getUid() == Integer.MIN_VALUE + 1) { + } else if (span.getUid() == Long.MIN_VALUE + 1) { filterFlags &=~ MessagesController.DIALOG_FILTER_FLAG_NON_CONTACTS; - } else if (span.getUid() == Integer.MIN_VALUE + 2) { + } else if (span.getUid() == Long.MIN_VALUE + 2) { filterFlags &=~ MessagesController.DIALOG_FILTER_FLAG_GROUPS; - } else if (span.getUid() == Integer.MIN_VALUE + 3) { + } else if (span.getUid() == Long.MIN_VALUE + 3) { filterFlags &=~ MessagesController.DIALOG_FILTER_FLAG_CHANNELS; - } else if (span.getUid() == Integer.MIN_VALUE + 4) { + } else if (span.getUid() == Long.MIN_VALUE + 4) { filterFlags &=~ MessagesController.DIALOG_FILTER_FLAG_BOTS; - } else if (span.getUid() == Integer.MIN_VALUE + 5) { + } else if (span.getUid() == Long.MIN_VALUE + 5) { filterFlags &=~ MessagesController.DIALOG_FILTER_FLAG_EXCLUDE_MUTED; - } else if (span.getUid() == Integer.MIN_VALUE + 6) { + } else if (span.getUid() == Long.MIN_VALUE + 6) { filterFlags &=~ MessagesController.DIALOG_FILTER_FLAG_EXCLUDE_READ; - } else if (span.getUid() == Integer.MIN_VALUE + 7) { + } else if (span.getUid() == Long.MIN_VALUE + 7) { filterFlags &=~ MessagesController.DIALOG_FILTER_FLAG_EXCLUDE_ARCHIVED; } updateHint(); @@ -597,21 +598,21 @@ public boolean onKey(View v, int keyCode, KeyEvent event) { } else if (event.getAction() == KeyEvent.ACTION_UP && wasEmpty && !allSpans.isEmpty()) { GroupCreateSpan span = allSpans.get(allSpans.size() - 1); spansContainer.removeSpan(span); - if (span.getUid() == Integer.MIN_VALUE) { + if (span.getUid() == Long.MIN_VALUE) { filterFlags &=~ MessagesController.DIALOG_FILTER_FLAG_CONTACTS; - } else if (span.getUid() == Integer.MIN_VALUE + 1) { + } else if (span.getUid() == Long.MIN_VALUE + 1) { filterFlags &=~ MessagesController.DIALOG_FILTER_FLAG_NON_CONTACTS; - } else if (span.getUid() == Integer.MIN_VALUE + 2) { + } else if (span.getUid() == Long.MIN_VALUE + 2) { filterFlags &=~ MessagesController.DIALOG_FILTER_FLAG_GROUPS; - } else if (span.getUid() == Integer.MIN_VALUE + 3) { + } else if (span.getUid() == Long.MIN_VALUE + 3) { filterFlags &=~ MessagesController.DIALOG_FILTER_FLAG_CHANNELS; - } else if (span.getUid() == Integer.MIN_VALUE + 4) { + } else if (span.getUid() == Long.MIN_VALUE + 4) { filterFlags &=~ MessagesController.DIALOG_FILTER_FLAG_BOTS; - } else if (span.getUid() == Integer.MIN_VALUE + 5) { + } else if (span.getUid() == Long.MIN_VALUE + 5) { filterFlags &=~ MessagesController.DIALOG_FILTER_FLAG_EXCLUDE_MUTED; - } else if (span.getUid() == Integer.MIN_VALUE + 6) { + } else if (span.getUid() == Long.MIN_VALUE + 6) { filterFlags &=~ MessagesController.DIALOG_FILTER_FLAG_EXCLUDE_READ; - } else if (span.getUid() == Integer.MIN_VALUE + 7) { + } else if (span.getUid() == Long.MIN_VALUE + 7) { filterFlags &=~ MessagesController.DIALOG_FILTER_FLAG_EXCLUDE_ARCHIVED; } updateHint(); @@ -683,30 +684,30 @@ public void afterTextChanged(Editable editable) { if (isInclude) { if (position == 1) { flag = MessagesController.DIALOG_FILTER_FLAG_CONTACTS; - id = Integer.MIN_VALUE; + id = Long.MIN_VALUE; } else if (position == 2) { flag = MessagesController.DIALOG_FILTER_FLAG_NON_CONTACTS; - id = Integer.MIN_VALUE + 1; + id = Long.MIN_VALUE + 1; } else if (position == 3) { flag = MessagesController.DIALOG_FILTER_FLAG_GROUPS; - id = Integer.MIN_VALUE + 2; + id = Long.MIN_VALUE + 2; } else if (position == 4) { flag = MessagesController.DIALOG_FILTER_FLAG_CHANNELS; - id = Integer.MIN_VALUE + 3; + id = Long.MIN_VALUE + 3; } else { flag = MessagesController.DIALOG_FILTER_FLAG_BOTS; - id = Integer.MIN_VALUE + 4; + id = Long.MIN_VALUE + 4; } } else { if (position == 1) { flag = MessagesController.DIALOG_FILTER_FLAG_EXCLUDE_MUTED; - id = Integer.MIN_VALUE + 5; + id = Long.MIN_VALUE + 5; } else if (position == 2) { flag = MessagesController.DIALOG_FILTER_FLAG_EXCLUDE_READ; - id = Integer.MIN_VALUE + 6; + id = Long.MIN_VALUE + 6; } else { flag = MessagesController.DIALOG_FILTER_FLAG_EXCLUDE_ARCHIVED; - id = Integer.MIN_VALUE + 7; + id = Long.MIN_VALUE + 7; } } if (cell.isChecked()) { @@ -926,29 +927,29 @@ private void checkVisibleRows() { String str = (String) object; switch (str) { case "contacts": - id = Integer.MIN_VALUE; + id = Long.MIN_VALUE; break; case "non_contacts": - id = Integer.MIN_VALUE + 1; + id = Long.MIN_VALUE + 1; break; case "groups": - id = Integer.MIN_VALUE + 2; + id = Long.MIN_VALUE + 2; break; case "channels": - id = Integer.MIN_VALUE + 3; + id = Long.MIN_VALUE + 3; break; case "bots": - id = Integer.MIN_VALUE + 4; + id = Long.MIN_VALUE + 4; break; case "muted": - id = Integer.MIN_VALUE + 5; + id = Long.MIN_VALUE + 5; break; case "read": - id = Integer.MIN_VALUE + 6; + id = Long.MIN_VALUE + 6; break; case "archived": default: - id = Integer.MIN_VALUE + 7; + id = Long.MIN_VALUE + 7; break; } } else if (object instanceof TLRPC.User) { @@ -973,7 +974,7 @@ private boolean onDonePressed(boolean alert) { ArrayList result = new ArrayList<>(); for (int a = 0; a < selectedContacts.size(); a++) { long uid = selectedContacts.keyAt(a); - if (uid <= Integer.MIN_VALUE + 7) { + if (uid <= Long.MIN_VALUE + 7) { continue; } result.add(selectedContacts.keyAt(a)); @@ -1038,7 +1039,7 @@ public class GroupCreateAdapter extends RecyclerListView.FastScrollAdapter { private Runnable searchRunnable; private boolean searching; private ArrayList contacts = new ArrayList<>(); - private final int usersStartRow = type == TYPE_FILTER ? isInclude ? 7 : 5 : 0; + private final int usersStartRow = type == TYPE_FILTER && !noChatTypes ? isInclude ? 7 : 5 : 0; public GroupCreateAdapter(Context ctx) { context = ctx; @@ -1157,7 +1158,9 @@ public int getItemCount() { return count; } else { if (type == TYPE_FILTER) { - if (isInclude) { + if (noChatTypes) { + count = 0; + } else if (isInclude) { count = 7; } else { count = 5; @@ -1358,7 +1361,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } case 2: { GraySectionCell cell = (GraySectionCell) holder.itemView; - if (position == 0) { + if (position == 0 && !noChatTypes) { cell.setText(LocaleController.getString("FilterChatTypes", R.string.FilterChatTypes)); } else { cell.setText(LocaleController.getString("FilterChats", R.string.FilterChats)); @@ -1374,7 +1377,11 @@ public int getItemViewType(int position) { return 1; } else { if (type == TYPE_FILTER) { - if (isInclude) { + if (noChatTypes) { + if (position == 0) { + return 2; + } + } else if (isInclude) { if (position == 0 || position == 6) { return 2; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/VoIPFragment.java b/TMessagesProj/src/main/java/org/telegram/ui/VoIPFragment.java index 0af9af66d1..6bc74b0d97 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/VoIPFragment.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/VoIPFragment.java @@ -18,6 +18,8 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.RectF; import android.graphics.drawable.GradientDrawable; import android.os.Build; import android.os.PowerManager; @@ -51,6 +53,7 @@ import androidx.core.view.ViewCompat; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.ContactsController; import org.telegram.messenger.Emoji; import org.telegram.messenger.FileLog; @@ -182,7 +185,7 @@ public class VoIPFragment implements VoIPService.StateListener, NotificationCent private boolean deviceIsLocked; long lastContentTapTime; - int animationIndex = -1; + AnimationNotificationsLocker notificationsLocker = new AnimationNotificationsLocker(); VoIPNotificationsLayout notificationsLayout; HintView tapToVideoTooltip; @@ -259,6 +262,10 @@ public static void show(Activity activity, boolean overlay, int account) { fragment.activity = activity; instance = fragment; VoIPWindowView windowView = new VoIPWindowView(activity, !transitionFromPip) { + + private Path clipPath = new Path(); + private RectF rectF = new RectF(); + @Override public boolean dispatchKeyEvent(KeyEvent event) { if (fragment.isFinished || fragment.switchingToPip) { @@ -280,6 +287,29 @@ public boolean dispatchKeyEvent(KeyEvent event) { } return super.dispatchKeyEvent(event); } + + @Override + protected void dispatchDraw(Canvas canvas) { + if (fragment.switchingToPip && getAlpha() != 0) { + float width = fragment.callingUserTextureView.getWidth() * fragment.callingUserTextureView.getScaleX(); + float height = fragment.callingUserTextureView.getHeight() * fragment.callingUserTextureView.getScaleY(); + float padX = (fragment.callingUserTextureView.getWidth() - width) / 2; + float padY = (fragment.callingUserTextureView.getHeight() - height) / 2; + float x = fragment.callingUserTextureView.getX() + padX; + float y = fragment.callingUserTextureView.getY() + padY; + canvas.save(); + clipPath.rewind(); + rectF.set(x, y, x + width, y + height); + float round = AndroidUtilities.dp(4); + clipPath.addRoundRect(rectF, round, round, Path.Direction.CW); + clipPath.close(); + canvas.clipPath(clipPath); + super.dispatchDraw(canvas); + canvas.restore(); + } else { + super.dispatchDraw(canvas); + } + } }; instance.deviceIsLocked = ((KeyguardManager) activity.getSystemService(Context.KEYGUARD_SERVICE)).inKeyguardRestrictedInputMode(); @@ -1106,13 +1136,13 @@ public void switchToPip() { VoIPPiPView.switchingToPip = true; switchingToPip = true; Animator animator = createPiPTransition(false); - animationIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(animationIndex, null); + notificationsLocker.lock(); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { VoIPPiPView.getInstance().windowView.setAlpha(1f); AndroidUtilities.runOnUIThread(() -> { - NotificationCenter.getInstance(currentAccount).onAnimationFinish(animationIndex); + notificationsLocker.unlock(); VoIPPiPView.getInstance().onTransitionEnd(); currentUserCameraFloatingLayout.setCornerRadius(-1f); callingUserTextureView.renderer.release(); @@ -1143,11 +1173,11 @@ public void startTransitionFromPiP() { switchingToPip = true; VoIPPiPView.switchingToPip = true; VoIPPiPView.prepareForTransition(); - animationIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(animationIndex, null); + notificationsLocker.lock(); AndroidUtilities.runOnUIThread(() -> { windowView.setAlpha(1f); + windowView.invalidate(); Animator animator = createPiPTransition(true); - backIcon.setAlpha(0f); emojiLayout.setAlpha(0f); statusLayout.setAlpha(0f); @@ -1176,7 +1206,7 @@ public void startTransitionFromPiP() { animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - NotificationCenter.getInstance(currentAccount).onAnimationFinish(animationIndex); + notificationsLocker.unlock(); currentUserCameraFloatingLayout.setCornerRadius(-1f); switchingToPip = false; currentUserCameraFloatingLayout.switchingToPip = false; @@ -1296,7 +1326,7 @@ public Animator createPiPTransition(boolean enter) { if (!currentUserCameraFloatingLayout.measuredAsFloatingMode) { currentUserTextureView.setScreenshareMiniProgress(v, false); } - + windowView.invalidate(); callingUserPhotoView.setScaleX(callingUserScale); callingUserPhotoView.setScaleY(callingUserScale); callingUserPhotoView.setTranslationX(tx); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/VoiceMessageEnterTransition.java b/TMessagesProj/src/main/java/org/telegram/ui/VoiceMessageEnterTransition.java index b4d7208971..7b17cdf7c5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/VoiceMessageEnterTransition.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/VoiceMessageEnterTransition.java @@ -173,8 +173,7 @@ public void onDraw(Canvas canvas) { } } - private int getThemedColor(String key) { - Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null; - return color != null ? color : Theme.getColor(key); + private int getThemedColor(int key) { + return Theme.getColor(key, resourcesProvider); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/WallpapersListActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/WallpapersListActivity.java index 0317bb1b41..4f63455bd6 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/WallpapersListActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/WallpapersListActivity.java @@ -98,6 +98,7 @@ public class WallpapersListActivity extends BaseFragment implements Notification private int resetInfoRow; private int currentType; + private final long dialogId; private Paint colorPaint; private Paint colorFramePaint; @@ -406,8 +407,13 @@ public FileWallpaper(String s, int r, int t) { } public WallpapersListActivity(int type) { + this(type, 0); + } + + public WallpapersListActivity(int type, long dialogId) { super(); currentType = type; + this.dialogId = dialogId; } @Override @@ -418,15 +424,7 @@ public boolean onFragmentCreate() { NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.wallpapersNeedReload); getMessagesStorage().getWallpapers(); } else { - boolean darkTheme = Theme.isCurrentThemeDark(); - int[][] defaultColors = darkTheme ? defaultColorsDark : defaultColorsLight; - for (int a = 0; a < defaultColors.length; a++) { - if (defaultColors[a].length == 1) { - wallPapers.add(new ColorWallpaper(Theme.COLOR_BACKGROUND_SLUG, defaultColors[a][0], 0, 45)); - } else { - wallPapers.add(new ColorWallpaper(Theme.COLOR_BACKGROUND_SLUG, defaultColors[a][0], defaultColors[a][1], defaultColors[a][2], defaultColors[a][3])); - } - } + fillDefaultColors(wallPapers, Theme.isCurrentThemeDark()); if (currentType == TYPE_COLOR && patterns.isEmpty()) { NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.wallpapersDidLoad); getMessagesStorage().getWallpapers(); @@ -435,6 +433,18 @@ public boolean onFragmentCreate() { return super.onFragmentCreate(); } + public static void fillDefaultColors(ArrayList wallPapers, boolean isDark) { + boolean darkTheme = isDark; + int[][] defaultColors = darkTheme ? defaultColorsDark : defaultColorsLight; + for (int a = 0; a < defaultColors.length; a++) { + if (defaultColors[a].length == 1) { + wallPapers.add(new ColorWallpaper(Theme.COLOR_BACKGROUND_SLUG, defaultColors[a][0], 0, 45)); + } else { + wallPapers.add(new ColorWallpaper(Theme.COLOR_BACKGROUND_SLUG, defaultColors[a][0], defaultColors[a][1], defaultColors[a][2], defaultColors[a][3])); + } + } + } + @Override public void onFragmentDestroy() { if (currentType == TYPE_ALL) { @@ -460,7 +470,12 @@ public View createView(Context context) { updater = new WallpaperUpdater(getParentActivity(), this, new WallpaperUpdater.WallpaperUpdaterDelegate() { @Override public void didSelectWallpaper(File file, Bitmap bitmap, boolean gallery) { - presentFragment(new ThemePreviewActivity(new FileWallpaper("", file, file), bitmap), gallery); + ThemePreviewActivity themePreviewActivity = new ThemePreviewActivity(new FileWallpaper("", file, file), bitmap); + themePreviewActivity.setDialogId(dialogId); + if (dialogId != 0) { + themePreviewActivity.setDelegate(WallpapersListActivity.this::removeSelfFromStack); + } + presentFragment(themePreviewActivity, gallery); } @Override @@ -537,7 +552,7 @@ public void onItemClick(int id) { if (wallPaper.slug != null && wallPaper.slug.equals(selectedBackgroundSlug)) { selectedBackgroundSlug = Theme.hasWallpaperFromTheme() ? Theme.THEME_BACKGROUND_SLUG : Theme.DEFAULT_BACKGROUND_SLUG; Theme.getActiveTheme().setOverrideWallpaper(null); - Theme.reloadWallpaper(); + Theme.reloadWallpaper(true); } ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { @@ -559,7 +574,7 @@ public void onItemClick(int id) { showDialog(alertDialog); TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } else if (id == forward) { Bundle args = new Bundle(); @@ -594,10 +609,10 @@ public void onItemClick(int id) { for (int a = 0; a < dids.size(); a++) { long did = dids.get(a).dialogId; if (message != null) { - SendMessagesHelper.getInstance(currentAccount).sendMessage(message.toString(), did, null, null, null, true, null, null, null, true, 0, null, false); + SendMessagesHelper.getInstance(currentAccount).sendMessage(SendMessagesHelper.SendMessageParams.of(message.toString(), did, null, null, null, true, null, null, null, true, 0, null, false)); } if (!TextUtils.isEmpty(fmessage)) { - SendMessagesHelper.getInstance(currentAccount).sendMessage(fmessage.toString(), did, null, null, null, true, null, null, null, true, 0, null, false); + SendMessagesHelper.getInstance(currentAccount).sendMessage(SendMessagesHelper.SendMessageParams.of(fmessage.toString(), did, null, null, null, true, null, null, null, true, 0, null, false)); } } fragment1.finishFragment(); @@ -621,7 +636,7 @@ public void onItemClick(int id) { ChatActivity chatActivity = new ChatActivity(args1); presentFragment(chatActivity, true); - SendMessagesHelper.getInstance(currentAccount).sendMessage(fmessage.toString(), did, null, null, null, true, null, null, null, true, 0, null, false); + SendMessagesHelper.getInstance(currentAccount).sendMessage(SendMessagesHelper.SendMessageParams.of(fmessage.toString(), did, null, null, null, true, null, null, null, true, 0, null, false)); } return true; }); @@ -765,7 +780,7 @@ public boolean supportsPredictiveItemAnimations() { showDialog(dialog); TextView button = (TextView) dialog.getButton(DialogInterface.BUTTON_POSITIVE); if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed)); + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); } } }); @@ -812,27 +827,50 @@ public void onResume() { super.onResume(); SharedPreferences preferences = MessagesController.getGlobalMainSettings(); Theme.ThemeInfo themeInfo = Theme.getActiveTheme(); - Theme.OverrideWallpaperInfo overrideWallpaper = themeInfo.overrideWallpaper; - if (overrideWallpaper != null) { - selectedBackgroundSlug = overrideWallpaper.slug; - selectedColor = overrideWallpaper.color; - selectedGradientColor1 = overrideWallpaper.gradientColor1; - selectedGradientColor2 = overrideWallpaper.gradientColor2; - selectedGradientColor3 = overrideWallpaper.gradientColor3; - selectedGradientRotation = overrideWallpaper.rotation; - selectedIntensity = overrideWallpaper.intensity; - selectedBackgroundMotion = overrideWallpaper.isMotion; - selectedBackgroundBlurred = overrideWallpaper.isBlurred; + if (dialogId != 0) { + TLRPC.UserFull userFull = getMessagesController().getUserFull(dialogId); + if (userFull != null && userFull.wallpaper != null) { + selectedBackgroundSlug = userFull.wallpaper.slug; + if (selectedBackgroundSlug == null) { + selectedBackgroundSlug = ""; + } + if (userFull.wallpaper.settings != null) { + selectedColor = userFull.wallpaper.settings.background_color; + selectedGradientColor1 = userFull.wallpaper.settings.second_background_color; + selectedGradientColor2 = userFull.wallpaper.settings.third_background_color; + selectedGradientColor3 = userFull.wallpaper.settings.fourth_background_color; + selectedGradientRotation = userFull.wallpaper.settings.rotation; + selectedIntensity = userFull.wallpaper.settings.intensity; + selectedBackgroundMotion = userFull.wallpaper.settings.motion; + selectedBackgroundBlurred = userFull.wallpaper.settings.blur; + } + } } else { - selectedBackgroundSlug = Theme.hasWallpaperFromTheme() ? Theme.THEME_BACKGROUND_SLUG : Theme.DEFAULT_BACKGROUND_SLUG; - selectedColor = 0; - selectedGradientColor1 = 0; - selectedGradientColor2 = 0; - selectedGradientColor3 = 0; - selectedGradientRotation = 45; - selectedIntensity = 1.0f; - selectedBackgroundMotion = false; - selectedBackgroundBlurred = false; + Theme.OverrideWallpaperInfo overrideWallpaper = themeInfo.overrideWallpaper; + if (overrideWallpaper != null) { + selectedBackgroundSlug = overrideWallpaper.slug; + if (selectedBackgroundSlug == null) { + selectedBackgroundSlug = ""; + } + selectedColor = overrideWallpaper.color; + selectedGradientColor1 = overrideWallpaper.gradientColor1; + selectedGradientColor2 = overrideWallpaper.gradientColor2; + selectedGradientColor3 = overrideWallpaper.gradientColor3; + selectedGradientRotation = overrideWallpaper.rotation; + selectedIntensity = overrideWallpaper.intensity; + selectedBackgroundMotion = overrideWallpaper.isMotion; + selectedBackgroundBlurred = overrideWallpaper.isBlurred; + } else { + selectedBackgroundSlug = Theme.hasWallpaperFromTheme() ? Theme.THEME_BACKGROUND_SLUG : Theme.DEFAULT_BACKGROUND_SLUG; + selectedColor = 0; + selectedGradientColor1 = 0; + selectedGradientColor2 = 0; + selectedGradientColor3 = 0; + selectedGradientRotation = 45; + selectedIntensity = 1.0f; + selectedBackgroundMotion = false; + selectedBackgroundBlurred = false; + } } fillWallpapersWithCustom(); fixLayout(); @@ -926,13 +964,14 @@ private void onItemClick(WallpaperCell view, Object object, int index) { } } ThemePreviewActivity wallpaperActivity = new ThemePreviewActivity(object, null, true, false); - if (currentType == TYPE_COLOR) { + if (currentType == TYPE_COLOR || dialogId != 0) { wallpaperActivity.setDelegate(WallpapersListActivity.this::removeSelfFromStack); } if (selectedBackgroundSlug.equals(slug)) { - wallpaperActivity.setInitialModes(selectedBackgroundBlurred, selectedBackgroundMotion); + wallpaperActivity.setInitialModes(selectedBackgroundBlurred, selectedBackgroundMotion, selectedIntensity); } wallpaperActivity.setPatterns(patterns); + wallpaperActivity.setDialogId(dialogId); presentFragment(wallpaperActivity); } } @@ -1029,7 +1068,9 @@ public void didReceivedNotification(int id, int account, Object... args) { getMessagesStorage().deleteWallpaper(wallPapersToDelete.get(a).id); } } - selectedBackgroundSlug = Theme.getSelectedBackgroundSlug(); + if (dialogId == 0) { + selectedBackgroundSlug = Theme.getSelectedBackgroundSlug(); + } fillWallpapersWithCustom(); loadWallpapers(false); } else if (id == NotificationCenter.didSetNewWallpapper) { @@ -1773,7 +1814,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType } case 1: { view = new ShadowSectionCell(mContext); - Drawable drawable = Theme.getThemedDrawable(mContext, wallPaperStartRow == -1 ? R.drawable.greydivider_bottom : R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow); + Drawable drawable = Theme.getThemedDrawableByKey(mContext, wallPaperStartRow == -1 ? R.drawable.greydivider_bottom : R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow); CombinedDrawable combinedDrawable = new CombinedDrawable(new ColorDrawable(Theme.getColor(Theme.key_windowBackgroundGray)), drawable); combinedDrawable.setFullsize(true); view.setBackgroundDrawable(combinedDrawable); @@ -1781,7 +1822,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType } case 3: { view = new TextInfoPrivacyCell(mContext); - Drawable drawable = Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow); + Drawable drawable = Theme.getThemedDrawableByKey(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow); CombinedDrawable combinedDrawable = new CombinedDrawable(new ColorDrawable(Theme.getColor(Theme.key_windowBackgroundGray)), drawable); combinedDrawable.setFullsize(true); view.setBackgroundDrawable(combinedDrawable); @@ -1855,7 +1896,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } else { if (object instanceof ColorWallpaper) { ColorWallpaper colorWallpaper = (ColorWallpaper) object; - if (Theme.DEFAULT_BACKGROUND_SLUG.equals(colorWallpaper.slug) && selectedBackgroundSlug.equals(colorWallpaper.slug)) { + if (Theme.DEFAULT_BACKGROUND_SLUG.equals(colorWallpaper.slug) && selectedBackgroundSlug != null && selectedBackgroundSlug.equals(colorWallpaper.slug)) { selectedWallpaper = object; } else if (colorWallpaper.color != selectedColor || colorWallpaper.gradientColor1 != selectedGradientColor1 || colorWallpaper.gradientColor2 != selectedGradientColor2 || colorWallpaper.gradientColor3 != selectedGradientColor3 || selectedGradientColor1 != 0 && colorWallpaper.gradientRotation != selectedGradientRotation) { selectedWallpaper = null; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/WrappedResourceProvider.java b/TMessagesProj/src/main/java/org/telegram/ui/WrappedResourceProvider.java new file mode 100644 index 0000000000..cc522fea8b --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/WrappedResourceProvider.java @@ -0,0 +1,72 @@ +package org.telegram.ui; + +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.drawable.Drawable; +import android.util.SparseIntArray; + +import org.telegram.ui.ActionBar.Theme; + +public class WrappedResourceProvider implements Theme.ResourcesProvider { + + public SparseIntArray sparseIntArray = new SparseIntArray(); + + Theme.ResourcesProvider resourcesProvider; + public WrappedResourceProvider(Theme.ResourcesProvider resourcesProvider) { + this.resourcesProvider = resourcesProvider; + appendColors(); + } + + public void appendColors() { + + } + + @Override + public int getColor(int key) { + int index = sparseIntArray.indexOfKey(key); + if (index >= 0) { + return sparseIntArray.valueAt(index); + } + return resourcesProvider.getColor(key); + } + + @Override + public int getColorOrDefault(int key) { + return resourcesProvider.getColorOrDefault(key); + } + + @Override + public int getCurrentColor(int key) { + return resourcesProvider.getCurrentColor(key); + } + + @Override + public void setAnimatedColor(int key, int color) { + resourcesProvider.setAnimatedColor(key, color); + } + + @Override + public Drawable getDrawable(String drawableKey) { + return resourcesProvider.getDrawable(drawableKey); + } + + @Override + public Paint getPaint(String paintKey) { + return resourcesProvider.getPaint(paintKey); + } + + @Override + public boolean hasGradientService() { + return resourcesProvider.hasGradientService(); + } + + @Override + public void applyServiceShaderMatrix(int w, int h, float translationX, float translationY) { + resourcesProvider.applyServiceShaderMatrix(w, h, translationX, translationY); + } + + @Override + public ColorFilter getAnimatedEmojiColorFilter() { + return resourcesProvider.getAnimatedEmojiColorFilter(); + } +} diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/BackButtonMenuRecent.java b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/BackButtonMenuRecent.java new file mode 100644 index 0000000000..05addfda4e --- /dev/null +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/BackButtonMenuRecent.java @@ -0,0 +1,290 @@ +package tw.nekomimi.nekogram; + +import android.content.Context; +import android.content.SharedPreferences; +import android.graphics.Canvas; +import android.graphics.Path; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.Base64; +import android.util.SparseArray; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.view.WindowManager; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.TextView; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.ImageLocation; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.R; +import org.telegram.messenger.UserObject; +import org.telegram.messenger.Utilities; +import org.telegram.tgnet.SerializedData; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.ActionBarPopupWindow; +import org.telegram.ui.ActionBar.AlertDialog; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.ChatActivity; +import org.telegram.ui.Components.AvatarDrawable; +import org.telegram.ui.Components.BackupImageView; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.ProfileActivity; +import org.telegram.ui.TopicsFragment; + +import java.util.LinkedList; +import java.util.concurrent.atomic.AtomicReference; + +public class BackButtonMenuRecent { + + private static final int MAX_RECENT_DIALOGS = 25; + + private static final SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("nekorecentdialogs", Context.MODE_PRIVATE); + private static final SparseArray> recentDialogs = new SparseArray<>(); + + public static void show(int currentAccount, BaseFragment fragment, View backButton) { + if (fragment == null) { + return; + } + final Context context = fragment.getParentActivity(); + final View fragmentView = fragment.getFragmentView(); + if (context == null || fragmentView == null) { + return; + } + LinkedList dialogs = getRecentDialogs(fragment.getCurrentAccount()); + if (dialogs.isEmpty()) { + return; + } + + ActionBarPopupWindow.ActionBarPopupWindowLayout layout = new ActionBarPopupWindow.ActionBarPopupWindowLayout(context) { + final Path path = new Path(); + + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + canvas.save(); + path.rewind(); + AndroidUtilities.rectTmp.set(child.getLeft(), child.getTop(), child.getRight(), child.getBottom()); + path.addRoundRect(AndroidUtilities.rectTmp, AndroidUtilities.dp(6), AndroidUtilities.dp(6), Path.Direction.CW); + canvas.clipPath(path); + boolean draw = super.drawChild(canvas, child, drawingTime); + canvas.restore(); + return draw; + } + }; + Rect backgroundPaddings = new Rect(); + Drawable shadowDrawable = fragment.getParentActivity().getResources().getDrawable(R.drawable.popup_fixed_alert2).mutate(); + shadowDrawable.getPadding(backgroundPaddings); + layout.setBackgroundColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuBackground)); + + AtomicReference scrimPopupWindowRef = new AtomicReference<>(); + + FrameLayout headerView = new FrameLayout(context); + headerView.setBackgroundColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuBackground)); + + TextView titleTextView = new TextView(context); + titleTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlue)); + titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + titleTextView.setText(LocaleController.getString("RecentChats", R.string.RecentChats)); + titleTextView.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + headerView.addView(titleTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 24, Gravity.LEFT)); + + ImageView clearImageView = new ImageView(context); + clearImageView.setScaleType(ImageView.ScaleType.CENTER); + clearImageView.setColorFilter(Theme.getColor(Theme.key_actionBarDefaultSubmenuItemIcon)); + clearImageView.setImageResource(R.drawable.msg_close); + clearImageView.setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_listSelector))); + clearImageView.setOnClickListener(e2 -> { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(LocaleController.getString("ClearRecentChats", R.string.ClearRecentChats)); + builder.setMessage(LocaleController.getString("ClearRecentChatAlert", R.string.ClearRecentChatAlert)); + builder.setPositiveButton(LocaleController.getString("ClearButton", R.string.ClearButton).toUpperCase(), (dialogInterface, i) -> { + if (scrimPopupWindowRef.get() != null) { + scrimPopupWindowRef.getAndSet(null).dismiss(); + } + clearRecentDialogs(currentAccount); + }); + builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + fragment.showDialog(builder.create()); + }); + headerView.addView(clearImageView, LayoutHelper.createFrame(24, 24, Gravity.RIGHT | Gravity.CENTER_VERTICAL)); + + headerView.setPadding(AndroidUtilities.dp(9), AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8)); + layout.addView(headerView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 4, 4, 4, 4)); + + for (Long dialogId : dialogs) { + final TLRPC.Chat chat; + final TLRPC.User user; + if (dialogId < 0) { + chat = MessagesController.getInstance(currentAccount).getChat(-dialogId); + user = null; + } else { + chat = null; + user = MessagesController.getInstance(currentAccount).getUser(dialogId); + } + if (chat == null && user == null) { + continue; + } + FrameLayout cell = new FrameLayout(context); + cell.setMinimumWidth(AndroidUtilities.dp(200)); + + BackupImageView imageView = new BackupImageView(context); + imageView.setRoundRadius(chat != null && chat.forum ? AndroidUtilities.dp(8) : AndroidUtilities.dp(16)); + cell.addView(imageView, LayoutHelper.createFrameRelatively(32, 32, Gravity.START | Gravity.CENTER_VERTICAL, 13, 0, 0, 0)); + + TextView titleView = new TextView(context); + titleView.setLines(1); + titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + titleView.setTextColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuItem)); + titleView.setEllipsize(TextUtils.TruncateAt.END); + cell.addView(titleView, LayoutHelper.createFrameRelatively(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.START | Gravity.CENTER_VERTICAL, 59, 0, 12, 0)); + + AvatarDrawable avatarDrawable = new AvatarDrawable(); + avatarDrawable.setScaleSize(.8f); + Drawable thumb = avatarDrawable; + + if (chat != null) { + avatarDrawable.setInfo(chat); + if (chat.photo != null && chat.photo.strippedBitmap != null) { + thumb = chat.photo.strippedBitmap; + } + imageView.setImage(ImageLocation.getForChat(chat, ImageLocation.TYPE_SMALL), "50_50", thumb, chat); + titleView.setText(chat.title); + } else { + String name; + if (user.photo != null && user.photo.strippedBitmap != null) { + thumb = user.photo.strippedBitmap; + } + if (UserObject.isReplyUser(user)) { + name = LocaleController.getString("RepliesTitle", R.string.RepliesTitle); + avatarDrawable.setAvatarType(AvatarDrawable.AVATAR_TYPE_REPLIES); + imageView.setImageDrawable(avatarDrawable); + } else if (UserObject.isDeleted(user)) { + name = LocaleController.getString("HiddenName", R.string.HiddenName); + avatarDrawable.setInfo(user); + imageView.setImage(ImageLocation.getForUser(user, ImageLocation.TYPE_SMALL), "50_50", avatarDrawable, user); + } else { + name = UserObject.getUserName(user); + avatarDrawable.setInfo(user); + imageView.setImage(ImageLocation.getForUser(user, ImageLocation.TYPE_SMALL), "50_50", thumb, user); + } + titleView.setText(name); + } + + cell.setBackground(Theme.getSelectorDrawable(Theme.getColor(Theme.key_listSelector), false)); + cell.setOnClickListener(e2 -> { + if (scrimPopupWindowRef.get() != null) { + scrimPopupWindowRef.getAndSet(null).dismiss(); + } + Bundle bundle = new Bundle(); + if (dialogId < 0) { + bundle.putLong("chat_id", -dialogId); + if (MessagesController.getInstance(currentAccount).isForum(dialogId)) { + fragment.presentFragment(new TopicsFragment(bundle)); + } else { + fragment.presentFragment(new ChatActivity(bundle)); + } + } else { + bundle.putLong("user_id", dialogId); + fragment.presentFragment(new ChatActivity(bundle)); + } + }); + cell.setOnLongClickListener(e2 -> { + if (scrimPopupWindowRef.get() != null) { + scrimPopupWindowRef.getAndSet(null).dismiss(); + } + Bundle bundle = new Bundle(); + if (dialogId < 0) { + bundle.putLong("chat_id", -dialogId); + } else { + bundle.putLong("user_id", dialogId); + } + fragment.presentFragment(new ProfileActivity(bundle)); + return true; + }); + layout.addView(cell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48)); + } + + ActionBarPopupWindow scrimPopupWindow = new ActionBarPopupWindow(layout, LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT); + scrimPopupWindowRef.set(scrimPopupWindow); + scrimPopupWindow.setPauseNotifications(true); + scrimPopupWindow.setDismissAnimationDuration(220); + scrimPopupWindow.setOutsideTouchable(true); + scrimPopupWindow.setClippingEnabled(true); + scrimPopupWindow.setAnimationStyle(R.style.PopupContextAnimation); + scrimPopupWindow.setFocusable(true); + layout.measure(View.MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(1000), View.MeasureSpec.AT_MOST), View.MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(1000), View.MeasureSpec.AT_MOST)); + scrimPopupWindow.setInputMethodMode(ActionBarPopupWindow.INPUT_METHOD_NOT_NEEDED); + scrimPopupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED); + scrimPopupWindow.getContentView().setFocusableInTouchMode(true); + layout.setFitItems(true); + + int popupX = AndroidUtilities.dp(8) - backgroundPaddings.left; + if (AndroidUtilities.isTablet()) { + int[] location = new int[2]; + fragmentView.getLocationInWindow(location); + popupX += location[0]; + } + int popupY = backButton.getBottom() - backgroundPaddings.top - AndroidUtilities.dp(8); + scrimPopupWindow.showAtLocation(fragmentView, Gravity.LEFT | Gravity.TOP, popupX, popupY); + scrimPopupWindow.dimBehind(); + } + + private static LinkedList getRecentDialogs(int currentAccount) { + LinkedList recentDialog = recentDialogs.get(currentAccount); + if (recentDialog == null) { + recentDialog = new LinkedList<>(); + String list = preferences.getString("recents_" + currentAccount, null); + if (!TextUtils.isEmpty(list)) { + byte[] bytes = Base64.decode(list, Base64.NO_WRAP | Base64.NO_PADDING); + SerializedData data = new SerializedData(bytes); + int count = data.readInt32(false); + for (int a = 0; a < count; a++) { + recentDialog.add(data.readInt64(false)); + } + data.cleanup(); + } + recentDialogs.put(currentAccount, recentDialog); + } + return recentDialog; + } + + public static void addToRecentDialogs(int currentAccount, long dialogId) { + LinkedList recentDialog = getRecentDialogs(currentAccount); + for (int i = 0; i < recentDialog.size(); i++) { + if (recentDialog.get(i) == dialogId) { + recentDialog.remove(i); + break; + } + } + + if (recentDialog.size() > MAX_RECENT_DIALOGS) { + recentDialog.removeLast(); + } + recentDialog.addFirst(dialogId); + LinkedList finalRecentDialog = new LinkedList<>(recentDialog); + Utilities.globalQueue.postRunnable(() -> saveRecentDialogs(currentAccount, finalRecentDialog)); + } + + private static void saveRecentDialogs(int currentAccount, LinkedList recentDialog) { + SerializedData serializedData = new SerializedData(); + int count = recentDialog.size(); + serializedData.writeInt32(count); + for (Long dialog : recentDialog) { + serializedData.writeInt64(dialog); + } + preferences.edit().putString("recents_" + currentAccount, Base64.encodeToString(serializedData.toByteArray(), Base64.NO_WRAP | Base64.NO_PADDING)).apply(); + serializedData.cleanup(); + } + + public static void clearRecentDialogs(int currentAccount) { + getRecentDialogs(currentAccount).clear(); + preferences.edit().putString("recents_" + currentAccount, "").apply(); + } +} diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/ui/MessageHelper.java b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/ui/MessageHelper.java index 9fd6b004cd..8bf32a00b1 100644 --- a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/ui/MessageHelper.java +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/ui/MessageHelper.java @@ -20,6 +20,7 @@ import org.telegram.messenger.MessageObject; import org.telegram.messenger.MessagesController; import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.SendMessagesHelper; import org.telegram.messenger.Utilities; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.NativeByteBuffer; @@ -135,18 +136,18 @@ public void processForwardFromMyName(ArrayList messages, long did } } if (messageObject.messageOwner.media.photo instanceof TLRPC.TL_photo) { - getSendMessagesHelper().sendMessage((TLRPC.TL_photo) messageObject.messageOwner.media.photo, null, did, null, null, messageObject.messageOwner.message, entities, null, params, notify, scheduleDate, 0, messageObject, false); + getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of((TLRPC.TL_photo) messageObject.messageOwner.media.photo, null, did, null, null, messageObject.messageOwner.message, entities, null, params, notify, scheduleDate, 0, messageObject, false)); } else if (messageObject.messageOwner.media.document instanceof TLRPC.TL_document) { - getSendMessagesHelper().sendMessage((TLRPC.TL_document) messageObject.messageOwner.media.document, null, messageObject.messageOwner.attachPath, did, null, null, messageObject.messageOwner.message, entities, null, params, notify, scheduleDate, 0, messageObject, null, false); + getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of((TLRPC.TL_document) messageObject.messageOwner.media.document, null, messageObject.messageOwner.attachPath, did, null, null, messageObject.messageOwner.message, entities, null, params, notify, scheduleDate, 0, messageObject, null, false)); } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaVenue || messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeo) { - getSendMessagesHelper().sendMessage(messageObject.messageOwner.media, did, null, null, null, null, notify, scheduleDate); + getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of(messageObject.messageOwner.media, did, null, null, null, null, notify, scheduleDate)); } else if (messageObject.messageOwner.media.phone_number != null) { TLRPC.User user = new TLRPC.TL_userContact_old2(); user.phone = messageObject.messageOwner.media.phone_number; user.first_name = messageObject.messageOwner.media.first_name; user.last_name = messageObject.messageOwner.media.last_name; user.id = messageObject.messageOwner.media.user_id; - getSendMessagesHelper().sendMessage(user, did, null, null, null, null, notify, scheduleDate); + getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of(user, did, null, null, null, null, notify, scheduleDate)); } else if ((int) did != 0) { ArrayList arrayList = new ArrayList<>(); arrayList.add(messageObject); @@ -157,7 +158,7 @@ public void processForwardFromMyName(ArrayList messages, long did if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaWebPage) { webPage = messageObject.messageOwner.media.webpage; } - getSendMessagesHelper().sendMessage(messageObject.messageOwner.message, did, null, null, webPage, webPage != null, entities, null, null, notify, scheduleDate, null, false); + getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of(messageObject.messageOwner.message, did, null, null, webPage, webPage != null, entities, null, null, notify, scheduleDate, null, false)); } else if ((int) did != 0) { ArrayList arrayList = new ArrayList<>(); arrayList.add(messageObject); diff --git a/TMessagesProj/src/main/res/drawable-hdpi/fab_compose_small.png b/TMessagesProj/src/main/res/drawable-hdpi/fab_compose_small.png new file mode 100644 index 0000000000..fe3cfd3769 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/fab_compose_small.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/large_archive.png b/TMessagesProj/src/main/res/drawable-hdpi/large_archive.png new file mode 100644 index 0000000000..d32d52b568 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/large_archive.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/media_download.png b/TMessagesProj/src/main/res/drawable-hdpi/media_download.png new file mode 100644 index 0000000000..daf56bd4b9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/media_download.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/media_dual_camera2.png b/TMessagesProj/src/main/res/drawable-hdpi/media_dual_camera2.png new file mode 100644 index 0000000000..637a1e5785 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/media_dual_camera2.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/media_dual_camera2_shadow.png b/TMessagesProj/src/main/res/drawable-hdpi/media_dual_camera2_shadow.png new file mode 100644 index 0000000000..0ee4c769d6 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/media_dual_camera2_shadow.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/media_more.png b/TMessagesProj/src/main/res/drawable-hdpi/media_more.png new file mode 100644 index 0000000000..ad7a1bad06 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/media_more.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/media_mute.png b/TMessagesProj/src/main/res/drawable-hdpi/media_mute.png new file mode 100644 index 0000000000..5a076e828d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/media_mute.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/media_photo_flash_auto2.png b/TMessagesProj/src/main/res/drawable-hdpi/media_photo_flash_auto2.png new file mode 100644 index 0000000000..c0db5663a1 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/media_photo_flash_auto2.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/media_photo_flash_off2.png b/TMessagesProj/src/main/res/drawable-hdpi/media_photo_flash_off2.png new file mode 100644 index 0000000000..416d86d227 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/media_photo_flash_off2.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/media_photo_flash_on2.png b/TMessagesProj/src/main/res/drawable-hdpi/media_photo_flash_on2.png new file mode 100644 index 0000000000..8d357b5b83 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/media_photo_flash_on2.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/media_share.png b/TMessagesProj/src/main/res/drawable-hdpi/media_share.png new file mode 100644 index 0000000000..fa59449cc8 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/media_share.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/media_unmute.png b/TMessagesProj/src/main/res/drawable-hdpi/media_unmute.png new file mode 100644 index 0000000000..e638bf475a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/media_unmute.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg2_chats_add.png b/TMessagesProj/src/main/res/drawable-hdpi/msg2_chats_add.png new file mode 100644 index 0000000000..4b302023ec Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg2_chats_add.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg2_link2.png b/TMessagesProj/src/main/res/drawable-hdpi/msg2_link2.png new file mode 100644 index 0000000000..c6f12123c6 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg2_link2.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg2_night_auto.png b/TMessagesProj/src/main/res/drawable-hdpi/msg2_night_auto.png new file mode 100644 index 0000000000..d06fb88bac Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg2_night_auto.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg2_proxy_off.png b/TMessagesProj/src/main/res/drawable-hdpi/msg2_proxy_off.png new file mode 100644 index 0000000000..35fef92acc Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg2_proxy_off.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg2_proxy_on.png b/TMessagesProj/src/main/res/drawable-hdpi/msg2_proxy_on.png new file mode 100644 index 0000000000..dc62ce20e1 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg2_proxy_on.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_archive_archive.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_archive_archive.png new file mode 100644 index 0000000000..64b44e1f1e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_archive_archive.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_archive_hide.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_archive_hide.png index a1737b5c60..c7007407f2 100644 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/msg_archive_hide.png and b/TMessagesProj/src/main/res/drawable-hdpi/msg_archive_hide.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_archive_stories.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_archive_stories.png new file mode 100644 index 0000000000..e494ef31f6 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_archive_stories.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_autodelete_2d.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_autodelete_2d.png new file mode 100644 index 0000000000..ecd6762e63 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_autodelete_2d.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_button_back.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_button_back.png new file mode 100644 index 0000000000..0b60e24916 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_button_back.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_button_colors.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_button_colors.png new file mode 100644 index 0000000000..e469c3c394 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_button_colors.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_button_wallpaper.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_button_wallpaper.png new file mode 100644 index 0000000000..466eb1960c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_button_wallpaper.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_draw_more.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_draw_more.png new file mode 100644 index 0000000000..dd3a81709b Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_draw_more.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_draw_pen.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_draw_pen.png index 994d12bbe5..1154204cad 100644 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/msg_draw_pen.png and b/TMessagesProj/src/main/res/drawable-hdpi/msg_draw_pen.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_filled_lockedrecord.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_filled_lockedrecord.png new file mode 100644 index 0000000000..726a07fbdf Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_filled_lockedrecord.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_filled_stories.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_filled_stories.png new file mode 100644 index 0000000000..369b0b5c60 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_filled_stories.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_filled_story_share.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_filled_story_share.png new file mode 100644 index 0000000000..17535e0b14 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_filled_story_share.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_filled_unlockedrecord.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_filled_unlockedrecord.png new file mode 100644 index 0000000000..28f76f1b94 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_filled_unlockedrecord.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_header_draw.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_header_draw.png new file mode 100644 index 0000000000..f3938176d9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_header_draw.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_header_qr.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_header_qr.png new file mode 100644 index 0000000000..544aa65306 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_header_qr.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_header_share.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_header_share.png new file mode 100644 index 0000000000..67c8649dca Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_header_share.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_hidetocontacts.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_hidetocontacts.png new file mode 100644 index 0000000000..69b5bf97d0 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_hidetocontacts.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_input_attach2.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_input_attach2.png new file mode 100644 index 0000000000..e07d6cddfd Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_input_attach2.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_input_like.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_input_like.png new file mode 100644 index 0000000000..f523d5d83e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_input_like.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_link_folder.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_link_folder.png new file mode 100644 index 0000000000..7aaa478872 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_link_folder.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_menu_stories.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_menu_stories.png new file mode 100644 index 0000000000..59bacc7a16 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_menu_stories.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_mini_addstory.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_mini_addstory.png new file mode 100644 index 0000000000..aea0749a30 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_mini_addstory.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_mini_arrow_mediabold.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_mini_arrow_mediabold.png new file mode 100644 index 0000000000..55ade156b0 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_mini_arrow_mediabold.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_mini_arrow_mediathin.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_mini_arrow_mediathin.png new file mode 100644 index 0000000000..dbcccb9396 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_mini_arrow_mediathin.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_mini_bomb.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_mini_bomb.png new file mode 100644 index 0000000000..92e69c18e4 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_mini_bomb.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_mini_close_tooltip.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_mini_close_tooltip.png new file mode 100644 index 0000000000..c8f82afa54 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_mini_close_tooltip.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_mini_closefriends.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_mini_closefriends.png new file mode 100644 index 0000000000..7f5bc8d2bb Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_mini_closefriends.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_mini_replystory.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_mini_replystory.png new file mode 100644 index 0000000000..861684f73e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_mini_replystory.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_mini_replystory2.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_mini_replystory2.png new file mode 100644 index 0000000000..090d08dc58 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_mini_replystory2.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_online.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_online.png new file mode 100644 index 0000000000..be42492d26 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_online.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_other_new_filled.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_other_new_filled.png new file mode 100644 index 0000000000..eb43b008b2 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_other_new_filled.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_other_new_filled_text.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_other_new_filled_text.png new file mode 100644 index 0000000000..c10dd28819 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_other_new_filled_text.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_other_new_outline.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_other_new_outline.png new file mode 100644 index 0000000000..1171cf460b Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_other_new_outline.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_back.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_back.png new file mode 100644 index 0000000000..65c45eed4e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_back.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_clearvideo.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_clearvideo.png new file mode 100644 index 0000000000..727f186719 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_clearvideo.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_close.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_close.png new file mode 100644 index 0000000000..1ba764364f Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_close.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_flash_auto.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_flash_auto.png new file mode 100644 index 0000000000..ce6a636d95 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_flash_auto.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_flash_off.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_flash_off.png new file mode 100644 index 0000000000..6d3d6a0209 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_flash_off.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_flash_on.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_flash_on.png new file mode 100644 index 0000000000..970284d0c5 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_flash_on.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_settings.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_settings.png index 75c317a8b8..b2b0e3efc9 100644 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_settings.png and b/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_settings.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_sticker.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_sticker.png new file mode 100644 index 0000000000..5ab0b7116e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_sticker.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_switch2.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_switch2.png new file mode 100644 index 0000000000..4731183ed9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_switch2.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_text2.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_text2.png new file mode 100644 index 0000000000..87f9f7dca2 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_text2.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_text_framed.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_text_framed.png new file mode 100644 index 0000000000..bce63a1de4 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_text_framed.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_text_framed2.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_text_framed2.png new file mode 100644 index 0000000000..5e03275a4f Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_text_framed2.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_text_framed3.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_text_framed3.png new file mode 100644 index 0000000000..4566cea3b2 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_text_framed3.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_text_regular.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_text_regular.png new file mode 100644 index 0000000000..ae30a937db Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_photo_text_regular.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_save_story.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_save_story.png new file mode 100644 index 0000000000..eb5c81ce14 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_save_story.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_share_filled.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_share_filled.png index 5e70648b76..49dd1f7157 100644 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/msg_share_filled.png and b/TMessagesProj/src/main/res/drawable-hdpi/msg_share_filled.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_status_edit.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_status_edit.png new file mode 100644 index 0000000000..d972c30aef Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_status_edit.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_status_set.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_status_set.png new file mode 100644 index 0000000000..a18fe2553d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_status_set.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_stories_add.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_stories_add.png new file mode 100644 index 0000000000..c61a06339e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_stories_add.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_stories_archive.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_stories_archive.png new file mode 100644 index 0000000000..c8c1db57bb Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_stories_archive.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_stories_closefriends.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_stories_closefriends.png new file mode 100644 index 0000000000..32c74a5fa4 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_stories_closefriends.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_stories_saved.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_stories_saved.png new file mode 100644 index 0000000000..280dd44340 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_stories_saved.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_story_12h.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_story_12h.png new file mode 100644 index 0000000000..a4f1855a1c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_story_12h.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_story_24h.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_story_24h.png new file mode 100644 index 0000000000..cf06905c2b Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_story_24h.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_story_48h.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_story_48h.png new file mode 100644 index 0000000000..3aeebddde5 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_story_48h.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_story_6h.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_story_6h.png new file mode 100644 index 0000000000..50b2adc9c8 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_story_6h.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_story_infinite.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_story_infinite.png new file mode 100644 index 0000000000..7d5f52fd6a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_story_infinite.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_story_once.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_story_once.png new file mode 100644 index 0000000000..5134e1ef50 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_story_once.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_story_once_filled1.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_story_once_filled1.png new file mode 100644 index 0000000000..6a042ea774 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_story_once_filled1.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_tabs_camera_filled.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_tabs_camera_filled.png new file mode 100644 index 0000000000..06d59931aa Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_tabs_camera_filled.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_tabs_media.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_tabs_media.png new file mode 100644 index 0000000000..4675d14b54 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_tabs_media.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_tabs_stories_archive.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_tabs_stories_archive.png new file mode 100644 index 0000000000..e74c74df63 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_tabs_stories_archive.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_tabs_stories_saved.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_tabs_stories_saved.png new file mode 100644 index 0000000000..7d95f13a8f Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_tabs_stories_saved.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_tabs_stories_saved2.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_tabs_stories_saved2.png new file mode 100644 index 0000000000..b60ff05493 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_tabs_stories_saved2.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/proxy_off.png b/TMessagesProj/src/main/res/drawable-hdpi/proxy_off.png deleted file mode 100644 index 1e6bb23eb2..0000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/proxy_off.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/proxy_on.png b/TMessagesProj/src/main/res/drawable-hdpi/proxy_on.png deleted file mode 100644 index 00fb01273c..0000000000 Binary files a/TMessagesProj/src/main/res/drawable-hdpi/proxy_on.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/shadow_story_bottom.9.png b/TMessagesProj/src/main/res/drawable-hdpi/shadow_story_bottom.9.png new file mode 100644 index 0000000000..1015498fb6 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/shadow_story_bottom.9.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/shadow_story_top.9.png b/TMessagesProj/src/main/res/drawable-hdpi/shadow_story_top.9.png new file mode 100644 index 0000000000..50775a9eb7 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/shadow_story_top.9.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/fab_compose_small.png b/TMessagesProj/src/main/res/drawable-mdpi/fab_compose_small.png new file mode 100644 index 0000000000..ca475345b5 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/fab_compose_small.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/large_archive.png b/TMessagesProj/src/main/res/drawable-mdpi/large_archive.png new file mode 100644 index 0000000000..7808654aeb Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/large_archive.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/media_download.png b/TMessagesProj/src/main/res/drawable-mdpi/media_download.png new file mode 100644 index 0000000000..527fbd6b0c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/media_download.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/media_dual_camera2.png b/TMessagesProj/src/main/res/drawable-mdpi/media_dual_camera2.png new file mode 100644 index 0000000000..4824024b4f Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/media_dual_camera2.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/media_dual_camera2_shadow.png b/TMessagesProj/src/main/res/drawable-mdpi/media_dual_camera2_shadow.png new file mode 100644 index 0000000000..1fee07bb9a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/media_dual_camera2_shadow.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/media_more.png b/TMessagesProj/src/main/res/drawable-mdpi/media_more.png new file mode 100644 index 0000000000..e60af13d9b Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/media_more.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/media_mute.png b/TMessagesProj/src/main/res/drawable-mdpi/media_mute.png new file mode 100644 index 0000000000..6280bedd78 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/media_mute.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/media_photo_flash_auto2.png b/TMessagesProj/src/main/res/drawable-mdpi/media_photo_flash_auto2.png new file mode 100644 index 0000000000..0d38e17582 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/media_photo_flash_auto2.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/media_photo_flash_off2.png b/TMessagesProj/src/main/res/drawable-mdpi/media_photo_flash_off2.png new file mode 100644 index 0000000000..2e67eacbb9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/media_photo_flash_off2.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/media_photo_flash_on2.png b/TMessagesProj/src/main/res/drawable-mdpi/media_photo_flash_on2.png new file mode 100644 index 0000000000..b88532d51d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/media_photo_flash_on2.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/media_share.png b/TMessagesProj/src/main/res/drawable-mdpi/media_share.png new file mode 100644 index 0000000000..518219d822 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/media_share.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/media_unmute.png b/TMessagesProj/src/main/res/drawable-mdpi/media_unmute.png new file mode 100644 index 0000000000..0baa71cc27 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/media_unmute.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg2_chats_add.png b/TMessagesProj/src/main/res/drawable-mdpi/msg2_chats_add.png new file mode 100644 index 0000000000..4a36dddbe1 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg2_chats_add.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg2_link2.png b/TMessagesProj/src/main/res/drawable-mdpi/msg2_link2.png new file mode 100644 index 0000000000..60f5659574 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg2_link2.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg2_night_auto.png b/TMessagesProj/src/main/res/drawable-mdpi/msg2_night_auto.png new file mode 100644 index 0000000000..bd72aa392c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg2_night_auto.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg2_proxy_off.png b/TMessagesProj/src/main/res/drawable-mdpi/msg2_proxy_off.png new file mode 100644 index 0000000000..e9b7691a1e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg2_proxy_off.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg2_proxy_on.png b/TMessagesProj/src/main/res/drawable-mdpi/msg2_proxy_on.png new file mode 100644 index 0000000000..75ccd6e480 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg2_proxy_on.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_archive_archive.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_archive_archive.png new file mode 100644 index 0000000000..bba37cb1c0 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_archive_archive.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_archive_hide.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_archive_hide.png index d0dca82301..1714a7de1f 100644 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/msg_archive_hide.png and b/TMessagesProj/src/main/res/drawable-mdpi/msg_archive_hide.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_archive_stories.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_archive_stories.png new file mode 100644 index 0000000000..3d4c5de686 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_archive_stories.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_autodelete_2d.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_autodelete_2d.png new file mode 100644 index 0000000000..f7922ebc3a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_autodelete_2d.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_button_back.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_button_back.png new file mode 100644 index 0000000000..b67b4fa67c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_button_back.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_button_colors.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_button_colors.png new file mode 100644 index 0000000000..e573d5c765 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_button_colors.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_button_wallpaper.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_button_wallpaper.png new file mode 100644 index 0000000000..26a0fba6e9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_button_wallpaper.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_draw_more.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_draw_more.png new file mode 100644 index 0000000000..6127458372 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_draw_more.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_draw_pen.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_draw_pen.png index 93b0dec965..0ef713dc3d 100644 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/msg_draw_pen.png and b/TMessagesProj/src/main/res/drawable-mdpi/msg_draw_pen.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_filled_lockedrecord.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_filled_lockedrecord.png new file mode 100644 index 0000000000..4b36baf403 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_filled_lockedrecord.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_filled_stories.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_filled_stories.png new file mode 100644 index 0000000000..79311e37b3 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_filled_stories.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_filled_story_share.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_filled_story_share.png new file mode 100644 index 0000000000..7f64fb7466 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_filled_story_share.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_filled_unlockedrecord.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_filled_unlockedrecord.png new file mode 100644 index 0000000000..4c46b827d9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_filled_unlockedrecord.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_header_draw.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_header_draw.png new file mode 100644 index 0000000000..d8de5cc1eb Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_header_draw.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_header_qr.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_header_qr.png new file mode 100644 index 0000000000..f220f84b2a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_header_qr.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_header_share.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_header_share.png new file mode 100644 index 0000000000..d5bdcb92a3 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_header_share.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_hidetocontacts.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_hidetocontacts.png new file mode 100644 index 0000000000..5881c552d4 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_hidetocontacts.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_input_attach2.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_input_attach2.png new file mode 100644 index 0000000000..53ed2045b0 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_input_attach2.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_input_like.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_input_like.png new file mode 100644 index 0000000000..5ec07c6562 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_input_like.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_link_folder.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_link_folder.png new file mode 100644 index 0000000000..0bd4896d08 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_link_folder.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_menu_stories.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_menu_stories.png new file mode 100644 index 0000000000..be4cd195a5 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_menu_stories.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_mini_addstory.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_mini_addstory.png new file mode 100644 index 0000000000..bd702849ac Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_mini_addstory.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_mini_arrow_mediabold.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_mini_arrow_mediabold.png new file mode 100644 index 0000000000..27b56ebb62 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_mini_arrow_mediabold.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_mini_arrow_mediathin.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_mini_arrow_mediathin.png new file mode 100644 index 0000000000..8f97d68d31 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_mini_arrow_mediathin.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_mini_bomb.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_mini_bomb.png new file mode 100644 index 0000000000..c7e60ce192 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_mini_bomb.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_mini_close_tooltip.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_mini_close_tooltip.png new file mode 100644 index 0000000000..ee9ec1a15e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_mini_close_tooltip.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_mini_closefriends.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_mini_closefriends.png new file mode 100644 index 0000000000..db7f1f0005 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_mini_closefriends.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_mini_replystory.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_mini_replystory.png new file mode 100644 index 0000000000..8617750dc5 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_mini_replystory.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_mini_replystory2.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_mini_replystory2.png new file mode 100644 index 0000000000..cecd2fc580 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_mini_replystory2.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_online.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_online.png new file mode 100644 index 0000000000..9e3e22f417 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_online.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_other_new_filled.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_other_new_filled.png new file mode 100644 index 0000000000..8a546ef95f Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_other_new_filled.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_other_new_filled_text.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_other_new_filled_text.png new file mode 100644 index 0000000000..0f62d8cca1 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_other_new_filled_text.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_other_new_outline.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_other_new_outline.png new file mode 100644 index 0000000000..2851a88ce0 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_other_new_outline.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_back.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_back.png new file mode 100644 index 0000000000..83c3f2b634 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_back.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_clearvideo.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_clearvideo.png new file mode 100644 index 0000000000..7e275955a2 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_clearvideo.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_close.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_close.png new file mode 100644 index 0000000000..d4db7b4d83 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_close.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_flash_auto.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_flash_auto.png new file mode 100644 index 0000000000..03c126889c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_flash_auto.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_flash_off.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_flash_off.png new file mode 100644 index 0000000000..b3aaf6f05a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_flash_off.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_flash_on.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_flash_on.png new file mode 100644 index 0000000000..d44cf70c1b Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_flash_on.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_settings.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_settings.png index 527c546633..5aa5c908e5 100644 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_settings.png and b/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_settings.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_sticker.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_sticker.png new file mode 100644 index 0000000000..9ee3b51fdc Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_sticker.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_switch2.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_switch2.png new file mode 100644 index 0000000000..0e38103cd0 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_switch2.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_text2.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_text2.png new file mode 100644 index 0000000000..92881d7ed3 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_text2.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_text_framed.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_text_framed.png new file mode 100644 index 0000000000..d03e2e38bc Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_text_framed.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_text_framed2.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_text_framed2.png new file mode 100644 index 0000000000..9d0b336d44 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_text_framed2.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_text_framed3.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_text_framed3.png new file mode 100644 index 0000000000..43133b902e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_text_framed3.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_text_regular.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_text_regular.png new file mode 100644 index 0000000000..1921b645b9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_photo_text_regular.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_save_story.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_save_story.png new file mode 100644 index 0000000000..a67ed70024 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_save_story.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_share_filled.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_share_filled.png index 61b009aea3..0b351db93a 100644 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/msg_share_filled.png and b/TMessagesProj/src/main/res/drawable-mdpi/msg_share_filled.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_status_edit.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_status_edit.png new file mode 100644 index 0000000000..c6c1616ae8 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_status_edit.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_status_set.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_status_set.png new file mode 100644 index 0000000000..e32a6c2462 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_status_set.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_stories_add.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_stories_add.png new file mode 100644 index 0000000000..77e8d8c818 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_stories_add.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_stories_archive.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_stories_archive.png new file mode 100644 index 0000000000..95c60f4abb Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_stories_archive.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_stories_closefriends.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_stories_closefriends.png new file mode 100644 index 0000000000..9d443aabb8 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_stories_closefriends.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_stories_saved.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_stories_saved.png new file mode 100644 index 0000000000..6d5f4e1231 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_stories_saved.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_story_12h.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_story_12h.png new file mode 100644 index 0000000000..dcdbe13971 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_story_12h.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_story_24h.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_story_24h.png new file mode 100644 index 0000000000..b8ae2d6327 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_story_24h.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_story_48h.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_story_48h.png new file mode 100644 index 0000000000..f27049b9c0 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_story_48h.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_story_6h.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_story_6h.png new file mode 100644 index 0000000000..9d1f89729f Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_story_6h.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_story_infinite.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_story_infinite.png new file mode 100644 index 0000000000..5fa80ebb8f Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_story_infinite.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_story_once.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_story_once.png new file mode 100644 index 0000000000..d7a3ca82bf Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_story_once.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_story_once_filled1.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_story_once_filled1.png new file mode 100644 index 0000000000..af0c453809 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_story_once_filled1.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_tabs_camera_filled.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_tabs_camera_filled.png new file mode 100644 index 0000000000..65e10d043a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_tabs_camera_filled.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_tabs_media.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_tabs_media.png new file mode 100644 index 0000000000..a935f8ef49 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_tabs_media.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_tabs_stories_archive.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_tabs_stories_archive.png new file mode 100644 index 0000000000..447e1552fe Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_tabs_stories_archive.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_tabs_stories_saved.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_tabs_stories_saved.png new file mode 100644 index 0000000000..91edef3404 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_tabs_stories_saved.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_tabs_stories_saved2.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_tabs_stories_saved2.png new file mode 100644 index 0000000000..c7ec17f0d0 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_tabs_stories_saved2.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/proxy_off.png b/TMessagesProj/src/main/res/drawable-mdpi/proxy_off.png deleted file mode 100644 index 02ef8797ae..0000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/proxy_off.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/proxy_on.png b/TMessagesProj/src/main/res/drawable-mdpi/proxy_on.png deleted file mode 100644 index 2baf57564e..0000000000 Binary files a/TMessagesProj/src/main/res/drawable-mdpi/proxy_on.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/shadow_story_bottom.9.png b/TMessagesProj/src/main/res/drawable-mdpi/shadow_story_bottom.9.png new file mode 100644 index 0000000000..53d6c5dccd Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/shadow_story_bottom.9.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/shadow_story_top.9.png b/TMessagesProj/src/main/res/drawable-mdpi/shadow_story_top.9.png new file mode 100644 index 0000000000..0e3cac9d33 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/shadow_story_top.9.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/fab_compose_small.png b/TMessagesProj/src/main/res/drawable-xhdpi/fab_compose_small.png new file mode 100644 index 0000000000..02485d4c75 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/fab_compose_small.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/large_archive.png b/TMessagesProj/src/main/res/drawable-xhdpi/large_archive.png new file mode 100644 index 0000000000..a15966db63 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/large_archive.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/media_download.png b/TMessagesProj/src/main/res/drawable-xhdpi/media_download.png new file mode 100644 index 0000000000..ccaa8194be Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/media_download.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/media_dual_camera2.png b/TMessagesProj/src/main/res/drawable-xhdpi/media_dual_camera2.png new file mode 100644 index 0000000000..503b769f93 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/media_dual_camera2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/media_dual_camera2_shadow.png b/TMessagesProj/src/main/res/drawable-xhdpi/media_dual_camera2_shadow.png new file mode 100644 index 0000000000..49aa1a5159 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/media_dual_camera2_shadow.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/media_more.png b/TMessagesProj/src/main/res/drawable-xhdpi/media_more.png new file mode 100644 index 0000000000..e6773f82dd Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/media_more.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/media_mute.png b/TMessagesProj/src/main/res/drawable-xhdpi/media_mute.png new file mode 100644 index 0000000000..c3b687add9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/media_mute.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/media_photo_flash_auto2.png b/TMessagesProj/src/main/res/drawable-xhdpi/media_photo_flash_auto2.png new file mode 100644 index 0000000000..947ef3e2df Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/media_photo_flash_auto2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/media_photo_flash_off2.png b/TMessagesProj/src/main/res/drawable-xhdpi/media_photo_flash_off2.png new file mode 100644 index 0000000000..b951375603 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/media_photo_flash_off2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/media_photo_flash_on2.png b/TMessagesProj/src/main/res/drawable-xhdpi/media_photo_flash_on2.png new file mode 100644 index 0000000000..13e59b3790 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/media_photo_flash_on2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/media_share.png b/TMessagesProj/src/main/res/drawable-xhdpi/media_share.png new file mode 100644 index 0000000000..078b750c0e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/media_share.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/media_unmute.png b/TMessagesProj/src/main/res/drawable-xhdpi/media_unmute.png new file mode 100644 index 0000000000..acd43dee50 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/media_unmute.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg2_chats_add.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg2_chats_add.png new file mode 100644 index 0000000000..13e2eadc03 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg2_chats_add.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg2_link2.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg2_link2.png new file mode 100644 index 0000000000..ce9169c113 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg2_link2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg2_night_auto.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg2_night_auto.png new file mode 100644 index 0000000000..c91c5465c3 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg2_night_auto.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg2_proxy_off.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg2_proxy_off.png new file mode 100644 index 0000000000..0fdccec40a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg2_proxy_off.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg2_proxy_on.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg2_proxy_on.png new file mode 100644 index 0000000000..461b51da1f Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg2_proxy_on.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_archive_archive.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_archive_archive.png new file mode 100644 index 0000000000..867333abae Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_archive_archive.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_archive_hide.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_archive_hide.png index 5ea995f1e8..48a1b81380 100644 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/msg_archive_hide.png and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_archive_hide.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_archive_stories.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_archive_stories.png new file mode 100644 index 0000000000..395c354275 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_archive_stories.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_autodelete_2d.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_autodelete_2d.png new file mode 100644 index 0000000000..85567a0cd7 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_autodelete_2d.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_button_back.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_button_back.png new file mode 100644 index 0000000000..52c72ee92f Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_button_back.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_button_colors.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_button_colors.png new file mode 100644 index 0000000000..2ecfaafdca Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_button_colors.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_button_wallpaper.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_button_wallpaper.png new file mode 100644 index 0000000000..8ee179cea4 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_button_wallpaper.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_draw_more.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_draw_more.png new file mode 100644 index 0000000000..eac40e641d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_draw_more.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_draw_pen.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_draw_pen.png index 755aeff9ad..24fee2400e 100644 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/msg_draw_pen.png and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_draw_pen.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_filled_lockedrecord.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_filled_lockedrecord.png new file mode 100644 index 0000000000..58d0dd8e19 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_filled_lockedrecord.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_filled_stories.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_filled_stories.png new file mode 100644 index 0000000000..3445b9e040 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_filled_stories.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_filled_story_share.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_filled_story_share.png new file mode 100644 index 0000000000..2ee13bb5cb Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_filled_story_share.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_filled_unlockedrecord.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_filled_unlockedrecord.png new file mode 100644 index 0000000000..68319982b0 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_filled_unlockedrecord.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_header_draw.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_header_draw.png new file mode 100644 index 0000000000..2ca49ccac5 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_header_draw.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_header_qr.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_header_qr.png new file mode 100644 index 0000000000..9804f7df50 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_header_qr.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_header_share.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_header_share.png new file mode 100644 index 0000000000..13221af07a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_header_share.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_hidetocontacts.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_hidetocontacts.png new file mode 100644 index 0000000000..e402ceb508 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_hidetocontacts.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_input_attach2.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_input_attach2.png new file mode 100644 index 0000000000..e5941b0ae8 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_input_attach2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_input_like.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_input_like.png new file mode 100644 index 0000000000..24e12ba493 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_input_like.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_link_folder.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_link_folder.png new file mode 100644 index 0000000000..e4b4a655ad Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_link_folder.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_menu_stories.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_menu_stories.png new file mode 100644 index 0000000000..2b405925ce Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_menu_stories.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_mini_addstory.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_mini_addstory.png new file mode 100644 index 0000000000..85213a9815 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_mini_addstory.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_mini_arrow_mediabold.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_mini_arrow_mediabold.png new file mode 100644 index 0000000000..84309b95f1 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_mini_arrow_mediabold.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_mini_arrow_mediathin.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_mini_arrow_mediathin.png new file mode 100644 index 0000000000..fdd88dde79 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_mini_arrow_mediathin.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_mini_bomb.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_mini_bomb.png new file mode 100644 index 0000000000..991883ffdf Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_mini_bomb.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_mini_close_tooltip.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_mini_close_tooltip.png new file mode 100644 index 0000000000..a458ce017e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_mini_close_tooltip.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_mini_closefriends.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_mini_closefriends.png new file mode 100644 index 0000000000..3f0bc43810 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_mini_closefriends.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_mini_replystory.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_mini_replystory.png new file mode 100644 index 0000000000..4b4eeb56ad Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_mini_replystory.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_mini_replystory2.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_mini_replystory2.png new file mode 100644 index 0000000000..9f09dbd362 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_mini_replystory2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_online.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_online.png new file mode 100644 index 0000000000..2494c4ad17 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_online.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_other_new_filled.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_other_new_filled.png new file mode 100644 index 0000000000..3335b1542c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_other_new_filled.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_other_new_filled_text.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_other_new_filled_text.png new file mode 100644 index 0000000000..1f87c2c621 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_other_new_filled_text.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_other_new_outline.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_other_new_outline.png new file mode 100644 index 0000000000..39600e4341 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_other_new_outline.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_back.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_back.png new file mode 100644 index 0000000000..95b02a2175 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_back.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_clearvideo.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_clearvideo.png new file mode 100644 index 0000000000..acdae74eb9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_clearvideo.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_close.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_close.png new file mode 100644 index 0000000000..12660f1bab Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_close.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_flash_auto.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_flash_auto.png new file mode 100644 index 0000000000..77aebba3eb Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_flash_auto.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_flash_off.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_flash_off.png new file mode 100644 index 0000000000..e48a665942 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_flash_off.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_flash_on.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_flash_on.png new file mode 100644 index 0000000000..2e4efea423 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_flash_on.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_settings.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_settings.png index 0a22ffd539..01cf7cc2c2 100644 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_settings.png and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_settings.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_sticker.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_sticker.png new file mode 100644 index 0000000000..d6f6c5e3ea Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_sticker.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_switch2.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_switch2.png new file mode 100644 index 0000000000..3194d72ec3 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_switch2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_text2.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_text2.png new file mode 100644 index 0000000000..1f5746eac4 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_text2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_text_framed.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_text_framed.png new file mode 100644 index 0000000000..0896a87e07 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_text_framed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_text_framed2.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_text_framed2.png new file mode 100644 index 0000000000..b4cf3fef76 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_text_framed2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_text_framed3.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_text_framed3.png new file mode 100644 index 0000000000..3ca3113d72 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_text_framed3.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_text_regular.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_text_regular.png new file mode 100644 index 0000000000..89bdd74f3e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_photo_text_regular.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_save_story.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_save_story.png new file mode 100644 index 0000000000..a278afd742 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_save_story.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_share_filled.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_share_filled.png index a7347fd355..bb73d53936 100644 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/msg_share_filled.png and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_share_filled.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_status_edit.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_status_edit.png new file mode 100644 index 0000000000..522795edac Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_status_edit.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_status_set.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_status_set.png new file mode 100644 index 0000000000..ce9a0a93b3 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_status_set.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_stories_add.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_stories_add.png new file mode 100644 index 0000000000..589571076e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_stories_add.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_stories_archive.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_stories_archive.png new file mode 100644 index 0000000000..cd6357026c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_stories_archive.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_stories_closefriends.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_stories_closefriends.png new file mode 100644 index 0000000000..f75aa60f34 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_stories_closefriends.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_stories_saved.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_stories_saved.png new file mode 100644 index 0000000000..00cc32817a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_stories_saved.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_story_12h.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_story_12h.png new file mode 100644 index 0000000000..21eb23c616 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_story_12h.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_story_24h.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_story_24h.png new file mode 100644 index 0000000000..9b9cada2b9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_story_24h.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_story_48h.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_story_48h.png new file mode 100644 index 0000000000..b24b062f43 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_story_48h.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_story_6h.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_story_6h.png new file mode 100644 index 0000000000..fb80422726 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_story_6h.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_story_infinite.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_story_infinite.png new file mode 100644 index 0000000000..4b68f913fe Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_story_infinite.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_story_once.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_story_once.png new file mode 100644 index 0000000000..8ebdcd01f3 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_story_once.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_story_once_filled1.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_story_once_filled1.png new file mode 100644 index 0000000000..9663cc64c9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_story_once_filled1.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_tabs_camera_filled.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_tabs_camera_filled.png new file mode 100644 index 0000000000..b60a9cb6ee Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_tabs_camera_filled.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_tabs_media.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_tabs_media.png new file mode 100644 index 0000000000..59106e6d4d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_tabs_media.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_tabs_stories_archive.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_tabs_stories_archive.png new file mode 100644 index 0000000000..279458a422 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_tabs_stories_archive.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_tabs_stories_saved.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_tabs_stories_saved.png new file mode 100644 index 0000000000..947fbbd944 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_tabs_stories_saved.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_tabs_stories_saved2.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_tabs_stories_saved2.png new file mode 100644 index 0000000000..54044b423a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_tabs_stories_saved2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/proxy_off.png b/TMessagesProj/src/main/res/drawable-xhdpi/proxy_off.png deleted file mode 100644 index 2b9910bb28..0000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/proxy_off.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/proxy_on.png b/TMessagesProj/src/main/res/drawable-xhdpi/proxy_on.png deleted file mode 100644 index 0c331af7c9..0000000000 Binary files a/TMessagesProj/src/main/res/drawable-xhdpi/proxy_on.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/shadow_story_bottom.9.png b/TMessagesProj/src/main/res/drawable-xhdpi/shadow_story_bottom.9.png new file mode 100644 index 0000000000..e136dc48cb Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/shadow_story_bottom.9.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/shadow_story_top.9.png b/TMessagesProj/src/main/res/drawable-xhdpi/shadow_story_top.9.png new file mode 100644 index 0000000000..c3f0a712ec Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/shadow_story_top.9.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/fab_compose_small.png b/TMessagesProj/src/main/res/drawable-xxhdpi/fab_compose_small.png new file mode 100644 index 0000000000..64a3c85277 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/fab_compose_small.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/large_archive.png b/TMessagesProj/src/main/res/drawable-xxhdpi/large_archive.png new file mode 100644 index 0000000000..81fd21f926 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/large_archive.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/media_download.png b/TMessagesProj/src/main/res/drawable-xxhdpi/media_download.png new file mode 100644 index 0000000000..3d81c4bb88 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/media_download.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/media_dual_camera2.png b/TMessagesProj/src/main/res/drawable-xxhdpi/media_dual_camera2.png new file mode 100644 index 0000000000..aca1bedc37 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/media_dual_camera2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/media_dual_camera2_shadow.png b/TMessagesProj/src/main/res/drawable-xxhdpi/media_dual_camera2_shadow.png new file mode 100644 index 0000000000..947a7f181d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/media_dual_camera2_shadow.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/media_more.png b/TMessagesProj/src/main/res/drawable-xxhdpi/media_more.png new file mode 100644 index 0000000000..0a846ccf7c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/media_more.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/media_mute.png b/TMessagesProj/src/main/res/drawable-xxhdpi/media_mute.png new file mode 100644 index 0000000000..e238152bd7 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/media_mute.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/media_photo_flash_auto2.png b/TMessagesProj/src/main/res/drawable-xxhdpi/media_photo_flash_auto2.png new file mode 100644 index 0000000000..3fa06945a9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/media_photo_flash_auto2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/media_photo_flash_off2.png b/TMessagesProj/src/main/res/drawable-xxhdpi/media_photo_flash_off2.png new file mode 100644 index 0000000000..1f256fee58 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/media_photo_flash_off2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/media_photo_flash_on2.png b/TMessagesProj/src/main/res/drawable-xxhdpi/media_photo_flash_on2.png new file mode 100644 index 0000000000..8f5c72687a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/media_photo_flash_on2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/media_share.png b/TMessagesProj/src/main/res/drawable-xxhdpi/media_share.png new file mode 100644 index 0000000000..34d8f5c3c2 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/media_share.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/media_unmute.png b/TMessagesProj/src/main/res/drawable-xxhdpi/media_unmute.png new file mode 100644 index 0000000000..c1c2a8b26b Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/media_unmute.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg2_chats_add.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg2_chats_add.png new file mode 100644 index 0000000000..de025e4181 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg2_chats_add.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg2_link2.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg2_link2.png new file mode 100644 index 0000000000..ee1e90c6f8 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg2_link2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg2_night_auto.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg2_night_auto.png new file mode 100644 index 0000000000..4785d8293d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg2_night_auto.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg2_proxy_off.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg2_proxy_off.png new file mode 100644 index 0000000000..317d95eb32 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg2_proxy_off.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg2_proxy_on.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg2_proxy_on.png new file mode 100644 index 0000000000..dcc00ab34f Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg2_proxy_on.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_archive_archive.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_archive_archive.png new file mode 100644 index 0000000000..2db50e7c2a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_archive_archive.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_archive_hide.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_archive_hide.png index 624241d389..5bd0e4c635 100644 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_archive_hide.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_archive_hide.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_archive_stories.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_archive_stories.png new file mode 100644 index 0000000000..e497fa9ca9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_archive_stories.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_autodelete_2d.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_autodelete_2d.png new file mode 100644 index 0000000000..d8943a4f06 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_autodelete_2d.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_button_back.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_button_back.png new file mode 100644 index 0000000000..aa0d2b8438 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_button_back.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_button_colors.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_button_colors.png new file mode 100644 index 0000000000..dd85a4d22e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_button_colors.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_button_wallpaper.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_button_wallpaper.png new file mode 100644 index 0000000000..f46a28217a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_button_wallpaper.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_draw_more.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_draw_more.png new file mode 100644 index 0000000000..a53d9f35f2 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_draw_more.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_draw_pen.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_draw_pen.png index db96c2c6a4..ced355d355 100644 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_draw_pen.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_draw_pen.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_filled_lockedrecord.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_filled_lockedrecord.png new file mode 100644 index 0000000000..0049e1b69f Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_filled_lockedrecord.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_filled_stories.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_filled_stories.png new file mode 100644 index 0000000000..82d9b83ff1 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_filled_stories.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_filled_story_share.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_filled_story_share.png new file mode 100644 index 0000000000..956b226840 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_filled_story_share.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_filled_unlockedrecord.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_filled_unlockedrecord.png new file mode 100644 index 0000000000..68e0081a67 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_filled_unlockedrecord.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_header_draw.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_header_draw.png new file mode 100644 index 0000000000..27d9b44c2b Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_header_draw.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_header_qr.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_header_qr.png new file mode 100644 index 0000000000..b9fc07e282 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_header_qr.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_header_share.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_header_share.png new file mode 100644 index 0000000000..c1de1fe9f4 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_header_share.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_hidetocontacts.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_hidetocontacts.png new file mode 100644 index 0000000000..448d1e7578 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_hidetocontacts.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_input_attach2.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_input_attach2.png new file mode 100644 index 0000000000..d07e0a006a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_input_attach2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_input_like.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_input_like.png new file mode 100644 index 0000000000..2390f22e5b Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_input_like.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_link_folder.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_link_folder.png new file mode 100644 index 0000000000..859cfc6ee6 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_link_folder.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_menu_stories.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_menu_stories.png new file mode 100644 index 0000000000..b263b3e3d1 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_menu_stories.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_mini_addstory.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_mini_addstory.png new file mode 100644 index 0000000000..c783946170 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_mini_addstory.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_mini_arrow_mediabold.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_mini_arrow_mediabold.png new file mode 100644 index 0000000000..88a08b298a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_mini_arrow_mediabold.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_mini_arrow_mediathin.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_mini_arrow_mediathin.png new file mode 100644 index 0000000000..aa4ee11df5 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_mini_arrow_mediathin.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_mini_bomb.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_mini_bomb.png new file mode 100644 index 0000000000..5df65db9fa Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_mini_bomb.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_mini_close_tooltip.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_mini_close_tooltip.png new file mode 100644 index 0000000000..edb3f2dbeb Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_mini_close_tooltip.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_mini_closefriends.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_mini_closefriends.png new file mode 100644 index 0000000000..aa9a1da359 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_mini_closefriends.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_mini_replystory.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_mini_replystory.png new file mode 100644 index 0000000000..b269ed9748 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_mini_replystory.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_mini_replystory2.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_mini_replystory2.png new file mode 100644 index 0000000000..4c438750f6 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_mini_replystory2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_online.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_online.png new file mode 100644 index 0000000000..c096f8131e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_online.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_other_new_filled.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_other_new_filled.png new file mode 100644 index 0000000000..98b17d142b Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_other_new_filled.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_other_new_filled_text.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_other_new_filled_text.png new file mode 100644 index 0000000000..9a7b376052 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_other_new_filled_text.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_other_new_outline.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_other_new_outline.png new file mode 100644 index 0000000000..de814959b4 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_other_new_outline.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_back.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_back.png new file mode 100644 index 0000000000..0018227391 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_back.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_clearvideo.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_clearvideo.png new file mode 100644 index 0000000000..41ea3e5f3d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_clearvideo.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_close.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_close.png new file mode 100644 index 0000000000..a686acf5d6 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_close.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_flash_auto.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_flash_auto.png new file mode 100644 index 0000000000..54467fc328 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_flash_auto.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_flash_off.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_flash_off.png new file mode 100644 index 0000000000..aec44d4c8d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_flash_off.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_flash_on.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_flash_on.png new file mode 100644 index 0000000000..db8a32b0e5 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_flash_on.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_settings.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_settings.png index 79d60a1e7c..f69066fe1d 100644 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_settings.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_settings.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_sticker.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_sticker.png new file mode 100644 index 0000000000..706da61bde Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_sticker.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_switch2.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_switch2.png new file mode 100644 index 0000000000..5745f2e398 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_switch2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_text2.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_text2.png new file mode 100644 index 0000000000..2f81fb788f Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_text2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_text_framed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_text_framed.png new file mode 100644 index 0000000000..9b2bc19087 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_text_framed.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_text_framed2.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_text_framed2.png new file mode 100644 index 0000000000..8f8554339a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_text_framed2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_text_framed3.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_text_framed3.png new file mode 100644 index 0000000000..48a93949ef Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_text_framed3.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_text_regular.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_text_regular.png new file mode 100644 index 0000000000..dc364a070e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_photo_text_regular.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_save_story.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_save_story.png new file mode 100644 index 0000000000..cd77461bd3 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_save_story.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_share_filled.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_share_filled.png index c18a5d02ba..8a29131cad 100644 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_share_filled.png and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_share_filled.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_status_edit.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_status_edit.png new file mode 100644 index 0000000000..95fb5fc288 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_status_edit.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_status_set.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_status_set.png new file mode 100644 index 0000000000..fbd282152d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_status_set.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_stories_add.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_stories_add.png new file mode 100644 index 0000000000..82bccf2d72 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_stories_add.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_stories_archive.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_stories_archive.png new file mode 100644 index 0000000000..ac5cd68a32 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_stories_archive.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_stories_closefriends.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_stories_closefriends.png new file mode 100644 index 0000000000..5f2597fcf5 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_stories_closefriends.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_stories_saved.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_stories_saved.png new file mode 100644 index 0000000000..312e8bf53c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_stories_saved.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_story_12h.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_story_12h.png new file mode 100644 index 0000000000..2974959fca Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_story_12h.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_story_24h.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_story_24h.png new file mode 100644 index 0000000000..b83ba17953 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_story_24h.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_story_48h.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_story_48h.png new file mode 100644 index 0000000000..6d90ce95c1 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_story_48h.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_story_6h.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_story_6h.png new file mode 100644 index 0000000000..16fb36620a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_story_6h.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_story_infinite.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_story_infinite.png new file mode 100644 index 0000000000..84cbf8f8c5 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_story_infinite.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_story_once.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_story_once.png new file mode 100644 index 0000000000..77c7624c33 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_story_once.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_story_once_filled1.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_story_once_filled1.png new file mode 100644 index 0000000000..b1b976285d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_story_once_filled1.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_tabs_camera_filled.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_tabs_camera_filled.png new file mode 100644 index 0000000000..741407626e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_tabs_camera_filled.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_tabs_media.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_tabs_media.png new file mode 100644 index 0000000000..7600964254 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_tabs_media.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_tabs_stories_archive.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_tabs_stories_archive.png new file mode 100644 index 0000000000..434d1dc46b Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_tabs_stories_archive.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_tabs_stories_saved.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_tabs_stories_saved.png new file mode 100644 index 0000000000..96724f739a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_tabs_stories_saved.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_tabs_stories_saved2.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_tabs_stories_saved2.png new file mode 100644 index 0000000000..19a1ff07b6 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_tabs_stories_saved2.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/proxy_off.png b/TMessagesProj/src/main/res/drawable-xxhdpi/proxy_off.png deleted file mode 100644 index cb8c194401..0000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/proxy_off.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/proxy_on.png b/TMessagesProj/src/main/res/drawable-xxhdpi/proxy_on.png deleted file mode 100644 index 88b1297d2b..0000000000 Binary files a/TMessagesProj/src/main/res/drawable-xxhdpi/proxy_on.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/shadow_story_bottom.9.png b/TMessagesProj/src/main/res/drawable-xxhdpi/shadow_story_bottom.9.png new file mode 100644 index 0000000000..bae2636961 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/shadow_story_bottom.9.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/shadow_story_top.9.png b/TMessagesProj/src/main/res/drawable-xxhdpi/shadow_story_top.9.png new file mode 100644 index 0000000000..3d04938e5b Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/shadow_story_top.9.png differ diff --git a/TMessagesProj/src/main/res/drawable/msg_media_gallery.png b/TMessagesProj/src/main/res/drawable/msg_media_gallery.png new file mode 100644 index 0000000000..9f38842442 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable/msg_media_gallery.png differ diff --git a/TMessagesProj/src/main/res/drawable/story_camera.png b/TMessagesProj/src/main/res/drawable/story_camera.png new file mode 100644 index 0000000000..79a9d54d1c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable/story_camera.png differ diff --git a/TMessagesProj/src/main/res/raw/camera_frag.glsl b/TMessagesProj/src/main/res/raw/camera_frag.glsl new file mode 100644 index 0000000000..08ef4b1ca7 --- /dev/null +++ b/TMessagesProj/src/main/res/raw/camera_frag.glsl @@ -0,0 +1,86 @@ +#extension GL_OES_EGL_image_external : require + +precision lowp float; + +varying vec2 uv; +uniform samplerExternalOES sTexture; +uniform vec2 pixelWH; +uniform float roundRadius; +uniform float scale; +uniform float alpha; +uniform float shapeFrom, shapeTo, shapeT; +uniform float dual; +uniform float blur; + +float modI(float a,float b) { + return floor(a-floor((a+0.5)/b)*b+0.5); +} +bool eq(float a, float b) { + return abs(a - b) < .1; +} +bool eq(float a, float b, float b2) { + return abs(a - b) < .1 || abs(a - b2) < .1; +} +float box(vec2 position, vec2 halfSize, float cornerRadius) { + position = abs(position) - halfSize + cornerRadius; + return length(max(position, 0.0)) + min(max(position.x, position.y), 0.0) - cornerRadius; +} +float star(in vec2 p, in float r) { + const vec2 acs = vec2(.9659258, .258819); + const vec2 ecs = vec2(.8090169, .5877852); + float bn = mod(atan(p.x,p.y),.52359876)-.26179938; + p = length(p)*vec2(cos(bn),abs(sin(bn))) - r*acs; + p += ecs*clamp( -dot(p,ecs), 0.0, r*acs.y/ecs.y); + return length(p)*sign(p.x); +} +float opSmoothUnion(float d1, float d2, float k) { + float h = max(k-abs(d1-d2),0.0); + return min(d1, d2) - h*h*0.25/k; +} +float scene() { + vec2 p = (uv - vec2(.5)) * vec2(1., pixelWH.x / pixelWH.y); + vec2 r = .5 * vec2(1., pixelWH.x / pixelWH.y) * scale; + float R = min(r.x, r.y), rr = roundRadius / pixelWH.y; + float a = modI(shapeFrom, 3.), b = modI(shapeTo, 3.); + float boxSDF = box( + p, + mix(eq(a, 2.) ? r : vec2(R), eq(b, 2.) ? r : vec2(R), shapeT), + mix(eq(a, 0., 3.) ? R : rr, eq(b, 0., 3.) ? R : rr, shapeT) + ) * pixelWH.x; + if (eq(3., a, b)) { + float starSDF = opSmoothUnion(box(p, vec2(R * .78), R), star(p, R * .78), .25) * pixelWH.x; + float starA = eq(a, 3.) ? 1. - shapeT : 0.; + float starB = eq(b, 3.) ? shapeT : 0.; + return mix(boxSDF, starSDF, starA + starB); + } else { + return boxSDF; + } +} +vec4 makeblur() { + vec2 S = 4. * vec2(1., pixelWH.x / pixelWH.y); + vec2 st = fract(uv * S); + st=st*st*(3.0-2.0*st); + vec2 u = floor(uv * S) / S; + return mix( + mix(texture2D(sTexture, u + vec2(0., 0.) / S), texture2D(sTexture, u + vec2(1., 0.) / S), st.x), + mix(texture2D(sTexture, u + vec2(0., 1.) / S), texture2D(sTexture, u + vec2(1., 1.) / S), st.x), + st.y + ); +} +vec4 program() { + if (scale <= 0.) + return vec4(0.); + float dalpha = clamp(1. - scene(), 0., 2.) / 2. * alpha; + if (dalpha <= 0.) + return vec4(0.); + + if (blur >= 1.) + return makeblur(); + else if (blur <= 0.) + return texture2D(sTexture, uv) * vec4(1., 1., 1., dalpha); + else + return mix(texture2D(sTexture, uv), makeblur(), blur) * vec4(1., 1., 1., dalpha); +} +void main() { + gl_FragColor = dual < .5 ? texture2D(sTexture, uv) : program(); +} \ No newline at end of file diff --git a/TMessagesProj/src/main/res/raw/camera_vert.glsl b/TMessagesProj/src/main/res/raw/camera_vert.glsl new file mode 100644 index 0000000000..48dd0145ec --- /dev/null +++ b/TMessagesProj/src/main/res/raw/camera_vert.glsl @@ -0,0 +1,16 @@ +uniform mat4 uMVPMatrix; +uniform mat4 uSTMatrix; +attribute vec4 aPosition; +attribute vec4 aTextureCoord; +varying vec2 uv; +uniform float crossfade; +uniform mat4 cameraMatrix; +uniform mat4 oppositeCameraMatrix; +mat4 mix(mat4 a, mat4 b, float t) { + return a * (1. - t) + b * t; +} +void main() { + mat4 matrix = mix(cameraMatrix, oppositeCameraMatrix, crossfade); + gl_Position = uMVPMatrix * matrix * aPosition; + uv = (uSTMatrix * aTextureCoord).xy; +} \ No newline at end of file diff --git a/TMessagesProj/src/main/res/raw/emoji_status_change_to_set.tgs b/TMessagesProj/src/main/res/raw/emoji_status_change_to_set.tgs deleted file mode 100644 index 5a3f05e89c..0000000000 --- a/TMessagesProj/src/main/res/raw/emoji_status_change_to_set.tgs +++ /dev/null @@ -1 +0,0 @@ -{"tgs":1,"v":"5.9.6","fr":60,"ip":0,"op":60,"w":84,"h":84,"nm":"change_to_set","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"A","parent":7,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.4,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[29.196,15.099,0],"to":[0.667,-0.667,0],"ti":[-0.667,0.667,0]},{"t":15,"s":[25.196,19.099,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.4,0.4,0.4],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"t":15,"s":[0,0,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.522,0.091],[0,0],[0.699,0.48],[0,0],[-0.147,-0.827],[0,0]],"o":[[0,0],[0.842,-0.147],[0,0],[-0.699,-0.48],[0,0],[0.091,0.513]],"v":[[-5.136,7.235],[7.705,4.986],[8.092,3.285],[-6.988,-7.078],[-8.485,-6.139],[-6.246,6.472]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":1,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":6,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector 43","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"line","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[40.5,53.25,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[5.193,0],[2.815,3.797]],"o":[[-2.801,3.934],[-5.085,0],[0,0]],"v":[[12.5,-3.25],[-0.088,3.25],[-12.5,-3.008]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4.8,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"oval","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[51,36,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":10,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":15,"s":[100,20,100]},{"t":20,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-2.247,0],[0,-2.669],[2.247,0],[0,2.669]],"o":[[2.247,0],[0,2.669],[-2.247,0],[0,-2.669]],"v":[[0,-4.75],[4,0],[0,4.75],[-4,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Oval Copy","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"oval","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[30,35.75,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":10,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":15,"s":[100,20,100]},{"t":20,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-2.247,0],[0,-2.669],[2.247,0],[0,2.669]],"o":[[2.247,0],[0,2.669],[-2.247,0],[0,-2.669]],"v":[[0,-4.75],[4,0],[0,4.75],[-4,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Oval","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"line","parent":7,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25,19,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.4,0.4,0.4],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":0,"s":[50,50,100]},{"t":15,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,10],[0,-10]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":0,"s":[50]},{"t":15,"s":[0]}],"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector 42","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"line","parent":7,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25,19,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.4,0.4,0.4],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":0,"s":[50,50,100]},{"t":15,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,10],[0,-10]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":0,"s":[50]},{"t":15,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":3,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":-90,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector 41","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"oval","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.741]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.87]},"o":{"x":[0.167],"y":[0.123]},"t":1,"s":[22.159]},{"i":{"x":[0.833],"y":[0.752]},"o":{"x":[0.167],"y":[0.231]},"t":2,"s":[68.835]},{"i":{"x":[0.833],"y":[0.842]},"o":{"x":[0.167],"y":[0.126]},"t":3,"s":[95.127]},{"i":{"x":[0.833],"y":[0.847]},"o":{"x":[0.167],"y":[0.176]},"t":4,"s":[146.94]},{"i":{"x":[0.833],"y":[0.881]},"o":{"x":[0.167],"y":[0.183]},"t":5,"s":[193.379]},{"i":{"x":[0.833],"y":[0.777]},"o":{"x":[0.167],"y":[0.279]},"t":6,"s":[232.355]},{"i":{"x":[0.833],"y":[0.882]},"o":{"x":[0.167],"y":[0.133]},"t":7,"s":[248.961]},{"i":{"x":[0.833],"y":[0.78]},"o":{"x":[0.167],"y":[0.285]},"t":8,"s":[276.812]},{"i":{"x":[0.833],"y":[0.854]},"o":{"x":[0.167],"y":[0.134]},"t":9,"s":[288.307]},{"i":{"x":[0.833],"y":[0.883]},"o":{"x":[0.167],"y":[0.194]},"t":10,"s":[307.137]},{"i":{"x":[0.833],"y":[0.783]},"o":{"x":[0.167],"y":[0.291]},"t":11,"s":[321.343]},{"i":{"x":[0.833],"y":[0.855]},"o":{"x":[0.167],"y":[0.135]},"t":12,"s":[327.032]},{"i":{"x":[0.833],"y":[0.884]},"o":{"x":[0.167],"y":[0.197]},"t":13,"s":[336.133]},{"i":{"x":[0.833],"y":[0.785]},"o":{"x":[0.167],"y":[0.295]},"t":14,"s":[342.816]},{"i":{"x":[0.833],"y":[0.856]},"o":{"x":[0.167],"y":[0.136]},"t":15,"s":[345.446]},{"i":{"x":[0.833],"y":[0.884]},"o":{"x":[0.167],"y":[0.199]},"t":16,"s":[349.594]},{"i":{"x":[0.833],"y":[0.786]},"o":{"x":[0.167],"y":[0.298]},"t":17,"s":[352.589]},{"i":{"x":[0.833],"y":[0.857]},"o":{"x":[0.167],"y":[0.137]},"t":18,"s":[353.754]},{"i":{"x":[0.833],"y":[0.885]},"o":{"x":[0.167],"y":[0.2]},"t":19,"s":[355.574]},{"i":{"x":[0.833],"y":[0.787]},"o":{"x":[0.167],"y":[0.299]},"t":20,"s":[356.873]},{"i":{"x":[0.833],"y":[0.858]},"o":{"x":[0.167],"y":[0.137]},"t":21,"s":[357.374]},{"i":{"x":[0.833],"y":[0.885]},"o":{"x":[0.167],"y":[0.201]},"t":22,"s":[358.152]},{"i":{"x":[0.833],"y":[0.788]},"o":{"x":[0.167],"y":[0.301]},"t":23,"s":[358.702]},{"i":{"x":[0.833],"y":[0.858]},"o":{"x":[0.167],"y":[0.137]},"t":24,"s":[358.913]},{"i":{"x":[0.833],"y":[0.885]},"o":{"x":[0.167],"y":[0.202]},"t":25,"s":[359.239]},{"i":{"x":[0.833],"y":[0.789]},"o":{"x":[0.167],"y":[0.302]},"t":26,"s":[359.468]},{"i":{"x":[0.833],"y":[0.858]},"o":{"x":[0.167],"y":[0.138]},"t":27,"s":[359.556]},{"i":{"x":[0.833],"y":[0.885]},"o":{"x":[0.167],"y":[0.202]},"t":28,"s":[359.69]},{"i":{"x":[0.833],"y":[0.789]},"o":{"x":[0.167],"y":[0.302]},"t":29,"s":[359.784]},{"i":{"x":[0.833],"y":[0.859]},"o":{"x":[0.167],"y":[0.138]},"t":30,"s":[359.82]},{"i":{"x":[0.833],"y":[0.885]},"o":{"x":[0.167],"y":[0.203]},"t":31,"s":[359.875]},{"i":{"x":[0.833],"y":[0.789]},"o":{"x":[0.167],"y":[0.303]},"t":32,"s":[359.913]},{"i":{"x":[0.833],"y":[0.859]},"o":{"x":[0.167],"y":[0.138]},"t":33,"s":[359.928]},{"i":{"x":[0.833],"y":[0.885]},"o":{"x":[0.167],"y":[0.203]},"t":34,"s":[359.95]},{"i":{"x":[0.833],"y":[0.79]},"o":{"x":[0.167],"y":[0.304]},"t":35,"s":[359.965]},{"i":{"x":[0.833],"y":[0.859]},"o":{"x":[0.167],"y":[0.138]},"t":36,"s":[359.971]},{"i":{"x":[0.833],"y":[0.885]},"o":{"x":[0.167],"y":[0.204]},"t":37,"s":[359.98]},{"i":{"x":[0.833],"y":[0.79]},"o":{"x":[0.167],"y":[0.304]},"t":38,"s":[359.986]},{"i":{"x":[0.833],"y":[0.859]},"o":{"x":[0.167],"y":[0.138]},"t":39,"s":[359.989]},{"i":{"x":[0.833],"y":[0.885]},"o":{"x":[0.167],"y":[0.204]},"t":40,"s":[359.992]},{"i":{"x":[0.833],"y":[0.79]},"o":{"x":[0.167],"y":[0.304]},"t":41,"s":[359.995]},{"i":{"x":[0.833],"y":[0.859]},"o":{"x":[0.167],"y":[0.138]},"t":42,"s":[359.996]},{"i":{"x":[0.833],"y":[0.885]},"o":{"x":[0.167],"y":[0.204]},"t":43,"s":[359.997]},{"i":{"x":[0.833],"y":[0.79]},"o":{"x":[0.167],"y":[0.305]},"t":44,"s":[359.998]},{"i":{"x":[0.833],"y":[0.859]},"o":{"x":[0.167],"y":[0.138]},"t":45,"s":[359.998]},{"i":{"x":[0.833],"y":[0.885]},"o":{"x":[0.167],"y":[0.204]},"t":46,"s":[359.999]},{"i":{"x":[0.833],"y":[0.79]},"o":{"x":[0.167],"y":[0.305]},"t":47,"s":[359.999]},{"i":{"x":[0.833],"y":[0.859]},"o":{"x":[0.167],"y":[0.138]},"t":48,"s":[359.999]},{"i":{"x":[0.833],"y":[0.885]},"o":{"x":[0.167],"y":[0.205]},"t":49,"s":[360]},{"i":{"x":[0.833],"y":[0.79]},"o":{"x":[0.167],"y":[0.305]},"t":50,"s":[360]},{"i":{"x":[0.833],"y":[0.86]},"o":{"x":[0.167],"y":[0.138]},"t":51,"s":[360]},{"i":{"x":[0.833],"y":[0.885]},"o":{"x":[0.167],"y":[0.205]},"t":52,"s":[360]},{"i":{"x":[0.833],"y":[0.791]},"o":{"x":[0.167],"y":[0.306]},"t":53,"s":[360]},{"i":{"x":[0.833],"y":[0.86]},"o":{"x":[0.167],"y":[0.138]},"t":54,"s":[360]},{"i":{"x":[0.833],"y":[0.885]},"o":{"x":[0.167],"y":[0.205]},"t":55,"s":[360]},{"i":{"x":[0.833],"y":[0.791]},"o":{"x":[0.167],"y":[0.306]},"t":56,"s":[360]},{"i":{"x":[0.833],"y":[0.86]},"o":{"x":[0.167],"y":[0.138]},"t":57,"s":[360]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.205]},"t":58,"s":[360]},{"t":59,"s":[360]}],"ix":10},"p":{"a":0,"k":[40.5,41.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-17.978,0],[0,-17.978],[17.978,0],[0,17.978]],"o":[[17.978,0],[0,17.978],[-17.978,0],[0,-17.978]],"v":[[0,-32],[32,0],[0,32],[-32,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":0,"s":[9]},{"t":15,"s":[19.5]}],"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":0,"s":[116]},{"t":15,"s":[90]}],"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Oval","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"ct":1,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/TMessagesProj/src/main/res/raw/emoji_status_set_to_change.tgs b/TMessagesProj/src/main/res/raw/emoji_status_set_to_change.tgs deleted file mode 100644 index e467ca53cd..0000000000 --- a/TMessagesProj/src/main/res/raw/emoji_status_set_to_change.tgs +++ /dev/null @@ -1 +0,0 @@ -{"tgs":1,"v":"5.9.6","fr":60,"ip":0,"op":60,"w":84,"h":84,"nm":"set_to_change","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"A","parent":7,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.4,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[25.196,19.099,0],"to":[0.667,-0.667,0],"ti":[-0.667,0.667,0]},{"t":15,"s":[29.196,15.099,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.4,0.4,0.4],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":0,"s":[0,0,100]},{"t":15,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.522,0.091],[0,0],[0.699,0.48],[0,0],[-0.147,-0.827],[0,0]],"o":[[0,0],[0.842,-0.147],[0,0],[-0.699,-0.48],[0,0],[0.091,0.513]],"v":[[-5.136,7.235],[7.705,4.986],[8.092,3.285],[-6.988,-7.078],[-8.485,-6.139],[-6.246,6.472]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"rd","nm":"Round Corners 1","r":{"a":0,"k":1,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":6,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector 43","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"line","parent":7,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,11.75,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[5.193,0],[2.815,3.797]],"o":[[-2.801,3.934],[-5.085,0],[0,0]],"v":[[12.5,-3.25],[-0.088,3.25],[-12.5,-3.008]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4.8,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Path","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"oval","parent":7,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[10.5,-5.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":10,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":15,"s":[100,20,100]},{"t":20,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-2.247,0],[0,-2.669],[2.247,0],[0,2.669]],"o":[[2.247,0],[0,2.669],[-2.247,0],[0,-2.669]],"v":[[0,-4.75],[4,0],[0,4.75],[-4,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Oval Copy","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"oval","parent":7,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-10.5,-5.75,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":10,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":15,"s":[100,20,100]},{"t":20,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-2.247,0],[0,-2.669],[2.247,0],[0,2.669]],"o":[[2.247,0],[0,2.669],[-2.247,0],[0,-2.669]],"v":[[0,-4.75],[4,0],[0,4.75],[-4,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Oval","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"line","parent":7,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25,19,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.4,0.4,0.4],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"t":15,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,10],[0,-10]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":0,"s":[0]},{"t":15,"s":[50]}],"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector 42","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"line","parent":7,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25,19,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.4,0.4,0.4],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"t":15,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0,10],[0,-10]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":0,"s":[100]},{"t":15,"s":[50]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":3,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":-90,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector 41","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"oval","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":1,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":2,"s":[0]},{"i":{"x":[0.833],"y":[1.031]},"o":{"x":[0.167],"y":[0]},"t":3,"s":[0]},{"i":{"x":[0.833],"y":[0.355]},"o":{"x":[0.167],"y":[0.083]},"t":4,"s":[0]},{"i":{"x":[0.833],"y":[0.855]},"o":{"x":[0.167],"y":[0.096]},"t":5,"s":[-0.373]},{"i":{"x":[0.833],"y":[0.726]},"o":{"x":[0.167],"y":[0.197]},"t":6,"s":[-2.892]},{"i":{"x":[0.833],"y":[0.874]},"o":{"x":[0.167],"y":[0.12]},"t":7,"s":[-4.741]},{"i":{"x":[0.833],"y":[0.775]},"o":{"x":[0.167],"y":[0.246]},"t":8,"s":[-8.974]},{"i":{"x":[0.833],"y":[0.898]},"o":{"x":[0.167],"y":[0.132]},"t":9,"s":[-11.147]},{"i":{"x":[0.833],"y":[0.968]},"o":{"x":[0.167],"y":[0.455]},"t":10,"s":[-14.835]},{"i":{"x":[0.833],"y":[0.548]},"o":{"x":[0.167],"y":[-0.051]},"t":11,"s":[-15.663]},{"i":{"x":[0.833],"y":[0.799]},"o":{"x":[0.167],"y":[0.102]},"t":12,"s":[-15.148]},{"i":{"x":[0.833],"y":[0.872]},"o":{"x":[0.167],"y":[0.143]},"t":13,"s":[-12.874]},{"i":{"x":[0.833],"y":[0.756]},"o":{"x":[0.167],"y":[0.239]},"t":14,"s":[-9.675]},{"i":{"x":[0.833],"y":[0.845]},"o":{"x":[0.167],"y":[0.127]},"t":15,"s":[-7.961]},{"i":{"x":[0.833],"y":[0.882]},"o":{"x":[0.167],"y":[0.181]},"t":16,"s":[-4.66]},{"i":{"x":[0.833],"y":[0.79]},"o":{"x":[0.167],"y":[0.286]},"t":17,"s":[-1.841]},{"i":{"x":[0.833],"y":[0.867]},"o":{"x":[0.167],"y":[0.138]},"t":18,"s":[-0.684]},{"i":{"x":[0.833],"y":[0.894]},"o":{"x":[0.167],"y":[0.224]},"t":19,"s":[1.074]},{"i":{"x":[0.833],"y":[0.867]},"o":{"x":[0.167],"y":[0.393]},"t":20,"s":[2.112]},{"i":{"x":[0.833],"y":[1.027]},"o":{"x":[0.167],"y":[0.223]},"t":21,"s":[2.391]},{"i":{"x":[0.833],"y":[0.839]},"o":{"x":[0.167],"y":[0.02]},"t":22,"s":[2.558]},{"i":{"x":[0.833],"y":[0.709]},"o":{"x":[0.167],"y":[0.173]},"t":23,"s":[2.337]},{"i":{"x":[0.833],"y":[0.827]},"o":{"x":[0.167],"y":[0.117]},"t":24,"s":[2.131]},{"i":{"x":[0.833],"y":[0.877]},"o":{"x":[0.167],"y":[0.161]},"t":25,"s":[1.616]},{"i":{"x":[0.833],"y":[0.77]},"o":{"x":[0.167],"y":[0.258]},"t":26,"s":[1.06]},{"i":{"x":[0.833],"y":[0.853]},"o":{"x":[0.167],"y":[0.131]},"t":27,"s":[0.795]},{"i":{"x":[0.833],"y":[0.886]},"o":{"x":[0.167],"y":[0.193]},"t":28,"s":[0.328]},{"i":{"x":[0.833],"y":[0.805]},"o":{"x":[0.167],"y":[0.307]},"t":29,"s":[-0.027]},{"i":{"x":[0.833],"y":[0.881]},"o":{"x":[0.167],"y":[0.146]},"t":30,"s":[-0.159]},{"i":{"x":[0.833],"y":[0.909]},"o":{"x":[0.167],"y":[0.28]},"t":31,"s":[-0.336]},{"i":{"x":[0.833],"y":[1.334]},"o":{"x":[0.167],"y":[1.045]},"t":32,"s":[-0.411]},{"i":{"x":[0.833],"y":[0.732]},"o":{"x":[0.167],"y":[0.067]},"t":33,"s":[-0.418]},{"i":{"x":[0.833],"y":[0.866]},"o":{"x":[0.167],"y":[0.121]},"t":34,"s":[-0.385]},{"i":{"x":[0.833],"y":[0.743]},"o":{"x":[0.167],"y":[0.221]},"t":35,"s":[-0.313]},{"i":{"x":[0.833],"y":[0.839]},"o":{"x":[0.167],"y":[0.123]},"t":36,"s":[-0.27]},{"i":{"x":[0.833],"y":[0.88]},"o":{"x":[0.167],"y":[0.173]},"t":37,"s":[-0.179]},{"i":{"x":[0.833],"y":[0.782]},"o":{"x":[0.167],"y":[0.275]},"t":38,"s":[-0.095]},{"i":{"x":[0.833],"y":[0.861]},"o":{"x":[0.167],"y":[0.135]},"t":39,"s":[-0.058]},{"i":{"x":[0.833],"y":[0.89]},"o":{"x":[0.167],"y":[0.208]},"t":40,"s":[0.001]},{"i":{"x":[0.833],"y":[0.83]},"o":{"x":[0.167],"y":[0.342]},"t":41,"s":[0.041]},{"i":{"x":[0.833],"y":[0.915]},"o":{"x":[0.167],"y":[0.164]},"t":42,"s":[0.053]},{"i":{"x":[0.833],"y":[2.306]},"o":{"x":[0.167],"y":[5.423]},"t":43,"s":[0.067]},{"i":{"x":[0.833],"y":[0.641]},"o":{"x":[0.167],"y":[0.078]},"t":44,"s":[0.067]},{"i":{"x":[0.833],"y":[0.811]},"o":{"x":[0.167],"y":[0.109]},"t":45,"s":[0.063]},{"i":{"x":[0.833],"y":[0.874]},"o":{"x":[0.167],"y":[0.149]},"t":46,"s":[0.052]},{"i":{"x":[0.833],"y":[0.761]},"o":{"x":[0.167],"y":[0.246]},"t":47,"s":[0.038]},{"i":{"x":[0.833],"y":[0.848]},"o":{"x":[0.167],"y":[0.128]},"t":48,"s":[0.03]},{"i":{"x":[0.833],"y":[0.883]},"o":{"x":[0.167],"y":[0.184]},"t":49,"s":[0.016]},{"i":{"x":[0.833],"y":[0.794]},"o":{"x":[0.167],"y":[0.292]},"t":50,"s":[0.005]},{"i":{"x":[0.833],"y":[0.871]},"o":{"x":[0.167],"y":[0.14]},"t":51,"s":[0]},{"i":{"x":[0.833],"y":[0.897]},"o":{"x":[0.167],"y":[0.235]},"t":52,"s":[-0.006]},{"i":{"x":[0.833],"y":[0.901]},"o":{"x":[0.167],"y":[0.441]},"t":53,"s":[-0.01]},{"i":{"x":[0.833],"y":[1.612]},"o":{"x":[0.167],"y":[0.532]},"t":54,"s":[-0.011]},{"i":{"x":[0.833],"y":[0.853]},"o":{"x":[0.167],"y":[0.073]},"t":55,"s":[-0.011]},{"i":{"x":[0.833],"y":[0.723]},"o":{"x":[0.167],"y":[0.193]},"t":56,"s":[-0.01]},{"i":{"x":[0.833],"y":[0.832]},"o":{"x":[0.167],"y":[0.119]},"t":57,"s":[-0.009]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.165]},"t":58,"s":[-0.006]},{"t":59,"s":[-0.004]}],"ix":10},"p":{"a":0,"k":[40.5,41.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-17.978,0],[0,-17.978],[17.978,0],[0,17.978]],"o":[[17.978,0],[0,17.978],[-17.978,0],[0,-17.978]],"v":[[0,-32],[32,0],[0,32],[-32,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":0,"s":[19.5]},{"t":15,"s":[9]}],"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":0,"s":[90]},{"t":15,"s":[116]}],"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Oval","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"ct":1,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/TMessagesProj/src/main/res/raw/folder_share.json b/TMessagesProj/src/main/res/raw/folder_share.json new file mode 100644 index 0000000000..93b976a33a --- /dev/null +++ b/TMessagesProj/src/main/res/raw/folder_share.json @@ -0,0 +1 @@ +{"tgs":1,"v":"5.5.2","fr":60,"ip":0,"op":91,"w":512,"h":512,"nm":"Cloud Folder 2v","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"NULL FOLDER","sr":1,"ks":{"o":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0.7,"y":1},"o":{"x":0.8,"y":0},"t":32,"s":[256,346,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.8,"y":1},"o":{"x":0.6,"y":0},"t":42,"s":[256,210,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.2,"y":0},"t":53,"s":[256,270,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.3,"y":0},"t":64,"s":[256,248,0],"to":[0,0,0],"ti":[0,0,0]},{"t":80,"s":[256,256,0]}]},"s":{"a":1,"k":[{"i":{"x":[0.8,0.8,0.8],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":44,"s":[100,100,100]},{"i":{"x":[0.4,0.4,0.4],"y":[1,1,1]},"o":{"x":[0.2,0.2,0.2],"y":[0,0,0]},"t":56,"s":[102,98,100]},{"i":{"x":[0.4,0.4,0.4],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":67,"s":[98,102,100]},{"t":90,"s":[100,100,100]}]}},"ao":0,"ip":0,"op":191,"st":11,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Link","parent":4,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":40,"s":[30]},{"t":49,"s":[100]}]},"p":{"a":0,"k":[15.21,16.65,0]},"a":{"a":0,"k":[15.21,16.65,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":40,"s":[{"i":[[0,0],[0,0],[7.082,8.482],[-7.252,6.717],[0,0],[-12.077,-10.438],[-0.785,-1.693]],"o":[[0,0],[-7.585,6.384],[-7.11,-8.516],[0,0],[8.645,-8.132],[1.394,1.205],[3.811,8.223]],"v":[[11.713,21.237],[-3.208,32.085],[-32.364,30.304],[-30.748,-0.418],[-14.188,-15.373],[24.709,-24.415],[27.737,-19.789]],"c":false}]},{"i":{"x":0.8,"y":1},"o":{"x":0.6,"y":0},"t":44,"s":[{"i":[[0,0],[0,0],[7.311,6.298],[-3.84,9.379],[0,0],[-7.227,-5.703],[-0.986,-1.481]],"o":[[0,0],[-4.217,9.338],[-7.34,-6.323],[0,0],[4.56,-11.312],[1.248,0.985],[4.789,7.193]],"v":[[34.937,28.773],[27.066,47.091],[4.708,55.619],[1.961,22.371],[10.412,2.714],[30.347,-7.642],[33.706,-3.91]],"c":false}]},{"i":{"x":0.4,"y":1},"o":{"x":0.2,"y":0},"t":56,"s":[{"i":[[0,0],[0,0],[10.591,2.713],[-5.9,7.727],[0,0],[-10.48,-2.311],[-1.416,-0.815]],"o":[[0,0],[-6.377,7.644],[-10.633,-2.724],[0,0],[6.994,-9.296],[1.81,0.399],[6.874,3.955]],"v":[[42.833,54.053],[30.914,68.951],[-2.049,80.383],[-10.117,58.728],[2.941,42.406],[36.853,29.303],[41.702,31.144]],"c":false}]},{"i":{"x":0.4,"y":1},"o":{"x":0.3,"y":0},"t":67,"s":[{"i":[[0,0],[0,0],[8.882,5.984],[-5.823,8.794],[0,0],[-8.824,-5.43],[-1.144,-1.393]],"o":[[0,0],[-6.227,8.577],[-8.917,-6.008],[0,0],[6.916,-10.605],[1.524,0.938],[5.557,6.764]],"v":[[19.88,37.841],[8.183,54.674],[-21.345,62.144],[-26.254,31.871],[-13.44,13.429],[17.065,3.981],[21.075,7.508]],"c":false}]},{"t":90,"s":[{"i":[[0,0],[0,0],[8.66,6.3],[-6.136,8.579],[0,0],[-8.623,-5.745],[-1.093,-1.433]],"o":[[0,0],[-6.532,8.347],[-8.695,-6.325],[0,0],[7.294,-10.349],[1.489,0.992],[5.31,6.96]],"v":[[23.21,42.551],[10.914,58.952],[-18.863,65.354],[-22.68,34.923],[-9.209,16.955],[21.616,8.612],[25.496,12.28]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.435294121504,0.501960813999,0.529411792755,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":40,"s":[{"i":[[0,0],[0,0],[-7.141,-7.93],[8.505,-8.577],[0,0],[14.526,7.8],[1.056,1.807]],"o":[[0,0],[8.645,-8.132],[7.221,8.019],[0,0],[-7.585,6.385],[-1.81,-0.972],[-2.39,-4.088]],"v":[[-16.758,-13.166],[-3.323,-22.672],[27.152,-22.504],[25.439,8.753],[8.446,23.733],[-27.684,34.189],[-32.874,29.761]],"c":false}]},{"i":{"x":0.8,"y":1},"o":{"x":0.6,"y":0},"t":44,"s":[{"i":[[0,0],[0,0],[-7.227,-5.703],[4.398,-11.945],[0,0],[7.311,6.298],[0.771,1.81]],"o":[[0,0],[4.56,-11.312],[7.308,5.767],[0,0],[-4.217,9.338],[-1.359,-1.171],[-3.391,-7.965]],"v":[[24.079,-25.902],[31.138,-42.319],[51.697,-53.689],[57.707,-20.581],[48.416,1.043],[26.058,9.572],[23.183,5.458]],"c":false}]},{"i":{"x":0.4,"y":1},"o":{"x":0.2,"y":0},"t":56,"s":[{"i":[[0,0],[0,0],[-10.48,-2.311],[6.696,-9.538],[0,0],[10.591,2.713],[1.456,1.01]],"o":[[0,0],[6.994,-9.296],[10.597,2.337],[0,0],[-6.377,7.644],[-1.969,-0.505],[-6.407,-4.445]],"v":[[21.856,20.656],[32.762,7.024],[66.674,-6.08],[74.805,15.981],[60.735,33.569],[27.771,45],[22.627,42.69]],"c":false}]},{"i":{"x":0.4,"y":1},"o":{"x":0.3,"y":0},"t":67,"s":[{"i":[[0,0],[0,0],[-8.824,-5.43],[6.682,-10.991],[0,0],[8.882,5.984],[1.161,1.659]],"o":[[0,0],[6.916,-10.605],[8.923,5.491],[0,0],[-6.227,8.577],[-1.651,-1.113],[-5.107,-7.3]],"v":[[4.943,-10.8],[15.646,-26.202],[46.151,-35.651],[51.077,-4.83],[37.269,15.042],[7.741,22.512],[3.521,18.303]],"c":false}]},{"t":90,"s":[{"i":[[0,0],[0,0],[-8.623,-5.745],[7.073,-10.743],[0,0],[8.66,6.3],[1.1,1.7]],"o":[[0,0],[7.294,-10.349],[8.72,5.809],[0,0],[-6.532,8.347],[-1.61,-1.171],[-4.841,-7.479]],"v":[[10.034,-6.596],[21.285,-21.603],[52.11,-29.947],[55.923,1.031],[41.408,20.393],[11.63,26.795],[7.565,22.437]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.435294121504,0.501960813999,0.529411792755,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 2","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":40,"s":[{"i":[[0,0],[0,0],[7.082,8.482],[-7.252,6.717],[0,0],[-12.077,-10.438],[-0.785,-1.693]],"o":[[0,0],[-7.585,6.384],[-7.11,-8.516],[0,0],[8.645,-8.132],[1.394,1.205],[3.811,8.223]],"v":[[11.713,21.237],[-3.208,32.085],[-32.364,30.304],[-30.748,-0.418],[-14.188,-15.373],[24.709,-24.415],[27.737,-19.789]],"c":false}]},{"i":{"x":0.8,"y":1},"o":{"x":0.6,"y":0},"t":44,"s":[{"i":[[0,0],[0,0],[7.311,6.298],[-3.84,9.379],[0,0],[-7.227,-5.703],[-0.986,-1.481]],"o":[[0,0],[-4.217,9.338],[-7.34,-6.323],[0,0],[4.56,-11.312],[1.248,0.985],[4.789,7.193]],"v":[[34.937,28.773],[27.066,47.091],[4.708,55.619],[1.961,22.371],[10.412,2.714],[30.347,-7.642],[33.706,-3.91]],"c":false}]},{"i":{"x":0.4,"y":1},"o":{"x":0.2,"y":0},"t":56,"s":[{"i":[[0,0],[0,0],[10.591,2.713],[-5.9,7.727],[0,0],[-10.48,-2.311],[-1.416,-0.815]],"o":[[0,0],[-6.377,7.644],[-10.633,-2.724],[0,0],[6.994,-9.296],[1.81,0.399],[6.874,3.955]],"v":[[42.833,54.053],[30.914,68.951],[-2.049,80.383],[-10.117,58.728],[2.941,42.406],[36.853,29.303],[41.702,31.144]],"c":false}]},{"i":{"x":0.4,"y":1},"o":{"x":0.3,"y":0},"t":67,"s":[{"i":[[0,0],[0,0],[8.882,5.984],[-5.823,8.794],[0,0],[-8.824,-5.43],[-1.144,-1.393]],"o":[[0,0],[-6.227,8.577],[-8.917,-6.008],[0,0],[6.916,-10.605],[1.524,0.938],[5.557,6.764]],"v":[[19.88,37.841],[8.183,54.674],[-21.345,62.144],[-26.254,31.871],[-13.44,13.429],[17.065,3.981],[21.075,7.508]],"c":false}]},{"t":90,"s":[{"i":[[0,0],[0,0],[8.66,6.3],[-6.136,8.579],[0,0],[-8.623,-5.745],[-1.093,-1.433]],"o":[[0,0],[-6.532,8.347],[-8.695,-6.325],[0,0],[7.294,-10.349],[1.489,0.992],[5.31,6.96]],"v":[[23.21,42.551],[10.914,58.952],[-18.863,65.354],[-22.68,34.923],[-9.209,16.955],[21.616,8.612],[25.496,12.28]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.956862745098,0.956862745098,0.956862745098,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[-3.733,-2.597]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100.952,100.952]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":40,"s":[{"i":[[0,0],[0,0],[-7.141,-7.93],[8.505,-8.577],[0,0],[14.526,7.8]],"o":[[0,0],[8.645,-8.132],[7.221,8.019],[0,0],[-7.585,6.385],[-1.81,-0.972]],"v":[[-16.758,-13.166],[-3.323,-22.672],[27.152,-22.504],[25.439,8.753],[8.446,23.733],[-27.684,34.189]],"c":false}]},{"i":{"x":0.8,"y":1},"o":{"x":0.6,"y":0},"t":44,"s":[{"i":[[0,0],[0,0],[-7.227,-5.703],[4.398,-11.945],[0,0],[6.966,7.892]],"o":[[0,0],[4.56,-11.312],[7.308,5.767],[0,0],[-4.217,9.338],[-1.187,-1.345]],"v":[[24.079,-25.902],[31.138,-42.319],[51.697,-53.689],[57.707,-20.581],[48.416,1.043],[28.665,7.226]],"c":false}]},{"i":{"x":0.4,"y":1},"o":{"x":0.2,"y":0},"t":56,"s":[{"i":[[0,0],[0,0],[-10.48,-2.311],[6.696,-9.538],[0,0],[12.344,4.126]],"o":[[0,0],[6.994,-9.296],[10.597,2.337],[0,0],[-6.377,7.644],[-1.928,-0.644]],"v":[[21.856,20.656],[32.762,7.024],[66.674,-6.08],[74.805,15.981],[60.735,33.569],[28.155,43.005]],"c":false}]},{"i":{"x":0.4,"y":1},"o":{"x":0.3,"y":0},"t":67,"s":[{"i":[[0,0],[0,0],[-8.824,-5.43],[6.682,-10.991],[0,0],[9.922,7.566]],"o":[[0,0],[6.916,-10.605],[8.923,5.491],[0,0],[-6.227,8.577],[-1.583,-1.207]],"v":[[4.943,-10.8],[15.646,-26.202],[46.151,-35.651],[51.077,-4.83],[37.269,15.042],[8.805,20.34]],"c":false}]},{"t":90,"s":[{"i":[[0,0],[0,0],[-8.623,-5.745],[7.073,-10.743],[0,0],[9.142,6.277]],"o":[[0,0],[7.294,-10.349],[8.72,5.809],[0,0],[-6.532,8.347],[-1.642,-1.127]],"v":[[10.034,-6.596],[21.285,-21.603],[52.11,-29.947],[55.923,1.031],[41.408,20.393],[10.718,25.753]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.956862745098,0.956862745098,0.956862745098,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[-3.733,-2.597]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100.952,100.952]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 2","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[-0.527,0.263]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 2","bm":0,"hd":false}],"ip":40,"op":184,"st":-9,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Cloud","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":5,"s":[100]}]},"p":{"a":1,"k":[{"i":{"x":0.4,"y":1},"o":{"x":0.31,"y":0},"t":0,"s":[256,226,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.31,"y":0},"t":11,"s":[256,293,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.5,"y":0},"t":19,"s":[256,261,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.69,"y":1},"o":{"x":0.8,"y":0},"t":32,"s":[256,345,0],"to":[0,0,0],"ti":[0,0,0]},{"t":42,"s":[256,208,0]}]},"a":{"a":0,"k":[542.419,4.14,0]},"s":{"a":1,"k":[{"i":{"x":[0.4,0.4,0.4],"y":[1,1,1]},"o":{"x":[0.31,0.31,0.31],"y":[0,0,0]},"t":1,"s":[29,39.6,100]},{"i":{"x":[0.4,0.4,0.4],"y":[1,1,1]},"o":{"x":[0.31,0.31,0.31],"y":[0,0,0]},"t":13,"s":[91,72.596,100]},{"i":{"x":[0.5,0.5,0.5],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.5],"y":[0,0,0]},"t":21,"s":[80,83.077,100]},{"i":{"x":[0.69,0.69,0.69],"y":[1,1,1]},"o":{"x":[0.8,0.8,0.8],"y":[0,0,0]},"t":34,"s":[94,71.556,100]},{"t":44,"s":[77,83,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":34,"s":[{"i":[[10.892,-17.704],[43.717,0],[0,0],[18.235,28.656]],"o":[[-18.154,29.507],[0,0],[-43.235,0],[-11.451,-17.995]],"v":[[763.125,113.21],[668.852,162.117],[416.334,162.117],[322.889,114.723]],"c":false}]},{"t":44,"s":[{"i":[[2.507,-20.126],[23.315,-7.684],[0,0],[-2.548,45.442]],"o":[[-2.139,17.173],[0,0],[-43.235,0],[1.194,-21.296]],"v":[[724.429,104.437],[684.364,140.74],[375.425,213.322],[329.383,125.566]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[1.251]},"o":{"x":[0.3],"y":[0]},"t":28,"s":[0.541176497936,0.592156887054,0.607843160629,1]},{"t":44,"s":[0.435294147566,0.501960784314,0.529411764706,1]}]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":34,"s":[{"i":[[0,0],[49.269,12.206],[60.754,0],[21.921,-29.994],[-2.665,-20.155],[0,-32.463],[-70.386,0],[0,0]],"o":[[-1.012,-41.866],[-4.549,-65.607],[-37.795,0],[-75.206,-20.055],[-55.568,7.707],[0,61.264],[0,0],[117.599,-5.729]],"v":[[779.72,51.157],[693.13,-46.022],[576.051,-154.236],[482.521,-106.997],[383.289,-36.2],[305.405,51.189],[416.334,162.117],[668.852,162.117]],"c":true}]},{"t":44,"s":[{"i":[[0,0],[-6.23,25.464],[59.812,-10.654],[28.08,-9.044],[-2.665,-20.155],[0,-32.463],[-70.386,0],[0,0]],"o":[[-1.012,-41.866],[4.16,-42.609],[17.667,-62.708],[-45.946,3.607],[-0.285,11.725],[0,61.264],[0,0],[69.744,-21.54]],"v":[[728.421,4.169],[723.649,-171.926],[507.22,-146.706],[358.495,-160.611],[326.146,-73.248],[330.081,62.032],[375.425,213.322],[684.364,140.74]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[1.264]},"o":{"x":[0.3],"y":[0]},"t":28,"s":[0.639215707779,0.686274528503,0.698039233685,1]},{"t":44,"s":[0.435294151306,0.501960813999,0.529411792755,1]}]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 2","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":34,"s":[{"i":[[-0.225,-0.075],[-6.874,-4.396]],"o":[[8.616,2.864],[6.002,3.838]],"v":[[483.651,-106.563],[503.574,-96.716]],"c":false}]},{"t":44,"s":[{"i":[[-0.225,-0.075],[-6.874,-4.396]],"o":[[8.616,2.864],[6.002,3.838]],"v":[[359.625,-160.178],[382.795,-163.583]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":28,"s":[0.827450990677,0.854901969433,0.866666674614,1]},{"t":44,"s":[0.827450990677,0.854901969433,0.866666674614,1]}]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 3","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":34,"s":[{"i":[[-0.234,0.038],[-18.034,-3.661]],"o":[[8.961,-1.459],[52.546,10.667]],"v":[[384.599,-36.421],[423.78,-34.661]],"c":false}]},{"t":44,"s":[{"i":[[-0.234,0.038],[0.458,-11.801]],"o":[[8.961,-1.459],[-2.08,53.578]],"v":[[327.456,-73.469],[328.325,-26.83]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":28,"s":[0.827450990677,0.854901969433,0.866666674614,1]},{"t":44,"s":[0.827450990677,0.854901969433,0.866666674614,1]}]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 4","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":34,"s":[{"i":[[2.368,0.397],[21.344,-26.886],[13.594,0],[13.249,-9.913]],"o":[[-64.234,-10.757],[-11.186,-6.021],[-17.82,0],[-19.352,14.479]],"v":[[693.145,-45.858],[572.342,-5.499],[534.771,-14.942],[487.253,0.842]],"c":false}]},{"t":44,"s":[{"i":[[2.368,0.397],[44.753,-13.855],[13.593,-0.09],[12.796,6.762]],"o":[[-64.234,-10.757],[-12.066,1.958],[5.538,-21.882],[-21.369,-11.293]],"v":[[717.82,-181.4],[553.186,-155.65],[506.524,-146.719],[487.903,-178.827]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":28,"s":[0.827450990677,0.854901969433,0.866666674614,1]},{"t":44,"s":[0.827450990677,0.854901969433,0.866666674614,1]}]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 5","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":34,"s":[{"i":[[0,0],[-51.525,0],[0,0],[-7.913,68.397],[87.853,0.399],[20.141,-0.399]],"o":[[6.793,67.853],[0,0],[54.286,0],[9.188,23.804],[-176.36,-0.801],[-112.74,-24.852]],"v":[[306.234,30.359],[421.083,126.123],[656.39,126.123],[778.313,27.384],[682.02,162.117],[396.193,162.516]],"c":true}]},{"t":44,"s":[{"i":[[0,0],[-51.525,0],[0,0],[-7.913,68.397],[54.809,-11.165],[20.141,-0.399]],"o":[[6.793,67.853],[0,0],[48.184,-10.485],[9.188,23.804],[-156.98,31.978],[-51.176,-17.412]],"v":[[330.909,41.202],[367.836,194.798],[684.156,119.12],[727.015,-19.604],[696.052,138.197],[355.284,213.721]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"gf","o":{"a":0,"k":100},"r":1,"bm":0,"g":{"p":3,"k":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.3,"y":0},"t":28,"s":[0,0.82,0.847,0.859,0.5,0.898,0.914,0.92,1,0.976,0.98,0.98]},{"t":44,"s":[0,0.757,0.799,0.817,0.5,0.752,0.794,0.803,1,0.747,0.788,0.788]}]}},"s":{"a":0,"k":[542,156]},"e":{"a":0,"k":[542,59.132]},"t":1,"nm":"Gradient Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 6","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":34,"s":[{"i":[[48.357,12.143],[59.705,0],[20.729,-26.08],[0.681,-37.993],[0,-44.144],[-51.792,0],[-37.259,0],[4.618,70.534]],"o":[[-4.468,-56.35],[-36.757,0],[-53.3,-18.487],[-43.519,7.818],[0,49.837],[29.122,0],[62.156,0],[-2.863,-43.73]],"v":[[686.021,-35.93],[574.916,-136.665],[484.763,-91.758],[391.958,-28.531],[311.489,60.147],[406.527,161.773],[668.852,162.117],[773.705,54.272]],"c":true}]},{"t":44,"s":[{"i":[[3.476,19.89],[60.685,-7.388],[28.761,-4.101],[-1.162,-30.883],[0,-44.144],[-51.792,0],[-33.071,7.196],[4.618,70.534]],"o":[[-4.468,-56.35],[11.659,-68.833],[-39.421,8.248],[-2.786,21.225],[0,49.837],[29.122,0],[55.17,-12.005],[-2.863,-43.73]],"v":[[716.541,-161.834],[508.033,-145.4],[358.139,-158.023],[327.673,-64.675],[336.165,70.991],[365.617,212.978],[684.364,140.74],[722.406,7.284]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[1.264]},"o":{"x":[0.3],"y":[0]},"t":28,"s":[0.949019610882,0.952941179276,0.956862747669,1]},{"t":44,"s":[0.784313797951,0.807843208313,0.8156863451,1]}]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 7","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":34,"s":[{"i":[[50.062,13.064],[61.811,0],[21.46,-28.059],[0.705,-40.876],[0,-47.493],[-53.618,0],[-38.573,0],[4.781,75.885]],"o":[[-4.626,-60.626],[-38.053,0],[-55.18,-19.89],[-45.054,8.411],[0,53.618],[30.149,0],[64.348,0],[-2.964,-47.048]],"v":[[693.145,-45.858],[576.051,-154.236],[482.719,-108.074],[384.571,-37.897],[305.405,57.509],[406.527,161.773],[658.076,162.516],[779.78,51.189]],"c":true}]},{"t":44,"s":[{"i":[[-1.375,21.986],[61.811,0],[30.805,-4.955],[0.705,-40.876],[0,-47.494],[-53.618,0],[-34.238,7.45],[4.781,75.885]],"o":[[1.222,-41.87],[9.875,-59.395],[-44.52,7.093],[-14.23,8.603],[0,53.618],[30.149,0],[57.116,-12.428],[-2.964,-47.049]],"v":[[723.664,-171.761],[507.22,-146.706],[358.693,-161.688],[327.428,-74.945],[330.081,68.353],[365.617,212.978],[674.884,143.211],[728.481,4.201]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":1,"k":[{"i":{"x":[0.833],"y":[1.289]},"o":{"x":[0.3],"y":[0]},"t":28,"s":[1,1,1,1]},{"t":44,"s":[0.956862807274,0.956862807274,0.956862807274,1]}]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 8","bm":0,"hd":false}],"ip":1,"op":40,"st":4,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Front","parent":1,"sr":1,"ks":{"p":{"a":0,"k":[11.432,25.43,0]},"a":{"a":0,"k":[12.034,21.506,0]},"s":{"a":0,"k":[95,95,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":40,"s":[{"i":[[10.625,-2.684],[0,0],[0,0],[8.49,29.908],[-19.794,28.894],[-11.079,2.54],[0,0],[0,0],[-12.134,-2.748],[-11.385,-22.649],[25.133,-52.56]],"o":[[0,0],[0,0],[-7.533,-8.078],[-18.778,-66.154],[14.299,-20.873],[0,0],[0,0],[0,0],[12.134,2.748],[11.385,22.649],[-14.302,29.91]],"v":[[140.554,101.685],[-125.239,138.569],[-171.941,135.418],[-192.801,97.178],[-181.142,-47.05],[-138.251,-114.928],[-42.116,-132.839],[-41.321,-133.022],[125.76,-129.618],[157.562,-98.993],[175.92,73.584]],"c":true}]},{"i":{"x":0.8,"y":1},"o":{"x":0.6,"y":0},"t":44,"s":[{"i":[[8.408,-2.927],[0,0],[0,0],[-0.216,7.674],[15.381,55.342],[-8.767,2.77],[0,0],[0,0],[0,0],[-4.92,-12.469],[11.471,-47.46]],"o":[[0,0],[0,0],[9.357,-2.94],[0,0],[-6.192,-22.28],[0,0],[0,0],[0,0],[10.391,-2.316],[13.487,34.183],[-1.589,6.575]],"v":[[115.034,97.65],[-97.795,171.741],[-116.842,177.488],[-103.733,129.949],[-116.433,-74.269],[-101.235,-100.08],[-68.475,-110.43],[-67.846,-110.629],[114.346,-168.452],[136.25,-152.057],[130.108,72.744]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.2,"y":0},"t":56,"s":[{"i":[[11.163,-2.62],[0,0],[0,0],[-0.287,6.868],[-20.553,67.931],[-11.64,2.479],[0,0],[0,0],[0,0],[2.359,-8.529],[7.341,-58.786]],"o":[[0,0],[0,0],[12.423,-2.631],[0,0],[3.993,-13.196],[0,0],[0,0],[0,0],[7.521,-1.602],[-17.115,69.365],[-1.491,11.939]],"v":[[148.172,106.079],[-134.388,172.389],[-159.677,177.533],[-143.991,134.044],[-116.289,-31.086],[-95.322,-53.923],[-51.829,-63.186],[-50.993,-63.364],[197.53,-116.293],[211.851,-101.994],[169.502,78.262]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.3,"y":0},"t":67,"s":[{"i":[[10.625,-2.684],[0,0],[0,0],[-0.273,7.036],[0,0],[-11.079,2.54],[0,0],[0,0],[0,0],[0.492,-7.328],[0,0]],"o":[[0,0],[0,0],[11.824,-2.696],[0,0],[0.204,-11.365],[0,0],[0,0],[0,0],[7.158,-1.641],[0,0],[-0.734,10.935]],"v":[[142.134,104.316],[-126.818,172.253],[-150.888,177.523],[-139.643,132.967],[-141.142,-50.208],[-121.935,-73.875],[-80.537,-83.366],[-79.742,-83.548],[156.813,-137.776],[170.194,-126.362],[161.183,81.479]],"c":true}]},{"t":90,"s":[{"i":[[10.625,-2.684],[0,0],[0,0],[-0.273,7.036],[0,0],[-11.079,2.54],[0,0],[0,0],[0,0],[0.492,-7.328],[0,0]],"o":[[0,0],[0,0],[11.824,-2.696],[0,0],[0.204,-11.365],[0,0],[0,0],[0,0],[7.158,-1.641],[0,0],[-0.734,10.935]],"v":[[142.134,104.316],[-126.818,172.253],[-150.888,177.523],[-139.643,132.967],[-136.405,-47.05],[-117.198,-70.718],[-75.8,-80.208],[-75.005,-80.39],[161.55,-134.618],[174.931,-123.204],[161.183,81.479]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.435294121504,0.501960813999,0.529411792755,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":40,"s":[{"i":[[0,0],[4.881,-2.918],[0,0],[-0.703,10.461],[0,0]],"o":[[-0.524,9.919],[0,0],[10.165,-2.568],[0,0],[0,0]],"v":[[-174.061,122.06],[-144.314,143.175],[140.551,106.76],[169.303,82.806],[169.692,79.872]],"c":true}]},{"i":{"x":0.8,"y":1},"o":{"x":0.6,"y":0},"t":44,"s":[{"i":[[0,0],[3.863,-3.182],[0,0],[-0.556,11.409],[0,0]],"o":[[-0.415,10.817],[0,0],[8.044,-2.8],[0,0],[0,0]],"v":[[-107.275,154.885],[-113.306,177.912],[113.782,100.315],[128.204,76.487],[128.512,73.288]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.2,"y":0},"t":56,"s":[{"i":[[0,0],[5.128,-2.848],[0,0],[-0.738,10.21],[0,0]],"o":[[-0.551,9.681],[0,0],[10.68,-2.506],[0,0],[0,0]],"v":[[-146.974,157.303],[-154.981,177.912],[146.51,108.464],[165.657,87.139],[166.066,84.276]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.3,"y":0},"t":67,"s":[{"i":[[0,0],[4.881,-2.918],[0,0],[-0.703,10.461],[0,0]],"o":[[-0.524,9.919],[0,0],[10.165,-2.568],[0,0],[0,0]],"v":[[-138.798,156.797],[-146.419,177.912],[140.552,106.76],[158.776,84.911],[159.166,81.978]],"c":true}]},{"t":90,"s":[{"i":[[0,0],[4.881,-2.918],[0,0],[-0.703,10.461],[0,0]],"o":[[-0.524,9.919],[0,0],[10.165,-2.568],[0,0],[0,0]],"v":[[-138.798,156.797],[-146.419,177.912],[140.552,106.76],[158.776,84.911],[159.166,81.978]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"gf","o":{"a":0,"k":40},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.627,0.694,0.729,0.7,0.627,0.694,0.729,1,0.627,0.694,0.729,0,1,0.7,0.5,1,0]}},"s":{"a":0,"k":[3,119]},"e":{"a":0,"k":[5.739,130.083]},"t":1,"nm":"Gradient Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 2","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":40,"s":[{"i":[[10.165,-2.568],[0,0],[18.65,37.533],[0,0],[-10.6,2.43],[0,0],[0,0],[0,0],[-12.488,-22.152],[0,0]],"o":[[0,0],[4.991,-2.983],[-38.858,-78.2],[0.196,-10.873],[0,0],[0,0],[0,0],[6.848,-1.57],[61.756,109.544],[-0.703,10.461]],"v":[[140.551,105.463],[-136.693,140.097],[-186.406,106.593],[-184.776,-38.533],[-131.472,-105.245],[-41.537,-121.813],[-40.464,-122.061],[137.051,-118.893],[158.244,-94.836],[169.302,81.509]],"c":true}]},{"i":{"x":0.8,"y":1},"o":{"x":0.6,"y":0},"t":44,"s":[{"i":[[8.044,-2.8],[0,0],[-0.363,10.945],[0,0],[-8.388,2.65],[0,0],[0,0],[0,0],[-2.822,-9.654],[3.902,-22.421]],"o":[[0,0],[3.95,-3.254],[0,0],[0.155,-11.857],[0,0],[0,0],[0,0],[5.419,-1.712],[12.441,41.135],[-2.613,15.014]],"v":[[113.782,98.901],[-107.275,174.554],[-101.217,146.626],[-108.897,-68.998],[-98.239,-88.731],[-68.018,-98.404],[-67.958,-98.675],[116.003,-158.359],[129.664,-145.325],[125.046,66.652]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.2,"y":0},"t":56,"s":[{"i":[[10.68,-2.506],[0,0],[-0.482,9.796],[-12.4,30.056],[-11.136,2.372],[0,0],[0,0],[0,0],[0.495,-6.842],[0,0]],"o":[[0,0],[5.244,-2.912],[0,0],[4.994,-12.106],[0,0],[0,0],[0,0],[7.195,-1.532],[0,0],[-0.738,10.21]],"v":[[146.51,107.198],[-146.974,174.907],[-138.931,149.912],[-106.547,-25.316],[-88.2,-44.473],[-51.221,-52.424],[-50.094,-52.666],[198.334,-106.082],[208.434,-94.416],[165.657,85.873]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.3,"y":0},"t":67,"s":[{"i":[[10.165,-2.568],[0,0],[-0.458,10.036],[0,0],[-10.599,2.43],[0,0],[0,0],[0,0],[0.471,-7.01],[0,0]],"o":[[0,0],[4.991,-2.983],[0,0],[0.196,-10.873],[0,0],[0,0],[0,0],[6.848,-1.57],[0,0],[-0.703,10.461]],"v":[[140.552,105.463],[-138.798,174.833],[-131.142,149.224],[-131.618,-45.375],[-115.156,-64.193],[-79.959,-72.339],[-78.885,-72.587],[157.578,-127.314],[167.192,-115.362],[158.776,83.614]],"c":true}]},{"t":90,"s":[{"i":[[10.165,-2.568],[0,0],[-0.458,10.036],[0,0],[-10.599,2.43],[0,0],[0,0],[0,0],[0.471,-7.01],[0,0]],"o":[[0,0],[4.991,-2.983],[0,0],[0.196,-10.873],[0,0],[0,0],[0,0],[6.848,-1.57],[0,0],[-0.703,10.461]],"v":[[140.552,105.463],[-138.798,174.833],[-131.142,149.224],[-126.881,-42.217],[-110.419,-61.035],[-75.222,-69.181],[-74.149,-69.429],[162.314,-124.157],[171.928,-112.204],[158.776,83.614]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"gf","o":{"a":0,"k":100},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.922,0.929,0.929,0.5,0.849,0.865,0.869,1,0.776,0.8,0.808]}},"s":{"a":0,"k":[16,116]},"e":{"a":0,"k":[16,-84.112]},"t":1,"nm":"Gradient Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 3","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":40,"s":[{"i":[[10.625,-2.684],[22.299,1.437],[-49.911,104.39],[-11.079,2.54],[0,0],[0,0],[0,0],[0.492,-7.328],[19.343,-74.665]],"o":[[0,0],[-39.685,-2.557],[11.142,-23.505],[0,0],[0,0],[0,0],[7.158,-1.641],[0,0],[-11.242,43.396]],"v":[[142.134,104.316],[-146.631,139.897],[-183.773,-43.366],[-138.251,-114.928],[-25.892,-136.559],[-23.468,-137.114],[136.287,-129.355],[149.668,-117.941],[180.657,61.478]],"c":true}]},{"i":{"x":0.8,"y":1},"o":{"x":0.6,"y":0},"t":44,"s":[{"i":[[8.408,-2.927],[0,0],[19.065,80.605],[-8.767,2.77],[0,0],[0,0],[0,0],[-4.125,-18.685],[0,0]],"o":[[0,0],[0,0],[-5.146,-16.763],[0,0],[0,0],[0,0],[5.665,-1.79],[15.855,71.815],[-0.581,11.925]],"v":[[115.034,97.65],[-109.308,175.485],[-116.433,-74.269],[-101.235,-100.08],[-55.637,-114.486],[-53.719,-115.092],[119.346,-169.768],[138.092,-146.794],[130.108,72.744]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.2,"y":0},"t":56,"s":[{"i":[[11.163,-2.62],[0,0],[0,0],[-11.64,2.479],[0,0],[0,0],[0,0],[0.517,-7.152],[0,0]],"o":[[0,0],[0,0],[0.215,-11.093],[0,0],[0,0],[0,0],[7.521,-1.602],[0,0],[-0.772,10.673]],"v":[[148.172,106.079],[-149.674,175.74],[-115.5,-30.823],[-95.322,-53.923],[-34.784,-66.816],[-32.238,-67.359],[197.53,-116.293],[211.588,-105.152],[168.186,83.789]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.3,"y":0},"t":67,"s":[{"i":[[10.625,-2.684],[0,0],[0,0],[-11.079,2.54],[0,0],[0,0],[0,0],[0.492,-7.328],[0,0]],"o":[[0,0],[0,0],[0.204,-11.365],[0,0],[0,0],[0,0],[7.158,-1.641],[0,0],[-0.734,10.935]],"v":[[142.134,104.316],[-141.367,175.687],[-141.142,-50.208],[-121.935,-73.875],[-64.313,-87.085],[-61.889,-87.64],[156.813,-137.776],[170.194,-126.362],[161.183,81.479]],"c":true}]},{"t":90,"s":[{"i":[[10.625,-2.684],[0,0],[0,0],[-11.079,2.54],[0,0],[0,0],[0,0],[0.492,-7.328],[0,0]],"o":[[0,0],[0,0],[0.204,-11.365],[0,0],[0,0],[0,0],[7.158,-1.641],[0,0],[-0.734,10.935]],"v":[[142.134,104.316],[-141.367,175.687],[-136.405,-47.05],[-117.198,-70.718],[-59.576,-83.927],[-57.153,-84.483],[161.55,-134.618],[174.93,-123.204],[161.183,81.479]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.956862747669,0.956862747669,0.956862747669,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 4","bm":0,"hd":false}],"ip":40,"op":194,"st":-53,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Shadow","parent":6,"sr":1,"ks":{"p":{"a":0,"k":[-9.93,13.253,0]},"a":{"a":0,"k":[-8.757,15.465,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":40,"s":[{"i":[[10.123,-2.557],[0,16.093],[0,0],[-12.746,3.166],[0,0],[0.307,-5.139],[0,0]],"o":[[-12.246,2.892],[0,0],[-0.194,-13.188],[0,0],[18.326,-4.719],[-0.046,0.77],[-0.7,10.418]],"v":[[-142.941,124.143],[-163.698,93.208],[-150.65,-70.311],[-131.927,-95.052],[116.38,-108.822],[137.414,-87.303],[148.456,89.137]],"c":true}]},{"i":{"x":0.8,"y":1},"o":{"x":0.6,"y":0},"t":44,"s":[{"i":[[10.123,-2.557],[-1.695,15.865],[0,0],[-12.746,3.166],[0,0],[0.307,-5.139],[0,0]],"o":[[-14.967,1.285],[8.425,-113.951],[-1.965,-13.151],[0,0],[14.882,-3.567],[-0.046,0.77],[-0.7,10.418]],"v":[[-97.954,166.073],[-115.448,128.282],[-125.23,-71.184],[-110.243,-98.819],[93.115,-164.251],[109.52,-141.172],[120.248,90.617]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.2,"y":0},"t":56,"s":[{"i":[[10.123,-2.557],[0,16.093],[-18.13,55.85],[-12.857,3.167],[0,0],[0.307,-5.139],[0,0]],"o":[[-15.535,3.553],[0,0],[3.898,-12.008],[0,0],[14.882,-3.567],[-0.046,0.77],[-0.7,10.418]],"v":[[-129.533,173.895],[-155.448,135.124],[-136.564,-34.911],[-117.238,-64.261],[144.398,-119.773],[153.852,-100.897],[128.603,97.717]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.3,"y":0},"t":67,"s":[{"i":[[10.123,-2.557],[0,16.093],[0,0],[-12.746,3.166],[0,0],[0.307,-5.139],[0,0]],"o":[[-12.246,2.892],[0,0],[-0.194,-13.188],[0,0],[18.326,-4.719],[-0.046,0.77],[-0.7,10.418]],"v":[[-131.638,169.684],[-153.378,138.58],[-153.092,-58.237],[-134.369,-82.978],[119.344,-140.407],[136.938,-118.154],[126.341,93.544]],"c":true}]},{"t":90,"s":[{"i":[[10.123,-2.557],[0,16.093],[0,0],[-12.746,3.166],[0,0],[0.307,-5.139],[0,0]],"o":[[-12.246,2.892],[0,0],[-0.194,-13.188],[0,0],[18.326,-4.719],[-0.046,0.77],[-0.7,10.418]],"v":[[-131.638,169.684],[-152.395,138.749],[-152.125,-55.131],[-133.401,-79.872],[119.206,-137.713],[136.922,-115.215],[126.341,93.544]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.43137254902,0.525490196078,0.56862745098,1]},"o":{"a":0,"k":15},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[-2.105,-3.684]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[107.095,107.48]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 3","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":40,"s":[{"i":[[10.123,-2.557],[0,16.093],[0,0],[-12.746,3.166],[0,0],[0.307,-5.139],[0,0]],"o":[[-12.246,2.892],[0,0],[-0.194,-13.188],[0,0],[18.326,-4.719],[-0.046,0.77],[-0.7,10.418]],"v":[[-143.362,122.278],[-164.119,91.343],[-150.596,-70.933],[-131.872,-95.674],[119.675,-109.178],[137.514,-86.18],[149.362,88.936]],"c":true}]},{"i":{"x":0.8,"y":1},"o":{"x":0.6,"y":0},"t":44,"s":[{"i":[[10.123,-2.557],[-1.695,15.865],[0,0],[-12.746,3.166],[0,0],[0.307,-5.139],[0,0]],"o":[[-15.535,3.553],[9.358,-118.609],[-1.965,-13.151],[0,0],[14.882,-3.567],[-0.046,0.77],[-0.7,10.418]],"v":[[-99.006,164.947],[-116.301,131.973],[-126.31,-72.779],[-111.296,-100.924],[96.113,-167.641],[108.467,-143.277],[123.218,91.789]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.2,"y":0},"t":56,"s":[{"i":[[10.123,-2.557],[0,16.093],[-19.395,65.56],[-12.857,3.167],[0,0],[0.307,-5.139],[0,0]],"o":[[-15.535,3.553],[0,0],[3.581,-12.106],[0,0],[14.882,-3.567],[-0.046,0.77],[-0.7,10.418]],"v":[[-130.585,171.79],[-155.791,138.05],[-138.762,-36.117],[-120.995,-64.517],[144.238,-122.433],[154.52,-103.408],[127.55,95.612]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.3,"y":0},"t":67,"s":[{"i":[[10.123,-2.557],[0,16.093],[0,0],[-12.746,3.166],[0,0],[0.307,-5.139],[0,0]],"o":[[-12.246,2.892],[0,0],[-0.194,-13.188],[0,0],[18.326,-4.719],[-0.046,0.77],[-0.7,10.418]],"v":[[-131.638,169.684],[-153.414,138.574],[-153.128,-58.364],[-134.405,-83.105],[122.75,-142.056],[137.021,-118.294],[126.423,93.524]],"c":true}]},{"t":90,"s":[{"i":[[10.123,-2.557],[0,16.093],[0,0],[-12.746,3.166],[0,0],[0.307,-5.139],[0,0]],"o":[[-12.246,2.892],[0,0],[-0.194,-13.188],[0,0],[18.326,-4.719],[-0.046,0.77],[-0.7,10.418]],"v":[[-131.638,169.684],[-152.395,138.749],[-152.125,-55.131],[-133.401,-79.872],[122.606,-139.253],[137.004,-115.235],[126.423,93.524]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.43137254902,0.525490196078,0.56862745098,1]},"o":{"a":0,"k":30},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[-1.053,-1.579]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[103.252,103.252]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 2","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":40,"s":[{"i":[[10.123,-2.557],[0,16.093],[0,0],[-12.746,3.166],[0,0],[0.307,-5.139],[0,0]],"o":[[-15.535,3.553],[0,0],[-0.194,-13.188],[0,0],[14.882,-3.567],[-0.046,0.77],[-0.7,10.418]],"v":[[-143.743,120.737],[-164.5,89.801],[-150.546,-71.447],[-131.822,-96.188],[123.589,-109.269],[137.677,-89.33],[150.182,88.77]],"c":true}]},{"i":{"x":0.8,"y":1},"o":{"x":0.6,"y":0},"t":44,"s":[{"i":[[10.123,-2.557],[-1.695,15.865],[0,0],[-12.746,3.166],[0,0],[0.307,-5.139],[0,0]],"o":[[-15.535,3.553],[9.358,-118.609],[-1.965,-13.151],[0,0],[14.882,-3.567],[-0.046,0.77],[-0.7,10.418]],"v":[[-100.059,162.842],[-116.079,131.906],[-126.598,-75.394],[-112.349,-103.03],[96.221,-169.796],[107.415,-145.383],[126.498,93.507]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.2,"y":0},"t":56,"s":[{"i":[[10.123,-2.557],[0,16.093],[-16.164,61.057],[-12.857,3.167],[0,0],[0.307,-5.139],[0,0]],"o":[[-15.535,3.553],[0,0],[3.231,-12.204],[0,0],[14.882,-3.567],[-0.046,0.77],[-0.7,10.418]],"v":[[-131.638,169.684],[-156.079,138.749],[-139.999,-38.289],[-122.232,-65.924],[144.115,-123.217],[153.467,-105.646],[126.498,93.507]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.3,"y":0},"t":67,"s":[{"i":[[10.123,-2.557],[0,16.093],[0,0],[-12.746,3.166],[0,0],[0.307,-5.139],[0,0]],"o":[[-15.535,3.553],[0,0],[-0.194,-13.188],[0,0],[14.882,-3.567],[-0.046,0.77],[-0.7,10.418]],"v":[[-131.638,169.684],[-153.448,138.568],[-153.161,-58.47],[-134.437,-83.211],[126.764,-143.217],[137.168,-122.488],[126.498,93.507]],"c":true}]},{"t":90,"s":[{"i":[[10.123,-2.557],[0,16.093],[0,0],[-12.746,3.166],[0,0],[0.307,-5.139],[0,0]],"o":[[-15.535,3.553],[0,0],[-0.194,-13.188],[0,0],[14.882,-3.567],[-0.046,0.77],[-0.7,10.418]],"v":[[-131.638,169.684],[-152.395,138.749],[-152.125,-55.131],[-133.401,-79.872],[126.616,-140.322],[137.151,-119.33],[126.498,93.507]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.43137254902,0.525490196078,0.56862745098,1]},"o":{"a":0,"k":45},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0.526]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":40,"op":169,"st":-24,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Back","parent":1,"sr":1,"ks":{"p":{"a":0,"k":[-13.837,5.111,0]},"a":{"a":0,"k":[-14.565,0.116,0]},"s":{"a":0,"k":[95,95,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":40,"s":[{"i":[[64.806,-92.799],[0,0],[9.729,44.965],[0,0],[-10.861,2.36],[0,0],[-0.165,-7.303],[0,0],[-5.276,1.254],[0,0],[0.381,-6.796]],"o":[[-13.352,14.307],[-17.034,3.533],[-15.299,-70.708],[-0.047,-11.114],[0,0],[7.138,-1.551],[0,0],[0.107,5.422],[0,0],[6.622,-1.574],[0,0]],"v":[[160.457,96.981],[-157.04,134.892],[-196.543,78.311],[-171.272,-92.318],[-152.727,-115.427],[-22.138,-150.743],[-8.052,-139.65],[-4.652,-140.066],[5.817,-131.961],[133.293,-129.447],[145.805,-118.979]],"c":true}]},{"i":{"x":0.8,"y":1},"o":{"x":0.6,"y":0},"t":44,"s":[{"i":[[0,0],[0,0],[-8.179,52.032],[0,0],[-8.423,2.481],[0,0],[-1.763,-7.473],[0,0],[-4.091,1.318],[0,0],[0.295,-7.144]],"o":[[0,0],[-13.209,3.714],[12.311,-78.317],[-0.037,-11.683],[0,0],[5.06,-2.774],[0,0],[0.083,5.7],[0,0],[5.135,-1.655],[0,0]],"v":[[97.933,103.188],[-118.487,177.342],[-136.522,110.969],[-150.49,-138.512],[-136.108,-162.804],[-56.792,-185.188],[-43.632,-176.126],[-38.421,-163.809],[-30.302,-155.288],[100.132,-195.693],[109.835,-184.689]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.2,"y":0},"t":56,"s":[{"i":[[0,0],[0,0],[4.819,22.715],[-12.286,50.557],[-11.724,2.278],[0,0],[-0.179,-7.047],[0,0],[-5.695,1.21],[0,0],[-3.688,-9.541]],"o":[[0,0],[-18.387,3.409],[-10.256,-48.345],[4.6,-18.928],[0,0],[7.705,-1.497],[0,0],[0.116,5.232],[0,0],[7.148,-1.519],[11.99,31.014]],"v":[[141.969,109.574],[-159.284,177.647],[-190.27,122.558],[-191.398,-114.238],[-167.695,-136.538],[-47.182,-159.953],[-31.977,-149.248],[-31.716,-137.46],[-20.415,-129.638],[145.031,-164.797],[161.958,-154.169]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.3,"y":0},"t":67,"s":[{"i":[[0,0],[0,0],[-1.045,36.214],[0,0],[-10.861,2.36],[0,0],[-0.165,-7.303],[0,0],[-5.276,1.254],[0,0],[0.381,-6.796]],"o":[[0,0],[-17.034,3.533],[0,0],[-0.047,-11.114],[0,0],[7.138,-1.551],[0,0],[0.107,5.422],[0,0],[6.622,-1.574],[0,0]],"v":[[130.457,106.981],[-148.619,177.523],[-173.911,119.89],[-174.956,-124.95],[-156.411,-148.058],[-44.769,-172.322],[-30.683,-161.229],[-30.442,-149.014],[-19.972,-140.908],[133.294,-177.342],[145.805,-166.874]],"c":true}]},{"t":90,"s":[{"i":[[0,0],[0,0],[-1.045,36.214],[0,0],[-10.861,2.36],[0,0],[-0.165,-7.303],[0,0],[-5.276,1.254],[0,0],[0.381,-6.796]],"o":[[0,0],[-17.034,3.533],[0,0],[-0.047,-11.114],[0,0],[7.138,-1.551],[0,0],[0.107,5.422],[0,0],[6.622,-1.574],[0,0]],"v":[[130.457,106.981],[-148.619,177.523],[-173.911,119.89],[-174.956,-124.95],[-156.411,-148.058],[-44.769,-172.322],[-30.683,-161.229],[-30.442,-149.014],[-19.972,-140.908],[133.294,-177.342],[145.805,-166.874]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.435294121504,0.501960813999,0.529411792755,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":40,"s":[{"i":[[62.752,-94.229],[0,0],[18.213,47.179],[0,0],[-10.861,2.36],[0,0],[-0.166,-7.303],[0,0],[-5.276,1.254],[0,0],[0.381,-6.796]],"o":[[-23.564,13.14],[-9.521,1.836],[-30.3,-78.489],[-0.047,-11.114],[0,0],[7.138,-1.551],[0,0],[0.107,5.422],[0,0],[6.622,-1.574],[0,0]],"v":[[160.406,97.884],[-149.789,133.055],[-193.647,94.514],[-171.279,-86.193],[-148.711,-106.241],[-22.138,-140.52],[-4.894,-141.346],[-4.652,-129.13],[5.482,-121.773],[134.751,-119.801],[145.804,-108.043]],"c":true}]},{"i":{"x":0.8,"y":1},"o":{"x":0.6,"y":0},"t":44,"s":[{"i":[[0,0],[0,0],[-4.032,30.34],[0,0],[-8.423,2.481],[0,0],[-0.128,-7.677],[0,0],[-4.091,1.318],[0,0],[0.295,-7.144]],"o":[[0,0],[-7.383,1.93],[15.726,-118.345],[-0.037,-11.683],[0,0],[5.06,-2.774],[0,0],[0.083,5.7],[0,0],[5.135,-1.655],[0,0]],"v":[[97.893,104.138],[-112.863,175.412],[-128.884,130.471],[-141.924,-132.074],[-132.994,-153.148],[-56.078,-175.741],[-38.608,-165.154],[-38.421,-152.313],[-30.562,-144.579],[101.263,-185.552],[109.835,-173.193]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.2,"y":0},"t":56,"s":[{"i":[[0,0],[0,0],[5.081,30.604],[-15.262,80.962],[-11.724,2.278],[0,0],[-0.179,-7.047],[0,0],[-5.695,1.21],[0,0],[-2.866,-8.036]],"o":[[0,0],[-10.277,1.772],[-6.883,-41.459],[2.36,-12.52],[0,0],[7.705,-1.497],[0,0],[0.116,5.232],[0,0],[7.148,-1.519],[10.674,29.934]],"v":[[141.914,110.446],[-151.456,175.875],[-178.38,128.83],[-181.58,-107.538],[-161.781,-128.2],[-47.182,-150.087],[-31.977,-138.695],[-31.716,-126.907],[-20.776,-119.807],[146.604,-155.488],[162.22,-146.51]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.3,"y":0},"t":67,"s":[{"i":[[0,0],[0,0],[0.972,32.174],[0,0],[-10.861,2.36],[0,0],[-0.165,-7.303],[0,0],[-5.276,1.254],[0,0],[0.381,-6.796]],"o":[[0,0],[-9.521,1.836],[0,0],[-0.047,-11.114],[0,0],[7.138,-1.551],[0,0],[0.107,5.422],[0,0],[6.622,-1.574],[0,0]],"v":[[130.406,107.884],[-141.367,175.687],[-163.384,132.935],[-163.911,-118.824],[-152.395,-138.873],[-44.769,-162.099],[-30.683,-150.293],[-30.442,-138.078],[-20.308,-130.72],[134.751,-167.696],[145.805,-155.938]],"c":true}]},{"t":90,"s":[{"i":[[0,0],[0,0],[0.972,32.174],[0,0],[-10.861,2.36],[0,0],[-0.165,-7.303],[0,0],[-5.276,1.254],[0,0],[0.381,-6.796]],"o":[[0,0],[-9.521,1.836],[0,0],[-0.047,-11.114],[0,0],[7.138,-1.551],[0,0],[0.107,5.422],[0,0],[6.622,-1.574],[0,0]],"v":[[130.406,107.884],[-141.367,175.687],[-163.384,132.935],[-163.911,-118.824],[-152.395,-138.873],[-44.769,-162.099],[-30.683,-150.293],[-30.442,-138.078],[-20.308,-130.72],[134.751,-167.696],[145.805,-155.938]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"gf","o":{"a":0,"k":100},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.773,0.808,0.827,0.5,0.743,0.771,0.788,1,0.714,0.733,0.749]}},"s":{"a":0,"k":[-1,179]},"e":{"a":0,"k":[-15.224,-135.356]},"t":1,"nm":"Gradient Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 2","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":40,"s":[{"i":[[68.015,-112.65],[0,0],[15.1,45.724],[0,0],[-10.861,2.36],[0,0],[-0.165,-7.303],[0,0],[-5.276,1.254],[0,0],[0.381,-6.796]],"o":[[-13.038,3.666],[-17.034,3.533],[-21.352,-64.656],[-0.047,-11.114],[0,0],[7.138,-1.551],[0,0],[0.107,5.422],[0,0],[6.622,-1.574],[0,0]],"v":[[160.406,97.885],[-157.04,134.892],[-196.543,78.311],[-171.272,-92.318],[-152.727,-115.427],[-22.138,-150.743],[-8.052,-139.65],[-4.652,-140.066],[5.817,-131.961],[133.294,-129.447],[145.805,-118.979]],"c":true}]},{"i":{"x":0.8,"y":1},"o":{"x":0.6,"y":0},"t":44,"s":[{"i":[[0,0],[0,0],[-8.209,38.085],[0,0],[-8.423,2.481],[0,0],[-1.763,-7.473],[0,0],[-4.091,1.318],[0,0],[0.295,-7.144]],"o":[[0,0],[-13.209,3.714],[13.364,-62.001],[-0.037,-11.683],[0,0],[5.06,-2.774],[0,0],[0.083,5.7],[0,0],[5.135,-1.655],[0,0]],"v":[[97.893,104.138],[-118.487,177.342],[-135.995,113.601],[-150.49,-138.512],[-136.108,-162.804],[-56.792,-185.188],[-43.632,-176.126],[-38.421,-163.809],[-30.302,-155.288],[100.132,-195.693],[109.834,-184.689]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.2,"y":0},"t":56,"s":[{"i":[[0,0],[0,0],[7.639,35.34],[-26.497,93.715],[-11.724,2.278],[0,0],[-0.179,-7.047],[0,0],[-5.695,1.21],[0,0],[-10.472,-15.71]],"o":[[0,0],[-18.387,3.409],[0,0],[4.329,-15.31],[0,0],[7.705,-1.497],[0,0],[0.116,5.232],[0,0],[7.148,-1.519],[12.779,19.172]],"v":[[141.914,110.446],[-159.284,177.647],[-190.27,123.084],[-189.819,-114.765],[-167.695,-136.538],[-47.182,-159.953],[-31.977,-149.248],[-31.716,-137.46],[-20.415,-129.638],[145.031,-164.797],[162.484,-151.801]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.3,"y":0},"t":67,"s":[{"i":[[0,0],[0,0],[-1.045,36.214],[0,0],[-10.861,2.36],[0,0],[-0.165,-7.303],[0,0],[-5.276,1.254],[0,0],[0.381,-6.796]],"o":[[0,0],[-17.034,3.533],[0,0],[-0.047,-11.114],[0,0],[7.138,-1.551],[0,0],[0.107,5.422],[0,0],[6.622,-1.574],[0,0]],"v":[[130.406,107.884],[-148.619,177.523],[-173.911,119.89],[-174.956,-124.95],[-156.411,-148.058],[-44.769,-172.322],[-30.683,-161.229],[-30.442,-149.014],[-19.972,-140.908],[133.294,-177.342],[145.805,-166.874]],"c":true}]},{"t":90,"s":[{"i":[[0,0],[0,0],[-1.045,36.214],[0,0],[-10.861,2.36],[0,0],[-0.165,-7.303],[0,0],[-5.276,1.254],[0,0],[0.381,-6.796]],"o":[[0,0],[-17.034,3.533],[0,0],[-0.047,-11.114],[0,0],[7.138,-1.551],[0,0],[0.107,5.422],[0,0],[6.622,-1.574],[0,0]],"v":[[130.406,107.884],[-148.619,177.523],[-173.911,119.89],[-174.956,-124.95],[-156.411,-148.058],[-44.769,-172.322],[-30.683,-161.229],[-30.442,-149.014],[-19.972,-140.908],[133.294,-177.342],[145.805,-166.874]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"gf","o":{"a":0,"k":100},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.859,0.867,0.867,0.18,0.908,0.912,0.912,1,0.957,0.957,0.957]}},"s":{"a":0,"k":[-170,0]},"e":{"a":0,"k":[-85.947,-0.315]},"t":1,"nm":"Gradient Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 3","bm":0,"hd":false}],"ip":40,"op":194,"st":-53,"bm":0}]} \ No newline at end of file diff --git a/TMessagesProj/src/main/res/raw/media_mute_unmute.json b/TMessagesProj/src/main/res/raw/media_mute_unmute.json new file mode 100644 index 0000000000..d5e9a7e06a --- /dev/null +++ b/TMessagesProj/src/main/res/raw/media_mute_unmute.json @@ -0,0 +1 @@ +{"tgs":1,"v":"5.5.2","fr":60,"ip":0,"op":44,"w":512,"h":512,"nm":"MEDIA MUTE / UNMUTE","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Line Small 4","parent":4,"sr":1,"ks":{"p":{"a":1,"k":[{"i":{"x":0.6,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[105.272,-4.983,0],"to":[4.5,0.5,0],"ti":[-4.5,-0.5,0]},{"t":13,"s":[132.272,-1.983,0]}]},"a":{"a":0,"k":[105.272,-4.983,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-15.933,-11.882],[0,-17.553],[17.385,-13.182]],"o":[[17.789,13.266],[0,17.323],[-15.643,11.861]],"v":[[91.693,-52.966],[118.852,-4.73],[92.365,43.001]],"c":false}},"nm":"Path 1","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.6],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":0,"s":[0]},{"t":13,"s":[47]}]},"e":{"a":1,"k":[{"i":{"x":[0.6],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":0,"s":[100]},{"t":13,"s":[53]}]},"o":{"a":0,"k":0},"m":1,"nm":"Trim Paths 1","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":46},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":12,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Line Big 4","parent":4,"sr":1,"ks":{"p":{"a":1,"k":[{"i":{"x":0.6,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[131.883,-5.898,0],"to":[0,0,0],"ti":[0,0,0]},{"t":13,"s":[100.883,0.102,0]}]},"a":{"a":0,"k":[131.883,-5.898,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.471,-0.164],[-15.933,-19.728],[0,-29.144],[17.385,-21.886],[25.381,-9.147]],"o":[[25.715,8.926],[17.789,22.026],[0,28.763],[-15.643,19.694],[-0.457,0.165]],"v":[[86.037,-128.22],[149.57,-84.036],[177.729,-5.608],[150.242,71.983],[87.678,116.424]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":46},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.6],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":0,"s":[0]},{"t":13,"s":[48.5]}]},"e":{"a":1,"k":[{"i":{"x":[0.6],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":0,"s":[100]},{"t":13,"s":[51.5]}]},"o":{"a":0,"k":0},"m":1,"nm":"Trim Paths 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":12,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Cross 4","parent":4,"sr":1,"ks":{"p":{"a":0,"k":[146.626,5.78,0]},"a":{"a":0,"k":[140.253,5.78,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0.167},"t":12,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[141.255,0.625],[139.252,10.936]],"c":false}]},{"t":21,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[186.424,-40.422],[94.083,51.983]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":46},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[139.75,-0.5]},"a":{"a":0,"k":[139.75,5.5]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0.167},"t":12,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[139.252,0.625],[141.255,10.936]],"c":false}]},{"t":21,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[94.083,-40.422],[186.424,51.983]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":46},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[139.75,-0.5]},"a":{"a":0,"k":[139.75,5.5]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 2","bm":0,"hd":false}],"ip":12,"op":21,"st":-1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Speaker 4","sr":1,"ks":{"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[184.119,256.078,0],"to":[-1.062,0,0],"ti":[0,0,0]},{"t":21,"s":[177.746,256.078,0]}]},"a":{"a":0,"k":[-71.881,0.078,0]},"s":{"a":1,"k":[{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":12,"s":[93,93,100]},{"t":21,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[16.806,-17.492],[0,0],[6.343,0],[0,0],[0,-14.237],[0,0],[-22.972,0],[0,0],[-4.378,-4.611],[0,0],[0,37.153],[0,0]],"o":[[0,-37.153],[0,0],[-4.378,4.611],[0,0],[-22.972,0],[0,0],[0,14.237],[0,0],[6.343,0],[0,0],[16.806,17.492],[0,0],[0,0]],"v":[[33.271,-121.847],[-15.728,-144.085],[-80.954,-75.396],[-97.742,-68.176],[-144.423,-68.176],[-177.034,-39.797],[-177.034,39.953],[-144.423,68.333],[-97.742,68.333],[-80.954,75.552],[-15.728,144.241],[33.271,122.004],[33.271,2.96]],"c":true}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":46},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":21,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Line Small 3","parent":8,"sr":1,"ks":{"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.1,"y":0},"t":31,"s":[20.883,0.352,0],"to":[0,0,0],"ti":[0,0,0]},{"t":43,"s":[105.272,-4.983,0]}]},"a":{"a":0,"k":[105.272,-4.983,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-15.933,-11.882],[0,-17.553],[17.385,-13.182]],"o":[[17.789,13.266],[0,17.323],[-15.643,11.861]],"v":[[91.693,-52.966],[118.852,-4.73],[92.365,43.001]],"c":false}},"nm":"Path 1","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.1],"y":[0]},"t":31,"s":[48.5]},{"t":43,"s":[0]}]},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.1],"y":[0]},"t":31,"s":[51.5]},{"t":43,"s":[100]}]},"o":{"a":0,"k":0},"m":1,"nm":"Trim Paths 1","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":46},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":31,"op":179,"st":33,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Line Big 3","parent":8,"sr":1,"ks":{"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.1,"y":0},"t":31,"s":[80.133,0.102,0],"to":[0,0,0],"ti":[0,0,0]},{"t":43,"s":[131.883,-5.898,0]}]},"a":{"a":0,"k":[131.883,-5.898,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.471,-0.164],[-15.933,-19.728],[0,-29.144],[17.385,-21.886],[25.381,-9.147]],"o":[[25.715,8.926],[17.789,22.026],[0,28.763],[-15.643,19.694],[-0.457,0.165]],"v":[[86.037,-128.22],[149.57,-84.036],[177.729,-5.608],[150.242,71.983],[87.678,116.424]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":46},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.1],"y":[0]},"t":31,"s":[40]},{"t":43,"s":[0]}]},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.1],"y":[0]},"t":31,"s":[60]},{"t":43,"s":[100]}]},"o":{"a":0,"k":0},"m":1,"nm":"Trim Paths 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":31,"op":179,"st":33,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Cross 3","parent":8,"sr":1,"ks":{"p":{"a":1,"k":[{"i":{"x":0.6,"y":1},"o":{"x":0.6,"y":0},"t":21,"s":[146.626,5.78,0],"to":[-3,0,0],"ti":[3,0,0]},{"t":32,"s":[128.626,5.78,0]}]},"a":{"a":0,"k":[140.253,5.78,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.6,"y":1},"o":{"x":0.6,"y":0},"t":21,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[186.424,-40.422],[94.083,51.983]],"c":false}]},{"t":32,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[141.255,0.625],[139.252,10.936]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":46},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[139.75,-0.5]},"a":{"a":0,"k":[139.75,5.5]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.6,"y":1},"o":{"x":0.6,"y":0},"t":21,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[94.083,-40.422],[186.424,51.983]],"c":false}]},{"t":32,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[139.252,0.625],[141.255,10.936]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":46},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[139.75,-0.5]},"a":{"a":0,"k":[139.75,5.5]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 2","bm":0,"hd":false}],"ip":21,"op":31,"st":20,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Speaker 3","sr":1,"ks":{"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.6,"y":0},"t":21,"s":[177.746,256.078,0],"to":[0,0,0],"ti":[-1.062,0,0]},{"t":43,"s":[184.119,256.078,0]}]},"a":{"a":0,"k":[-71.881,0.078,0]},"s":{"a":1,"k":[{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":21,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":30,"s":[93,93,100]},{"t":43,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[16.806,-17.492],[0,0],[6.343,0],[0,0],[0,-14.237],[0,0],[-22.972,0],[0,0],[-4.378,-4.611],[0,0],[0,37.153],[0,0]],"o":[[0,-37.153],[0,0],[-4.378,4.611],[0,0],[-22.972,0],[0,0],[0,14.237],[0,0],[6.343,0],[0,0],[16.806,17.492],[0,0],[0,0]],"v":[[33.271,-121.847],[-15.728,-144.085],[-80.954,-75.396],[-97.742,-68.176],[-144.423,-68.176],[-177.034,-39.797],[-177.034,39.953],[-144.423,68.333],[-97.742,68.333],[-80.954,75.552],[-15.728,144.241],[33.271,122.004],[33.271,2.96]],"c":true}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":46},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":21,"op":184,"st":21,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Line Small","parent":4,"sr":1,"ks":{"p":{"a":1,"k":[{"i":{"x":0.6,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[105.272,-4.983,0],"to":[4.5,0.5,0],"ti":[-4.5,-0.5,0]},{"t":13,"s":[132.272,-1.983,0]}]},"a":{"a":0,"k":[105.272,-4.983,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-15.933,-11.882],[0,-17.553],[17.385,-13.182]],"o":[[17.789,13.266],[0,17.323],[-15.643,11.861]],"v":[[91.693,-52.966],[118.852,-4.73],[92.365,43.001]],"c":false}},"nm":"Path 1","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.6],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":0,"s":[0]},{"t":13,"s":[47]}]},"e":{"a":1,"k":[{"i":{"x":[0.6],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":0,"s":[100]},{"t":13,"s":[53]}]},"o":{"a":0,"k":0},"m":1,"nm":"Trim Paths 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":30},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":12,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Line Big","parent":4,"sr":1,"ks":{"p":{"a":1,"k":[{"i":{"x":0.6,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[131.883,-5.898,0],"to":[0,0,0],"ti":[0,0,0]},{"t":13,"s":[100.883,0.102,0]}]},"a":{"a":0,"k":[131.883,-5.898,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.471,-0.164],[-15.933,-19.728],[0,-29.144],[17.385,-21.886],[25.381,-9.147]],"o":[[25.715,8.926],[17.789,22.026],[0,28.763],[-15.643,19.694],[-0.457,0.165]],"v":[[86.037,-128.22],[149.57,-84.036],[177.729,-5.608],[150.242,71.983],[87.678,116.424]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":30},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.6],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":0,"s":[0]},{"t":13,"s":[48.5]}]},"e":{"a":1,"k":[{"i":{"x":[0.6],"y":[1]},"o":{"x":[0.6],"y":[0]},"t":0,"s":[100]},{"t":13,"s":[51.5]}]},"o":{"a":0,"k":0},"m":1,"nm":"Trim Paths 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":12,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Cross","parent":4,"sr":1,"ks":{"p":{"a":0,"k":[146.626,5.78,0]},"a":{"a":0,"k":[140.253,5.78,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0.167},"t":12,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[141.255,0.625],[139.252,10.936]],"c":false}]},{"t":21,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[186.424,-40.422],[94.083,51.983]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":30},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[139.75,-0.5]},"a":{"a":0,"k":[139.75,5.5]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0.167},"t":12,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[139.252,0.625],[141.255,10.936]],"c":false}]},{"t":21,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[94.083,-40.422],[186.424,51.983]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":30},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[139.75,-0.5]},"a":{"a":0,"k":[139.75,5.5]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 2","bm":0,"hd":false}],"ip":12,"op":21,"st":-1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Speaker","sr":1,"ks":{"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[184.119,256.078,0],"to":[-1.062,0,0],"ti":[0,0,0]},{"t":21,"s":[177.746,256.078,0]}]},"a":{"a":0,"k":[-71.881,0.078,0]},"s":{"a":1,"k":[{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":12,"s":[93,93,100]},{"t":21,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[16.806,-17.492],[0,0],[6.343,0],[0,0],[0,-14.237],[0,0],[-22.972,0],[0,0],[-4.378,-4.611],[0,0],[0,37.153],[0,0]],"o":[[0,-37.153],[0,0],[-4.378,4.611],[0,0],[-22.972,0],[0,0],[0,14.237],[0,0],[6.343,0],[0,0],[16.806,17.492],[0,0],[0,0]],"v":[[33.271,-121.847],[-15.728,-144.085],[-80.954,-75.396],[-97.742,-68.176],[-144.423,-68.176],[-177.034,-39.797],[-177.034,39.953],[-144.423,68.333],[-97.742,68.333],[-80.954,75.552],[-15.728,144.241],[33.271,122.004],[33.271,2.96]],"c":true}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":30},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":21,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Line Small 2","parent":8,"sr":1,"ks":{"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.1,"y":0},"t":31,"s":[20.883,0.352,0],"to":[0,0,0],"ti":[0,0,0]},{"t":43,"s":[105.272,-4.983,0]}]},"a":{"a":0,"k":[105.272,-4.983,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-15.933,-11.882],[0,-17.553],[17.385,-13.182]],"o":[[17.789,13.266],[0,17.323],[-15.643,11.861]],"v":[[91.693,-52.966],[118.852,-4.73],[92.365,43.001]],"c":false}},"nm":"Path 1","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.1],"y":[0]},"t":31,"s":[48.5]},{"t":43,"s":[0]}]},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.1],"y":[0]},"t":31,"s":[51.5]},{"t":43,"s":[100]}]},"o":{"a":0,"k":0},"m":1,"nm":"Trim Paths 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":30},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":31,"op":179,"st":33,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Line Big 2","parent":8,"sr":1,"ks":{"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.1,"y":0},"t":31,"s":[80.133,0.102,0],"to":[0,0,0],"ti":[0,0,0]},{"t":43,"s":[131.883,-5.898,0]}]},"a":{"a":0,"k":[131.883,-5.898,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.471,-0.164],[-15.933,-19.728],[0,-29.144],[17.385,-21.886],[25.381,-9.147]],"o":[[25.715,8.926],[17.789,22.026],[0,28.763],[-15.643,19.694],[-0.457,0.165]],"v":[[86.037,-128.22],[149.57,-84.036],[177.729,-5.608],[150.242,71.983],[87.678,116.424]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":30},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.1],"y":[0]},"t":31,"s":[40]},{"t":43,"s":[0]}]},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.1],"y":[0]},"t":31,"s":[60]},{"t":43,"s":[100]}]},"o":{"a":0,"k":0},"m":1,"nm":"Trim Paths 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":31,"op":179,"st":33,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Cross 2","parent":8,"sr":1,"ks":{"p":{"a":1,"k":[{"i":{"x":0.6,"y":1},"o":{"x":0.6,"y":0},"t":21,"s":[146.626,5.78,0],"to":[-3,0,0],"ti":[3,0,0]},{"t":32,"s":[128.626,5.78,0]}]},"a":{"a":0,"k":[140.253,5.78,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.6,"y":1},"o":{"x":0.6,"y":0},"t":21,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[186.424,-40.422],[94.083,51.983]],"c":false}]},{"t":32,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[141.255,0.625],[139.252,10.936]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":30},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[139.75,-0.5]},"a":{"a":0,"k":[139.75,5.5]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.6,"y":1},"o":{"x":0.6,"y":0},"t":21,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[94.083,-40.422],[186.424,51.983]],"c":false}]},{"t":32,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[139.252,0.625],[141.255,10.936]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":30},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[139.75,-0.5]},"a":{"a":0,"k":[139.75,5.5]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 2","bm":0,"hd":false}],"ip":21,"op":31,"st":20,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Speaker 2","sr":1,"ks":{"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.6,"y":0},"t":21,"s":[177.746,256.078,0],"to":[0,0,0],"ti":[-1.062,0,0]},{"t":43,"s":[184.119,256.078,0]}]},"a":{"a":0,"k":[-71.881,0.078,0]},"s":{"a":1,"k":[{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":21,"s":[100,100,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":30,"s":[93,93,100]},{"t":43,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[16.806,-17.492],[0,0],[6.343,0],[0,0],[0,-14.237],[0,0],[-22.972,0],[0,0],[-4.378,-4.611],[0,0],[0,37.153],[0,0]],"o":[[0,-37.153],[0,0],[-4.378,4.611],[0,0],[-22.972,0],[0,0],[0,14.237],[0,0],[6.343,0],[0,0],[16.806,17.492],[0,0],[0,0]],"v":[[33.271,-121.847],[-15.728,-144.085],[-80.954,-75.396],[-97.742,-68.176],[-144.423,-68.176],[-177.034,-39.797],[-177.034,39.953],[-144.423,68.333],[-97.742,68.333],[-80.954,75.552],[-15.728,144.241],[33.271,122.004],[33.271,2.96]],"c":true}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":30},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":21,"op":184,"st":21,"bm":0},{"ddd":0,"ind":9,"ty":0,"nm":"Shadow","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":14},"p":{"a":0,"k":[256,256,0]},"a":{"a":0,"k":[256,256,0]}},"ao":0,"w":512,"h":512,"ip":0,"op":180,"st":0,"bm":0}]} diff --git a/TMessagesProj/src/main/res/raw/msg_stories_archive.json b/TMessagesProj/src/main/res/raw/msg_stories_archive.json new file mode 100644 index 0000000000..143adc7ada --- /dev/null +++ b/TMessagesProj/src/main/res/raw/msg_stories_archive.json @@ -0,0 +1 @@ +{"tgs":1,"v":"5.5.2","fr":60,"ip":50,"op":77,"w":512,"h":512,"nm":"MAIN ARCHIVE","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":3,"nm":"NULL SCALE","sr":1,"ks":{"o":{"a":0,"k":0},"p":{"a":0,"k":[256,256,0]}},"ao":0,"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Arrow Bottom","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"t":0,"s":[100],"h":1},{"t":5,"s":[0],"h":1},{"t":13,"s":[100],"h":1}]},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":4.908,"s":[0]},{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":13.092,"s":[-163]},{"t":27,"s":[0]}]},"p":{"a":0,"k":[4.85,5.823,0]},"a":{"a":0,"k":[4.85,5.823,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-1.936,-1.267],[0,0]],"o":[[0,2.313],[0,0],[0,0]],"v":[[4.85,5.823],[7.952,11.561],[76.85,56.653]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":36},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Path--","bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Arrow Top","parent":1,"sr":1,"ks":{"r":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":21.2734375,"s":[360]}]},"p":{"a":0,"k":[7.952,11.561,0]},"a":{"a":0,"k":[7.952,11.561,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[-1.936,-1.267]],"o":[[0,0],[0,2.313],[0,0]],"v":[[4.85,-81.347],[4.85,5.823],[7.952,11.561]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":36},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Path--","bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Circle Solid 3","parent":1,"sr":1,"ks":{"p":{"a":0,"k":[1.939,0.069,0]},"a":{"a":0,"k":[0.235,0.069,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[{"i":[[0.333,0.025],[5.132,-0.025],[6.278,-0.649],[-10.914,-102.232],[0,0],[-96.933,6.002]],"o":[[-5.047,-0.385],[-6.182,0.03],[-102.899,10.646],[0,0],[14.443,95.394],[0.55,-0.034]],"v":[[14.443,-185.509],[-0.83,-186.052],[-19.528,-185.039],[-186.08,19.345],[-185.022,27.541],[12.114,185.829]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0.818,"s":[{"i":[[0.25,0.026],[12.525,1.574],[20.791,-1.578],[-10.914,-102.232],[0,0],[-96.933,6.002]],"o":[[-11.955,-5.089],[-13.582,-2.777],[-102.899,10.646],[0,0],[14.443,95.394],[0.55,-0.034]],"v":[[70.144,-173.312],[31.842,-183.302],[-19.528,-185.039],[-186.08,19.345],[-185.022,27.541],[12.114,185.829]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":1.637,"s":[{"i":[[0.166,0.026],[14.497,5.486],[35.305,-2.506],[-10.914,-102.232],[0,0],[-96.933,6.002]],"o":[[-17.484,-18.995],[-22.017,-8.333],[-102.898,10.646],[0,0],[14.443,95.394],[0.55,-0.034]],"v":[[130.964,-131.936],[64.003,-175.432],[-19.528,-185.039],[-186.08,19.345],[-185.022,27.541],[12.114,185.829]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":2.455,"s":[{"i":[[0.083,0.026],[30.63,21.947],[49.82,-3.434],[-10.914,-102.232],[0,0],[-96.933,6.002]],"o":[[-13.033,-30.163],[-34.898,-22.83],[-102.898,10.646],[0,0],[14.443,95.394],[0.55,-0.034]],"v":[[173.236,-72.497],[106.912,-153.548],[-19.528,-185.039],[-186.08,19.345],[-185.022,27.541],[12.114,185.829]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":3.273,"s":[{"i":[[0,0.026],[21.621,24.541],[64.334,-4.363],[-10.914,-102.232],[0,0],[-96.933,6.002]],"o":[[0.304,-57.232],[-35.536,-41.582],[-102.898,10.646],[0,0],[14.443,95.394],[0.55,-0.034]],"v":[[187.112,-1.045],[138.899,-124.565],[-19.528,-185.039],[-186.08,19.345],[-185.022,27.541],[12.114,185.829]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":4.092,"s":[{"i":[[-0.084,0.026],[20.551,52.771],[85.903,-5.714],[-10.914,-102.232],[0,0],[-96.933,6.002]],"o":[[20.572,-53.686],[-26.954,-63.418],[-102.898,10.646],[0,0],[14.443,95.394],[0.55,-0.034]],"v":[[174.615,70.856],[168.912,-81.961],[-19.528,-185.039],[-186.08,19.345],[-185.022,27.541],[12.114,185.829]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":4.908,"s":[{"i":[[-0.167,0.026],[13.895,66.018],[107.472,-7.065],[-10.914,-102.232],[0,0],[-96.933,6.002]],"o":[[50.497,-54.469],[-26.272,-87.665],[-102.898,10.646],[0,0],[14.443,95.394],[0.55,-0.034]],"v":[[133.499,131.31],[182.326,-46.225],[-19.528,-185.039],[-186.08,19.345],[-185.022,27.541],[12.114,185.829]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":5.727,"s":[{"i":[[-0.25,0.026],[11.636,89.345],[112.524,-7.937],[-10.914,-102.232],[0,0],[-96.933,6.002]],"o":[[73.41,-29.316],[-17.265,-94.087],[-102.898,10.646],[0,0],[14.443,95.394],[0.55,-0.034]],"v":[[76.468,170.871],[186.017,-28.595],[-19.528,-185.039],[-186.08,19.345],[-185.022,27.541],[12.114,185.829]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":6.545,"s":[{"i":[[-0.334,0.026],[5.677,105.059],[117.576,-8.809],[-10.914,-102.232],[0,0],[-96.933,6.002]],"o":[[104.449,-8.19],[-5.258,-97.311],[-102.898,10.646],[0,0],[14.443,95.394],[0.55,-0.034]],"v":[[12.359,185.66],[186.168,-10.964],[-19.528,-185.039],[-186.08,19.345],[-185.022,27.541],[12.114,185.829]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":12,"s":[{"i":[[-0.334,0.026],[5.677,105.059],[117.576,-8.809],[-10.914,-102.232],[0,0],[-96.933,6.002]],"o":[[104.449,-8.19],[-5.258,-97.311],[-102.898,10.646],[0,0],[14.443,95.394],[0.55,-0.034]],"v":[[12.359,185.66],[186.168,-10.964],[-19.528,-185.039],[-186.08,19.345],[-185.022,27.541],[12.114,185.829]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":12.273,"s":[{"i":[[0.299,-0.026],[-5.729,-99.524],[-111.718,8.379],[11.994,113.311],[0,0],[93.53,-5.041]],"o":[[-86.138,30.327],[5.307,92.187],[102.898,-10.646],[0,0],[-14.188,-100.559],[-0.55,0.03]],"v":[[-51.767,-179.555],[-184.832,14.447],[14.575,184.636],[185.864,-20.34],[184.807,-28.536],[-14.698,-185.64]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":13.092,"s":[{"i":[[0.299,-0.026],[-5.729,-99.523],[-111.718,8.379],[11.994,113.311],[0,0],[59.864,17.494]],"o":[[-86.138,30.327],[5.307,92.188],[102.898,-10.646],[0,0],[-14.188,-100.559],[-0.529,-0.155]],"v":[[-51.767,-179.555],[-184.832,14.447],[14.575,184.636],[185.864,-20.34],[184.807,-28.536],[35.687,-183.296]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":13.908,"s":[{"i":[[0.299,-0.026],[-5.729,-99.523],[-111.718,8.379],[11.994,113.311],[0,0],[27.353,16.812]],"o":[[-95.611,32.726],[5.307,92.188],[102.898,-10.646],[0,0],[-13.091,-87.656],[-0.469,-0.289]],"v":[[-44.252,-182.446],[-184.832,14.447],[14.575,184.636],[185.864,-20.34],[184.807,-28.536],[86.557,-167.688]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":14.727,"s":[{"i":[[0.299,-0.026],[-5.729,-99.523],[-111.718,8.379],[11.994,113.311],[0,0],[27.286,25.404]],"o":[[-100.287,33.351],[5.307,92.188],[102.898,-10.646],[0,0],[-3.273,-40.89],[-0.403,-0.375]],"v":[[-41.407,-182.446],[-184.832,14.447],[14.575,184.636],[185.864,-20.34],[184.807,-28.536],[130.917,-134.664]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":15.545,"s":[{"i":[[0.299,-0.026],[-5.729,-99.523],[-111.718,8.379],[11.994,113.311],[0,0],[6.148,18.869]],"o":[[-100.287,33.351],[5.307,92.188],[102.898,-10.646],[0,0],[-4.98,-29.509],[-0.171,-0.524]],"v":[[-41.407,-182.446],[-184.832,14.447],[14.575,184.636],[185.864,-20.34],[184.807,-28.536],[165.001,-89.913]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":16.363,"s":[{"i":[[0.299,-0.026],[-4.877,-101.303],[-111.718,8.379],[11.994,113.311],[0,0],[0.764,4.105]],"o":[[-107.623,23.026],[4.441,92.233],[102.898,-10.646],[0,0],[-0.43,-8.021],[-0.469,-0.289]],"v":[[-27.664,-184.645],[-184.832,14.447],[14.575,184.636],[185.864,-20.34],[184.807,-28.536],[181.414,-46.159]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":17.182,"s":[{"i":[[0.299,-0.026],[-4.877,-101.303],[-111.718,8.379],[-22.137,75.754],[0,0],[0.764,4.106]],"o":[[-107.623,23.026],[4.441,92.233],[102.899,-10.646],[0,0],[0.275,-7.146],[-0.469,-0.289]],"v":[[-27.664,-184.645],[-184.832,14.447],[14.575,184.636],[184.783,36.697],[186.969,23.905],[187.631,6.011]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":18,"s":[{"i":[[0.299,-0.026],[-4.877,-101.303],[-111.718,8.379],[-18.221,31.995],[0,0],[-2.432,5.704]],"o":[[-116.215,19.041],[4.441,92.233],[87.556,-6.785],[0,0],[2.756,-7.132],[-0.469,-0.289]],"v":[[-15.949,-185.71],[-184.832,14.447],[14.575,184.636],[161.353,93.673],[172.06,72.095],[180.176,50.74]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":19,"s":[{"i":[[0.299,-0.026],[-4.877,-101.303],[-111.718,8.379],[-8.572,10.894],[0,0],[-2.173,4.171]],"o":[[-116.215,19.041],[4.441,92.233],[63.065,-3.352],[0,0],[5.155,-6.15],[-0.469,-0.289]],"v":[[-15.949,-185.71],[-184.832,14.447],[14.575,184.636],[130.614,133.608],[150.516,112.818],[163.887,91.201]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":19.637,"s":[{"i":[[0.334,-0.012],[3.152,-0.203],[6.117,-1.555],[-10.914,-102.232],[0,0],[-91.75,114.985]],"o":[[-4.588,0.162],[-6.169,0.398],[-91.28,23.202],[0,0],[34.21,177.158],[0.344,-0.431]],"v":[[-6.858,-185.768],[-19.534,-185.273],[-37.712,-182.701],[-186.08,19.345],[-185.022,27.541],[146.156,117.25]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20.455,"s":[{"i":[[0.334,-0.008],[3.372,-0.184],[6.134,-1.454],[-10.914,-102.232],[0,0],[-86.054,79.832]],"o":[[-4.639,0.102],[-6.171,0.357],[-92.571,21.806],[0,0],[32.013,168.073],[0.367,-0.387]],"v":[[-4.491,-185.74],[-17.456,-185.359],[-35.691,-182.96],[-186.08,19.345],[-185.022,27.541],[119.345,143.523]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":21.273,"s":[{"i":[[0.334,-0.004],[3.592,-0.164],[6.152,-1.354],[-10.914,-102.232],[0,0],[-87.137,59.065]],"o":[[-4.69,0.041],[-6.172,0.317],[-93.862,20.411],[0,0],[29.817,158.988],[0.389,-0.342]],"v":[[-2.125,-185.711],[-15.377,-185.446],[-33.671,-183.22],[-186.08,19.345],[-185.022,27.541],[98.71,158.473]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":22.092,"s":[{"i":[[0.334,0.001],[3.812,-0.144],[6.17,-1.253],[-10.914,-102.232],[0,0],[-83.229,39.972]],"o":[[-4.741,-0.02],[-6.174,0.276],[-95.153,19.016],[0,0],[27.621,149.903],[0.412,-0.298]],"v":[[0.242,-185.682],[-13.299,-185.532],[-31.651,-183.48],[-186.08,19.345],[-185.022,27.541],[74.421,170.621]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":22.908,"s":[{"i":[[0.334,0.005],[4.032,-0.124],[6.188,-1.152],[-10.914,-102.232],[0,0],[-79.123,24.783]],"o":[[-4.792,-0.081],[-6.175,0.235],[-96.444,17.621],[0,0],[25.425,140.819],[0.435,-0.254]],"v":[[2.609,-185.653],[-11.221,-185.619],[-29.63,-183.74],[-186.08,19.345],[-185.022,27.541],[54.191,178.709]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":23.727,"s":[{"i":[[0.334,0.009],[4.252,-0.104],[6.206,-1.052],[-10.914,-102.232],[0,0],[-74.902,19.201]],"o":[[-4.843,-0.142],[-6.177,0.194],[-97.735,16.226],[0,0],[23.228,131.734],[0.458,-0.21]],"v":[[4.976,-185.624],[-9.143,-185.706],[-27.61,-183.999],[-186.08,19.345],[-185.022,27.541],[36.476,182.01]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":24.545,"s":[{"i":[[0.333,0.013],[4.472,-0.085],[6.224,-0.951],[-10.914,-102.232],[0,0],[-76.245,9.413]],"o":[[-4.894,-0.203],[-6.178,0.153],[-99.026,14.831],[0,0],[21.032,122.649],[0.481,-0.166]],"v":[[7.343,-185.595],[-7.065,-185.792],[-25.589,-184.259],[-186.08,19.345],[-185.022,27.541],[21.869,184.218]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":25.363,"s":[{"i":[[0.333,0.017],[4.692,-0.065],[6.242,-0.851],[-10.914,-102.232],[0,0],[-82.078,4.844]],"o":[[-4.945,-0.263],[-6.179,0.112],[-100.317,13.436],[0,0],[18.836,113.564],[0.504,-0.122]],"v":[[9.709,-185.566],[-4.987,-185.879],[-23.569,-184.519],[-186.08,19.345],[-185.022,27.541],[12.279,185.422]],"c":false}]},{"t":27,"s":[{"i":[[0.333,0.025],[5.132,-0.025],[6.278,-0.649],[-10.914,-102.232],[0,0],[-96.933,6.002]],"o":[[-5.047,-0.385],[-6.182,0.03],[-102.899,10.646],[0,0],[14.443,95.394],[0.55,-0.034]],"v":[[14.443,-185.509],[-0.83,-186.052],[-19.528,-185.039],[-186.08,19.345],[-185.022,27.541],[12.114,185.829]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":32},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Path","bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Circle Dash 2","parent":4,"sr":1,"ks":{"o":{"a":1,"k":[{"t":0,"s":[100],"h":1},{"t":6,"s":[0],"h":1},{"t":15,"s":[100],"h":1}]},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":6.545,"s":[0]},{"i":{"x":[0.5],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":11.455,"s":[-60]},{"t":27,"s":[0]}]},"p":{"a":0,"k":[-0.834,0.21,0]},"a":{"a":0,"k":[-0.834,0.21,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[1.344,0.143],[1.101,7.136],[-8.739,1.274],[-10.686,-0.057],[-3.134,-0.157],[0.451,-8.58],[8.825,0.439],[2.653,0.017],[8.847,-1.29]],"o":[[-7.046,-0.75],[-1.31,-8.496],[10.481,-1.528],[3.12,0.017],[8.829,0.443],[-0.451,8.58],[-2.651,-0.132],[-9.051,-0.057],[-1.399,0.204]],"v":[[-25.649,-168.518],[-39.719,-181.676],[-26.268,-199.367],[5.631,-201.568],[15.066,-201.309],[30.228,-184.98],[13.433,-170.238],[5.441,-170.458],[-21.523,-168.6]],"c":true}},"nm":"Path 1","hd":false},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[5.099,0.529],[2.544,2.141],[10.237,5.55],[-4.213,7.768],[-7.77,-4.212],[-10.576,-8.899],[5.689,-6.762]],"o":[[-3.075,-0.319],[-8.924,-7.509],[-7.769,-4.212],[4.213,-7.768],[12.136,6.579],[6.761,5.689],[-3.548,4.217]],"v":[[121.993,-124.422],[113.358,-128.094],[84.483,-147.774],[78.042,-169.465],[99.734,-175.905],[133.961,-152.58],[135.902,-130.036]],"c":true}},"nm":"Path 2","hd":false},{"ind":2,"ty":"sh","ks":{"a":0,"k":{"i":[[4.63,0.67],[2.481,2.537],[-6.139,6.004],[-11.32,7.249],[-4.63,-7.232],[7.232,-4.63],[8.089,-7.911]],"o":[[-3.26,-0.472],[-6.004,-6.14],[9.605,-9.394],[7.232,-4.631],[4.63,7.232],[-9.535,6.105],[-3.603,3.523]],"v":[[-131.093,-118.643],[-139.97,-123.159],[-139.725,-145.147],[-108.188,-170.227],[-86.71,-165.516],[-91.421,-144.037],[-117.981,-122.914]],"c":true}},"nm":"Path 3","hd":false},{"ind":3,"ty":"sh","ks":{"a":0,"k":{"i":[[1.624,0.16],[1.515,6.955],[4.775,10.621],[-8.06,3.621],[-3.623,-8.06],[-2.944,-13.519],[8.634,-1.882]],"o":[[-6.72,-0.663],[-2.473,-11.354],[-3.623,-8.06],[8.06,-3.621],[5.684,12.643],[1.88,8.634],[-1.68,0.366]],"v":[[182.327,-22.753],[168.269,-35.275],[157.346,-68.392],[165.379,-89.545],[186.533,-81.512],[199.535,-42.085],[187.307,-23.047]],"c":true}},"nm":"Path 4","hd":false},{"ind":4,"ty":"sh","ks":{"a":0,"k":{"i":[[6.474,0.517],[1.779,0.828],[-3.729,8.011],[-2.608,11.315],[-8.609,-1.984],[1.984,-8.611],[5.851,-12.568]],"o":[[-1.847,-0.147],[-8.011,-3.73],[4.91,-10.547],[1.985,-8.611],[8.612,1.985],[-3.108,13.489],[-2.902,6.233]],"v":[[169.326,94.663],[163.848,93.214],[156.097,71.957],[167.427,39.009],[186.611,27.01],[198.61,46.194],[185.106,85.463]],"c":true}},"nm":"Path 5","hd":false},{"ind":5,"ty":"sh","ks":{"a":0,"k":{"i":[[4.056,3.185],[1.02,2.769],[1.985,13.113],[-8.391,1.27],[-1.27,-8.39],[-3.872,-10.508],[7.963,-2.934]],"o":[[-2.162,-1.698],[-4.588,-12.455],[-1.27,-8.391],[8.392,-1.27],[1.675,11.062],[2.934,7.963],[-5.195,1.914]],"v":[[-185.436,75.706],[-190.351,68.941],[-200.257,30.41],[-187.364,12.916],[-169.871,25.809],[-161.513,58.317],[-170.62,78.048]],"c":true}},"nm":"Path 7","hd":false},{"ind":6,"ty":"sh","ks":{"a":0,"k":{"i":[[2.021,4.811],[-0.459,2.951],[-4.69,12.574],[-8.046,-3.001],[3.001,-8.045],[1.741,-11.198],[8.485,1.32]],"o":[[-1.078,-2.565],[2.064,-13.271],[3.002,-8.047],[8.047,3.001],[-3.957,10.608],[-1.319,8.486],[-5.536,-0.861]],"v":[[-198.626,-24.536],[-199.647,-32.936],[-189.469,-71.887],[-169.465,-81.021],[-160.33,-61.019],[-168.918,-28.157],[-186.672,-15.182]],"c":true}},"nm":"Path 11","hd":false},{"ind":7,"ty":"sh","ks":{"a":0,"k":{"i":[[5.233,0.099],[2.52,1.619],[9.627,9.405],[-6.018,6.161],[-6.16,-6.017],[-9.563,-6.142],[4.654,-7.246]],"o":[[-2.79,-0.053],[-11.333,-7.28],[-6.161,-6.019],[6.019,-6.161],[8.122,7.934],[7.246,4.654],[-3.036,4.727]],"v":[[-102.625,170.686],[-110.743,168.213],[-142.332,143.069],[-142.589,121.016],[-120.537,120.758],[-93.887,141.973],[-89.194,163.521]],"c":true}},"nm":"Path 10","hd":false},{"ind":8,"ty":"sh","ks":{"a":0,"k":{"i":[[2.857,0.188],[2.694,5.094],[-7.811,4.131],[-8.978,7.431],[-5.635,-6.807],[6.807,-5.635],[12.221,-6.46]],"o":[[-5.358,-0.353],[-4.131,-7.811],[10.29,-5.442],[6.806,-5.634],[5.635,6.806],[-10.659,8.823],[-2.718,1.437]],"v":[[88.696,179.956],[75.59,171.469],[82.253,149.844],[111.29,130.443],[133.817,132.566],[131.694,155.093],[97.215,178.132]],"c":true}},"nm":"Path 8","hd":false},{"ind":9,"ty":"sh","ks":{"a":0,"k":{"i":[[6.916,0.291],[6.748,0.936],[-1.274,8.326],[-8.755,-1.213],[-11.684,0.618],[-0.499,-8.401],[8.822,-0.476]],"o":[[-6.835,-0.288],[-8.744,-1.212],[1.274,-8.327],[11.461,1.588],[8.824,-0.467],[0.499,8.401],[-6.951,0.375]],"v":[[-8.453,201.084],[-28.923,199.239],[-41.7,181.968],[-24.31,169.088],[10.634,170.536],[27.514,184.885],[12.445,200.958]],"c":true}},"nm":"Path 9","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Stories Archive 3","refId":"comp_0","sr":1,"ks":{"p":{"a":0,"k":[256,256,0]},"a":{"a":0,"k":[256,256,0]}},"ao":0,"w":512,"h":512,"ip":50,"op":230,"st":50,"bm":0}]} \ No newline at end of file diff --git a/TMessagesProj/src/main/res/raw/msg_stories_saved.json b/TMessagesProj/src/main/res/raw/msg_stories_saved.json new file mode 100644 index 0000000000..6b7180a9c2 --- /dev/null +++ b/TMessagesProj/src/main/res/raw/msg_stories_saved.json @@ -0,0 +1 @@ +{"tgs":1,"v":"5.5.2","fr":60,"ip":0,"op":40,"w":512,"h":512,"nm":"MAIN 2","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"","sr":1,"ks":{"p":{"a":0,"k":[261.503,267.484,0]},"a":{"a":0,"k":[549.127,0.073,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[{"i":[[28.608,-29.132],[0,0],[3.505,3.63],[0,0],[-1.12,33.781],[-12.439,7.393],[-13.712,2.739],[-35.521,-19.244],[-0.574,-14.842]],"o":[[0,0],[-1.99,2.423],[0,0],[-26.093,-26.968],[0.492,-14.842],[34.059,-20.244],[10.788,3.239],[11.808,8.364],[-4.619,34.365]],"v":[[554.766,68.971],[554.114,69.791],[544.37,70.209],[543.718,69.307],[468.468,-31.276],[485.065,-67.417],[549.837,-47.9],[613.146,-67.417],[631.243,-30.026]],"c":true}]},{"t":20,"s":[{"i":[[0.162,-0.153],[0,0],[0.023,0.022],[0,0],[-0.004,0.125],[-0.044,0.031],[-0.054,-0.066],[-0.081,-0.058],[-0.002,-0.055]],"o":[[0,0],[-0.023,0.022],[0,0],[-0.162,-0.153],[0.002,-0.055],[0.081,-0.058],[0.054,-0.066],[0.044,0.031],[0.004,0.125]],"v":[[549.169,-1.861],[549.166,-1.858],[549.083,-1.858],[549.08,-1.861],[548.816,-2.239],[548.888,-2.376],[549.124,-2.324],[549.361,-2.376],[549.433,-2.239]],"c":true}]}]},"nm":"Path 1","hd":false},{"ind":1,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.02,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[{"i":[[1.533,45.236],[17.711,12.21],[13.76,-16.098],[29.488,-20.887],[0.657,-19.825],[-52.535,-47.43],[0,0],[-8.434,7.996],[0,0]],"o":[[-0.767,-19.825],[-29.039,-21.54],[-17.99,-19.098],[-15.772,11.172],[-1.424,45.127],[0,0],[8.434,8.105],[0,0],[51.544,-47.649]],"v":[[659.76,-34.574],[632.663,-90.121],[549.115,-82.563],[466.326,-90.621],[438.341,-36.55],[528.659,95.019],[529.864,96.224],[568.876,96.334],[570.08,95.238]],"c":true}]},{"t":20,"s":[{"i":[[1.4,41.3],[14.4,10.2],[17.9,-21.9],[26.9,-19.1],[0.6,-18.1],[-53.5,-50.5],[0,0],[-7.7,7.3],[0,0]],"o":[[-0.7,-18.1],[-26.9,-19.2],[-17.9,-21.9],[-14.4,10.2],[-1.3,41.2],[0,0],[7.7,7.4],[0,0],[53.6,-50.7]],"v":[[651.125,-38.411],[627.225,-84.011],[549.125,-66.611],[471.025,-84.011],[447.125,-38.411],[534.325,86.389],[535.425,87.489],[562.825,87.589],[563.925,86.589]],"c":true}]}]},"nm":"Path 2","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":1,"op":18,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Heart OUTLINE","sr":1,"ks":{"p":{"a":0,"k":[261.4,267.514,0]},"a":{"a":0,"k":[5.4,11.514,0]},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.2],"y":[1,1,1]},"o":{"x":[0.1,0.1,0.1],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"t":7,"s":[92,92,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[7.3,7],[0,0],[-1.3,38.6],[-13.6,9.6],[-16.9,-20.5],[-25.3,-18],[-0.6,-16.9],[50.3,-47.5]],"o":[[-7.3,6.9],[0,0],[-50.4,-47.2],[0.6,-16.9],[25.3,-17.9],[16.9,-20.5],[13.5,9.5],[1.3,38.6],[0,0]],"v":[[18.3,93.4],[-7.5,93.3],[-8.6,92.3],[-90.6,-24.5],[-68.1,-67.2],[5.4,-50.9],[78.9,-67.2],[101.4,-24.5],[19.4,92.5]],"c":true}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":30},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Path","bm":0,"hd":false}],"ip":0,"op":1,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Heart FILL 2","sr":1,"ks":{"p":{"a":0,"k":[261.503,267.484,0]},"a":{"a":0,"k":[5.503,11.484,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[7.7,7.4],[0,0],[-1.3,41.2],[-14.4,10.2],[-17.9,-21.9],[-26.9,-19.2],[-0.7,-18.1],[53.6,-50.7]],"o":[[-7.7,7.3],[0,0],[-53.5,-50.5],[0.6,-18.1],[26.9,-19.1],[17.9,-21.9],[14.4,10.2],[1.4,41.3],[0,0]],"v":[[19.2,99],[-8.2,98.9],[-9.3,97.8],[-96.5,-27],[-72.6,-72.6],[5.5,-55.2],[83.6,-72.6],[107.5,-27],[20.3,98]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Path","bm":0,"hd":false}],"ip":9,"op":182,"st":-63,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Circle Dash","sr":1,"ks":{"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.1],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":6,"s":[-10]},{"t":20,"s":[0]}]},"p":{"a":0,"k":[255.166,256.21,0]},"a":{"a":0,"k":[-0.834,0.21,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[1.344,0.147],[1.101,7.34],[-8.739,1.31],[-10.686,-0.059],[-3.134,-0.162],[0.451,-8.825],[8.825,0.452],[2.653,0.017],[8.847,-1.327]],"o":[[-7.046,-0.772],[-1.31,-8.739],[10.481,-1.572],[3.12,0.017],[8.829,0.455],[-0.451,8.825],[-2.651,-0.136],[-9.051,-0.059],[-1.399,0.21]],"v":[[-25.524,-167.963],[-39.594,-181.498],[-26.143,-199.694],[5.756,-201.958],[15.191,-201.691],[30.353,-184.896],[13.558,-169.733],[5.566,-169.958],[-21.398,-168.047]],"c":true}},"nm":"Path 1","hd":false},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[5.099,0.529],[2.544,2.141],[10.237,5.55],[-4.213,7.768],[-7.77,-4.212],[-10.576,-8.899],[5.689,-6.762]],"o":[[-3.075,-0.319],[-8.924,-7.509],[-7.769,-4.212],[4.213,-7.768],[12.136,6.579],[6.761,5.689],[-3.548,4.217]],"v":[[121.993,-124.422],[113.358,-128.094],[84.483,-147.774],[78.042,-169.465],[99.734,-175.905],[133.961,-152.58],[135.902,-130.036]],"c":true}},"nm":"Path 2","hd":false},{"ind":2,"ty":"sh","ks":{"a":0,"k":{"i":[[4.764,0.69],[2.553,2.61],[-6.317,6.178],[-11.649,7.459],[-4.765,-7.442],[7.442,-4.765],[8.324,-8.14]],"o":[[-3.355,-0.486],[-6.179,-6.318],[9.884,-9.666],[7.442,-4.765],[4.765,7.442],[-9.812,6.282],[-3.707,3.625]],"v":[[-131.838,-117.962],[-140.972,-122.609],[-140.721,-145.235],[-108.269,-171.043],[-86.167,-166.195],[-91.015,-144.093],[-118.346,-122.358]],"c":true}},"nm":"Path 3","hd":false},{"ind":3,"ty":"sh","ks":{"a":0,"k":{"i":[[1.624,0.16],[1.515,6.955],[4.775,10.621],[-8.06,3.621],[-3.623,-8.06],[-2.944,-13.519],[8.634,-1.882]],"o":[[-6.72,-0.663],[-2.473,-11.354],[-3.623,-8.06],[8.06,-3.621],[5.684,12.643],[1.88,8.634],[-1.68,0.366]],"v":[[182.327,-22.753],[168.269,-35.275],[157.346,-68.392],[165.379,-89.545],[186.533,-81.512],[199.535,-42.085],[187.307,-23.047]],"c":true}},"nm":"Path 4","hd":false},{"ind":4,"ty":"sh","ks":{"a":0,"k":{"i":[[6.474,0.517],[1.779,0.828],[-3.729,8.011],[-2.608,11.315],[-8.609,-1.984],[1.984,-8.611],[5.851,-12.568]],"o":[[-1.847,-0.147],[-8.011,-3.73],[4.91,-10.547],[1.985,-8.611],[8.612,1.985],[-3.108,13.489],[-2.902,6.233]],"v":[[169.326,94.663],[163.848,93.214],[156.097,71.957],[167.427,39.009],[186.611,27.01],[198.61,46.194],[185.106,85.463]],"c":true}},"nm":"Path 5","hd":false},{"ind":5,"ty":"sh","ks":{"a":0,"k":{"i":[[4.223,3.317],[1.062,2.883],[2.067,13.654],[-8.737,1.323],[-1.322,-8.736],[-4.031,-10.942],[8.291,-3.055]],"o":[[-2.251,-1.768],[-4.777,-12.968],[-1.322,-8.738],[8.738,-1.323],[1.744,11.518],[3.055,8.292],[-5.409,1.993]],"v":[[-185.158,76.939],[-190.276,69.895],[-200.591,29.775],[-187.166,11.56],[-168.951,24.984],[-160.249,58.834],[-169.731,79.377]],"c":true}},"nm":"Path 7","hd":false},{"ind":6,"ty":"sh","ks":{"a":0,"k":{"i":[[2.08,4.95],[-0.472,3.036],[-4.826,12.938],[-8.279,-3.088],[3.088,-8.278],[1.792,-11.522],[8.731,1.358]],"o":[[-1.109,-2.639],[2.124,-13.656],[3.089,-8.28],[8.28,3.088],[-4.071,10.915],[-1.358,8.731],[-5.696,-0.886]],"v":[[-199.202,-23.829],[-200.254,-32.472],[-189.78,-72.551],[-169.197,-81.951],[-159.797,-61.369],[-168.634,-27.554],[-186.903,-14.204]],"c":true}},"nm":"Path 11","hd":false},{"ind":7,"ty":"sh","ks":{"a":0,"k":{"i":[[5.361,0.307],[2.52,1.758],[9.501,10.021],[-6.412,6.08],[-6.079,-6.411],[-9.563,-6.673],[5.057,-7.246]],"o":[[-2.858,-0.164],[-11.333,-7.909],[-6.08,-6.413],[6.413,-6.081],[8.015,8.454],[7.247,5.057],[-3.299,4.727]],"v":[[-103.241,171.817],[-111.467,168.963],[-142.865,141.942],[-142.263,119.322],[-119.644,119.924],[-93.153,142.721],[-89.189,164.998]],"c":true}},"nm":"Path 10","hd":false},{"ind":8,"ty":"sh","ks":{"a":0,"k":{"i":[[2.857,0.188],[2.694,5.094],[-7.811,4.131],[-8.978,7.431],[-5.635,-6.807],[6.807,-5.635],[12.221,-6.46]],"o":[[-5.358,-0.353],[-4.131,-7.811],[10.29,-5.442],[6.806,-5.634],[5.635,6.806],[-10.659,8.823],[-2.718,1.437]],"v":[[88.696,179.956],[75.59,171.469],[82.253,149.844],[111.29,130.443],[133.817,132.566],[131.694,155.093],[97.215,178.132]],"c":true}},"nm":"Path 8","hd":false},{"ind":9,"ty":"sh","ks":{"a":0,"k":{"i":[[6.916,0.306],[6.748,0.983],[-1.274,8.744],[-8.755,-1.274],[-11.684,0.65],[-0.499,-8.823],[8.822,-0.499]],"o":[[-6.835,-0.302],[-8.744,-1.273],[1.274,-8.745],[11.461,1.668],[8.824,-0.491],[0.499,8.823],[-6.951,0.394]],"v":[[-8.453,202.128],[-28.923,200.191],[-41.7,182.052],[-24.31,168.526],[10.634,170.046],[27.514,185.115],[12.445,201.995]],"c":true}},"nm":"Path 9","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Circle Solid","parent":4,"sr":1,"ks":{"p":{"a":0,"k":[0.235,0.069,0]},"a":{"a":0,"k":[0.235,0.069,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0.333,0.025],[11.489,-1.189],[-10.914,-102.232],[0,0],[-96.933,6.002]],"o":[[-11.125,-0.849],[-102.899,10.646],[0,0],[14.443,95.394],[0.55,-0.034]],"v":[[14.443,-185.509],[-19.528,-185.039],[-186.08,19.345],[-185.022,27.541],[12.114,185.829]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":32},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Path","bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0}]},{"id":"comp_1","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Heart OUTLINE 2","parent":2,"sr":1,"ks":{"p":{"a":0,"k":[5.4,11.514,0]},"a":{"a":0,"k":[5.4,11.514,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":1,"s":[{"i":[[4.099,3.462],[1.165,31.702],[-9.703,5.477],[-9.011,-5.844],[-14.398,-9.011],[0.727,-8.906],[18.43,-11.218]],"o":[[-16.105,-12.452],[-0.422,-11.486],[12.702,-7.038],[8.751,-5.643],[8.927,4.965],[-1.897,30.716],[-2.366,2.141]],"v":[[-1.145,51.202],[-49.116,-10.667],[-37.743,-36.059],[4.406,-6.272],[48.961,-34.489],[58.345,-8.856],[11.634,51.105]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":2,"s":[{"i":[[4.099,3.462],[1.165,31.702],[-13.257,8.809],[-9.011,-5.844],[-18.961,-12.511],[1.155,-6.644],[20.366,-12.105]],"o":[[-20.105,-12.702],[-0.422,-11.486],[18.993,-8.441],[8.751,-5.643],[14.039,10.239],[-0.845,26.356],[-2.366,2.141]],"v":[[-0.895,56.202],[-58.366,-5.917],[-41.743,-39.809],[5.406,-34.022],[54.961,-37.739],[69.345,-5.856],[11.884,56.105]],"c":true}]},{"i":{"x":0.01,"y":1},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[5.076,4.287],[1.444,39.264],[-12.018,6.783],[-11.161,-7.238],[-17.832,-11.161],[0.9,-11.03],[22.827,-13.894]],"o":[[-27.345,-14.627],[-0.523,-14.226],[15.732,-8.717],[10.839,-6.988],[11.057,6.149],[-2.35,38.043],[-2.931,2.651]],"v":[[-2.655,60.377],[-66.694,-13.764],[-47.982,-45.783],[3.911,-43.262],[59.404,-43.839],[76.6,-10.543],[13.173,60.894]],"c":true}]},{"t":16,"s":[{"i":[[9.247,8.259],[-1.299,38.568],[-13.578,9.607],[-16.886,-20.483],[-25.279,-17.985],[-0.6,-16.886],[45.715,-43.727]],"o":[[-42.769,-40.075],[0.6,-16.886],[25.279,-17.885],[16.886,-20.483],[13.489,9.492],[1.299,38.568],[-5.339,5.107]],"v":[[-8.588,92.241],[-90.521,-24.462],[-68.039,-67.127],[5.4,-50.84],[78.839,-67.127],[101.321,-24.462],[19.388,92.441]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":1,"s":[69]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":2,"s":[44]},{"i":{"x":[0.01],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":4,"s":[40]},{"t":16,"s":[30]}]},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Path","bm":0,"hd":false}],"ip":1,"op":17,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Heart FILL","sr":1,"ks":{"p":{"a":0,"k":[261.503,267.484,0]},"a":{"a":0,"k":[5.503,11.484,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[7.7,7.4],[0,0],[-1.3,41.2],[-14.4,10.2],[-17.9,-21.9],[-26.9,-19.2],[-0.7,-18.1],[53.6,-50.7]],"o":[[-7.7,7.3],[0,0],[-53.5,-50.5],[0.6,-18.1],[26.9,-19.1],[17.9,-21.9],[14.4,10.2],[1.4,41.3],[0,0]],"v":[[19.2,99],[-8.2,98.9],[-9.3,97.8],[-96.5,-27],[-72.6,-72.6],[5.5,-55.2],[83.6,-72.6],[107.5,-27],[20.3,98]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Path","bm":0,"hd":false}],"ip":0,"op":1,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Heart OUTLINE","parent":2,"sr":1,"ks":{"p":{"a":0,"k":[5.4,11.514,0]},"a":{"a":0,"k":[5.4,11.514,0]},"s":{"a":1,"k":[{"i":{"x":[0.01,0.01,0.01],"y":[1,1,1]},"o":{"x":[0.1,0.1,0.1],"y":[0,0,0]},"t":0,"s":[91.225,82,100]},{"t":20,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.167,"y":0.167},"t":1,"s":[{"i":[[0,0],[7.3,7],[0,0],[-1.3,38.6],[-13.6,9.6],[-17.238,-27.995],[-25.3,-18],[-0.6,-16.9],[50.3,-47.5]],"o":[[-7.3,6.9],[0,0],[-50.4,-47.2],[0.6,-16.9],[25.3,-17.9],[17.997,-25.319],[13.5,9.5],[1.3,38.6],[0,0]],"v":[[14.505,99.942],[-2.35,101.328],[-3.45,100.328],[-90.058,-32.826],[-69.997,-77.012],[4.858,-46.44],[81.339,-77.904],[101.129,-30.447],[15.605,99.042]],"c":true}]},{"t":16,"s":[{"i":[[0,0],[7.3,7],[0,0],[-1.3,38.6],[-13.6,9.6],[-16.9,-20.5],[-25.3,-18],[-0.6,-16.9],[50.3,-47.5]],"o":[[-7.3,6.9],[0,0],[-50.4,-47.2],[0.6,-16.9],[25.3,-17.9],[16.9,-20.5],[13.5,9.5],[1.3,38.6],[0,0]],"v":[[18.3,93.4],[-7.5,93.3],[-8.6,92.3],[-90.6,-24.5],[-68.1,-67.2],[5.4,-50.9],[78.9,-67.2],[101.4,-24.5],[19.4,92.5]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":30},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Path","bm":0,"hd":false}],"ip":1,"op":183,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Circle Dash","sr":1,"ks":{"r":{"a":1,"k":[{"i":{"x":[0.21],"y":[1]},"o":{"x":[0.1],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.17],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":6,"s":[10]},{"t":20,"s":[0]}]},"p":{"a":0,"k":[255.166,256.21,0]},"a":{"a":0,"k":[-0.834,0.21,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[6.916,0.306],[6.748,0.983],[-1.274,8.744],[-8.755,-1.274],[-11.684,0.65],[-0.499,-8.823],[8.822,-0.499]],"o":[[-6.835,-0.302],[-8.744,-1.273],[1.274,-8.745],[11.461,1.668],[8.824,-0.491],[0.499,8.823],[-6.951,0.394]],"v":[[-8.453,202.128],[-28.923,200.191],[-41.7,182.052],[-24.31,168.526],[10.634,170.046],[27.514,185.115],[12.445,201.995]],"c":true}},"nm":"Path 10","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 10","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[2.857,0.188],[2.694,5.094],[-7.811,4.131],[-8.978,7.431],[-5.635,-6.807],[6.807,-5.635],[12.221,-6.46]],"o":[[-5.358,-0.353],[-4.131,-7.811],[10.29,-5.442],[6.806,-5.634],[5.635,6.806],[-10.659,8.823],[-2.718,1.437]],"v":[[88.696,179.956],[75.59,171.469],[82.253,149.844],[111.29,130.443],[133.817,132.566],[131.694,155.093],[97.215,178.132]],"c":true}},"nm":"Path 9","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 9","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[1.344,0.147],[1.101,7.34],[-8.739,1.31],[-10.686,-0.059],[-3.134,-0.162],[0.451,-8.825],[8.825,0.452],[2.653,0.017],[8.847,-1.327]],"o":[[-7.046,-0.772],[-1.31,-8.739],[10.481,-1.572],[3.12,0.017],[8.829,0.455],[-0.451,8.825],[-2.651,-0.136],[-9.051,-0.059],[-1.399,0.21]],"v":[[-25.524,-167.963],[-39.594,-181.498],[-26.143,-199.694],[5.756,-201.958],[15.191,-201.691],[30.353,-184.896],[13.558,-169.733],[5.566,-169.958],[-21.398,-168.047]],"c":true}},"nm":"Path 1","hd":false},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[5.361,0.307],[2.52,1.758],[9.501,10.021],[-6.412,6.08],[-6.079,-6.411],[-9.563,-6.673],[5.057,-7.246]],"o":[[-2.858,-0.164],[-11.333,-7.909],[-6.08,-6.413],[6.413,-6.081],[8.015,8.454],[7.247,5.057],[-3.299,4.727]],"v":[[-103.241,171.817],[-111.467,168.963],[-142.865,141.942],[-142.263,119.322],[-119.644,119.924],[-93.153,142.721],[-89.189,164.998]],"c":true}},"nm":"Path 8","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 8","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[2.08,4.95],[-0.472,3.036],[-4.826,12.938],[-8.279,-3.088],[3.088,-8.278],[1.792,-11.522],[8.731,1.358]],"o":[[-1.109,-2.639],[2.124,-13.656],[3.089,-8.28],[8.28,3.088],[-4.071,10.915],[-1.358,8.731],[-5.696,-0.886]],"v":[[-199.202,-23.829],[-200.254,-32.472],[-189.78,-72.551],[-169.197,-81.951],[-159.797,-61.369],[-168.634,-27.554],[-186.903,-14.204]],"c":true}},"nm":"Path 7","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 7","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[4.223,3.317],[1.062,2.883],[2.067,13.654],[-8.737,1.323],[-1.322,-8.736],[-4.031,-10.942],[8.291,-3.055]],"o":[[-2.251,-1.768],[-4.777,-12.968],[-1.322,-8.738],[8.738,-1.323],[1.744,11.518],[3.055,8.292],[-5.409,1.993]],"v":[[-185.158,76.939],[-190.276,69.895],[-200.591,29.775],[-187.166,11.56],[-168.951,24.984],[-160.249,58.834],[-169.731,79.377]],"c":true}},"nm":"Path 6","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 6","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[6.474,0.517],[1.779,0.828],[-3.729,8.011],[-2.608,11.315],[-8.609,-1.984],[1.984,-8.611],[5.851,-12.568]],"o":[[-1.847,-0.147],[-8.011,-3.73],[4.91,-10.547],[1.985,-8.611],[8.612,1.985],[-3.108,13.489],[-2.902,6.233]],"v":[[169.326,94.663],[163.848,93.214],[156.097,71.957],[167.427,39.009],[186.611,27.01],[198.61,46.194],[185.106,85.463]],"c":true}},"nm":"Path 5","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 5","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[1.624,0.16],[1.515,6.955],[4.775,10.621],[-8.06,3.621],[-3.623,-8.06],[-2.944,-13.519],[8.634,-1.882]],"o":[[-6.72,-0.663],[-2.473,-11.354],[-3.623,-8.06],[8.06,-3.621],[5.684,12.643],[1.88,8.634],[-1.68,0.366]],"v":[[182.327,-22.753],[168.269,-35.275],[157.346,-68.392],[165.379,-89.545],[186.533,-81.512],[199.535,-42.085],[187.307,-23.047]],"c":true}},"nm":"Path 4","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 4","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[4.764,0.69],[2.553,2.61],[-6.317,6.178],[-11.649,7.459],[-4.765,-7.442],[7.442,-4.765],[8.324,-8.14]],"o":[[-3.355,-0.486],[-6.179,-6.318],[9.884,-9.666],[7.442,-4.765],[4.765,7.442],[-9.812,6.282],[-3.707,3.625]],"v":[[-131.838,-117.962],[-140.972,-122.609],[-140.721,-145.235],[-108.269,-171.043],[-86.167,-166.195],[-91.015,-144.093],[-118.346,-122.358]],"c":true}},"nm":"Path 3","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 3","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[5.099,0.529],[2.544,2.141],[10.237,5.55],[-4.213,7.768],[-7.77,-4.212],[-10.576,-8.899],[5.689,-6.762]],"o":[[-3.075,-0.319],[-8.924,-7.509],[-7.769,-4.212],[4.213,-7.768],[12.136,6.579],[6.761,5.689],[-3.548,4.217]],"v":[[121.993,-124.422],[113.358,-128.094],[84.483,-147.774],[78.042,-169.465],[99.734,-175.905],[133.961,-152.58],[135.902,-130.036]],"c":true}},"nm":"Path 2","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 2","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[1.344,0.147],[1.101,7.34],[-8.739,1.31],[-10.686,-0.059],[-3.134,-0.162],[0.451,-8.825],[8.825,0.452],[2.653,0.017],[8.847,-1.327]],"o":[[-7.046,-0.772],[-1.31,-8.739],[10.481,-1.572],[3.12,0.017],[8.829,0.455],[-0.451,8.825],[-2.651,-0.136],[-9.051,-0.059],[-1.399,0.21]],"v":[[-25.524,-167.963],[-39.594,-181.498],[-26.143,-199.694],[5.756,-201.958],[15.191,-201.691],[30.353,-184.896],[13.558,-169.733],[5.566,-169.958],[-21.398,-168.047]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Circle Solid","parent":4,"sr":1,"ks":{"p":{"a":0,"k":[0.235,0.069,0]},"a":{"a":0,"k":[0.235,0.069,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0.333,0.025],[11.489,-1.189],[-10.914,-102.232],[0,0],[-96.933,6.002]],"o":[[-11.125,-0.849],[-102.899,10.646],[0,0],[14.443,95.394],[0.55,-0.034]],"v":[[14.443,-185.509],[-19.528,-185.039],[-186.08,19.345],[-185.022,27.541],[12.114,185.829]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":32},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Path","bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0}]},{"id":"comp_2","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Heart OUTLINE 2","sr":1,"ks":{"p":{"a":0,"k":[261.4,267.514,0]},"a":{"a":0,"k":[5.4,11.514,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[{"i":[[9.046,8.079],[-1.271,37.727],[-13.282,9.397],[-16.518,-20.036],[-24.728,-17.593],[-0.586,-16.518],[44.718,-42.774]],"o":[[-41.837,-39.201],[0.586,-16.518],[24.728,-17.495],[16.518,-20.036],[13.195,9.285],[1.271,37.727],[-5.222,4.995]],"v":[[-8.283,90.398],[-88.429,-23.761],[-66.438,-65.496],[5.4,-49.564],[77.238,-65.496],[99.229,-23.761],[19.083,90.594]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":2,"s":[{"i":[[5.02,4.483],[3.929,25.63],[-10.222,6.287],[-13.158,-16.796],[-17.561,-11.651],[1.458,-14.061],[27.771,-24.394]],"o":[[-29.962,-25.93],[-5.001,-16.988],[17.078,-10.97],[12,-15.678],[11.162,7.333],[-2.323,28.332],[-2.898,2.772]],"v":[[-1.892,67.517],[-61.931,-7.22],[-46.484,-44.477],[5.503,-30.013],[57.7,-44.301],[73.269,-10.52],[13.251,67.139]],"c":true}]},{"t":4,"s":[{"i":[[2.575,2.305],[3.622,16.484],[-6.732,3.85],[-9.39,-12.633],[-11.137,-7.531],[2.658,-10.702],[16.299,-14.35]],"o":[[-17.765,-15.544],[-4.424,-12.752],[10.512,-6.367],[8.307,-11.945],[7.439,4.672],[-3.966,19.26],[-1.486,1.425]],"v":[[1.602,48.226],[-38.919,0.282],[-28.936,-26.936],[5.507,-17.026],[40.461,-26.644],[49.788,-1.318],[9.531,48.155]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":1,"k":[{"i":{"x":[0.381],"y":[1.414]},"o":{"x":[0.141],"y":[0]},"t":0,"s":[32]},{"t":4,"s":[15]}]},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Path","bm":0,"hd":false}],"ip":1,"op":5,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Heart OUTLINE 3","sr":1,"ks":{"p":{"a":0,"k":[261.4,267.514,0]},"a":{"a":0,"k":[5.4,11.514,0]},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.2],"y":[1,1,1]},"o":{"x":[0.1,0.1,0.1],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"t":8,"s":[79,79,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[{"i":[[0,0],[7.3,7],[0,0],[-1.3,38.6],[-13.6,9.6],[-16.9,-20.5],[-25.3,-18],[-0.6,-16.9],[50.3,-47.5]],"o":[[-7.3,6.9],[0,0],[-50.4,-47.2],[0.6,-16.9],[25.3,-17.9],[16.9,-20.5],[13.5,9.5],[1.3,38.6],[0,0]],"v":[[18.3,93.4],[-7.5,93.3],[-8.6,92.3],[-90.6,-24.5],[-68.1,-67.2],[5.4,-50.9],[78.9,-67.2],[101.4,-24.5],[19.4,92.5]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[0,0],[5.596,5.366],[0,0],[-0.997,29.589],[-10.417,7.37],[-12.955,-15.715],[-19.394,-13.798],[-0.46,-12.955],[38.558,-36.412]],"o":[[-5.596,5.289],[0,0],[-38.635,-36.182],[0.46,-12.955],[19.394,-13.721],[12.955,-15.715],[10.349,7.282],[0.997,29.589],[0,0]],"v":[[15.289,73.36],[-4.489,73.283],[-5.332,72.516],[-68.19,-17.018],[-50.942,-49.751],[5.4,-37.256],[61.742,-49.751],[78.99,-17.018],[16.132,72.67]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":5,"s":[{"i":[[0,0],[2.432,2.332],[0,0],[-0.456,12.857],[-4.527,3.203],[-5.629,-6.828],[-8.427,-5.996],[-0.19,-5.63],[19.447,-14.529]],"o":[[-2.432,2.298],[0,0],[-19.315,-12.816],[0.2,-5.629],[8.427,-5.962],[5.629,-6.828],[4.497,3.164],[0.433,12.857],[0,0]],"v":[[9.574,38.676],[0.98,38.642],[0.614,38.309],[-32.42,-4.031],[-33.222,-22.8],[5.597,-19.388],[41.199,-22.475],[42.905,-3.931],[9.941,38.376]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":6,"s":[{"i":[[0,0],[3.226,3.093],[0,0],[-0.606,17.057],[-6.006,4.249],[-7.469,-9.059],[-11.181,-7.955],[-0.252,-7.469],[25.801,-19.277]],"o":[[-3.226,3.049],[0,0],[-25.626,-17.003],[0.265,-7.469],[11.181,-7.91],[7.469,-9.059],[5.966,4.198],[0.575,17.058],[0,0]],"v":[[11.008,45.072],[-0.394,45.028],[-0.88,44.586],[-42.37,-9.311],[-32.114,-28.495],[5.089,-23.723],[43.252,-27.528],[50.354,-8.342],[11.494,44.674]],"c":true}]},{"t":8,"s":[{"i":[[0,0],[3.226,3.093],[0,0],[-0.606,17.057],[-6.006,4.249],[-7.469,-9.059],[-11.181,-7.955],[-0.252,-7.469],[25.801,-19.277]],"o":[[-3.226,3.049],[0,0],[-25.626,-17.003],[0.265,-7.469],[11.181,-7.91],[7.469,-9.059],[5.966,4.198],[0.575,17.058],[0,0]],"v":[[11.008,45.072],[-0.394,45.028],[-0.88,44.586],[-42.37,-9.311],[-32.114,-28.495],[4.967,-4.42],[43.252,-27.528],[50.354,-8.342],[11.494,44.674]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[30]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":4,"s":[30]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[91]},{"t":6,"s":[81]}]},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Path","bm":0,"hd":false}],"ip":1,"op":8,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Heart FILL 2","sr":1,"ks":{"p":{"a":0,"k":[261.503,267.484,0]},"a":{"a":0,"k":[5.503,11.484,0]},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.2],"y":[1,1,1]},"o":{"x":[0.2,0.2,0.2],"y":[0,0,0]},"t":8,"s":[86,86,100]},{"t":22,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0.167},"t":8,"s":[{"i":[[0,0],[7.7,7.4],[0,0],[-1.3,41.2],[-14.4,10.2],[-15.408,-16.437],[-26.9,-19.2],[-0.7,-18.1],[56.13,-46.09]],"o":[[-7.7,7.3],[0,0],[-59.619,-51.123],[0.6,-18.1],[26.9,-19.1],[9.883,-15.856],[14.4,10.2],[1.4,41.3],[0,0]],"v":[[22.688,98.419],[-11.688,99.772],[-12.788,98.672],[-96.5,-27],[-70.274,-72.309],[5.5,-63.049],[81.565,-72.019],[107.5,-27],[23.788,97.419]],"c":true}]},{"t":15,"s":[{"i":[[0,0],[7.7,7.4],[0,0],[-1.3,41.2],[-14.4,10.2],[-17.9,-21.9],[-26.9,-19.2],[-0.7,-18.1],[53.6,-50.7]],"o":[[-7.7,7.3],[0,0],[-53.5,-50.5],[0.6,-18.1],[26.9,-19.1],[17.9,-21.9],[14.4,10.2],[1.4,41.3],[0,0]],"v":[[19.2,99],[-8.2,98.9],[-9.3,97.8],[-96.5,-27],[-72.6,-72.6],[5.5,-55.2],[83.6,-72.6],[107.5,-27],[20.3,98]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Path","bm":0,"hd":false}],"ip":8,"op":182,"st":-63,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Heart OUTLINE","sr":1,"ks":{"p":{"a":0,"k":[261.4,267.514,0]},"a":{"a":0,"k":[5.4,11.514,0]},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.2],"y":[1,1,1]},"o":{"x":[0.1,0.1,0.1],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"t":8,"s":[79,79,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[7.3,7],[0,0],[-1.3,38.6],[-13.6,9.6],[-16.9,-20.5],[-25.3,-18],[-0.6,-16.9],[50.3,-47.5]],"o":[[-7.3,6.9],[0,0],[-50.4,-47.2],[0.6,-16.9],[25.3,-17.9],[16.9,-20.5],[13.5,9.5],[1.3,38.6],[0,0]],"v":[[18.3,93.4],[-7.5,93.3],[-8.6,92.3],[-90.6,-24.5],[-68.1,-67.2],[5.4,-50.9],[78.9,-67.2],[101.4,-24.5],[19.4,92.5]],"c":true}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":30},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Path","bm":0,"hd":false}],"ip":0,"op":8,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Circle Dash","sr":1,"ks":{"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.1],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":8,"s":[-12]},{"t":22,"s":[0]}]},"p":{"a":0,"k":[255.166,256.21,0]},"a":{"a":0,"k":[-0.834,0.21,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[1.344,0.147],[1.101,7.34],[-8.739,1.31],[-10.686,-0.059],[-3.134,-0.162],[0.451,-8.825],[8.825,0.452],[2.653,0.017],[8.847,-1.327]],"o":[[-7.046,-0.772],[-1.31,-8.739],[10.481,-1.572],[3.12,0.017],[8.829,0.455],[-0.451,8.825],[-2.651,-0.136],[-9.051,-0.059],[-1.399,0.21]],"v":[[-25.524,-167.963],[-39.594,-181.498],[-26.143,-199.694],[5.756,-201.958],[15.191,-201.691],[30.353,-184.896],[13.558,-169.733],[5.566,-169.958],[-21.398,-168.047]],"c":true}},"nm":"Path 1","hd":false},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[5.099,0.529],[2.544,2.141],[10.237,5.55],[-4.213,7.768],[-7.77,-4.212],[-10.576,-8.899],[5.689,-6.762]],"o":[[-3.075,-0.319],[-8.924,-7.509],[-7.769,-4.212],[4.213,-7.768],[12.136,6.579],[6.761,5.689],[-3.548,4.217]],"v":[[121.993,-124.422],[113.358,-128.094],[84.483,-147.774],[78.042,-169.465],[99.734,-175.905],[133.961,-152.58],[135.902,-130.036]],"c":true}},"nm":"Path 2","hd":false},{"ind":2,"ty":"sh","ks":{"a":0,"k":{"i":[[4.764,0.69],[2.553,2.61],[-6.317,6.178],[-11.649,7.459],[-4.765,-7.442],[7.442,-4.765],[8.324,-8.14]],"o":[[-3.355,-0.486],[-6.179,-6.318],[9.884,-9.666],[7.442,-4.765],[4.765,7.442],[-9.812,6.282],[-3.707,3.625]],"v":[[-131.838,-117.962],[-140.972,-122.609],[-140.721,-145.235],[-108.269,-171.043],[-86.167,-166.195],[-91.015,-144.093],[-118.346,-122.358]],"c":true}},"nm":"Path 3","hd":false},{"ind":3,"ty":"sh","ks":{"a":0,"k":{"i":[[1.624,0.16],[1.515,6.955],[4.775,10.621],[-8.06,3.621],[-3.623,-8.06],[-2.944,-13.519],[8.634,-1.882]],"o":[[-6.72,-0.663],[-2.473,-11.354],[-3.623,-8.06],[8.06,-3.621],[5.684,12.643],[1.88,8.634],[-1.68,0.366]],"v":[[182.327,-22.753],[168.269,-35.275],[157.346,-68.392],[165.379,-89.545],[186.533,-81.512],[199.535,-42.085],[187.307,-23.047]],"c":true}},"nm":"Path 4","hd":false},{"ind":4,"ty":"sh","ks":{"a":0,"k":{"i":[[6.474,0.517],[1.779,0.828],[-3.729,8.011],[-2.608,11.315],[-8.609,-1.984],[1.984,-8.611],[5.851,-12.568]],"o":[[-1.847,-0.147],[-8.011,-3.73],[4.91,-10.547],[1.985,-8.611],[8.612,1.985],[-3.108,13.489],[-2.902,6.233]],"v":[[169.326,94.663],[163.848,93.214],[156.097,71.957],[167.427,39.009],[186.611,27.01],[198.61,46.194],[185.106,85.463]],"c":true}},"nm":"Path 5","hd":false},{"ind":5,"ty":"sh","ks":{"a":0,"k":{"i":[[4.223,3.317],[1.062,2.883],[2.067,13.654],[-8.737,1.323],[-1.322,-8.736],[-4.031,-10.942],[8.291,-3.055]],"o":[[-2.251,-1.768],[-4.777,-12.968],[-1.322,-8.738],[8.738,-1.323],[1.744,11.518],[3.055,8.292],[-5.409,1.993]],"v":[[-185.158,76.939],[-190.276,69.895],[-200.591,29.775],[-187.166,11.56],[-168.951,24.984],[-160.249,58.834],[-169.731,79.377]],"c":true}},"nm":"Path 7","hd":false},{"ind":6,"ty":"sh","ks":{"a":0,"k":{"i":[[2.08,4.95],[-0.472,3.036],[-4.826,12.938],[-8.279,-3.088],[3.088,-8.278],[1.792,-11.522],[8.731,1.358]],"o":[[-1.109,-2.639],[2.124,-13.656],[3.089,-8.28],[8.28,3.088],[-4.071,10.915],[-1.358,8.731],[-5.696,-0.886]],"v":[[-199.202,-23.829],[-200.254,-32.472],[-189.78,-72.551],[-169.197,-81.951],[-159.797,-61.369],[-168.634,-27.554],[-186.903,-14.204]],"c":true}},"nm":"Path 11","hd":false},{"ind":7,"ty":"sh","ks":{"a":0,"k":{"i":[[5.361,0.307],[2.52,1.758],[9.501,10.021],[-6.412,6.08],[-6.079,-6.411],[-9.563,-6.673],[5.057,-7.246]],"o":[[-2.858,-0.164],[-11.333,-7.909],[-6.08,-6.413],[6.413,-6.081],[8.015,8.454],[7.247,5.057],[-3.299,4.727]],"v":[[-103.241,171.817],[-111.467,168.963],[-142.865,141.942],[-142.263,119.322],[-119.644,119.924],[-93.153,142.721],[-89.189,164.998]],"c":true}},"nm":"Path 10","hd":false},{"ind":8,"ty":"sh","ks":{"a":0,"k":{"i":[[2.857,0.188],[2.694,5.094],[-7.811,4.131],[-8.978,7.431],[-5.635,-6.807],[6.807,-5.635],[12.221,-6.46]],"o":[[-5.358,-0.353],[-4.131,-7.811],[10.29,-5.442],[6.806,-5.634],[5.635,6.806],[-10.659,8.823],[-2.718,1.437]],"v":[[88.696,179.956],[75.59,171.469],[82.253,149.844],[111.29,130.443],[133.817,132.566],[131.694,155.093],[97.215,178.132]],"c":true}},"nm":"Path 8","hd":false},{"ind":9,"ty":"sh","ks":{"a":0,"k":{"i":[[6.916,0.306],[6.748,0.983],[-1.274,8.744],[-8.755,-1.274],[-11.684,0.65],[-0.499,-8.823],[8.822,-0.499]],"o":[[-6.835,-0.302],[-8.744,-1.273],[1.274,-8.745],[11.461,1.668],[8.824,-0.491],[0.499,8.823],[-6.951,0.394]],"v":[[-8.453,202.128],[-28.923,200.191],[-41.7,182.052],[-24.31,168.526],[10.634,170.046],[27.514,185.115],[12.445,201.995]],"c":true}},"nm":"Path 9","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Circle Solid","parent":5,"sr":1,"ks":{"p":{"a":0,"k":[0.235,0.069,0]},"a":{"a":0,"k":[0.235,0.069,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0.333,0.025],[11.489,-1.189],[-10.914,-102.232],[0,0],[-96.933,6.002]],"o":[[-11.125,-0.849],[-102.899,10.646],[0,0],[14.443,95.394],[0.55,-0.034]],"v":[[14.443,-185.509],[-19.528,-185.039],[-186.08,19.345],[-185.022,27.541],[12.114,185.829]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":32},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Path","bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0}]},{"id":"comp_3","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Heart FILL","sr":1,"ks":{"p":{"a":0,"k":[261.503,267.484,0]},"a":{"a":0,"k":[5.503,11.484,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[7.7,7.4],[0,0],[-1.3,41.2],[-14.4,10.2],[-17.9,-21.9],[-26.9,-19.2],[-0.7,-18.1],[53.6,-50.7]],"o":[[-7.7,7.3],[0,0],[-53.5,-50.5],[0.6,-18.1],[26.9,-19.1],[17.9,-21.9],[14.4,10.2],[1.4,41.3],[0,0]],"v":[[19.2,99],[-8.2,98.9],[-9.3,97.8],[-96.5,-27],[-72.6,-72.6],[5.5,-55.2],[83.6,-72.6],[107.5,-27],[20.3,98]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Path","bm":0,"hd":false}],"ip":0,"op":1,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Heart OUTLINE 2","parent":1,"sr":1,"ks":{"p":{"a":0,"k":[5.4,11.514,0]},"a":{"a":0,"k":[5.4,11.514,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.1,"y":0},"t":0,"s":[{"i":[[0,0],[6.528,13.652],[-14.65,5.47],[-9.657,-8.027],[-18.15,-9.28],[4.493,-8.75],[19.325,-17.331]],"o":[[-16.794,-13.23],[-4.543,-9.5],[15.052,-5.62],[16.1,-16.663],[15.1,7.22],[-6.912,13.462],[0,0]],"v":[[4.132,58.98],[-55.707,-7.5],[-48.35,-44.22],[5.3,-5.337],[62.9,-42.97],[64.507,-2.75],[5.85,60.581]],"c":true}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0.167},"t":3,"s":[{"i":[[5.076,4.287],[1.444,39.264],[-12.018,6.783],[-11.161,-7.238],[-17.832,-11.161],[0.9,-11.03],[22.827,-13.894]],"o":[[-27.345,-14.627],[-0.523,-14.226],[15.732,-8.717],[10.839,-6.988],[11.057,6.149],[-2.35,38.043],[-2.931,2.651]],"v":[[-2.655,60.377],[-66.694,-13.764],[-47.982,-45.783],[3.911,-43.262],[59.404,-43.839],[76.6,-10.543],[13.173,60.894]],"c":true}]},{"t":8,"s":[{"i":[[10.044,8.971],[-1.411,41.893],[-14.749,10.435],[-18.342,-22.249],[-27.458,-19.535],[-0.651,-18.342],[49.656,-47.497]],"o":[[-46.456,-43.529],[0.651,-18.342],[27.458,-19.427],[18.342,-22.249],[14.652,10.31],[1.411,41.893],[-5.799,5.547]],"v":[[-9.794,99.529],[-98.789,-27.234],[-74.37,-73.576],[5.4,-55.886],[85.17,-73.576],[109.589,-27.234],[20.594,99.747]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":1,"k":[{"i":{"x":[0.416],"y":[0.642]},"o":{"x":[0.16],"y":[0]},"t":0,"s":[73]},{"i":{"x":[0.553],"y":[1]},"o":{"x":[0.233],"y":[0.982]},"t":3,"s":[40]},{"t":8,"s":[32]}]},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Path","bm":0,"hd":false}],"ip":1,"op":8,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Heart OUTLINE","parent":1,"sr":1,"ks":{"p":{"a":0,"k":[5.4,11.514,0]},"a":{"a":0,"k":[5.4,11.514,0]},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.1,0.1,0.1],"y":[0,0,0]},"t":0,"s":[89,80,100]},{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.2,0.2,0.2],"y":[0,0,0]},"t":8,"s":[110,110,100]},{"t":22,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[7.3,7],[0,0],[-1.3,38.6],[-13.6,9.6],[-16.9,-20.5],[-25.3,-18],[-0.6,-16.9],[50.3,-47.5]],"o":[[-7.3,6.9],[0,0],[-50.4,-47.2],[0.6,-16.9],[25.3,-17.9],[16.9,-20.5],[13.5,9.5],[1.3,38.6],[0,0]],"v":[[18.3,93.4],[-7.5,93.3],[-8.6,92.3],[-90.6,-24.5],[-68.1,-67.2],[5.4,-50.9],[78.9,-67.2],[101.4,-24.5],[19.4,92.5]],"c":true}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":30},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Path","bm":0,"hd":false}],"ip":0,"op":183,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Circle Dash","sr":1,"ks":{"r":{"a":1,"k":[{"i":{"x":[0.21],"y":[1]},"o":{"x":[0.1],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":8,"s":[12]},{"t":22,"s":[0]}]},"p":{"a":0,"k":[255.166,256.21,0]},"a":{"a":0,"k":[-0.834,0.21,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[6.916,0.306],[6.748,0.983],[-1.274,8.744],[-8.755,-1.274],[-11.684,0.65],[-0.499,-8.823],[8.822,-0.499]],"o":[[-6.835,-0.302],[-8.744,-1.273],[1.274,-8.745],[11.461,1.668],[8.824,-0.491],[0.499,8.823],[-6.951,0.394]],"v":[[-8.453,202.128],[-28.923,200.191],[-41.7,182.052],[-24.31,168.526],[10.634,170.046],[27.514,185.115],[12.445,201.995]],"c":true}},"nm":"Path 10","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 10","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[2.857,0.188],[2.694,5.094],[-7.811,4.131],[-8.978,7.431],[-5.635,-6.807],[6.807,-5.635],[12.221,-6.46]],"o":[[-5.358,-0.353],[-4.131,-7.811],[10.29,-5.442],[6.806,-5.634],[5.635,6.806],[-10.659,8.823],[-2.718,1.437]],"v":[[88.696,179.956],[75.59,171.469],[82.253,149.844],[111.29,130.443],[133.817,132.566],[131.694,155.093],[97.215,178.132]],"c":true}},"nm":"Path 9","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 9","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[1.344,0.147],[1.101,7.34],[-8.739,1.31],[-10.686,-0.059],[-3.134,-0.162],[0.451,-8.825],[8.825,0.452],[2.653,0.017],[8.847,-1.327]],"o":[[-7.046,-0.772],[-1.31,-8.739],[10.481,-1.572],[3.12,0.017],[8.829,0.455],[-0.451,8.825],[-2.651,-0.136],[-9.051,-0.059],[-1.399,0.21]],"v":[[-25.524,-167.963],[-39.594,-181.498],[-26.143,-199.694],[5.756,-201.958],[15.191,-201.691],[30.353,-184.896],[13.558,-169.733],[5.566,-169.958],[-21.398,-168.047]],"c":true}},"nm":"Path 1","hd":false},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[5.361,0.307],[2.52,1.758],[9.501,10.021],[-6.412,6.08],[-6.079,-6.411],[-9.563,-6.673],[5.057,-7.246]],"o":[[-2.858,-0.164],[-11.333,-7.909],[-6.08,-6.413],[6.413,-6.081],[8.015,8.454],[7.247,5.057],[-3.299,4.727]],"v":[[-103.241,171.817],[-111.467,168.963],[-142.865,141.942],[-142.263,119.322],[-119.644,119.924],[-93.153,142.721],[-89.189,164.998]],"c":true}},"nm":"Path 8","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 8","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[2.08,4.95],[-0.472,3.036],[-4.826,12.938],[-8.279,-3.088],[3.088,-8.278],[1.792,-11.522],[8.731,1.358]],"o":[[-1.109,-2.639],[2.124,-13.656],[3.089,-8.28],[8.28,3.088],[-4.071,10.915],[-1.358,8.731],[-5.696,-0.886]],"v":[[-199.202,-23.829],[-200.254,-32.472],[-189.78,-72.551],[-169.197,-81.951],[-159.797,-61.369],[-168.634,-27.554],[-186.903,-14.204]],"c":true}},"nm":"Path 7","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 7","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[4.223,3.317],[1.062,2.883],[2.067,13.654],[-8.737,1.323],[-1.322,-8.736],[-4.031,-10.942],[8.291,-3.055]],"o":[[-2.251,-1.768],[-4.777,-12.968],[-1.322,-8.738],[8.738,-1.323],[1.744,11.518],[3.055,8.292],[-5.409,1.993]],"v":[[-185.158,76.939],[-190.276,69.895],[-200.591,29.775],[-187.166,11.56],[-168.951,24.984],[-160.249,58.834],[-169.731,79.377]],"c":true}},"nm":"Path 6","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 6","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[6.474,0.517],[1.779,0.828],[-3.729,8.011],[-2.608,11.315],[-8.609,-1.984],[1.984,-8.611],[5.851,-12.568]],"o":[[-1.847,-0.147],[-8.011,-3.73],[4.91,-10.547],[1.985,-8.611],[8.612,1.985],[-3.108,13.489],[-2.902,6.233]],"v":[[169.326,94.663],[163.848,93.214],[156.097,71.957],[167.427,39.009],[186.611,27.01],[198.61,46.194],[185.106,85.463]],"c":true}},"nm":"Path 5","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 5","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[1.624,0.16],[1.515,6.955],[4.775,10.621],[-8.06,3.621],[-3.623,-8.06],[-2.944,-13.519],[8.634,-1.882]],"o":[[-6.72,-0.663],[-2.473,-11.354],[-3.623,-8.06],[8.06,-3.621],[5.684,12.643],[1.88,8.634],[-1.68,0.366]],"v":[[182.327,-22.753],[168.269,-35.275],[157.346,-68.392],[165.379,-89.545],[186.533,-81.512],[199.535,-42.085],[187.307,-23.047]],"c":true}},"nm":"Path 4","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 4","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[4.764,0.69],[2.553,2.61],[-6.317,6.178],[-11.649,7.459],[-4.765,-7.442],[7.442,-4.765],[8.324,-8.14]],"o":[[-3.355,-0.486],[-6.179,-6.318],[9.884,-9.666],[7.442,-4.765],[4.765,7.442],[-9.812,6.282],[-3.707,3.625]],"v":[[-131.838,-117.962],[-140.972,-122.609],[-140.721,-145.235],[-108.269,-171.043],[-86.167,-166.195],[-91.015,-144.093],[-118.346,-122.358]],"c":true}},"nm":"Path 3","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 3","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[5.099,0.529],[2.544,2.141],[10.237,5.55],[-4.213,7.768],[-7.77,-4.212],[-10.576,-8.899],[5.689,-6.762]],"o":[[-3.075,-0.319],[-8.924,-7.509],[-7.769,-4.212],[4.213,-7.768],[12.136,6.579],[6.761,5.689],[-3.548,4.217]],"v":[[121.993,-124.422],[113.358,-128.094],[84.483,-147.774],[78.042,-169.465],[99.734,-175.905],[133.961,-152.58],[135.902,-130.036]],"c":true}},"nm":"Path 2","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 2","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[1.344,0.147],[1.101,7.34],[-8.739,1.31],[-10.686,-0.059],[-3.134,-0.162],[0.451,-8.825],[8.825,0.452],[2.653,0.017],[8.847,-1.327]],"o":[[-7.046,-0.772],[-1.31,-8.739],[10.481,-1.572],[3.12,0.017],[8.829,0.455],[-0.451,8.825],[-2.651,-0.136],[-9.051,-0.059],[-1.399,0.21]],"v":[[-25.524,-167.963],[-39.594,-181.498],[-26.143,-199.694],[5.756,-201.958],[15.191,-201.691],[30.353,-184.896],[13.558,-169.733],[5.566,-169.958],[-21.398,-168.047]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Circle Solid","parent":4,"sr":1,"ks":{"p":{"a":0,"k":[0.235,0.069,0]},"a":{"a":0,"k":[0.235,0.069,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0.333,0.025],[11.489,-1.189],[-10.914,-102.232],[0,0],[-96.933,6.002]],"o":[[-11.125,-0.849],[-102.899,10.646],[0,0],[14.443,95.394],[0.55,-0.034]],"v":[[14.443,-185.509],[-19.528,-185.039],[-186.08,19.345],[-185.022,27.541],[12.114,185.829]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":32},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Path","bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Stories Saved NEW","refId":"comp_0","sr":1,"ks":{"p":{"a":0,"k":[256,256,0]},"a":{"a":0,"k":[256,256,0]}},"ao":0,"w":512,"h":512,"ip":0,"op":20,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"Stories Saved NEW Reverse","refId":"comp_1","sr":1,"ks":{"p":{"a":0,"k":[256,256,0]},"a":{"a":0,"k":[256,256,0]}},"ao":0,"w":512,"h":512,"ip":20,"op":40,"st":20,"bm":0},{"ddd":0,"ind":3,"ty":0,"nm":"Stories Saved NEW","refId":"comp_2","sr":1,"ks":{"p":{"a":0,"k":[256,256,0]},"a":{"a":0,"k":[256,256,0]}},"ao":0,"w":512,"h":512,"ip":40,"op":62,"st":40,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"Stories Saved NEW Reverse","refId":"comp_3","sr":1,"ks":{"p":{"a":0,"k":[256,256,0]},"a":{"a":0,"k":[256,256,0]}},"ao":0,"w":512,"h":512,"ip":62,"op":242,"st":62,"bm":0}]} \ No newline at end of file diff --git a/TMessagesProj/src/main/res/raw/msg_story_keep.json b/TMessagesProj/src/main/res/raw/msg_story_keep.json new file mode 100644 index 0000000000..7e7031a50f --- /dev/null +++ b/TMessagesProj/src/main/res/raw/msg_story_keep.json @@ -0,0 +1 @@ +{"tgs":1,"v":"5.5.2","fr":60,"ip":0,"op":46,"w":512,"h":512,"nm":"TOAST PROFILE MAIN 2","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":3,"ty":4,"nm":"Done Circle","sr":1,"ks":{"p":{"a":1,"k":[{"i":{"x":0.7,"y":1},"o":{"x":0.8,"y":0},"t":5,"s":[254,250.5,0],"to":[0,0,0],"ti":[0,0,0]},{"t":31,"s":[373.5,376.746,0]}]},"a":{"a":0,"k":[117.5,120.746,0]},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.2],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.4],"y":[0,0,0]},"t":0,"s":[110,110,100]},{"i":{"x":[0.5,0.5,0.5],"y":[1,1,1]},"o":{"x":[0.2,0.2,0.2],"y":[0,0,0]},"t":8,"s":[250,250,100]},{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.7],"y":[0,0,0]},"t":16,"s":[215,215,100]},{"i":{"x":[0.5,0.5,0.5],"y":[1,1,1]},"o":{"x":[0.2,0.2,0.2],"y":[0,0,0]},"t":31,"s":[75,75,100]},{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":39,"s":[104,104,100]},{"t":45,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[{"i":[[4.75,5.01],[0.489,-0.284],[0,0],[0,0],[-13.031,-13.336],[0,0],[-5.703,5.948],[-0.188,0.041]],"o":[[-5.306,-5.596],[0,0],[0,0],[-13.868,-12.116],[0,0],[6.007,5.647],[0.2,0.013],[5.259,-5.633]],"v":[[85.859,127.053],[74.472,116.822],[74.488,116.764],[82.579,123.76],[63.263,143.905],[64.77,145.933],[85.972,145.388],[86.515,145.091]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":6,"s":[{"i":[[4.281,4.516],[0.44,-0.256],[0,0],[0,0],[-11.746,-12.02],[0,0],[-5.14,5.361],[-0.17,0.037]],"o":[[-4.782,-5.044],[0,0],[0,0],[-12.5,-10.92],[0,0],[5.414,5.09],[0.18,0.012],[4.74,-5.077]],"v":[[114.402,156.012],[104.139,146.79],[104.153,146.738],[80.594,123.734],[63.184,141.891],[95.394,173.029],[114.505,172.538],[114.994,172.27]],"c":true}]},{"i":{"x":0.2,"y":1},"o":{"x":0.167,"y":0.167},"t":8,"s":[{"i":[[5.198,4.758],[4.805,-5.147],[0,0],[0,0],[-11.907,-12.185],[0,0],[-5.211,5.434],[-0.172,0.037]],"o":[[-5.198,-4.758],[0,0],[0,0],[-12.671,-11.07],[0,0],[5.488,5.16],[0.183,0.012],[4.805,-5.147]],"v":[[126.235,144.764],[108.123,145.468],[104.005,146.756],[80.123,123.436],[62.473,141.842],[95.126,173.407],[114.499,172.91],[126.946,162.697]],"c":true}]},{"i":{"x":0.7,"y":1},"o":{"x":0.65,"y":0},"t":19,"s":[{"i":[[5.207,4.766],[4.813,-5.155],[0,0],[0,0],[-11.927,-12.206],[0,0],[-5.22,5.444],[-0.172,0.037]],"o":[[-5.207,-4.766],[0,0],[0,0],[-12.693,-11.089],[0,0],[5.498,5.169],[0.183,0.012],[4.813,-5.156]],"v":[[177.933,85.038],[159.791,85.744],[102.181,147.903],[78.257,124.543],[60.577,142.981],[93.286,174.601],[112.693,174.103],[178.646,103.003]],"c":true}]},{"i":{"x":0.5,"y":1},"o":{"x":0.2,"y":0},"t":31,"s":[{"i":[[4.476,4.097],[4.138,-4.432],[0,0],[0,0],[-10.254,-10.494],[0,0],[-4.488,4.68],[-0.148,0.032]],"o":[[-4.476,-4.097],[0,0],[0,0],[-10.913,-9.534],[0,0],[4.727,4.444],[0.157,0.01],[4.138,-4.433]],"v":[[169.253,91.348],[153.656,91.955],[104.127,145.395],[83.559,125.312],[68.359,141.163],[96.48,168.347],[113.164,167.919],[169.866,106.793]],"c":true}]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":39,"s":[{"i":[[5.373,4.918],[4.967,-5.32],[0,0],[0,0],[-12.309,-12.596],[0,0],[-5.387,5.618],[-0.178,0.038]],"o":[[-5.373,-4.918],[0,0],[0,0],[-13.099,-11.444],[0,0],[5.674,5.334],[0.189,0.012],[4.967,-5.321]],"v":[[179.911,83.601],[161.189,84.329],[101.737,148.475],[77.049,124.368],[58.804,143.396],[92.558,176.026],[112.585,175.512],[180.647,102.139]],"c":true}]},{"t":45,"s":[{"i":[[4.847,4.437],[4.481,-4.799],[0,0],[0,0],[-11.104,-11.363],[0,0],[-4.86,5.068],[-0.16,0.035]],"o":[[-4.847,-4.437],[0,0],[0,0],[-11.817,-10.323],[0,0],[5.118,4.812],[0.17,0.011],[4.481,-4.8]],"v":[[173.661,88.144],[156.771,88.801],[103.139,146.669],[80.867,124.922],[64.408,142.087],[94.858,171.523],[112.925,171.059],[174.325,104.868]],"c":true}]}]},"nm":"Path 2","hd":false},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[-53.019,0],[0,-53.019],[46.209,-0.701],[2.547,64.367]],"o":[[53.019,0],[-2.5,62.958],[-47.256,0.717],[0,-53.019]],"v":[[117.5,25],[213.5,121],[119.064,216.481],[21.5,120.999]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 2","bm":0,"hd":false}],"ip":1,"op":180,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Person Circle","sr":1,"ks":{"p":{"a":1,"k":[{"i":{"x":0.7,"y":1},"o":{"x":0.8,"y":0},"t":5,"s":[381.616,370.123,0],"to":[0,0,0],"ti":[0,0,0]},{"t":31,"s":[253.616,251.123,0]}]},"a":{"a":0,"k":[-2.384,-4.877,0]},"s":{"a":1,"k":[{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.65,0.65,0.65],"y":[0,0,0]},"t":16,"s":[32,32,100]},{"i":{"x":[0.5,0.5,0.5],"y":[1,1,1]},"o":{"x":[0.2,0.2,0.2],"y":[0,0,0]},"t":31,"s":[110,110,100]},{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":39,"s":[98,98,100]},{"t":45,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":16,"s":[{"i":[[30.031,-0.184],[14.005,-14.839],[-14.064,-14.569],[-13.841,-4.141],[-22.077,24.252],[14.913,14.914]],"o":[[-29.947,-0.185],[-14.691,14.847],[8.472,9.114],[36.282,10.854],[14.007,-14.715],[-14.173,-14.812]],"v":[[-2.511,55.277],[-72.775,80.208],[-72.775,131.131],[-38.279,151.032],[67.753,131.272],[67.629,80.208]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":25,"s":[{"i":[[27.682,-0.224],[12.91,-18.087],[-12.964,-17.758],[-12.759,-5.047],[-20.35,29.56],[13.747,18.178]],"o":[[-27.605,-0.226],[-13.542,18.096],[7.81,11.109],[33.445,13.23],[12.912,-17.936],[-13.064,-18.053]],"v":[[49.224,-45.405],[-15.545,-15.018],[-15.545,47.051],[16.254,71.308],[113.993,47.224],[113.879,-15.018]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":26,"s":[{"i":[[26.971,-0.236],[12.578,-19.072],[-12.631,-18.724],[-8.454,-0.466],[-32.492,21.442],[13.393,19.168]],"o":[[-26.896,-0.238],[-13.194,19.081],[9.192,14.346],[20.76,-2.222],[20.2,-12.157],[-12.728,-19.036]],"v":[[35.154,-65.155],[-25.004,-30.169],[-31.221,26.116],[-1.774,47.187],[87.076,4.582],[94.874,-39.331]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":27,"s":[{"i":[[26.259,-0.248],[12.246,-20.056],[-12.297,-19.691],[-4.15,4.114],[-44.633,13.324],[13.04,20.157]],"o":[[-26.186,-0.25],[-12.846,20.066],[10.575,17.583],[9.8,-18.177],[27.488,-6.379],[-12.393,-20.018]],"v":[[14.54,-97.339],[-46.898,-63.644],[-46.898,5.181],[-23.972,20.765],[58.721,-40.073],[75.87,-63.644]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.167,"y":0.167},"t":28,"s":[{"i":[[25.547,-0.261],[11.914,-21.041],[-11.964,-20.657],[-11.774,-5.871],[-18.78,34.386],[12.687,21.146]],"o":[[-25.476,-0.262],[-12.498,21.051],[7.207,12.922],[30.865,15.39],[11.916,-20.865],[-12.057,-21.001]],"v":[[-2.802,-123.306],[-62.575,-87.958],[-62.575,-15.755],[-33.229,12.462],[56.971,-15.554],[56.865,-87.958]],"c":true}]},{"i":{"x":0.5,"y":1},"o":{"x":0.2,"y":0},"t":31,"s":[{"i":[[24.53,-0.278],[11.44,-22.447],[-11.488,-22.038],[-11.306,-6.263],[-18.033,36.684],[12.182,22.559]],"o":[[-24.462,-0.28],[-12,22.458],[6.92,13.786],[29.636,16.418],[11.442,-22.259],[-11.577,-22.404]],"v":[[-2.868,-163.8],[-60.262,-126.089],[-60.262,-49.061],[-32.084,-18.959],[54.526,-48.847],[54.425,-126.089]],"c":true}]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":39,"s":[{"i":[[28.476,-0.273],[13.28,-22.086],[-13.336,-21.683],[-13.124,-6.163],[-20.934,36.094],[14.141,22.197]],"o":[[-28.397,-0.275],[-13.931,22.097],[8.034,13.564],[34.404,16.154],[13.282,-21.901],[-13.439,-22.044]],"v":[[-2.541,-112.38],[-69.168,-75.276],[-69.167,0.514],[-36.457,30.132],[64.086,0.724],[63.969,-75.276]],"c":true}]},{"t":45,"s":[{"i":[[26.192,-0.28],[12.215,-22.645],[-12.266,-22.233],[-12.072,-6.319],[-19.254,37.009],[13.007,22.759]],"o":[[-26.119,-0.282],[-12.813,22.657],[7.389,13.908],[31.644,16.563],[12.217,-22.456],[-12.361,-22.603]],"v":[[-2.608,-124.645],[-63.89,-86.601],[-63.889,-8.891],[-33.803,21.477],[58.673,-8.675],[58.565,-86.601]],"c":true}]}]},"nm":"Path 2","hd":false},{"ind":1,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":16,"s":[{"i":[[-112.731,-1.936],[-35.64,-28.543],[20.475,-82.195],[0.354,0.388],[44.52,-35.386],[2.284,0.141],[19.23,-26.705],[-36.598,-2.064],[-0.293,-0.571],[-7.748,-8.03],[-0.41,-0.412],[44.334,71.573],[-0.322,39.68]],"o":[[49.221,-0.391],[61.368,49.149],[-0.35,-0.395],[-40.931,-44.914],[-11.017,8.757],[-51.758,-3.197],[32.511,31.107],[0.283,0.577],[5.132,10.015],[0.404,0.418],[-87.864,17.594],[-19.665,-31.747],[-1.805,-112.663]],"v":[[-2.608,-215],[127.258,-169.504],[201.749,44.195],[200.693,43.02],[45.831,32.109],[11.351,64.508],[-84.447,119.297],[15.131,171.218],[15.995,172.94],[35.454,200.149],[36.674,201.394],[-181.731,103.508],[-212.5,-5.109]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":25,"s":[{"i":[[-112.708,-2.995],[-35.252,-28.514],[25.294,-3.015],[23.533,-9.011],[20.343,-15.128],[7.344,-8.504],[2.231,-23.781],[0,0],[0,0],[0,0],[0,0],[-0.627,-3.26],[-0.689,39.36]],"o":[[48.872,0.071],[-32.122,-9.582],[-0.339,-0.39],[-23.533,9.011],[-18.641,11.955],[-45.851,50.406],[-0.891,8.339],[0,0],[0,0],[0,0],[0,0],[-20.3,-30.518],[-0.747,-112.675]],"v":[[-5.627,-216.861],[123.05,-170.762],[35.545,-176.484],[-24.921,-162.087],[-88.808,-129.112],[-127.701,-95.614],[-186.815,32.24],[-187.765,46.023],[-188.321,58.402],[-188.639,75.709],[-187.864,88.419],[-185.365,101.573],[-217.481,-8.951]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":26,"s":[{"i":[[-112.731,-1.936],[-35.64,-28.543],[-5.924,-17.016],[0.412,0.31],[46.009,-79.163],[2.627,-11.268],[-1.831,-20.094],[0.173,-0.456],[-0.644,-1.959],[-2.889,-5.71],[2.915,0.501],[37.487,56.394],[-0.322,39.681]],"o":[[49.221,-0.391],[61.368,49.149],[-0.346,-0.381],[-105.361,-79.072],[-8.31,12.459],[-10.35,24.007],[1.129,10.786],[0.003,-0.373],[6.783,22.418],[0.287,0.567],[-15.68,-1.045],[-20.404,-30.696],[-1.805,-112.663]],"v":[[-2.608,-215],[127.258,-169.504],[207.269,-64.588],[205.956,-65.184],[-82.192,-10.775],[-103.157,36.156],[-110.921,120.926],[-108.387,143.066],[-106.459,148.896],[-93.713,181.849],[-94.411,182.675],[-181.652,102.356],[-212.5,-5.109]],"c":true}]},{"i":{"x":0.7,"y":1},"o":{"x":0.167,"y":0.167},"t":27,"s":[{"i":[[-112.731,-1.936],[-35.64,-28.543],[4.974,-49.517],[0.304,0.394],[51.819,-96.918],[1.096,-2.021],[20.189,-32.958],[-23.999,-6.177],[-3.215,-0.03],[-6.747,-5.437],[2.506,0.337],[44.426,70.225],[-0.322,39.68]],"o":[[49.221,-0.391],[61.368,49.149],[-0.332,-0.375],[-49.814,-64.581],[-2.041,2.674],[-30.674,16.388],[25.366,27.73],[1.698,-0.234],[6.283,12.07],[0.452,0.364],[-42.575,3.734],[-19.705,-31.149],[-1.805,-112.663]],"v":[[-2.608,-215],[127.258,-169.504],[208.364,-1.824],[206.779,-1.428],[-31.188,31.771],[-39.584,47.32],[-115.47,119.676],[-39.644,168.54],[-38.178,169.192],[-15.98,203.744],[-19.007,204.42],[-181.652,102.356],[-212.5,-5.109]],"c":true}]},{"i":{"x":0.2,"y":1},"o":{"x":0.2,"y":0},"t":28,"s":[{"i":[[-112.731,-1.936],[-35.64,-28.543],[20.475,-82.195],[0.289,0.406],[41.009,-48.391],[1.095,-2.021],[22.526,-32.393],[-50.348,-0.298],[-3.612,-0.231],[-7.201,-5.848],[14.245,-2.519],[44.426,70.225],[-0.322,39.68]],"o":[[49.221,-0.391],[61.368,49.149],[-0.332,-0.375],[-55.033,-77.18],[-2.041,2.674],[-67.293,4.96],[29.799,29.426],[1.925,-0.128],[6.213,12.439],[0.483,0.392],[-65.812,2.856],[-19.705,-31.149],[-1.805,-112.663]],"v":[[-2.608,-215],[127.258,-169.504],[201.749,44.195],[200.163,44.591],[15.695,34.935],[9.456,42.862],[-115.47,119.676],[2.72,170.701],[5.76,170.868],[31.067,200.789],[9.784,205.014],[-181.652,102.356],[-212.5,-5.109]],"c":true}]},{"i":{"x":0.5,"y":1},"o":{"x":0.2,"y":0},"t":31,"s":[{"i":[[-116.219,-1.996],[-36.742,-29.426],[21.108,-84.739],[8.203,-10.553],[33.274,-36.738],[2.291,0.351],[22.526,-32.393],[-50.348,-0.298],[-9.956,2.991],[-24.593,-4.315],[14.686,-2.597],[45.8,72.398],[-0.332,40.908]],"o":[[50.744,-0.403],[63.267,50.67],[-0.342,-0.387],[-25.394,-75.366],[-3.317,-1.057],[-88.639,-13.583],[29.799,29.426],[1.925,-0.128],[6.862,6.854],[0.631,0.111],[-90.297,13.945],[-20.315,-32.113],[-1.861,-116.149]],"v":[[-2.811,-221.622],[131.074,-174.718],[207.869,45.594],[189.268,86.741],[50.6,49.931],[36.604,46.094],[-115.47,119.676],[15.131,171.218],[44.285,167.248],[90.739,193.417],[37.687,207.657],[-187.396,105.555],[-219.198,-5.236]],"c":true}]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":39,"s":[{"i":[[-111.46,-1.914],[-35.057,-28.13],[18.403,-75.449],[0.35,0.384],[39.77,-42.644],[3.622,-6.052],[29.458,-43.968],[-46.205,2.605],[-0.293,-0.571],[-7.66,-7.939],[-0.405,-0.407],[43.16,70.638],[-0.318,39.233]],"o":[[48.666,-0.387],[60.366,48.438],[-0.346,-0.391],[-40.469,-44.407],[-11.79,12.642],[-48.037,-2.797],[36.063,37.782],[0.283,0.577],[5.132,10.015],[0.399,0.414],[-82.395,15.887],[-19.144,-31.332],[-1.785,-111.392]],"v":[[-2.539,-212.566],[125.426,-167.386],[200.523,36.645],[199.48,35.483],[35.691,39.115],[7.779,72.791],[-136.361,119.746],[7.733,172.494],[8.597,174.216],[23.443,196.867],[24.649,198.099],[-180.021,102.208],[-210.064,-5.041]],"c":true}]},{"t":45,"s":[{"i":[[-112.731,-1.936],[-35.64,-28.543],[20.475,-82.195],[0.354,0.388],[44.52,-35.386],[2.284,0.141],[29.458,-43.968],[-51.791,5.403],[-0.293,-0.571],[-7.748,-8.03],[-0.41,-0.412],[44.334,71.573],[-0.322,39.68]],"o":[[49.221,-0.391],[61.368,49.149],[-0.35,-0.395],[-40.931,-44.914],[-11.017,8.757],[-51.758,-3.197],[36.064,37.782],[0.283,0.577],[5.132,10.015],[0.404,0.418],[-87.864,17.594],[-19.665,-31.747],[-1.805,-112.663]],"v":[[-2.608,-215],[127.258,-169.504],[201.749,44.195],[200.693,43.02],[45.831,32.109],[11.351,64.508],[-128.197,119.617],[15.131,171.218],[15.995,172.94],[35.454,200.149],[36.674,201.394],[-181.731,103.508],[-212.5,-5.109]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 2","bm":0,"hd":false}],"ip":25,"op":179,"st":-1,"bm":0}]} \ No newline at end of file diff --git a/TMessagesProj/src/main/res/raw/story_bomb1.json b/TMessagesProj/src/main/res/raw/story_bomb1.json new file mode 100644 index 0000000000..e1f94c1026 --- /dev/null +++ b/TMessagesProj/src/main/res/raw/story_bomb1.json @@ -0,0 +1 @@ +{"tgs":1,"v":"5.5.2","fr":60,"ip":0,"op":44,"w":512,"h":512,"nm":"Bomb 2","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Explosion 3","sr":1,"ks":{"r":{"a":0,"k":-53},"p":{"a":0,"k":[401.994,340.32,0]},"a":{"a":0,"k":[-24.096,-34.222,0]},"s":{"a":1,"k":[{"i":{"x":[0.5,0.5,0.5],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,3.095]},"t":14,"s":[15,15,100]},{"t":26,"s":[45,45,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-102.148,61.79],[-76.441,214.996],[6.981,96.274],[116.715,152.332],[69.721,38.135],[216.658,-9.625],[66.631,-54.05],[201.376,-213.452],[9.044,-130.444],[-66.685,-283.439],[-99.417,-132.266],[-255.946,-134.501],[-151.245,-18.588],[-220.31,80.021]],"c":true}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":1,"k":[{"i":{"x":[0.5],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":14,"s":[90]},{"t":26,"s":[20]}]},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":14,"op":25,"st":14,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Explosion 2","sr":1,"ks":{"r":{"a":0,"k":56},"p":{"a":0,"k":[262.679,137.125,0]},"a":{"a":0,"k":[-5.645,4.699,0]},"s":{"a":1,"k":[{"i":{"x":[0.5,0.5,0.5],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,-1.282]},"t":7,"s":[30,30,100]},{"t":19,"s":[60,60,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[-9.365,-1.051],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[9.366,1.051],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-81.065,84.681],[-89.105,231.741],[2.002,112.102],[103.426,222.192],[81.88,86.865],[190.233,86.673],[112.398,10.401],[221.89,-86.876],[60.331,-78.849],[82.044,-194.368],[-16.92,-105.885],[-114.353,-222.343],[-88.39,-59.379],[-233.18,-50.207],[-127.204,11.162],[-216.586,114.156]],"c":true}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":1,"k":[{"i":{"x":[0.5],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":7,"s":[75]},{"t":19,"s":[18]}]},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":7,"op":19,"st":7,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Explosion","sr":1,"ks":{"r":{"a":0,"k":18},"p":{"a":0,"k":[155.9,359.05,0]},"a":{"a":0,"k":[-0.1,0.05,0]},"s":{"a":1,"k":[{"i":{"x":[0.5,0.5,0.5],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,2.92]},"t":0,"s":[27,27,100]},{"t":12,"s":[68,68,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[-9.366,-1.051],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[9.366,1.051],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-60.402,97.958],[-51.706,235],[2.002,112.102],[61.158,206.808],[61.158,98.053],[175.264,136.375],[88.586,21.505],[237.669,-53.037],[72.435,-46.348],[122.129,-144.399],[35.451,-81.229],[45.772,-213.302],[-16.92,-105.885],[-113.442,-234.9],[-72.253,-75.495],[-216.367,-122.132],[-104.459,-19.111],[-237.869,11.279],[-107.134,39.949],[-180.529,170.684]],"c":true}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":1,"k":[{"i":{"x":[0.5],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[70]},{"t":12,"s":[16]}]},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":1,"op":12,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Lines","parent":9,"sr":1,"ks":{"p":{"a":0,"k":[-78.005,-76.877,0]},"a":{"a":0,"k":[-78.005,-76.877,0]},"s":{"a":1,"k":[{"i":{"x":[0.8,0.8,0.8],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.5],"y":[0,0,0]},"t":23,"s":[102.5,102.5,100]},{"i":{"x":[0.5,0.5,0.5],"y":[1,1,1]},"o":{"x":[0.2,0.2,0.2],"y":[0,0,0]},"t":33,"s":[99.5,99.5,100]},{"t":42,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-86.528,-57.861],[-215.79,-58.291]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":25},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"1","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-78.981,-84.01],[-173.334,-169.015]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":25},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"2","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-57.394,-87.864],[-57.352,-211.796]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":25},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"3","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-82.112,-41.253],[-173.244,47.042]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":25},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"4","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-40.458,-85.45],[48.03,-171.898]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":25},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"5","bm":0,"hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.8],"y":[1]},"o":{"x":[0.5],"y":[0]},"t":23,"s":[0]},{"i":{"x":[0.5],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":33,"s":[87]},{"t":42,"s":[69]}]},"e":{"a":1,"k":[{"i":{"x":[0.8],"y":[1]},"o":{"x":[0.5],"y":[0]},"t":23,"s":[0]},{"i":{"x":[0.5],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":33,"s":[67]},{"t":42,"s":[41]}]},"o":{"a":0,"k":0},"m":1,"nm":"Trim Paths 1","hd":false}],"ip":23,"op":178,"st":-2,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Top","parent":9,"sr":1,"ks":{"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.167,"y":0.167},"t":23,"s":[-16.935,-27.142,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.2,"y":0},"t":33,"s":[-71.492,-74.623,0],"to":[0,0,0],"ti":[0,0,0]},{"t":42,"s":[-56.982,-61.715,0]}]},"a":{"a":0,"k":[-56.982,-61.715,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-10.634,-10.623],[0,0],[12.985,-25.821],[0,0],[-10.615,10.626],[0,0]],"o":[[0,0],[-25.477,13.526],[0,0],[-10.615,-10.626],[0,0],[10.623,-10.634]],"v":[[-37.976,-100.946],[-22.136,-85.09],[-81.374,-24.523],[-97.867,-41.032],[-97.867,-79.501],[-76.464,-100.926]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Combined-Shape","bm":0,"hd":false}],"ip":23,"op":179,"st":-1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"Bomb","sr":1,"ks":{"r":{"a":1,"k":[{"i":{"x":[0.8],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":23,"s":[-15]},{"i":{"x":[0.5],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":34,"s":[4]},{"t":43,"s":[0]}]},"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.167,"y":0.167},"t":22,"s":[304.995,377.061,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.2,"y":0},"t":32,"s":[304.995,285.061,0],"to":[0,0,0],"ti":[0,0,0]},{"t":41,"s":[304.995,300.061,0]}]},"a":{"a":0,"k":[48.995,44.061,0]},"s":{"a":1,"k":[{"i":{"x":[0.8,0.8,0.8],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,-5.853]},"t":22,"s":[40,40,100]},{"i":{"x":[0.5,0.5,0.5],"y":[1,1,1]},"o":{"x":[0.2,0.2,0.2],"y":[0,0,0]},"t":32,"s":[110,110,100]},{"t":41,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-70.143,0],[0,-70.217],[70.143,0],[0,70.217]],"o":[[70.143,0],[0,70.217],[-70.143,0],[0,-70.217]],"v":[[48.995,-83.077],[176,44.061],[48.995,171.2],[-78.011,44.061]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Oval","bm":0,"hd":false}],"ip":23,"op":179,"st":-1,"bm":0}]} \ No newline at end of file diff --git a/TMessagesProj/src/main/res/raw/story_bomb2.json b/TMessagesProj/src/main/res/raw/story_bomb2.json new file mode 100644 index 0000000000..4f08e5e01d --- /dev/null +++ b/TMessagesProj/src/main/res/raw/story_bomb2.json @@ -0,0 +1 @@ +{"tgs":1,"v":"5.5.2","fr":60,"ip":0,"op":51,"w":512,"h":512,"nm":"Bomb 3","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Fire","parent":2,"sr":1,"ks":{"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[-48.58]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[-84.3]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":11.48,"s":[-137.6]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":17.959,"s":[-122.957]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":23.359,"s":[-53.981]},{"t":28.759765625,"s":[5]}]},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[470.345,54.81,0],"to":[10.784,-3.866,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":5,"s":[492.049,40.464,0],"to":[0,0,0],"ti":[15.065,23.114,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":11.48,"s":[495.453,-7.681,0],"to":[-11.809,-17.308,0],"ti":[1.081,11.671,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":17.959,"s":[466.435,-53.817,0],"to":[-7.108,-20.228,0],"ti":[-11.62,10.41,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":23.359,"s":[473.704,-99.757,0],"to":[11.205,-10.038,0],"ti":[0,0,0]},{"t":28.759765625,"s":[515.348,-115.758,0]}]},"a":{"a":0,"k":[430.422,49.813,0]},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":0,"s":[110,110,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":3,"s":[70,70,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":6,"s":[110,110,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":9,"s":[70,70,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":12,"s":[110,110,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":15,"s":[70,70,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":18,"s":[110,110,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":21,"s":[70,70,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":24,"s":[110,110,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":27,"s":[70,70,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":30,"s":[110,110,100]},{"t":33,"s":[30,30,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[{"i":[[-0.087,1.173],[0,14.215],[-14.215,0],[-4.636,-4.556],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[-16.797,0.11],[0,-14.215],[7.812,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[434.422,78.64],[405.246,50.316],[430.986,24.577],[450.636,32.056],[478.115,-31.233],[421.594,21.016],[394.363,-31.113],[396.337,27.88],[341.73,30.2],[390.275,61.683],[383.015,94.017],[409.896,77.491],[435.785,143.738]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":3,"s":[{"i":[[-0.087,1.173],[-1.677,19.988],[-16.124,0.26],[-4.636,-4.556],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[-20.089,-2.935],[1.188,-14.166],[7.811,-0.126],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[433.593,87.143],[401.4,47.55],[430.98,19.402],[453.615,26.427],[493.167,-52.344],[421.594,21.016],[398.25,-8.861],[396.337,27.88],[351.268,34.893],[388.6,58.57],[347.267,131.678],[408.22,74.378],[437.086,131.661]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":6,"s":[{"i":[[-0.087,1.173],[0,14.216],[-14.215,0],[-4.636,-4.556],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[-16.797,0.11],[0,-14.215],[7.812,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[434.422,78.64],[405.246,50.316],[430.986,24.577],[450.636,32.056],[457.607,-4.589],[421.594,21.016],[381.428,-56.985],[396.337,27.88],[300.784,23.278],[388.6,58.57],[355.901,119.512],[408.22,74.378],[428.175,97.456]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":9,"s":[{"i":[[-0.087,1.173],[-3.064,16.388],[-14.215,0],[-4.636,-4.556],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[-22.743,1.134],[2.612,-13.973],[7.812,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[434.296,84.469],[401.329,49.031],[430.934,19.03],[453.593,24.858],[478.115,-31.233],[421.594,21.016],[394.363,-31.113],[396.337,27.88],[341.73,30.2],[390.275,61.683],[383.015,94.017],[409.896,77.491],[435.785,143.738]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":12,"s":[{"i":[[-0.087,1.173],[0,14.216],[-14.215,0],[-4.636,-4.556],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[-16.797,0.11],[0,-14.215],[7.812,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[434.422,78.64],[405.246,50.316],[430.986,24.577],[450.636,32.056],[485.628,-42.843],[421.594,21.016],[380.082,-29.626],[396.337,27.88],[351.268,34.893],[388.6,58.57],[347.267,131.678],[408.22,74.378],[437.086,131.661]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":15,"s":[{"i":[[-0.087,1.173],[0,14.216],[-14.215,0],[-4.636,-4.556],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[-19.975,-0.652],[0,-14.215],[7.812,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[431.151,83.374],[401.004,50.343],[431.185,18.683],[451.046,22.153],[480.978,-36.79],[426.718,11.085],[399.123,-16.081],[384.028,19.471],[276.734,22.013],[388.6,58.57],[355.901,119.512],[404.501,82.887],[428.175,97.456]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":18,"s":[{"i":[[-0.087,1.173],[0,14.215],[-14.215,0],[-4.636,-4.556],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[-16.797,0.11],[0,-14.215],[7.812,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[434.422,78.64],[405.246,50.316],[430.986,24.577],[450.636,32.056],[465.574,-13.355],[421.594,21.016],[384.886,-65.844],[396.337,27.88],[341.73,30.2],[390.275,61.683],[371.54,105.269],[409.896,77.491],[435.785,143.738]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":21,"s":[{"i":[[-0.087,1.173],[0,14.216],[-14.215,0],[-4.636,-4.556],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[-22.565,1.51],[0,-14.215],[7.812,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[434.196,85.644],[403.262,51.347],[431.293,22.905],[452.251,28.697],[493.229,-48.344],[425.528,13.573],[392.596,-39.164],[391.506,17.287],[345.207,30.986],[385.232,62.574],[320.328,157.43],[403.093,87.792],[437.086,131.661]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":24,"s":[{"i":[[-0.087,1.173],[0,14.216],[-14.215,0],[-4.636,-4.556],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[-16.797,0.11],[0,-14.215],[7.812,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[434.422,78.64],[405.246,50.316],[430.986,24.577],[450.636,32.056],[458.681,-5.429],[425.241,12.396],[395.352,-19.983],[396.337,27.88],[324.19,25.174],[388.6,58.57],[364.541,115.065],[408.077,80.837],[429.106,103.075]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":27,"s":[{"i":[[-0.087,1.173],[0,14.215],[-14.215,0],[-4.636,-4.556],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[-22.628,-2.94],[0,-14.215],[7.812,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[433.31,82.841],[401.908,47.741],[432.236,17.953],[453.189,22.708],[525.816,-83.44],[425.858,6.617],[390.44,-41.558],[396.337,27.88],[304.102,23.134],[388.6,58.57],[352.985,106.917],[407.187,83.852],[425.152,169.167]],"c":true}]},{"t":30,"s":[{"i":[[-0.087,1.173],[0,14.216],[-14.215,0],[-4.636,-4.556],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[-16.797,0.11],[0,-14.215],[7.812,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[434.422,78.64],[405.246,50.316],[430.986,24.577],[450.636,32.056],[462.206,-9.869],[421.594,21.016],[387.999,-52.022],[396.337,27.88],[341.73,30.2],[390.275,61.683],[345.288,131.29],[409.896,77.491],[429.421,111.92]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":1,"op":34,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Wick","parent":5,"sr":1,"ks":{"r":{"a":0,"k":-5},"p":{"a":0,"k":[-105.468,-106.644,0]},"a":{"a":0,"k":[535.427,-105.928,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-3.046,-1.114],[43.481,50.755],[-46.419,-29.103]],"o":[[37.803,13.827],[-46.864,-54.704],[72.937,45.729]],"v":[[441.678,55.167],[483.869,-26.344],[534.948,-106.826]],"c":false}},"nm":"Path 1","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.1],"y":[0]},"t":0,"s":[12]},{"t":29,"s":[94]}]},"e":{"a":0,"k":100},"o":{"a":0,"k":0},"m":1,"nm":"Trim Paths 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":25},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":1,"op":34,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Lines 2","parent":5,"sr":1,"ks":{"p":{"a":0,"k":[-78.005,-76.877,0]},"a":{"a":0,"k":[-78.005,-76.877,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-86.528,-57.861],[-215.79,-58.291]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":25},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"1","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-78.981,-84.01],[-173.334,-169.015]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":25},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"2","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-57.394,-87.864],[-57.352,-211.796]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":25},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"3","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-82.112,-41.253],[-173.244,47.042]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":25},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"4","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-40.458,-85.45],[48.03,-171.898]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":25},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"5","bm":0,"hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":34,"s":[15]},{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":42,"s":[76]},{"t":49,"s":[69]}]},"e":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":34,"s":[14]},{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":42,"s":[65]},{"t":49,"s":[41]}]},"o":{"a":0,"k":0},"m":1,"nm":"Trim Paths 1","hd":false}],"ip":35,"op":177,"st":-3,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Top","parent":5,"sr":1,"ks":{"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.167,"y":0.167},"t":32,"s":[-56.982,-61.715,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.5,"y":1},"o":{"x":0.2,"y":0},"t":38,"s":[-67.158,-70.903,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":45,"s":[-54.147,-59.138,0],"to":[0,0,0],"ti":[0,0,0]},{"t":50,"s":[-56.982,-61.715,0]}]},"a":{"a":0,"k":[-56.982,-61.715,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-10.634,-10.623],[0,0],[12.985,-25.821],[0,0],[-10.615,10.626],[0,0]],"o":[[0,0],[-25.477,13.526],[0,0],[-10.615,-10.626],[0,0],[10.623,-10.634]],"v":[[-37.976,-100.946],[-22.136,-85.09],[-81.374,-24.523],[-97.867,-41.032],[-97.867,-79.501],[-76.464,-100.926]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Combined-Shape","bm":0,"hd":false}],"ip":1,"op":179,"st":-1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Bomb","sr":1,"ks":{"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3,"s":[-1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":6,"s":[1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":9,"s":[-2]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":12,"s":[2]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":15,"s":[-3]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":18,"s":[3]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":21,"s":[-5]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":24,"s":[6]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":27,"s":[-7]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[7]},{"i":{"x":[0.8],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":33,"s":[-8]},{"i":{"x":[0.5],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":38,"s":[8]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":44,"s":[-2]},{"t":49,"s":[0]}]},"p":{"a":1,"k":[{"i":{"x":0.4,"y":1},"o":{"x":0.167,"y":0.167},"t":0,"s":[304.995,400.061,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.57,"y":1},"o":{"x":0.219,"y":0},"t":5,"s":[304.995,267.061,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.247,"y":0},"t":11,"s":[304.995,300.061,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":14,"s":[304.995,301.311,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":17,"s":[304.995,298.686,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[304.995,303.311,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":23,"s":[304.995,295.061,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":26,"s":[304.995,307.061,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":29,"s":[304.995,295.061,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":32,"s":[304.995,304.061,0],"to":[0,0,0],"ti":[0,0,0]},{"t":35,"s":[304.995,300.061,0]}]},"a":{"a":0,"k":[48.995,44.061,0]},"s":{"a":1,"k":[{"i":{"x":[0.4,0.4,0.4],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,-2.288]},"t":0,"s":[35,35,100]},{"i":{"x":[0.57,0.57,0.57],"y":[1,1,1]},"o":{"x":[0.219,0.219,0.219],"y":[0,0,0]},"t":7,"s":[105,105,100]},{"i":{"x":[0.843,0.843,0.843],"y":[1,1,1]},"o":{"x":[0.309,0.309,0.309],"y":[0,0,0]},"t":13,"s":[94,94,100]},{"i":{"x":[0.8,0.8,0.8],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.442]},"t":32,"s":[94,94,100]},{"i":{"x":[0.5,0.5,0.5],"y":[1,1,1]},"o":{"x":[0.2,0.2,0.2],"y":[0,0,0]},"t":38,"s":[127,127,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":45,"s":[97,97,100]},{"t":50,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-70.143,0],[0,-70.217],[70.143,0],[0,70.217]],"o":[[70.143,0],[0,70.217],[-70.143,0],[0,-70.217]],"v":[[48.995,-83.077],[176,44.061],[48.995,171.2],[-78.011,44.061]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Oval","bm":0,"hd":false}],"ip":1,"op":179,"st":-1,"bm":0}]} \ No newline at end of file diff --git a/TMessagesProj/src/main/res/raw/write_contacts_fab_icon_camera.json b/TMessagesProj/src/main/res/raw/write_contacts_fab_icon_camera.json new file mode 100644 index 0000000000..ddde2daae6 --- /dev/null +++ b/TMessagesProj/src/main/res/raw/write_contacts_fab_icon_camera.json @@ -0,0 +1 @@ +{"v":"4.8.0","meta":{"g":"LottieFiles AE 3.3.6","a":"","k":"","d":"","tc":""},"fr":60,"ip":0,"op":47,"w":512,"h":512,"nm":"MAIN","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Camera Center","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[0,12.641,0],"to":[0,0,0],"ti":[0,0,0]},{"t":10,"s":[0,-414.945,0]}],"ix":2},"a":{"a":0,"k":[0,16.089,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.2,"y":0},"t":0,"s":[{"i":[[-32.375,0],[0,-32.375],[32.375,0],[0,32.375]],"o":[[32.375,0],[0,32.375],[-32.375,0],[0,-32.375]],"v":[[0,-42.532],[58.621,16.089],[0,74.71],[-58.621,16.089]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":2,"s":[{"i":[[-31.661,0],[0,-39.578],[31.661,0],[0,39.577]],"o":[[31.661,0],[0,39.578],[-31.661,0],[0,-39.578]],"v":[[0,-44.738],[57.328,26.924],[0,98.585],[-57.328,26.924]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[-30.152,0],[3.666,-44.6],[21.438,0],[7.94,52.18]],"o":[[30.152,0],[-3.591,43.683],[-21.438,0],[-6.724,-44.19]],"v":[[-0.18,15.607],[53.847,93.276],[-0.18,189.636],[-55.345,93.276]],"c":true}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0.167},"t":6,"s":[{"i":[[-27.614,0],[0,-45.64],[27.614,0],[0,45.64]],"o":[[27.614,0],[0,45.64],[-27.614,0],[0,-45.64]],"v":[[0,11.899],[50,94.537],[0,177.176],[-50,94.537]],"c":true}]},{"t":10,"s":[{"i":[[-33.594,0],[0,-33.594],[33.594,0],[0,33.594]],"o":[[33.594,0],[0,33.594],[-33.594,0],[0,-33.594]],"v":[[0,-44.738],[60.827,16.089],[0,76.916],[-60.827,16.089]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Oval","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":10,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Camera Body","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[255.625,265.625,0],"to":[0,0,0],"ti":[0,0,0]},{"t":10,"s":[251.625,345.625,0]}],"ix":2},"a":{"a":0,"k":[-0.647,16.095,0],"ix":1},"s":{"a":0,"k":[58,58,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.6,"y":0},"t":0,"s":[{"i":[[0,0],[0,0],[32.082,-2.767],[40.175,0],[39.192,3.368],[0.002,32.201],[0,0],[-31.407,0],[0,0],[-6.181,7.614],[0,0],[-11.768,0],[0,0],[-7.417,-9.137],[0,0],[-9.807,0],[0,0],[0,-31.407],[0,0],[0,0],[46.682,0],[0,-50.077],[-50.336,0],[1.228,53.183]],"o":[[0,0],[-0.002,32.201],[-39.191,3.38],[-40.175,0],[-32.083,-2.757],[0,0],[0,-31.407],[0,0],[9.807,0],[0,0],[7.417,-9.137],[0,0],[11.768,0],[0,0],[6.181,7.614],[0,0],[31.407,0],[0,0],[0,0],[-3.428,-49.053],[-50.336,0],[0,50.077],[50.336,0],[0,0]],"v":[[175.773,-7.533],[175.773,94.333],[119.05,156.137],[0,161.207],[-119.05,156.137],[-175.773,94.333],[-175.773,-55.221],[-118.905,-112.089],[-101.474,-112.089],[-76.202,-124.125],[-58.635,-145.764],[-28.309,-160.207],[28.309,-160.207],[58.635,-145.764],[76.202,-124.125],[101.474,-112.089],[118.905,-112.089],[175.773,-55.221],[175.773,-7.533],[90.893,8.4],[0,-77.748],[-91.142,12.923],[0,103.595],[90.893,8.4]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":2,"s":[{"i":[[0,0],[0,0],[0,0],[28.996,0],[0,0],[0.133,25.759],[0,0],[-29.349,0.015],[0,0],[-3.533,-0.175],[-5.627,6.78],[-13.624,0],[0,0],[-4.538,-5.9],[0,0],[-8.515,0],[0,0],[-0.133,-24.663],[0,0],[0,0],[41.84,0],[0,-39.934],[-42.582,0],[6.492,44.865]],"o":[[0,0],[0,0],[0,25.162],[0,0],[-28.996,0],[0,0],[0.133,-25.259],[0,0],[8.515,0],[0,0],[7.01,-8.446],[0,0],[21.434,1.426],[7.014,9.118],[2.47,0.094],[0,0],[28.996,0],[0,0],[0,0],[-1.177,-38.972],[-42.582,0],[-4.957,43.604],[42.582,0],[0,0]],"v":[[186.673,40.505],[186.673,78.992],[186.673,117.479],[133.507,162.682],[-133.773,162.671],[-186.673,117.121],[-186.673,-31.085],[-133.109,-77.485],[-106.752,-77.546],[-77.278,-77.412],[-71.011,-100.176],[-39.457,-111.341],[38.38,-111.556],[71.334,-98.56],[77.386,-77.574],[106.752,-77.546],[133.507,-77.503],[186.673,-31.443],[186.673,40.505],[77.32,-78.702],[-1.946,-111.424],[-76.932,-77.441],[-1.515,1.369],[77.351,-78.702]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":3,"s":[{"i":[[0,0],[0,0],[0,0],[30.235,0],[0,0],[0.242,24.342],[0,0],[-30.877,0.028],[0,0],[-4.774,0.058],[-2.813,0.267],[-14.206,0],[0,0],[-7.081,-0.336],[0,0],[-8.878,0],[0,0],[-0.242,-22.347],[0,0],[0,0],[49.253,0],[0,2.773],[-50.127,0],[0.013,-2.803]],"o":[[0,0],[0,0],[0,23.256],[0,0],[-30.235,0],[0,0],[0.242,-23.433],[0,0],[8.879,0],[0,0],[8.317,-0.436],[0,0],[18.111,0.056],[3.507,0.359],[4.243,0.068],[0,0],[30.235,0],[0,0],[0,0],[-1.385,2.706],[-50.127,0],[0,-2.773],[50.127,0],[0,0]],"v":[[194.648,60.265],[194.648,90.767],[194.648,121.268],[138.694,162.726],[-139.178,162.706],[-194.648,120.617],[-194.648,5.063],[-137.969,-38.573],[-111.312,-38.685],[-84.557,-39.186],[-74.747,-40.226],[-39.739,-40.829],[39.201,-40.838],[74.908,-40.163],[84.61,-39.192],[111.312,-38.685],[138.694,-38.607],[194.648,4.412],[194.648,60.265],[90.877,10.252],[-0.973,12.464],[-90.705,10.164],[0.105,5.141],[90.914,10.252]],"c":true}]},{"i":{"x":0.1,"y":1},"o":{"x":0.167,"y":0.167},"t":4,"s":[{"i":[[0,0],[0,0],[0,0],[31.473,0],[0,0],[0.351,22.925],[0,0],[-32.405,0.04],[0,0],[-6.015,0.373],[0,0],[-14.788,0],[0,0],[-9.624,-0.597],[0,0],[-9.242,0],[0,0],[-0.351,-20.031],[0,0],[0,0],[56.666,0],[0,0.883],[-57.672,0],[0.015,-0.893]],"o":[[0,0],[0,0],[0,21.35],[0,0],[-31.473,0],[0,0],[0.351,-21.606],[0,0],[9.242,0],[0,0],[9.624,-0.597],[0,0],[14.788,0],[0,0],[6.015,0.373],[0,0],[31.473,0],[0,0],[0,0],[-1.594,0.862],[-57.672,0],[0,-0.883],[57.672,0],[0,0]],"v":[[202.623,80.025],[202.623,102.542],[202.623,125.058],[143.882,162.77],[-144.583,162.742],[-202.623,124.113],[-202.623,41.212],[-142.829,0.338],[-115.873,0.177],[-91.835,-0.411],[-78.482,-1.239],[-40.021,-2.179],[40.021,-2.179],[78.482,-1.239],[91.835,-0.411],[115.873,0.177],[143.882,0.29],[202.623,40.267],[202.623,80.025],[104.435,79.868],[0,81.44],[-104.477,79.84],[0,78.239],[104.477,79.868]],"c":true}]},{"t":10,"s":[{"i":[[0,0],[0,0],[0,0],[32.171,0],[0,0],[0.469,21.247],[0,0],[-33.416,0.055],[0,0],[-6.148,-0.01],[0,0],[-15.116,0],[0,0],[-9.837,0.015],[0,0],[-9.447,0],[0,0],[-0.469,-17.287],[0,0],[0,0],[57.922,0],[0,0.878],[-58.95,0],[0.015,-0.888]],"o":[[0,0],[0,0],[0,19.092],[0,0],[-32.171,0],[0,0],[0.469,-19.442],[0,0],[9.447,0],[0,0],[9.837,0.015],[0,0],[15.116,0],[0,0],[6.148,-0.01],[0,0],[32.171,0],[0,0],[0,0],[-1.629,0.857],[-58.951,0],[0,-0.878],[58.95,0],[0,0]],"v":[[207.114,103.43],[207.114,116.488],[207.114,129.546],[146.52,162.823],[-147.461,162.784],[-207.119,128.253],[-207.304,104.717],[-134.208,46.56],[-118.445,46.206],[-93.874,46.222],[-80.225,46.243],[-40.911,46.267],[40.907,46.267],[80.22,46.243],[93.869,46.222],[118.44,46.206],[139.246,46.427],[206.929,103.424],[207.114,103.43],[106.749,103.325],[-0.002,104.888],[-106.797,103.297],[-0.002,101.707],[106.792,103.325]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":10,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Head Person","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.8,"y":0},"t":11,"s":[-7.929,-253.524,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.2,"y":0},"t":23,"s":[-8.429,-37.524,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":28,"s":[-8.429,-19.024,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":33,"s":[-8.429,-49.524,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":38,"s":[-8.429,-33.524,0],"to":[0,0,0],"ti":[0,0,0]},{"t":42,"s":[-8.429,-38.524,0]}],"ix":2},"a":{"a":0,"k":[-8.429,-38.524,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.3,"y":0},"t":15,"s":[{"i":[[-19.343,0],[0,-19.343],[19.343,0],[0,19.343]],"o":[[19.343,0],[0,19.343],[-19.343,0],[0,-19.343]],"v":[[-8.429,-73.548],[26.595,-38.524],[-8.429,-3.5],[-43.452,-38.524]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":21,"s":[{"i":[[-20.106,0.23],[0,-20.482],[18.496,0],[0,20.482]],"o":[[21.894,-0.25],[0,20.482],[-18.496,0],[0,-20.482]],"v":[[-8.202,-91.989],[25.171,-34.343],[-8.187,1.442],[-41.808,-34.343]],"c":true}]},{"i":{"x":0.3,"y":1},"o":{"x":0.167,"y":0.167},"t":24,"s":[{"i":[[-18.013,0.001],[0,-17.921],[21.853,-0.001],[0.001,17.921]],"o":[[18.916,-0.001],[0.001,17.921],[-21.853,0.001],[0,-17.921]],"v":[[-8.182,-86.75],[31.25,-36.312],[-8.162,-5],[-47.887,-36.31]],"c":true}]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":29,"s":[{"i":[[-25.504,0],[0,-16.693],[25.504,0],[0,16.693]],"o":[[25.504,0],[0,16.693],[-25.504,0],[0,-16.693]],"v":[[-8.429,-66.5],[37.75,-36.274],[-8.429,-6.048],[-54.607,-36.274]],"c":true}]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":34,"s":[{"i":[[-20.06,0],[0,-23.35],[20.06,0],[0,23.35]],"o":[[20.06,0],[0,23.35],[-20.06,0],[0,-23.35]],"v":[[-8.429,-83.75],[27.893,-41.47],[-8.429,0.81],[-44.75,-41.47]],"c":true}]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":39,"s":[{"i":[[-22.328,0],[0,-20.106],[22.328,0],[0,20.106]],"o":[[22.328,0],[0,20.106],[-22.328,0],[0,-20.106]],"v":[[-8.429,-72],[32,-35.595],[-8.429,0.81],[-48.857,-35.595]],"c":true}]},{"t":43,"s":[{"i":[[-21.723,0],[0,-21.723],[21.723,0],[0,21.723]],"o":[[21.723,0],[0,21.723],[-21.723,0],[0,-21.723]],"v":[[-8.429,-77.857],[30.905,-38.524],[-8.429,0.81],[-47.762,-38.524]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":10,"op":183,"st":-14,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Body Person","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.8,"y":0},"t":10,"s":[251.571,422.857,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.2,"y":0},"t":22,"s":[247.571,305.857,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":27,"s":[247.571,357.857,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":32,"s":[247.571,324.857,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":37,"s":[247.571,334.857,0],"to":[0,0,0],"ti":[0,0,0]},{"t":41,"s":[247.571,331.857,0]}],"ix":2},"a":{"a":0,"k":[-8.429,73.857,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.8,"y":0},"t":10,"s":[{"i":[[0,0],[0,-20.324],[0,0],[27.617,1.009],[0,0],[0.528,22.518],[0,0],[-21.307,0]],"o":[[23.59,0],[0,0],[-1.45,23.568],[0,0],[-20.176,0.314],[0,0],[0,-13.941],[0,0]],"v":[[63.141,14.714],[110.151,51.145],[109.936,58.904],[47.74,82.616],[-83.727,81.436],[-126.642,58.607],[-126.559,49.478],[-83.121,14.991]],"c":true}]},{"i":{"x":0.2,"y":1},"o":{"x":0.2,"y":0},"t":22,"s":[{"i":[[0,0],[0,-34.383],[0,0],[9.658,0],[0,0],[0,6.954],[0,0],[-38.946,0.55]],"o":[[39.777,0],[0,0],[0,6.954],[0,0],[-15.488,-0.212],[0,0],[0,-34.039],[0,0]],"v":[[-8.374,-11.5],[58.643,56.696],[58.643,69.007],[40.975,90],[-53.828,90],[-75.5,69.007],[-75.5,56.696],[-9.561,-11.492]],"c":true}]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":27,"s":[{"i":[[0,0],[0,-16.512],[0,0],[6.043,0],[0,0],[0,3.34],[0,0],[-63.965,0.264]],"o":[[65.33,0],[0,0],[0,3.34],[0,0],[-6.043,0],[0,0],[0,-16.347],[0,0]],"v":[[-8.429,32],[86.25,61.898],[86.25,67.81],[75.308,73.857],[-92.165,73.857],[-103.107,67.81],[-103.107,61.898],[-10.378,32.004]],"c":true}]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":32,"s":[{"i":[[0,0],[0,-24.402],[0,0],[4.393,0],[0,0],[0,4.935],[0,0],[-46.496,0.39]],"o":[[47.488,0],[0,0],[0,4.935],[0,0],[-4.393,0],[0,0],[0,-24.158],[0,0]],"v":[[-8.429,12],[60.393,56.184],[60.393,64.921],[52.439,73.857],[-69.296,73.857],[-77.25,64.921],[-77.25,56.184],[-9.845,12.006]],"c":true}]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":37,"s":[{"i":[[0,0],[0,-21.147],[0,0],[4.559,0],[0,0],[0,4.277],[0,0],[-48.257,0.338]],"o":[[49.287,0],[0,0],[0,4.277],[0,0],[-4.559,0],[0,0],[0,-20.936],[0,0]],"v":[[-8.429,20.25],[63,58.541],[63,66.113],[54.745,73.857],[-71.602,73.857],[-79.857,66.113],[-79.857,58.541],[-9.899,20.255]],"c":true}]},{"t":41,"s":[{"i":[[0,0],[0,-22.167],[0,0],[4.483,0],[0,0],[0,4.483],[0,0],[-47.453,0.354]],"o":[[48.466,0],[0,0],[0,4.483],[0,0],[-4.483,0],[0,0],[0,-21.945],[0,0]],"v":[[-8.429,17.667],[61.81,57.803],[61.81,65.74],[53.692,73.857],[-70.549,73.857],[-78.667,65.74],[-78.667,57.803],[-9.875,17.672]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":10,"op":183,"st":-14,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Plus Person","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.477],"y":[1.018]},"o":{"x":[0.437],"y":[0]},"t":23,"s":[-35]},{"i":{"x":[0.7],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":31,"s":[13]},{"i":{"x":[0.7],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":37,"s":[-6]},{"i":{"x":[0.7],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":42,"s":[3]},{"t":46,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.2,"y":0},"t":22,"s":[307.901,230.13,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":30,"s":[350.441,289.83,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":35,"s":[339.651,247.005,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":40,"s":[342.651,260.505,0],"to":[0,0,0],"ti":[0,0,0]},{"t":44,"s":[341.401,254.63,0]}],"ix":2},"a":{"a":0,"k":[-50.491,-44.113,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":22,"s":[46.667,70,100]},{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":25,"s":[50,110,100]},{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":30,"s":[115,85,100]},{"t":35,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-78.417,-44.113],[-22.565,-44.113]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-50.491,-72.039],[-50.491,-16.187]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Line","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":22,"op":183,"st":-14,"bm":0}],"markers":[{"tm":0,"cm":"1","dr":0},{"tm":46,"cm":"2","dr":0}]} \ No newline at end of file diff --git a/TMessagesProj/src/main/res/raw/write_contacts_fab_icon_reverse_camera.json b/TMessagesProj/src/main/res/raw/write_contacts_fab_icon_reverse_camera.json new file mode 100644 index 0000000000..f8fecdaea0 --- /dev/null +++ b/TMessagesProj/src/main/res/raw/write_contacts_fab_icon_reverse_camera.json @@ -0,0 +1 @@ +{"tgs":1,"v":"5.5.2","fr":60,"ip":0,"op":43,"w":512,"h":512,"nm":"MAIN","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Camera Center","sr":1,"ks":{"p":{"a":1,"k":[{"i":{"x":0.2,"y":0.898},"o":{"x":0.3,"y":0},"t":12,"s":[253.25,314.372,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.8,"y":1},"o":{"x":0.6,"y":0.115},"t":20,"s":[256,183.622,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.2,"y":0},"t":30,"s":[256,292.122,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":37,"s":[256,254.122,0],"to":[0,0,0],"ti":[0,0,0]},{"t":42,"s":[256,263.622,0]}]},"a":{"a":0,"k":[0,16.089,0]},"s":{"a":0,"k":[58,58,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.69,"y":1},"o":{"x":0.4,"y":0},"t":12,"s":[{"i":[[-44.338,0],[0,-35.05],[44.338,0],[0,35.05]],"o":[[44.338,0],[0,35.05],[-44.338,0],[0,-35.05]],"v":[[-0.323,-42.526],[79.957,20.938],[-0.323,84.403],[-80.603,20.938]],"c":true}]},{"i":{"x":0.6,"y":1},"o":{"x":0.167,"y":0},"t":17,"s":[{"i":[[-34.674,0],[0,-40.617],[34.674,0],[0,40.617]],"o":[[34.674,0],[0,40.617],[-34.674,0],[0,-40.617]],"v":[[-0.06,-42.53],[62.723,31.012],[-0.406,132.199],[-62.843,31.012]],"c":true}]},{"i":{"x":0.8,"y":1},"o":{"x":0.221,"y":0.007},"t":20,"s":[{"i":[[-37.374,0],[0,-32.079],[37.374,0],[0,32.079]],"o":[[37.374,0],[0,32.079],[-37.374,0],[0,-32.079]],"v":[[0,-45.98],[67.672,12.105],[0,70.19],[-67.672,12.105]],"c":true}]},{"i":{"x":0.8,"y":1},"o":{"x":0.5,"y":0},"t":25,"s":[{"i":[[-29.757,0],[0,-30.917],[29.757,0],[0,30.917]],"o":[[29.757,0],[0,30.917],[-29.757,0],[0,-30.917]],"v":[[0,-48.888],[53.879,18.73],[0,74.71],[-53.879,18.73]],"c":true}]},{"i":{"x":0.7,"y":1},"o":{"x":0.2,"y":0},"t":30,"s":[{"i":[[-35.708,0],[0,-27.196],[35.708,0],[0,27.196]],"o":[[35.708,0],[0,27.196],[-35.708,0],[0,-27.196]],"v":[[0,-23.776],[64.655,25.467],[0,74.71],[-64.655,25.467]],"c":true}]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":37,"s":[{"i":[[-29.519,0],[0,-34.814],[29.519,0],[0,34.814]],"o":[[29.519,0],[0,34.814],[-29.519,0],[0,-34.814]],"v":[[0,-51.362],[53.448,11.674],[0,74.71],[-53.448,11.674]],"c":true}]},{"t":42,"s":[{"i":[[-32.375,0],[0,-32.375],[32.375,0],[0,32.375]],"o":[[32.375,0],[0,32.375],[-32.375,0],[0,-32.375]],"v":[[0,-42.532],[58.621,16.089],[0,74.71],[-58.621,16.089]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Oval","bm":0,"hd":false}],"ip":12,"op":179,"st":13,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Camera Body","sr":1,"ks":{"p":{"a":1,"k":[{"i":{"x":0.742,"y":0.52},"o":{"x":0.42,"y":0.048},"t":20,"s":[255.875,218.125,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.74,"y":1},"o":{"x":0.48,"y":0.44},"t":25,"s":[256.323,241.24,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.2,"y":0},"t":30,"s":[255.625,288.125,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.167,"y":0},"t":37,"s":[255.625,255.625,0],"to":[0,0,0],"ti":[0,0,0]},{"t":42,"s":[255.625,265.625,0]}]},"a":{"a":0,"k":[-0.647,16.095,0]},"s":{"a":0,"k":[58,58,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.6,"y":0.055},"t":20,"s":[{"i":[[0,0],[2.157,-5.862],[12.855,-7.164],[13.924,0],[9.929,3.949],[3.044,9.536],[-2.241,8.505],[-15.829,5.707],[0,0],[-1.843,0.147],[0,0],[-3.509,0],[0,0],[-2.212,-0.177],[0,0],[-2.559,-1.084],[0,0],[-1.744,-6.691],[0,0],[0,0],[6.331,7.511],[4.834,3.056],[5.352,1.589],[6.072,0],[6.168,-2.02],[3.417,-1.944],[4.38,-6.064],[0,-9.739],[-4.081,-6.853],[-2.522,-2.672],[-2.187,-1.7],[-14.41,0],[-9.928,7.633],[-2.317,2.658],[-1.365,2.15],[0.254,9.707]],"o":[[0,0],[-3.198,8.69],[-9.328,5.199],[-13.924,0],[-16.302,-6.484],[-2.786,-8.728],[2.241,-8.505],[0,0],[2.051,-0.761],[0,0],[2.212,-0.177],[0,0],[2.694,0.047],[0,0],[0.826,0.595],[0,0],[13.499,6.234],[0,0],[0,0],[-0.801,-10.096],[-3.584,-4.253],[-4.565,-2.887],[-5.572,-1.654],[-6.972,0],[-3.873,1.268],[-6.867,3.906],[-5.535,7.663],[0,8.265],[1.846,3.099],[1.864,1.975],[9.993,7.769],[13.931,0],[2.847,-2.189],[1.686,-1.934],[4.899,-7.719],[0,0]],"v":[[61.885,-53.232],[60.385,-32.19],[34.909,-4.168],[0.357,5.103],[-29.057,-0.424],[-60.145,-31.25],[-60.151,-60.182],[-30.744,-91.924],[-25.343,-93.796],[-17.403,-96.071],[-12.165,-96.83],[-1.3,-97.79],[8.935,-97.412],[16.293,-96.166],[20.621,-94.641],[26.638,-92.621],[32.14,-90.664],[59.631,-60.975],[61.885,-53.232],[57.393,-49.419],[46.175,-76.197],[33.454,-87.227],[18.498,-93.996],[0.942,-96.54],[-18.877,-93.415],[-29.839,-88.574],[-46.925,-73.429],[-55.664,-46.945],[-49.276,-24.035],[-42.7,-15.357],[-36.613,-9.835],[0.942,2.651],[37.677,-9.545],[45.445,-16.833],[50.028,-22.966],[57.393,-49.419]],"c":true}]},{"i":{"x":0.8,"y":1},"o":{"x":0.167,"y":0.167},"t":25,"s":[{"i":[[-4.418,-5.736],[1.51,-5.058],[14.202,-11.869],[18.262,-0.531],[20.93,13.731],[2.131,19.832],[-10.133,26.139],[-11.029,5.691],[0,0],[-3.439,2.871],[0,0],[-6.549,0],[0,0],[-3.727,-0.611],[0,0],[-4.786,-2.851],[0,0],[-4.204,-12.294],[0,0],[0,0],[8.317,12.363],[6.351,5.03],[7.032,2.615],[7.978,0],[8.104,-3.325],[4.49,-3.199],[5.755,-9.981],[0,-16.029],[-5.362,-11.279],[-3.313,-4.397],[-2.873,-2.798],[-18.932,0],[-13.044,12.563],[-3.044,4.375],[-1.793,3.539],[0.334,15.976]],"o":[[3.34,11.908],[-2.239,19.102],[-19.383,16.199],[-18.262,0.531],[-12.337,-8.093],[-4.5,-20.206],[6.478,-16.711],[0,0],[4.865,-2.854],[0,0],[6.48,-2.324],[0,0],[5.978,0.04],[0,0],[2.728,3.258],[0,0],[11.528,7.653],[0,0],[0,0],[-1.053,-16.618],[-4.709,-6.999],[-5.998,-4.751],[-7.32,-2.722],[-9.161,0],[-5.088,2.088],[-9.022,6.428],[-7.272,12.612],[0,13.603],[2.425,5.101],[2.449,3.251],[13.129,12.786],[18.303,0],[3.74,-3.602],[2.215,-3.183],[6.437,-12.705],[0,0]],"v":[[102.358,-42.086],[99.592,3.635],[66.692,58.72],[-0.117,82.009],[-61.124,63.475],[-99.912,4.7],[-93.054,-71.11],[-59.803,-110.607],[-51.922,-116.286],[-38.556,-123.349],[-27.066,-126.976],[-7.778,-131.301],[14.506,-130.011],[30.782,-126.972],[41.056,-122.115],[52.83,-115.019],[62.498,-107.234],[94.162,-67.73],[102.358,-42.086],[74.325,-26.59],[59.587,-70.664],[42.873,-88.818],[23.222,-99.959],[0.157,-104.146],[-25.882,-99.003],[-40.284,-91.034],[-62.733,-66.109],[-74.214,-22.518],[-65.821,15.189],[-57.181,29.471],[-49.184,38.559],[0.157,59.109],[48.421,39.036],[58.626,27.041],[64.649,16.947],[74.325,-26.59]],"c":true}]},{"i":{"x":0.7,"y":1},"o":{"x":0.2,"y":0},"t":30,"s":[{"i":[[0,0],[0,0],[34.459,-2.512],[43.151,0],[42.096,3.059],[0.002,29.242],[0,0],[-33.734,0],[0,0],[-6.639,5.277],[0,0],[-12.64,0],[0,0],[-7.966,-6.333],[0,0],[-10.533,0],[0,0],[0,-28.521],[0,0],[0,0],[10.948,11.954],[8.36,4.864],[9.256,2.529],[10.502,0],[10.667,-3.215],[5.91,-3.093],[7.575,-9.651],[0,-15.499],[-7.058,-10.906],[-4.361,-4.252],[-3.782,-2.706],[-24.92,0],[-17.169,12.148],[-4.007,4.23],[-2.36,3.422],[0.44,15.448]],"o":[[0,0],[-0.002,29.242],[-42.094,3.069],[-43.151,0],[-34.459,-2.504],[0,0],[0,-28.521],[0,0],[10.533,0],[0,0],[7.966,-6.333],[0,0],[12.64,0],[0,0],[6.639,5.277],[0,0],[33.734,0],[0,0],[0,0],[-1.385,-16.068],[-6.198,-6.768],[-7.895,-4.594],[-9.635,-2.632],[-12.058,0],[-6.698,2.019],[-11.875,6.216],[-9.571,12.195],[0,13.153],[3.192,4.932],[3.224,3.143],[17.281,12.363],[24.092,0],[4.923,-3.483],[2.915,-3.078],[8.473,-12.285],[0,0]],"v":[[188.793,7.973],[188.793,100.478],[127.868,156.603],[0,161.207],[-127.868,156.603],[-188.793,100.478],[-188.793,-35.334],[-127.713,-86.976],[-108.99,-86.976],[-81.846,-95.318],[-62.979,-110.317],[-30.406,-120.328],[30.406,-120.328],[62.978,-110.317],[81.846,-95.318],[108.99,-86.976],[127.713,-86.976],[188.793,-35.334],[188.793,7.973],[97.626,26.217],[78.226,-16.4],[56.226,-33.954],[30.361,-44.727],[0,-48.776],[-34.274,-43.803],[-53.232,-36.097],[-82.781,-11.996],[-97.893,30.154],[-86.845,66.615],[-75.473,80.425],[-64.946,89.213],[0,109.084],[63.529,89.674],[76.962,78.076],[84.889,68.316],[97.626,26.217]],"c":true}]},{"i":{"x":0.7,"y":1},"o":{"x":0.167,"y":0},"t":37,"s":[{"i":[[0,0],[0,0],[30.682,-2.891],[38.422,0],[37.482,3.519],[0.002,33.647],[0,0],[-30.037,0],[0,0],[-5.911,7.956],[0,0],[-11.255,0],[0,0],[-7.093,-9.547],[0,0],[-9.379,0],[0,0],[0,-32.817],[0,0],[0,0],[9.748,14.383],[7.443,5.852],[8.242,3.042],[9.351,0],[9.498,-3.868],[5.262,-3.722],[6.745,-11.612],[0,-18.648],[-6.284,-13.122],[-3.883,-5.116],[-3.368,-3.256],[-22.189,0],[-15.288,14.617],[-3.568,5.09],[-2.101,4.117],[0.392,18.587]],"o":[[0,0],[-0.002,33.647],[-37.481,3.531],[-38.422,0],[-30.683,-2.881],[0,0],[0,-32.817],[0,0],[9.379,0],[0,0],[7.093,-9.547],[0,0],[11.255,0],[0,0],[5.911,7.956],[0,0],[30.037,0],[0,0],[0,0],[-1.234,-19.333],[-5.519,-8.143],[-7.03,-5.527],[-8.579,-3.167],[-10.737,0],[-5.964,2.429],[-10.574,7.479],[-8.522,14.673],[0,15.826],[2.842,5.934],[2.871,3.782],[15.387,14.876],[21.451,0],[4.383,-4.191],[2.596,-3.703],[7.544,-14.781],[0,0]],"v":[[168.103,-15.109],[168.103,91.33],[113.855,155.91],[0,161.207],[-113.855,155.91],[-168.103,91.33],[-168.103,-64.939],[-113.717,-124.36],[-97.046,-124.36],[-72.877,-136.936],[-56.077,-159.547],[-27.074,-174.638],[27.074,-174.638],[56.077,-159.547],[72.877,-136.936],[97.046,-124.36],[113.717,-124.36],[168.103,-64.939],[168.103,-15.109],[86.927,1.528],[69.653,-49.748],[50.064,-70.869],[27.033,-83.831],[0,-88.702],[-30.518,-82.719],[-47.398,-73.448],[-73.709,-44.449],[-87.165,6.266],[-77.328,50.135],[-67.202,66.751],[-57.829,77.324],[0,101.233],[56.567,77.879],[68.528,63.924],[75.586,52.181],[86.927,1.528]],"c":true}]},{"t":42,"s":[{"i":[[0,0],[0,0],[32.082,-2.767],[40.175,0],[39.192,3.368],[0.002,32.201],[0,0],[-31.407,0],[0,0],[-6.181,7.614],[0,0],[-11.768,0],[0,0],[-7.417,-9.137],[0,0],[-9.807,0],[0,0],[0,-31.407],[0,0],[0,0],[10.193,13.765],[7.783,5.601],[8.618,2.912],[9.777,0],[9.931,-3.702],[5.503,-3.562],[7.052,-11.113],[0,-17.847],[-6.571,-12.558],[-4.06,-4.896],[-3.521,-3.116],[-23.202,0],[-15.985,13.989],[-3.731,4.871],[-2.197,3.94],[0.41,17.788]],"o":[[0,0],[-0.002,32.201],[-39.191,3.38],[-40.175,0],[-32.083,-2.757],[0,0],[0,-31.407],[0,0],[9.807,0],[0,0],[7.417,-9.137],[0,0],[11.768,0],[0,0],[6.181,7.614],[0,0],[31.407,0],[0,0],[0,0],[-1.29,-18.503],[-5.771,-7.793],[-7.351,-5.29],[-8.971,-3.031],[-11.226,0],[-6.236,2.325],[-11.056,7.157],[-8.911,14.042],[0,15.146],[2.972,5.679],[3.002,3.619],[16.089,14.236],[22.43,0],[4.583,-4.011],[2.714,-3.544],[7.888,-14.146],[0,0]],"v":[[175.773,-7.533],[175.773,94.333],[119.05,156.137],[0,161.207],[-119.05,156.137],[-175.773,94.333],[-175.773,-55.221],[-118.905,-112.089],[-101.474,-112.089],[-76.202,-124.125],[-58.635,-145.764],[-28.309,-160.207],[28.309,-160.207],[58.635,-145.764],[76.202,-124.125],[101.474,-112.089],[118.905,-112.089],[175.773,-55.221],[175.773,-7.533],[90.893,8.389],[72.831,-40.684],[52.348,-60.897],[28.267,-73.302],[0,-77.964],[-31.91,-72.238],[-49.56,-63.365],[-77.072,-35.612],[-91.142,12.923],[-80.856,54.907],[-70.268,70.81],[-60.467,80.929],[0,103.81],[59.147,81.46],[71.654,68.105],[79.035,56.866],[90.893,8.389]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":20,"op":179,"st":13,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Head Person","sr":1,"ks":{"p":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[247.571,219.476,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.762,"y":0.332},"o":{"x":0.444,"y":0},"t":3,"s":[247.571,188.976,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.546,"y":0.31},"t":8,"s":[248.798,214.291,0],"to":[0,0,0],"ti":[0,0,0]},{"t":12,"s":[252.571,314.476,0]}]},"a":{"a":0,"k":[-8.429,-38.524,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[{"i":[[-21.723,0],[0,-21.723],[21.723,0],[0,21.723]],"o":[[21.723,0],[0,21.723],[-21.723,0],[0,-21.723]],"v":[[-8.429,-77.857],[30.905,-38.524],[-8.429,0.81],[-47.762,-38.524]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.8,"y":0},"t":3,"s":[{"i":[[-24.34,0],[0,-19.139],[24.34,0],[0,19.139]],"o":[[24.34,0],[0,19.139],[-24.34,0],[0,-19.139]],"v":[[-8.429,-68.5],[35.643,-33.845],[-8.429,0.81],[-52.5,-33.845]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.5,"y":0},"t":10,"s":[{"i":[[-18.662,0],[0,-23.407],[18.662,0],[0,23.407]],"o":[[18.662,0],[0,23.407],[-18.662,0],[0,-23.407]],"v":[[-8.617,-89.315],[25.363,-41.572],[-8.429,0.81],[-42.22,-41.572]],"c":true}]},{"t":13,"s":[{"i":[[-27.575,0],[0,-20.382],[27.575,0],[0,20.382]],"o":[[27.575,0],[0,20.382],[-27.575,0],[0,-20.382]],"v":[[-8.429,-73],[41.5,-36.095],[-8.429,0.81],[-58.357,-36.095]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":12,"st":-27,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Body Person","sr":1,"ks":{"p":{"a":0,"k":[247.571,331.857,0]},"a":{"a":0,"k":[-8.429,73.857,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[{"i":[[0,0],[0,-22.167],[0,0],[4.483,0],[0,0],[0,4.483],[0,0],[-47.453,0.354]],"o":[[48.466,0],[0,0],[0,4.483],[0,0],[-4.483,0],[0,0],[0,-21.945],[0,0]],"v":[[-8.429,17.667],[61.81,57.803],[61.81,65.74],[53.692,73.857],[-70.549,73.857],[-78.667,65.74],[-78.667,57.803],[-9.875,17.672]],"c":true}]},{"i":{"x":0.8,"y":1},"o":{"x":0.7,"y":0},"t":3,"s":[{"i":[[0,0],[0,-32.883],[0,0],[4.249,0],[0,0],[0,6.651],[0,0],[-31.061,-0.459]],"o":[[31.724,0],[0,0],[0,6.651],[0,0],[-4.249,0],[0,0],[0,-32.555],[0,0]],"v":[[-8.484,-17],[58.143,50.041],[58.143,61.815],[50.449,73.857],[-67.306,73.857],[-75,61.815],[-75,50.041],[-9.431,-17.007]],"c":true}]},{"t":11,"s":[{"i":[[0,0],[0,0.055],[0,0],[1.74,0],[0,0],[0,-0.011],[0,0],[-12.72,0.001]],"o":[[12.992,0],[0,0],[0,-0.011],[0,0],[-1.74,0],[0,0],[0,0.055],[0,0]],"v":[[-8.288,83.499],[19,83.397],[19,83.377],[15.849,83.357],[-32.373,83.357],[-35.524,83.377],[-35.524,83.397],[-8.676,83.499]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Shape","bm":0,"hd":false}],"ip":0,"op":11,"st":-27,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Plus Person","sr":1,"ks":{"r":{"a":1,"k":[{"i":{"x":[0.369],"y":[0.986]},"o":{"x":[0.15],"y":[0]},"t":0,"s":[0]},{"t":11,"s":[12]}]},"p":{"a":1,"k":[{"i":{"x":0.5,"y":0.946},"o":{"x":0.3,"y":0},"t":0,"s":[341.401,254.63,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.8,"y":1},"o":{"x":0.7,"y":0.062},"t":3,"s":[336.401,228.63,0],"to":[0,0,0],"ti":[0,0,0]},{"t":11,"s":[323.401,314.13,0]}]},"a":{"a":0,"k":[-50.491,-44.113,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-78.417,-44.113],[-22.565,-44.113]],"c":false}]},{"i":{"x":0.8,"y":1},"o":{"x":0.7,"y":0},"t":3,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-74.023,-44.113],[-26.96,-44.113]],"c":false}]},{"t":11,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-50.922,-44.113],[-50.06,-44.113]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":14},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.5,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-50.491,-72.039],[-50.491,-16.187]],"c":false}]},{"i":{"x":0.8,"y":1},"o":{"x":0.7,"y":0},"t":3,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-50.491,-74.381],[-50.491,-13.845]],"c":false}]},{"t":11,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-50.491,-44.456],[-50.491,-43.771]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":14},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Line","bm":0,"hd":false}],"ip":0,"op":10,"st":-40,"bm":0}]} \ No newline at end of file diff --git a/TMessagesProj/src/main/res/raw/yuv_hlg2rgb.glsl b/TMessagesProj/src/main/res/raw/yuv_hlg2rgb.glsl new file mode 100644 index 0000000000..77f9c21a1a --- /dev/null +++ b/TMessagesProj/src/main/res/raw/yuv_hlg2rgb.glsl @@ -0,0 +1,141 @@ +#version 320 es + +#extension GL_OES_EGL_image_external : require +#extension GL_EXT_YUV_target : require + +precision mediump float; + +uniform __samplerExternal2DY2YEXT sTexture; + +const mat4 YUV_RGB2020 = mat4(1.167808, 1.167808, 1.167808, 0, 0, -0.187877, 2.148072, 0, 1.683611, -0.652337, 0, 0, -0.914865, 0.347048, -1.147095, 1); +const mat3 BT2020_BT709 = mat3(1.6605, -0.1246, -0.0182, -0.5876, 1.1329, -0.1006, -0.0728, -0.0083, 1.1187); +const highp vec3 Lvec = vec3(0.2627, 0.6780, 0.0593); + +const float sRGB_Lw = 80.0; // sRGB Lw +const float HLG_Lw = 1000.0; // HLG Lw +const float HLG_gamma = 1.2; // HLG gamma + +const float a = .17883277; +const float b = 1. - 4. * a; +const float c = .5 - a * log(4. * a); + +highp vec3 HLG_OOTF_norm(highp vec3 x) { + return pow(dot(Lvec, mix((x * x) / 3., (b + exp((x - c) / a)) / 12., step(.5, x))), HLG_gamma - 1.0) * x; +} + +highp vec3 sRGB_EOTF_Inv(highp vec3 x) { + return mix(12.92 * x, 1.055 * pow(x, vec3(1. / 2.4)) - .055, step(.0031308, x)); +} + +highp vec3 sRGB_EOTF(highp vec3 x) { + return mix(x / 12.92, pow((x + 0.055) / 1.055, vec3(2.4)), step(.04045, x)); +} + +const float L_HDR = HLG_Lw; +const float rho_HDR = 1. + 32. * pow(L_HDR / 10000., 1. / 2.4); +const float L_SDR = sRGB_Lw; +const float rho_SDR = 1. + 32. * pow(L_SDR / 10000., 1. / 2.4); + +highp vec3 BT2446_tone_mapping(highp vec3 rgb_BT2020) { + highp vec3 rgb_BT2020_prime = sRGB_EOTF_Inv(rgb_BT2020); + + float Y_prime = dot(Lvec, rgb_BT2020_prime); + + float Y_prime_p = log(1. + (rho_HDR - 1.) * Y_prime) / log(rho_HDR); + float Y_prime_c = mix(1.0770 * Y_prime_p, mix((-1.1510 * Y_prime_p + 2.7811) * Y_prime_p - .6302, .5 * Y_prime_p + .5, step(.9909, Y_prime_p)), step(.7399, Y_prime_p)); + float Y_prime_SDR = (pow(rho_SDR, Y_prime_c) - 1.) / (rho_SDR - 1.); + + float f_Y_prime_SDR = Y_prime_SDR / (1.1 * Y_prime); + float Cb_prime_TMO = f_Y_prime_SDR * (rgb_BT2020_prime.b - Y_prime) / 1.8814; + float Cr_prime_TMO = f_Y_prime_SDR * (rgb_BT2020_prime.r - Y_prime) / 1.4746; + float Y_prime_TMO = Y_prime_SDR - max(.1 * Cr_prime_TMO, .0); + + float R_prime_TMO = Cr_prime_TMO * 1.4746 + Y_prime_TMO; + float B_prime_TMO = Cb_prime_TMO * 1.8814 + Y_prime_TMO; + float G_prime_TMO = (Y_prime_TMO - Lvec.x * R_prime_TMO - Lvec.z * B_prime_TMO) / Lvec.y; + highp vec3 rgb_BT2020_prime_TMO = vec3(R_prime_TMO, G_prime_TMO, B_prime_TMO); + + return sRGB_EOTF(rgb_BT2020_prime_TMO); +} + +uniform vec2 texSize; + +vec4 at(vec2 uv) { + highp vec4 srcYuv = texture(sTexture, uv); + highp vec3 rgb_BT2020 = clamp((YUV_RGB2020 * srcYuv).rgb, 0., 1.); + highp vec3 rgb_BT2020_displayLinear = HLG_OOTF_norm(rgb_BT2020); + highp vec3 rgb_BT2020_displayLinear_TMO = BT2446_tone_mapping(rgb_BT2020_displayLinear); + highp vec3 rgb_BT709_displayLinear = BT2020_BT709 * rgb_BT2020_displayLinear_TMO; + rgb_BT709_displayLinear = clamp(rgb_BT709_displayLinear, 0., 1.); + highp vec3 rgb_BT709_sRGB = sRGB_EOTF_Inv(rgb_BT709_displayLinear); + return vec4(rgb_BT709_sRGB, 1.0); +} + +// vec4 BilinearTextureSample(vec2 P) { +// vec2 onePixel = 1.0 / texSize, twoPixels = 2.0 / texSize; +// vec2 pixel = P * texSize + .5; +// vec2 frac = fract(pixel); +// pixel = (floor(pixel) / texSize) - onePixel / 2.; +// return mix( +// mix(at(pixel + vec2(0., 0.) * onePixel), at(pixel + vec2(1., 0.) * onePixel), frac.x), +// mix(at(pixel + vec2(0., 1.) * onePixel), at(pixel + vec2(1., 1.) * onePixel), frac.x), +// frac.y +// ); +// } + +// vec3 NearestTextureSample(vec2 P) { +// vec2 onePixel = 1.0 / texSize, twoPixels = 2.0 / texSize; +// vec2 pixel = P * texSize; +// vec2 frac = fract(pixel); +// pixel = floor(pixel) / texSize; +// return at(pixel + onePixel / 2.).xyz; +// } + +// vec3 CubicHermite (vec3 A, vec3 B, vec3 C, vec3 D, float t) { +// float t2 = t*t; +// float t3 = t*t*t; +// vec3 a = -A/2.0 + (3.0*B)/2.0 - (3.0*C)/2.0 + D/2.0; +// vec3 b = A - (5.0*B)/2.0 + 2.0*C - D / 2.0; +// vec3 c = -A/2.0 + C/2.0; +// vec3 d = B; +// return a*t3 + b*t2 + c*t + d; +// } + +// vec3 BicubicHermiteTextureSample (vec2 P) +// { +// vec2 pixel = P * texSize + .5; +// vec2 onePixel = 1.0 / texSize, twoPixels = 2.0 / texSize; +// vec2 frac = fract(pixel); +// pixel = floor(pixel) / texSize - onePixel / 2.; +// +// vec3 C00 = at(pixel + vec2(-1., -1.) * onePixel).xyz; +// vec3 C10 = at(pixel + vec2( 0., -1.) * onePixel).xyz; +// vec3 C20 = at(pixel + vec2(-1., -1.) * onePixel).xyz; +// vec3 C30 = at(pixel + vec2(twoPixels.x, -onePixel.y)).xyz; +// +// vec3 C01 = at(pixel + vec2(-onePixel.x , 0.0)).xyz; +// vec3 C11 = at(pixel + vec2( 0.0 , 0.0)).xyz; +// vec3 C21 = at(pixel + vec2( onePixel.x , 0.0)).xyz; +// vec3 C31 = at(pixel + vec2( twoPixels.x, 0.0)).xyz; +// +// vec3 C02 = at(pixel + vec2(-onePixel.x , onePixel.y)).xyz; +// vec3 C12 = at(pixel + vec2( 0.0 , onePixel.y)).xyz; +// vec3 C22 = at(pixel + vec2( onePixel.x , onePixel.y)).xyz; +// vec3 C32 = at(pixel + vec2( twoPixels.x, onePixel.y)).xyz; +// +// vec3 C03 = at(pixel + vec2(-onePixel.x , twoPixels.y)).xyz; +// vec3 C13 = at(pixel + vec2( 0.0 , twoPixels.y)).xyz; +// vec3 C23 = at(pixel + vec2( onePixel.x , twoPixels.y)).xyz; +// vec3 C33 = at(pixel + vec2( twoPixels.y, twoPixels.y)).xyz; +// +// vec3 CP0X = CubicHermite(C00, C10, C20, C30, frac.x); +// vec3 CP1X = CubicHermite(C01, C11, C21, C31, frac.x); +// vec3 CP2X = CubicHermite(C02, C12, C22, C32, frac.x); +// vec3 CP3X = CubicHermite(C03, C13, C23, C33, frac.x); +// +// return CubicHermite(CP0X, CP1X, CP2X, CP3X, frac.y); +// } + +vec4 TEX(vec2 uv) { + return at(uv); +} \ No newline at end of file diff --git a/TMessagesProj/src/main/res/raw/yuv_pq2rgb.glsl b/TMessagesProj/src/main/res/raw/yuv_pq2rgb.glsl new file mode 100644 index 0000000000..c8840088ca --- /dev/null +++ b/TMessagesProj/src/main/res/raw/yuv_pq2rgb.glsl @@ -0,0 +1,105 @@ +#version 320 es + +#extension GL_OES_EGL_image_external : require +#extension GL_EXT_YUV_target : require + +precision mediump float; + +uniform __samplerExternal2DY2YEXT sTexture; + +const mat4 YUV_TO_RGB_REC2020 = mat4(1.167808, 1.167808, 1.167808, 0, 0, -0.187877, 2.148072, 0, 1.683611, -0.652337, 0, 0, -0.914865, 0.347048, -1.147095, 1); + +const mat3 REC709_XYZ = mat3(0.4124564, 0.3575761, 0.1804375, 0.2126729, 0.7151522, 0.0721750, 0.0193339, 0.1191920, 0.9503041); +const mat3 XYZ_REC709 = mat3(3.2404542, -1.5371385, -0.4985314, -0.9692660, 1.8760108, 0.0415560, 0.0556434, -0.2040259, 1.0572252); +const mat3 REC2020_XYZ = mat3(0.6370, 0.1446, 0.1689, 0.2627, 0.6780, 0.0593, 0.0, 0.0281, 1.0610); +const mat3 XYZ_REC2020 = mat3(1.7167, -0.3557, -0.2534, -0.6667, 1.6165, 0.0158, 0.0176, -0.0428, 0.9421); + +highp vec3 sRGB_EOTF_Inv(highp vec3 x) { + return mix(12.92 * x, 1.055 * pow(x, vec3(1.0 / 2.4)) - .055, step(.0031308, x)); +} + +uniform vec2 texSize; + +vec4 at(vec2 uv) { + highp vec4 srcYuv = texture(sTexture, uv); + highp vec3 rgb_BT2020 = clamp((YUV_TO_RGB_REC2020 * srcYuv).rgb, 0., 1.); + highp vec3 rgb_BT2020_pqexp = pow(rgb_BT2020, vec3(1.0 / 78.84375)); + highp vec3 rgb_BT2020_sceneLinear = 5000.0 * pow(max(rgb_BT2020_pqexp - 0.8359375, 0.0) / (18.8515625 - 18.6875 * rgb_BT2020_pqexp), vec3(1.0 / 0.1593017578125)); + highp vec3 rgb_BT2020_displayLinear = rgb_BT2020_sceneLinear / 100.; + highp vec3 xyz_displayLinear = rgb_BT2020_displayLinear*REC2020_XYZ; // REC709_XYZ + highp vec3 xyz_tonemap = xyz_displayLinear / (xyz_displayLinear.y + 1.); + highp vec3 rgb_BT709_displayLinear = clamp( xyz_tonemap*XYZ_REC709, 0., 1.); // XYZ_REC2020 + highp vec3 rgb_BT709_sRGB = sRGB_EOTF_Inv(rgb_BT709_displayLinear); + return vec4(rgb_BT709_sRGB, 1.); +} + +// vec4 BilinearTextureSample(vec2 P) { +// vec2 onePixel = 1. / texSize; +// vec2 pixel = P * texSize + .5; +// vec2 frac = fract(pixel); +// pixel = (floor(pixel) / texSize) - onePixel / 2.; +// return mix( +// mix(at(pixel + vec2(0., 0.) * onePixel), at(pixel + vec2(1., 0.) * onePixel), frac.x), +// mix(at(pixel + vec2(0., 1.) * onePixel), at(pixel + vec2(1., 1.) * onePixel), frac.x), +// frac.y +// ); +// } + +// vec4 NearestTextureSample (vec2 P) { +// vec2 onePixel = 1. / texSize; +// vec2 pixel = P * texSize; +// vec2 frac = fract(pixel); +// pixel = floor(pixel) / texSize; +// return at(pixel + onePixel / 2.); +// } + +//vec3 CubicHermite (vec3 A, vec3 B, vec3 C, vec3 D, float t) { +// float t2 = t*t; +// float t3 = t*t*t; +// vec3 a = -A/2.0 + (3.0*B)/2.0 - (3.0*C)/2.0 + D/2.0; +// vec3 b = A - (5.0*B)/2.0 + 2.0*C - D / 2.0; +// vec3 c = -A/2.0 + C/2.0; +// vec3 d = B; +// return a * t3 + b * t2 + c*t + d; +//} + +//vec3 BicubicHermiteTextureSample (vec2 P) +//{ +// vec2 pixel = P * texSize + .5; +// vec2 onePixel = 1. / texSize; +// vec2 twoPixels = 2. / texSize; +// +// vec2 frac = fract(pixel); +// pixel = floor(pixel) / texSize - onePixel / 2.; +// +// vec3 C00 = at(pixel + vec2(-1., -1.) * onePixel).xyz; +// vec3 C10 = at(pixel + vec2( 0., -1.) * onePixel).xyz; +// vec3 C20 = at(pixel + vec2(-1., -1.) * onePixel).xyz; +// vec3 C30 = at(pixel + vec2(twoPixels.x, -onePixel.y)).xyz; +// +// vec3 C01 = at(pixel + vec2(-onePixel.x , 0.0)).xyz; +// vec3 C11 = at(pixel + vec2( 0.0 , 0.0)).xyz; +// vec3 C21 = at(pixel + vec2( onePixel.x , 0.0)).xyz; +// vec3 C31 = at(pixel + vec2( twoPixels.x, 0.0)).xyz; +// +// vec3 C02 = at(pixel + vec2(-onePixel.x , onePixel.y)).xyz; +// vec3 C12 = at(pixel + vec2( 0.0 , onePixel.y)).xyz; +// vec3 C22 = at(pixel + vec2( onePixel.x , onePixel.y)).xyz; +// vec3 C32 = at(pixel + vec2( twoPixels.x, onePixel.y)).xyz; +// +// vec3 C03 = at(pixel + vec2(-onePixel.x , twoPixels.y)).xyz; +// vec3 C13 = at(pixel + vec2( 0.0 , twoPixels.y)).xyz; +// vec3 C23 = at(pixel + vec2( onePixel.x , twoPixels.y)).xyz; +// vec3 C33 = at(pixel + vec2( twoPixels.y, twoPixels.y)).xyz; +// +// vec3 CP0X = CubicHermite(C00, C10, C20, C30, frac.x); +// vec3 CP1X = CubicHermite(C01, C11, C21, C31, frac.x); +// vec3 CP2X = CubicHermite(C02, C12, C22, C32, frac.x); +// vec3 CP3X = CubicHermite(C03, C13, C23, C33, frac.x); +// +// return CubicHermite(CP0X, CP1X, CP2X, CP3X, frac.y); +//} + +vec4 TEX(vec2 uv) { + return at(uv); +} \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-zh-rCN/strings_neko.xml b/TMessagesProj/src/main/res/values-zh-rCN/strings_neko.xml index 9e219369d5..7328d3eee9 100644 --- a/TMessagesProj/src/main/res/values-zh-rCN/strings_neko.xml +++ b/TMessagesProj/src/main/res/values-zh-rCN/strings_neko.xml @@ -193,4 +193,7 @@ 使用 OSMDroid 地图 修复 Google 地图在中国的漂移问题 在聊天中使用 Telegram 翻译界面 + 最近对话 + 清除最近对话 + 您确定要清除您的最近对话吗? diff --git a/TMessagesProj/src/main/res/values/strings.xml b/TMessagesProj/src/main/res/values/strings.xml index ff10d277c3..d7692a4881 100644 --- a/TMessagesProj/src/main/res/values/strings.xml +++ b/TMessagesProj/src/main/res/values/strings.xml @@ -131,6 +131,7 @@ Sorry, the payment was cancelled by the bot. Sorry, the payment was declined. Unable to reach payment server. Please check your internet connection and try again. + There was an error during payment confirmation. Warning Neither Telegram, nor %1$s will have access to your credit card information. Credit card details will be handled only by the payment system, %2$s.\n\nPayments will go directly to the developer of %1$s. Telegram cannot provide any guarantees, so proceed at your own risk. In case of problems, please contact the developer of %1$s or your bank. Password & Email @@ -315,8 +316,24 @@ Send Message Mention Notifications muted + Notifications muted for %d chat + Notifications muted for %d chats + Notifications muted for %d chats + Notifications muted for %d chats + Notifications muted for %d chats + Notifications unmuted for %d chat + Notifications unmuted for %d chats + Notifications unmuted for %d chats + Notifications unmuted for %d chats + Notifications unmuted for %d chats Notifications muted for %1$s Notifications unmuted + Do Not Notify + Do Not Notify\nAbout Stories + Notify + Notify About\nStories + Disabled notifications for **%s** stories. + You will receive a notification whenever **%s** posts a new story. %1$d messages deleted Message deleted %1$d messages deleted @@ -692,6 +709,7 @@ Link Tap to add a permanent link Choose photo + Add Image Choose photo or video Set new photo Take photo @@ -1027,10 +1045,10 @@ recommended Advertiser Info What are sponsored\nmessages? - Unlike other apps, Telegram never uses your private data to target ads. You are seeing this message only because someone chose this public one-to-many channel as a space to promote their messages. This means that no user data is mined or analyzed to display ads, and every user viewing a channel on Telegram sees the same sponsored message. - Unlike other apps, Telegram doesn\'t track whether you tapped on a sponsored message and doesn\'t profile you based on your activity. We also prevent external links in sponsored messages to ensure that third parties can’t spy on our users. We believe that everyone has the right to privacy, and technological platforms should respect that. - Telegram offers free and unlimited service to hundreds of millions of users, which involves significant server and traffic costs. In order to remain independent and stay true to its values, Telegram developed a paid tool to promote messages with user privacy in mind. We welcome responsible advertisers at: - Ads should no longer be synonymous with abuse of user privacy. Let us redefine how a tech company should operate – together. + Unlike other apps, Telegram never uses your private data to target ads. [Learn more in the Privacy Policy](https://telegram.org/privacy#5-6-no-ads-based-on-user-data) + Unlike other apps, Telegram doesn\'t track whether you tapped on a sponsored message and doesn\'t profile you based on your activity. We also prevent external links in sponsored messages to ensure that third parties can’t spy on our users. We believe that everyone has the right to privacy, and technological platforms should respect that. + Telegram offers free and unlimited service to hundreds of millions of users, which involves significant server and traffic costs. In order to remain independent and stay true to its values, Telegram developed a paid tool to promote messages with user privacy in mind. We welcome responsible advertisers at: + Ads should no longer be synonymous with abuse of user privacy. Let us redefine how a tech company should operate – together. Sponsored messages See https://telegram.org LEARN MORE @@ -1629,6 +1647,7 @@ Email copied to clipboard Invite to Group via Link Invite Link + Invite Link only works for group members Invite Links Are you sure you want to revoke this link? Once the link is revoked, no one will be able to join using it. The previous invite link is now inactive. A new link has been generated. @@ -1765,7 +1784,6 @@ Stickers Settings - Manage your sticker packs, emoji and reactions. Loop Animated Stickers Animated stickers will play in chat continuously. Animated Stickers @@ -1897,6 +1915,7 @@ No archived masks No archived emoji packs You can add up to 200 sticker sets.\nUnused sets are archived when you add more. + You can add up to 200 emoji packs.\nUnused packs are archived when you add more. You can have up to 200 sets of masks.\nUnused sets are archived when you add more. SEND STICKER Archived stickers @@ -2065,6 +2084,9 @@ Message notifications Show notifications Message Preview + Show Senders Name + Automatic Exceptions + Automatically enable story notifications from 5 users you contact most frequently. Group notifications Sound In-app notifications @@ -2125,7 +2147,7 @@ Reset chat backgrounds Are you sure you want to reset all chat backgrounds? Are you sure you want to delete the selected backgrounds? - Background Preview + Wallpaper Hint: some background images look better when blurred. Woo-hoo! Thanks Swipe left or right to see more colors. @@ -2287,7 +2309,9 @@ The account was hidden by the user Auto-play media Raise to Speak + Record voice messages by raising phone to your ear Raise to Listen + Switch sound to the earpiece by raising phone to your ear Save to Gallery Sound muted Edit name @@ -2376,6 +2400,8 @@ You can change your language later in Settings. Choose your language Other + Paste + Flip Paste from clipboard Proxy Settings Proxy Details @@ -2430,12 +2456,14 @@ Private Chats Groups Channels + Stories Please note that **%1$s** are listed as exceptions and won\'t be affected by this change. Please note that **%1$s** is listed as an exception and won\'t be affected by this change. View Exceptions Notifications for groups Notifications for private chats Notifications for channels + Story Notifications Tap to change Alternative options Add another account @@ -2777,6 +2805,8 @@ Reorder Edit folder Edit folders + Mute All + Unmute All Contacts Non Contacts Groups @@ -2807,6 +2837,8 @@ Please wait a few moments while we fill this folder for you... No chats found No chats currently belong to this folder. + No chats found + No chats in this folder to forward to. New Folder No Chats Contact @@ -2814,6 +2846,7 @@ Add Exception Folder name Are you sure you want to delete this folder? + Are you sure you want to delete this folder? This will also deactivate all the invite links used to share this folder. Apply Changes? You have edited this folder. Apply changes? Create Folder? @@ -3096,7 +3129,7 @@ Are you sure you want to delete this photo for everyone? Are you sure you want to delete this video for everyone? Are you sure you want to delete this GIF for everyone? - Discard changes? + Discard changes Are you sure you want to discard all changes? Clear search history? Are you sure you want to clear your search history? @@ -3384,6 +3417,10 @@ You can add users or entire groups that will **not** see your profile photo. Add users or entire groups that will still see your profile photo. Add users or entire groups as exceptions that will override the settings above. + Bio + Who can see my bio? + You can restrict who can see you bio with granular precision. + You can add users or entire groups that will not see your bio. Phone Number Who can see my phone number? Who can find me by my number? @@ -3572,6 +3609,7 @@ un1 disabled the auto-delete timer You disabled the auto-delete timer un1 joined the group via invite link + un1 joined the group via folder invite link un1 removed un2 un1 left the group un1 added un2 @@ -3614,6 +3652,7 @@ You invited un2 to the voice chat un1 invited you to the voice chat You allowed this bot to message you when you logged in on %1$s. + You allowed this bot to message you when you logged in on "%1$s" app. %1$s received the following documents: %2$s Personal details Address @@ -3775,6 +3814,7 @@ Do you want to send this contact to **%1$s**? There is no Telegram account with this username. There is no Telegram account with this phone number. + No chat list found. This bot can\'t join groups. Would you like to enable extended link previews in Secret Chats? Note that link previews are generated on Telegram servers. Please note that inline bots are provided by third-party developers. For the bot to work, the symbols you type after the bot\'s username are sent to the respective developer. @@ -4335,6 +4375,7 @@ Invite link for this admin Everyone on Telegram can scan this code to join your channel. Your friends can add this proxy by scanning this code with phone or in-app camera. + Everyone on Telegram can scan this code to add this folder and join the chats included in this invite link. Anyone who has Telegram installed will be able to join your channel by following this link. The link expires in %s Public link @@ -4697,6 +4738,12 @@ %1$d exceptions %1$d exceptions %1$d exceptions + %1$d automatic exceptions + %1$d automatic exception + %1$d automatic exceptions + %1$d automatic exceptions + %1$d automatic exceptions + %1$d automatic exceptions %1$d removed users %1$d removed user %1$d removed users @@ -4715,6 +4762,12 @@ updated %1$d minutes ago updated %1$d minutes ago updated %1$d minutes ago + %1$d minutes ago + %1$d minute ago + %1$d minutes ago + %1$d minutes ago + %1$d minutes ago + %1$d minutes ago **%1$d** seconds **%1$d** second **%1$d** seconds @@ -5277,6 +5330,7 @@ %1$d unread chats With new reaction Choose speed + Capture story MMM yyyy MMM dd yyyy, h:mm a @@ -5342,6 +5396,7 @@ Hide caption Tap here for forwarding options Change colors + Set Wallpaper Select theme Apply Theme Reset Theme @@ -5364,7 +5419,6 @@ Theme will be also disabled for **%s**. To protect privacy, views are only stored for **7 days** If **%s** were viewing the chat now, they would also see this animation. - Tap here to view how **%s** will see this theme when using night mode. Attach... Calendar Zoom in @@ -5382,6 +5436,14 @@ Approve New Members Turn this on if you want users to join only after they are approved by an admin. No join requests + %1$s, %2$s + %1$s, %2$s and %3$s + %1$s, %2$s and %3$d other members + %1$s, %2$s and %3$d other member + %1$s, %2$s and %3$d other members + %1$s, %2$s and %3$d other members + %1$s, %2$s and %3$d other members + %1$s, %2$s and %3$d other members Request to Join Group Request to Join Channel This group accepts new members only after they are approved by its admins. @@ -5412,6 +5474,7 @@ Dismiss request requested to join %1$s added by %1$s %2$s + joined via a folder invite link %1$s added to the group %1$s added to the channel **%d members** added to the group @@ -5442,6 +5505,7 @@ Copying and forwarding is not allowed from this bot. This topic contains **%s** This message contains **%s** + This story contains **%s** This message contains emoji from %s pack. This message contains emoji from **%d Packs**. This message contains emoji from **%d Packs**. @@ -5643,6 +5707,7 @@ Private Chats Sound Groups Sound Channels Sound + Stories Sound Too long Too large Invalid format @@ -6274,6 +6339,7 @@ Save on your subscription up to **%1$d%%** Sign up for the annual payment plan for Telegram Premium now to get the discount. Pause music while recording + Pause music when you start recording a video message Set Profile Photo Status Free up to **%s** @@ -6385,4 +6451,559 @@ SD Card Error Telegram is unable to save data on your SD card. Do not use SD card + Show next media on Tap + Tap near the edge of the screen while viewing media to navigate between photos + Media and Sound + Other Settings + Manage stickers, emoji and reactions + + Share Folder + Invite Links + Share folder + Create an Invite Link + Share access to some of this folder’s groups and channels with others. + Create more links to set up different access levels for different people. + Name this link + Includes %d chat + Includes %d chats + Includes %d chats + Includes %d chats + Includes %d chats + Anyone with this link can add **%2$s** folder and the **%1$d** chat selected below. + Anyone with this link can add **%2$s** folder and the **%1$d** chats selected below. + Anyone with this link can add **%2$s** folder and the **%1$d** chats selected below. + Anyone with this link can add **%2$s** folder and the **%1$d** chats selected below. + Anyone with this link can add **%2$s** folder and the **%1$d** chats selected below. + There are no chats in this folder that you can share with others. + %d chat selected + %d chats selected + %d chats selected + %d chats selected + %d chats selected + These chats cannot be shared + No chats selected + Select group and channels that you want everyone who adds the folder via invite link to join. + You can only share groups and channels in which you are allowed to create invite links. + you can’t invite others here + You don’t have the admin rights to share invite links to this group. + You don’t have the admin rights to share invite links to this private group. + you can’t invite others here + You don’t have the admin rights to share invite links to this channel. + You don’t have the admin rights to share invite links to this private channel. + you can’t share chats with users + You can’t put users into shared folders. + you can’t share chats with bots + You can’t put bots into shared folders. + Please, finish creating this folder to share it. + You can’t share empty folders. + You can’t share folders without a name. + You can’t share folders with excluded chats. + You can’t share folders with include chat types. + You can only share folders without chat types and excluded chats. + Unsaved Changes + You have changed the settings of this invite link. Apply changes? + You can only create %1$d invite links.\nUpgrade to **Telegram Premium** to increase this limit to **%2$d**. + You can only create %1$d invite links. Try deleting some folder invite links. + You can only create %1$d invite links. We are working to let you increase this limit in the future. + You can only have %1$d shareable folders.\nUpgrade to **Telegram Premium** to increase this limit to **%2$d**. + You can only have %1$d shareable folders. Try deleting some shareable folder. + You can only have %1$d shareable folders. We are working to let you increase this limit in the future. + Set Background From Gallery + You set a new wallpaper for this chat. + You set the same wallpaper for the chat. + %s set a new wallpaper for this chat. + %s set a same wallpaper for this chat. + Remove Folder + Folder already added + Add folder + Add Chats to Folder + All chats + Personal + Do you also want to quit the chats included in this folder? + You have already added the folder **%s** and all its chats. + Do you want to join **%1$d chat** and add them to the folder **%2$s**? + Do you want to join **%1$d chats** and add them to the folder **%2$s**? + Do you want to join **%1$d chats** and add them to the folder **%2$s**? + Do you want to join **%1$d chats** and add them to the folder **%2$s**? + Do you want to join **%1$d chats** and add them to the folder **%2$s**? + Do you want to add a new chat folder **%s** and join its group and channels? + Remove Folder + Remove Folder and Chats + Add %s + Join chat + Join chats + Join chats + Join chats + Join chats + Do not join any chats + %d chat to quit + %d chats to quit + %d chats to quit + %d chats to quit + %d chats to quit + %d chat to join + %d chats to join + %d chats to join + %d chats to join + %d chats to join + Chats you already joined + You can deselect the chats you don’t want to join. + You can select the chats you want to leave. + You already subscribed to this channel. + You are already a member of this group. + Select All + Deselect All + Folder **%s** Added + You also joined %d chat. + You also joined %d chats. + You also joined %d chats. + You also joined %d chats. + You also joined %d chats. + Folder **%s** Updated + You have joined %d new chat. + You have joined %d new chats. + You have joined %d new chats. + You have joined %d new chats. + You have joined %d new chats. + You have added %d new chat. + You have added %d new chats. + You have added %d new chats. + You have added %d new chats. + You have added %d new chats. + Folder **%s** deleted. + Folder %s Deleted + You also left %d chat. + You also left %d chats. + You also left %d chats. + You also left %d chats. + You also left %d chats. + Link Name saved. + View Chat List + You can join **%d new chat** + You can join **%d new chats** + You can join **%d new chats** + You can join **%d new chats** + You can join **%d new chats** + Tap here to view it + Tap here to view them + Tap here to view them + Tap here to view them + Tap here to view them + Share Folder %s + Share access to some of this folder’s groups and channels with others. + You can create more links to set up different access levels for different people. + Create Invite Link + Create a New Invite Link + Chat added to folder + %d chats added to folder + %d chats added to folder + %d chats added to folder + %d chats added to folder + Chat removed from folder + %d chats removed from folder + %d chats removed from folder + %d chats removed from folder + %d chats removed from folder + It will not affect chatlist of the links of this folder. + You don’t have access to share any of these chats. + One of the groups in this folder can’t be added because one of its admins has too many groups and channels. + You have changed some settings for this bot. Apply changes? + Delete Bot + Public Link + Public Links + %1$s/%2$s + Edit Intro + Edit Commands + Change Bot Settings + Use @BotFather to manage this bot. + Set public link + Drag and drop links to change the order in which they will be displayed on the bot info page. + Send When Online + Choose Wallpaper from Gallery + Choose a New Wallpaper + Choose Color as a Wallpaper + Theme will also be applied for %s + Background dimming + Apply the wallpaper in this chat + %s will be able to apply this wallpaper + APPLY FOR THIS CHAT + Remove Wallpaper + Set a Color as Wallpaper + Bot Name + This link cannot be edited. You can acquire additional usernames on *Fragment*. + Preview this background in night mode. + Preview this background in day mode. + View Wallpaper + Wallpaper + Set a new chat Wallpaper + Set a same chat Wallpaper + You will connect to this bot\'s website to launch its web app. + More about this bot + Tap to view this theme in the day mode. + Tap to view this theme in the night mode. + Open App + Get Premium back with up to **%1$d%%** off + Your Telegram Premium has recently expired. Tap here to extend it. + Reply privately... + My Story + %d Stories + %d Story + %d Stories + %d Stories + %d Stories + %d Stories + %d new stories + %d new story + %d new stories + %d new stories + %d new stories + %d new stories + Delete Story + Are you sure you want to delete this story? + Delete Stories + Are you sure you want to delete %d stories? + Are you sure you want to delete %d story? + Are you sure you want to delete %d stories? + Are you sure you want to delete %d stories? + Are you sure you want to delete %d stories? + Are you sure you want to delete %d stories? + Uploading edit… + No views yet + No views yet… + No views available + Uploading story error + My Story + Uploading… + Story + Message sent. + Just now + Expired story + Stories + Reaction sent. + My Stories + Saved + %d saved stories + %d saved story + %d saved stories + %d saved stories + %d saved stories + %d saved stories + Stories Archive + Archive + Only you can see archived stories unless you choose to save them to your profile. + %d archived stories + %d archived story + %d archived stories + %d archived stories + %d archived stories + %d public stories + %d public story + %d public stories + %d public stories + %d public stories + %d public stories + %d Selected + %d Selected + %d Selected + %d Selected + %d Selected + %d Selected + Photo + Video + None + %d recipients + %d recipient + %d recipients + %d recipients + %d recipients + %d recipients + Everyone + Close Friends + Contacts + %d contacts + %d contact + %d contacts + %d contacts + %d contacts + %d contacts + Contacts (-%d) + Contacts (-%d) + Contacts (-%d) + Contacts (-%d) + Contacts (-%d) + Contacts (-%d) + Share story + Edit Privacy Settings + Choose who can view for %d hours + Choose who can view for %d hour + Choose who can view for %d hours + Choose who can view for %d hours + Choose who can view for %d hours + Choose who can view for %d hours + Choose who can view in your profile + Close Friends + Choose close friends + Excluded Contacts + Exclude contacts + Select Contacts + Choose contacts + Send as message + Choose recipients + Save Settings + Post Story + Save List + Exclude Contacts + Everyone + Contacts + exclude people + Close Friends + edit list + Selected Contacts + edit list + %d people + %d person + %d people + %d people + %d people + %d people + except %s + except %d people + except %d person + except %d people + except %d people + except %d people + except %d people + Set who can view your story + Pinch to zoom + Swipe to zoom + Add a caption… + Save Image + Save Video + Save to Profile + Archive Story + Archive Stories + Archive Story + Archive Stories + Archive Stories + Archive Stories + Archive Stories + Edit Story + Who can see + Everyone + Close Friends + Failed to process video file. + Preparing video… + No saved stories + No public stories + Open the ’Archive’ tab to select stories you want to be displayed in your profile. + No stories yet… + Upload a new story to view it here. + Save to profile + %d stories saved to your profile + %d story saved to your profile + %d stories saved to your profile + %d stories saved to your profile + %d stories saved to your profile + %d stories saved to your profile + Saved stories can be viewed by others on your profile until you remove them. + %d stories are hidden from your profile + %d story is hidden from your profile + %d stories are hidden from your profile + %d stories are hidden from your profile + %d stories are hidden from your profile + %d stories are hidden from your profile + Open Link + This story is shown to everyone. + This story is shown to your close friends. + This story is shown to all your contacts. + This story is shown to your contacts (excluding %d). + This story is shown to your contacts (excluding %d). + This story is shown to your contacts (excluding %d). + This story is shown to your contacts (excluding %d). + This story is shown to your contacts (excluding %d). + This story is shown to your contacts (excluding %d). + This story is shown to %d contacts. + This story is shown to %d contact. + This story is shown to %d contacts. + This story is shown to %d contacts. + This story is shown to %d contacts. + This story is shown to %d contacts. + This story is pinned to your profile. + This story is hidden from your profile. + Draft + Drafts + Keep Draft + Save Draft + Delete Draft + Show in Chats + Forwarded Story + From **%s** + Choose how long the story will be visible. + Keep Always + Stories of **%s** were moved to **Archive**. + Stories of **%s** were moved to **Chats**. + Story shared + Add Story + %d Drafts + %d Draft + %d Drafts + %d Drafts + %d Drafts + %d Drafts + Sound is now muted. + Sound is now not muted. + Draft saved + Uploading story… + %s shared a story with you + %s shared a story to the group + %s shared a story + Stories + View Story + Subscribe to **Telegram Premium** to make your stories disappear after %d hours. + Subscribe to **Telegram Premium** to make your stories disappear after %d hour. + Subscribe to **Telegram Premium** to make your stories disappear after %d hours. + Subscribe to **Telegram Premium** to make your stories disappear after %d hours. + Subscribe to **Telegram Premium** to make your stories disappear after %d hours. + Subscribe to **Telegram Premium** to make your stories disappear after %d hours. + This story is not supported by your version of Telegram. + You can’t reply to this story + Story forwarded to **%s**. + Story forwarded to **Saved Messages** + Story forwarded to **a chat**. + Story forwarded to **%1$d chats**. + Story forwarded to **%1$d chats**. + Story forwarded to **%1$d chats**. + Story forwarded to **%1$d chats**. + This story is no longer available + %d stories were posted + %d story was posted + %d stories were posted + %d stories were posted + %d stories were posted + %d stories were posted + posted a story + posted %d stories + posted a story + posted %d stories + posted %d stories + posted %d stories + posted %d stories + posted by %1$s and %2$s + posted by %1$s, %2$s and %3$s + posted by %2$s, %3$s and %1$d more + posted by %2$s, %3$s and %1$d more + posted by %2$s, %3$s and %1$d more + posted by %2$s, %3$s and %1$d more + mentioned you in a story + Story unavailable + This story has expired + Stories + On + Hidden Stories + Mentioned you in a story + You mentioned %s + Stories + Enabled + Automatically enabled + Disabled + No one has viewed this story so far. + Only people from **%s’s** Close Friends list can view this story + Only some contacts **%s** selected can view this story. + Only **%s’s** contacts can view this story. + List of viewers isn\'t available after **24 hours** of story expiration. + Archived Stories + Saved Stories + New Story + Edit + Allow Screenshots + Keep on My Page + Keep this story on your page even after it expires in %d hours. Privacy settings will apply. + Keep this story on your page even after it expires in %d hour. Privacy settings will apply. + Keep this story on your page even after it expires in %d hours. Privacy settings will apply. + Keep this story on your page even after it expires in %d hours. Privacy settings will apply. + Keep this story on your page even after it expires in %d hours. Privacy settings will apply. + Keep this story on your page even after it expires in %d hours. Privacy settings will apply. + Downloading and taking screenshots will be enabled for this story. + Downloading, sharing and taking screenshots will be enabled for this story. + Downloading and taking screenshots will be disabled for this story. + Downloading, sharing and taking screenshots will be disabled for this story. + Users allowed to view your story will see it on your page after it expires. + The story will disappear after it expires. + Privacy Restrictions + The privacy settings of your story will prevent some users you tagged (%s) from viewing it. + Tap on %s to post a story for your contacts. + Take photos or videos to share with all your contacts or close friends at once. + Turn on both cameras at once + **%s** mentioned you in a story. + View Story + The story you were mentioned in is no longer available + The story where you mentioned %s is no longer available + You mentioned %s in a story. + Stories + cached story + Stories + This video has no sound + Drag to delete + Release to delete + Add text + This story contains stickers from **%d packs**. + This story contains stickers from **%d pack**. + This story contains stickers from **%d packs**. + This story contains stickers from **%d packs**. + This story contains stickers from **%d packs**. + This story contains stickers from **%d packs**. + This story contains stickers from %s. + This story contains emoji from **%d packs**. + This story contains emoji from **%d pack**. + This story contains emoji from **%d packs**. + This story contains emoji from **%d packs**. + This story contains emoji from **%d packs**. + This story contains emoji from **%d packs**. + This story contains emoji from %s. + This story contains emoji and stickers from **%d packs**. + This story contains emoji and stickers from **%d pack**. + This story contains emoji and stickers from **%d packs**. + This story contains emoji and stickers from **%d packs**. + This story contains emoji and stickers from **%d packs**. + This story contains emoji and stickers from **%d packs**. + This story contains emoji and stickers from %s. + Tap here to disable the second camera + Tap here to disable the selfie camera + Stories + Subscribe to **Telegram Premium** to unlock this and many other features. + Unmuted chats + Always keep archived + Keep archived chats in the Archive even if they are unmuted and get a new message. + Chats from folders + Always keep archived + Keep archived chats from folders in the Archive even if they are unmuted and get a new message. + Automatically archive + Archived chats will remain in the Archive when you receive a new message. **Tap to change >** + When you receive a new message, muted chats will remain in the Archive, while unmuted chats will be moved to Chats. **Tap to change >** + Archive Settings + How does it work? + Archived Chats + Move any chat into your Archive and back by swiping on it. + Hiding Archive + Hide the Archive from your Main screen by swiping on it. + Stories + Archive Stories from your contacts separately from chats with them. + Got it + Hide Dual Camera + Dual camera button is hidden + Show Dual Camera + Dual camera is enabled at your peril + :( + Unfortunately, the Dual camera mode is not supported on your device. + You can’t post more than **%d stories** in **24 hours**. + You can’t post more than **%d story** in **24 hours**. + You can’t post more than **%d stories** in **24 hours**. + You can’t post more than **%d stories** in **24 hours**. + You can’t post more than **%d stories** in **24 hours**. + You can’t post more than **%d stories** in **24 hours**. + Posting stories is currently available only to subscribers of **Telegram Premium**. + Hide Stories + Unhide Stories + Group Too Large + You can select groups that are up to 200 members. diff --git a/TMessagesProj/src/main/res/values/strings_neko.xml b/TMessagesProj/src/main/res/values/strings_neko.xml index f5bf2445b3..69f5411a22 100644 --- a/TMessagesProj/src/main/res/values/strings_neko.xml +++ b/TMessagesProj/src/main/res/values/strings_neko.xml @@ -195,4 +195,7 @@ Emoji set already applied. Use Telegram Translate UI in Chat Telegram API + Recent chats + Clear recent chats + Do you want to clear your recent chats? diff --git a/TMessagesProj_AppHockeyApp/build.gradle b/TMessagesProj_AppHockeyApp/build.gradle index ef33612c73..b20a2c85c4 100644 --- a/TMessagesProj_AppHockeyApp/build.gradle +++ b/TMessagesProj_AppHockeyApp/build.gradle @@ -22,6 +22,7 @@ dependencies { implementation "com.microsoft.appcenter:appcenter-distribute:3.3.1" implementation "com.microsoft.appcenter:appcenter-crashes:3.3.1" + implementation "com.microsoft.appcenter:appcenter-analytics:3.3.1" } android { diff --git a/TMessagesProj_AppHockeyApp/src/main/java/org/telegram/messenger/ApplicationLoaderImpl.java b/TMessagesProj_AppHockeyApp/src/main/java/org/telegram/messenger/ApplicationLoaderImpl.java index 1a7503e189..b578b6196c 100644 --- a/TMessagesProj_AppHockeyApp/src/main/java/org/telegram/messenger/ApplicationLoaderImpl.java +++ b/TMessagesProj_AppHockeyApp/src/main/java/org/telegram/messenger/ApplicationLoaderImpl.java @@ -1,16 +1,22 @@ package org.telegram.messenger; import android.app.Activity; +import android.os.Build; import android.os.SystemClock; import android.text.TextUtils; import com.microsoft.appcenter.AppCenter; +import com.microsoft.appcenter.CustomProperties; +import com.microsoft.appcenter.analytics.Analytics; +import com.microsoft.appcenter.analytics.EventProperties; import com.microsoft.appcenter.crashes.Crashes; import com.microsoft.appcenter.distribute.Distribute; import org.telegram.messenger.regular.BuildConfig; import org.telegram.tgnet.TLRPC; +import java.util.Locale; + public class ApplicationLoaderImpl extends ApplicationLoader { @Override protected String onGetApplicationId() { @@ -30,12 +36,24 @@ protected void startAppCenterInternal(Activity context) { if (TextUtils.isEmpty(appHash)) { throw new RuntimeException("App Center hash is empty. add to local.properties field APP_CENTER_HASH_PRIVATE and APP_CENTER_HASH_PUBLIC"); } - AppCenter.start(context.getApplication(), appHash, Distribute.class, Crashes.class); + AppCenter.start(context.getApplication(), appHash, Distribute.class, Crashes.class, Analytics.class); Crashes.getMinidumpDirectory().thenAccept(path -> { if (path != null) { Utilities.setupNativeCrashesListener(path); } }); + CustomProperties props = new CustomProperties(); + props.set("model", Build.MODEL); + props.set("manufacturer", Build.MANUFACTURER); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + props.set("model", Build.SOC_MODEL); + props.set("manufacturer", Build.SOC_MANUFACTURER); + } + props.set("device", Build.DEVICE); + props.set("product", Build.PRODUCT); + props.set("hardware", Build.HARDWARE); + props.set("user", Build.USER); + AppCenter.setCustomProperties(props); AppCenter.setUserId("uid=" + UserConfig.getInstance(UserConfig.selectedAccount).clientUserId); } } catch (Throwable e) { @@ -67,5 +85,19 @@ protected void appCenterLogInternal(Throwable e) { } } + protected void logDualCameraInternal(boolean success, boolean vendor) { + try { + Analytics.trackEvent("dual-camera[" + (Build.MANUFACTURER + " " + Build.DEVICE).toUpperCase() + "]", + new EventProperties() + .set("success", success) + .set("vendor", vendor) + .set("product", Build.PRODUCT + "") + .set("model", Build.MODEL) + ); + } catch (Throwable ignore) { + + } + } + } diff --git a/TMessagesProj_AppHuawei/src/main/java/org/telegram/messenger/GoogleVoiceClientService.java b/TMessagesProj_AppHuawei/src/main/java/org/telegram/messenger/GoogleVoiceClientService.java index 625a2aaefa..b31710977b 100644 --- a/TMessagesProj_AppHuawei/src/main/java/org/telegram/messenger/GoogleVoiceClientService.java +++ b/TMessagesProj_AppHuawei/src/main/java/org/telegram/messenger/GoogleVoiceClientService.java @@ -44,7 +44,7 @@ public void performAction(Intent intent, boolean isVerified, Bundle options) { } if (user != null) { ContactsController.getInstance(currentAccount).markAsContacted(contactUri); - SendMessagesHelper.getInstance(currentAccount).sendMessage(text, user.id, null, null, null, true, null, null, null, true, 0, null, false); + SendMessagesHelper.getInstance(currentAccount).sendMessage(SendMessagesHelper.SendMessageParams.of(text, user.id, null, null, null, true, null, null, null, true, 0, null, false)); } } } catch (Exception e) {